Skip to content

Commit

Permalink
learn test
Browse files Browse the repository at this point in the history
  • Loading branch information
liusanchuan committed Jan 31, 2023
1 parent 269a6e4 commit dc9c463
Show file tree
Hide file tree
Showing 2 changed files with 228 additions and 0 deletions.
109 changes: 109 additions & 0 deletions src/test/bridges/nft_trading/ExampleE2E.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2022 Aztec.
pragma solidity >=0.8.4;

import {BridgeTestBase} from "./../../aztec/base/BridgeTestBase.sol";
import {AztecTypes} from "rollup-encoder/libraries/AztecTypes.sol";

// Example-specific imports
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ExampleBridge} from "../../../bridges/example/ExampleBridge.sol";
import {ErrorLib} from "../../../bridges/base/ErrorLib.sol";

/**
* @notice The purpose of this test is to test the bridge in an environment that is as close to the final deployment
* as possible without spinning up all the rollup infrastructure (sequencer, proof generator etc.).
*/
contract ExampleE2ETest is BridgeTestBase {
address public constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
address private constant BENEFICIARY = address(11);

// The reference to the example bridge
ExampleBridge internal bridge;
// To store the id of the example bridge after being added
uint256 private id;

function setUp() public {
// Deploy a new example bridge
bridge = new ExampleBridge(address(ROLLUP_PROCESSOR));

// Use the label cheatcode to mark the address with "Example Bridge" in the traces
vm.label(address(bridge), "Example Bridge");

// Impersonate the multi-sig to add a new bridge
vm.startPrank(MULTI_SIG);

// List the example-bridge with a gasLimit of 120k
// WARNING: If you set this value too low the interaction will fail for seemingly no reason!
// OTOH if you se it too high bridge users will pay too much
ROLLUP_PROCESSOR.setSupportedBridge(address(bridge), 120000);

// List USDC with a gasLimit of 100k
// Note: necessary for assets which are not already registered on RollupProcessor
// Call https://etherscan.io/address/0xFF1F2B4ADb9dF6FC8eAFecDcbF96A2B351680455#readProxyContract#F25 to get
// addresses of all the listed ERC20 tokens
ROLLUP_PROCESSOR.setSupportedAsset(USDC, 100000);

vm.stopPrank();

// Fetch the id of the example bridge
id = ROLLUP_PROCESSOR.getSupportedBridgesLength();

// Subsidize the bridge when used with USDC and register a beneficiary
AztecTypes.AztecAsset memory usdcAsset = ROLLUP_ENCODER.getRealAztecAsset(USDC);
uint256 criteria = bridge.computeCriteria(usdcAsset, emptyAsset, usdcAsset, emptyAsset, 0);
uint32 gasPerMinute = 200;
SUBSIDY.subsidize{value: 1 ether}(address(bridge), criteria, gasPerMinute);

SUBSIDY.registerBeneficiary(BENEFICIARY);

// Set the rollupBeneficiary on BridgeTestBase so that it gets included in the proofData
ROLLUP_ENCODER.setRollupBeneficiary(BENEFICIARY);
}

// @dev In order to avoid overflows we set _depositAmount to be uint96 instead of uint256.
function testExampleBridgeE2ETest(uint96 _depositAmount) public {
vm.assume(_depositAmount > 1);
vm.warp(block.timestamp + 1 days);

// Use the helper function to fetch the support AztecAsset for DAI
AztecTypes.AztecAsset memory usdcAsset = ROLLUP_ENCODER.getRealAztecAsset(address(USDC));

// Mint the depositAmount of Dai to rollupProcessor
deal(USDC, address(ROLLUP_PROCESSOR), _depositAmount);

// Computes the encoded data for the specific bridge interaction
ROLLUP_ENCODER.defiInteractionL2(id, usdcAsset, emptyAsset, usdcAsset, emptyAsset, 0, _depositAmount);

// Execute the rollup with the bridge interaction. Ensure that event as seen above is emitted.
(uint256 outputValueA, uint256 outputValueB, bool isAsync) = ROLLUP_ENCODER.processRollupAndGetBridgeResult();

// Note: Unlike in unit tests there is no need to manually transfer the tokens - RollupProcessor does this

// Check the output values are as expected
assertEq(outputValueA, _depositAmount, "outputValueA doesn't equal deposit");
assertEq(outputValueB, 0, "Non-zero outputValueB");
assertFalse(isAsync, "Bridge is not synchronous");

// Check that the balance of the rollup is same as before interaction (bridge just sends funds back)
assertEq(_depositAmount, IERC20(USDC).balanceOf(address(ROLLUP_PROCESSOR)), "Balances must match");

// Perform a second rollup with half the deposit, perform similar checks.
uint256 secondDeposit = _depositAmount / 2;

ROLLUP_ENCODER.defiInteractionL2(id, usdcAsset, emptyAsset, usdcAsset, emptyAsset, 0, secondDeposit);

// Execute the rollup with the bridge interaction. Ensure that event as seen above is emitted.
(outputValueA, outputValueB, isAsync) = ROLLUP_ENCODER.processRollupAndGetBridgeResult();

// Check the output values are as expected
assertEq(outputValueA, secondDeposit, "outputValueA doesn't equal second deposit");
assertEq(outputValueB, 0, "Non-zero outputValueB");
assertFalse(isAsync, "Bridge is not synchronous");

// Check that the balance of the rollup is same as before interaction (bridge just sends funds back)
assertEq(_depositAmount, IERC20(USDC).balanceOf(address(ROLLUP_PROCESSOR)), "Balances must match");

assertGt(SUBSIDY.claimableAmount(BENEFICIARY), 0, "Claimable was not updated");
}
}
119 changes: 119 additions & 0 deletions src/test/bridges/nft_trading/ExampleUnit.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2022 Aztec.
pragma solidity >=0.8.4;

import {BridgeTestBase} from "./../../aztec/base/BridgeTestBase.sol";
import {AztecTypes} from "rollup-encoder/libraries/AztecTypes.sol";

// Example-specific imports
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ExampleBridge} from "../../../bridges/example/ExampleBridge.sol";
import {ErrorLib} from "../../../bridges/base/ErrorLib.sol";

// @notice The purpose of this test is to directly test convert functionality of the bridge.
contract ExampleUnitTest is BridgeTestBase {
address private constant DAI = 0x6B175474E89094C44Da98b954EedeAC495271d0F;
address private constant BENEFICIARY = address(11);

address private rollupProcessor;
// The reference to the example bridge
ExampleBridge private bridge;

// @dev This method exists on RollupProcessor.sol. It's defined here in order to be able to receive ETH like a real
// rollup processor would.
function receiveEthFromBridge(uint256 _interactionNonce) external payable {}

function setUp() public {
// In unit tests we set address of rollupProcessor to the address of this test contract
rollupProcessor = address(this);

// Deploy a new example bridge
bridge = new ExampleBridge(rollupProcessor);

// Set ETH balance of bridge and BENEFICIARY to 0 for clarity (somebody sent ETH to that address on mainnet)
vm.deal(address(bridge), 0);
vm.deal(BENEFICIARY, 0);

// Use the label cheatcode to mark the address with "Example Bridge" in the traces
vm.label(address(bridge), "Example Bridge");

// Subsidize the bridge when used with Dai and register a beneficiary
AztecTypes.AztecAsset memory daiAsset = ROLLUP_ENCODER.getRealAztecAsset(DAI);
uint256 criteria = bridge.computeCriteria(daiAsset, emptyAsset, daiAsset, emptyAsset, 0);
uint32 gasPerMinute = 200;
SUBSIDY.subsidize{value: 1 ether}(address(bridge), criteria, gasPerMinute);

SUBSIDY.registerBeneficiary(BENEFICIARY);
}

function testInvalidCaller(address _callerAddress) public {
vm.assume(_callerAddress != rollupProcessor);
// Use HEVM cheatcode to call from a different address than is address(this)
vm.prank(_callerAddress);
vm.expectRevert(ErrorLib.InvalidCaller.selector);
bridge.convert(emptyAsset, emptyAsset, emptyAsset, emptyAsset, 0, 0, 0, address(0));
}

function testInvalidInputAssetType() public {
vm.expectRevert(ErrorLib.InvalidInputA.selector);
bridge.convert(emptyAsset, emptyAsset, emptyAsset, emptyAsset, 0, 0, 0, address(0));
}

function testInvalidOutputAssetType() public {
AztecTypes.AztecAsset memory inputAssetA =
AztecTypes.AztecAsset({id: 1, erc20Address: DAI, assetType: AztecTypes.AztecAssetType.ERC20});
vm.expectRevert(ErrorLib.InvalidOutputA.selector);
bridge.convert(inputAssetA, emptyAsset, emptyAsset, emptyAsset, 0, 0, 0, address(0));
}

function testExampleBridgeUnitTestFixed() public {
testExampleBridgeUnitTest(10 ether);
}

// @notice The purpose of this test is to directly test convert functionality of the bridge.
// @dev In order to avoid overflows we set _depositAmount to be uint96 instead of uint256.
function testExampleBridgeUnitTest(uint96 _depositAmount) public {
vm.warp(block.timestamp + 1 days);

// Define input and output assets
AztecTypes.AztecAsset memory inputAssetA =
AztecTypes.AztecAsset({id: 1, erc20Address: DAI, assetType: AztecTypes.AztecAssetType.ERC20});

AztecTypes.AztecAsset memory outputAssetA = inputAssetA;

// Rollup processor transfers ERC20 tokens to the bridge before calling convert. Since we are calling
// bridge.convert(...) function directly we have to transfer the funds in the test on our own. In this case
// we'll solve it by directly minting the _depositAmount of Dai to the bridge.
deal(DAI, address(bridge), _depositAmount);

// Store dai balance before interaction to be able to verify the balance after interaction is correct
uint256 daiBalanceBefore = IERC20(DAI).balanceOf(rollupProcessor);

(uint256 outputValueA, uint256 outputValueB, bool isAsync) = bridge.convert(
inputAssetA, // _inputAssetA - definition of an input asset
emptyAsset, // _inputAssetB - not used so can be left empty
outputAssetA, // _outputAssetA - in this example equal to input asset
emptyAsset, // _outputAssetB - not used so can be left empty
_depositAmount, // _totalInputValue - an amount of input asset A sent to the bridge
0, // _interactionNonce
0, // _auxData - not used in the example bridge
BENEFICIARY // _rollupBeneficiary - address, the subsidy will be sent to
);

// Now we transfer the funds back from the bridge to the rollup processor
// In this case input asset equals output asset so I only work with the input asset definition
// Basically in all the real world use-cases output assets would differ from input assets
IERC20(inputAssetA.erc20Address).transferFrom(address(bridge), rollupProcessor, outputValueA);

assertEq(outputValueA, _depositAmount, "Output value A doesn't equal deposit amount");
assertEq(outputValueB, 0, "Output value B is not 0");
assertTrue(!isAsync, "Bridge is incorrectly in an async mode");

uint256 daiBalanceAfter = IERC20(DAI).balanceOf(rollupProcessor);

assertEq(daiBalanceAfter - daiBalanceBefore, _depositAmount, "Balances must match");

SUBSIDY.withdraw(BENEFICIARY);
assertGt(BENEFICIARY.balance, 0, "Subsidy was not claimed");
}
}

0 comments on commit dc9c463

Please sign in to comment.