Skip to content

Commit

Permalink
test(contracts-rfq): gas benchmark for the arbitrary call [SLT-233] (#…
Browse files Browse the repository at this point in the history
…3273)

* test: gas bench for bridge with arbitrary call

* refactor: deduplicate checks

* fix: gas benchmark contract names

* test: gas benchmark for relay with arbitrary call

* test: separate set of cases for bridge/prove/claim with arbitrary calls

* fix: start from nonce=1 in gas benchmark tests
  • Loading branch information
ChiTimesChi authored Oct 11, 2024
1 parent 1f61284 commit 0428f30
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 32 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {FastBridgeV2GasBenchmarkDstTest} from "./FastBridgeV2.GasBench.Dst.t.sol";
import {RecipientMock} from "./mocks/RecipientMock.sol";

// solhint-disable func-name-mixedcase, ordering
contract FastBridgeV2GasBenchmarkDstArbitraryCallTest is FastBridgeV2GasBenchmarkDstTest {
// To get an idea about how much overhead the arbitrary call adds to the relaying process, we use a mock
// recipient that has the hook function implemented as a no-op.
// The mocked callParams are chosen to be similar to the real use cases:
// - user address
// - some kind of ID to decide what to do with the tokens next

function setUp() public virtual override {
// In the inherited tests userB is always used as the recipient of the tokens.
userB = address(new RecipientMock());
vm.label(userB, "ContractRecipient");
super.setUp();
}

function createFixturesV2() public virtual override {
super.createFixturesV2();
bytes memory mockCallParams = abi.encode(userA, keccak256("Random ID"));
setTokenTestCallParams(mockCallParams);
setEthTestCallParams(mockCallParams);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {FastBridgeV2DstGasBenchmarkTest} from "./FastBridgeV2.GasBench.Dst.t.sol";
import {FastBridgeV2GasBenchmarkDstTest} from "./FastBridgeV2.GasBench.Dst.t.sol";

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

function setUp() public virtual override {
Expand All @@ -13,6 +13,7 @@ contract FastBridgeV2DstExclusivityTest is FastBridgeV2DstGasBenchmarkTest {
}

function createFixturesV2() public virtual override {
super.createFixturesV2();
setTokenTestExclusivityParams(relayerA, EXCLUSIVITY_PERIOD);
setEthTestExclusivityParams(relayerA, EXCLUSIVITY_PERIOD);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ 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 {
contract FastBridgeV2GasBenchmarkDstTest is FastBridgeV2DstBaseTest {
uint256 public constant INITIAL_USER_BALANCE = 100 ether;

function mintTokens() public virtual override {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ pragma solidity ^0.8.20;
import {FastBridgeV2SrcBaseTest} from "./FastBridgeV2.Src.Base.t.sol";

// solhint-disable func-name-mixedcase, ordering
contract FastBridgeV2GasBenchmarkSrcProtocolFeesTest is FastBridgeV2SrcBaseTest {
// TODO: add more tests with variable length requests once arbitrary call is done

contract FastBridgeV2GasBenchmarkEncodingTest is FastBridgeV2SrcBaseTest {
function test_getBridgeTransaction() public view {
bytes memory request = abi.encode(extractV1(tokenTx));
fastBridge.getBridgeTransaction(request);
Expand All @@ -16,4 +14,9 @@ contract FastBridgeV2GasBenchmarkSrcProtocolFeesTest is FastBridgeV2SrcBaseTest
bytes memory request = abi.encode(tokenTx);
fastBridge.getBridgeTransactionV2(request);
}

function test_getBridgeTransactionV2_withArbitraryCall() public {
setTokenTestCallParams({callParams: abi.encode(userA, keccak256("Random ID"))});
test_getBridgeTransactionV2();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {FastBridgeV2GasBenchmarkSrcTest} from "./FastBridgeV2.GasBench.Src.t.sol";

// solhint-disable func-name-mixedcase, ordering
contract FastBridgeV2GasBenchmarkSrcArbitraryCallTest is FastBridgeV2GasBenchmarkSrcTest {
function createFixturesV2() public virtual override {
super.createFixturesV2();
bytes memory mockCallParams = abi.encode(userA, keccak256("Random ID"));
setTokenTestCallParams(mockCallParams);
setEthTestCallParams(mockCallParams);
bridgedTokenTx.callParams = mockCallParams;
bridgedEthTx.callParams = mockCallParams;
provenTokenTx.callParams = mockCallParams;
provenEthTx.callParams = mockCallParams;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ contract FastBridgeV2GasBenchmarkSrcProtocolFeesTest is FastBridgeV2GasBenchmark
provenTokenTx = tokenTx;
bridgedEthTx = ethTx;
provenEthTx = ethTx;

bridgedTokenTx.nonce = 0;
bridgedEthTx.nonce = 1;
provenTokenTx.nonce = 2;
provenEthTx.nonce = 3;
// See FastBridgeV2GasBenchmarkSrcTest.initExistingTxs for why these start from 1, not 0
bridgedTokenTx.nonce = 1;
bridgedEthTx.nonce = 2;
provenTokenTx.nonce = 3;
provenEthTx.nonce = 4;
}
}
50 changes: 29 additions & 21 deletions packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,14 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest {
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;
// See initExistingTxs for why these start from 1, not 0
bridgedTokenTx.nonce = 1;
bridgedEthTx.nonce = 2;
provenTokenTx.nonce = 3;
provenEthTx.nonce = 4;
// Next nonce for userA tx would be 5 (either token or eth)
tokenTx.nonce = 5;
ethTx.nonce = 5;
}

function createFixturesV2() public virtual override {
Expand All @@ -67,10 +68,13 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest {
}

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});
// Set userA nonce to 1 so that the first bridge tx doesn't have inflated gas costs due to
// the storage write from the zero initial value
cheatSenderNonce(userA, 1);
bridge({caller: userA, msgValue: 0, params: tokenParams, paramsV2: tokenParamsV2});
bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams, paramsV2: ethParamsV2});
bridge({caller: userA, msgValue: 0, params: tokenParams, paramsV2: tokenParamsV2});
bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams, paramsV2: ethParamsV2});
skipBlocksExactly(1);
prove({caller: relayerA, bridgeTx: provenTokenTx, destTxHash: hex"01"});
prove({caller: relayerB, transactionId: getTxId(provenEthTx), destTxHash: hex"02", relayer: relayerA});
Expand All @@ -96,19 +100,21 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest {

// ═══════════════════════════════════════════════════ TOKEN ═══════════════════════════════════════════════════════

function test_bridge_token() public {
bridge({caller: userA, msgValue: 0, params: tokenParams});
function checkAfterBridgeToken() public view {
assertEq(fastBridge.bridgeStatuses(getTxId(tokenTx)), IFastBridgeV2.BridgeStatus.REQUESTED);
assertEq(srcToken.balanceOf(userA), initialUserBalanceToken - tokenParams.originAmount);
assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken + tokenParams.originAmount);
}

function test_bridge_token() public {
bridge({caller: userA, msgValue: 0, params: tokenParams, paramsV2: tokenParamsV2});
checkAfterBridgeToken();
}

function test_bridge_token_withExclusivity() public {
setTokenTestExclusivityParams(relayerA, EXCLUSIVITY_PERIOD);
bridge({caller: userA, msgValue: 0, params: tokenParams, paramsV2: tokenParamsV2});
assertEq(fastBridge.bridgeStatuses(getTxId(tokenTx)), IFastBridgeV2.BridgeStatus.REQUESTED);
assertEq(srcToken.balanceOf(userA), initialUserBalanceToken - tokenParams.originAmount);
assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken + tokenParams.originAmount);
checkAfterBridgeToken();
}

function test_prove_token() public {
Expand Down Expand Up @@ -177,19 +183,21 @@ contract FastBridgeV2GasBenchmarkSrcTest is FastBridgeV2SrcBaseTest {

// ════════════════════════════════════════════════════ ETH ════════════════════════════════════════════════════════

function test_bridge_eth() public {
bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams});
function checkAfterBridgeEth() public view {
assertEq(fastBridge.bridgeStatuses(getTxId(ethTx)), IFastBridgeV2.BridgeStatus.REQUESTED);
assertEq(userA.balance, initialUserBalanceEth - ethParams.originAmount);
assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth + ethParams.originAmount);
}

function test_bridge_eth() public {
bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams, paramsV2: ethParamsV2});
checkAfterBridgeEth();
}

function test_bridge_eth_withExclusivity() public {
setEthTestExclusivityParams(relayerA, EXCLUSIVITY_PERIOD);
bridge({caller: userA, msgValue: ethParams.originAmount, params: ethParams, paramsV2: ethParamsV2});
assertEq(fastBridge.bridgeStatuses(getTxId(ethTx)), IFastBridgeV2.BridgeStatus.REQUESTED);
assertEq(userA.balance, initialUserBalanceEth - ethParams.originAmount);
assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth + ethParams.originAmount);
checkAfterBridgeEth();
}

function test_prove_eth() public {
Expand Down
4 changes: 4 additions & 0 deletions packages/contracts-rfq/test/FastBridgeV2.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -231,4 +231,8 @@ abstract contract FastBridgeV2Test is Test, IFastBridgeV2Errors {
function cheatCollectedProtocolFees(address token, uint256 amount) public {
stdstore.target(address(fastBridge)).sig("protocolFees(address)").with_key(token).checked_write(amount);
}

function cheatSenderNonce(address sender, uint256 nonce) public {
stdstore.target(address(fastBridge)).sig("senderNonces(address)").with_key(sender).checked_write(nonce);
}
}

0 comments on commit 0428f30

Please sign in to comment.