Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
a74b425
Scale retryable fee to fee token decimals denomination
gvladika Apr 24, 2024
fca3d6e
Approve the correct amount
gvladika Apr 24, 2024
645568e
Merge branch 'add-e2e-tests' into non-18-decimals
gvladika Apr 29, 2024
bc685e9
Provide retryable fee for creating child chain contract from creator …
gvladika Apr 30, 2024
c3de1de
Remove debug logs
gvladika Apr 30, 2024
886215d
Move back struct
gvladika Apr 30, 2024
745bfa5
Move to internal function
gvladika Apr 30, 2024
d9142a0
Call _getFeeToken only once, lower the contract size
gvladika May 6, 2024
f22e38e
Calculate fee amount denominated in fee token decimals
gvladika May 7, 2024
7870283
Fix default gateway deposit test
gvladika May 7, 2024
9ab28f6
Fix custom gateway deposit test
gvladika May 7, 2024
44bad07
Update slither db
gvladika May 7, 2024
6eb6ac4
Refactor _getScaledAmount to decreaase bytecode size
gvladika May 8, 2024
299c9a0
Signature change in retryable sender
gvladika May 8, 2024
5b3fd89
Use current token bridge branch in testnode
gvladika May 8, 2024
e928fef
Use temporarily custom branch for CI
gvladika May 8, 2024
183929b
Add CI job that runs over 6-decimals fee token chain
gvladika May 8, 2024
9cf47a3
Fix param name
gvladika May 8, 2024
37ee02a
Use custom testnode branch for decimals test
gvladika May 8, 2024
d85762a
Add unit tests to test 6 decimals fee token
gvladika May 9, 2024
07f5fe5
Add fuzz test for custom number of decimals in fee token
gvladika May 9, 2024
f5d4459
Merge branch 'add-e2e-tests' into non-18-decimals
gvladika May 10, 2024
eb12508
Merge branch 'add-e2e-tests' into non-18-decimals
gvladika May 10, 2024
2dab492
Update slither db
gvladika May 17, 2024
8b5c834
Merge branch 'main' into non-18-decimals
gvladika May 23, 2024
5c22f30
Bump nitro-contract for test
gvladika May 23, 2024
657972f
Use temporary for test until nitrp-contract official relese is bumped
gvladika May 23, 2024
61224dc
Update branch
gvladika Jul 8, 2024
01aa7fd
Bump node in CI
gvladika Jul 8, 2024
bdd49cd
Node18 expects 127.0.0.1 instead of localhost
gvladika Jul 8, 2024
0c71efa
Fix test input
gvladika Jul 8, 2024
33a2061
Use nitro-contracts develop
gvladika Jul 8, 2024
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
57 changes: 52 additions & 5 deletions .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- name: Setup node/yarn
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
cache: 'yarn'
cache-dependency-path: '**/yarn.lock'

Expand Down Expand Up @@ -53,7 +53,7 @@ jobs:
- name: Setup node/yarn
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
cache: 'yarn'
cache-dependency-path: '**/yarn.lock'

Expand Down Expand Up @@ -82,7 +82,7 @@ jobs:
- name: Setup node/yarn
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
cache: 'yarn'
cache-dependency-path: '**/yarn.lock'

Expand Down Expand Up @@ -111,11 +111,13 @@ jobs:
l3-node: true
no-token-bridge: true
no-l3-token-bridge: true
token-bridge-branch: '${{ github.head_ref }}'
nitro-testnode-ref: node-18

- name: Setup node/yarn
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
cache: 'yarn'
cache-dependency-path: '**/yarn.lock'

Expand Down Expand Up @@ -148,11 +150,56 @@ jobs:
args: --l3-fee-token
no-token-bridge: true
no-l3-token-bridge: true
token-bridge-branch: '${{ github.head_ref }}'
nitro-testnode-ref: node-18

- name: Setup node/yarn
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
cache: 'yarn'
cache-dependency-path: '**/yarn.lock'

- name: Install packages
run: yarn

- name: Compile contracts
run: yarn build

- name: Deploy creator and create token bridge
run: yarn deploy:local:token-bridge

- name: Verify deployed token bridge
run: yarn test:tokenbridge:deployment

- name: Verify creation code generation
run: yarn test:creation-code

- name: Test e2e orbit token bridge actions
run: yarn hardhat test test-e2e/orbitTokenBridge.ts

test-e2e-6-decimals-fee-token:
name: Test e2e on 6-decimals custom fee token chain
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive

- uses: OffchainLabs/actions/run-nitro-test-node@main
with:
l3-node: true
args: --l3-fee-token --l3-fee-token-decimals 6
no-token-bridge: true
no-l3-token-bridge: true
token-bridge-branch: '${{ github.head_ref }}'
nitro-contracts-branch: 'develop'
nitro-testnode-ref: 'non18-decimal-token-node-18'

- name: Setup node/yarn
uses: actions/setup-node@v3
with:
node-version: 18
cache: 'yarn'
cache-dependency-path: '**/yarn.lock'

Expand Down
3 changes: 2 additions & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
[submodule "lib/nitro-contracts"]
path = lib/nitro-contracts
url = https://github.com/OffchainLabs/nitro-contracts.git
branch = v1.2.1
branch = develop

123 changes: 85 additions & 38 deletions contracts/tokenbridge/ethereum/L1AtomicTokenBridgeCreator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
L2TemplateAddresses,
IERC20Inbox,
IERC20,
ERC20,
SafeERC20
} from "./L1TokenBridgeRetryableSender.sol";
import {L1GatewayRouter} from "./gateway/L1GatewayRouter.sol";
Expand Down Expand Up @@ -216,7 +217,7 @@ contract L1AtomicTokenBridgeCreator is Initializable, OwnableUpgradeable {
// deployment mappings should not be updated in case of resend
bool isResend = (inboxToL1Deployment[inbox].router != address(0));

bool isUsingFeeToken = _getFeeToken(inbox) != address(0);
address feeToken = _getFeeToken(inbox);

// store L2 addresses before deployments
L1DeploymentAddresses memory l1Deployment;
Expand All @@ -233,7 +234,7 @@ contract L1AtomicTokenBridgeCreator is Initializable, OwnableUpgradeable {
l2Deployment.router = _getProxyAddress(OrbitSalts.L2_ROUTER, chainId);
l2Deployment.standardGateway = _getProxyAddress(OrbitSalts.L2_STANDARD_GATEWAY, chainId);
l2Deployment.customGateway = _getProxyAddress(OrbitSalts.L2_CUSTOM_GATEWAY, chainId);
if (!isUsingFeeToken) {
if (feeToken == address(0)) {
l2Deployment.wethGateway = _getProxyAddress(OrbitSalts.L2_WETH_GATEWAY, chainId);
l2Deployment.weth = _getProxyAddress(OrbitSalts.L2_WETH, chainId);
}
Expand All @@ -256,7 +257,7 @@ contract L1AtomicTokenBridgeCreator is Initializable, OwnableUpgradeable {
if (!isResend) {
// l1 router deployment block
{
address routerTemplate = isUsingFeeToken
address routerTemplate = feeToken != address(0)
? address(l1Templates.feeTokenBasedRouterTemplate)
: address(l1Templates.routerTemplate);
l1Deployment.router = _deployProxyWithSalt(
Expand All @@ -266,7 +267,7 @@ contract L1AtomicTokenBridgeCreator is Initializable, OwnableUpgradeable {

// l1 standard gateway deployment block
{
address template = isUsingFeeToken
address template = feeToken != address(0)
? address(l1Templates.feeTokenBasedStandardGatewayTemplate)
: address(l1Templates.standardGatewayTemplate);

Expand All @@ -289,7 +290,7 @@ contract L1AtomicTokenBridgeCreator is Initializable, OwnableUpgradeable {

// l1 custom gateway deployment block
{
address template = isUsingFeeToken
address template = feeToken != address(0)
? address(l1Templates.feeTokenBasedCustomGatewayTemplate)
: address(l1Templates.customGatewayTemplate);

Expand All @@ -307,7 +308,7 @@ contract L1AtomicTokenBridgeCreator is Initializable, OwnableUpgradeable {
}

// l1 weth gateway deployment block
if (!isUsingFeeToken) {
if (feeToken == address(0)) {
L1WethGateway wethGateway = L1WethGateway(
payable(
_deployProxyWithSalt(
Expand Down Expand Up @@ -338,14 +339,37 @@ contract L1AtomicTokenBridgeCreator is Initializable, OwnableUpgradeable {

// deploy factory and then L2 contracts through L2 factory, using 2 retryables calls
// we do not care if it is a resend or not, if the L2 deployment already exists it will simply fail on L2
_deployL2Factory(inbox, gasPriceBid, isUsingFeeToken);
if (isUsingFeeToken) {
_deployL2Factory(inbox, gasPriceBid, feeToken);

RetryableParams memory retryableParams = RetryableParams(
inbox,
canonicalL2FactoryAddress,
msg.sender,
msg.sender,
maxGasForContracts,
gasPriceBid,
0
);

if (feeToken != address(0)) {
// transfer fee tokens to inbox to pay for 2nd retryable
address feeToken = _getFeeToken(inbox);
uint256 fee = maxGasForContracts * gasPriceBid;
IERC20(feeToken).safeTransferFrom(msg.sender, inbox, fee);
retryableParams.feeTokenTotalFeeAmount =
_getScaledAmount(feeToken, maxGasForContracts * gasPriceBid);
IERC20(feeToken).safeTransferFrom(
msg.sender, inbox, retryableParams.feeTokenTotalFeeAmount
);
}

L2TemplateAddresses memory l2TemplateAddress = L2TemplateAddresses(
l2RouterTemplate,
l2StandardGatewayTemplate,
l2CustomGatewayTemplate,
feeToken != address(0) ? address(0) : l2WethGatewayTemplate,
feeToken != address(0) ? address(0) : l2WethTemplate,
address(l1Templates.upgradeExecutor),
l2MulticallTemplate
);

// alias rollup owner if it is a contract
address l2RollupOwner = rollupOwner.code.length == 0
? rollupOwner
Expand All @@ -354,30 +378,13 @@ contract L1AtomicTokenBridgeCreator is Initializable, OwnableUpgradeable {
// sweep the balance to send the retryable and refund the difference
// it is known that any eth previously in this contract can be extracted
// tho it is not expected that this contract will have any eth
retryableSender.sendRetryable{value: isUsingFeeToken ? 0 : address(this).balance}(
RetryableParams(
inbox,
canonicalL2FactoryAddress,
msg.sender,
msg.sender,
maxGasForContracts,
gasPriceBid
),
L2TemplateAddresses(
l2RouterTemplate,
l2StandardGatewayTemplate,
l2CustomGatewayTemplate,
isUsingFeeToken ? address(0) : l2WethGatewayTemplate,
isUsingFeeToken ? address(0) : l2WethTemplate,
address(l1Templates.upgradeExecutor),
l2MulticallTemplate
),
_sendRetryableToCreateContracts(
retryableParams,
l2TemplateAddress,
l1Deployment,
l2Deployment.standardGateway,
l2Deployment,
l2RollupOwner,
msg.sender,
upgradeExecutor,
isUsingFeeToken
upgradeExecutor
);

// deployment mappings should not be updated in case of resend
Expand All @@ -390,6 +397,27 @@ contract L1AtomicTokenBridgeCreator is Initializable, OwnableUpgradeable {
}
}

function _sendRetryableToCreateContracts(
RetryableParams memory retryableParams,
L2TemplateAddresses memory l2TemplateAddress,
L1DeploymentAddresses memory l1Deployment,
L2DeploymentAddresses memory l2Deployment,
address l2RollupOwner,
address upgradeExecutor
) internal {
retryableSender.sendRetryable{
value: retryableParams.feeTokenTotalFeeAmount > 0 ? 0 : address(this).balance
}(
retryableParams,
l2TemplateAddress,
l1Deployment,
l2Deployment.standardGateway,
l2RollupOwner,
msg.sender,
upgradeExecutor
);
}

/**
* @notice Rollup owner can override deployment
*/
Expand All @@ -416,16 +444,16 @@ contract L1AtomicTokenBridgeCreator is Initializable, OwnableUpgradeable {
return inboxToL1Deployment[inbox].router;
}

function _deployL2Factory(address inbox, uint256 gasPriceBid, bool isUsingFeeToken) internal {
function _deployL2Factory(address inbox, uint256 gasPriceBid, address feeToken) internal {
// encode L2 factory bytecode
bytes memory deploymentData =
CreationCodeHelper.getCreationCodeFor(l2TokenBridgeFactoryTemplate.code);

if (isUsingFeeToken) {
if (feeToken != address(0)) {
// transfer fee tokens to inbox to pay for 1st retryable
address feeToken = _getFeeToken(inbox);
uint256 retryableFee = gasLimitForL2FactoryDeployment * gasPriceBid;
IERC20(feeToken).safeTransferFrom(msg.sender, inbox, retryableFee);
uint256 scaledRetryableFee = _getScaledAmount(feeToken, retryableFee);
IERC20(feeToken).safeTransferFrom(msg.sender, inbox, scaledRetryableFee);

IERC20Inbox(inbox).createRetryableTicket(
address(0),
Expand All @@ -435,7 +463,7 @@ contract L1AtomicTokenBridgeCreator is Initializable, OwnableUpgradeable {
msg.sender,
gasLimitForL2FactoryDeployment,
gasPriceBid,
retryableFee,
scaledRetryableFee,
deploymentData
);
} else {
Expand Down Expand Up @@ -569,6 +597,25 @@ contract L1AtomicTokenBridgeCreator is Initializable, OwnableUpgradeable {
{
return address(new TransparentUpgradeableProxy{salt: salt}(logic, admin, bytes("")));
}

/**
* @notice Scale amount to the fee token's decimals. Ie. amount of 1e18 will be scaled to 1e6 if fee token has 6 decimals like USDC.
*/
function _getScaledAmount(address feeToken, uint256 amount) internal view returns (uint256) {
uint8 decimals = ERC20(feeToken).decimals();
if (decimals == 18) {
return amount;
}
if (decimals < 18) {
uint256 scaledAmount = amount / (10 ** (18 - decimals));
// round up if necessary
if (scaledAmount * (10 ** (18 - decimals)) < amount) {
scaledAmount++;
}
return scaledAmount;
}
return amount * (10 ** (decimals - 18));
}
}

interface IERC20Bridge {
Expand Down
Loading