Skip to content

Commit

Permalink
Merge 95daba4 into 1652c22
Browse files Browse the repository at this point in the history
  • Loading branch information
parodime authored Oct 1, 2024
2 parents 1652c22 + 95daba4 commit afd430a
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 106 deletions.
212 changes: 106 additions & 106 deletions packages/contracts-rfq/contracts/FastBridgeV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,49 +31,14 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors {

/// @dev to prevent replays
uint256 public nonce;

// @dev the block the contract was deployed at
uint256 public immutable deployBlock;

function bridgeStatuses(bytes32 transactionId) public view returns (BridgeStatus status) {
return bridgeTxDetails[transactionId].status;
}

function bridgeProofs(bytes32 transactionId) public view returns (uint96 timestamp, address relayer) {
timestamp = bridgeTxDetails[transactionId].proofBlockTimestamp;
relayer = bridgeTxDetails[transactionId].proofRelayer;
}

constructor(address _owner) Admin(_owner) {
deployBlock = block.number;
}

/// @notice Pulls a requested token from the user to the requested recipient.
/// @dev Be careful of re-entrancy issues when msg.value > 0 and recipient != address(this)
function _pullToken(address recipient, address token, uint256 amount) internal returns (uint256 amountPulled) {
if (token != UniversalTokenLib.ETH_ADDRESS) {
token.assertIsContract();
// Record token balance before transfer
amountPulled = IERC20(token).balanceOf(recipient);
// Token needs to be pulled only if msg.value is zero
// This way user can specify WETH as the origin asset
IERC20(token).safeTransferFrom(msg.sender, recipient, amount);
// Use the difference between the recorded balance and the current balance as the amountPulled
amountPulled = IERC20(token).balanceOf(recipient) - amountPulled;
} else {
// Otherwise, we need to check that ETH amount matches msg.value
if (amount != msg.value) revert MsgValueIncorrect();
// Transfer value to recipient if not this address
if (recipient != address(this)) token.universalTransfer(recipient, amount);
// We will forward msg.value in the external call later, if recipient is not this contract
amountPulled = msg.value;
}
}

/// @inheritdoc IFastBridge
function getBridgeTransaction(bytes memory request) public pure returns (BridgeTransaction memory) {
return abi.decode(request, (BridgeTransaction));
}

/// @inheritdoc IFastBridge
function bridge(BridgeParams memory params) external payable {
// check bridge params
Expand Down Expand Up @@ -127,7 +92,68 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors {

/// @inheritdoc IFastBridge
function relay(bytes memory request) external payable {
relay(request, msg.sender);
relay({request: request, relayer: msg.sender});
}

/// @inheritdoc IFastBridge
function prove(bytes memory request, bytes32 destTxHash) external {
prove({transactionId: keccak256(request), destTxHash: destTxHash, relayer: msg.sender});
}

/// @inheritdoc IFastBridgeV2
function claim(bytes memory request) external {
claim({request: request, to: address(0)});
}

/// @inheritdoc IFastBridge
function dispute(bytes32 transactionId) external onlyRole(GUARD_ROLE) {
if (bridgeTxDetails[transactionId].status != BridgeStatus.RELAYER_PROVED) revert StatusIncorrect();
if (_timeSince(bridgeTxDetails[transactionId].proofBlockTimestamp) > DISPUTE_PERIOD) {
revert DisputePeriodPassed();
}

// @dev relayer gets slashed effectively if dest relay has gone thru
bridgeTxDetails[transactionId].status = BridgeStatus.REQUESTED;
bridgeTxDetails[transactionId].proofRelayer = address(0);
bridgeTxDetails[transactionId].proofBlockTimestamp = 0;
bridgeTxDetails[transactionId].proofBlockNumber = 0;

emit BridgeProofDisputed(transactionId, msg.sender);
}

/// @inheritdoc IFastBridge
function refund(bytes memory request) external {
bytes32 transactionId = keccak256(request);

BridgeTransaction memory transaction = getBridgeTransaction(request);

if (bridgeTxDetails[transactionId].status != BridgeStatus.REQUESTED) revert StatusIncorrect();

if (hasRole(REFUNDER_ROLE, msg.sender)) {
// Refunder can refund if deadline has passed
if (block.timestamp <= transaction.deadline) revert DeadlineNotExceeded();
} else {
// Permissionless refund is allowed after REFUND_DELAY
if (block.timestamp <= transaction.deadline + REFUND_DELAY) revert DeadlineNotExceeded();
}

// if all checks passed, set to REFUNDED status
bridgeTxDetails[transactionId].status = BridgeStatus.REFUNDED;

// transfer origin collateral back to original sender
address to = transaction.originSender;
address token = transaction.originToken;
uint256 amount = transaction.originAmount + transaction.originFeeAmount;
token.universalTransfer(to, amount);

emit BridgeDepositRefunded(transactionId, to, token, amount);
}

/// @inheritdoc IFastBridge
function canClaim(bytes32 transactionId, address relayer) external view returns (bool) {
if (bridgeTxDetails[transactionId].status != BridgeStatus.RELAYER_PROVED) revert StatusIncorrect();
if (bridgeTxDetails[transactionId].proofRelayer != relayer) revert SenderIncorrect();
return _timeSince(bridgeTxDetails[transactionId].proofBlockTimestamp) > DISPUTE_PERIOD;
}

/// @inheritdoc IFastBridgeV2
Expand Down Expand Up @@ -178,18 +204,6 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors {
);
}

/// @inheritdoc IFastBridgeV2
function bridgeRelays(bytes32 transactionId) public view returns (bool) {
// has this transactionId been relayed?
return bridgeRelayDetails[transactionId].relayer != address(0);
}

/// @inheritdoc IFastBridge
function prove(bytes memory request, bytes32 destTxHash) external {
bytes32 transactionId = keccak256(request);
prove(transactionId, destTxHash, msg.sender);
}

/// @inheritdoc IFastBridgeV2
function prove(bytes32 transactionId, bytes32 destTxHash, address relayer) public onlyRole(RELAYER_ROLE) {
// update bridge tx status given proof provided
Expand All @@ -202,30 +216,6 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors {
emit BridgeProofProvided(transactionId, relayer, destTxHash);
}

/// @notice Calculates time since proof submitted
/// @dev proof.timestamp stores casted uint40(block.timestamp) block timestamps for gas optimization
/// _timeSince(proof) can accomodate rollover case when block.timestamp > type(uint40).max but
/// proof.timestamp < type(uint40).max via unchecked statement
/// @param proofBlockTimestamp The bridge proof block timestamp
/// @return delta Time delta since proof submitted
function _timeSince(uint40 proofBlockTimestamp) internal view returns (uint256 delta) {
unchecked {
delta = uint40(block.timestamp) - proofBlockTimestamp;
}
}

/// @inheritdoc IFastBridge
function canClaim(bytes32 transactionId, address relayer) external view returns (bool) {
if (bridgeTxDetails[transactionId].status != BridgeStatus.RELAYER_PROVED) revert StatusIncorrect();
if (bridgeTxDetails[transactionId].proofRelayer != relayer) revert SenderIncorrect();
return _timeSince(bridgeTxDetails[transactionId].proofBlockTimestamp) > DISPUTE_PERIOD;
}

/// @inheritdoc IFastBridgeV2
function claim(bytes memory request) external {
claim(request, address(0));
}

/// @inheritdoc IFastBridge
function claim(bytes memory request, address to) public {
bytes32 transactionId = keccak256(request);
Expand Down Expand Up @@ -258,47 +248,57 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors {
emit BridgeDepositClaimed(transactionId, bridgeTxDetails[transactionId].proofRelayer, to, token, amount);
}

/// @inheritdoc IFastBridge
function dispute(bytes32 transactionId) external onlyRole(GUARD_ROLE) {
if (bridgeTxDetails[transactionId].status != BridgeStatus.RELAYER_PROVED) revert StatusIncorrect();
if (_timeSince(bridgeTxDetails[transactionId].proofBlockTimestamp) > DISPUTE_PERIOD) {
revert DisputePeriodPassed();
}
function bridgeStatuses(bytes32 transactionId) public view returns (BridgeStatus status) {
return bridgeTxDetails[transactionId].status;
}

// @dev relayer gets slashed effectively if dest relay has gone thru
bridgeTxDetails[transactionId].status = BridgeStatus.REQUESTED;
bridgeTxDetails[transactionId].proofRelayer = address(0);
bridgeTxDetails[transactionId].proofBlockTimestamp = 0;
bridgeTxDetails[transactionId].proofBlockNumber = 0;
function bridgeProofs(bytes32 transactionId) public view returns (uint96 timestamp, address relayer) {
timestamp = bridgeTxDetails[transactionId].proofBlockTimestamp;
relayer = bridgeTxDetails[transactionId].proofRelayer;
}

emit BridgeProofDisputed(transactionId, msg.sender);
/// @inheritdoc IFastBridgeV2
function bridgeRelays(bytes32 transactionId) public view returns (bool) {
// has this transactionId been relayed?
return bridgeRelayDetails[transactionId].relayer != address(0);
}

/// @inheritdoc IFastBridge
function refund(bytes memory request) external {
bytes32 transactionId = keccak256(request);

BridgeTransaction memory transaction = getBridgeTransaction(request);

if (bridgeTxDetails[transactionId].status != BridgeStatus.REQUESTED) revert StatusIncorrect();
function getBridgeTransaction(bytes memory request) public pure returns (BridgeTransaction memory) {
return abi.decode(request, (BridgeTransaction));
}

if (hasRole(REFUNDER_ROLE, msg.sender)) {
// Refunder can refund if deadline has passed
if (block.timestamp <= transaction.deadline) revert DeadlineNotExceeded();
/// @notice Pulls a requested token from the user to the requested recipient.
/// @dev Be careful of re-entrancy issues when msg.value > 0 and recipient != address(this)
function _pullToken(address recipient, address token, uint256 amount) internal returns (uint256 amountPulled) {
if (token != UniversalTokenLib.ETH_ADDRESS) {
token.assertIsContract();
// Record token balance before transfer
amountPulled = IERC20(token).balanceOf(recipient);
// Token needs to be pulled only if msg.value is zero
// This way user can specify WETH as the origin asset
IERC20(token).safeTransferFrom(msg.sender, recipient, amount);
// Use the difference between the recorded balance and the current balance as the amountPulled
amountPulled = IERC20(token).balanceOf(recipient) - amountPulled;
} else {
// Permissionless refund is allowed after REFUND_DELAY
if (block.timestamp <= transaction.deadline + REFUND_DELAY) revert DeadlineNotExceeded();
// Otherwise, we need to check that ETH amount matches msg.value
if (amount != msg.value) revert MsgValueIncorrect();
// Transfer value to recipient if not this address
if (recipient != address(this)) token.universalTransfer(recipient, amount);
// We will forward msg.value in the external call later, if recipient is not this contract
amountPulled = msg.value;
}
}

// if all checks passed, set to REFUNDED status
bridgeTxDetails[transactionId].status = BridgeStatus.REFUNDED;

// transfer origin collateral back to original sender
address to = transaction.originSender;
address token = transaction.originToken;
uint256 amount = transaction.originAmount + transaction.originFeeAmount;
token.universalTransfer(to, amount);

emit BridgeDepositRefunded(transactionId, to, token, amount);
/// @notice Calculates time since proof submitted
/// @dev proof.timestamp stores casted uint40(block.timestamp) block timestamps for gas optimization
/// _timeSince(proof) can accomodate rollover case when block.timestamp > type(uint40).max but
/// proof.timestamp < type(uint40).max via unchecked statement
/// @param proofBlockTimestamp The bridge proof block timestamp
/// @return delta Time delta since proof submitted
function _timeSince(uint40 proofBlockTimestamp) internal view returns (uint256 delta) {
unchecked {
delta = uint40(block.timestamp) - proofBlockTimestamp;
}
}
}
2 changes: 2 additions & 0 deletions packages/contracts-rfq/test/FastBridgeV2.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
pragma solidity ^0.8.20;

import {IFastBridge} from "../contracts/interfaces/IFastBridge.sol";

// solhint-disable-next-line no-unused-import
import {IFastBridgeV2} from "../contracts/interfaces/IFastBridgeV2.sol";

import {IFastBridgeV2Errors} from "../contracts/interfaces/IFastBridgeV2Errors.sol";
Expand Down

0 comments on commit afd430a

Please sign in to comment.