Skip to content

Commit

Permalink
store relayer on relay [SLT-182] (#3170)
Browse files Browse the repository at this point in the history
* store relayer on relay [SLT-182]

* +tests, zeroAddr check, fmt
  • Loading branch information
parodime authored Sep 26, 2024
1 parent 5c8a069 commit 65ee496
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 4 deletions.
17 changes: 13 additions & 4 deletions packages/contracts-rfq/contracts/FastBridgeV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors {
mapping(bytes32 => BridgeStatus) public bridgeStatuses;
/// @notice Proof of relayed bridge tx on origin chain
mapping(bytes32 => BridgeProof) public bridgeProofs;
/// @notice Whether bridge has been relayed on destination chain
mapping(bytes32 => bool) public bridgeRelays;
/// @notice Relay details on destination chain
mapping(bytes32 => BridgeRelay) public bridgeRelayDetails;

/// @dev to prevent replays
uint256 public nonce;
Expand Down Expand Up @@ -132,16 +132,19 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors {

/// @inheritdoc IFastBridgeV2
function relay(bytes memory request, address relayer) public payable {
if (relayer == address(0)) revert ZeroAddress();
bytes32 transactionId = keccak256(request);
BridgeTransaction memory transaction = getBridgeTransaction(request);
if (transaction.destChainId != uint32(block.chainid)) revert ChainIncorrect();

// check haven't exceeded deadline for relay to happen
if (block.timestamp > transaction.deadline) revert DeadlineExceeded();

if (bridgeRelayDetails[transactionId].relayer != address(0)) revert TransactionRelayed();

// mark bridge transaction as relayed
if (bridgeRelays[transactionId]) revert TransactionRelayed();
bridgeRelays[transactionId] = true;
bridgeRelayDetails[transactionId] =
BridgeRelay({blockNumber: uint48(block.number), blockTimestamp: uint48(block.timestamp), relayer: relayer});

// transfer tokens to recipient on destination chain and gas rebate if requested
address to = transaction.destRecipient;
Expand Down Expand Up @@ -175,6 +178,12 @@ contract FastBridgeV2 is Admin, IFastBridgeV2, IFastBridgeV2Errors {
);
}

Check notice

Code scanning / Slither

Reentrancy vulnerabilities Low

Reentrancy in FastBridgeV2.relay(bytes,address):
External calls:
- _pullToken(to,token,amount)
- returndata = address(token).functionCall(data)
- IERC20(token).safeTransferFrom(msg.sender,recipient,amount)
- (success,returndata) = target.call{value: value}(data)
- (success,None) = to.call{value: value}()
- token.universalTransfer(recipient,amount)
- IERC20(token).safeTransfer(to,value)
- _pullToken(to,token,amount + rebate)
- returndata = address(token).functionCall(data)
- IERC20(token).safeTransferFrom(msg.sender,recipient,amount)
- (success,returndata) = target.call{value: value}(data)
- (success,None) = to.call{value: value}()
- token.universalTransfer(recipient,amount)
- IERC20(token).safeTransfer(to,value)
- _pullToken(to,token,amount)
- returndata = address(token).functionCall(data)
- IERC20(token).safeTransferFrom(msg.sender,recipient,amount)
- (success,returndata) = target.call{value: value}(data)
- (success,None) = to.call{value: value}()
- token.universalTransfer(recipient,amount)
- IERC20(token).safeTransfer(to,value)
- _pullToken(to,UniversalTokenLib.ETH_ADDRESS,rebate)
- returndata = address(token).functionCall(data)
- IERC20(token).safeTransferFrom(msg.sender,recipient,amount)
- (success,returndata) = target.call{value: value}(data)
- (success,None) = to.call{value: value}()
- token.universalTransfer(recipient,amount)
- IERC20(token).safeTransfer(to,value)
External calls sending eth:
- _pullToken(to,token,amount)
- (success,returndata) = target.call{value: value}(data)
- (success,None) = to.call{value: value}()
- _pullToken(to,token,amount + rebate)
- (success,returndata) = target.call{value: value}(data)
- [(success,None) = to.call{value:

Check notice

Code scanning / Slither

Block timestamp Low

FastBridgeV2.relay(bytes,address) uses timestamp for comparisons
Dangerous comparisons:
- block.timestamp > transaction.deadline

/// @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);
Expand Down
10 changes: 10 additions & 0 deletions packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ pragma solidity ^0.8.20;
import {IFastBridge} from "./IFastBridge.sol";

interface IFastBridgeV2 is IFastBridge {
struct BridgeRelay {
uint48 blockNumber;
uint48 blockTimestamp;
address relayer;
}

/// @notice Relays destination side of bridge transaction by off-chain relayer
/// @param request The encoded bridge transaction to relay on destination chain
/// @param relayer The address of the relaying entity which should have control of the origin funds when claimed
Expand All @@ -19,4 +25,8 @@ interface IFastBridgeV2 is IFastBridge {
/// @notice Can only send funds to the relayer address on the proof.
/// @param request The encoded bridge transaction to claim on origin chain
function claim(bytes memory request) external;
/// @notice Checks if a transaction has been relayed
/// @param transactionId The ID of the transaction to check
/// @return True if the transaction has been relayed, false otherwise
function bridgeRelays(bytes32 transactionId) external view returns (bool);
}
22 changes: 22 additions & 0 deletions packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {ChainIncorrect, DeadlineExceeded, TransactionRelayed, ZeroAddress} from "../contracts/libs/Errors.sol";

import {FastBridgeV2, FastBridgeV2Test, IFastBridge} from "./FastBridgeV2.t.sol";

// solhint-disable func-name-mixedcase, ordering
Expand Down Expand Up @@ -113,6 +115,21 @@ contract FastBridgeV2DstTest is FastBridgeV2Test {
assertEq(address(fastBridge).balance, 0);
}

/// @notice RelayerB completes the ETH bridge request, using relayerA's address
function test_relay_eth_withRelayerAddress_checkBlockData() public {
vm.roll(987_654_321);
vm.warp(123_456_789);
bytes32 txId = getTxId(ethTx);
expectBridgeRelayed(ethTx, txId, address(relayerA));
relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: ethParams.destAmount, bridgeTx: ethTx});
assertTrue(fastBridge.bridgeRelays(txId));
(uint48 recordedBlockNumber, uint48 recordedblockTimestamp,) = fastBridge.bridgeRelayDetails(txId);
assertEq(recordedBlockNumber, 987_654_321);
assertEq(recordedblockTimestamp, 123_456_789);
assertEq(address(userB).balance, ethParams.destAmount);
assertEq(address(relayerB).balance, LEFTOVER_BALANCE);
assertEq(address(fastBridge).balance, 0);
}
// ══════════════════════════════════════════════════ REVERTS ══════════════════════════════════════════════════════

function test_relay_revert_chainIncorrect() public {
Expand Down Expand Up @@ -150,4 +167,9 @@ contract FastBridgeV2DstTest is FastBridgeV2Test {
vm.expectRevert(DeadlineExceeded.selector);
relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: 0, bridgeTx: tokenTx});
}

function test_relay_withRelayerAddress_revert_zeroAddr() public {
vm.expectRevert(ZeroAddress.selector);
relayWithAddress({caller: relayerA, relayer: address(0), msgValue: 0, bridgeTx: tokenTx});
}
}

0 comments on commit 65ee496

Please sign in to comment.