Skip to content

Commit

Permalink
Merge pull request #37 from OffchainLabs/fee-token-deployer
Browse files Browse the repository at this point in the history
Add support to do single-TX deployment of token bridge for fee token based Orbit chains
  • Loading branch information
gvladika authored Sep 11, 2023
2 parents 07234a0 + b408bcf commit ce11f80
Show file tree
Hide file tree
Showing 21 changed files with 1,328 additions and 1,316 deletions.
17 changes: 13 additions & 4 deletions .env-sample
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
## Goerli - Deploy L1TokenBridgeCreator
ARB_GOERLI_RPC=""
ARB_GOERLI_DEPLOYER_KEY=""
ORBIT_RPC=""
## Rollup on top of which token bridge will be created
ROLLUP_ADDRESS=""
ROLLUP_OWNER=""
L1_TOKEN_BRIDGE_CREATOR=""
# needed for verification
L1_RETRYABLE_SENDER=""

## RPC endpoints
BASECHAIN_RPC=""
ORBIT_RPC=""

## Deployer key used for deploying creator and creating token bridge
BASECHAIN_DEPLOYER_KEY=""
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
.gitignore
.env
node_modules
.vscode/

#Hardhat files
cache
Expand Down
209 changes: 148 additions & 61 deletions contracts/tokenbridge/arbitrum/L2AtomicTokenBridgeFactory.sol

Large diffs are not rendered by default.

525 changes: 389 additions & 136 deletions contracts/tokenbridge/ethereum/L1AtomicTokenBridgeCreator.sol

Large diffs are not rendered by default.

117 changes: 105 additions & 12 deletions contracts/tokenbridge/ethereum/L1TokenBridgeRetryableSender.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,20 @@
pragma solidity ^0.8.4;

import {IInbox} from "@arbitrum/nitro-contracts/src/bridge/IInbox.sol";
import {L2AtomicTokenBridgeFactory, L2RuntimeCode} from "../arbitrum/L2AtomicTokenBridgeFactory.sol";
import {Initializable, OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {
L2AtomicTokenBridgeFactory,
L2RuntimeCode,
ProxyAdmin
} from "../arbitrum/L2AtomicTokenBridgeFactory.sol";
import {
Initializable,
OwnableUpgradeable
} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
import {TransparentUpgradeableProxy} from
"@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

/**
* @title Token Bridge Retryable Ticket Sender
Expand All @@ -26,13 +38,14 @@ contract L1TokenBridgeRetryableSender is Initializable, OwnableUpgradeable {
* @dev Function will build retryable data, calculate submission cost and retryable value, create retryable
* and then refund the remaining funds to original delpoyer.
*/
function sendRetryable(
function sendRetryableUsingEth(
RetryableParams calldata retryableParams,
L2TemplateAddresses calldata l2,
L1Addresses calldata l1,
L1DeploymentAddresses calldata l1,
address l2StandardGatewayAddress,
address rollupOwner,
address deployer
address deployer,
address aliasedL1UpgradeExecutor
) external payable onlyOwner {
bytes memory data = abi.encodeCall(
L2AtomicTokenBridgeFactory.deployL2Contracts,
Expand All @@ -42,29 +55,75 @@ contract L1TokenBridgeRetryableSender is Initializable, OwnableUpgradeable {
l2.standardGatewayTemplate.code,
l2.customGatewayTemplate.code,
l2.wethGatewayTemplate.code,
l2.wethTemplate.code
l2.wethTemplate.code,
l2.upgradeExecutorTemplate.code,
l2.multicallTemplate.code
),
l1.router,
l1.standardGateway,
l1.customGateway,
l1.wethGateway,
l1.weth,
l2StandardGatewayAddress,
rollupOwner
rollupOwner,
aliasedL1UpgradeExecutor
)
);

uint256 maxSubmissionCost = IInbox(retryableParams.inbox).calculateRetryableSubmissionFee(data.length, 0);
uint256 retryableValue = maxSubmissionCost + retryableParams.maxGas * retryableParams.gasPriceBid;
_createRetryable(retryableParams, maxSubmissionCost, retryableValue, data);
uint256 maxSubmissionCost =
IInbox(retryableParams.inbox).calculateRetryableSubmissionFee(data.length, 0);
uint256 retryableValue =
maxSubmissionCost + retryableParams.maxGas * retryableParams.gasPriceBid;
_createRetryableUsingEth(retryableParams, maxSubmissionCost, retryableValue, data);

// refund excess value to the deployer
uint256 refund = msg.value - retryableValue;
(bool success,) = deployer.call{value: refund}("");
if (!success) revert L1TokenBridgeRetryableSender_RefundFailed();
}

function _createRetryable(
/**
* @notice Creates retryable which deploys L2 side of the token bridge.
* @dev Function will build retryable data, calculate submission cost and retryable value, create retryable
* and then refund the remaining funds to original delpoyer.
*/
function sendRetryableUsingFeeToken(
RetryableParams calldata retryableParams,
L2TemplateAddresses calldata l2,
L1DeploymentAddresses calldata l1,
address l2StandardGatewayAddress,
address rollupOwner,
address aliasedL1UpgradeExecutor
) external payable onlyOwner {
bytes memory data = abi.encodeCall(
L2AtomicTokenBridgeFactory.deployL2Contracts,
(
L2RuntimeCode(
l2.routerTemplate.code,
l2.standardGatewayTemplate.code,
l2.customGatewayTemplate.code,
"",
"",
l2.upgradeExecutorTemplate.code,
l2.multicallTemplate.code
),
l1.router,
l1.standardGateway,
l1.customGateway,
address(0),
address(0),
l2StandardGatewayAddress,
rollupOwner,
aliasedL1UpgradeExecutor
)
);

uint256 retryableFee = retryableParams.maxGas * retryableParams.gasPriceBid;

_createRetryableUsingFeeToken(retryableParams, retryableFee, data);
}

function _createRetryableUsingEth(
RetryableParams calldata retryableParams,
uint256 maxSubmissionCost,
uint256 value,
Expand All @@ -81,6 +140,24 @@ contract L1TokenBridgeRetryableSender is Initializable, OwnableUpgradeable {
data
);
}

function _createRetryableUsingFeeToken(
RetryableParams calldata retryableParams,
uint256 retryableFee,
bytes memory data
) internal {
IERC20Inbox(retryableParams.inbox).createRetryableTicket(
retryableParams.target,
0,
0,
retryableParams.excessFeeRefundAddress,
retryableParams.callValueRefundAddress,
retryableParams.maxGas,
retryableParams.gasPriceBid,
retryableFee,
data
);
}
}

/**
Expand All @@ -104,15 +181,31 @@ struct L2TemplateAddresses {
address customGatewayTemplate;
address wethGatewayTemplate;
address wethTemplate;
address upgradeExecutorTemplate;
address multicallTemplate;
}

/**
* L1 side of token bridge addresses
*/
struct L1Addresses {
struct L1DeploymentAddresses {
address router;
address standardGateway;
address customGateway;
address wethGateway;
address weth;
}

interface IERC20Inbox {
function createRetryableTicket(
address to,
uint256 l2CallValue,
uint256 maxSubmissionCost,
address excessFeeRefundAddress,
address callValueRefundAddress,
uint256 gasLimit,
uint256 maxFeePerGas,
uint256 tokenTotalFeeAmount,
bytes calldata data
) external returns (uint256);
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ contract L1OrbitERC20Gateway is L1ERC20Gateway {
/**
* @notice get rollup's native token that's used to pay for fees
*/
function _getNativeFeeToken() internal returns (address) {
function _getNativeFeeToken() internal view returns (address) {
address bridge = address(getBridge(inbox));
return IERC20Bridge(bridge).nativeToken();
}
Expand Down
2 changes: 1 addition & 1 deletion contracts/tokenbridge/libraries/IERC20Bridge.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ interface IERC20Bridge {
/**
* @dev token that is escrowed in bridge on L1 side and minted on L2 as native currency. Also fees are paid in this token.
*/
function nativeToken() external returns (address);
function nativeToken() external view returns (address);
}
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ libs = ["node_modules", "lib"]
test = 'test-foundry'
cache_path = 'forge-cache'
optimizer = true
optimizer_runs = 20000
optimizer_runs = 100
via_ir = false

[fmt]
Expand Down
2 changes: 1 addition & 1 deletion hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const config = {
},
},
{
version: '0.8.17',
version: '0.8.16',
settings: {
optimizer: {
enabled: true,
Expand Down
7 changes: 3 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@
"test:unit": "forge test",
"test:e2e:local-env": "yarn hardhat test test-e2e/*",
"test:storage": "./scripts/storage_layout_test.bash",
"deploy:orbit-tokenbridge:script": "ts-node ./scripts/local-deployment/orbitTokenBridgeDeployer.ts",
"deploy:local:token-bridge": "ts-node ./scripts/local-deployment/deploy.ts",
"deploy:local:token-bridge": "ts-node ./scripts/local-deployment/deployCreatorAndCreateTokenBridge.ts",
"deploy:goerli:token-bridge-creator": "ts-node ./scripts/goerli-deployment/deployTokenBridgeCreator.ts",
"create:goerli:token-bridge": "ts-node ./scripts/goerli-deployment/createTokenBridge.ts",
"test:tokenbridge:deployment": "hardhat test test-e2e/tokenBridgeDeploymentTest.ts",
Expand All @@ -39,9 +38,9 @@
],
"dependencies": {
"@arbitrum/nitro-contracts": "^1.0.0-beta.8",
"@arbitrum/sdk": "^3.1.5",
"@openzeppelin/contracts": "4.8.3",
"@openzeppelin/contracts-upgradeable": "4.8.3"
"@openzeppelin/contracts-upgradeable": "4.8.3",
"@offchainlabs/upgrade-executor": "^1.0.0-beta.2"
},
"devDependencies": {
"@arbitrum/sdk": "^3.1.3",
Expand Down
Loading

0 comments on commit ce11f80

Please sign in to comment.