Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(rfq-relayer): support FastBridgeV2 with arbitrary calls [SLT-320] #3258

Open
wants to merge 45 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
36a42bd
feat: add `callValue` to bridge params
ChiTimesChi Oct 2, 2024
919a0a9
feat: add `callValue` to bridge tx V2
ChiTimesChi Oct 2, 2024
867d5d9
test: add cases for SRC calls with `callValue`
ChiTimesChi Oct 3, 2024
c93c403
refactor: simplify tests further
ChiTimesChi Oct 3, 2024
9575246
test: add cases for DST relays with `callValue`
ChiTimesChi Oct 3, 2024
7872f2a
feat: support `callValue` in `bridge()`
ChiTimesChi Oct 3, 2024
ad7dd4c
feat: support `callValue` in `relay()`
ChiTimesChi Oct 3, 2024
5ca5ad1
test: update revert message for failed ETH transfers
ChiTimesChi Oct 3, 2024
e8355c3
refactor: always forward full msg.value for the hook call
ChiTimesChi Oct 3, 2024
eb2bbb8
refactor: use `_pullToken` only in `bridge()`
ChiTimesChi Oct 3, 2024
c50042a
refactor: isolate validation of relay params
ChiTimesChi Oct 3, 2024
9fb4461
refactor: isolate validation of the bridge params
ChiTimesChi Oct 3, 2024
156e333
docs: could -> can
ChiTimesChi Oct 8, 2024
2b77b4a
test: enable the backwards compatibility encoding test
ChiTimesChi Oct 8, 2024
271f59d
fix: getBridgeTransaction partial support for V2 structs
ChiTimesChi Oct 8, 2024
2317a58
test: add clarifications about expected reverts
ChiTimesChi Oct 8, 2024
c6a1fdc
Feat: initial fastbridgev2 bindings
dwasse Oct 8, 2024
e5e8646
Feat: abigen helpers
dwasse Oct 8, 2024
ab63286
Fix: generate
dwasse Oct 8, 2024
93d9b7d
Fix: bridge enum test
dwasse Oct 8, 2024
e34a08b
Feat: relayer integrates fastbridgev2
dwasse Oct 8, 2024
1056ef1
Feat: testutils uses v2
dwasse Oct 8, 2024
1301270
Fix: deployer uses v2
dwasse Oct 8, 2024
1081e0a
Fix: current e2e tests with v2 bridge
dwasse Oct 8, 2024
467d5c7
Feat: add recipient mock test contract
dwasse Oct 9, 2024
f456bb7
Feat: add TestUSDCtoUSDCWithCallData
dwasse Oct 9, 2024
de621a7
Rename: TestArbitraryCall
dwasse Oct 9, 2024
e3c1604
Feat: add TransactionV1 in QuoteRequest struct for access to SendChai…
dwasse Oct 9, 2024
bff4f72
Cleanup: bridge tx fetching
dwasse Oct 9, 2024
4ed813b
Cleanup: remove guard check for now
dwasse Oct 9, 2024
5b6ec12
Feat: add v2 of fastbridgemock
dwasse Oct 11, 2024
d3dbeb0
Fix: build
dwasse Oct 11, 2024
0bc22c5
Cleanup: lint
dwasse Oct 11, 2024
117ce59
Cleanup: remove unnecessary test
dwasse Oct 11, 2024
6dee3d1
Fix: mock fast bridge deployer
dwasse Oct 11, 2024
f57312b
Revert "Cleanup: remove unnecessary test"
dwasse Oct 11, 2024
4b8dc68
Fix: flatten all files
dwasse Oct 11, 2024
b24d31d
Revert "Fix: flatten all files"
dwasse Oct 11, 2024
91a6b8f
Feat: flatten mocks
dwasse Oct 11, 2024
b6a4609
Fix: tests
dwasse Oct 11, 2024
05ab3dd
Fix: test
dwasse Oct 11, 2024
94ee810
Fix: use nativeTokenDecimals instead of origin decimals for call value
dwasse Oct 15, 2024
337782c
Merge branch 'master' into feat/relayer-arb-call
dwasse Oct 15, 2024
8759318
Update abigen
dwasse Oct 15, 2024
f44c7ae
Fix: use native token decimals for CallValue
dwasse Oct 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
247 changes: 143 additions & 104 deletions packages/contracts-rfq/contracts/FastBridgeV2.sol

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ interface IFastBridge {

/// @notice Decodes bridge request into a bridge transaction
/// @param request The bridge request to decode
function getBridgeTransaction(bytes memory request) external pure returns (BridgeTransaction memory);
function getBridgeTransaction(bytes memory request) external view returns (BridgeTransaction memory);

/// @notice Checks if the dispute period has passed so bridge deposit can be claimed
/// @param transactionId The transaction id associated with the encoded bridge transaction to check
Expand Down
7 changes: 5 additions & 2 deletions packages/contracts-rfq/contracts/interfaces/IFastBridgeV2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,17 @@ interface IFastBridgeV2 is IFastBridge {
/// for backwards compatibility.
/// Note: quoteRelayer and quoteExclusivitySeconds are either both zero (indicating no exclusivity)
/// or both non-zero (indicating exclusivity for the given period).
/// Note: callValue > 0 can NOT be used with destToken = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE (ETH_ADDRESS)
/// @param quoteRelayer Relayer that provided the quote for the transaction
/// @param quoteExclusivitySeconds Period of time the quote relayer is guaranteed exclusivity after user's deposit
/// @param quoteId Unique quote identifier used for tracking the quote
/// @param callValue ETH value to send to the recipient (if any)
/// @param callParams Parameters for the arbitrary call to the destination recipient (if any)
struct BridgeParamsV2 {
address quoteRelayer;
int256 quoteExclusivitySeconds;
bytes quoteId;
uint256 callValue;
bytes callParams;
}

Expand All @@ -54,7 +57,7 @@ interface IFastBridgeV2 is IFastBridge {
uint256 originAmount; // amount in on origin bridge less originFeeAmount
uint256 destAmount;
uint256 originFeeAmount;
bool sendChainGas;
uint256 callValue; // ETH value to send to the recipient (if any) - replaces V1's sendChainGas flag
uint256 deadline; // user specified deadline for destination relay
uint256 nonce;
address exclusivityRelayer;
Expand All @@ -67,7 +70,7 @@ interface IFastBridgeV2 is IFastBridge {
/// @notice Initiates bridge on origin chain to be relayed by off-chain relayer, with the ability
/// to provide temporary exclusivity fill rights for the quote relayer.
/// @param params The parameters required to bridge
/// @param paramsV2 The parameters for exclusivity fill rights (optional, could be left empty)
/// @param paramsV2 The parameters for exclusivity fill rights (optional, can be left empty)
function bridge(BridgeParams memory params, BridgeParamsV2 memory paramsV2) external payable;

/// @notice Relays destination side of bridge transaction by off-chain relayer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ interface IFastBridgeV2Errors {
error ChainIncorrect();
error ExclusivityParamsIncorrect();
error MsgValueIncorrect();
error NativeTokenCallValueNotSupported();
error SenderIncorrect();
error StatusIncorrect();
error ZeroAddress();
Expand Down
111 changes: 108 additions & 3 deletions packages/contracts-rfq/test/FastBridgeV2.Dst.ArbitraryCall.t.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {FastBridgeV2DstExclusivityTest, IFastBridgeV2} from "./FastBridgeV2.Dst.Exclusivity.t.sol";
import {IFastBridgeV2} from "../contracts/interfaces/IFastBridgeV2.sol";
import {FastBridgeV2DstExclusivityTest} from "./FastBridgeV2.Dst.Exclusivity.t.sol";
import {RecipientMock} from "./mocks/RecipientMock.sol";

import {Address} from "@openzeppelin/contracts/utils/Address.sol";
Expand Down Expand Up @@ -98,6 +99,28 @@ contract FastBridgeV2DstArbitraryCallTest is FastBridgeV2DstExclusivityTest {
relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: 0, bridgeTx: tokenTx});
}

function test_relay_token_withCallValue_excessiveReturnValueRecipient_revertWhenCallParamsPresent()
public
virtual
override
{
setTokenTestCallValue(CALL_VALUE);
setTokenTestRecipient(excessiveReturnValueRecipient);
vm.expectRevert(RecipientIncorrectReturnValue.selector);
relay({caller: relayerA, msgValue: CALL_VALUE, bridgeTx: tokenTx});
}

function test_relay_token_withRelayerAddressCallValue_excessiveReturnValueRecipient_revertWhenCallParamsPresent()
public
virtual
override
{
setTokenTestCallValue(CALL_VALUE);
setTokenTestRecipient(excessiveReturnValueRecipient);
vm.expectRevert(RecipientIncorrectReturnValue.selector);
relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: CALL_VALUE, bridgeTx: tokenTx});
}

function test_relay_eth_excessiveReturnValueRecipient_revertWhenCallParamsPresent() public virtual override {
setEthTestRecipient(excessiveReturnValueRecipient);
vm.expectRevert(RecipientIncorrectReturnValue.selector);
Expand Down Expand Up @@ -132,6 +155,28 @@ contract FastBridgeV2DstArbitraryCallTest is FastBridgeV2DstExclusivityTest {
relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: 0, bridgeTx: tokenTx});
}

function test_relay_token_withCallValue_incorrectReturnValueRecipient_revertWhenCallParamsPresent()
public
virtual
override
{
setTokenTestCallValue(CALL_VALUE);
setTokenTestRecipient(incorrectReturnValueRecipient);
vm.expectRevert(RecipientIncorrectReturnValue.selector);
relay({caller: relayerA, msgValue: CALL_VALUE, bridgeTx: tokenTx});
}

function test_relay_token_withRelayerAddressCallValue_incorrectReturnValueRecipient_revertWhenCallParamsPresent()
public
virtual
override
{
setTokenTestCallValue(CALL_VALUE);
setTokenTestRecipient(incorrectReturnValueRecipient);
vm.expectRevert(RecipientIncorrectReturnValue.selector);
relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: CALL_VALUE, bridgeTx: tokenTx});
}

function test_relay_eth_incorrectReturnValueRecipient_revertWhenCallParamsPresent() public virtual override {
setEthTestRecipient(incorrectReturnValueRecipient);
vm.expectRevert(RecipientIncorrectReturnValue.selector);
Expand All @@ -150,6 +195,8 @@ contract FastBridgeV2DstArbitraryCallTest is FastBridgeV2DstExclusivityTest {

// ══════════════════════════════════════════════ NO-OP RECIPIENT ══════════════════════════════════════════════════

// Note: in these tests NoOpRecipient doesn't implement hook function, so we expect a generic OZ library revert.

function test_relay_token_noOpRecipient_revertWhenCallParamsPresent() public virtual override {
setTokenTestRecipient(noOpRecipient);
vm.expectRevert(Address.FailedInnerCall.selector);
Expand All @@ -162,6 +209,24 @@ contract FastBridgeV2DstArbitraryCallTest is FastBridgeV2DstExclusivityTest {
relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: 0, bridgeTx: tokenTx});
}

function test_relay_token_withCallValue_noOpRecipient_revertWhenCallParamsPresent() public virtual override {
setTokenTestCallValue(CALL_VALUE);
setTokenTestRecipient(noOpRecipient);
vm.expectRevert(Address.FailedInnerCall.selector);
relay({caller: relayerA, msgValue: CALL_VALUE, bridgeTx: tokenTx});
}

function test_relay_token_withRelayerAddressCallValue_noOpRecipient_revertWhenCallParamsPresent()
public
virtual
override
{
setTokenTestCallValue(CALL_VALUE);
setTokenTestRecipient(noOpRecipient);
vm.expectRevert(Address.FailedInnerCall.selector);
relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: CALL_VALUE, bridgeTx: tokenTx});
}

function test_relay_eth_noOpRecipient_revertWhenCallParamsPresent() public virtual override {
setEthTestRecipient(noOpRecipient);
vm.expectRevert(Address.FailedInnerCall.selector);
Expand Down Expand Up @@ -192,6 +257,28 @@ contract FastBridgeV2DstArbitraryCallTest is FastBridgeV2DstExclusivityTest {
relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: 0, bridgeTx: tokenTx});
}

function test_relay_token_withCallValue_noReturnValueRecipient_revertWhenCallParamsPresent()
public
virtual
override
{
setTokenTestCallValue(CALL_VALUE);
setTokenTestRecipient(noReturnValueRecipient);
vm.expectRevert(RecipientNoReturnValue.selector);
relay({caller: relayerA, msgValue: CALL_VALUE, bridgeTx: tokenTx});
}

function test_relay_token_withRelayerAddressCallValue_noReturnValueRecipient_revertWhenCallParamsPresent()
public
virtual
override
{
setTokenTestCallValue(CALL_VALUE);
setTokenTestRecipient(noReturnValueRecipient);
vm.expectRevert(RecipientNoReturnValue.selector);
relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: CALL_VALUE, bridgeTx: tokenTx});
}

function test_relay_eth_noReturnValueRecipient_revertWhenCallParamsPresent() public virtual override {
setEthTestRecipient(noReturnValueRecipient);
vm.expectRevert(RecipientNoReturnValue.selector);
Expand Down Expand Up @@ -222,6 +309,20 @@ contract FastBridgeV2DstArbitraryCallTest is FastBridgeV2DstExclusivityTest {
relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: 0, bridgeTx: tokenTx});
}

function test_relay_token_withCallValue_revert_recipientReverts() public {
setTokenTestCallValue(CALL_VALUE);
mockRecipientRevert(tokenTx);
vm.expectRevert(REVERT_MSG);
relay({caller: relayerA, msgValue: CALL_VALUE, bridgeTx: tokenTx});
}

function test_relay_token_withRelayerAddressCallValue_revert_recipientReverts() public {
setTokenTestCallValue(CALL_VALUE);
mockRecipientRevert(tokenTx);
vm.expectRevert(REVERT_MSG);
relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: CALL_VALUE, bridgeTx: tokenTx});
}

function test_relay_eth_revert_recipientReverts() public {
mockRecipientRevert(ethTx);
vm.expectRevert(REVERT_MSG);
Expand All @@ -237,14 +338,18 @@ contract FastBridgeV2DstArbitraryCallTest is FastBridgeV2DstExclusivityTest {
function test_relay_eth_noCallParams_revert_recipientReverts() public {
setEthTestCallParams("");
vm.mockCallRevert({callee: userB, data: "", revertData: bytes(REVERT_MSG)});
vm.expectRevert("ETH transfer failed");
// Note: OZ library doesn't bubble the revert message for just sending ETH
// (as opposed to doing an external hook call). Therefore we expect a generic library revert.
vm.expectRevert(Address.FailedInnerCall.selector);
relay({caller: relayerB, msgValue: ethParams.destAmount, bridgeTx: ethTx});
}

function test_relay_eth_withRelayerAddress_noCallParams_revert_recipientReverts() public {
setEthTestCallParams("");
vm.mockCallRevert({callee: userB, data: "", revertData: bytes(REVERT_MSG)});
vm.expectRevert("ETH transfer failed");
// Note: OZ library doesn't bubble the revert message for just sending ETH
// (as opposed to doing an external hook call). Therefore we expect a generic library revert.
vm.expectRevert(Address.FailedInnerCall.selector);
relayWithAddress({caller: relayerA, relayer: relayerB, msgValue: ethParams.destAmount, bridgeTx: ethTx});
}
}
21 changes: 3 additions & 18 deletions packages/contracts-rfq/test/FastBridgeV2.Dst.Exclusivity.t.sol
Original file line number Diff line number Diff line change
@@ -1,30 +1,15 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {FastBridgeV2DstTest, IFastBridgeV2} from "./FastBridgeV2.Dst.t.sol";
import {FastBridgeV2DstTest} from "./FastBridgeV2.Dst.t.sol";

// solhint-disable func-name-mixedcase, ordering
contract FastBridgeV2DstExclusivityTest is FastBridgeV2DstTest {
uint256 public constant EXCLUSIVITY_PERIOD = 60 seconds;

function createFixturesV2() public virtual override {
tokenParamsV2 = IFastBridgeV2.BridgeParamsV2({
quoteRelayer: relayerA,
quoteExclusivitySeconds: int256(EXCLUSIVITY_PERIOD),
quoteId: "",
callParams: ""
});
ethParamsV2 = IFastBridgeV2.BridgeParamsV2({
quoteRelayer: relayerB,
quoteExclusivitySeconds: int256(EXCLUSIVITY_PERIOD),
quoteId: "",
callParams: ""
});

tokenTx.exclusivityRelayer = relayerA;
tokenTx.exclusivityEndTime = block.timestamp + EXCLUSIVITY_PERIOD;
ethTx.exclusivityRelayer = relayerB;
ethTx.exclusivityEndTime = block.timestamp + EXCLUSIVITY_PERIOD;
setTokenTestExclusivityParams(relayerA, EXCLUSIVITY_PERIOD);
setEthTestExclusivityParams(relayerB, EXCLUSIVITY_PERIOD);
}

// ═══════════════════════════════════════════════ RELAY: TOKEN ════════════════════════════════════════════════════
Expand Down
Loading
Loading