synapsecns/sanguine

View on GitHub
packages/contracts-rfq/test/FastBridgeV2.GasBench.Src.t.sol

Summary

Maintainability
Test Coverage
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {FastBridgeV2SrcBaseTest, IFastBridgeV2} 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;
    uint256 public constant EXCLUSIVITY_PERIOD = 60 seconds;

    IFastBridgeV2.BridgeTransactionV2 internal bridgedTokenTx;
    IFastBridgeV2.BridgeTransactionV2 internal bridgedEthTx;

    IFastBridgeV2.BridgeTransactionV2 internal provenTokenTx;
    IFastBridgeV2.BridgeTransactionV2 internal 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;

        // 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 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 {
        // 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});
        // Status checks
        assertEq(fastBridge.bridgeStatuses(getTxId(bridgedTokenTx)), IFastBridgeV2.BridgeStatus.REQUESTED);
        assertEq(fastBridge.bridgeStatuses(getTxId(bridgedEthTx)), IFastBridgeV2.BridgeStatus.REQUESTED);
        assertEq(fastBridge.bridgeStatuses(getTxId(provenTokenTx)), IFastBridgeV2.BridgeStatus.RELAYER_PROVED);
        assertEq(fastBridge.bridgeStatuses(getTxId(provenEthTx)), IFastBridgeV2.BridgeStatus.RELAYER_PROVED);
        assertEq(fastBridge.bridgeStatuses(getTxId(tokenTx)), IFastBridgeV2.BridgeStatus.NULL);
        assertEq(fastBridge.bridgeStatuses(getTxId(ethTx)), IFastBridgeV2.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 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});
        checkAfterBridgeToken();
    }

    function test_prove_token() public {
        bytes32 txId = getTxId(bridgedTokenTx);
        prove({caller: relayerA, bridgeTx: bridgedTokenTx, destTxHash: hex"03"});
        assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.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), IFastBridgeV2.BridgeStatus.RELAYER_PROVED);
        (uint96 timestamp, address relayer) = fastBridge.bridgeProofs(txId);
        assertEq(timestamp, block.timestamp);
        assertEq(relayer, relayerA);
    }

    function test_claim_token() public {
        bytes32 txId = getTxId(provenTokenTx);
        skipTimeAtLeast({time: CLAIM_DELAY + 1});
        assertTrue(fastBridge.canClaim(txId, relayerA));
        claim({caller: relayerA, bridgeTx: provenTokenTx});
        assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.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 {
        bytes32 txId = getTxId(provenTokenTx);
        skipTimeAtLeast({time: CLAIM_DELAY + 1});
        assertTrue(fastBridge.canClaim(txId, relayerA));
        claim({caller: relayerA, bridgeTx: provenTokenTx, to: relayerB});
        assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.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), IFastBridgeV2.BridgeStatus.REQUESTED);
        assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken);
    }

    function test_cancelPermissioned_token() public {
        bytes32 txId = getTxId(bridgedTokenTx);
        skipTimeAtLeast({time: DEADLINE});
        cancel({caller: canceler, bridgeTx: bridgedTokenTx});
        assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED);
        assertEq(srcToken.balanceOf(userA), initialUserBalanceToken + tokenParams.originAmount);
        assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken - tokenParams.originAmount);
    }

    function test_cancelPermissionless_token() public {
        bytes32 txId = getTxId(bridgedTokenTx);
        skipTimeAtLeast({time: DEADLINE + PERMISSIONLESS_CANCEL_DELAY});
        cancel({caller: userB, bridgeTx: bridgedTokenTx});
        assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED);
        assertEq(srcToken.balanceOf(userA), initialUserBalanceToken + tokenParams.originAmount);
        assertEq(srcToken.balanceOf(address(fastBridge)), initialFastBridgeBalanceToken - tokenParams.originAmount);
    }

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

    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});
        checkAfterBridgeEth();
    }

    function test_prove_eth() public {
        bytes32 txId = getTxId(bridgedEthTx);
        prove({caller: relayerA, bridgeTx: bridgedEthTx, destTxHash: hex"03"});
        assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.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), IFastBridgeV2.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 {
        bytes32 txId = getTxId(provenEthTx);
        skipTimeAtLeast({time: CLAIM_DELAY + 1});
        assertTrue(fastBridge.canClaim(txId, relayerA));
        claim({caller: relayerA, bridgeTx: provenEthTx});
        assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.RELAYER_CLAIMED);
        assertEq(relayerA.balance, INITIAL_RELAYER_BALANCE + ethTx.originAmount);
        assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth - ethTx.originAmount);
    }

    function test_claimWithAddress_eth() public {
        bytes32 txId = getTxId(provenEthTx);
        skipTimeAtLeast({time: CLAIM_DELAY + 1});
        assertTrue(fastBridge.canClaim(txId, relayerA));
        claim({caller: relayerA, bridgeTx: provenEthTx, to: relayerB});
        assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.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), IFastBridgeV2.BridgeStatus.REQUESTED);
        assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth);
    }

    function test_cancelPermissioned_eth() public {
        bytes32 txId = getTxId(bridgedEthTx);
        skipTimeAtLeast({time: DEADLINE});
        cancel({caller: canceler, bridgeTx: bridgedEthTx});
        assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED);
        assertEq(userA.balance, initialUserBalanceEth + ethParams.originAmount);
        assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth - ethParams.originAmount);
    }

    function test_cancelPermissionless_eth() public {
        bytes32 txId = getTxId(bridgedEthTx);
        skipTimeAtLeast({time: DEADLINE + PERMISSIONLESS_CANCEL_DELAY});
        cancel({caller: userB, bridgeTx: bridgedEthTx});
        assertEq(fastBridge.bridgeStatuses(txId), IFastBridgeV2.BridgeStatus.REFUNDED);
        assertEq(userA.balance, initialUserBalanceEth + ethParams.originAmount);
        assertEq(address(fastBridge).balance, initialFastBridgeBalanceEth - ethParams.originAmount);
    }
}