Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions contracts/chain-adapters/ZkStack_Adapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -198,8 +198,8 @@ contract ZkStack_Adapter is AdapterInterface, CircleCCTPAdapter {
}
} else {
// An standard bridged ERC20, separate from WETH and Circle Bridged/Native USDC.
address sharedBridge = BRIDGE_HUB.sharedBridge();
IERC20(l1Token).forceApprove(sharedBridge, amount);
address assetRouter = BRIDGE_HUB.assetRouter();
IERC20(l1Token).forceApprove(assetRouter, amount);
txHash = BRIDGE_HUB.requestL2TransactionTwoBridges{ value: txBaseCost }(
BridgeHubInterface.L2TransactionRequestTwoBridgesOuter({
chainId: CHAIN_ID,
Expand All @@ -208,7 +208,7 @@ contract ZkStack_Adapter is AdapterInterface, CircleCCTPAdapter {
l2GasLimit: L2_GAS_LIMIT,
l2GasPerPubdataByteLimit: L1_GAS_TO_L2_GAS_PER_PUB_DATA_LIMIT,
refundRecipient: L2_REFUND_ADDRESS,
secondBridgeAddress: sharedBridge,
secondBridgeAddress: assetRouter,
secondBridgeValue: 0,
secondBridgeCalldata: _secondBridgeCalldata(to, l1Token, amount)
})
Expand Down
24 changes: 12 additions & 12 deletions contracts/chain-adapters/ZkStack_CustomGasToken_Adapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ contract ZkStack_CustomGasToken_Adapter is AdapterInterface, CircleCCTPAdapter {
*/
function relayMessage(address target, bytes memory message) external payable override {
uint256 txBaseCost = _pullCustomGas(L2_GAS_LIMIT);
IERC20(CUSTOM_GAS_TOKEN).forceApprove(BRIDGE_HUB.sharedBridge(), txBaseCost);
IERC20(CUSTOM_GAS_TOKEN).forceApprove(BRIDGE_HUB.assetRouter(), txBaseCost);

BRIDGE_HUB.requestL2TransactionDirect(
BridgeHubInterface.L2TransactionRequestDirect({
Expand Down Expand Up @@ -180,7 +180,7 @@ contract ZkStack_CustomGasToken_Adapter is AdapterInterface, CircleCCTPAdapter {
address to
) external payable override {
// The Hub Pool will always bridge via CCTP to a ZkStack network if CCTP is enabled for that network. Therefore, we can short-circuit ZkStack-specific logic
// like pulling custom gas or getting the shared bridge address if CCTP is enabled and we are bridging USDC.
// like pulling custom gas or getting the asset router address if CCTP is enabled and we are bridging USDC.
if (l1Token == address(usdcToken) && _isCCTPEnabled()) {
_transferUsdc(to, amount);
emit TokensRelayed(l1Token, l2Token, amount, to);
Expand All @@ -189,16 +189,16 @@ contract ZkStack_CustomGasToken_Adapter is AdapterInterface, CircleCCTPAdapter {
// A bypass proxy seems to no longer be needed to avoid deposit limits. The tracking of these limits seems to be deprecated.
// See: https://github.com/matter-labs/era-contracts/blob/bce4b2d0f34bd87f1aaadd291772935afb1c3bd6/l1-contracts/contracts/bridge/L1ERC20Bridge.sol#L54-L55
uint256 txBaseCost = _pullCustomGas(L2_GAS_LIMIT);
address sharedBridge = BRIDGE_HUB.sharedBridge();
address assetRouter = BRIDGE_HUB.assetRouter();

bytes32 txHash;
if (l1Token == address(L1_WETH)) {
// If the l1Token is WETH then unwrap it to ETH then send the ETH to the standard bridge along with the base
// cost of custom gas tokens.
L1_WETH.withdraw(amount);
IERC20(CUSTOM_GAS_TOKEN).forceApprove(sharedBridge, txBaseCost);
// Note: When bridging ETH with `L2TransactionRequestTwoBridgesOuter`, the second bridge must be 0 for the shared bridge call to not revert.
// https://github.com/matter-labs/era-contracts/blob/aafee035db892689df3f7afe4b89fd6467a39313/l1-contracts/contracts/bridge/L1SharedBridge.sol#L328
IERC20(CUSTOM_GAS_TOKEN).forceApprove(assetRouter, txBaseCost);
// Note: When bridging ETH with `L2TransactionRequestTwoBridgesOuter`, the second bridge must be 0 for the asset router call to not revert.
// https://github.com/matter-labs/era-contracts/blob/aafee035db892689df3f7afe4b89fd6467a39313/l1-contracts/contracts/bridge/L1AssetRouter.sol#L328
txHash = BRIDGE_HUB.requestL2TransactionTwoBridges{ value: amount }(
BridgeHubInterface.L2TransactionRequestTwoBridgesOuter({
chainId: CHAIN_ID,
Expand All @@ -207,14 +207,14 @@ contract ZkStack_CustomGasToken_Adapter is AdapterInterface, CircleCCTPAdapter {
l2GasLimit: L2_GAS_LIMIT,
l2GasPerPubdataByteLimit: L1_GAS_TO_L2_GAS_PER_PUB_DATA_LIMIT,
refundRecipient: L2_REFUND_ADDRESS,
secondBridgeAddress: sharedBridge,
secondBridgeAddress: assetRouter,
secondBridgeValue: amount,
secondBridgeCalldata: _secondBridgeCalldata(to, ETH_TOKEN_ADDRESS, 0)
})
);
} else if (l1Token == CUSTOM_GAS_TOKEN) {
// The chain's custom gas token.
IERC20(l1Token).forceApprove(sharedBridge, txBaseCost + amount);
IERC20(l1Token).forceApprove(assetRouter, txBaseCost + amount);
txHash = BRIDGE_HUB.requestL2TransactionDirect(
BridgeHubInterface.L2TransactionRequestDirect({
chainId: CHAIN_ID,
Expand All @@ -230,7 +230,7 @@ contract ZkStack_CustomGasToken_Adapter is AdapterInterface, CircleCCTPAdapter {
);
} else if (l1Token == address(usdcToken)) {
// Since we already checked if we are bridging USDC via CCTP, if this conditional is hit, then we must be bridging USDC via the `USDC_SHARED_BRIDGE`.
IERC20(CUSTOM_GAS_TOKEN).forceApprove(sharedBridge, txBaseCost);
IERC20(CUSTOM_GAS_TOKEN).forceApprove(assetRouter, txBaseCost);
IERC20(l1Token).forceApprove(USDC_SHARED_BRIDGE, amount);
txHash = BRIDGE_HUB.requestL2TransactionTwoBridges(
BridgeHubInterface.L2TransactionRequestTwoBridgesOuter({
Expand All @@ -247,8 +247,8 @@ contract ZkStack_CustomGasToken_Adapter is AdapterInterface, CircleCCTPAdapter {
);
} else {
// An standard bridged ERC20, separate from WETH and Circle Bridged/Native USDC.
IERC20(CUSTOM_GAS_TOKEN).forceApprove(sharedBridge, txBaseCost);
IERC20(l1Token).forceApprove(sharedBridge, amount);
IERC20(CUSTOM_GAS_TOKEN).forceApprove(assetRouter, txBaseCost);
IERC20(l1Token).forceApprove(assetRouter, amount);
txHash = BRIDGE_HUB.requestL2TransactionTwoBridges(
BridgeHubInterface.L2TransactionRequestTwoBridgesOuter({
chainId: CHAIN_ID,
Expand All @@ -257,7 +257,7 @@ contract ZkStack_CustomGasToken_Adapter is AdapterInterface, CircleCCTPAdapter {
l2GasLimit: L2_GAS_LIMIT,
l2GasPerPubdataByteLimit: L1_GAS_TO_L2_GAS_PER_PUB_DATA_LIMIT,
refundRecipient: L2_REFUND_ADDRESS,
secondBridgeAddress: sharedBridge,
secondBridgeAddress: assetRouter,
secondBridgeValue: 0,
secondBridgeCalldata: _secondBridgeCalldata(to, l1Token, amount)
})
Expand Down
14 changes: 7 additions & 7 deletions contracts/interfaces/ZkStackBridgeHub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ interface BridgeHubInterface {
}

/**
* @notice the mailbox is called directly after the sharedBridge received the deposit.
* @notice the mailbox is called directly after the asset router 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
* asset router. 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.
*/
Expand All @@ -42,9 +42,9 @@ interface BridgeHubInterface {
}

/**
* @notice After depositing funds to the sharedBridge, the secondBridge is called to return the actual L2 message
* @notice After depositing funds to the asset router, 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
* the asset router 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.
Expand All @@ -55,10 +55,10 @@ interface BridgeHubInterface {
) external payable returns (bytes32 canonicalTxHash);

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

/**
* @notice Gets the base token for a chain.
Expand Down
6 changes: 3 additions & 3 deletions contracts/test/MockZkStackBridgeHub.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ pragma solidity ^0.8.0;
import { BridgeHubInterface } from "../interfaces/ZkStackBridgeHub.sol";

contract MockBridgeHub is BridgeHubInterface {
address public immutable sharedBridge;
address public immutable assetRouter;

constructor(address _sharedBridge) {
sharedBridge = _sharedBridge;
constructor(address _assetRouter) {
assetRouter = _assetRouter;
}

mapping(uint256 => address) baseTokens;
Expand Down
35 changes: 19 additions & 16 deletions test/evm/foundry/local/ZkStack_Adapter.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import { IL1StandardBridge } from "@eth-optimism/contracts/L1/messaging/IL1Stand
import { FinderInterface } from "../../../../contracts/external/uma/core/contracts/data-verification-mechanism/interfaces/FinderInterface.sol";

import { ZkStack_Adapter } from "../../../../contracts/chain-adapters/ZkStack_Adapter.sol";
import { ZkStack_CustomGasToken_Adapter, FunderInterface } from "../../../../contracts/chain-adapters/ZkStack_CustomGasToken_Adapter.sol";
import {
ZkStack_CustomGasToken_Adapter,
FunderInterface
} from "../../../../contracts/chain-adapters/ZkStack_CustomGasToken_Adapter.sol";
import { AdapterInterface } from "../../../../contracts/chain-adapters/interfaces/AdapterInterface.sol";
import { WETH9Interface } from "../../../../contracts/external/interfaces/WETH9Interface.sol";
import { WETH9 } from "../../../../contracts/external/WETH9.sol";
Expand Down Expand Up @@ -116,7 +119,7 @@ contract ZkStackAdapterTest is Test {
MockBridgeHub bridgeHub;

address owner;
address sharedBridge;
address assetRouter;
address usdcSharedBridge;
uint256 baseCost;

Expand All @@ -127,8 +130,8 @@ contract ZkStackAdapterTest is Test {

function setUp() public {
owner = makeAddr("owner");
sharedBridge = makeAddr("sharedBridge");
usdcSharedBridge = makeAddr("sharedBridge");
assetRouter = makeAddr("assetRouter");
usdcSharedBridge = makeAddr("usdcSharedBridge");

l1Token = new Token_ERC20("l1Token", "l1Token");
l2Token = new Token_ERC20("l2Token", "l2Token");
Expand All @@ -140,7 +143,7 @@ contract ZkStackAdapterTest is Test {
l2Weth = new WETH9();

MockFunder funder = new MockFunder();
bridgeHub = new MockBridgeHub(sharedBridge);
bridgeHub = new MockBridgeHub(assetRouter);

bridgeHub.setBaseToken(ZK_ALT_CHAIN_ID, address(l1CustomGasToken));
baseCost = bridgeHub.l2TransactionBaseCost(0, 0, L2_GAS_LIMIT, L2_GAS_PER_PUBDATA_LIMIT);
Expand Down Expand Up @@ -233,7 +236,7 @@ contract ZkStackAdapterTest is Test {
vm.stopPrank();
}

// Sending USDC should construct a L2TransactionTwoBridges struct with the shared bridge as the USDC shared bridge.
// Sending USDC should construct a L2TransactionTwoBridges struct with the USDC shared bridge as the second bridge.
function testRelayUsdc(uint256 amountToSend, address random) public {
amountToSend = uint256(bound(amountToSend, 1, type(uint256).max));
l1Usdc.mint(address(zksAdapter), amountToSend);
Expand Down Expand Up @@ -271,7 +274,7 @@ contract ZkStackAdapterTest is Test {
l2GasLimit: L2_GAS_LIMIT,
l2GasPerPubdataByteLimit: L2_GAS_PER_PUBDATA_LIMIT,
refundRecipient: owner,
secondBridgeAddress: sharedBridge,
secondBridgeAddress: assetRouter,
secondBridgeValue: 0,
secondBridgeCalldata: abi.encode(address(l1Token), amountToSend, random)
})
Expand Down Expand Up @@ -308,7 +311,7 @@ contract ZkStackAdapterTest is Test {
emit AdapterInterface.MessageRelayed(target, message);
zksCustomGasAdapter.relayMessage(target, message);
// Approve only the amount of the fee of the custom gas token. Since we don't actually transferFrom, the approval should stand.
assertEq(l1CustomGasToken.allowance(address(zksCustomGasAdapter), sharedBridge), baseCost);
assertEq(l1CustomGasToken.allowance(address(zksCustomGasAdapter), assetRouter), baseCost);
}

// Sending Weth should be a TwoBridgesOuter struct, should withdraw weth that it owns, and then call the l2 bridge with the token address as address(1).
Expand All @@ -329,7 +332,7 @@ contract ZkStackAdapterTest is Test {
l2GasLimit: L2_GAS_LIMIT,
l2GasPerPubdataByteLimit: L2_GAS_PER_PUBDATA_LIMIT,
refundRecipient: owner,
secondBridgeAddress: sharedBridge,
secondBridgeAddress: assetRouter,
secondBridgeValue: amountToSend,
secondBridgeCalldata: abi.encode(address(1), 0, random)
})
Expand All @@ -341,7 +344,7 @@ contract ZkStackAdapterTest is Test {
zksCustomGasAdapter.relayTokens(address(l1Weth), address(l2Weth), amountToSend, random);
vm.stopPrank();
// Approve only the amount of the fee of the custom gas token. Since we don't actually transferFrom, the approval should stand.
assertEq(l1CustomGasToken.allowance(address(zksCustomGasAdapter), sharedBridge), baseCost);
assertEq(l1CustomGasToken.allowance(address(zksCustomGasAdapter), assetRouter), baseCost);
}

// There should be a hash from L2TransactionTwoBridges with the USDC shared bridge as the second bridge address, an approval for the fee amount from the
Expand All @@ -367,7 +370,7 @@ contract ZkStackAdapterTest is Test {
emit AdapterInterface.TokensRelayed(address(l1Usdc), address(l2Usdc), amountToSend, random);
zksCustomGasAdapter.relayTokens(address(l1Usdc), address(l2Usdc), amountToSend, random);
// Approve only the amount of the fee of the custom gas token. Since we don't actually transferFrom, the approval should stand.
assertEq(l1CustomGasToken.allowance(address(zksCustomGasAdapter), usdcSharedBridge), baseCost);
assertEq(l1CustomGasToken.allowance(address(zksCustomGasAdapter), assetRouter), baseCost);
assertEq(l1Usdc.allowance(address(zksCustomGasAdapter), usdcSharedBridge), amountToSend);
}

Expand All @@ -383,7 +386,7 @@ contract ZkStackAdapterTest is Test {
l2GasLimit: L2_GAS_LIMIT,
l2GasPerPubdataByteLimit: L2_GAS_PER_PUBDATA_LIMIT,
refundRecipient: owner,
secondBridgeAddress: sharedBridge,
secondBridgeAddress: assetRouter,
secondBridgeValue: 0,
secondBridgeCalldata: abi.encode(address(l1Token), amountToSend, random)
})
Expand All @@ -394,8 +397,8 @@ contract ZkStackAdapterTest is Test {
emit AdapterInterface.TokensRelayed(address(l1Token), address(l2Token), amountToSend, random);
zksCustomGasAdapter.relayTokens(address(l1Token), address(l2Token), amountToSend, random);
// Approve only the amount of the fee of the custom gas token. Since we don't actually transferFrom, the approval should stand.
assertEq(l1CustomGasToken.allowance(address(zksCustomGasAdapter), sharedBridge), baseCost);
assertEq(l1Token.allowance(address(zksCustomGasAdapter), sharedBridge), amountToSend);
assertEq(l1CustomGasToken.allowance(address(zksCustomGasAdapter), assetRouter), baseCost);
assertEq(l1Token.allowance(address(zksCustomGasAdapter), assetRouter), amountToSend);
}

// There should be a hash of L2TransactionDirect and an approval for the custom gas token.
Expand All @@ -421,7 +424,7 @@ contract ZkStackAdapterTest is Test {
zksCustomGasAdapter.relayTokens(address(l1CustomGasToken), address(l2CustomGasToken), amountToSend, random);

// Approve only the amount of the fee of the custom gas token. Since we don't actually transferFrom, the approval should stand.
assertEq(l1CustomGasToken.allowance(address(zksCustomGasAdapter), sharedBridge), baseCost + amountToSend);
assertEq(l1Token.allowance(address(zksCustomGasAdapter), sharedBridge), 0);
assertEq(l1CustomGasToken.allowance(address(zksCustomGasAdapter), assetRouter), baseCost + amountToSend);
assertEq(l1Token.allowance(address(zksCustomGasAdapter), assetRouter), 0);
}
}
Loading