From a94e3e6fcb33f9dc6f9b30bcd5ec77f8be56c5df Mon Sep 17 00:00:00 2001 From: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> Date: Fri, 27 Sep 2024 13:11:07 +0100 Subject: [PATCH 1/6] refactor: isolate common utils for SRC tests --- .../test/FastBridgeV2.Src.Base.t.sol | 89 +++++++++++++++++++ .../contracts-rfq/test/FastBridgeV2.Src.t.sol | 84 +---------------- 2 files changed, 91 insertions(+), 82 deletions(-) create mode 100644 packages/contracts-rfq/test/FastBridgeV2.Src.Base.t.sol 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..47bfc5d40b --- /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 = 1 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(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 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 { From ac9a58e5726e755c6e0edfd512557cee3bd9528b Mon Sep 17 00:00:00 2001 From: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> Date: Fri, 27 Sep 2024 13:56:02 +0100 Subject: [PATCH 2/6] test: gas benchmark for SRC actions --- .../FastBridgeV2.GasBench.Src.PFees.t.sol | 25 +++ .../test/FastBridgeV2.GasBench.Src.t.sol | 167 ++++++++++++++++++ 2 files changed, 192 insertions(+) create mode 100644 packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.PFees.t.sol create mode 100644 packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol 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..b436bad929 --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.PFees.t.sol @@ -0,0 +1,25 @@ +// 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; + } +} 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..c3cb27e6e1 --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {FastBridgeV2, FastBridgeV2SrcBaseTest} 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; + + function createFixtures() public virtual override { + super.createFixtures(); + // Make both nonces 0 to simplify the tests + ethTx.nonce = 0; + } + + function skipBlocksExactly(uint256 blocks) public { + vm.roll(block.number + blocks); + vm.warp(block.timestamp + blocks * BLOCK_TIME); + } + + function rewindBlocksExactly(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); + } + + function rewindTimeAtLeast(uint256 time) public { + uint256 blocksToRewind = time / BLOCK_TIME; + if (blocksToRewind * BLOCK_TIME < time) blocksToRewind++; + rewindBlocksExactly(blocksToRewind); + } + + // ═══════════════════════════════════════════════════ TOKEN ═══════════════════════════════════════════════════════ + + function test_bridge_token() public { + bridge({caller: userA, msgValue: 0, params: tokenParams}); + assertEq(srcToken.balanceOf(userA), LEFTOVER_BALANCE); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount); + skipBlocksExactly(1); + } + + function test_prove_token() public { + test_bridge_token(); + prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(getTxId(tokenTx)); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + skipTimeAtLeast(CLAIM_DELAY + 1); + } + + function test_proveWithAddress_token() public { + test_bridge_token(); + prove({caller: relayerB, transactionId: getTxId(tokenTx), destTxHash: hex"01", relayer: relayerA}); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(getTxId(tokenTx)); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + skipTimeAtLeast(CLAIM_DELAY + 1); + } + + function test_claim_token() public { + test_prove_token(); + claim({caller: relayerA, bridgeTx: tokenTx}); + assertEq(srcToken.balanceOf(relayerA), tokenTx.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenTx.originFeeAmount); + } + + function test_claimWithAddress_token() public { + test_proveWithAddress_token(); + claim({caller: relayerA, bridgeTx: tokenTx, to: relayerB}); + assertEq(srcToken.balanceOf(relayerB), tokenTx.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenTx.originFeeAmount); + } + + function test_dispute_token() public { + test_prove_token(); + rewindTimeAtLeast({time: CLAIM_DELAY / 2}); + dispute({caller: guard, txId: getTxId(tokenTx)}); + assertEq(fastBridge.bridgeStatuses(getTxId(tokenTx)), FastBridgeV2.BridgeStatus.REQUESTED); + } + + function test_refundPermissioned_token() public { + test_bridge_token(); + skipTimeAtLeast({time: DEADLINE}); + refund({caller: refunder, bridgeTx: tokenTx}); + assertEq(srcToken.balanceOf(userA), LEFTOVER_BALANCE + tokenParams.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN); + } + + function test_refundPermissionless_token() public { + test_bridge_token(); + skipTimeAtLeast({time: DEADLINE + PERMISSIONLESS_REFUND_DELAY}); + refund({caller: userB, bridgeTx: tokenTx}); + assertEq(srcToken.balanceOf(userA), LEFTOVER_BALANCE + tokenParams.originAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN); + } + + // ════════════════════════════════════════════════════ ETH ════════════════════════════════════════════════════════ + + function test_bridge_eth() public { + bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams}); + assertEq(userA.balance, LEFTOVER_BALANCE); + assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH + ethParams.originAmount); + skipBlocksExactly(1); + } + + function test_prove_eth() public { + test_bridge_eth(); + prove({caller: relayerA, bridgeTx: ethTx, destTxHash: hex"01"}); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(getTxId(ethTx)); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + skipTimeAtLeast(CLAIM_DELAY + 1); + } + + function test_proveWithAddress_eth() public { + test_bridge_eth(); + prove({caller: relayerB, transactionId: getTxId(ethTx), destTxHash: hex"01", relayer: relayerA}); + (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(getTxId(ethTx)); + assertEq(timestamp, block.timestamp); + assertEq(relayer, relayerA); + skipTimeAtLeast(CLAIM_DELAY + 1); + } + + function test_claim_eth() public { + test_prove_eth(); + claim({caller: relayerA, bridgeTx: ethTx}); + assertEq(relayerA.balance, ethTx.originAmount); + assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH + ethTx.originFeeAmount); + } + + function test_claimWithAddress_eth() public { + test_proveWithAddress_eth(); + claim({caller: relayerA, bridgeTx: ethTx, to: relayerB}); + assertEq(relayerB.balance, ethTx.originAmount); + assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH + ethTx.originFeeAmount); + } + + function test_dispute_eth() public { + test_prove_eth(); + rewindTimeAtLeast({time: CLAIM_DELAY / 2}); + dispute({caller: guard, txId: getTxId(ethTx)}); + assertEq(fastBridge.bridgeStatuses(getTxId(ethTx)), FastBridgeV2.BridgeStatus.REQUESTED); + } + + function test_refundPermissioned_eth() public { + test_bridge_eth(); + skipTimeAtLeast({time: DEADLINE}); + refund({caller: refunder, bridgeTx: ethTx}); + assertEq(userA.balance, LEFTOVER_BALANCE + ethParams.originAmount); + assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH); + } + + function test_refundPermissionless_eth() public { + test_bridge_eth(); + skipTimeAtLeast({time: DEADLINE + PERMISSIONLESS_REFUND_DELAY}); + refund({caller: userB, bridgeTx: ethTx}); + assertEq(userA.balance, LEFTOVER_BALANCE + ethParams.originAmount); + assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH); + } +} From 43e0aabf3d59927216ac180c93ff3eb502a37e8d Mon Sep 17 00:00:00 2001 From: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> Date: Fri, 27 Sep 2024 14:04:51 +0100 Subject: [PATCH 3/6] test: gas benchmark for DST actions --- .../test/FastBridgeV2.Dst.Base.t.sol | 50 ++++++++++++++++ .../contracts-rfq/test/FastBridgeV2.Dst.t.sol | 41 +------------ .../test/FastBridgeV2.GasBench.Dst.t.sol | 57 +++++++++++++++++++ 3 files changed, 109 insertions(+), 39 deletions(-) create mode 100644 packages/contracts-rfq/test/FastBridgeV2.Dst.Base.t.sol create mode 100644 packages/contracts-rfq/test/FastBridgeV2.GasBench.Dst.t.sol 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..c0cadcc1ef --- /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 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(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); + } +} 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..939ddfdb28 --- /dev/null +++ b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Dst.t.sol @@ -0,0 +1,57 @@ +// 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 { + // ═══════════════════════════════════════════════════ 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), 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), 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, 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, ethParams.destAmount); + assertEq(address(relayerB).balance, LEFTOVER_BALANCE); + } +} From b63ccc924ccb9d064aaba8604e57f7c99e37f64a Mon Sep 17 00:00:00 2001 From: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> Date: Fri, 27 Sep 2024 15:22:22 +0100 Subject: [PATCH 4/6] test: rework bench tests with mroe isolation --- .../test/FastBridgeV2.Dst.Base.t.sol | 4 +- .../FastBridgeV2.GasBench.Src.PFees.t.sol | 11 + .../test/FastBridgeV2.GasBench.Src.t.sol | 191 +++++++++++------- .../test/FastBridgeV2.Src.Base.t.sol | 16 +- 4 files changed, 140 insertions(+), 82 deletions(-) diff --git a/packages/contracts-rfq/test/FastBridgeV2.Dst.Base.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Dst.Base.t.sol index c0cadcc1ef..15b6bdb62f 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.Dst.Base.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.Dst.Base.t.sol @@ -31,7 +31,7 @@ contract FastBridgeV2DstBaseTest is FastBridgeV2Test { function relay(address caller, uint256 msgValue, IFastBridge.BridgeTransaction memory bridgeTx) public { bytes memory request = abi.encode(bridgeTx); - vm.prank(caller); + vm.prank({msgSender: caller, txOrigin: caller}); fastBridge.relay{value: msgValue}(request); } @@ -44,7 +44,7 @@ contract FastBridgeV2DstBaseTest is FastBridgeV2Test { public { bytes memory request = abi.encode(bridgeTx); - vm.prank(caller); + vm.prank({msgSender: caller, txOrigin: caller}); fastBridge.relay{value: msgValue}(request, relayer); } } diff --git a/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.PFees.t.sol b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.PFees.t.sol index b436bad929..77b439d18e 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.PFees.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.PFees.t.sol @@ -21,5 +21,16 @@ contract FastBridgeV2GasBenchmarkSrcProtocolFeesTest is FastBridgeV2GasBenchmark 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 index c3cb27e6e1..609794479b 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {FastBridgeV2, FastBridgeV2SrcBaseTest} from "./FastBridgeV2.Src.Base.t.sol"; +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. @@ -9,10 +9,57 @@ import {FastBridgeV2, FastBridgeV2SrcBaseTest} from "./FastBridgeV2.Src.Base.t.s contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { uint256 public constant BLOCK_TIME = 12 seconds; + 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(); - // Make both nonces 0 to simplify the tests - ethTx.nonce = 0; + 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 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 { @@ -20,148 +67,148 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { vm.warp(block.timestamp + blocks * BLOCK_TIME); } - function rewindBlocksExactly(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); } - function rewindTimeAtLeast(uint256 time) public { - uint256 blocksToRewind = time / BLOCK_TIME; - if (blocksToRewind * BLOCK_TIME < time) blocksToRewind++; - rewindBlocksExactly(blocksToRewind); - } - // ═══════════════════════════════════════════════════ TOKEN ═══════════════════════════════════════════════════════ function test_bridge_token() public { bridge({caller: userA, msgValue: 0, params: tokenParams}); - assertEq(srcToken.balanceOf(userA), LEFTOVER_BALANCE); - assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenParams.originAmount); - skipBlocksExactly(1); + 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 { - test_bridge_token(); - prove({caller: relayerA, bridgeTx: tokenTx, destTxHash: hex"01"}); - (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(getTxId(tokenTx)); + 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); - skipTimeAtLeast(CLAIM_DELAY + 1); + assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken); } function test_proveWithAddress_token() public { - test_bridge_token(); - prove({caller: relayerB, transactionId: getTxId(tokenTx), destTxHash: hex"01", relayer: relayerA}); - (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(getTxId(tokenTx)); + 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); - skipTimeAtLeast(CLAIM_DELAY + 1); } function test_claim_token() public { - test_prove_token(); - claim({caller: relayerA, bridgeTx: tokenTx}); + skipTimeAtLeast({time: CLAIM_DELAY + 1}); + claim({caller: relayerA, bridgeTx: provenTokenTx}); + assertEq(fastBridge.bridgeStatuses(getTxId(provenTokenTx)), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); assertEq(srcToken.balanceOf(relayerA), tokenTx.originAmount); - assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenTx.originFeeAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken - tokenTx.originAmount); } function test_claimWithAddress_token() public { - test_proveWithAddress_token(); - claim({caller: relayerA, bridgeTx: tokenTx, to: relayerB}); + 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), tokenTx.originAmount); - assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN + tokenTx.originFeeAmount); + assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken - tokenTx.originAmount); } function test_dispute_token() public { - test_prove_token(); - rewindTimeAtLeast({time: CLAIM_DELAY / 2}); - dispute({caller: guard, txId: getTxId(tokenTx)}); - assertEq(fastBridge.bridgeStatuses(getTxId(tokenTx)), FastBridgeV2.BridgeStatus.REQUESTED); + 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 { - test_bridge_token(); + bytes32 txId = getTxId(bridgedTokenTx); skipTimeAtLeast({time: DEADLINE}); - refund({caller: refunder, bridgeTx: tokenTx}); - assertEq(srcToken.balanceOf(userA), LEFTOVER_BALANCE + tokenParams.originAmount); - assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN); + 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 { - test_bridge_token(); + bytes32 txId = getTxId(bridgedTokenTx); skipTimeAtLeast({time: DEADLINE + PERMISSIONLESS_REFUND_DELAY}); - refund({caller: userB, bridgeTx: tokenTx}); - assertEq(srcToken.balanceOf(userA), LEFTOVER_BALANCE + tokenParams.originAmount); - assertEq(srcToken.balanceOf(address(fastBridge)), INITIAL_PROTOCOL_FEES_TOKEN); + 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(userA.balance, LEFTOVER_BALANCE); - assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH + ethParams.originAmount); - skipBlocksExactly(1); + 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 { - test_bridge_eth(); - prove({caller: relayerA, bridgeTx: ethTx, destTxHash: hex"01"}); - (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(getTxId(ethTx)); + 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); - skipTimeAtLeast(CLAIM_DELAY + 1); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth); } function test_proveWithAddress_eth() public { - test_bridge_eth(); - prove({caller: relayerB, transactionId: getTxId(ethTx), destTxHash: hex"01", relayer: relayerA}); - (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(getTxId(ethTx)); + 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); - skipTimeAtLeast(CLAIM_DELAY + 1); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth); } function test_claim_eth() public { - test_prove_eth(); - claim({caller: relayerA, bridgeTx: ethTx}); + skipTimeAtLeast({time: CLAIM_DELAY + 1}); + claim({caller: relayerA, bridgeTx: provenEthTx}); + assertEq(fastBridge.bridgeStatuses(getTxId(provenEthTx)), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); assertEq(relayerA.balance, ethTx.originAmount); - assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH + ethTx.originFeeAmount); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth - ethTx.originAmount); } function test_claimWithAddress_eth() public { - test_proveWithAddress_eth(); - claim({caller: relayerA, bridgeTx: ethTx, to: relayerB}); + skipTimeAtLeast({time: CLAIM_DELAY + 1}); + claim({caller: relayerA, bridgeTx: provenEthTx, to: relayerB}); + assertEq(fastBridge.bridgeStatuses(getTxId(provenEthTx)), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); assertEq(relayerB.balance, ethTx.originAmount); - assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH + ethTx.originFeeAmount); + assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth - ethTx.originAmount); } function test_dispute_eth() public { - test_prove_eth(); - rewindTimeAtLeast({time: CLAIM_DELAY / 2}); - dispute({caller: guard, txId: getTxId(ethTx)}); - assertEq(fastBridge.bridgeStatuses(getTxId(ethTx)), FastBridgeV2.BridgeStatus.REQUESTED); + 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 { - test_bridge_eth(); + bytes32 txId = getTxId(bridgedEthTx); skipTimeAtLeast({time: DEADLINE}); - refund({caller: refunder, bridgeTx: ethTx}); - assertEq(userA.balance, LEFTOVER_BALANCE + ethParams.originAmount); - assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH); + 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 { - test_bridge_eth(); + bytes32 txId = getTxId(bridgedEthTx); skipTimeAtLeast({time: DEADLINE + PERMISSIONLESS_REFUND_DELAY}); - refund({caller: userB, bridgeTx: ethTx}); - assertEq(userA.balance, LEFTOVER_BALANCE + ethParams.originAmount); - assertEq(address(fastBridge).balance, INITIAL_PROTOCOL_FEES_ETH); + 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 index 47bfc5d40b..2ce33eca6f 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.Src.Base.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.Src.Base.t.sol @@ -9,7 +9,7 @@ abstract contract FastBridgeV2SrcBaseTest is FastBridgeV2Test { 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 LEFTOVER_BALANCE = 10 ether; uint256 public constant INITIAL_PROTOCOL_FEES_TOKEN = 456_789; uint256 public constant INITIAL_PROTOCOL_FEES_ETH = 0.123 ether; @@ -49,37 +49,37 @@ abstract contract FastBridgeV2SrcBaseTest is FastBridgeV2Test { // ══════════════════════════════════════════════════ HELPERS ══════════════════════════════════════════════════════ function bridge(address caller, uint256 msgValue, IFastBridge.BridgeParams memory params) public { - vm.prank(caller); + vm.prank({msgSender: caller, txOrigin: caller}); fastBridge.bridge{value: msgValue}(params); } function prove(address caller, bytes32 transactionId, bytes32 destTxHash, address relayer) public { - vm.prank(caller); + vm.prank({msgSender: caller, txOrigin: caller}); fastBridge.prove(transactionId, destTxHash, relayer); } function prove(address caller, IFastBridge.BridgeTransaction memory bridgeTx, bytes32 destTxHash) public { - vm.prank(caller); + vm.prank({msgSender: caller, txOrigin: caller}); fastBridge.prove(abi.encode(bridgeTx), destTxHash); } function claim(address caller, IFastBridge.BridgeTransaction memory bridgeTx) public { - vm.prank(caller); + vm.prank({msgSender: caller, txOrigin: caller}); fastBridge.claim(abi.encode(bridgeTx)); } function claim(address caller, IFastBridge.BridgeTransaction memory bridgeTx, address to) public { - vm.prank(caller); + vm.prank({msgSender: caller, txOrigin: caller}); fastBridge.claim(abi.encode(bridgeTx), to); } function dispute(address caller, bytes32 txId) public { - vm.prank(caller); + vm.prank({msgSender: caller, txOrigin: caller}); fastBridge.dispute(txId); } function refund(address caller, IFastBridge.BridgeTransaction memory bridgeTx) public { - vm.prank(caller); + vm.prank({msgSender: caller, txOrigin: caller}); fastBridge.refund(abi.encode(bridgeTx)); } From bb1cd07c6864276a35491c2b1366ef0b5b761986 Mon Sep 17 00:00:00 2001 From: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> Date: Fri, 27 Sep 2024 15:32:14 +0100 Subject: [PATCH 5/6] fix: set non-zero initial balances for asset receivers --- .../test/FastBridgeV2.Dst.Base.t.sol | 2 +- .../test/FastBridgeV2.GasBench.Dst.t.sol | 16 ++++++++++++---- .../test/FastBridgeV2.GasBench.Src.t.sol | 17 +++++++++++++---- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/packages/contracts-rfq/test/FastBridgeV2.Dst.Base.t.sol b/packages/contracts-rfq/test/FastBridgeV2.Dst.Base.t.sol index 15b6bdb62f..bf113a2078 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.Dst.Base.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.Dst.Base.t.sol @@ -16,7 +16,7 @@ contract FastBridgeV2DstBaseTest is FastBridgeV2Test { return new FastBridgeV2(address(this)); } - function mintTokens() public override { + 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); diff --git a/packages/contracts-rfq/test/FastBridgeV2.GasBench.Dst.t.sol b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Dst.t.sol index 939ddfdb28..6afabce02e 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.GasBench.Dst.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Dst.t.sol @@ -7,6 +7,14 @@ import {FastBridgeV2DstBaseTest} from "./FastBridgeV2.Dst.Base.t.sol"; /// @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 { @@ -16,7 +24,7 @@ contract FastBridgeV2DstGasBenchmarkTest is FastBridgeV2DstBaseTest { assertEq(blockNumber, block.number); assertEq(blockTimestamp, block.timestamp); assertEq(relayer, relayerA); - assertEq(dstToken.balanceOf(userB), tokenParams.destAmount); + assertEq(dstToken.balanceOf(userB), INITIAL_USER_BALANCE + tokenParams.destAmount); assertEq(dstToken.balanceOf(relayerA), LEFTOVER_BALANCE); } @@ -27,7 +35,7 @@ contract FastBridgeV2DstGasBenchmarkTest is FastBridgeV2DstBaseTest { assertEq(blockNumber, block.number); assertEq(blockTimestamp, block.timestamp); assertEq(relayer, relayerA); - assertEq(dstToken.balanceOf(userB), tokenParams.destAmount); + assertEq(dstToken.balanceOf(userB), INITIAL_USER_BALANCE + tokenParams.destAmount); assertEq(dstToken.balanceOf(relayerB), LEFTOVER_BALANCE); } @@ -40,7 +48,7 @@ contract FastBridgeV2DstGasBenchmarkTest is FastBridgeV2DstBaseTest { assertEq(blockNumber, block.number); assertEq(blockTimestamp, block.timestamp); assertEq(relayer, relayerA); - assertEq(address(userB).balance, ethParams.destAmount); + assertEq(address(userB).balance, INITIAL_USER_BALANCE + ethParams.destAmount); assertEq(address(relayerA).balance, LEFTOVER_BALANCE); } @@ -51,7 +59,7 @@ contract FastBridgeV2DstGasBenchmarkTest is FastBridgeV2DstBaseTest { assertEq(blockNumber, block.number); assertEq(blockTimestamp, block.timestamp); assertEq(relayer, relayerA); - assertEq(address(userB).balance, ethParams.destAmount); + 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.t.sol b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol index 609794479b..424fdd5e2e 100644 --- a/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol +++ b/packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol @@ -8,6 +8,7 @@ import {FastBridgeV2, FastBridgeV2SrcBaseTest, IFastBridge} from "./FastBridgeV2 /// 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; @@ -45,6 +46,14 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { 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}); @@ -105,7 +114,7 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { skipTimeAtLeast({time: CLAIM_DELAY + 1}); claim({caller: relayerA, bridgeTx: provenTokenTx}); assertEq(fastBridge.bridgeStatuses(getTxId(provenTokenTx)), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); - assertEq(srcToken.balanceOf(relayerA), tokenTx.originAmount); + assertEq(srcToken.balanceOf(relayerA), INITIAL_RELAYER_BALANCE + tokenTx.originAmount); assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken - tokenTx.originAmount); } @@ -113,7 +122,7 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { 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), tokenTx.originAmount); + assertEq(srcToken.balanceOf(relayerB), INITIAL_RELAYER_BALANCE + tokenTx.originAmount); assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken - tokenTx.originAmount); } @@ -175,7 +184,7 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { skipTimeAtLeast({time: CLAIM_DELAY + 1}); claim({caller: relayerA, bridgeTx: provenEthTx}); assertEq(fastBridge.bridgeStatuses(getTxId(provenEthTx)), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); - assertEq(relayerA.balance, ethTx.originAmount); + assertEq(relayerA.balance, INITIAL_RELAYER_BALANCE + ethTx.originAmount); assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth - ethTx.originAmount); } @@ -183,7 +192,7 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest { skipTimeAtLeast({time: CLAIM_DELAY + 1}); claim({caller: relayerA, bridgeTx: provenEthTx, to: relayerB}); assertEq(fastBridge.bridgeStatuses(getTxId(provenEthTx)), FastBridgeV2.BridgeStatus.RELAYER_CLAIMED); - assertEq(relayerB.balance, ethTx.originAmount); + assertEq(relayerB.balance, INITIAL_RELAYER_BALANCE + ethTx.originAmount); assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth - ethTx.originAmount); } From 129cde2620b58801d47bd500fceba61fcaa6ed74 Mon Sep 17 00:00:00 2001 From: ChiTimesChi <88190723+ChiTimesChi@users.noreply.github.com> Date: Fri, 27 Sep 2024 15:38:33 +0100 Subject: [PATCH 6/6] ci: use gas bench tests only --- .github/workflows/solidity.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) 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