diff --git a/.github/workflows/solidity.yml b/.github/workflows/solidity.yml index ee44b8d940..19d0d643a1 100644 --- a/.github/workflows/solidity.yml +++ b/.github/workflows/solidity.yml @@ -264,15 +264,10 @@ jobs: with: version: nightly - # TODO: consider defining a package-specific script for this - name: Run tests and generate gas report working-directory: './packages/${{matrix.package}}' - # Excluding tests with reverts to get accurate average gas cost estimates - run: forge test --nmt "(fail|revert)" --gas-report > "../../gas-report-${{ matrix.package }}.ansi" - env: - # make fuzzing semi-deterministic to avoid noisy gas cost estimation - # due to non-deterministic fuzzing (but still use pseudo-random fuzzing seeds) - FOUNDRY_FUZZ_SEED: 0x${{ github.event.pull_request.base.sha || github.sha }} + # Run separate set of tests (no fuzzing) to get accurate average gas cost estimates + run: forge test --mc GasBenchmark --gas-report > "../../gas-report-${{ matrix.package }}.ansi" - name: Compare gas reports uses: Rubilmax/foundry-gas-diff@v3.18 diff --git a/packages/contracts-rfq/test/FastBridgeV2.Dst.Base.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Dst.Base.t.sol new file mode 100644 index 0000000000..bf113a2078 --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.Dst.Base.t.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2, FastBridgeV2Test, IFastBridge} from "./FastBridgeV2.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2DstBaseTest is FastBridgeV2Test { + uint256 public constant LEFTOVER_BALANCE = 1 ether; + + function setUp() public override { + vm.chainId(DST_CHAIN_ID); + super.setUp(); + } + + function deployFastBridge() public override returns (FastBridgeV2) { + return new FastBridgeV2(address(this)); + } + + function mintTokens() public virtual override { + dstToken.mint(address(relayerA), LEFTOVER_BALANCE + tokenParams.destAmount); + dstToken.mint(address(relayerB), LEFTOVER_BALANCE + tokenParams.destAmount); + deal(relayerA, LEFTOVER_BALANCE + ethParams.destAmount); + deal(relayerB, LEFTOVER_BALANCE + ethParams.destAmount); + vm.prank(relayerA); + dstToken.approve(address(fastBridge), type(uint256).max); + vm.prank(relayerB); + dstToken.approve(address(fastBridge), type(uint256).max); + } + + // ══════════════════════════════════════════════════ HELPERS ══════════════════════════════════════════════════════ + + function relay(address caller, uint256 msgValue, IFastBridge.BridgeTransaction memory bridgeTx) public { + bytes memory request = abi.encode(bridgeTx); + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.relay{value: msgValue}(request); + } + + function relayWithAddress( + address caller, + address relayer, + uint256 msgValue, + IFastBridge.BridgeTransaction memory bridgeTx + ) + public + { + bytes memory request = abi.encode(bridgeTx); + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.relay{value: msgValue}(request, relayer); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol index e4848d53dc..3389c1690a 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.Dst.t.sol @@ -3,10 +3,10 @@ pragma solidity ^0.8.20; import {ChainIncorrect, DeadlineExceeded, TransactionRelayed, ZeroAddress} from "../contracts/libs/Errors.sol"; -import {FastBridgeV2, FastBridgeV2Test, IFastBridge} from "./FastBridgeV2.t.sol"; +import {FastBridgeV2DstBaseTest, IFastBridge} from "./FastBridgeV2.Dst.Base.t.sol"; // solhint-disable func-name-mixedcase, ordering -contract FastBridgeV2DstTest is FastBridgeV2Test { +contract FastBridgeV2DstTest is FastBridgeV2DstBaseTest { event BridgeRelayed( bytes32 indexed transactionId, address indexed relayer, @@ -19,24 +19,6 @@ contract FastBridgeV2DstTest is FastBridgeV2Test { uint256 chainGasAmount ); - uint256 public constant LEFTOVER_BALANCE = 1 ether; - - function setUp() public override { - vm.chainId(DST_CHAIN_ID); - super.setUp(); - } - - function deployFastBridge() public override returns (FastBridgeV2) { - return new FastBridgeV2(address(this)); - } - - function mintTokens() public override { - dstToken.mint(address(relayerA), LEFTOVER_BALANCE + tokenParams.destAmount); - deal(relayerB, LEFTOVER_BALANCE + ethParams.destAmount); - vm.prank(relayerA); - dstToken.approve(address(fastBridge), type(uint256).max); - } - function expectBridgeRelayed(IFastBridge.BridgeTransaction memory bridgeTx, bytes32 txId, address relayer) public { vm.expectEmit(address(fastBridge)); emit BridgeRelayed({ @@ -52,25 +34,6 @@ contract FastBridgeV2DstTest is FastBridgeV2Test { }); } - function relay(address caller, uint256 msgValue, IFastBridge.BridgeTransaction memory bridgeTx) public { - bytes memory request = abi.encode(bridgeTx); - vm.prank(caller); - fastBridge.relay{value: msgValue}(request); - } - - function relayWithAddress( - address caller, - address relayer, - uint256 msgValue, - IFastBridge.BridgeTransaction memory bridgeTx - ) - public - { - bytes memory request = abi.encode(bridgeTx); - vm.prank(caller); - fastBridge.relay{value: msgValue}(request, relayer); - } - /// @notice RelayerA completes the ERC20 bridge request function test_relay_token() public { bytes32 txId = getTxId(tokenTx); diff --git a/packages/contracts-rfq/test/FastBridgeV2.GasBench.Dst.t.sol b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Dst.t.sol new file mode 100644 index 0000000000..6afabce02e --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Dst.t.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2DstBaseTest} from "./FastBridgeV2.Dst.Base.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +/// @notice This test is used to estimate the gas cost of FastBridgeV2 destination chain functions. +/// Very little state checks are performed, make sure to do full coverage in different tests. +contract FastBridgeV2DstGasBenchmarkTest is FastBridgeV2DstBaseTest { + uint256 public constant INITIAL_USER_BALANCE = 100 ether; + + function mintTokens() public virtual override { + super.mintTokens(); + deal(userB, INITIAL_USER_BALANCE); + dstToken.mint(userB, INITIAL_USER_BALANCE); + } + + // ═══════════════════════════════════════════════════ TOKEN ═══════════════════════════════════════════════════════ + + function test_relay_token() public { + bytes32 txId = getTxId(tokenTx); + relay({caller: relayerA, msgValue: 0, bridgeTx: tokenTx}); + (uint256 blockNumber, uint256 blockTimestamp, address relayer) = fastBridge.bridgeRelayDetails(txId); + assertEq(blockNumber, block.number); + assertEq(blockTimestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(dstToken.balanceOf(userB), INITIAL_USER_BALANCE + tokenParams.destAmount); + assertEq(dstToken.balanceOf(relayerA), LEFTOVER_BALANCE); + } + + function test_relay_token_withRelayerAddress() public { + bytes32 txId = getTxId(tokenTx); + relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: 0, bridgeTx: tokenTx}); + (uint256 blockNumber, uint256 blockTimestamp, address relayer) = fastBridge.bridgeRelayDetails(txId); + assertEq(blockNumber, block.number); + assertEq(blockTimestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(dstToken.balanceOf(userB), INITIAL_USER_BALANCE + tokenParams.destAmount); + assertEq(dstToken.balanceOf(relayerB), LEFTOVER_BALANCE); + } + + // ════════════════════════════════════════════════════ ETH ════════════════════════════════════════════════════════ + + function test_relay_eth() public { + bytes32 txId = getTxId(ethTx); + relay({caller: relayerA, msgValue: ethParams.destAmount, bridgeTx: ethTx}); + (uint256 blockNumber, uint256 blockTimestamp, address relayer) = fastBridge.bridgeRelayDetails(txId); + assertEq(blockNumber, block.number); + assertEq(blockTimestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(address(userB).balance, INITIAL_USER_BALANCE + ethParams.destAmount); + assertEq(address(relayerA).balance, LEFTOVER_BALANCE); + } + + function test_relay_eth_withRelayerAddress() public { + bytes32 txId = getTxId(ethTx); + relayWithAddress({caller: relayerB, relayer: relayerA, msgValue: ethParams.destAmount, bridgeTx: ethTx}); + (uint256 blockNumber, uint256 blockTimestamp, address relayer) = fastBridge.bridgeRelayDetails(txId); + assertEq(blockNumber, block.number); + assertEq(blockTimestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(address(userB).balance, INITIAL_USER_BALANCE + ethParams.destAmount); + assertEq(address(relayerB).balance, LEFTOVER_BALANCE); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.PFees.t.sol b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.PFees.t.sol new file mode 100644 index 0000000000..77b439d18e --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.PFees.t.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2GasBenchmarkSrcTest} from "./FastBridgeV2.GasBench.Src.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +contract FastBridgeV2GasBenchmarkSrcProtocolFeesTest is FastBridgeV2GasBenchmarkSrcTest { + function configureFastBridge() public virtual override { + super.configureFastBridge(); + fastBridge.grantRole(fastBridge.GOVERNOR_ROLE(), address(this)); + fastBridge.setProtocolFeeRate(1e4); // 1% + } + + function createFixtures() public virtual override { + super.createFixtures(); + tokenTx.originFeeAmount = 0.01e6; + tokenTx.originAmount = 0.99e6; + tokenTx.destAmount = 0.98e6; + tokenParams.destAmount = 0.98e6; + ethTx.originFeeAmount = 0.01 ether; + ethTx.originAmount = 0.99 ether; + ethTx.destAmount = 0.98 ether; + ethParams.destAmount = 0.98 ether; + + // Copy txs to bridged and proven with different nonce + bridgedTokenTx = tokenTx; + provenTokenTx = tokenTx; + bridgedEthTx = ethTx; + provenEthTx = ethTx; + + bridgedTokenTx.nonce = 0; + bridgedEthTx.nonce = 1; + provenTokenTx.nonce = 2; + provenEthTx.nonce = 3; + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol new file mode 100644 index 0000000000..424fdd5e2e --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2, FastBridgeV2SrcBaseTest, IFastBridge} from "./FastBridgeV2.Src.Base.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +/// @notice This test is used to estimate the gas cost of FastBridgeV2 source chain functions. +/// Very little state checks are performed, make sure to do full coverage in different tests. +contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { + uint256 public constant BLOCK_TIME = 12 seconds; + uint256 public constant INITIAL_RELAYER_BALANCE = 100 ether; + + IFastBridge.BridgeTransaction public bridgedTokenTx; + IFastBridge.BridgeTransaction public bridgedEthTx; + + IFastBridge.BridgeTransaction public provenTokenTx; + IFastBridge.BridgeTransaction public provenEthTx; + + uint256 public initialUserBalanceToken; + uint256 public initialUserBalanceEth; + uint256 public initialFastBridgeBalanceToken; + uint256 public initialFastBridgeBalanceEth; + + function setUp() public virtual override { + super.setUp(); + initExistingTxs(); + initialUserBalanceToken = srcToken.balanceOf(userA); + initialUserBalanceEth = userA.balance; + initialFastBridgeBalanceToken = srcToken.balanceOf(address(fastBridge)); + initialFastBridgeBalanceEth = address(fastBridge).balance; + } + + function createFixtures() public virtual override { + super.createFixtures(); + bridgedTokenTx = tokenTx; + provenTokenTx = tokenTx; + bridgedEthTx = ethTx; + provenEthTx = ethTx; + + bridgedTokenTx.nonce = 0; + bridgedEthTx.nonce = 1; + provenTokenTx.nonce = 2; + provenEthTx.nonce = 3; + // Next nonce for userA tx would be 4 (either token or eth) + tokenTx.nonce = 4; + ethTx.nonce = 4; + } + + function mintTokens() public virtual override { + super.mintTokens(); + srcToken.mint(relayerA, INITIAL_RELAYER_BALANCE); + srcToken.mint(relayerB, INITIAL_RELAYER_BALANCE); + deal(relayerA, INITIAL_RELAYER_BALANCE); + deal(relayerB, INITIAL_RELAYER_BALANCE); + } + + function initExistingTxs() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + bridge({caller: userA, msgValue: 0, params: tokenParams}); + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + skipBlocksExactly(1); + prove({caller: relayerA, bridgeTx: provenTokenTx, destTxHash: hex"01"}); + prove({caller: relayerB, transactionId: getTxId(provenEthTx), destTxHash: hex"02", relayer: relayerA}); + // Status checks + assertEq(fastBridge.bridgeStatuses(getTxId(bridgedTokenTx)), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.bridgeStatuses(getTxId(bridgedEthTx)), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(fastBridge.bridgeStatuses(getTxId(provenTokenTx)), FastBridgeV2.BridgeStatus.RELAYER_PROVED); + assertEq(fastBridge.bridgeStatuses(getTxId(provenEthTx)), FastBridgeV2.BridgeStatus.RELAYER_PROVED); + assertEq(fastBridge.bridgeStatuses(getTxId(tokenTx)), FastBridgeV2.BridgeStatus.NULL); + assertEq(fastBridge.bridgeStatuses(getTxId(ethTx)), FastBridgeV2.BridgeStatus.NULL); + } + + function skipBlocksExactly(uint256 blocks) public { + vm.roll(block.number + blocks); + vm.warp(block.timestamp + blocks * BLOCK_TIME); + } + + function skipTimeAtLeast(uint256 time) public { + uint256 blocksToSkip = time / BLOCK_TIME; + if (blocksToSkip * BLOCK_TIME < time) blocksToSkip++; + skipBlocksExactly(blocksToSkip); + } + + // ═══════════════════════════════════════════════════ TOKEN ═══════════════════════════════════════════════════════ + + function test_bridge_token() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + assertEq(fastBridge.bridgeStatuses(getTxId(tokenTx)), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(srcToken.balanceOf(userA), initialUserBalanceToken - tokenParams.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken + tokenParams.originAmount); + } + + function test_prove_token() public { + bytes32 txId = getTxId(bridgedTokenTx); + prove({caller: relayerA, bridgeTx: bridgedTokenTx, destTxHash: hex"03"}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_PROVED); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken); + } + + function test_proveWithAddress_token() public { + bytes32 txId = getTxId(bridgedTokenTx); + prove({caller: relayerB, transactionId: txId, destTxHash: hex"03", relayer: relayerA}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_PROVED); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + } + + function test_claim_token() public { + skipTimeAtLeast({time: CLAIM_DELAY + 1}); + claim({caller: relayerA, bridgeTx: provenTokenTx}); + assertEq(fastBridge.bridgeStatuses(getTxId(provenTokenTx)), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(srcToken.balanceOf(relayerA), INITIAL_RELAYER_BALANCE + tokenTx.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken - tokenTx.originAmount); + } + + function test_claimWithAddress_token() public { + skipTimeAtLeast({time: CLAIM_DELAY + 1}); + claim({caller: relayerA, bridgeTx: provenTokenTx, to: relayerB}); + assertEq(fastBridge.bridgeStatuses(getTxId(provenTokenTx)), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(srcToken.balanceOf(relayerB), INITIAL_RELAYER_BALANCE + tokenTx.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken - tokenTx.originAmount); + } + + function test_dispute_token() public { + bytes32 txId = getTxId(provenTokenTx); + dispute({caller: guard, txId: txId}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken); + } + + function test_refundPermissioned_token() public { + bytes32 txId = getTxId(bridgedTokenTx); + skipTimeAtLeast({time: DEADLINE}); + refund({caller: refunder, bridgeTx: bridgedTokenTx}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(srcToken.balanceOf(userA), initialUserBalanceToken + tokenParams.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken - tokenParams.originAmount); + } + + function test_refundPermissionless_token() public { + bytes32 txId = getTxId(bridgedTokenTx); + skipTimeAtLeast({time: DEADLINE + PERMISSIONLESS_REFUND_DELAY}); + refund({caller: userB, bridgeTx: bridgedTokenTx}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(srcToken.balanceOf(userA), initialUserBalanceToken + tokenParams.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken - tokenParams.originAmount); + } + + // ════════════════════════════════════════════════════ ETH ════════════════════════════════════════════════════════ + + function test_bridge_eth() public { + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + assertEq(fastBridge.bridgeStatuses(getTxId(ethTx)), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(userA.balance, initialUserBalanceEth - ethParams.originAmount); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth + ethParams.originAmount); + } + + function test_prove_eth() public { + bytes32 txId = getTxId(bridgedEthTx); + prove({caller: relayerA, bridgeTx: bridgedEthTx, destTxHash: hex"03"}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_PROVED); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth); + } + + function test_proveWithAddress_eth() public { + bytes32 txId = getTxId(bridgedEthTx); + prove({caller: relayerB, transactionId: txId, destTxHash: hex"03", relayer: relayerA}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.RELAYER_PROVED); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth); + } + + function test_claim_eth() public { + skipTimeAtLeast({time: CLAIM_DELAY + 1}); + claim({caller: relayerA, bridgeTx: provenEthTx}); + assertEq(fastBridge.bridgeStatuses(getTxId(provenEthTx)), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(relayerA.balance, INITIAL_RELAYER_BALANCE + ethTx.originAmount); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth - ethTx.originAmount); + } + + function test_claimWithAddress_eth() public { + skipTimeAtLeast({time: CLAIM_DELAY + 1}); + claim({caller: relayerA, bridgeTx: provenEthTx, to: relayerB}); + assertEq(fastBridge.bridgeStatuses(getTxId(provenEthTx)), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); + assertEq(relayerB.balance, INITIAL_RELAYER_BALANCE + ethTx.originAmount); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth - ethTx.originAmount); + } + + function test_dispute_eth() public { + bytes32 txId = getTxId(provenEthTx); + dispute({caller: guard, txId: txId}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REQUESTED); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth); + } + + function test_refundPermissioned_eth() public { + bytes32 txId = getTxId(bridgedEthTx); + skipTimeAtLeast({time: DEADLINE}); + refund({caller: refunder, bridgeTx: bridgedEthTx}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(userA.balance, initialUserBalanceEth + ethParams.originAmount); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth - ethParams.originAmount); + } + + function test_refundPermissionless_eth() public { + bytes32 txId = getTxId(bridgedEthTx); + skipTimeAtLeast({time: DEADLINE + PERMISSIONLESS_REFUND_DELAY}); + refund({caller: userB, bridgeTx: bridgedEthTx}); + assertEq(fastBridge.bridgeStatuses(txId), FastBridgeV2.BridgeStatus.REFUNDED); + assertEq(userA.balance, initialUserBalanceEth + ethParams.originAmount); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth - ethParams.originAmount); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.Src.Base.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Src.Base.t.sol new file mode 100644 index 0000000000..2ce33eca6f --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.Src.Base.t.sol @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2, FastBridgeV2Test, IFastBridge} from "./FastBridgeV2.t.sol"; + +// solhint-disable func-name-mixedcase, ordering +abstract contract FastBridgeV2SrcBaseTest is FastBridgeV2Test { + uint256 public constant MIN_DEADLINE = 30 minutes; + uint256 public constant CLAIM_DELAY = 30 minutes; + uint256 public constant PERMISSIONLESS_REFUND_DELAY = 7 days; + + uint256 public constant LEFTOVER_BALANCE = 10 ether; + uint256 public constant INITIAL_PROTOCOL_FEES_TOKEN = 456_789; + uint256 public constant INITIAL_PROTOCOL_FEES_ETH = 0.123 ether; + + function setUp() public virtual override { + vm.chainId(SRC_CHAIN_ID); + super.setUp(); + } + + function deployFastBridge() public virtual override returns (FastBridgeV2) { + return new FastBridgeV2(address(this)); + } + + function configureFastBridge() public virtual override { + fastBridge.grantRole(fastBridge.RELAYER_ROLE(), relayerA); + fastBridge.grantRole(fastBridge.RELAYER_ROLE(), relayerB); + fastBridge.grantRole(fastBridge.GUARD_ROLE(), guard); + fastBridge.grantRole(fastBridge.REFUNDER_ROLE(), refunder); + } + + function mintTokens() public virtual override { + // Prior Protocol fees + srcToken.mint(address(fastBridge), INITIAL_PROTOCOL_FEES_TOKEN); + deal(address(fastBridge), INITIAL_PROTOCOL_FEES_ETH); + cheatCollectedProtocolFees(address(srcToken), INITIAL_PROTOCOL_FEES_TOKEN); + cheatCollectedProtocolFees(ETH_ADDRESS, INITIAL_PROTOCOL_FEES_ETH); + // Users + srcToken.mint(userA, LEFTOVER_BALANCE + tokenParams.originAmount); + srcToken.mint(userB, LEFTOVER_BALANCE + tokenParams.originAmount); + deal(userA, LEFTOVER_BALANCE + ethParams.originAmount); + deal(userB, LEFTOVER_BALANCE + ethParams.originAmount); + vm.prank(userA); + srcToken.approve(address(fastBridge), type(uint256).max); + vm.prank(userB); + srcToken.approve(address(fastBridge), type(uint256).max); + } + + // ══════════════════════════════════════════════════ HELPERS ══════════════════════════════════════════════════════ + + function bridge(address caller, uint256 msgValue, IFastBridge.BridgeParams memory params) public { + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.bridge{value: msgValue}(params); + } + + function prove(address caller, bytes32 transactionId, bytes32 destTxHash, address relayer) public { + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.prove(transactionId, destTxHash, relayer); + } + + function prove(address caller, IFastBridge.BridgeTransaction memory bridgeTx, bytes32 destTxHash) public { + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.prove(abi.encode(bridgeTx), destTxHash); + } + + function claim(address caller, IFastBridge.BridgeTransaction memory bridgeTx) public { + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.claim(abi.encode(bridgeTx)); + } + + function claim(address caller, IFastBridge.BridgeTransaction memory bridgeTx, address to) public { + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.claim(abi.encode(bridgeTx), to); + } + + function dispute(address caller, bytes32 txId) public { + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.dispute(txId); + } + + function refund(address caller, IFastBridge.BridgeTransaction memory bridgeTx) public { + vm.prank({msgSender: caller, txOrigin: caller}); + fastBridge.refund(abi.encode(bridgeTx)); + } + + function assertEq(FastBridgeV2.BridgeStatus a, FastBridgeV2.BridgeStatus b) public pure { + assertEq(uint8(a), uint8(b)); + } +} diff --git a/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol index d1336885e3..593722ab1d 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.Src.t.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {FastBridgeV2, FastBridgeV2Test, IFastBridge} from "./FastBridgeV2.t.sol"; +import {FastBridgeV2, FastBridgeV2SrcBaseTest, IFastBridge} from "./FastBridgeV2.Src.Base.t.sol"; // solhint-disable func-name-mixedcase, ordering -contract FastBridgeV2SrcTest is FastBridgeV2Test { +contract FastBridgeV2SrcTest is FastBridgeV2SrcBaseTest { event BridgeRequested( bytes32 indexed transactionId, address indexed sender, @@ -27,84 +27,8 @@ contract FastBridgeV2SrcTest is FastBridgeV2Test { event BridgeDepositRefunded(bytes32 indexed transactionId, address indexed to, address token, uint256 amount); - uint256 public constant MIN_DEADLINE = 30 minutes; - uint256 public constant CLAIM_DELAY = 30 minutes; - uint256 public constant PERMISSIONLESS_REFUND_DELAY = 7 days; - - uint256 public constant LEFTOVER_BALANCE = 1 ether; - uint256 public constant INITIAL_PROTOCOL_FEES_TOKEN = 456_789; - uint256 public constant INITIAL_PROTOCOL_FEES_ETH = 0.123 ether; - address public claimTo = makeAddr("Claim To"); - function setUp() public override { - vm.chainId(SRC_CHAIN_ID); - super.setUp(); - } - - function deployFastBridge() public override returns (FastBridgeV2) { - return new FastBridgeV2(address(this)); - } - - function configureFastBridge() public virtual override { - fastBridge.grantRole(fastBridge.RELAYER_ROLE(), relayerA); - fastBridge.grantRole(fastBridge.RELAYER_ROLE(), relayerB); - fastBridge.grantRole(fastBridge.GUARD_ROLE(), guard); - fastBridge.grantRole(fastBridge.REFUNDER_ROLE(), refunder); - } - - function mintTokens() public override { - // Prior Protocol fees - srcToken.mint(address(fastBridge), INITIAL_PROTOCOL_FEES_TOKEN); - deal(address(fastBridge), INITIAL_PROTOCOL_FEES_ETH); - cheatCollectedProtocolFees(address(srcToken), INITIAL_PROTOCOL_FEES_TOKEN); - cheatCollectedProtocolFees(ETH_ADDRESS, INITIAL_PROTOCOL_FEES_ETH); - // Users - srcToken.mint(userA, LEFTOVER_BALANCE + tokenParams.originAmount); - srcToken.mint(userB, LEFTOVER_BALANCE + tokenParams.originAmount); - deal(userA, LEFTOVER_BALANCE + ethParams.originAmount); - deal(userB, LEFTOVER_BALANCE + ethParams.originAmount); - vm.prank(userA); - srcToken.approve(address(fastBridge), type(uint256).max); - vm.prank(userB); - srcToken.approve(address(fastBridge), type(uint256).max); - } - - function bridge(address caller, uint256 msgValue, IFastBridge.BridgeParams memory params) public { - vm.prank(caller); - fastBridge.bridge{value: msgValue}(params); - } - - function prove(address caller, bytes32 transactionId, bytes32 destTxHash, address relayer) public { - vm.prank(caller); - fastBridge.prove(transactionId, destTxHash, relayer); - } - - function prove(address caller, IFastBridge.BridgeTransaction memory bridgeTx, bytes32 destTxHash) public { - vm.prank(caller); - fastBridge.prove(abi.encode(bridgeTx), destTxHash); - } - - function claim(address caller, IFastBridge.BridgeTransaction memory bridgeTx) public { - vm.prank(caller); - fastBridge.claim(abi.encode(bridgeTx)); - } - - function claim(address caller, IFastBridge.BridgeTransaction memory bridgeTx, address to) public { - vm.prank(caller); - fastBridge.claim(abi.encode(bridgeTx), to); - } - - function dispute(address caller, bytes32 txId) public { - vm.prank(caller); - fastBridge.dispute(txId); - } - - function refund(address caller, IFastBridge.BridgeTransaction memory bridgeTx) public { - vm.prank(caller); - fastBridge.refund(abi.encode(bridgeTx)); - } - function expectBridgeRequested(IFastBridge.BridgeTransaction memory bridgeTx, bytes32 txId) public { vm.expectEmit(address(fastBridge)); emit BridgeRequested({ @@ -159,10 +83,6 @@ contract FastBridgeV2SrcTest is FastBridgeV2Test { }); } - function assertEq(FastBridgeV2.BridgeStatus a, FastBridgeV2.BridgeStatus b) public pure { - assertEq(uint8(a), uint8(b)); - } - // ══════════════════════════════════════════════════ BRIDGE ═══════════════════════════════════════════════════════ function checkTokenBalancesAfterBridge(address caller) public view {