From 4f0f815c685aa8503b9cbc939fcf2dc842f95e46 Mon Sep 17 00:00:00 2001 From: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> Date: Wed, 24 Jul 2024 19:11:49 +0100 Subject: [PATCH 01/16] feat: copy FastBridgeRouter, scaffold new error --- contracts/rfq/FastBridgeRouterV2.sol | 107 +++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 contracts/rfq/FastBridgeRouterV2.sol diff --git a/contracts/rfq/FastBridgeRouterV2.sol b/contracts/rfq/FastBridgeRouterV2.sol new file mode 100644 index 000000000..7727bc218 --- /dev/null +++ b/contracts/rfq/FastBridgeRouterV2.sol @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import {DefaultRouter} from "../router/DefaultRouter.sol"; +import {UniversalTokenLib} from "../router/libs/UniversalToken.sol"; +import {ActionLib, LimitedToken} from "../router/libs/Structs.sol"; +import {IFastBridge} from "./interfaces/IFastBridge.sol"; +import {IFastBridgeRouter, SwapQuery} from "./interfaces/IFastBridgeRouter.sol"; +import {ISwapQuoter} from "./interfaces/ISwapQuoter.sol"; + +import {Ownable} from "@openzeppelin/contracts-4.5.0/access/Ownable.sol"; + +contract FastBridgeRouterV2 is DefaultRouter, Ownable, IFastBridgeRouter { + using UniversalTokenLib for address; + + error FastBridgeRouterV2__OriginSenderNotSpecified(); + + /// @notice Emitted when the swap quoter is set. + /// @param newSwapQuoter The new swap quoter. + event SwapQuoterSet(address newSwapQuoter); + + /// @notice Emitted when the new FastBridge contract is set. + /// @param newFastBridge The new FastBridge contract. + event FastBridgeSet(address newFastBridge); + + /// @inheritdoc IFastBridgeRouter + bytes1 public constant GAS_REBATE_FLAG = 0x2A; + + /// @inheritdoc IFastBridgeRouter + address public fastBridge; + /// @inheritdoc IFastBridgeRouter + address public swapQuoter; + + constructor(address owner_) { + transferOwnership(owner_); + } + + /// @inheritdoc IFastBridgeRouter + function setFastBridge(address fastBridge_) external onlyOwner { + fastBridge = fastBridge_; + emit FastBridgeSet(fastBridge_); + } + + /// @inheritdoc IFastBridgeRouter + function setSwapQuoter(address swapQuoter_) external onlyOwner { + swapQuoter = swapQuoter_; + emit SwapQuoterSet(swapQuoter_); + } + + /// @inheritdoc IFastBridgeRouter + function bridge( + address recipient, + uint256 chainId, + address token, + uint256 amount, + SwapQuery memory originQuery, + SwapQuery memory destQuery + ) external payable { + if (originQuery.hasAdapter()) { + // Perform a swap using the swap adapter, set this contract as recipient + (token, amount) = _doSwap(address(this), token, amount, originQuery); + } else { + // Otherwise, pull the token from the user to this contract + amount = _pullToken(address(this), token, amount); + } + IFastBridge.BridgeParams memory params = IFastBridge.BridgeParams({ + dstChainId: uint32(chainId), + sender: msg.sender, + to: recipient, + originToken: token, + destToken: destQuery.tokenOut, + originAmount: amount, + destAmount: destQuery.minAmountOut, + sendChainGas: _chainGasRequested(destQuery.rawParams), + deadline: destQuery.deadline + }); + token.universalApproveInfinity(fastBridge, amount); + uint256 msgValue = token == UniversalTokenLib.ETH_ADDRESS ? amount : 0; + IFastBridge(fastBridge).bridge{value: msgValue}(params); + } + + /// @inheritdoc IFastBridgeRouter + function getOriginAmountOut( + address tokenIn, + address[] memory rfqTokens, + uint256 amountIn + ) external view returns (SwapQuery[] memory originQueries) { + uint256 len = rfqTokens.length; + originQueries = new SwapQuery[](len); + for (uint256 i = 0; i < len; ++i) { + originQueries[i] = ISwapQuoter(swapQuoter).getAmountOut( + LimitedToken({actionMask: ActionLib.allActions(), token: tokenIn}), + rfqTokens[i], + amountIn + ); + // Adjust the Adapter address if it exists + if (originQueries[i].hasAdapter()) { + originQueries[i].routerAdapter = address(this); + } + } + } + + /// @dev Checks if the explicit instruction to send gas to the destination chain was provided. + function _chainGasRequested(bytes memory rawParams) internal pure returns (bool) { + return rawParams.length > 0 && rawParams[0] == GAS_REBATE_FLAG; + } +} From c09ea0d30255b7e44cb6483fe2530c44bbd7d90b Mon Sep 17 00:00:00 2001 From: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> Date: Wed, 24 Jul 2024 19:26:51 +0100 Subject: [PATCH 02/16] test: establish template for FBR v2 tests --- test/rfq/FBRTest.sol | 17 +++++++----- test/rfq/FastBridgeRouter.t.sol | 8 +++--- test/rfq/FastBridgeRouterV2.t.sol | 44 +++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 10 deletions(-) create mode 100644 test/rfq/FastBridgeRouterV2.t.sol diff --git a/test/rfq/FBRTest.sol b/test/rfq/FBRTest.sol index a2843c985..686fd66ac 100644 --- a/test/rfq/FBRTest.sol +++ b/test/rfq/FBRTest.sol @@ -10,10 +10,11 @@ import {MockDefaultPool} from "../mocks/MockDefaultPool.sol"; import {Test} from "forge-std/Test.sol"; abstract contract FBRTest is Test { - uint256 constant RFQ_DEADLINE = 12 hours; - address constant TOKEN_OUT = address(1337); - uint256 constant FIXED_FEE = 0.01 ether; - uint32 constant DST_CHAIN_ID = 420; + uint8 public constant REBATE_FLAG = 42; + uint256 public constant RFQ_DEADLINE = 12 hours; + address public constant TOKEN_OUT = address(1337); + uint256 public constant FIXED_FEE = 0.01 ether; + uint32 public constant DST_CHAIN_ID = 420; FastBridgeRouter public router; MockFastBridge public fastBridge; @@ -29,11 +30,15 @@ abstract contract FBRTest is Test { recipient = makeAddr("Recipient"); user = makeAddr("User"); fastBridge = new MockFastBridge(); - router = new FastBridgeRouter(owner); + router = FastBridgeRouter(deployRouter()); vm.prank(owner); router.setFastBridge(address(fastBridge)); } + function deployRouter() public virtual returns (address payable) { + return payable(new FastBridgeRouter(owner)); + } + function test_constructor() public { assertEq(address(router.fastBridge()), address(fastBridge)); assertEq(router.owner(), owner); @@ -75,7 +80,7 @@ abstract contract FBRTest is Test { tokenOut: TOKEN_OUT, minAmountOut: amount - FIXED_FEE, deadline: block.timestamp + RFQ_DEADLINE, - rawParams: abi.encodePacked(uint8(42)) + rawParams: abi.encodePacked(REBATE_FLAG) }); } diff --git a/test/rfq/FastBridgeRouter.t.sol b/test/rfq/FastBridgeRouter.t.sol index 1351fa3d2..727298454 100644 --- a/test/rfq/FastBridgeRouter.t.sol +++ b/test/rfq/FastBridgeRouter.t.sol @@ -92,7 +92,7 @@ abstract contract FastBridgeRouterTest is FBRTest { // ═══════════════════════════════════════════════ TESTS: BRIDGE ═══════════════════════════════════════════════════ - function test_bridge_noOriginSwap_noGasRebate() public { + function test_bridge_noOriginSwap_noGasRebate_senderEOA() public { uint256 amount = 1 ether; // No swap on origin chain SwapQuery memory originQuery = SwapQuery({ @@ -123,7 +123,7 @@ abstract contract FastBridgeRouterTest is FBRTest { }); } - function test_bridge_noOriginSwap_withGasRebate() public { + function test_bridge_noOriginSwap_withGasRebate_senderEOA() public { uint256 amount = 1 ether; // No swap on origin chain SwapQuery memory originQuery = SwapQuery({ @@ -154,7 +154,7 @@ abstract contract FastBridgeRouterTest is FBRTest { }); } - function test_bridge_withOriginSwap_noGasRebate() public { + function test_bridge_withOriginSwap_noGasRebate_senderEOA() public { uint256 amountBeforeSwap = 1 ether; // T0 -> T1 swap on origin chain uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); @@ -186,7 +186,7 @@ abstract contract FastBridgeRouterTest is FBRTest { }); } - function test_bridge_withOriginSwap_withGasRebate() public { + function test_bridge_withOriginSwap_withGasRebate_senderEOA() public { uint256 amountBeforeSwap = 1 ether; // T0 -> T1 swap on origin chain uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); diff --git a/test/rfq/FastBridgeRouterV2.t.sol b/test/rfq/FastBridgeRouterV2.t.sol new file mode 100644 index 000000000..772a54ad7 --- /dev/null +++ b/test/rfq/FastBridgeRouterV2.t.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import {FastBridgeRouterV2} from "../../contracts/rfq/FastBridgeRouterV2.sol"; + +import {FastBridgeRouterTest, SwapQuery} from "./FastBridgeRouter.t.sol"; + +abstract contract FastBridgeRouterV2Test is FastBridgeRouterTest { + function deployRouter() public virtual override returns (address payable) { + return payable(new FastBridgeRouterV2(owner)); + } + + function getDestQueryNoRebateWithOriginSender(uint256 amount, address originSender) + public + view + returns (SwapQuery memory destQuery) + { + destQuery = SwapQuery({ + routerAdapter: address(0), + tokenOut: TOKEN_OUT, + minAmountOut: amount - FIXED_FEE, + deadline: block.timestamp + RFQ_DEADLINE, + rawParams: abi.encodePacked(uint8(0), originSender) + }); + } + + function getDestQueryWithRebateWithOriginSender(uint256 amount, address originSender) + public + view + returns (SwapQuery memory destQuery) + { + destQuery = SwapQuery({ + routerAdapter: address(0), + tokenOut: TOKEN_OUT, + minAmountOut: amount - FIXED_FEE, + deadline: block.timestamp + RFQ_DEADLINE, + rawParams: abi.encodePacked(REBATE_FLAG, originSender) + }); + } + + function expectRevertOriginSenderNotSpecified() public { + vm.expectRevert(FastBridgeRouterV2.FastBridgeRouterV2__OriginSenderNotSpecified.selector); + } +} From 205aa4532eff55a4db6196373c0285dfe93b3103 Mon Sep 17 00:00:00 2001 From: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> Date: Wed, 24 Jul 2024 20:11:10 +0100 Subject: [PATCH 03/16] test: add token tests for FBR V2 --- test/mocks/MockSenderContract.sol | 10 + test/rfq/FastBridgeRouter.QuoterV1.t.sol | 2 +- test/rfq/FastBridgeRouter.t.sol | 19 +- test/rfq/FastBridgeRouterV2.QuoterV1.t.sol | 29 +++ test/rfq/FastBridgeRouterV2.QuoterV2.t.sol | 33 +++ test/rfq/FastBridgeRouterV2.t.sol | 281 ++++++++++++++++++++- 6 files changed, 363 insertions(+), 11 deletions(-) create mode 100644 test/mocks/MockSenderContract.sol create mode 100644 test/rfq/FastBridgeRouterV2.QuoterV1.t.sol create mode 100644 test/rfq/FastBridgeRouterV2.QuoterV2.t.sol diff --git a/test/mocks/MockSenderContract.sol b/test/mocks/MockSenderContract.sol new file mode 100644 index 000000000..d97efcdf0 --- /dev/null +++ b/test/mocks/MockSenderContract.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import {Address} from "@openzeppelin/contracts-4.5.0/utils/Address.sol"; + +contract MockSenderContract { + function doCall(address target, bytes memory data) external payable { + Address.functionCallWithValue(target, data, msg.value); + } +} diff --git a/test/rfq/FastBridgeRouter.QuoterV1.t.sol b/test/rfq/FastBridgeRouter.QuoterV1.t.sol index b8cdc8330..9f6c5ef87 100644 --- a/test/rfq/FastBridgeRouter.QuoterV1.t.sol +++ b/test/rfq/FastBridgeRouter.QuoterV1.t.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.17; import {FastBridgeRouterTest} from "./FastBridgeRouter.t.sol"; import {ISwapQuoterV1} from "../interfaces/ISwapQuoterV1.sol"; -contract FastBridgeRouterNativeTest is FastBridgeRouterTest { +contract FastBridgeRouterQuoterV1Test is FastBridgeRouterTest { function setUpSwapQuoter() internal override { // SwapQuoter V1 is solidity 0.6.12, so we use the cheatcode to deploy it // new SwapQuoter(synapseRouter, weth, owner) diff --git a/test/rfq/FastBridgeRouter.t.sol b/test/rfq/FastBridgeRouter.t.sol index 727298454..6927bb40b 100644 --- a/test/rfq/FastBridgeRouter.t.sol +++ b/test/rfq/FastBridgeRouter.t.sol @@ -16,7 +16,7 @@ abstract contract FastBridgeRouterTest is FBRTest { event FastBridgeSet(address newFastBridge); event SwapQuoterSet(address newSwapQuoter); - function setUp() public override { + function setUp() public virtual override { super.setUp(); token0 = new MockERC20("T0", 18); token1 = new MockERC20("T1", 18); @@ -28,16 +28,17 @@ abstract contract FastBridgeRouterTest is FBRTest { // Mint some tokens to the pool token0.mint(address(pool), 100 ether); token1.mint(address(pool), 120 ether); - // Mint some tokens to the user - token0.mint(user, 10 ether); - token1.mint(user, 10 ether); - // Approve the Router to spend the user's tokens - vm.prank(user); + prepareAccount(user); + setUpSwapQuoter(); + } + + function prepareAccount(address account) public { + token0.mint(account, 10 ether); + token1.mint(account, 10 ether); + vm.prank(account); token0.approve(address(router), 10 ether); - vm.prank(user); + vm.prank(account); token1.approve(address(router), 10 ether); - - setUpSwapQuoter(); } function setUpSwapQuoter() internal virtual; diff --git a/test/rfq/FastBridgeRouterV2.QuoterV1.t.sol b/test/rfq/FastBridgeRouterV2.QuoterV1.t.sol new file mode 100644 index 000000000..519645f61 --- /dev/null +++ b/test/rfq/FastBridgeRouterV2.QuoterV1.t.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import {FastBridgeRouterV2Test} from "./FastBridgeRouterV2.t.sol"; +import {ISwapQuoterV1} from "../interfaces/ISwapQuoterV1.sol"; + +contract FastBridgeRouterV2QuoterV1Test is FastBridgeRouterV2Test { + function setUpSwapQuoter() internal override { + // SwapQuoter V1 is solidity 0.6.12, so we use the cheatcode to deploy it + // new SwapQuoter(synapseRouter, weth, owner) + bytes memory constructorArgs = abi.encode( + // Existing SwapQuoter will be always pointing towards another router + address(1), // synapseRouter + // We don't care about WETH in this test + address(2), // weth + address(this) // owner + ); + address swapQuoter = deployCode("SwapQuoter.sol", constructorArgs); + addPool(swapQuoter); + vm.prank(owner); + router.setSwapQuoter(swapQuoter); + } + + function addPool(address swapQuoter) internal { + address[] memory pools = new address[](1); + pools[0] = address(pool); + ISwapQuoterV1(swapQuoter).addPools(pools); + } +} diff --git a/test/rfq/FastBridgeRouterV2.QuoterV2.t.sol b/test/rfq/FastBridgeRouterV2.QuoterV2.t.sol new file mode 100644 index 000000000..57f61cf07 --- /dev/null +++ b/test/rfq/FastBridgeRouterV2.QuoterV2.t.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import {FastBridgeRouterV2Test} from "./FastBridgeRouterV2.t.sol"; +import {DefaultPoolCalc} from "../../contracts/router/quoter/DefaultPoolCalc.sol"; +import {SwapQuoterV2} from "../../contracts/router/quoter/SwapQuoterV2.sol"; + +contract FastBridgeRouterV2QuoterV2Test is FastBridgeRouterV2Test { + function setUpSwapQuoter() internal override { + DefaultPoolCalc poolCalc = new DefaultPoolCalc(); + SwapQuoterV2 quoter = new SwapQuoterV2({ + // Existing SwapQuoter will be always pointing towards another router + synapseRouter_: address(1), + defaultPoolCalc_: address(poolCalc), + // We don't care about WETH in this test + weth_: address(2), + owner_: address(this) + }); + addPool(quoter); + vm.prank(owner); + router.setSwapQuoter(address(quoter)); + } + + function addPool(SwapQuoterV2 quoter) internal { + SwapQuoterV2.BridgePool[] memory pools = new SwapQuoterV2.BridgePool[](1); + pools[0] = SwapQuoterV2.BridgePool({ + bridgeToken: address(0), + poolType: SwapQuoterV2.PoolType.Default, + pool: address(pool) + }); + quoter.addPools(pools); + } +} diff --git a/test/rfq/FastBridgeRouterV2.t.sol b/test/rfq/FastBridgeRouterV2.t.sol index 772a54ad7..cbc3f670a 100644 --- a/test/rfq/FastBridgeRouterV2.t.sol +++ b/test/rfq/FastBridgeRouterV2.t.sol @@ -3,9 +3,22 @@ pragma solidity 0.8.17; import {FastBridgeRouterV2} from "../../contracts/rfq/FastBridgeRouterV2.sol"; -import {FastBridgeRouterTest, SwapQuery} from "./FastBridgeRouter.t.sol"; +import {MockSenderContract} from "../mocks/MockSenderContract.sol"; +import {FastBridgeRouterTest, IFastBridge, SwapQuery} from "./FastBridgeRouter.t.sol"; + +// solhint-disable not-rely-on-time +// solhint-disable ordering +// solhint-disable func-name-mixedcase abstract contract FastBridgeRouterV2Test is FastBridgeRouterTest { + address public externalContract; + + function setUp() public virtual override { + super.setUp(); + externalContract = address(new MockSenderContract()); + prepareAccount(externalContract); + } + function deployRouter() public virtual override returns (address payable) { return payable(new FastBridgeRouterV2(owner)); } @@ -41,4 +54,270 @@ abstract contract FastBridgeRouterV2Test is FastBridgeRouterTest { function expectRevertOriginSenderNotSpecified() public { vm.expectRevert(FastBridgeRouterV2.FastBridgeRouterV2__OriginSenderNotSpecified.selector); } + + // ════════════════════════════════ TESTS: BRIDGE (EOA, ORIGIN SENDER PROVIDED) ════════════════════════════════════ + + function check_bridge_noOriginSwap_noGasRebate(address caller, address originSender) public { + uint256 amount = 1 ether; + // No swap on origin chain + SwapQuery memory originQuery = SwapQuery({ + routerAdapter: address(0), + tokenOut: address(token0), + minAmountOut: amount, + deadline: block.timestamp, + rawParams: "" + }); + IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ + originToken: address(token0), + originAmount: amount, + sendChainGas: false + }); + vm.expectCall({ + callee: address(fastBridge), + msgValue: 0, + data: abi.encodeCall(IFastBridge.bridge, (expectedParams)) + }); + vm.prank(caller); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(token0), + amount: amount, + originQuery: originQuery, + destQuery: getDestQueryNoRebateWithOriginSender(amount, originSender) + }); + } + + function check_bridge_noOriginSwap_withGasRebate(address caller, address originSender) public { + uint256 amount = 1 ether; + // No swap on origin chain + SwapQuery memory originQuery = SwapQuery({ + routerAdapter: address(0), + tokenOut: address(token0), + minAmountOut: amount, + deadline: block.timestamp, + rawParams: "" + }); + IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ + originToken: address(token0), + originAmount: amount, + sendChainGas: true + }); + vm.expectCall({ + callee: address(fastBridge), + msgValue: 0, + data: abi.encodeCall(IFastBridge.bridge, (expectedParams)) + }); + vm.prank(caller); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(token0), + amount: amount, + originQuery: originQuery, + destQuery: getDestQueryWithRebateWithOriginSender(amount, originSender) + }); + } + + function check_bridge_withOriginSwap_noGasRebate(address caller, address originSender) public { + uint256 amountBeforeSwap = 1 ether; + // T0 -> T1 swap on origin chain + uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); + SwapQuery memory originQuery = SwapQuery({ + routerAdapter: address(router), + tokenOut: address(token1), + minAmountOut: amount, + deadline: block.timestamp, + rawParams: getOriginSwapParams(0, 1) + }); + IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ + originToken: address(token1), + originAmount: amount, + sendChainGas: false + }); + vm.expectCall({ + callee: address(fastBridge), + msgValue: 0, + data: abi.encodeCall(IFastBridge.bridge, (expectedParams)) + }); + vm.prank(caller); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(token0), + amount: amountBeforeSwap, + originQuery: originQuery, + destQuery: getDestQueryNoRebateWithOriginSender(amount, originSender) + }); + } + + function check_bridge_withOriginSwap_withGasRebate(address caller, address originSender) public { + uint256 amountBeforeSwap = 1 ether; + // T0 -> T1 swap on origin chain + uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); + SwapQuery memory originQuery = SwapQuery({ + routerAdapter: address(router), + tokenOut: address(token1), + minAmountOut: amount, + deadline: block.timestamp, + rawParams: getOriginSwapParams(0, 1) + }); + IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ + originToken: address(token1), + originAmount: amount, + sendChainGas: true + }); + vm.expectCall({ + callee: address(fastBridge), + msgValue: 0, + data: abi.encodeCall(IFastBridge.bridge, (expectedParams)) + }); + vm.prank(caller); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(token0), + amount: amountBeforeSwap, + originQuery: originQuery, + destQuery: getDestQueryWithRebateWithOriginSender(amount, originSender) + }); + } + + function test_bridge_noOriginSwap_noGasRebate_senderEOA_withOriginSenderSet() public { + check_bridge_noOriginSwap_noGasRebate({caller: user, originSender: user}); + } + + function test_bridge_noOriginSwap_withGasRebate_senderEOA_withOriginSenderSet() public { + check_bridge_noOriginSwap_withGasRebate({caller: user, originSender: user}); + } + + function test_bridge_withOriginSwap_noGasRebate_senderEOA_withOriginSenderSet() public { + check_bridge_withOriginSwap_noGasRebate({caller: user, originSender: user}); + } + + function test_bridge_withOriginSwap_withGasRebate_senderEOA_withOriginSenderSet() public { + check_bridge_withOriginSwap_withGasRebate({caller: user, originSender: user}); + } + + // Note: Calls from EOA with origin sender set to zero address should succeed + function test_bridge_noOriginSwap_noGasRebate_senderEOA_withOriginSenderZero() public { + check_bridge_noOriginSwap_noGasRebate({caller: user, originSender: address(0)}); + } + + function test_bridge_noOriginSwap_withGasRebate_senderEOA_withOriginSenderZero() public { + check_bridge_noOriginSwap_withGasRebate({caller: user, originSender: address(0)}); + } + + function test_bridge_withOriginSwap_noGasRebate_senderEOA_withOriginSenderZero() public { + check_bridge_withOriginSwap_noGasRebate({caller: user, originSender: address(0)}); + } + + function test_bridge_withOriginSwap_withGasRebate_senderEOA_withOriginSenderZero() public { + check_bridge_withOriginSwap_withGasRebate({caller: user, originSender: address(0)}); + } + + // ═════════════════════════════ TESTS: BRIDGE (CONTRACT, ORIGIN SENDER PROVIDED) ══════════════════════════════════ + + function test_bridge_noOriginSwap_noGasRebate_senderContract_withOriginSenderSet() public { + check_bridge_noOriginSwap_noGasRebate({caller: externalContract, originSender: user}); + } + + function test_bridge_noOriginSwap_withGasRebate_senderContract_withOriginSenderSet() public { + check_bridge_noOriginSwap_withGasRebate({caller: externalContract, originSender: user}); + } + + function test_bridge_withOriginSwap_noGasRebate_senderContract_withOriginSenderSet() public { + check_bridge_withOriginSwap_noGasRebate({caller: externalContract, originSender: user}); + } + + function test_bridge_withOriginSwap_withGasRebate_senderContract_withOriginSenderSet() public { + check_bridge_withOriginSwap_withGasRebate({caller: externalContract, originSender: user}); + } + + // ═══════════════════════════ TESTS: BRIDGE (CONTRACT, ORIGIN SENDER NOT PROVIDED) ════════════════════════════════ + + function check_bridge_noOriginSwap_senderContract_revert(SwapQuery memory destQuery) public { + uint256 amount = 1 ether; + // No swap on origin chain + SwapQuery memory originQuery = SwapQuery({ + routerAdapter: address(0), + tokenOut: address(token0), + minAmountOut: amount, + deadline: block.timestamp, + rawParams: "" + }); + expectRevertOriginSenderNotSpecified(); + vm.prank(externalContract); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(token0), + amount: amount, + originQuery: originQuery, + destQuery: destQuery + }); + } + + function check_bridge_withOriginSwap_senderContract_revert(SwapQuery memory destQuery) public { + uint256 amountBeforeSwap = 1 ether; + // T0 -> T1 swap on origin chain + uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); + SwapQuery memory originQuery = SwapQuery({ + routerAdapter: address(router), + tokenOut: address(token1), + minAmountOut: amount, + deadline: block.timestamp, + rawParams: getOriginSwapParams(0, 1) + }); + expectRevertOriginSenderNotSpecified(); + vm.prank(externalContract); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(token0), + amount: amountBeforeSwap, + originQuery: originQuery, + destQuery: destQuery + }); + } + + function test_bridge_noOriginSwap_noGasRebate_senderContract_revert() public { + check_bridge_noOriginSwap_senderContract_revert({destQuery: getDestQueryNoRebate(1 ether)}); + } + + function test_bridge_noOriginSwap_withGasRebate_senderContract_revert() public { + check_bridge_noOriginSwap_senderContract_revert({destQuery: getDestQueryWithRebate(1 ether)}); + } + + function test_bridge_withOriginSwap_noGasRebate_senderContract_revert() public { + check_bridge_withOriginSwap_senderContract_revert({destQuery: getDestQueryNoRebate(1 ether)}); + } + + function test_bridge_withOriginSwap_withGasRebate_senderContract_revert() public { + check_bridge_withOriginSwap_senderContract_revert({destQuery: getDestQueryWithRebate(1 ether)}); + } + + function test_bridge_noOriginSwap_noGasRebate_senderContract_withOriginSenderZero_revert() public { + check_bridge_noOriginSwap_senderContract_revert({ + destQuery: getDestQueryNoRebateWithOriginSender(1 ether, address(0)) + }); + } + + function test_bridge_noOriginSwap_withGasRebate_senderContract_withOriginSenderZero_revert() public { + check_bridge_noOriginSwap_senderContract_revert({ + destQuery: getDestQueryWithRebateWithOriginSender(1 ether, address(0)) + }); + } + + function test_bridge_withOriginSwap_noGasRebate_senderContract_withOriginSenderZero_revert() public { + check_bridge_withOriginSwap_senderContract_revert({ + destQuery: getDestQueryNoRebateWithOriginSender(1 ether, address(0)) + }); + } + + function test_bridge_withOriginSwap_withGasRebate_senderContract_withOriginSenderZero_revert() public { + check_bridge_withOriginSwap_senderContract_revert({ + destQuery: getDestQueryWithRebateWithOriginSender(1 ether, address(0)) + }); + } } From bece4743d5d83fd2630b728578b0f9dd7dd3f80b Mon Sep 17 00:00:00 2001 From: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> Date: Wed, 24 Jul 2024 20:35:06 +0100 Subject: [PATCH 04/16] feat: decode originSender in FastBridgeRouterV2 --- contracts/rfq/FastBridgeRouterV2.sol | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/contracts/rfq/FastBridgeRouterV2.sol b/contracts/rfq/FastBridgeRouterV2.sol index 7727bc218..dcbef5375 100644 --- a/contracts/rfq/FastBridgeRouterV2.sol +++ b/contracts/rfq/FastBridgeRouterV2.sol @@ -56,6 +56,10 @@ contract FastBridgeRouterV2 is DefaultRouter, Ownable, IFastBridgeRouter { SwapQuery memory originQuery, SwapQuery memory destQuery ) external payable { + address originSender = _getOriginSender(destQuery.rawParams); + if (originSender == address(0)) { + revert FastBridgeRouterV2__OriginSenderNotSpecified(); + } if (originQuery.hasAdapter()) { // Perform a swap using the swap adapter, set this contract as recipient (token, amount) = _doSwap(address(this), token, amount, originQuery); @@ -65,7 +69,7 @@ contract FastBridgeRouterV2 is DefaultRouter, Ownable, IFastBridgeRouter { } IFastBridge.BridgeParams memory params = IFastBridge.BridgeParams({ dstChainId: uint32(chainId), - sender: msg.sender, + sender: originSender, to: recipient, originToken: token, destToken: destQuery.tokenOut, @@ -100,6 +104,28 @@ contract FastBridgeRouterV2 is DefaultRouter, Ownable, IFastBridgeRouter { } } + /// @dev Retrieves the origin sender from the raw params. + /// Note: falls back to msg.sender if origin sender is not specified in the raw params, but + /// msg.sender is an EOA. + function _getOriginSender(bytes memory rawParams) internal view returns (address originSender) { + // Origin sender (if present) is encoded as 20 bytes following the rebate flag + if (rawParams.length >= 21) { + // The easiest way to read from memory is to use assembly + // solhint-disable-next-line no-inline-assembly + assembly { + // We need to skip the rawParams.length (32 bytes) and the rebate flag (1 byte) + originSender := mload(add(rawParams, 33)) + // Now We have the address in the highest 160 bits. Shift right by 96 to get it in the lowest 160 bits + originSender := shr(96, originSender) + } + } + if (originSender == address(0)) { + // We fallback to msg.sender if that is EOA. This establishes backwards compatibility + // for cases where we can safely assume that the origin sender is the same as msg.sender. + return msg.sender.code.length == 0 ? msg.sender : address(0); + } + } + /// @dev Checks if the explicit instruction to send gas to the destination chain was provided. function _chainGasRequested(bytes memory rawParams) internal pure returns (bool) { return rawParams.length > 0 && rawParams[0] == GAS_REBATE_FLAG; From 7c7830310bd6cb1c60529975459f0133e5d4bbfe Mon Sep 17 00:00:00 2001 From: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> Date: Thu, 25 Jul 2024 10:53:47 +0100 Subject: [PATCH 05/16] refactor: deduplicate origin query in tests --- test/rfq/FastBridgeRouter.Native.t.sol | 247 +++++++------------------ test/rfq/FastBridgeRouter.t.sol | 66 +++---- test/rfq/FastBridgeRouterV2.t.sol | 54 +----- 3 files changed, 101 insertions(+), 266 deletions(-) diff --git a/test/rfq/FastBridgeRouter.Native.t.sol b/test/rfq/FastBridgeRouter.Native.t.sol index f0f71b7b6..05463d3db 100644 --- a/test/rfq/FastBridgeRouter.Native.t.sol +++ b/test/rfq/FastBridgeRouter.Native.t.sol @@ -16,7 +16,7 @@ abstract contract FastBridgeRouterNativeTest is FBRTest { MockERC20 public token; MockWETH public weth; - function setUp() public override { + function setUp() public virtual override { super.setUp(); token = new MockERC20("TKN", 18); weth = new MockWETH(); @@ -28,34 +28,70 @@ abstract contract FastBridgeRouterNativeTest is FBRTest { // Mint some tokens to the pool token.mint(address(pool), 100 ether); weth.mint(address(pool), 120 ether); - // Mint some tokens to the user + prepareAccount(user); + setUpSwapQuoter(); + } + + function prepareAccount(address account) public { token.mint(user, 10 ether); weth.mint(user, 10 ether); deal(user, 10 ether); - // Approve the Router to spend the user's tokens vm.prank(user); token.approve(address(router), 10 ether); vm.prank(user); weth.approve(address(router), 10 ether); - - setUpSwapQuoter(); } function setUpSwapQuoter() internal virtual; - // ═══════════════════════════════════════════ TESTS: START FROM ETH ═══════════════════════════════════════════════ - - // Start from ETH, use ETH for RFQ - function test_bridge_eth_noOriginSwap_noGasRebate() public { - uint256 amount = 1 ether; - // No swap on origin chain - SwapQuery memory originQuery = SwapQuery({ + function getOriginQueryNoSwap(address tokenOut, uint256 amount) + internal + view + returns (SwapQuery memory originQuery) + { + originQuery = SwapQuery({ routerAdapter: address(0), - tokenOut: ETH, + tokenOut: tokenOut, minAmountOut: amount, deadline: block.timestamp, rawParams: "" }); + } + + function getOriginQueryWithSwap(address tokenOut, uint256 amount) + internal + view + returns (SwapQuery memory originQuery) + { + originQuery = SwapQuery({ + routerAdapter: address(router), + tokenOut: tokenOut, + minAmountOut: amount, + deadline: block.timestamp, + rawParams: tokenOut == address(token) ? getOriginSwapParams(1, 0) : getOriginSwapParams(0, 1) + }); + } + + function getOriginQueryWithHandleETH(address tokenOut, uint256 amount) + internal + view + returns (SwapQuery memory originQuery) + { + originQuery = SwapQuery({ + routerAdapter: address(router), + tokenOut: tokenOut, + minAmountOut: amount, + deadline: block.timestamp, + rawParams: getOriginHandleETHParams() + }); + } + + // ═══════════════════════════════════════════ TESTS: START FROM ETH ═══════════════════════════════════════════════ + + // Start from ETH, use ETH for RFQ + function test_bridge_eth_noOriginSwap_noGasRebate() public { + uint256 amount = 1 ether; + SwapQuery memory originQuery = getOriginQueryNoSwap(ETH, amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: ETH, originAmount: amount, @@ -80,14 +116,7 @@ abstract contract FastBridgeRouterNativeTest is FBRTest { // Start from ETH, use ETH for RFQ (with gas rebate) function test_bridge_eth_noOriginSwap_withGasRebate() public { uint256 amount = 1 ether; - // No swap on origin chain - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(0), - tokenOut: ETH, - minAmountOut: amount, - deadline: block.timestamp, - rawParams: "" - }); + SwapQuery memory originQuery = getOriginQueryNoSwap(ETH, amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: ETH, originAmount: amount, @@ -112,14 +141,7 @@ abstract contract FastBridgeRouterNativeTest is FBRTest { // Start from ETH, use WETH for RFQ function test_bridge_eth_withOriginWrap_noGasRebate() public { uint256 amount = 1 ether; - // Wrap ETH on origin chain - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(router), - tokenOut: address(weth), - minAmountOut: amount, - deadline: block.timestamp, - rawParams: getOriginHandleETHParams() - }); + SwapQuery memory originQuery = getOriginQueryWithHandleETH(address(weth), amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: address(weth), originAmount: amount, @@ -144,14 +166,7 @@ abstract contract FastBridgeRouterNativeTest is FBRTest { // Start from ETH, use WETH for RFQ (with gas rebate) function test_bridge_eth_withOriginWrap_withGasRebate() public { uint256 amount = 1 ether; - // Wrap ETH on origin chain - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(router), - tokenOut: address(weth), - minAmountOut: amount, - deadline: block.timestamp, - rawParams: getOriginHandleETHParams() - }); + SwapQuery memory originQuery = getOriginQueryWithHandleETH(address(weth), amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: address(weth), originAmount: amount, @@ -177,14 +192,7 @@ abstract contract FastBridgeRouterNativeTest is FBRTest { function test_bridge_eth_withOriginSwap_noGasRebate() public { uint256 amountBeforeSwap = 1 ether; uint256 amount = pool.calculateSwap(1, 0, amountBeforeSwap); - // Swap ETH on origin chain - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(router), - tokenOut: address(token), - minAmountOut: amount, - deadline: block.timestamp, - rawParams: getOriginSwapParams(1, 0) - }); + SwapQuery memory originQuery = getOriginQueryWithSwap(address(token), amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: address(token), originAmount: amount, @@ -210,14 +218,7 @@ abstract contract FastBridgeRouterNativeTest is FBRTest { function test_bridge_eth_withOriginSwap_withGasRebate() public { uint256 amountBeforeSwap = 1 ether; uint256 amount = pool.calculateSwap(1, 0, amountBeforeSwap); - // Swap ETH on origin chain - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(router), - tokenOut: address(token), - minAmountOut: amount, - deadline: block.timestamp, - rawParams: getOriginSwapParams(1, 0) - }); + SwapQuery memory originQuery = getOriginQueryWithSwap(address(token), amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: address(token), originAmount: amount, @@ -244,14 +245,7 @@ abstract contract FastBridgeRouterNativeTest is FBRTest { // Start from WETH, use WETH for RFQ function test_bridge_weth_noOriginSwap_noGasRebate() public { uint256 amount = 1 ether; - // No swap on origin chain - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(0), - tokenOut: address(weth), - minAmountOut: amount, - deadline: block.timestamp, - rawParams: "" - }); + SwapQuery memory originQuery = getOriginQueryNoSwap(address(weth), amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: address(weth), originAmount: amount, @@ -276,14 +270,7 @@ abstract contract FastBridgeRouterNativeTest is FBRTest { // Start from WETH, use WETH for RFQ (with gas rebate) function test_bridge_weth_noOriginSwap_withGasRebate() public { uint256 amount = 1 ether; - // No swap on origin chain - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(0), - tokenOut: address(weth), - minAmountOut: amount, - deadline: block.timestamp, - rawParams: "" - }); + SwapQuery memory originQuery = getOriginQueryNoSwap(address(weth), amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: address(weth), originAmount: amount, @@ -308,14 +295,7 @@ abstract contract FastBridgeRouterNativeTest is FBRTest { // Start from WETH, use ETH for RFQ function test_bridge_weth_withOriginUnwrap_noGasRebate() public { uint256 amount = 1 ether; - // Unwrap WETH on origin chain - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(router), - tokenOut: ETH, - minAmountOut: amount, - deadline: block.timestamp, - rawParams: getOriginHandleETHParams() - }); + SwapQuery memory originQuery = getOriginQueryWithHandleETH(ETH, amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: ETH, originAmount: amount, @@ -340,14 +320,7 @@ abstract contract FastBridgeRouterNativeTest is FBRTest { // Start from WETH, use ETH for RFQ (with gas rebate) function test_bridge_weth_withOriginUnwrap_withGasRebate() public { uint256 amount = 1 ether; - // Unwrap WETH on origin chain - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(router), - tokenOut: ETH, - minAmountOut: amount, - deadline: block.timestamp, - rawParams: getOriginHandleETHParams() - }); + SwapQuery memory originQuery = getOriginQueryWithHandleETH(ETH, amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: ETH, originAmount: amount, @@ -373,14 +346,7 @@ abstract contract FastBridgeRouterNativeTest is FBRTest { function test_bridge_weth_withOriginSwap_noGasRebate() public { uint256 amountBeforeSwap = 1 ether; uint256 amount = pool.calculateSwap(1, 0, amountBeforeSwap); - // Swap WETH on origin chain - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(router), - tokenOut: address(token), - minAmountOut: amount, - deadline: block.timestamp, - rawParams: getOriginSwapParams(1, 0) - }); + SwapQuery memory originQuery = getOriginQueryWithSwap(address(token), amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: address(token), originAmount: amount, @@ -406,14 +372,7 @@ abstract contract FastBridgeRouterNativeTest is FBRTest { function test_bridge_weth_withOriginSwap_withGasRebate() public { uint256 amountBeforeSwap = 1 ether; uint256 amount = pool.calculateSwap(1, 0, amountBeforeSwap); - // Swap WETH on origin chain - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(router), - tokenOut: address(token), - minAmountOut: amount, - deadline: block.timestamp, - rawParams: getOriginSwapParams(1, 0) - }); + SwapQuery memory originQuery = getOriginQueryWithSwap(address(token), amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: address(token), originAmount: amount, @@ -440,14 +399,7 @@ abstract contract FastBridgeRouterNativeTest is FBRTest { // Start from paired token, use paired token for RFQ function test_bridge_token_noOriginSwap_noGasRebate() public { uint256 amount = 1 ether; - // No swap on origin chain - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(0), - tokenOut: address(token), - minAmountOut: amount, - deadline: block.timestamp, - rawParams: "" - }); + SwapQuery memory originQuery = getOriginQueryNoSwap(address(token), amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: address(token), originAmount: amount, @@ -472,14 +424,7 @@ abstract contract FastBridgeRouterNativeTest is FBRTest { // Start from paired token, use paired token for RFQ (with gas rebate) function test_bridge_token_noOriginSwap_withGasRebate() public { uint256 amount = 1 ether; - // No swap on origin chain - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(0), - tokenOut: address(token), - minAmountOut: amount, - deadline: block.timestamp, - rawParams: "" - }); + SwapQuery memory originQuery = getOriginQueryNoSwap(address(token), amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: address(token), originAmount: amount, @@ -505,14 +450,7 @@ abstract contract FastBridgeRouterNativeTest is FBRTest { function test_bridge_token_withOriginSwap_noGasRebate() public { uint256 amountBeforeSwap = 1 ether; uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); - // Swap token on origin chain - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(router), - tokenOut: address(weth), - minAmountOut: amount, - deadline: block.timestamp, - rawParams: getOriginSwapParams(0, 1) - }); + SwapQuery memory originQuery = getOriginQueryWithSwap(address(weth), amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: address(weth), originAmount: amount, @@ -538,14 +476,7 @@ abstract contract FastBridgeRouterNativeTest is FBRTest { function test_bridge_token_withOriginSwap_withGasRebate() public { uint256 amountBeforeSwap = 1 ether; uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); - // Swap token on origin chain - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(router), - tokenOut: address(weth), - minAmountOut: amount, - deadline: block.timestamp, - rawParams: getOriginSwapParams(0, 1) - }); + SwapQuery memory originQuery = getOriginQueryWithSwap(address(weth), amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: address(weth), originAmount: amount, @@ -571,14 +502,7 @@ abstract contract FastBridgeRouterNativeTest is FBRTest { function test_bridge_token_withOriginSwapUnwrap_noGasRebate() public { uint256 amountBeforeSwap = 1 ether; uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); - // Swap & unwrap token on origin chain - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(router), - tokenOut: ETH, - minAmountOut: amount, - deadline: block.timestamp, - rawParams: getOriginSwapParams(0, 1) - }); + SwapQuery memory originQuery = getOriginQueryWithSwap(ETH, amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: ETH, originAmount: amount, @@ -604,14 +528,7 @@ abstract contract FastBridgeRouterNativeTest is FBRTest { function test_bridge_token_withOriginSwapUnwrap_withGasRebate() public { uint256 amountBeforeSwap = 1 ether; uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); - // Swap & unwrap token on origin chain - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(router), - tokenOut: ETH, - minAmountOut: amount, - deadline: block.timestamp, - rawParams: getOriginSwapParams(0, 1) - }); + SwapQuery memory originQuery = getOriginQueryWithSwap(ETH, amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: ETH, originAmount: amount, @@ -637,13 +554,7 @@ abstract contract FastBridgeRouterNativeTest is FBRTest { function test_bridge_revert_msgValueWithERC20() public { uint256 amount = 1 ether; - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(0), - tokenOut: address(token), - minAmountOut: amount, - deadline: block.timestamp, - rawParams: "" - }); + SwapQuery memory originQuery = getOriginQueryNoSwap(address(token), amount); vm.expectRevert(TokenNotETH.selector); vm.prank(user); router.bridge{value: 1 ether}({ @@ -658,13 +569,7 @@ abstract contract FastBridgeRouterNativeTest is FBRTest { function test_bridge_revert_msgValueZeroWithETH() public { uint256 amount = 1 ether; - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(0), - tokenOut: ETH, - minAmountOut: amount, - deadline: block.timestamp, - rawParams: "" - }); + SwapQuery memory originQuery = getOriginQueryNoSwap(ETH, amount); vm.expectRevert(TokenNotContract.selector); vm.prank(user); router.bridge({ @@ -679,13 +584,7 @@ abstract contract FastBridgeRouterNativeTest is FBRTest { function test_bridge_revert_msgValueLowerWithETH() public { uint256 amount = 1 ether; - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(0), - tokenOut: ETH, - minAmountOut: amount, - deadline: block.timestamp, - rawParams: "" - }); + SwapQuery memory originQuery = getOriginQueryNoSwap(ETH, amount); vm.expectRevert(MsgValueIncorrect.selector); vm.prank(user); router.bridge{value: amount - 1}({ @@ -700,13 +599,7 @@ abstract contract FastBridgeRouterNativeTest is FBRTest { function test_bridge_revert_msgValueHigherWithETH() public { uint256 amount = 1 ether; - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(0), - tokenOut: ETH, - minAmountOut: amount, - deadline: block.timestamp, - rawParams: "" - }); + SwapQuery memory originQuery = getOriginQueryNoSwap(ETH, amount); vm.expectRevert(MsgValueIncorrect.selector); vm.prank(user); router.bridge{value: amount + 1}({ diff --git a/test/rfq/FastBridgeRouter.t.sol b/test/rfq/FastBridgeRouter.t.sol index 6927bb40b..843e52639 100644 --- a/test/rfq/FastBridgeRouter.t.sol +++ b/test/rfq/FastBridgeRouter.t.sol @@ -43,6 +43,26 @@ abstract contract FastBridgeRouterTest is FBRTest { function setUpSwapQuoter() internal virtual; + function getOriginQueryNoSwap(uint256 amount) public view returns (SwapQuery memory originQuery) { + originQuery = SwapQuery({ + routerAdapter: address(0), + tokenOut: address(token0), + minAmountOut: amount, + deadline: block.timestamp, + rawParams: "" + }); + } + + function getOriginQueryWithSwap(uint256 amount) public view returns (SwapQuery memory originQuery) { + originQuery = SwapQuery({ + routerAdapter: address(router), + tokenOut: address(token1), + minAmountOut: amount, + deadline: block.timestamp, + rawParams: getOriginSwapParams(0, 1) + }); + } + // ══════════════════════════════════════════ TESTS: SET FAST BRIDGE ═══════════════════════════════════════════════ function test_setFastBridge_setsFastBridge() public { @@ -95,14 +115,7 @@ abstract contract FastBridgeRouterTest is FBRTest { function test_bridge_noOriginSwap_noGasRebate_senderEOA() public { uint256 amount = 1 ether; - // No swap on origin chain - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(0), - tokenOut: address(token0), - minAmountOut: amount, - deadline: block.timestamp, - rawParams: "" - }); + SwapQuery memory originQuery = getOriginQueryNoSwap(amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: address(token0), originAmount: amount, @@ -126,14 +139,7 @@ abstract contract FastBridgeRouterTest is FBRTest { function test_bridge_noOriginSwap_withGasRebate_senderEOA() public { uint256 amount = 1 ether; - // No swap on origin chain - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(0), - tokenOut: address(token0), - minAmountOut: amount, - deadline: block.timestamp, - rawParams: "" - }); + SwapQuery memory originQuery = getOriginQueryNoSwap(amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: address(token0), originAmount: amount, @@ -157,15 +163,8 @@ abstract contract FastBridgeRouterTest is FBRTest { function test_bridge_withOriginSwap_noGasRebate_senderEOA() public { uint256 amountBeforeSwap = 1 ether; - // T0 -> T1 swap on origin chain uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(router), - tokenOut: address(token1), - minAmountOut: amount, - deadline: block.timestamp, - rawParams: getOriginSwapParams(0, 1) - }); + SwapQuery memory originQuery = getOriginQueryWithSwap(amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: address(token1), originAmount: amount, @@ -189,15 +188,8 @@ abstract contract FastBridgeRouterTest is FBRTest { function test_bridge_withOriginSwap_withGasRebate_senderEOA() public { uint256 amountBeforeSwap = 1 ether; - // T0 -> T1 swap on origin chain uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(router), - tokenOut: address(token1), - minAmountOut: amount, - deadline: block.timestamp, - rawParams: getOriginSwapParams(0, 1) - }); + SwapQuery memory originQuery = getOriginQueryWithSwap(amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: address(token1), originAmount: amount, @@ -223,7 +215,6 @@ abstract contract FastBridgeRouterTest is FBRTest { function test_bridge_revert_originSwap_deadlineExceeded() public { uint256 amountBeforeSwap = 1 ether; - // T0 -> T1 swap on origin chain uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); SwapQuery memory originQuery = SwapQuery({ routerAdapter: address(router), @@ -246,15 +237,8 @@ abstract contract FastBridgeRouterTest is FBRTest { function test_bridge_revert_originSwap_minAmountOutNotMet() public { uint256 amountBeforeSwap = 1 ether; - // T0 -> T1 swap on origin chain uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(router), - tokenOut: address(token1), - minAmountOut: amount + 1, - deadline: block.timestamp, - rawParams: getOriginSwapParams(0, 1) - }); + SwapQuery memory originQuery = getOriginQueryWithSwap(amount + 1); vm.expectRevert(InsufficientOutputAmount.selector); vm.prank(user); router.bridge({ diff --git a/test/rfq/FastBridgeRouterV2.t.sol b/test/rfq/FastBridgeRouterV2.t.sol index cbc3f670a..2c1731893 100644 --- a/test/rfq/FastBridgeRouterV2.t.sol +++ b/test/rfq/FastBridgeRouterV2.t.sol @@ -59,14 +59,7 @@ abstract contract FastBridgeRouterV2Test is FastBridgeRouterTest { function check_bridge_noOriginSwap_noGasRebate(address caller, address originSender) public { uint256 amount = 1 ether; - // No swap on origin chain - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(0), - tokenOut: address(token0), - minAmountOut: amount, - deadline: block.timestamp, - rawParams: "" - }); + SwapQuery memory originQuery = getOriginQueryNoSwap(amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: address(token0), originAmount: amount, @@ -90,14 +83,7 @@ abstract contract FastBridgeRouterV2Test is FastBridgeRouterTest { function check_bridge_noOriginSwap_withGasRebate(address caller, address originSender) public { uint256 amount = 1 ether; - // No swap on origin chain - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(0), - tokenOut: address(token0), - minAmountOut: amount, - deadline: block.timestamp, - rawParams: "" - }); + SwapQuery memory originQuery = getOriginQueryNoSwap(amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: address(token0), originAmount: amount, @@ -121,15 +107,8 @@ abstract contract FastBridgeRouterV2Test is FastBridgeRouterTest { function check_bridge_withOriginSwap_noGasRebate(address caller, address originSender) public { uint256 amountBeforeSwap = 1 ether; - // T0 -> T1 swap on origin chain uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(router), - tokenOut: address(token1), - minAmountOut: amount, - deadline: block.timestamp, - rawParams: getOriginSwapParams(0, 1) - }); + SwapQuery memory originQuery = getOriginQueryWithSwap(amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: address(token1), originAmount: amount, @@ -153,15 +132,8 @@ abstract contract FastBridgeRouterV2Test is FastBridgeRouterTest { function check_bridge_withOriginSwap_withGasRebate(address caller, address originSender) public { uint256 amountBeforeSwap = 1 ether; - // T0 -> T1 swap on origin chain uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(router), - tokenOut: address(token1), - minAmountOut: amount, - deadline: block.timestamp, - rawParams: getOriginSwapParams(0, 1) - }); + SwapQuery memory originQuery = getOriginQueryWithSwap(amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: address(token1), originAmount: amount, @@ -238,14 +210,7 @@ abstract contract FastBridgeRouterV2Test is FastBridgeRouterTest { function check_bridge_noOriginSwap_senderContract_revert(SwapQuery memory destQuery) public { uint256 amount = 1 ether; - // No swap on origin chain - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(0), - tokenOut: address(token0), - minAmountOut: amount, - deadline: block.timestamp, - rawParams: "" - }); + SwapQuery memory originQuery = getOriginQueryNoSwap(amount); expectRevertOriginSenderNotSpecified(); vm.prank(externalContract); router.bridge({ @@ -260,15 +225,8 @@ abstract contract FastBridgeRouterV2Test is FastBridgeRouterTest { function check_bridge_withOriginSwap_senderContract_revert(SwapQuery memory destQuery) public { uint256 amountBeforeSwap = 1 ether; - // T0 -> T1 swap on origin chain uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); - SwapQuery memory originQuery = SwapQuery({ - routerAdapter: address(router), - tokenOut: address(token1), - minAmountOut: amount, - deadline: block.timestamp, - rawParams: getOriginSwapParams(0, 1) - }); + SwapQuery memory originQuery = getOriginQueryWithSwap(amount); expectRevertOriginSenderNotSpecified(); vm.prank(externalContract); router.bridge({ From 91e32012e530ef410be8cb102f07f6ec3b2344bb Mon Sep 17 00:00:00 2001 From: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> Date: Thu, 25 Jul 2024 12:12:11 +0100 Subject: [PATCH 06/16] refactor: deduplicate V2 tests --- test/rfq/FastBridgeRouterV2.t.sol | 127 +++++++++--------------------- 1 file changed, 38 insertions(+), 89 deletions(-) diff --git a/test/rfq/FastBridgeRouterV2.t.sol b/test/rfq/FastBridgeRouterV2.t.sol index 2c1731893..b29d586a0 100644 --- a/test/rfq/FastBridgeRouterV2.t.sol +++ b/test/rfq/FastBridgeRouterV2.t.sol @@ -57,13 +57,17 @@ abstract contract FastBridgeRouterV2Test is FastBridgeRouterTest { // ════════════════════════════════ TESTS: BRIDGE (EOA, ORIGIN SENDER PROVIDED) ════════════════════════════════════ - function check_bridge_noOriginSwap_noGasRebate(address caller, address originSender) public { + function check_bridge_noOriginSwap( + address caller, + address originSender, + bool gasRebate + ) public { uint256 amount = 1 ether; SwapQuery memory originQuery = getOriginQueryNoSwap(amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: address(token0), originAmount: amount, - sendChainGas: false + sendChainGas: gasRebate }); vm.expectCall({ callee: address(fastBridge), @@ -77,42 +81,24 @@ abstract contract FastBridgeRouterV2Test is FastBridgeRouterTest { token: address(token0), amount: amount, originQuery: originQuery, - destQuery: getDestQueryNoRebateWithOriginSender(amount, originSender) + destQuery: gasRebate + ? getDestQueryWithRebateWithOriginSender(amount, originSender) + : getDestQueryNoRebateWithOriginSender(amount, originSender) }); } - function check_bridge_noOriginSwap_withGasRebate(address caller, address originSender) public { - uint256 amount = 1 ether; - SwapQuery memory originQuery = getOriginQueryNoSwap(amount); - IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ - originToken: address(token0), - originAmount: amount, - sendChainGas: true - }); - vm.expectCall({ - callee: address(fastBridge), - msgValue: 0, - data: abi.encodeCall(IFastBridge.bridge, (expectedParams)) - }); - vm.prank(caller); - router.bridge({ - recipient: recipient, - chainId: DST_CHAIN_ID, - token: address(token0), - amount: amount, - originQuery: originQuery, - destQuery: getDestQueryWithRebateWithOriginSender(amount, originSender) - }); - } - - function check_bridge_withOriginSwap_noGasRebate(address caller, address originSender) public { + function check_bridge_withOriginSwap( + address caller, + address originSender, + bool gasRebate + ) public { uint256 amountBeforeSwap = 1 ether; uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); SwapQuery memory originQuery = getOriginQueryWithSwap(amount); IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ originToken: address(token1), originAmount: amount, - sendChainGas: false + sendChainGas: gasRebate }); vm.expectCall({ callee: address(fastBridge), @@ -126,84 +112,61 @@ abstract contract FastBridgeRouterV2Test is FastBridgeRouterTest { token: address(token0), amount: amountBeforeSwap, originQuery: originQuery, - destQuery: getDestQueryNoRebateWithOriginSender(amount, originSender) - }); - } - - function check_bridge_withOriginSwap_withGasRebate(address caller, address originSender) public { - uint256 amountBeforeSwap = 1 ether; - uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); - SwapQuery memory originQuery = getOriginQueryWithSwap(amount); - IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ - originToken: address(token1), - originAmount: amount, - sendChainGas: true - }); - vm.expectCall({ - callee: address(fastBridge), - msgValue: 0, - data: abi.encodeCall(IFastBridge.bridge, (expectedParams)) - }); - vm.prank(caller); - router.bridge({ - recipient: recipient, - chainId: DST_CHAIN_ID, - token: address(token0), - amount: amountBeforeSwap, - originQuery: originQuery, - destQuery: getDestQueryWithRebateWithOriginSender(amount, originSender) + destQuery: gasRebate + ? getDestQueryWithRebateWithOriginSender(amount, originSender) + : getDestQueryNoRebateWithOriginSender(amount, originSender) }); } function test_bridge_noOriginSwap_noGasRebate_senderEOA_withOriginSenderSet() public { - check_bridge_noOriginSwap_noGasRebate({caller: user, originSender: user}); + check_bridge_noOriginSwap({caller: user, originSender: user, gasRebate: false}); } function test_bridge_noOriginSwap_withGasRebate_senderEOA_withOriginSenderSet() public { - check_bridge_noOriginSwap_withGasRebate({caller: user, originSender: user}); + check_bridge_noOriginSwap({caller: user, originSender: user, gasRebate: true}); } function test_bridge_withOriginSwap_noGasRebate_senderEOA_withOriginSenderSet() public { - check_bridge_withOriginSwap_noGasRebate({caller: user, originSender: user}); + check_bridge_withOriginSwap({caller: user, originSender: user, gasRebate: false}); } function test_bridge_withOriginSwap_withGasRebate_senderEOA_withOriginSenderSet() public { - check_bridge_withOriginSwap_withGasRebate({caller: user, originSender: user}); + check_bridge_withOriginSwap({caller: user, originSender: user, gasRebate: true}); } // Note: Calls from EOA with origin sender set to zero address should succeed function test_bridge_noOriginSwap_noGasRebate_senderEOA_withOriginSenderZero() public { - check_bridge_noOriginSwap_noGasRebate({caller: user, originSender: address(0)}); + check_bridge_noOriginSwap({caller: user, originSender: address(0), gasRebate: false}); } function test_bridge_noOriginSwap_withGasRebate_senderEOA_withOriginSenderZero() public { - check_bridge_noOriginSwap_withGasRebate({caller: user, originSender: address(0)}); + check_bridge_noOriginSwap({caller: user, originSender: address(0), gasRebate: true}); } function test_bridge_withOriginSwap_noGasRebate_senderEOA_withOriginSenderZero() public { - check_bridge_withOriginSwap_noGasRebate({caller: user, originSender: address(0)}); + check_bridge_withOriginSwap({caller: user, originSender: address(0), gasRebate: false}); } function test_bridge_withOriginSwap_withGasRebate_senderEOA_withOriginSenderZero() public { - check_bridge_withOriginSwap_withGasRebate({caller: user, originSender: address(0)}); + check_bridge_withOriginSwap({caller: user, originSender: address(0), gasRebate: true}); } // ═════════════════════════════ TESTS: BRIDGE (CONTRACT, ORIGIN SENDER PROVIDED) ══════════════════════════════════ function test_bridge_noOriginSwap_noGasRebate_senderContract_withOriginSenderSet() public { - check_bridge_noOriginSwap_noGasRebate({caller: externalContract, originSender: user}); + check_bridge_noOriginSwap({caller: externalContract, originSender: user, gasRebate: false}); } function test_bridge_noOriginSwap_withGasRebate_senderContract_withOriginSenderSet() public { - check_bridge_noOriginSwap_withGasRebate({caller: externalContract, originSender: user}); + check_bridge_noOriginSwap({caller: externalContract, originSender: user, gasRebate: true}); } function test_bridge_withOriginSwap_noGasRebate_senderContract_withOriginSenderSet() public { - check_bridge_withOriginSwap_noGasRebate({caller: externalContract, originSender: user}); + check_bridge_withOriginSwap({caller: externalContract, originSender: user, gasRebate: false}); } function test_bridge_withOriginSwap_withGasRebate_senderContract_withOriginSenderSet() public { - check_bridge_withOriginSwap_withGasRebate({caller: externalContract, originSender: user}); + check_bridge_withOriginSwap({caller: externalContract, originSender: user, gasRebate: true}); } // ═══════════════════════════ TESTS: BRIDGE (CONTRACT, ORIGIN SENDER NOT PROVIDED) ════════════════════════════════ @@ -239,41 +202,27 @@ abstract contract FastBridgeRouterV2Test is FastBridgeRouterTest { }); } - function test_bridge_noOriginSwap_noGasRebate_senderContract_revert() public { + function test_bridge_noOriginSwap_senderContract_reverts() public { + // Revert when origin sender is not encoded into destQuery check_bridge_noOriginSwap_senderContract_revert({destQuery: getDestQueryNoRebate(1 ether)}); - } - - function test_bridge_noOriginSwap_withGasRebate_senderContract_revert() public { check_bridge_noOriginSwap_senderContract_revert({destQuery: getDestQueryWithRebate(1 ether)}); - } - - function test_bridge_withOriginSwap_noGasRebate_senderContract_revert() public { - check_bridge_withOriginSwap_senderContract_revert({destQuery: getDestQueryNoRebate(1 ether)}); - } - - function test_bridge_withOriginSwap_withGasRebate_senderContract_revert() public { - check_bridge_withOriginSwap_senderContract_revert({destQuery: getDestQueryWithRebate(1 ether)}); - } - - function test_bridge_noOriginSwap_noGasRebate_senderContract_withOriginSenderZero_revert() public { + // Revert when empty origin sender is encoded into destQuery check_bridge_noOriginSwap_senderContract_revert({ destQuery: getDestQueryNoRebateWithOriginSender(1 ether, address(0)) }); - } - - function test_bridge_noOriginSwap_withGasRebate_senderContract_withOriginSenderZero_revert() public { check_bridge_noOriginSwap_senderContract_revert({ destQuery: getDestQueryWithRebateWithOriginSender(1 ether, address(0)) }); } - function test_bridge_withOriginSwap_noGasRebate_senderContract_withOriginSenderZero_revert() public { + function test_bridge_withOriginSwap_senderContract_reverts() public { + // Revert when origin sender is not encoded into destQuery + check_bridge_withOriginSwap_senderContract_revert({destQuery: getDestQueryNoRebate(1 ether)}); + check_bridge_withOriginSwap_senderContract_revert({destQuery: getDestQueryWithRebate(1 ether)}); + // Revert when empty origin sender is encoded into destQuery check_bridge_withOriginSwap_senderContract_revert({ destQuery: getDestQueryNoRebateWithOriginSender(1 ether, address(0)) }); - } - - function test_bridge_withOriginSwap_withGasRebate_senderContract_withOriginSenderZero_revert() public { check_bridge_withOriginSwap_senderContract_revert({ destQuery: getDestQueryWithRebateWithOriginSender(1 ether, address(0)) }); From cce2d139d91028143af554143f12f6be667f4929 Mon Sep 17 00:00:00 2001 From: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> Date: Thu, 25 Jul 2024 12:13:02 +0100 Subject: [PATCH 07/16] test: add native ETH tests for FBR v2 --- test/rfq/FastBridgeRouter.Native.t.sol | 10 +- .../FastBridgeRouterV2.Native.QuoterV1.t.sol | 28 + .../FastBridgeRouterV2.Native.QuoterV2.t.sol | 32 + test/rfq/FastBridgeRouterV2.Native.t.sol | 823 ++++++++++++++++++ 4 files changed, 888 insertions(+), 5 deletions(-) create mode 100644 test/rfq/FastBridgeRouterV2.Native.QuoterV1.t.sol create mode 100644 test/rfq/FastBridgeRouterV2.Native.QuoterV2.t.sol create mode 100644 test/rfq/FastBridgeRouterV2.Native.t.sol diff --git a/test/rfq/FastBridgeRouter.Native.t.sol b/test/rfq/FastBridgeRouter.Native.t.sol index 05463d3db..7688d982e 100644 --- a/test/rfq/FastBridgeRouter.Native.t.sol +++ b/test/rfq/FastBridgeRouter.Native.t.sol @@ -33,12 +33,12 @@ abstract contract FastBridgeRouterNativeTest is FBRTest { } function prepareAccount(address account) public { - token.mint(user, 10 ether); - weth.mint(user, 10 ether); - deal(user, 10 ether); - vm.prank(user); + token.mint(account, 10 ether); + weth.mint(account, 10 ether); + deal(account, 10 ether); + vm.prank(account); token.approve(address(router), 10 ether); - vm.prank(user); + vm.prank(account); weth.approve(address(router), 10 ether); } diff --git a/test/rfq/FastBridgeRouterV2.Native.QuoterV1.t.sol b/test/rfq/FastBridgeRouterV2.Native.QuoterV1.t.sol new file mode 100644 index 000000000..7e7c92872 --- /dev/null +++ b/test/rfq/FastBridgeRouterV2.Native.QuoterV1.t.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import {FastBridgeRouterV2NativeTest} from "./FastBridgeRouterV2.Native.t.sol"; +import {ISwapQuoterV1} from "../interfaces/ISwapQuoterV1.sol"; + +contract FastBridgeRouterV2NativeQuoterV1Test is FastBridgeRouterV2NativeTest { + function setUpSwapQuoter() internal override { + // SwapQuoter V1 is solidity 0.6.12, so we use the cheatcode to deploy it + // new SwapQuoter(synapseRouter, weth, owner) + bytes memory constructorArgs = abi.encode( + // Existing SwapQuoter will be always pointing towards another router + address(1), // synapseRouter + address(weth), // weth + address(this) // owner + ); + address swapQuoter = deployCode("SwapQuoter.sol", constructorArgs); + addPool(swapQuoter); + vm.prank(owner); + router.setSwapQuoter(swapQuoter); + } + + function addPool(address swapQuoter) internal { + address[] memory pools = new address[](1); + pools[0] = address(pool); + ISwapQuoterV1(swapQuoter).addPools(pools); + } +} diff --git a/test/rfq/FastBridgeRouterV2.Native.QuoterV2.t.sol b/test/rfq/FastBridgeRouterV2.Native.QuoterV2.t.sol new file mode 100644 index 000000000..463bcea6a --- /dev/null +++ b/test/rfq/FastBridgeRouterV2.Native.QuoterV2.t.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import {FastBridgeRouterV2NativeTest} from "./FastBridgeRouterV2.Native.t.sol"; +import {DefaultPoolCalc} from "../../contracts/router/quoter/DefaultPoolCalc.sol"; +import {SwapQuoterV2} from "../../contracts/router/quoter/SwapQuoterV2.sol"; + +contract FastBridgeRouterV2NativeQuoterV2Test is FastBridgeRouterV2NativeTest { + function setUpSwapQuoter() internal override { + DefaultPoolCalc poolCalc = new DefaultPoolCalc(); + SwapQuoterV2 quoter = new SwapQuoterV2({ + // Existing SwapQuoter will be always pointing towards another router + synapseRouter_: address(1), + defaultPoolCalc_: address(poolCalc), + weth_: address(weth), + owner_: address(this) + }); + addPool(quoter); + vm.prank(owner); + router.setSwapQuoter(address(quoter)); + } + + function addPool(SwapQuoterV2 quoter) internal { + SwapQuoterV2.BridgePool[] memory pools = new SwapQuoterV2.BridgePool[](1); + pools[0] = SwapQuoterV2.BridgePool({ + bridgeToken: address(0), + poolType: SwapQuoterV2.PoolType.Default, + pool: address(pool) + }); + quoter.addPools(pools); + } +} diff --git a/test/rfq/FastBridgeRouterV2.Native.t.sol b/test/rfq/FastBridgeRouterV2.Native.t.sol new file mode 100644 index 000000000..fc719d636 --- /dev/null +++ b/test/rfq/FastBridgeRouterV2.Native.t.sol @@ -0,0 +1,823 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import {FastBridgeRouterV2} from "../../contracts/rfq/FastBridgeRouterV2.sol"; + +import {MockSenderContract} from "../mocks/MockSenderContract.sol"; + +import {FastBridgeRouterNativeTest, IFastBridge, SwapQuery} from "./FastBridgeRouter.Native.t.sol"; + +// solhint-disable not-rely-on-time +// solhint-disable ordering +// solhint-disable func-name-mixedcase +abstract contract FastBridgeRouterV2NativeTest is FastBridgeRouterNativeTest { + address public externalContract; + + function setUp() public virtual override { + super.setUp(); + externalContract = address(new MockSenderContract()); + prepareAccount(externalContract); + } + + function deployRouter() public virtual override returns (address payable) { + return payable(new FastBridgeRouterV2(owner)); + } + + function getDestQueryNoRebateWithOriginSender(uint256 amount, address originSender) + public + view + returns (SwapQuery memory destQuery) + { + destQuery = SwapQuery({ + routerAdapter: address(0), + tokenOut: TOKEN_OUT, + minAmountOut: amount - FIXED_FEE, + deadline: block.timestamp + RFQ_DEADLINE, + rawParams: abi.encodePacked(uint8(0), originSender) + }); + } + + function getDestQueryWithRebateWithOriginSender(uint256 amount, address originSender) + public + view + returns (SwapQuery memory destQuery) + { + destQuery = SwapQuery({ + routerAdapter: address(0), + tokenOut: TOKEN_OUT, + minAmountOut: amount - FIXED_FEE, + deadline: block.timestamp + RFQ_DEADLINE, + rawParams: abi.encodePacked(REBATE_FLAG, originSender) + }); + } + + function expectRevertOriginSenderNotSpecified() public { + vm.expectRevert(FastBridgeRouterV2.FastBridgeRouterV2__OriginSenderNotSpecified.selector); + } + + // ════════════════════════════ TESTS: START FROM ETH (EOA, ORIGIN SENDER PROVIDED) ════════════════════════════════ + + // Start from ETH, use ETH for RFQ + function check_bridge_eth_noOriginSwap( + address caller, + address originSender, + bool gasRebate + ) public { + uint256 amount = 1 ether; + SwapQuery memory originQuery = getOriginQueryNoSwap(ETH, amount); + IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ + originToken: ETH, + originAmount: amount, + sendChainGas: gasRebate + }); + vm.expectCall({ + callee: address(fastBridge), + msgValue: amount, + data: abi.encodeCall(IFastBridge.bridge, (expectedParams)) + }); + vm.prank(caller); + router.bridge{value: amount}({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: ETH, + amount: amount, + originQuery: originQuery, + destQuery: gasRebate + ? getDestQueryWithRebateWithOriginSender(amount, originSender) + : getDestQueryNoRebateWithOriginSender(amount, originSender) + }); + } + + // Start from ETH, use WETH for RFQ + function check_bridge_eth_withOriginWrap( + address caller, + address originSender, + bool gasRebate + ) public { + uint256 amount = 1 ether; + SwapQuery memory originQuery = getOriginQueryWithHandleETH(address(weth), amount); + IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ + originToken: address(weth), + originAmount: amount, + sendChainGas: gasRebate + }); + vm.expectCall({ + callee: address(fastBridge), + msgValue: 0, + data: abi.encodeCall(IFastBridge.bridge, (expectedParams)) + }); + vm.prank(caller); + router.bridge{value: amount}({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: ETH, + amount: amount, + originQuery: originQuery, + destQuery: gasRebate + ? getDestQueryWithRebateWithOriginSender(amount, originSender) + : getDestQueryNoRebateWithOriginSender(amount, originSender) + }); + } + + // Start from ETH, use paired token for RFQ + function check_bridge_eth_withOriginSwap( + address caller, + address originSender, + bool gasRebate + ) public { + uint256 amountBeforeSwap = 1 ether; + uint256 amount = pool.calculateSwap(1, 0, amountBeforeSwap); + SwapQuery memory originQuery = getOriginQueryWithSwap(address(token), amount); + IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ + originToken: address(token), + originAmount: amount, + sendChainGas: gasRebate + }); + vm.expectCall({ + callee: address(fastBridge), + msgValue: 0, + data: abi.encodeCall(IFastBridge.bridge, (expectedParams)) + }); + vm.prank(caller); + router.bridge{value: amountBeforeSwap}({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: ETH, + amount: amountBeforeSwap, + originQuery: originQuery, + destQuery: gasRebate + ? getDestQueryWithRebateWithOriginSender(amount, originSender) + : getDestQueryNoRebateWithOriginSender(amount, originSender) + }); + } + + function test_bridge_eth_noOriginSwap_noGasRebate_senderEOA_withOriginSenderSet() public { + check_bridge_eth_noOriginSwap({caller: user, originSender: user, gasRebate: false}); + } + + function test_bridge_eth_noOriginSwap_withGasRebate_senderEOA_withOriginSenderSet() public { + check_bridge_eth_noOriginSwap({caller: user, originSender: user, gasRebate: true}); + } + + function test_bridge_eth_withOriginWrap_noGasRebate_senderEOA_withOriginSenderSet() public { + check_bridge_eth_withOriginWrap({caller: user, originSender: user, gasRebate: false}); + } + + function test_bridge_eth_withOriginWrap_withGasRebate_senderEOA_withOriginSenderSet() public { + check_bridge_eth_withOriginWrap({caller: user, originSender: user, gasRebate: true}); + } + + function test_bridge_eth_withOriginSwap_noGasRebate_senderEOA_withOriginSenderSet() public { + check_bridge_eth_withOriginSwap({caller: user, originSender: user, gasRebate: false}); + } + + function test_bridge_eth_withOriginSwap_withGasRebate_senderEOA_withOriginSenderSet() public { + check_bridge_eth_withOriginSwap({caller: user, originSender: user, gasRebate: true}); + } + + // Note: Calls from EOA with origin sender set to zero address should succeed + function test_bridge_eth_noOriginSwap_noGasRebate_senderEOA_withOriginSenderZero() public { + check_bridge_eth_noOriginSwap({caller: user, originSender: address(0), gasRebate: false}); + } + + function test_bridge_eth_noOriginSwap_withGasRebate_senderEOA_withOriginSenderZero() public { + check_bridge_eth_noOriginSwap({caller: user, originSender: address(0), gasRebate: true}); + } + + function test_bridge_eth_withOriginWrap_noGasRebate_senderEOA_withOriginSenderZero() public { + check_bridge_eth_withOriginWrap({caller: user, originSender: address(0), gasRebate: false}); + } + + function test_bridge_eth_withOriginWrap_withGasRebate_senderEOA_withOriginSenderZero() public { + check_bridge_eth_withOriginWrap({caller: user, originSender: address(0), gasRebate: true}); + } + + function test_bridge_eth_withOriginSwap_noGasRebate_senderEOA_withOriginSenderZero() public { + check_bridge_eth_withOriginSwap({caller: user, originSender: address(0), gasRebate: false}); + } + + function test_bridge_eth_withOriginSwap_withGasRebate_senderEOA_withOriginSenderZero() public { + check_bridge_eth_withOriginSwap({caller: user, originSender: address(0), gasRebate: true}); + } + + // ═══════════════════════════ TESTS: START FROM WETH (EOA, ORIGIN SENDER PROVIDED) ════════════════════════════════ + + // Start from WETH, use WETH for RFQ + function check_bridge_weth_noOriginSwap( + address caller, + address originSender, + bool gasRebate + ) public { + uint256 amount = 1 ether; + SwapQuery memory originQuery = getOriginQueryNoSwap(address(weth), amount); + IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ + originToken: address(weth), + originAmount: amount, + sendChainGas: gasRebate + }); + vm.expectCall({ + callee: address(fastBridge), + msgValue: 0, + data: abi.encodeCall(IFastBridge.bridge, (expectedParams)) + }); + vm.prank(caller); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(weth), + amount: amount, + originQuery: originQuery, + destQuery: gasRebate + ? getDestQueryWithRebateWithOriginSender(amount, originSender) + : getDestQueryNoRebateWithOriginSender(amount, originSender) + }); + } + + // Start from WETH, use ETH for RFQ + function check_bridge_weth_withOriginUnwrap( + address caller, + address originSender, + bool gasRebate + ) public { + uint256 amount = 1 ether; + SwapQuery memory originQuery = getOriginQueryWithHandleETH(ETH, amount); + IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ + originToken: ETH, + originAmount: amount, + sendChainGas: gasRebate + }); + vm.expectCall({ + callee: address(fastBridge), + msgValue: amount, + data: abi.encodeCall(IFastBridge.bridge, (expectedParams)) + }); + vm.prank(caller); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(weth), + amount: amount, + originQuery: originQuery, + destQuery: gasRebate + ? getDestQueryWithRebateWithOriginSender(amount, originSender) + : getDestQueryNoRebateWithOriginSender(amount, originSender) + }); + } + + // Start from WETH, use paired token for RFQ + function check_bridge_weth_withOriginSwap( + address caller, + address originSender, + bool gasRebate + ) public { + uint256 amountBeforeSwap = 1 ether; + uint256 amount = pool.calculateSwap(1, 0, amountBeforeSwap); + SwapQuery memory originQuery = getOriginQueryWithSwap(address(token), amount); + IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ + originToken: address(token), + originAmount: amount, + sendChainGas: gasRebate + }); + vm.expectCall({ + callee: address(fastBridge), + msgValue: 0, + data: abi.encodeCall(IFastBridge.bridge, (expectedParams)) + }); + vm.prank(caller); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(weth), + amount: amountBeforeSwap, + originQuery: originQuery, + destQuery: gasRebate + ? getDestQueryWithRebateWithOriginSender(amount, originSender) + : getDestQueryNoRebateWithOriginSender(amount, originSender) + }); + } + + function test_bridge_weth_noOriginSwap_noGasRebate_senderEOA_withOriginSenderSet() public { + check_bridge_weth_noOriginSwap({caller: user, originSender: user, gasRebate: false}); + } + + function test_bridge_weth_noOriginSwap_withGasRebate_senderEOA_withOriginSenderSet() public { + check_bridge_weth_noOriginSwap({caller: user, originSender: user, gasRebate: true}); + } + + function test_bridge_weth_withOriginUnwrap_noGasRebate_senderEOA_withOriginSenderSet() public { + check_bridge_weth_withOriginUnwrap({caller: user, originSender: user, gasRebate: false}); + } + + function test_bridge_weth_withOriginUnwrap_withGasRebate_senderEOA_withOriginSenderSet() public { + check_bridge_weth_withOriginUnwrap({caller: user, originSender: user, gasRebate: true}); + } + + function test_bridge_weth_withOriginSwap_noGasRebate_senderEOA_withOriginSenderSet() public { + check_bridge_weth_withOriginSwap({caller: user, originSender: user, gasRebate: false}); + } + + function test_bridge_weth_withOriginSwap_withGasRebate_senderEOA_withOriginSenderSet() public { + check_bridge_weth_withOriginSwap({caller: user, originSender: user, gasRebate: true}); + } + + // Note: Calls from EOA with origin sender set to zero address should succeed + function test_bridge_weth_noOriginSwap_noGasRebate_senderEOA_withOriginSenderZero() public { + check_bridge_weth_noOriginSwap({caller: user, originSender: address(0), gasRebate: false}); + } + + function test_bridge_weth_noOriginSwap_withGasRebate_senderEOA_withOriginSenderZero() public { + check_bridge_weth_noOriginSwap({caller: user, originSender: address(0), gasRebate: true}); + } + + function test_bridge_weth_withOriginUnwrap_noGasRebate_senderEOA_withOriginSenderZero() public { + check_bridge_weth_withOriginUnwrap({caller: user, originSender: address(0), gasRebate: false}); + } + + function test_bridge_weth_withOriginUnwrap_withGasRebate_senderEOA_withOriginSenderZero() public { + check_bridge_weth_withOriginUnwrap({caller: user, originSender: address(0), gasRebate: true}); + } + + function test_bridge_weth_withOriginSwap_noGasRebate_senderEOA_withOriginSenderZero() public { + check_bridge_weth_withOriginSwap({caller: user, originSender: address(0), gasRebate: false}); + } + + function test_bridge_weth_withOriginSwap_withGasRebate_senderEOA_withOriginSenderZero() public { + check_bridge_weth_withOriginSwap({caller: user, originSender: address(0), gasRebate: true}); + } + + // ═══════════════════════ TESTS: START FROM PAIRED TOKEN (EOA, ORIGIN SENDER PROVIDED) ════════════════════════════ + + // Start from paired token, use paired token for RFQ + function check_bridge_token_noOriginSwap( + address caller, + address originSender, + bool gasRebate + ) public { + uint256 amount = 1 ether; + SwapQuery memory originQuery = getOriginQueryNoSwap(address(token), amount); + IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ + originToken: address(token), + originAmount: amount, + sendChainGas: gasRebate + }); + vm.expectCall({ + callee: address(fastBridge), + msgValue: 0, + data: abi.encodeCall(IFastBridge.bridge, (expectedParams)) + }); + vm.prank(caller); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(token), + amount: amount, + originQuery: originQuery, + destQuery: gasRebate + ? getDestQueryWithRebateWithOriginSender(amount, originSender) + : getDestQueryNoRebateWithOriginSender(amount, originSender) + }); + } + + // Start from paired token, use WETH for RFQ + function check_bridge_token_withOriginSwap( + address caller, + address originSender, + bool gasRebate + ) public { + uint256 amountBeforeSwap = 1 ether; + uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); + SwapQuery memory originQuery = getOriginQueryWithSwap(address(weth), amount); + IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ + originToken: address(weth), + originAmount: amount, + sendChainGas: gasRebate + }); + vm.expectCall({ + callee: address(fastBridge), + msgValue: 0, + data: abi.encodeCall(IFastBridge.bridge, (expectedParams)) + }); + vm.prank(caller); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(token), + amount: amountBeforeSwap, + originQuery: originQuery, + destQuery: gasRebate + ? getDestQueryWithRebateWithOriginSender(amount, originSender) + : getDestQueryNoRebateWithOriginSender(amount, originSender) + }); + } + + // Start from paired token, use ETH for RFQ + function check_bridge_token_withOriginSwapUnwrap( + address caller, + address originSender, + bool gasRebate + ) public { + uint256 amountBeforeSwap = 1 ether; + uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); + SwapQuery memory originQuery = getOriginQueryWithSwap(ETH, amount); + IFastBridge.BridgeParams memory expectedParams = getExpectedBridgeParams({ + originToken: ETH, + originAmount: amount, + sendChainGas: gasRebate + }); + vm.expectCall({ + callee: address(fastBridge), + msgValue: amount, + data: abi.encodeCall(IFastBridge.bridge, (expectedParams)) + }); + vm.prank(caller); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(token), + amount: amountBeforeSwap, + originQuery: originQuery, + destQuery: gasRebate + ? getDestQueryWithRebateWithOriginSender(amount, originSender) + : getDestQueryNoRebateWithOriginSender(amount, originSender) + }); + } + + function test_bridge_token_noOriginSwap_noGasRebate_senderEOA_withOriginSenderSet() public { + check_bridge_token_noOriginSwap({caller: user, originSender: user, gasRebate: false}); + } + + function test_bridge_token_noOriginSwap_withGasRebate_senderEOA_withOriginSenderSet() public { + check_bridge_token_noOriginSwap({caller: user, originSender: user, gasRebate: true}); + } + + function test_bridge_token_withOriginSwap_noGasRebate_senderEOA_withOriginSenderSet() public { + check_bridge_token_withOriginSwap({caller: user, originSender: user, gasRebate: false}); + } + + function test_bridge_token_withOriginSwap_withGasRebate_senderEOA_withOriginSenderSet() public { + check_bridge_token_withOriginSwap({caller: user, originSender: user, gasRebate: true}); + } + + function test_bridge_token_withOriginSwapUnwrap_noGasRebate_senderEOA_withOriginSenderSet() public { + check_bridge_token_withOriginSwapUnwrap({caller: user, originSender: user, gasRebate: false}); + } + + function test_bridge_token_withOriginSwapUnwrap_withGasRebate_senderEOA_withOriginSenderSet() public { + check_bridge_token_withOriginSwapUnwrap({caller: user, originSender: user, gasRebate: true}); + } + + // Note: Calls from EOA with origin sender set to zero address should succeed + function test_bridge_token_noOriginSwap_noGasRebate_senderEOA_withOriginSenderZero() public { + check_bridge_token_noOriginSwap({caller: user, originSender: address(0), gasRebate: false}); + } + + function test_bridge_token_noOriginSwap_withGasRebate_senderEOA_withOriginSenderZero() public { + check_bridge_token_noOriginSwap({caller: user, originSender: address(0), gasRebate: true}); + } + + function test_bridge_token_withOriginSwap_noGasRebate_senderEOA_withOriginSenderZero() public { + check_bridge_token_withOriginSwap({caller: user, originSender: address(0), gasRebate: false}); + } + + function test_bridge_token_withOriginSwap_withGasRebate_senderEOA_withOriginSenderZero() public { + check_bridge_token_withOriginSwap({caller: user, originSender: address(0), gasRebate: true}); + } + + function test_bridge_token_withOriginSwapUnwrap_noGasRebate_senderEOA_withOriginSenderZero() public { + check_bridge_token_withOriginSwapUnwrap({caller: user, originSender: address(0), gasRebate: false}); + } + + function test_bridge_token_withOriginSwapUnwrap_withGasRebate_senderEOA_withOriginSenderZero() public { + check_bridge_token_withOriginSwapUnwrap({caller: user, originSender: address(0), gasRebate: true}); + } + + // ═════════════════════════ TESTS: START FROM ETH (CONTRACT, ORIGIN SENDER PROVIDED) ══════════════════════════════ + + function test_bridge_eth_noOriginSwap_noGasRebate_senderContract_withOriginSenderSet() public { + check_bridge_eth_noOriginSwap({caller: externalContract, originSender: user, gasRebate: false}); + } + + function test_bridge_eth_noOriginSwap_withGasRebate_senderContract_withOriginSenderSet() public { + check_bridge_eth_noOriginSwap({caller: externalContract, originSender: user, gasRebate: true}); + } + + function test_bridge_eth_withOriginWrap_noGasRebate_senderContract_withOriginSenderSet() public { + check_bridge_eth_withOriginWrap({caller: externalContract, originSender: user, gasRebate: false}); + } + + function test_bridge_eth_withOriginWrap_withGasRebate_senderContract_withOriginSenderSet() public { + check_bridge_eth_withOriginWrap({caller: externalContract, originSender: user, gasRebate: true}); + } + + function test_bridge_eth_withOriginSwap_noGasRebate_senderContract_withOriginSenderSet() public { + check_bridge_eth_withOriginSwap({caller: externalContract, originSender: user, gasRebate: false}); + } + + function test_bridge_eth_withOriginSwap_withGasRebate_senderContract_withOriginSenderSet() public { + check_bridge_eth_withOriginSwap({caller: externalContract, originSender: user, gasRebate: true}); + } + + // ═════════════════════════ TESTS: START FROM WETH (CONTRACT, ORIGIN SENDER PROVIDED) ═════════════════════════════ + + function test_bridge_weth_noOriginSwap_noGasRebate_senderContract_withOriginSenderSet() public { + check_bridge_weth_noOriginSwap({caller: externalContract, originSender: user, gasRebate: false}); + } + + function test_bridge_weth_noOriginSwap_withGasRebate_senderContract_withOriginSenderSet() public { + check_bridge_weth_noOriginSwap({caller: externalContract, originSender: user, gasRebate: true}); + } + + function test_bridge_weth_withOriginUnwrap_noGasRebate_senderContract_withOriginSenderSet() public { + check_bridge_weth_withOriginUnwrap({caller: externalContract, originSender: user, gasRebate: false}); + } + + function test_bridge_weth_withOriginUnwrap_withGasRebate_senderContract_withOriginSenderSet() public { + check_bridge_weth_withOriginUnwrap({caller: externalContract, originSender: user, gasRebate: true}); + } + + function test_bridge_weth_withOriginSwap_noGasRebate_senderContract_withOriginSenderSet() public { + check_bridge_weth_withOriginSwap({caller: externalContract, originSender: user, gasRebate: false}); + } + + function test_bridge_weth_withOriginSwap_withGasRebate_senderContract_withOriginSenderSet() public { + check_bridge_weth_withOriginSwap({caller: externalContract, originSender: user, gasRebate: true}); + } + + // ═════════════════════ TESTS: START FROM PAIRED TOKEN (CONTRACT, ORIGIN SENDER PROVIDED) ═════════════════════════ + + function test_bridge_token_noOriginSwap_noGasRebate_senderContract_withOriginSenderSet() public { + check_bridge_token_noOriginSwap({caller: externalContract, originSender: user, gasRebate: false}); + } + + function test_bridge_token_noOriginSwap_withGasRebate_senderContract_withOriginSenderSet() public { + check_bridge_token_noOriginSwap({caller: externalContract, originSender: user, gasRebate: true}); + } + + function test_bridge_token_withOriginSwap_noGasRebate_senderContract_withOriginSenderSet() public { + check_bridge_token_withOriginSwap({caller: externalContract, originSender: user, gasRebate: false}); + } + + function test_bridge_token_withOriginSwap_withGasRebate_senderContract_withOriginSenderSet() public { + check_bridge_token_withOriginSwap({caller: externalContract, originSender: user, gasRebate: true}); + } + + function test_bridge_token_withOriginSwapUnwrap_noGasRebate_senderContract_withOriginSenderSet() public { + check_bridge_token_withOriginSwapUnwrap({caller: externalContract, originSender: user, gasRebate: false}); + } + + function test_bridge_token_withOriginSwapUnwrap_withGasRebate_senderContract_withOriginSenderSet() public { + check_bridge_token_withOriginSwapUnwrap({caller: externalContract, originSender: user, gasRebate: true}); + } + + // ═══════════════════════ TESTS: START FROM ETH (CONTRACT, ORIGIN SENDER NOT PROVIDED) ════════════════════════════ + + function check_bridge_eth_noOriginSwap_senderContract_revert(SwapQuery memory destQuery) public { + uint256 amount = 1 ether; + expectRevertOriginSenderNotSpecified(); + vm.prank(externalContract); + router.bridge{value: amount}({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: ETH, + amount: amount, + originQuery: getOriginQueryNoSwap(ETH, amount), + destQuery: destQuery + }); + } + + function check_bridge_eth_withOriginWrap_senderContract_revert(SwapQuery memory destQuery) public { + uint256 amount = 1 ether; + expectRevertOriginSenderNotSpecified(); + vm.prank(externalContract); + router.bridge{value: amount}({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: ETH, + amount: amount, + originQuery: getOriginQueryWithHandleETH(address(weth), amount), + destQuery: destQuery + }); + } + + function check_bridge_eth_withOriginSwap_senderContract_revert(SwapQuery memory destQuery) public { + uint256 amountBeforeSwap = 1 ether; + uint256 amount = pool.calculateSwap(1, 0, amountBeforeSwap); + expectRevertOriginSenderNotSpecified(); + vm.prank(externalContract); + router.bridge{value: amountBeforeSwap}({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: ETH, + amount: amountBeforeSwap, + originQuery: getOriginQueryWithSwap(address(token), amount), + destQuery: destQuery + }); + } + + function test_bridge_eth_noOriginSwap_senderContract_reverts() public { + // Revert when origin sender is not encoded into destQuery + check_bridge_eth_noOriginSwap_senderContract_revert({destQuery: getDestQueryNoRebate(1 ether)}); + check_bridge_eth_noOriginSwap_senderContract_revert({destQuery: getDestQueryWithRebate(1 ether)}); + // Revert when empty origin sender is encoded into destQuery + check_bridge_eth_noOriginSwap_senderContract_revert({ + destQuery: getDestQueryNoRebateWithOriginSender(1 ether, address(0)) + }); + check_bridge_eth_noOriginSwap_senderContract_revert({ + destQuery: getDestQueryWithRebateWithOriginSender(1 ether, address(0)) + }); + } + + function test_bridge_eth_withOriginWrap_senderContract_reverts() public { + // Revert when origin sender is not encoded into destQuery + check_bridge_eth_withOriginWrap_senderContract_revert({destQuery: getDestQueryNoRebate(1 ether)}); + check_bridge_eth_withOriginWrap_senderContract_revert({destQuery: getDestQueryWithRebate(1 ether)}); + // Revert when empty origin sender is encoded into destQuery + check_bridge_eth_withOriginWrap_senderContract_revert({ + destQuery: getDestQueryNoRebateWithOriginSender(1 ether, address(0)) + }); + check_bridge_eth_withOriginWrap_senderContract_revert({ + destQuery: getDestQueryWithRebateWithOriginSender(1 ether, address(0)) + }); + } + + function test_bridge_eth_withOriginSwap_senderContract_reverts() public { + // Revert when origin sender is not encoded into destQuery + check_bridge_eth_withOriginSwap_senderContract_revert({destQuery: getDestQueryNoRebate(1 ether)}); + check_bridge_eth_withOriginSwap_senderContract_revert({destQuery: getDestQueryWithRebate(1 ether)}); + // Revert when empty origin sender is encoded into destQuery + check_bridge_eth_withOriginSwap_senderContract_revert({ + destQuery: getDestQueryNoRebateWithOriginSender(1 ether, address(0)) + }); + check_bridge_eth_withOriginSwap_senderContract_revert({ + destQuery: getDestQueryWithRebateWithOriginSender(1 ether, address(0)) + }); + } + + // ═══════════════════════ TESTS: START FROM WETH (CONTRACT, ORIGIN SENDER NOT PROVIDED) ═══════════════════════════ + + function check_bridge_weth_noOriginSwap_senderContract_revert(SwapQuery memory destQuery) public { + uint256 amount = 1 ether; + expectRevertOriginSenderNotSpecified(); + vm.prank(externalContract); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(weth), + amount: amount, + originQuery: getOriginQueryNoSwap(address(weth), amount), + destQuery: destQuery + }); + } + + function check_bridge_weth_withOriginUnwrap_senderContract_revert(SwapQuery memory destQuery) public { + uint256 amount = 1 ether; + expectRevertOriginSenderNotSpecified(); + vm.prank(externalContract); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(weth), + amount: amount, + originQuery: getOriginQueryWithHandleETH(ETH, amount), + destQuery: destQuery + }); + } + + function check_bridge_weth_withOriginSwap_senderContract_revert(SwapQuery memory destQuery) public { + uint256 amount = 1 ether; + expectRevertOriginSenderNotSpecified(); + vm.prank(externalContract); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(weth), + amount: amount, + originQuery: getOriginQueryWithSwap(address(token), amount), + destQuery: destQuery + }); + } + + function test_bridge_weth_noOriginSwap_senderContract_reverts() public { + // Revert when origin sender is not encoded into destQuery + check_bridge_weth_noOriginSwap_senderContract_revert({destQuery: getDestQueryNoRebate(1 ether)}); + check_bridge_weth_noOriginSwap_senderContract_revert({destQuery: getDestQueryWithRebate(1 ether)}); + // Revert when empty origin sender is encoded into destQuery + check_bridge_weth_noOriginSwap_senderContract_revert({ + destQuery: getDestQueryNoRebateWithOriginSender(1 ether, address(0)) + }); + check_bridge_weth_noOriginSwap_senderContract_revert({ + destQuery: getDestQueryWithRebateWithOriginSender(1 ether, address(0)) + }); + } + + function test_bridge_weth_withOriginUnwrap_senderContract_reverts() public { + // Revert when origin sender is not encoded into destQuery + check_bridge_weth_withOriginUnwrap_senderContract_revert({destQuery: getDestQueryNoRebate(1 ether)}); + check_bridge_weth_withOriginUnwrap_senderContract_revert({destQuery: getDestQueryWithRebate(1 ether)}); + // Revert when empty origin sender is encoded into destQuery + check_bridge_weth_withOriginUnwrap_senderContract_revert({ + destQuery: getDestQueryNoRebateWithOriginSender(1 ether, address(0)) + }); + check_bridge_weth_withOriginUnwrap_senderContract_revert({ + destQuery: getDestQueryWithRebateWithOriginSender(1 ether, address(0)) + }); + } + + function test_bridge_weth_withOriginSwap_senderContract_reverts() public { + // Revert when origin sender is not encoded into destQuery + check_bridge_weth_withOriginSwap_senderContract_revert({destQuery: getDestQueryNoRebate(1 ether)}); + check_bridge_weth_withOriginSwap_senderContract_revert({destQuery: getDestQueryWithRebate(1 ether)}); + // Revert when empty origin sender is encoded into destQuery + check_bridge_weth_withOriginSwap_senderContract_revert({ + destQuery: getDestQueryNoRebateWithOriginSender(1 ether, address(0)) + }); + check_bridge_weth_withOriginSwap_senderContract_revert({ + destQuery: getDestQueryWithRebateWithOriginSender(1 ether, address(0)) + }); + } + + // ═══════════════════ TESTS: START FROM PAIRED TOKEN (CONTRACT, ORIGIN SENDER NOT PROVIDED) ═══════════════════════ + + function check_bridge_token_noOriginSwap_senderContract_revert(SwapQuery memory destQuery) public { + uint256 amount = 1 ether; + expectRevertOriginSenderNotSpecified(); + vm.prank(externalContract); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(token), + amount: amount, + originQuery: getOriginQueryNoSwap(address(token), amount), + destQuery: destQuery + }); + } + + function check_bridge_token_withOriginSwap_senderContract_revert(SwapQuery memory destQuery) public { + uint256 amountBeforeSwap = 1 ether; + uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); + expectRevertOriginSenderNotSpecified(); + vm.prank(externalContract); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(token), + amount: amount, + originQuery: getOriginQueryWithSwap(address(weth), amount), + destQuery: destQuery + }); + } + + function check_bridge_token_withOriginSwapUnwrap_senderContract_revert(SwapQuery memory destQuery) public { + uint256 amountBeforeSwap = 1 ether; + uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); + expectRevertOriginSenderNotSpecified(); + vm.prank(externalContract); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(token), + amount: amount, + originQuery: getOriginQueryWithSwap(ETH, amount), + destQuery: destQuery + }); + } + + function test_bridge_token_noOriginSwap_senderContract_reverts() public { + // Revert when origin sender is not encoded into destQuery + check_bridge_token_noOriginSwap_senderContract_revert({destQuery: getDestQueryNoRebate(1 ether)}); + check_bridge_token_noOriginSwap_senderContract_revert({destQuery: getDestQueryWithRebate(1 ether)}); + // Revert when empty origin sender is encoded into destQuery + check_bridge_token_noOriginSwap_senderContract_revert({ + destQuery: getDestQueryNoRebateWithOriginSender(1 ether, address(0)) + }); + check_bridge_token_noOriginSwap_senderContract_revert({ + destQuery: getDestQueryWithRebateWithOriginSender(1 ether, address(0)) + }); + } + + function test_bridge_token_withOriginSwap_senderContract_reverts() public { + // Revert when origin sender is not encoded into destQuery + check_bridge_token_withOriginSwap_senderContract_revert({destQuery: getDestQueryNoRebate(1 ether)}); + check_bridge_token_withOriginSwap_senderContract_revert({destQuery: getDestQueryWithRebate(1 ether)}); + // Revert when empty origin sender is encoded into destQuery + check_bridge_token_withOriginSwap_senderContract_revert({ + destQuery: getDestQueryNoRebateWithOriginSender(1 ether, address(0)) + }); + check_bridge_token_withOriginSwap_senderContract_revert({ + destQuery: getDestQueryWithRebateWithOriginSender(1 ether, address(0)) + }); + } + + function test_bridge_token_withOriginSwapUnwrap_senderContract_reverts() public { + // Revert when origin sender is not encoded into destQuery + check_bridge_token_withOriginSwapUnwrap_senderContract_revert({destQuery: getDestQueryNoRebate(1 ether)}); + check_bridge_token_withOriginSwapUnwrap_senderContract_revert({destQuery: getDestQueryWithRebate(1 ether)}); + // Revert when empty origin sender is encoded into destQuery + check_bridge_token_withOriginSwapUnwrap_senderContract_revert({ + destQuery: getDestQueryNoRebateWithOriginSender(1 ether, address(0)) + }); + check_bridge_token_withOriginSwapUnwrap_senderContract_revert({ + destQuery: getDestQueryWithRebateWithOriginSender(1 ether, address(0)) + }); + } +} From ca314f717536e5bc624c6fb131d9bb4d677613d9 Mon Sep 17 00:00:00 2001 From: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> Date: Fri, 26 Jul 2024 12:08:35 +0100 Subject: [PATCH 08/16] refactor: EOA fallback --- contracts/rfq/FastBridgeRouterV2.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/rfq/FastBridgeRouterV2.sol b/contracts/rfq/FastBridgeRouterV2.sol index dcbef5375..cf632aa2c 100644 --- a/contracts/rfq/FastBridgeRouterV2.sol +++ b/contracts/rfq/FastBridgeRouterV2.sol @@ -119,10 +119,10 @@ contract FastBridgeRouterV2 is DefaultRouter, Ownable, IFastBridgeRouter { originSender := shr(96, originSender) } } - if (originSender == address(0)) { + if (originSender == address(0) && msg.sender.code.length == 0) { // We fallback to msg.sender if that is EOA. This establishes backwards compatibility // for cases where we can safely assume that the origin sender is the same as msg.sender. - return msg.sender.code.length == 0 ? msg.sender : address(0); + originSender = msg.sender; } } From 112873dfee6bb4928a68d85931b3107e28f81c84 Mon Sep 17 00:00:00 2001 From: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> Date: Mon, 29 Jul 2024 11:37:10 +0200 Subject: [PATCH 09/16] chore: better docs for `_getOriginSender` --- contracts/rfq/FastBridgeRouterV2.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/rfq/FastBridgeRouterV2.sol b/contracts/rfq/FastBridgeRouterV2.sol index cf632aa2c..2175d9c4c 100644 --- a/contracts/rfq/FastBridgeRouterV2.sol +++ b/contracts/rfq/FastBridgeRouterV2.sol @@ -113,14 +113,14 @@ contract FastBridgeRouterV2 is DefaultRouter, Ownable, IFastBridgeRouter { // The easiest way to read from memory is to use assembly // solhint-disable-next-line no-inline-assembly assembly { - // We need to skip the rawParams.length (32 bytes) and the rebate flag (1 byte) + // Skip the rawParams.length (32 bytes) and the rebate flag (1 byte) originSender := mload(add(rawParams, 33)) - // Now We have the address in the highest 160 bits. Shift right by 96 to get it in the lowest 160 bits + // The address is in the highest 160 bits. Shift right by 96 to get it in the lowest 160 bits originSender := shr(96, originSender) } } if (originSender == address(0) && msg.sender.code.length == 0) { - // We fallback to msg.sender if that is EOA. This establishes backwards compatibility + // Fall back to msg.sender if it is an EOA. This maintains backward compatibility // for cases where we can safely assume that the origin sender is the same as msg.sender. originSender = msg.sender; } From 9de0434836c27705165d48c085bb55d0fdf01503 Mon Sep 17 00:00:00 2001 From: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> Date: Mon, 29 Jul 2024 17:12:50 +0200 Subject: [PATCH 10/16] feat: FBRv2 deploy, configure scripts --- script/configs/Create2Factory.salts.json | 5 ++++ script/rfq-run.sh | 26 +++++++++++++++++++ ....sol => ConfigureFastBridgeRouterV2.s.sol} | 24 ++++++++--------- script/rfq/DeployFastBridgeRouterV2.s.sol | 20 ++++++++++++++ 4 files changed, 62 insertions(+), 13 deletions(-) create mode 100755 script/rfq-run.sh rename script/rfq/{ConfigureFastBridgeRouter.t.sol => ConfigureFastBridgeRouterV2.s.sol} (61%) create mode 100644 script/rfq/DeployFastBridgeRouterV2.s.sol diff --git a/script/configs/Create2Factory.salts.json b/script/configs/Create2Factory.salts.json index 6a67b0080..b0a579f5b 100644 --- a/script/configs/Create2Factory.salts.json +++ b/script/configs/Create2Factory.salts.json @@ -9,6 +9,11 @@ "predictedAddress": "0x0000000000489d89D2B233D3375C045dfD05745F", "salt": "0x0000000000000000000000000000000000000000a887a859fa44720198162f0e" }, + "FastBridgeRouterV2": { + "initCodeHash": "0xa51b9e280aa2f3031dee11ee332b050e56252928afbbead89d5b3c2e5c1750ef", + "predictedAddress": "0xd50042193Db100FE0040005e00D5010000007e45", + "salt": "0x00000000000000000000000000000000000000006137d2b1087774010a8fadb4" + }, "SynapseRouter": { "initCodeHash": "0x4e3652671a68c0263a6844cbfabcab93d1a951bdc4b7983183b99f74308383e0", "predictedAddress": "0x0000000000365b1d5B142732CF4d33BcddED21Fc", diff --git a/script/rfq-run.sh b/script/rfq-run.sh new file mode 100755 index 000000000..0af50e48b --- /dev/null +++ b/script/rfq-run.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +# This script runs an RFQ script for all chains with FastBridge deployment +# Usage: ./script/rfq-run.sh pathToScript +# - ./script/run.sh pathToScript chain will be run for all RFQ chains + +# Colors +RED="\033[0;31m" +NC="\033[0m" # No Color + +scriptFN=$1 +# Get the rest of the args +shift 1 +# Check that all required args exist +if [ -z "$scriptFN" ]; then + echo -e "${RED}Usage: ./script/rfq-run.sh pathToScript ${NC}" + exit 1 +fi + +# Find all FastBridge.json files in ./deployments +fastBridgeDeployments=$(find ./deployments -name FastBridge.json) +# Extract chain name from the list of filenames, sort alphabetically +chainNames=$(echo "$fastBridgeDeployments" | sed 's/.*\/\(.*\)\/FastBridge.json/\1/' | sort) + +for chainName in $chainNames; do + ./script/run.sh "$scriptFN" "$chainName" "$@" +done diff --git a/script/rfq/ConfigureFastBridgeRouter.t.sol b/script/rfq/ConfigureFastBridgeRouterV2.s.sol similarity index 61% rename from script/rfq/ConfigureFastBridgeRouter.t.sol rename to script/rfq/ConfigureFastBridgeRouterV2.s.sol index 75347e8d8..e20d1b1d5 100644 --- a/script/rfq/ConfigureFastBridgeRouter.t.sol +++ b/script/rfq/ConfigureFastBridgeRouterV2.s.sol @@ -1,40 +1,38 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; -import {FastBridgeRouter} from "../../contracts/rfq/FastBridgeRouter.sol"; +import {FastBridgeRouterV2} from "../../contracts/rfq/FastBridgeRouterV2.sol"; import {console2, BasicSynapseScript} from "../templates/BasicSynapse.s.sol"; -contract ConfigureFastBridgeRouter is BasicSynapseScript { - string public constant FAST_BRIDGE_ROUTER = "FastBridgeRouter"; - - FastBridgeRouter public fastBridgeRouter; +contract ConfigureFastBridgeRouterV2 is BasicSynapseScript { + FastBridgeRouterV2 public router; function setUp() internal override { super.setUp(); - address payable routerDeployment = payable(getDeploymentAddress(FAST_BRIDGE_ROUTER)); - fastBridgeRouter = FastBridgeRouter(routerDeployment); + address payable routerDeployment = payable(getDeploymentAddress("FastBridgeRouterV2")); + router = FastBridgeRouterV2(routerDeployment); } function run() external { // Setup the BasicSynapseScript setUp(); vm.startBroadcast(); - configureFastBridgeRouter(); + configureFastBridgeRouterV2(); vm.stopBroadcast(); } - function configureFastBridgeRouter() internal { + function configureFastBridgeRouterV2() internal { address fastBridge = getDeploymentAddress("FastBridge"); - if (fastBridgeRouter.fastBridge() != fastBridge) { - fastBridgeRouter.setFastBridge(fastBridge); + if (router.fastBridge() != fastBridge) { + router.setFastBridge(fastBridge); printLog(string.concat(unicode"✅ Fast bridge set to ", vm.toString(fastBridge))); } else { printLog(string.concat(unicode"🟡 Skipping: Fast bridge is already set to ", vm.toString(fastBridge))); } address swapQuoter = getDeploymentAddress("SwapQuoterV2"); - if (fastBridgeRouter.swapQuoter() != swapQuoter) { - fastBridgeRouter.setSwapQuoter(swapQuoter); + if (router.swapQuoter() != swapQuoter) { + router.setSwapQuoter(swapQuoter); printLog(string.concat(unicode"✅ SwapQuoter set to ", vm.toString(swapQuoter))); } else { printLog(string.concat(unicode"🟡 Skipping: SwapQuoter is already set to ", vm.toString(swapQuoter))); diff --git a/script/rfq/DeployFastBridgeRouterV2.s.sol b/script/rfq/DeployFastBridgeRouterV2.s.sol new file mode 100644 index 000000000..201d8af1a --- /dev/null +++ b/script/rfq/DeployFastBridgeRouterV2.s.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import {BasicSynapseScript} from "../templates/BasicSynapse.s.sol"; + +contract DeployFastBridgeRouter is BasicSynapseScript { + function run() external { + // Setup the BasicSynapseScript + setUp(); + vm.startBroadcast(); + // Use `deployCreate2` as callback to deploy the contract with CREATE2 + // This will load deployment salt from the pre-saved list, if there's an entry for the contract + deployAndSave({ + contractName: "FastBridgeRouterV2", + constructorArgs: abi.encode(msg.sender), + deployCode: deployCreate2 + }); + vm.stopBroadcast(); + } +} From cfdc7db1fb3345d3a280dc304bfcc48c0322df2e Mon Sep 17 00:00:00 2001 From: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> Date: Tue, 30 Jul 2024 12:15:42 +0200 Subject: [PATCH 11/16] build: update network-specific options --- script/networks.json | 4 ---- script/rfq-run.sh | 3 +++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/script/networks.json b/script/networks.json index 86d15c107..b4c207f48 100644 --- a/script/networks.json +++ b/script/networks.json @@ -1,9 +1,6 @@ { "forgeOptions": { - "arbitrum": "--with-gas-price 100000000", "aurora": "--legacy --slow", - "base": "--with-gas-price 100000000", - "blast": "--with-gas-price 100000000", "boba": "--legacy --skip-simulation --slow", "canto": "--legacy --slow", "cronos": "--slow", @@ -14,7 +11,6 @@ "metis": "--legacy", "moonbeam": "--skip-simulation --slow", "moonriver": "--skip-simulation --slow", - "optimism": "--with-gas-price 1000000", "polygon": "--slow" } } diff --git a/script/rfq-run.sh b/script/rfq-run.sh index 0af50e48b..ac387886a 100755 --- a/script/rfq-run.sh +++ b/script/rfq-run.sh @@ -20,6 +20,9 @@ fi fastBridgeDeployments=$(find ./deployments -name FastBridge.json) # Extract chain name from the list of filenames, sort alphabetically chainNames=$(echo "$fastBridgeDeployments" | sed 's/.*\/\(.*\)\/FastBridge.json/\1/' | sort) +# Print the comma separated list of chain aliases, don't put a comma after the last one +chainNamesFormatted=$(echo "$chainNames" | sed ':a;N;$!ba;s/\n/, /g') +echo "Running script $scriptFN for chains: [$chainNamesFormatted]" for chainName in $chainNames; do ./script/run.sh "$scriptFN" "$chainName" "$@" From a9cd93d1f63c24d547dd7ed48611a2bae4d9ff22 Mon Sep 17 00:00:00 2001 From: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> Date: Tue, 30 Jul 2024 12:45:33 +0200 Subject: [PATCH 12/16] deploy: FBR v2 --- deployments/arbitrum/FastBridgeRouterV2.json | 396 +++++++++++++++++++ deployments/base/FastBridgeRouterV2.json | 396 +++++++++++++++++++ deployments/blast/FastBridgeRouterV2.json | 396 +++++++++++++++++++ deployments/bsc/FastBridgeRouterV2.json | 396 +++++++++++++++++++ deployments/linea/FastBridgeRouterV2.json | 396 +++++++++++++++++++ deployments/mainnet/FastBridgeRouterV2.json | 396 +++++++++++++++++++ deployments/optimism/FastBridgeRouterV2.json | 396 +++++++++++++++++++ deployments/scroll/FastBridgeRouterV2.json | 396 +++++++++++++++++++ 8 files changed, 3168 insertions(+) create mode 100644 deployments/arbitrum/FastBridgeRouterV2.json create mode 100644 deployments/base/FastBridgeRouterV2.json create mode 100644 deployments/blast/FastBridgeRouterV2.json create mode 100644 deployments/bsc/FastBridgeRouterV2.json create mode 100644 deployments/linea/FastBridgeRouterV2.json create mode 100644 deployments/mainnet/FastBridgeRouterV2.json create mode 100644 deployments/optimism/FastBridgeRouterV2.json create mode 100644 deployments/scroll/FastBridgeRouterV2.json diff --git a/deployments/arbitrum/FastBridgeRouterV2.json b/deployments/arbitrum/FastBridgeRouterV2.json new file mode 100644 index 000000000..2466ae177 --- /dev/null +++ b/deployments/arbitrum/FastBridgeRouterV2.json @@ -0,0 +1,396 @@ +{ + "address": "0xd50042193Db100FE0040005e00D5010000007e45", + "constructorArgs": "0x0000000000000000000000000fea3e5840334fc758a3decf14546bfdfbef5cd3", + "abi": [ + { + "type": "constructor", + "inputs": [ + { + "name": "owner_", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "receive", + "stateMutability": "payable" + }, + { + "type": "function", + "name": "GAS_REBATE_FLAG", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes1", + "internalType": "bytes1" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "adapterSwap", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenIn", + "type": "address", + "internalType": "address" + }, + { + "name": "amountIn", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "amountOut", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "bridge", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "chainId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "token", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "originQuery", + "type": "tuple", + "internalType": "struct SwapQuery", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + }, + { + "name": "destQuery", + "type": "tuple", + "internalType": "struct SwapQuery", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "fastBridge", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getOriginAmountOut", + "inputs": [ + { + "name": "tokenIn", + "type": "address", + "internalType": "address" + }, + { + "name": "rfqTokens", + "type": "address[]", + "internalType": "address[]" + }, + { + "name": "amountIn", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "originQueries", + "type": "tuple[]", + "internalType": "struct SwapQuery[]", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "renounceOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setFastBridge", + "inputs": [ + { + "name": "fastBridge_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setSwapQuoter", + "inputs": [ + { + "name": "swapQuoter_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "swapQuoter", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "transferOwnership", + "inputs": [ + { + "name": "newOwner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "FastBridgeSet", + "inputs": [ + { + "name": "newFastBridge", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnershipTransferred", + "inputs": [ + { + "name": "previousOwner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newOwner", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SwapQuoterSet", + "inputs": [ + { + "name": "newSwapQuoter", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "DeadlineExceeded", + "inputs": [] + }, + { + "type": "error", + "name": "FastBridgeRouterV2__OriginSenderNotSpecified", + "inputs": [] + }, + { + "type": "error", + "name": "InsufficientOutputAmount", + "inputs": [] + }, + { + "type": "error", + "name": "MsgValueIncorrect", + "inputs": [] + }, + { + "type": "error", + "name": "PoolNotFound", + "inputs": [] + }, + { + "type": "error", + "name": "TokenAddressMismatch", + "inputs": [] + }, + { + "type": "error", + "name": "TokenNotContract", + "inputs": [] + }, + { + "type": "error", + "name": "TokenNotETH", + "inputs": [] + }, + { + "type": "error", + "name": "TokensIdentical", + "inputs": [] + } + ] +} \ No newline at end of file diff --git a/deployments/base/FastBridgeRouterV2.json b/deployments/base/FastBridgeRouterV2.json new file mode 100644 index 000000000..2466ae177 --- /dev/null +++ b/deployments/base/FastBridgeRouterV2.json @@ -0,0 +1,396 @@ +{ + "address": "0xd50042193Db100FE0040005e00D5010000007e45", + "constructorArgs": "0x0000000000000000000000000fea3e5840334fc758a3decf14546bfdfbef5cd3", + "abi": [ + { + "type": "constructor", + "inputs": [ + { + "name": "owner_", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "receive", + "stateMutability": "payable" + }, + { + "type": "function", + "name": "GAS_REBATE_FLAG", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes1", + "internalType": "bytes1" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "adapterSwap", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenIn", + "type": "address", + "internalType": "address" + }, + { + "name": "amountIn", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "amountOut", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "bridge", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "chainId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "token", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "originQuery", + "type": "tuple", + "internalType": "struct SwapQuery", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + }, + { + "name": "destQuery", + "type": "tuple", + "internalType": "struct SwapQuery", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "fastBridge", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getOriginAmountOut", + "inputs": [ + { + "name": "tokenIn", + "type": "address", + "internalType": "address" + }, + { + "name": "rfqTokens", + "type": "address[]", + "internalType": "address[]" + }, + { + "name": "amountIn", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "originQueries", + "type": "tuple[]", + "internalType": "struct SwapQuery[]", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "renounceOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setFastBridge", + "inputs": [ + { + "name": "fastBridge_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setSwapQuoter", + "inputs": [ + { + "name": "swapQuoter_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "swapQuoter", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "transferOwnership", + "inputs": [ + { + "name": "newOwner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "FastBridgeSet", + "inputs": [ + { + "name": "newFastBridge", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnershipTransferred", + "inputs": [ + { + "name": "previousOwner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newOwner", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SwapQuoterSet", + "inputs": [ + { + "name": "newSwapQuoter", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "DeadlineExceeded", + "inputs": [] + }, + { + "type": "error", + "name": "FastBridgeRouterV2__OriginSenderNotSpecified", + "inputs": [] + }, + { + "type": "error", + "name": "InsufficientOutputAmount", + "inputs": [] + }, + { + "type": "error", + "name": "MsgValueIncorrect", + "inputs": [] + }, + { + "type": "error", + "name": "PoolNotFound", + "inputs": [] + }, + { + "type": "error", + "name": "TokenAddressMismatch", + "inputs": [] + }, + { + "type": "error", + "name": "TokenNotContract", + "inputs": [] + }, + { + "type": "error", + "name": "TokenNotETH", + "inputs": [] + }, + { + "type": "error", + "name": "TokensIdentical", + "inputs": [] + } + ] +} \ No newline at end of file diff --git a/deployments/blast/FastBridgeRouterV2.json b/deployments/blast/FastBridgeRouterV2.json new file mode 100644 index 000000000..2466ae177 --- /dev/null +++ b/deployments/blast/FastBridgeRouterV2.json @@ -0,0 +1,396 @@ +{ + "address": "0xd50042193Db100FE0040005e00D5010000007e45", + "constructorArgs": "0x0000000000000000000000000fea3e5840334fc758a3decf14546bfdfbef5cd3", + "abi": [ + { + "type": "constructor", + "inputs": [ + { + "name": "owner_", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "receive", + "stateMutability": "payable" + }, + { + "type": "function", + "name": "GAS_REBATE_FLAG", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes1", + "internalType": "bytes1" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "adapterSwap", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenIn", + "type": "address", + "internalType": "address" + }, + { + "name": "amountIn", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "amountOut", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "bridge", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "chainId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "token", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "originQuery", + "type": "tuple", + "internalType": "struct SwapQuery", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + }, + { + "name": "destQuery", + "type": "tuple", + "internalType": "struct SwapQuery", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "fastBridge", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getOriginAmountOut", + "inputs": [ + { + "name": "tokenIn", + "type": "address", + "internalType": "address" + }, + { + "name": "rfqTokens", + "type": "address[]", + "internalType": "address[]" + }, + { + "name": "amountIn", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "originQueries", + "type": "tuple[]", + "internalType": "struct SwapQuery[]", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "renounceOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setFastBridge", + "inputs": [ + { + "name": "fastBridge_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setSwapQuoter", + "inputs": [ + { + "name": "swapQuoter_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "swapQuoter", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "transferOwnership", + "inputs": [ + { + "name": "newOwner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "FastBridgeSet", + "inputs": [ + { + "name": "newFastBridge", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnershipTransferred", + "inputs": [ + { + "name": "previousOwner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newOwner", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SwapQuoterSet", + "inputs": [ + { + "name": "newSwapQuoter", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "DeadlineExceeded", + "inputs": [] + }, + { + "type": "error", + "name": "FastBridgeRouterV2__OriginSenderNotSpecified", + "inputs": [] + }, + { + "type": "error", + "name": "InsufficientOutputAmount", + "inputs": [] + }, + { + "type": "error", + "name": "MsgValueIncorrect", + "inputs": [] + }, + { + "type": "error", + "name": "PoolNotFound", + "inputs": [] + }, + { + "type": "error", + "name": "TokenAddressMismatch", + "inputs": [] + }, + { + "type": "error", + "name": "TokenNotContract", + "inputs": [] + }, + { + "type": "error", + "name": "TokenNotETH", + "inputs": [] + }, + { + "type": "error", + "name": "TokensIdentical", + "inputs": [] + } + ] +} \ No newline at end of file diff --git a/deployments/bsc/FastBridgeRouterV2.json b/deployments/bsc/FastBridgeRouterV2.json new file mode 100644 index 000000000..2466ae177 --- /dev/null +++ b/deployments/bsc/FastBridgeRouterV2.json @@ -0,0 +1,396 @@ +{ + "address": "0xd50042193Db100FE0040005e00D5010000007e45", + "constructorArgs": "0x0000000000000000000000000fea3e5840334fc758a3decf14546bfdfbef5cd3", + "abi": [ + { + "type": "constructor", + "inputs": [ + { + "name": "owner_", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "receive", + "stateMutability": "payable" + }, + { + "type": "function", + "name": "GAS_REBATE_FLAG", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes1", + "internalType": "bytes1" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "adapterSwap", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenIn", + "type": "address", + "internalType": "address" + }, + { + "name": "amountIn", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "amountOut", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "bridge", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "chainId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "token", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "originQuery", + "type": "tuple", + "internalType": "struct SwapQuery", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + }, + { + "name": "destQuery", + "type": "tuple", + "internalType": "struct SwapQuery", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "fastBridge", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getOriginAmountOut", + "inputs": [ + { + "name": "tokenIn", + "type": "address", + "internalType": "address" + }, + { + "name": "rfqTokens", + "type": "address[]", + "internalType": "address[]" + }, + { + "name": "amountIn", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "originQueries", + "type": "tuple[]", + "internalType": "struct SwapQuery[]", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "renounceOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setFastBridge", + "inputs": [ + { + "name": "fastBridge_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setSwapQuoter", + "inputs": [ + { + "name": "swapQuoter_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "swapQuoter", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "transferOwnership", + "inputs": [ + { + "name": "newOwner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "FastBridgeSet", + "inputs": [ + { + "name": "newFastBridge", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnershipTransferred", + "inputs": [ + { + "name": "previousOwner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newOwner", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SwapQuoterSet", + "inputs": [ + { + "name": "newSwapQuoter", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "DeadlineExceeded", + "inputs": [] + }, + { + "type": "error", + "name": "FastBridgeRouterV2__OriginSenderNotSpecified", + "inputs": [] + }, + { + "type": "error", + "name": "InsufficientOutputAmount", + "inputs": [] + }, + { + "type": "error", + "name": "MsgValueIncorrect", + "inputs": [] + }, + { + "type": "error", + "name": "PoolNotFound", + "inputs": [] + }, + { + "type": "error", + "name": "TokenAddressMismatch", + "inputs": [] + }, + { + "type": "error", + "name": "TokenNotContract", + "inputs": [] + }, + { + "type": "error", + "name": "TokenNotETH", + "inputs": [] + }, + { + "type": "error", + "name": "TokensIdentical", + "inputs": [] + } + ] +} \ No newline at end of file diff --git a/deployments/linea/FastBridgeRouterV2.json b/deployments/linea/FastBridgeRouterV2.json new file mode 100644 index 000000000..2466ae177 --- /dev/null +++ b/deployments/linea/FastBridgeRouterV2.json @@ -0,0 +1,396 @@ +{ + "address": "0xd50042193Db100FE0040005e00D5010000007e45", + "constructorArgs": "0x0000000000000000000000000fea3e5840334fc758a3decf14546bfdfbef5cd3", + "abi": [ + { + "type": "constructor", + "inputs": [ + { + "name": "owner_", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "receive", + "stateMutability": "payable" + }, + { + "type": "function", + "name": "GAS_REBATE_FLAG", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes1", + "internalType": "bytes1" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "adapterSwap", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenIn", + "type": "address", + "internalType": "address" + }, + { + "name": "amountIn", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "amountOut", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "bridge", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "chainId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "token", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "originQuery", + "type": "tuple", + "internalType": "struct SwapQuery", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + }, + { + "name": "destQuery", + "type": "tuple", + "internalType": "struct SwapQuery", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "fastBridge", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getOriginAmountOut", + "inputs": [ + { + "name": "tokenIn", + "type": "address", + "internalType": "address" + }, + { + "name": "rfqTokens", + "type": "address[]", + "internalType": "address[]" + }, + { + "name": "amountIn", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "originQueries", + "type": "tuple[]", + "internalType": "struct SwapQuery[]", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "renounceOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setFastBridge", + "inputs": [ + { + "name": "fastBridge_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setSwapQuoter", + "inputs": [ + { + "name": "swapQuoter_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "swapQuoter", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "transferOwnership", + "inputs": [ + { + "name": "newOwner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "FastBridgeSet", + "inputs": [ + { + "name": "newFastBridge", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnershipTransferred", + "inputs": [ + { + "name": "previousOwner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newOwner", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SwapQuoterSet", + "inputs": [ + { + "name": "newSwapQuoter", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "DeadlineExceeded", + "inputs": [] + }, + { + "type": "error", + "name": "FastBridgeRouterV2__OriginSenderNotSpecified", + "inputs": [] + }, + { + "type": "error", + "name": "InsufficientOutputAmount", + "inputs": [] + }, + { + "type": "error", + "name": "MsgValueIncorrect", + "inputs": [] + }, + { + "type": "error", + "name": "PoolNotFound", + "inputs": [] + }, + { + "type": "error", + "name": "TokenAddressMismatch", + "inputs": [] + }, + { + "type": "error", + "name": "TokenNotContract", + "inputs": [] + }, + { + "type": "error", + "name": "TokenNotETH", + "inputs": [] + }, + { + "type": "error", + "name": "TokensIdentical", + "inputs": [] + } + ] +} \ No newline at end of file diff --git a/deployments/mainnet/FastBridgeRouterV2.json b/deployments/mainnet/FastBridgeRouterV2.json new file mode 100644 index 000000000..2466ae177 --- /dev/null +++ b/deployments/mainnet/FastBridgeRouterV2.json @@ -0,0 +1,396 @@ +{ + "address": "0xd50042193Db100FE0040005e00D5010000007e45", + "constructorArgs": "0x0000000000000000000000000fea3e5840334fc758a3decf14546bfdfbef5cd3", + "abi": [ + { + "type": "constructor", + "inputs": [ + { + "name": "owner_", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "receive", + "stateMutability": "payable" + }, + { + "type": "function", + "name": "GAS_REBATE_FLAG", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes1", + "internalType": "bytes1" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "adapterSwap", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenIn", + "type": "address", + "internalType": "address" + }, + { + "name": "amountIn", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "amountOut", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "bridge", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "chainId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "token", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "originQuery", + "type": "tuple", + "internalType": "struct SwapQuery", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + }, + { + "name": "destQuery", + "type": "tuple", + "internalType": "struct SwapQuery", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "fastBridge", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getOriginAmountOut", + "inputs": [ + { + "name": "tokenIn", + "type": "address", + "internalType": "address" + }, + { + "name": "rfqTokens", + "type": "address[]", + "internalType": "address[]" + }, + { + "name": "amountIn", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "originQueries", + "type": "tuple[]", + "internalType": "struct SwapQuery[]", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "renounceOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setFastBridge", + "inputs": [ + { + "name": "fastBridge_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setSwapQuoter", + "inputs": [ + { + "name": "swapQuoter_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "swapQuoter", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "transferOwnership", + "inputs": [ + { + "name": "newOwner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "FastBridgeSet", + "inputs": [ + { + "name": "newFastBridge", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnershipTransferred", + "inputs": [ + { + "name": "previousOwner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newOwner", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SwapQuoterSet", + "inputs": [ + { + "name": "newSwapQuoter", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "DeadlineExceeded", + "inputs": [] + }, + { + "type": "error", + "name": "FastBridgeRouterV2__OriginSenderNotSpecified", + "inputs": [] + }, + { + "type": "error", + "name": "InsufficientOutputAmount", + "inputs": [] + }, + { + "type": "error", + "name": "MsgValueIncorrect", + "inputs": [] + }, + { + "type": "error", + "name": "PoolNotFound", + "inputs": [] + }, + { + "type": "error", + "name": "TokenAddressMismatch", + "inputs": [] + }, + { + "type": "error", + "name": "TokenNotContract", + "inputs": [] + }, + { + "type": "error", + "name": "TokenNotETH", + "inputs": [] + }, + { + "type": "error", + "name": "TokensIdentical", + "inputs": [] + } + ] +} \ No newline at end of file diff --git a/deployments/optimism/FastBridgeRouterV2.json b/deployments/optimism/FastBridgeRouterV2.json new file mode 100644 index 000000000..2466ae177 --- /dev/null +++ b/deployments/optimism/FastBridgeRouterV2.json @@ -0,0 +1,396 @@ +{ + "address": "0xd50042193Db100FE0040005e00D5010000007e45", + "constructorArgs": "0x0000000000000000000000000fea3e5840334fc758a3decf14546bfdfbef5cd3", + "abi": [ + { + "type": "constructor", + "inputs": [ + { + "name": "owner_", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "receive", + "stateMutability": "payable" + }, + { + "type": "function", + "name": "GAS_REBATE_FLAG", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes1", + "internalType": "bytes1" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "adapterSwap", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenIn", + "type": "address", + "internalType": "address" + }, + { + "name": "amountIn", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "amountOut", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "bridge", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "chainId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "token", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "originQuery", + "type": "tuple", + "internalType": "struct SwapQuery", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + }, + { + "name": "destQuery", + "type": "tuple", + "internalType": "struct SwapQuery", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "fastBridge", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getOriginAmountOut", + "inputs": [ + { + "name": "tokenIn", + "type": "address", + "internalType": "address" + }, + { + "name": "rfqTokens", + "type": "address[]", + "internalType": "address[]" + }, + { + "name": "amountIn", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "originQueries", + "type": "tuple[]", + "internalType": "struct SwapQuery[]", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "renounceOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setFastBridge", + "inputs": [ + { + "name": "fastBridge_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setSwapQuoter", + "inputs": [ + { + "name": "swapQuoter_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "swapQuoter", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "transferOwnership", + "inputs": [ + { + "name": "newOwner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "FastBridgeSet", + "inputs": [ + { + "name": "newFastBridge", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnershipTransferred", + "inputs": [ + { + "name": "previousOwner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newOwner", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SwapQuoterSet", + "inputs": [ + { + "name": "newSwapQuoter", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "DeadlineExceeded", + "inputs": [] + }, + { + "type": "error", + "name": "FastBridgeRouterV2__OriginSenderNotSpecified", + "inputs": [] + }, + { + "type": "error", + "name": "InsufficientOutputAmount", + "inputs": [] + }, + { + "type": "error", + "name": "MsgValueIncorrect", + "inputs": [] + }, + { + "type": "error", + "name": "PoolNotFound", + "inputs": [] + }, + { + "type": "error", + "name": "TokenAddressMismatch", + "inputs": [] + }, + { + "type": "error", + "name": "TokenNotContract", + "inputs": [] + }, + { + "type": "error", + "name": "TokenNotETH", + "inputs": [] + }, + { + "type": "error", + "name": "TokensIdentical", + "inputs": [] + } + ] +} \ No newline at end of file diff --git a/deployments/scroll/FastBridgeRouterV2.json b/deployments/scroll/FastBridgeRouterV2.json new file mode 100644 index 000000000..2466ae177 --- /dev/null +++ b/deployments/scroll/FastBridgeRouterV2.json @@ -0,0 +1,396 @@ +{ + "address": "0xd50042193Db100FE0040005e00D5010000007e45", + "constructorArgs": "0x0000000000000000000000000fea3e5840334fc758a3decf14546bfdfbef5cd3", + "abi": [ + { + "type": "constructor", + "inputs": [ + { + "name": "owner_", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "receive", + "stateMutability": "payable" + }, + { + "type": "function", + "name": "GAS_REBATE_FLAG", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "bytes1", + "internalType": "bytes1" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "adapterSwap", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenIn", + "type": "address", + "internalType": "address" + }, + { + "name": "amountIn", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [ + { + "name": "amountOut", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "bridge", + "inputs": [ + { + "name": "recipient", + "type": "address", + "internalType": "address" + }, + { + "name": "chainId", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "token", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "originQuery", + "type": "tuple", + "internalType": "struct SwapQuery", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + }, + { + "name": "destQuery", + "type": "tuple", + "internalType": "struct SwapQuery", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "outputs": [], + "stateMutability": "payable" + }, + { + "type": "function", + "name": "fastBridge", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getOriginAmountOut", + "inputs": [ + { + "name": "tokenIn", + "type": "address", + "internalType": "address" + }, + { + "name": "rfqTokens", + "type": "address[]", + "internalType": "address[]" + }, + { + "name": "amountIn", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "originQueries", + "type": "tuple[]", + "internalType": "struct SwapQuery[]", + "components": [ + { + "name": "routerAdapter", + "type": "address", + "internalType": "address" + }, + { + "name": "tokenOut", + "type": "address", + "internalType": "address" + }, + { + "name": "minAmountOut", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "deadline", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "rawParams", + "type": "bytes", + "internalType": "bytes" + } + ] + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "owner", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "renounceOwnership", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setFastBridge", + "inputs": [ + { + "name": "fastBridge_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setSwapQuoter", + "inputs": [ + { + "name": "swapQuoter_", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "swapQuoter", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "transferOwnership", + "inputs": [ + { + "name": "newOwner", + "type": "address", + "internalType": "address" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "FastBridgeSet", + "inputs": [ + { + "name": "newFastBridge", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "OwnershipTransferred", + "inputs": [ + { + "name": "previousOwner", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "newOwner", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SwapQuoterSet", + "inputs": [ + { + "name": "newSwapQuoter", + "type": "address", + "indexed": false, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "error", + "name": "DeadlineExceeded", + "inputs": [] + }, + { + "type": "error", + "name": "FastBridgeRouterV2__OriginSenderNotSpecified", + "inputs": [] + }, + { + "type": "error", + "name": "InsufficientOutputAmount", + "inputs": [] + }, + { + "type": "error", + "name": "MsgValueIncorrect", + "inputs": [] + }, + { + "type": "error", + "name": "PoolNotFound", + "inputs": [] + }, + { + "type": "error", + "name": "TokenAddressMismatch", + "inputs": [] + }, + { + "type": "error", + "name": "TokenNotContract", + "inputs": [] + }, + { + "type": "error", + "name": "TokenNotETH", + "inputs": [] + }, + { + "type": "error", + "name": "TokensIdentical", + "inputs": [] + } + ] +} \ No newline at end of file From 4cc42b3897bb2ec5688adaab4dbb2797b89c960b Mon Sep 17 00:00:00 2001 From: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> Date: Tue, 30 Jul 2024 16:33:03 +0200 Subject: [PATCH 13/16] test: add cases for deadline/minAmountOut --- test/rfq/FastBridgeRouterV2.Native.t.sol | 299 +++++++++++++++++++++++ test/rfq/FastBridgeRouterV2.t.sol | 69 ++++++ 2 files changed, 368 insertions(+) diff --git a/test/rfq/FastBridgeRouterV2.Native.t.sol b/test/rfq/FastBridgeRouterV2.Native.t.sol index fc719d636..3927feafb 100644 --- a/test/rfq/FastBridgeRouterV2.Native.t.sol +++ b/test/rfq/FastBridgeRouterV2.Native.t.sol @@ -2,6 +2,7 @@ pragma solidity 0.8.17; import {FastBridgeRouterV2} from "../../contracts/rfq/FastBridgeRouterV2.sol"; +import {DeadlineExceeded, InsufficientOutputAmount} from "../../contracts/router/DefaultRouter.sol"; import {MockSenderContract} from "../mocks/MockSenderContract.sol"; @@ -820,4 +821,302 @@ abstract contract FastBridgeRouterV2NativeTest is FastBridgeRouterNativeTest { destQuery: getDestQueryWithRebateWithOriginSender(1 ether, address(0)) }); } + + // ═══════════════════════════════════════════════ MISC REVERTS ════════════════════════════════════════════════════ + + function test_bridge_eth_noOriginSwap_revert_deadlineExceeded() public { + uint256 amount = 1 ether; + SwapQuery memory originQuery = getOriginQueryNoSwap(ETH, amount); + originQuery.deadline = block.timestamp - 1; + vm.expectRevert(DeadlineExceeded.selector); + vm.prank(user); + router.bridge{value: amount}({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: ETH, + amount: amount, + originQuery: originQuery, + destQuery: getDestQueryNoRebate(amount) + }); + } + + function test_bridge_eth_withOriginWrap_revert_deadlineExceeded() public { + uint256 amount = 1 ether; + SwapQuery memory originQuery = getOriginQueryWithHandleETH(address(weth), amount); + originQuery.deadline = block.timestamp - 1; + vm.expectRevert(DeadlineExceeded.selector); + vm.prank(user); + router.bridge{value: amount}({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: ETH, + amount: amount, + originQuery: originQuery, + destQuery: getDestQueryNoRebate(amount) + }); + } + + function test_bridge_eth_withOriginSwap_revert_deadlineExceeded() public { + uint256 amountBeforeSwap = 1 ether; + uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); + SwapQuery memory originQuery = getOriginQueryWithSwap(address(weth), amount); + originQuery.deadline = block.timestamp - 1; + vm.expectRevert(DeadlineExceeded.selector); + vm.prank(user); + router.bridge{value: amountBeforeSwap}({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: ETH, + amount: amountBeforeSwap, + originQuery: originQuery, + destQuery: getDestQueryNoRebate(amount) + }); + } + + function test_bridge_weth_noOriginSwap_revert_deadlineExceeded() public { + uint256 amount = 1 ether; + SwapQuery memory originQuery = getOriginQueryNoSwap(address(weth), amount); + originQuery.deadline = block.timestamp - 1; + vm.expectRevert(DeadlineExceeded.selector); + vm.prank(user); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(weth), + amount: amount, + originQuery: originQuery, + destQuery: getDestQueryNoRebate(amount) + }); + } + + function test_bridge_weth_withOriginUnwrap_revert_deadlineExceeded() public { + uint256 amount = 1 ether; + SwapQuery memory originQuery = getOriginQueryWithHandleETH(ETH, amount); + originQuery.deadline = block.timestamp - 1; + vm.expectRevert(DeadlineExceeded.selector); + vm.prank(user); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: ETH, + amount: amount, + originQuery: originQuery, + destQuery: getDestQueryNoRebate(amount) + }); + } + + function test_bridge_weth_withOriginSwap_revert_deadlineExceeded() public { + uint256 amountBeforeSwap = 1 ether; + uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); + SwapQuery memory originQuery = getOriginQueryWithSwap(address(weth), amount); + originQuery.deadline = block.timestamp - 1; + vm.expectRevert(DeadlineExceeded.selector); + vm.prank(user); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(weth), + amount: amountBeforeSwap, + originQuery: originQuery, + destQuery: getDestQueryNoRebate(amount) + }); + } + + function test_bridge_token_noOriginSwap_revert_deadlineExceeded() public { + uint256 amount = 1 ether; + SwapQuery memory originQuery = getOriginQueryNoSwap(address(token), amount); + originQuery.deadline = block.timestamp - 1; + vm.expectRevert(DeadlineExceeded.selector); + vm.prank(user); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(token), + amount: amount, + originQuery: originQuery, + destQuery: getDestQueryNoRebate(amount) + }); + } + + function test_bridge_token_withOriginSwap_revert_deadlineExceeded() public { + uint256 amountBeforeSwap = 1 ether; + uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); + SwapQuery memory originQuery = getOriginQueryWithSwap(address(weth), amount); + originQuery.deadline = block.timestamp - 1; + vm.expectRevert(DeadlineExceeded.selector); + vm.prank(user); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(token), + amount: amountBeforeSwap, + originQuery: originQuery, + destQuery: getDestQueryNoRebate(amount) + }); + } + + function test_bridge_token_withOriginSwapUnwrap_revert_deadlineExceeded() public { + uint256 amountBeforeSwap = 1 ether; + uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); + SwapQuery memory originQuery = getOriginQueryWithSwap(ETH, amount); + originQuery.deadline = block.timestamp - 1; + vm.expectRevert(DeadlineExceeded.selector); + vm.prank(user); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(token), + amount: amountBeforeSwap, + originQuery: originQuery, + destQuery: getDestQueryNoRebate(amount) + }); + } + + function test_bridge_eth_noOriginSwap_revert_insufficientOutputAmount() public { + uint256 amount = 1 ether; + SwapQuery memory originQuery = getOriginQueryNoSwap(ETH, amount); + originQuery.minAmountOut = amount + 1; + vm.expectRevert(InsufficientOutputAmount.selector); + vm.prank(user); + router.bridge{value: amount}({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: ETH, + amount: amount, + originQuery: originQuery, + destQuery: getDestQueryNoRebate(amount) + }); + } + + function test_bridge_eth_withOriginWrap_revert_insufficientOutputAmount() public { + uint256 amount = 1 ether; + SwapQuery memory originQuery = getOriginQueryWithHandleETH(address(weth), amount); + originQuery.minAmountOut = amount + 1; + vm.expectRevert(InsufficientOutputAmount.selector); + vm.prank(user); + router.bridge{value: amount}({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: ETH, + amount: amount, + originQuery: originQuery, + destQuery: getDestQueryNoRebate(amount) + }); + } + + function test_bridge_eth_withOriginSwap_revert_insufficientOutputAmount() public { + uint256 amountBeforeSwap = 1 ether; + uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); + SwapQuery memory originQuery = getOriginQueryWithSwap(address(token), amount); + originQuery.minAmountOut = amount + 1; + vm.expectRevert(InsufficientOutputAmount.selector); + vm.prank(user); + router.bridge{value: amountBeforeSwap}({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: ETH, + amount: amountBeforeSwap, + originQuery: originQuery, + destQuery: getDestQueryNoRebate(amount) + }); + } + + function test_bridge_weth_noOriginSwap_revert_insufficientOutputAmount() public { + uint256 amount = 1 ether; + SwapQuery memory originQuery = getOriginQueryNoSwap(address(weth), amount); + originQuery.minAmountOut = amount + 1; + vm.expectRevert(InsufficientOutputAmount.selector); + vm.prank(user); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(weth), + amount: amount, + originQuery: originQuery, + destQuery: getDestQueryNoRebate(amount) + }); + } + + function test_bridge_weth_withOriginUnwrap_revert_insufficientOutputAmount() public { + uint256 amount = 1 ether; + SwapQuery memory originQuery = getOriginQueryWithHandleETH(ETH, amount); + originQuery.minAmountOut = amount + 1; + vm.expectRevert(InsufficientOutputAmount.selector); + vm.prank(user); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(weth), + amount: amount, + originQuery: originQuery, + destQuery: getDestQueryNoRebate(amount) + }); + } + + function test_bridge_weth_withOriginSwap_revert_insufficientOutputAmount() public { + uint256 amountBeforeSwap = 1 ether; + uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); + SwapQuery memory originQuery = getOriginQueryWithSwap(address(token), amount); + originQuery.minAmountOut = amount + 1; + vm.expectRevert(InsufficientOutputAmount.selector); + vm.prank(user); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(weth), + amount: amountBeforeSwap, + originQuery: originQuery, + destQuery: getDestQueryNoRebate(amount) + }); + } + + function test_bridge_token_noOriginSwap_revert_insufficientOutputAmount() public { + uint256 amount = 1 ether; + SwapQuery memory originQuery = getOriginQueryNoSwap(address(token), amount); + originQuery.minAmountOut = amount + 1; + vm.expectRevert(InsufficientOutputAmount.selector); + vm.prank(user); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(token), + amount: amount, + originQuery: originQuery, + destQuery: getDestQueryNoRebate(amount) + }); + } + + function test_bridge_token_withOriginSwap_revert_insufficientOutputAmount() public { + uint256 amountBeforeSwap = 1 ether; + uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); + SwapQuery memory originQuery = getOriginQueryWithSwap(address(weth), amount); + originQuery.minAmountOut = amount + 1; + vm.expectRevert(InsufficientOutputAmount.selector); + vm.prank(user); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(token), + amount: amountBeforeSwap, + originQuery: originQuery, + destQuery: getDestQueryNoRebate(amount) + }); + } + + function test_bridge_token_withOriginSwapUnwrap_revert_insufficientOutputAmount() public { + uint256 amountBeforeSwap = 1 ether; + uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); + SwapQuery memory originQuery = getOriginQueryWithSwap(ETH, amount); + originQuery.minAmountOut = amount + 1; + vm.expectRevert(InsufficientOutputAmount.selector); + vm.prank(user); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(token), + amount: amountBeforeSwap, + originQuery: originQuery, + destQuery: getDestQueryNoRebate(amount) + }); + } } diff --git a/test/rfq/FastBridgeRouterV2.t.sol b/test/rfq/FastBridgeRouterV2.t.sol index b29d586a0..794099f82 100644 --- a/test/rfq/FastBridgeRouterV2.t.sol +++ b/test/rfq/FastBridgeRouterV2.t.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; +import {DeadlineExceeded, InsufficientOutputAmount} from "../../contracts/router/DefaultRouter.sol"; import {FastBridgeRouterV2} from "../../contracts/rfq/FastBridgeRouterV2.sol"; import {MockSenderContract} from "../mocks/MockSenderContract.sol"; @@ -227,4 +228,72 @@ abstract contract FastBridgeRouterV2Test is FastBridgeRouterTest { destQuery: getDestQueryWithRebateWithOriginSender(1 ether, address(0)) }); } + + // ═══════════════════════════════════════════════ MISC REVERTS ════════════════════════════════════════════════════ + + function test_bridge_noOriginSwap_revert_deadlineExceeded() public { + uint256 amount = 1 ether; + SwapQuery memory originQuery = getOriginQueryNoSwap(amount); + originQuery.deadline = block.timestamp - 1; + vm.expectRevert(DeadlineExceeded.selector); + vm.prank(user); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(token0), + amount: amount, + originQuery: originQuery, + destQuery: getDestQueryNoRebate(amount) + }); + } + + function test_bridge_withOriginSwap_revert_deadlineExceeded() public { + uint256 amountBeforeSwap = 1 ether; + uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); + SwapQuery memory originQuery = getOriginQueryWithSwap(amount); + originQuery.deadline = block.timestamp - 1; + vm.expectRevert(DeadlineExceeded.selector); + vm.prank(user); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(token0), + amount: amountBeforeSwap, + originQuery: originQuery, + destQuery: getDestQueryNoRebate(amount) + }); + } + + function test_bridge_noOriginSwap_revert_insufficientOutputAmount() public { + uint256 amount = 1 ether; + SwapQuery memory originQuery = getOriginQueryNoSwap(amount); + originQuery.minAmountOut = amount + 1; + vm.expectRevert(InsufficientOutputAmount.selector); + vm.prank(user); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(token0), + amount: amount, + originQuery: originQuery, + destQuery: getDestQueryNoRebate(amount) + }); + } + + function test_bridge_withOriginSwap_revert_insufficientOutputAmount() public { + uint256 amountBeforeSwap = 1 ether; + uint256 amount = pool.calculateSwap(0, 1, amountBeforeSwap); + SwapQuery memory originQuery = getOriginQueryWithSwap(amount); + originQuery.minAmountOut = amount + 1; + vm.expectRevert(InsufficientOutputAmount.selector); + vm.prank(user); + router.bridge({ + recipient: recipient, + chainId: DST_CHAIN_ID, + token: address(token0), + amount: amountBeforeSwap, + originQuery: originQuery, + destQuery: getDestQueryNoRebate(amount) + }); + } } From 17ccc4dbd661998b7ca24721941e510bfea6c26e Mon Sep 17 00:00:00 2001 From: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> Date: Tue, 30 Jul 2024 16:33:46 +0200 Subject: [PATCH 14/16] fix: check deadline/amountOut for no-swap cases --- contracts/rfq/FastBridgeRouterV2.sol | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/contracts/rfq/FastBridgeRouterV2.sol b/contracts/rfq/FastBridgeRouterV2.sol index 2175d9c4c..48ee0ecd0 100644 --- a/contracts/rfq/FastBridgeRouterV2.sol +++ b/contracts/rfq/FastBridgeRouterV2.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.17; -import {DefaultRouter} from "../router/DefaultRouter.sol"; +import {DefaultRouter, DeadlineExceeded, InsufficientOutputAmount} from "../router/DefaultRouter.sol"; import {UniversalTokenLib} from "../router/libs/UniversalToken.sol"; import {ActionLib, LimitedToken} from "../router/libs/Structs.sol"; import {IFastBridge} from "./interfaces/IFastBridge.sol"; @@ -65,6 +65,14 @@ contract FastBridgeRouterV2 is DefaultRouter, Ownable, IFastBridgeRouter { (token, amount) = _doSwap(address(this), token, amount, originQuery); } else { // Otherwise, pull the token from the user to this contract + // We still need to perform the deadline and amountOut checks + // solhint-disable-next-line not-rely-on-time + if (block.timestamp > originQuery.deadline) { + revert DeadlineExceeded(); + } + if (amount < originQuery.minAmountOut) { + revert InsufficientOutputAmount(); + } amount = _pullToken(address(this), token, amount); } IFastBridge.BridgeParams memory params = IFastBridge.BridgeParams({ From 8324cb35114e777a4e0dcb47b8fb7f38d998ba88 Mon Sep 17 00:00:00 2001 From: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> Date: Fri, 2 Aug 2024 11:03:15 +0100 Subject: [PATCH 15/16] deploy: updated FB RouterV2 --- deployments/arbitrum/FastBridgeRouterV2.json | 2 +- deployments/base/FastBridgeRouterV2.json | 2 +- deployments/blast/FastBridgeRouterV2.json | 2 +- deployments/bsc/FastBridgeRouterV2.json | 2 +- deployments/linea/FastBridgeRouterV2.json | 2 +- deployments/mainnet/FastBridgeRouterV2.json | 2 +- deployments/optimism/FastBridgeRouterV2.json | 2 +- deployments/scroll/FastBridgeRouterV2.json | 2 +- script/configs/Create2Factory.salts.json | 6 +++--- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/deployments/arbitrum/FastBridgeRouterV2.json b/deployments/arbitrum/FastBridgeRouterV2.json index 2466ae177..6c040224c 100644 --- a/deployments/arbitrum/FastBridgeRouterV2.json +++ b/deployments/arbitrum/FastBridgeRouterV2.json @@ -1,5 +1,5 @@ { - "address": "0xd50042193Db100FE0040005e00D5010000007e45", + "address": "0x00cD000000003f7F682BE4813200893d4e690000", "constructorArgs": "0x0000000000000000000000000fea3e5840334fc758a3decf14546bfdfbef5cd3", "abi": [ { diff --git a/deployments/base/FastBridgeRouterV2.json b/deployments/base/FastBridgeRouterV2.json index 2466ae177..6c040224c 100644 --- a/deployments/base/FastBridgeRouterV2.json +++ b/deployments/base/FastBridgeRouterV2.json @@ -1,5 +1,5 @@ { - "address": "0xd50042193Db100FE0040005e00D5010000007e45", + "address": "0x00cD000000003f7F682BE4813200893d4e690000", "constructorArgs": "0x0000000000000000000000000fea3e5840334fc758a3decf14546bfdfbef5cd3", "abi": [ { diff --git a/deployments/blast/FastBridgeRouterV2.json b/deployments/blast/FastBridgeRouterV2.json index 2466ae177..6c040224c 100644 --- a/deployments/blast/FastBridgeRouterV2.json +++ b/deployments/blast/FastBridgeRouterV2.json @@ -1,5 +1,5 @@ { - "address": "0xd50042193Db100FE0040005e00D5010000007e45", + "address": "0x00cD000000003f7F682BE4813200893d4e690000", "constructorArgs": "0x0000000000000000000000000fea3e5840334fc758a3decf14546bfdfbef5cd3", "abi": [ { diff --git a/deployments/bsc/FastBridgeRouterV2.json b/deployments/bsc/FastBridgeRouterV2.json index 2466ae177..6c040224c 100644 --- a/deployments/bsc/FastBridgeRouterV2.json +++ b/deployments/bsc/FastBridgeRouterV2.json @@ -1,5 +1,5 @@ { - "address": "0xd50042193Db100FE0040005e00D5010000007e45", + "address": "0x00cD000000003f7F682BE4813200893d4e690000", "constructorArgs": "0x0000000000000000000000000fea3e5840334fc758a3decf14546bfdfbef5cd3", "abi": [ { diff --git a/deployments/linea/FastBridgeRouterV2.json b/deployments/linea/FastBridgeRouterV2.json index 2466ae177..6c040224c 100644 --- a/deployments/linea/FastBridgeRouterV2.json +++ b/deployments/linea/FastBridgeRouterV2.json @@ -1,5 +1,5 @@ { - "address": "0xd50042193Db100FE0040005e00D5010000007e45", + "address": "0x00cD000000003f7F682BE4813200893d4e690000", "constructorArgs": "0x0000000000000000000000000fea3e5840334fc758a3decf14546bfdfbef5cd3", "abi": [ { diff --git a/deployments/mainnet/FastBridgeRouterV2.json b/deployments/mainnet/FastBridgeRouterV2.json index 2466ae177..6c040224c 100644 --- a/deployments/mainnet/FastBridgeRouterV2.json +++ b/deployments/mainnet/FastBridgeRouterV2.json @@ -1,5 +1,5 @@ { - "address": "0xd50042193Db100FE0040005e00D5010000007e45", + "address": "0x00cD000000003f7F682BE4813200893d4e690000", "constructorArgs": "0x0000000000000000000000000fea3e5840334fc758a3decf14546bfdfbef5cd3", "abi": [ { diff --git a/deployments/optimism/FastBridgeRouterV2.json b/deployments/optimism/FastBridgeRouterV2.json index 2466ae177..6c040224c 100644 --- a/deployments/optimism/FastBridgeRouterV2.json +++ b/deployments/optimism/FastBridgeRouterV2.json @@ -1,5 +1,5 @@ { - "address": "0xd50042193Db100FE0040005e00D5010000007e45", + "address": "0x00cD000000003f7F682BE4813200893d4e690000", "constructorArgs": "0x0000000000000000000000000fea3e5840334fc758a3decf14546bfdfbef5cd3", "abi": [ { diff --git a/deployments/scroll/FastBridgeRouterV2.json b/deployments/scroll/FastBridgeRouterV2.json index 2466ae177..6c040224c 100644 --- a/deployments/scroll/FastBridgeRouterV2.json +++ b/deployments/scroll/FastBridgeRouterV2.json @@ -1,5 +1,5 @@ { - "address": "0xd50042193Db100FE0040005e00D5010000007e45", + "address": "0x00cD000000003f7F682BE4813200893d4e690000", "constructorArgs": "0x0000000000000000000000000fea3e5840334fc758a3decf14546bfdfbef5cd3", "abi": [ { diff --git a/script/configs/Create2Factory.salts.json b/script/configs/Create2Factory.salts.json index b0a579f5b..74afec273 100644 --- a/script/configs/Create2Factory.salts.json +++ b/script/configs/Create2Factory.salts.json @@ -10,9 +10,9 @@ "salt": "0x0000000000000000000000000000000000000000a887a859fa44720198162f0e" }, "FastBridgeRouterV2": { - "initCodeHash": "0xa51b9e280aa2f3031dee11ee332b050e56252928afbbead89d5b3c2e5c1750ef", - "predictedAddress": "0xd50042193Db100FE0040005e00D5010000007e45", - "salt": "0x00000000000000000000000000000000000000006137d2b1087774010a8fadb4" + "initCodeHash": "0x27f5c13b6e4bed194d26ed65fa860d6af815eff7e634fee55ac9c039ce5105b7", + "predictedAddress": "0x00cD000000003f7F682BE4813200893d4e690000", + "salt": "0x0000000000000000000000000000000000000000d03957700745a831060da626" }, "SynapseRouter": { "initCodeHash": "0x4e3652671a68c0263a6844cbfabcab93d1a951bdc4b7983183b99f74308383e0", From ff620c8760500e4923e2c855a0c1602d5d1b0ff2 Mon Sep 17 00:00:00 2001 From: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> Date: Fri, 2 Aug 2024 12:26:10 +0100 Subject: [PATCH 16/16] build: add helper scripts --- script/rfq/BrickFastBridgeRouter.s.sol | 33 ++++++++++++++++++++++++++ script/{rfq-run.sh => rfq/rfq-cmd.sh} | 14 +++++------ script/rfq/rfq-run.sh | 19 +++++++++++++++ 3 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 script/rfq/BrickFastBridgeRouter.s.sol rename script/{rfq-run.sh => rfq/rfq-cmd.sh} (66%) create mode 100755 script/rfq/rfq-run.sh diff --git a/script/rfq/BrickFastBridgeRouter.s.sol b/script/rfq/BrickFastBridgeRouter.s.sol new file mode 100644 index 000000000..19ea7d7b0 --- /dev/null +++ b/script/rfq/BrickFastBridgeRouter.s.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.17; + +import {FastBridgeRouter} from "../../contracts/rfq/FastBridgeRouter.sol"; + +import {BasicSynapseScript} from "../templates/BasicSynapse.s.sol"; + +contract BrickFastBridgeRouter is BasicSynapseScript { + FastBridgeRouter public router; + + function setUp() internal override { + super.setUp(); + address payable routerDeployment = payable(getDeploymentAddress("FastBridgeRouter")); + router = FastBridgeRouter(routerDeployment); + } + + function run() external { + // Setup the BasicSynapseScript + setUp(); + vm.startBroadcast(); + brickFastBridgeRouter(); + vm.stopBroadcast(); + } + + function brickFastBridgeRouter() internal { + if (router.fastBridge() != address(0)) { + router.setFastBridge(address(0)); + printLog(string.concat(unicode"✅ Fast bridge set to zero")); + } else { + printLog(string.concat(unicode"🟡 Skipping: Fast bridge is already set to zero")); + } + } +} diff --git a/script/rfq-run.sh b/script/rfq/rfq-cmd.sh similarity index 66% rename from script/rfq-run.sh rename to script/rfq/rfq-cmd.sh index ac387886a..46c9a7d80 100755 --- a/script/rfq-run.sh +++ b/script/rfq/rfq-cmd.sh @@ -1,18 +1,18 @@ #!/usr/bin/env bash # This script runs an RFQ script for all chains with FastBridge deployment -# Usage: ./script/rfq-run.sh pathToScript -# - ./script/run.sh pathToScript chain will be run for all RFQ chains +# Usage: ./script/rfq-cmd.sh "" +# - chain will be run for all RFQ chains # Colors RED="\033[0;31m" NC="\033[0m" # No Color -scriptFN=$1 +command=$1 # Get the rest of the args shift 1 # Check that all required args exist -if [ -z "$scriptFN" ]; then - echo -e "${RED}Usage: ./script/rfq-run.sh pathToScript ${NC}" +if [ -z "$command" ]; then + echo -e "${RED}Usage: ./script/rfq-cmd.sh ${NC}" exit 1 fi @@ -22,8 +22,8 @@ fastBridgeDeployments=$(find ./deployments -name FastBridge.json) chainNames=$(echo "$fastBridgeDeployments" | sed 's/.*\/\(.*\)\/FastBridge.json/\1/' | sort) # Print the comma separated list of chain aliases, don't put a comma after the last one chainNamesFormatted=$(echo "$chainNames" | sed ':a;N;$!ba;s/\n/, /g') -echo "Running script $scriptFN for chains: [$chainNamesFormatted]" +echo "Running $command for chains: [$chainNamesFormatted]" for chainName in $chainNames; do - ./script/run.sh "$scriptFN" "$chainName" "$@" + $command "$chainName" "$@" done diff --git a/script/rfq/rfq-run.sh b/script/rfq/rfq-run.sh new file mode 100755 index 000000000..d52ba403f --- /dev/null +++ b/script/rfq/rfq-run.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# This script runs an RFQ script for all chains with FastBridge deployment +# Usage: ./script/rfq-run.sh pathToScript +# - ./script/run.sh pathToScript chain will be run for all RFQ chains + +# Colors +RED="\033[0;31m" +NC="\033[0m" # No Color + +scriptFN=$1 +# Get the rest of the args +shift 1 +# Check that all required args exist +if [ -z "$scriptFN" ]; then + echo -e "${RED}Usage: ./script/rfq-run.sh pathToScript ${NC}" + exit 1 +fi + +./script/rfq/rfq-cmd.sh "./script/run.sh $scriptFN" "$@"