Skip to content

Commit

Permalink
improve: add tests for zkstack adapters (#635)
Browse files Browse the repository at this point in the history
Signed-off-by: bennett <[email protected]>
  • Loading branch information
bmzig authored Oct 8, 2024
1 parent e38a2b7 commit 1416899
Show file tree
Hide file tree
Showing 5 changed files with 466 additions and 158 deletions.
80 changes: 1 addition & 79 deletions contracts/chain-adapters/ZkStack_Adapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,85 +6,7 @@ import "../external/interfaces/WETH9Interface.sol";

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

// The BridgeHub is the main interaction point for bridging into ZkStack chains.
interface BridgeHubInterface {
struct L2TransactionRequestDirect {
uint256 chainId;
uint256 mintValue;
address l2Contract;
uint256 l2Value;
bytes l2Calldata;
uint256 l2GasLimit;
uint256 l2GasPerPubdataByteLimit;
bytes[] factoryDeps;
address refundRecipient;
}

/**
* @notice the mailbox is called directly after the sharedBridge received the deposit.
* This assumes that either ether is the base token or the msg.sender has approved mintValue allowance for the
* sharedBridge. This means this is not ideal for contract calls, as the contract would have to handle token
* allowance of the base Token.
* @param _request the direct request.
*/
function requestL2TransactionDirect(L2TransactionRequestDirect calldata _request)
external
payable
returns (bytes32 canonicalTxHash);

struct L2TransactionRequestTwoBridgesOuter {
uint256 chainId;
uint256 mintValue;
uint256 l2Value;
uint256 l2GasLimit;
uint256 l2GasPerPubdataByteLimit;
address refundRecipient;
address secondBridgeAddress;
uint256 secondBridgeValue;
bytes secondBridgeCalldata;
}

/**
* @notice After depositing funds to the sharedBridge, the secondBridge is called to return the actual L2 message
* which is sent to the Mailbox. This assumes that either ether is the base token or the msg.sender has approved
* the sharedBridge with the mintValue, and also the necessary approvals are given for the second bridge. The logic
* of this bridge is to allow easy depositing for bridges. Each contract that handles the users ERC20 tokens needs
* approvals from the user, this contract allows the user to approve for each token only its respective bridge.
* This function is great for contract calls to L2, the secondBridge can be any contract.
* @param _request the two bridges request.
*/
function requestL2TransactionTwoBridges(L2TransactionRequestTwoBridgesOuter calldata _request)
external
payable
returns (bytes32 canonicalTxHash);

/**
* @notice Gets the shared bridge.
* @dev The shared bridge manages ERC20 tokens.
*/
function sharedBridge() external view returns (address);

/**
* @notice Gets the base token for a chain.
* @dev Base token == native token.
*/
function baseToken(uint256 _chainId) external view returns (address);

/**
* @notice Computes the base transaction cost for a transaction.
* @param _chainId the chain the transaction is being sent to.
* @param _gasPrice the l1 gas price at time of execution.
* @param _l2GasLimit the gas limit for the l2 transaction.
* @param _l2GasPerPubdataByteLimit configuration value that changes infrequently.
*/
function l2TransactionBaseCost(
uint256 _chainId,
uint256 _gasPrice,
uint256 _l2GasLimit,
uint256 _l2GasPerPubdataByteLimit
) external view returns (uint256);
}
import { BridgeHubInterface } from "../interfaces/ZkStackBridgeHub.sol";

/**
* @notice Contract containing logic to send messages from L1 to ZkStack with ETH as the gas token.
Expand Down
80 changes: 1 addition & 79 deletions contracts/chain-adapters/ZkStack_CustomGasToken_Adapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,85 +6,7 @@ import "../external/interfaces/WETH9Interface.sol";

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

// The BridgeHub is the main interaction point for briding into ZkStack chains.
interface BridgeHubInterface {
struct L2TransactionRequestDirect {
uint256 chainId;
uint256 mintValue;
address l2Contract;
uint256 l2Value;
bytes l2Calldata;
uint256 l2GasLimit;
uint256 l2GasPerPubdataByteLimit;
bytes[] factoryDeps;
address refundRecipient;
}

/**
* @notice the mailbox is called directly after the sharedBridge received the deposit.
* This assumes that either ether is the base token or the msg.sender has approved mintValue allowance for the
* sharedBridge. This means this is not ideal for contract calls, as the contract would have to handle token
* allowance of the base Token.
* @param _request the direct request.
*/
function requestL2TransactionDirect(L2TransactionRequestDirect calldata _request)
external
payable
returns (bytes32 canonicalTxHash);

struct L2TransactionRequestTwoBridgesOuter {
uint256 chainId;
uint256 mintValue;
uint256 l2Value;
uint256 l2GasLimit;
uint256 l2GasPerPubdataByteLimit;
address refundRecipient;
address secondBridgeAddress;
uint256 secondBridgeValue;
bytes secondBridgeCalldata;
}

/**
* @notice After depositing funds to the sharedBridge, the secondBridge is called to return the actual L2 message
* which is sent to the Mailbox. This assumes that either ether is the base token or the msg.sender has approved
* the sharedBridge with the mintValue, and also the necessary approvals are given for the second bridge. The logic
* of this bridge is to allow easy depositing for bridges. Each contract that handles the users ERC20 tokens needs
* approvals from the user, this contract allows the user to approve for each token only its respective bridge.
* This function is great for contract calls to L2, the secondBridge can be any contract.
* @param _request the two bridges request.
*/
function requestL2TransactionTwoBridges(L2TransactionRequestTwoBridgesOuter calldata _request)
external
payable
returns (bytes32 canonicalTxHash);

/**
* @notice Gets the shared bridge.
* @dev The shared bridge manages ERC20 tokens.
*/
function sharedBridge() external view returns (address);

/**
* @notice Gets the base token for a chain.
* @dev Base token == native token.
*/
function baseToken(uint256 _chainId) external view returns (address);

/**
* @notice Computes the base transaction cost for a transaction.
* @param _chainId the chain the transaction is being sent to.
* @param _gasPrice the l1 gas price at time of execution.
* @param _l2GasLimit the gas limit for the l2 transaction.
* @param _l2GasPerPubdataByteLimit configuration value that changes infrequently.
*/
function l2TransactionBaseCost(
uint256 _chainId,
uint256 _gasPrice,
uint256 _l2GasLimit,
uint256 _l2GasPerPubdataByteLimit
) external view returns (uint256);
}
import { BridgeHubInterface } from "../interfaces/ZkStackBridgeHub.sol";

/**
* @notice Interface for funder contract that this contract pulls from to pay for relayMessage()/relayTokens()
Expand Down
84 changes: 84 additions & 0 deletions contracts/interfaces/ZkStackBridgeHub.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

/**
* @title BridgeHubInterface
* @notice This interface is shared between ZkStack adapters. It is the main interaction point for bridging to ZkStack chains.
*/
interface BridgeHubInterface {
struct L2TransactionRequestDirect {
uint256 chainId;
uint256 mintValue;
address l2Contract;
uint256 l2Value;
bytes l2Calldata;
uint256 l2GasLimit;
uint256 l2GasPerPubdataByteLimit;
bytes[] factoryDeps;
address refundRecipient;
}

/**
* @notice the mailbox is called directly after the sharedBridge received the deposit.
* This assumes that either ether is the base token or the msg.sender has approved mintValue allowance for the
* sharedBridge. This means this is not ideal for contract calls, as the contract would have to handle token
* allowance of the base Token.
* @param _request the direct request.
*/
function requestL2TransactionDirect(L2TransactionRequestDirect calldata _request)
external
payable
returns (bytes32 canonicalTxHash);

struct L2TransactionRequestTwoBridgesOuter {
uint256 chainId;
uint256 mintValue;
uint256 l2Value;
uint256 l2GasLimit;
uint256 l2GasPerPubdataByteLimit;
address refundRecipient;
address secondBridgeAddress;
uint256 secondBridgeValue;
bytes secondBridgeCalldata;
}

/**
* @notice After depositing funds to the sharedBridge, the secondBridge is called to return the actual L2 message
* which is sent to the Mailbox. This assumes that either ether is the base token or the msg.sender has approved
* the sharedBridge with the mintValue, and also the necessary approvals are given for the second bridge. The logic
* of this bridge is to allow easy depositing for bridges. Each contract that handles the users ERC20 tokens needs
* approvals from the user, this contract allows the user to approve for each token only its respective bridge.
* This function is great for contract calls to L2, the secondBridge can be any contract.
* @param _request the two bridges request.
*/
function requestL2TransactionTwoBridges(L2TransactionRequestTwoBridgesOuter calldata _request)
external
payable
returns (bytes32 canonicalTxHash);

/**
* @notice Gets the shared bridge.
* @dev The shared bridge manages ERC20 tokens.
*/
function sharedBridge() external view returns (address);

/**
* @notice Gets the base token for a chain.
* @dev Base token == native token.
*/
function baseToken(uint256 _chainId) external view returns (address);

/**
* @notice Computes the base transaction cost for a transaction.
* @param _chainId the chain the transaction is being sent to.
* @param _gasPrice the l1 gas price at time of execution.
* @param _l2GasLimit the gas limit for the l2 transaction.
* @param _l2GasPerPubdataByteLimit configuration value that changes infrequently.
*/
function l2TransactionBaseCost(
uint256 _chainId,
uint256 _gasPrice,
uint256 _l2GasLimit,
uint256 _l2GasPerPubdataByteLimit
) external view returns (uint256);
}
47 changes: 47 additions & 0 deletions contracts/test/MockZkStackBridgeHub.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import { BridgeHubInterface } from "../interfaces/ZkStackBridgeHub.sol";

contract MockBridgeHub is BridgeHubInterface {
address public immutable sharedBridge;

constructor(address _sharedBridge) {
sharedBridge = _sharedBridge;
}

mapping(uint256 => address) baseTokens;

function setBaseToken(uint256 _chainId, address _baseToken) external {
baseTokens[_chainId] = _baseToken;
}

function requestL2TransactionDirect(L2TransactionRequestDirect calldata _request)
external
payable
returns (bytes32 canonicalTxHash)
{
canonicalTxHash = keccak256(abi.encode(_request));
}

function requestL2TransactionTwoBridges(L2TransactionRequestTwoBridgesOuter calldata _request)
external
payable
returns (bytes32 canonicalTxHash)
{
canonicalTxHash = keccak256(abi.encode(_request));
}

function baseToken(uint256 _chainId) external view returns (address) {
return baseTokens[_chainId] == address(0) ? address(1) : baseTokens[_chainId];
}

function l2TransactionBaseCost(
uint256,
uint256 _gasPrice,
uint256 _l2GasLimit,
uint256 _l2GasPerPubdataByteLimit
) external view returns (uint256) {
return _gasPrice + _l2GasLimit * _l2GasPerPubdataByteLimit;
}
}
Loading

0 comments on commit 1416899

Please sign in to comment.