Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for non-18 decimals in custom fee token #24

Merged
merged 18 commits into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from 17 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
84 changes: 53 additions & 31 deletions scripts/ethcommands.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { runStress } from "./stress";
import { ContractFactory, ethers, Wallet } from "ethers";
import { BigNumber, ContractFactory, ethers, Wallet } from "ethers";
import * as consts from "./consts";
import { namedAccount, namedAddress } from "./accounts";
import * as L1GatewayRouter from "@arbitrum/token-bridge-contracts/build/contracts/contracts/tokenbridge/ethereum/gateway/L1GatewayRouter.sol/L1GatewayRouter.json";
import * as ERC20PresetFixedSupplyArtifact from "@openzeppelin/contracts/build/contracts/ERC20PresetFixedSupply.json";
import * as ERC20 from "@openzeppelin/contracts/build/contracts/ERC20.json";
import * as fs from "fs";
const path = require("path");
Expand Down Expand Up @@ -59,14 +58,20 @@ async function bridgeNativeToken(argv: any, parentChainUrl: string, chainUrl: st

argv.to = "address_" + inboxAddr;

/// approve inbox to use fee token
// get token contract
const bridgerParentChain = namedAccount(argv.from, argv.threadId).connect(argv.provider)
const nativeTokenContract = new ethers.Contract(token, ERC20.abi, bridgerParentChain)
await nativeTokenContract.approve(inboxAddr, ethers.utils.parseEther(argv.amount))

// scale deposit amount
const decimals = await nativeTokenContract.decimals()
const depositAmount = BigNumber.from(argv.amount).mul(BigNumber.from('10').pow(decimals))

/// approve inbox to use fee token
await nativeTokenContract.approve(inboxAddr, depositAmount)

/// deposit fee token
const iface = new ethers.utils.Interface(["function depositERC20(uint256 amount)"])
argv.data = iface.encodeFunctionData("depositERC20", [ethers.utils.parseEther(argv.amount)]);
argv.data = iface.encodeFunctionData("depositERC20", [depositAmount]);

await runStress(argv, sendTransaction);

Expand All @@ -77,14 +82,42 @@ async function bridgeNativeToken(argv: any, parentChainUrl: string, chainUrl: st
const sleep = (ms: number) => new Promise(r => setTimeout(r, ms));
while (true) {
const balance = await bridger.getBalance()
if (balance.gte(ethers.utils.parseEther(argv.amount))) {
if (balance.gte(0)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.gte(depositAmount)?

gte(0) will not work for 2nd transfer, gte(depositAmount) will at least work if 2nd transfer is larger than previous balance which seems reasonable.

there are probably better ways

Copy link
Contributor Author

@gvladika gvladika Jul 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case when fee token has non-18 decimals, depositAmount is not equal to the amount minted on L2 (we do inflation/deflation to match 18-decimals denomination for native currency). I've improved this check to properly wait for expected amount to be minted on L2 3af86a0

return
}
await sleep(100)
}
}
}

async function deployERC20Contract(deployerWallet: Wallet, decimals: number): Promise<string> {
//// Bytecode below is generated from this simple ERC20 token contract which uses custom number of decimals

// pragma solidity 0.8.16;
//
// import {ERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
//
// contract TestToken is ERC20 {
// uint8 private immutable _decimals;
//
// constructor(uint8 decimals_, address mintTo) ERC20("testnode", "TN") {
// _decimals = decimals_;
// _mint(mintTo, 1_000_000_000 * 10 ** decimals_);
// }
//
// function decimals() public view virtual override returns (uint8) {
// return _decimals;
// }
// }

const erc20TokenBytecode = "0x60a06040523480156200001157600080fd5b5060405162000d4938038062000d49833981016040819052620000349162000195565b60405180604001604052806008815260200167746573746e6f646560c01b815250604051806040016040528060028152602001612a2760f11b815250816003908162000081919062000288565b50600462000090828262000288565b50505060ff8216608052620000c281620000ac84600a62000469565b620000bc90633b9aca0062000481565b620000ca565b5050620004b9565b6001600160a01b038216620001255760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015260640160405180910390fd5b8060026000828254620001399190620004a3565b90915550506001600160a01b038216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b505050565b60008060408385031215620001a957600080fd5b825160ff81168114620001bb57600080fd5b60208401519092506001600160a01b0381168114620001d957600080fd5b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806200020f57607f821691505b6020821081036200023057634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200019057600081815260208120601f850160051c810160208610156200025f5750805b601f850160051c820191505b8181101562000280578281556001016200026b565b505050505050565b81516001600160401b03811115620002a457620002a4620001e4565b620002bc81620002b58454620001fa565b8462000236565b602080601f831160018114620002f45760008415620002db5750858301515b600019600386901b1c1916600185901b17855562000280565b600085815260208120601f198616915b82811015620003255788860151825594840194600190910190840162000304565b5085821015620003445787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b634e487b7160e01b600052601160045260246000fd5b600181815b80851115620003ab5781600019048211156200038f576200038f62000354565b808516156200039d57918102915b93841c93908002906200036f565b509250929050565b600082620003c45750600162000463565b81620003d35750600062000463565b8160018114620003ec5760028114620003f75762000417565b600191505062000463565b60ff8411156200040b576200040b62000354565b50506001821b62000463565b5060208310610133831016604e8410600b84101617156200043c575081810a62000463565b6200044883836200036a565b80600019048211156200045f576200045f62000354565b0290505b92915050565b60006200047a60ff841683620003b3565b9392505050565b60008160001904831182151516156200049e576200049e62000354565b500290565b8082018082111562000463576200046362000354565b608051610874620004d5600039600061011b01526108746000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c80633950935111610071578063395093511461014557806370a082311461015857806395d89b4114610181578063a457c2d714610189578063a9059cbb1461019c578063dd62ed3e146101af57600080fd5b806306fdde03146100ae578063095ea7b3146100cc57806318160ddd146100ef57806323b872dd14610101578063313ce56714610114575b600080fd5b6100b66101c2565b6040516100c391906106be565b60405180910390f35b6100df6100da366004610728565b610254565b60405190151581526020016100c3565b6002545b6040519081526020016100c3565b6100df61010f366004610752565b61026e565b60405160ff7f00000000000000000000000000000000000000000000000000000000000000001681526020016100c3565b6100df610153366004610728565b610292565b6100f361016636600461078e565b6001600160a01b031660009081526020819052604090205490565b6100b66102b4565b6100df610197366004610728565b6102c3565b6100df6101aa366004610728565b610343565b6100f36101bd3660046107b0565b610351565b6060600380546101d1906107e3565b80601f01602080910402602001604051908101604052809291908181526020018280546101fd906107e3565b801561024a5780601f1061021f5761010080835404028352916020019161024a565b820191906000526020600020905b81548152906001019060200180831161022d57829003601f168201915b5050505050905090565b60003361026281858561037c565b60019150505b92915050565b60003361027c8582856104a0565b61028785858561051a565b506001949350505050565b6000336102628185856102a58383610351565b6102af919061081d565b61037c565b6060600480546101d1906107e3565b600033816102d18286610351565b9050838110156103365760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b60648201526084015b60405180910390fd5b610287828686840361037c565b60003361026281858561051a565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b6001600160a01b0383166103de5760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b606482015260840161032d565b6001600160a01b03821661043f5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b606482015260840161032d565b6001600160a01b0383811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b60006104ac8484610351565b9050600019811461051457818110156105075760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000604482015260640161032d565b610514848484840361037c565b50505050565b6001600160a01b03831661057e5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b606482015260840161032d565b6001600160a01b0382166105e05760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b606482015260840161032d565b6001600160a01b038316600090815260208190526040902054818110156106585760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b606482015260840161032d565b6001600160a01b03848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3610514565b600060208083528351808285015260005b818110156106eb578581018301518582016040015282016106cf565b506000604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b038116811461072357600080fd5b919050565b6000806040838503121561073b57600080fd5b6107448361070c565b946020939093013593505050565b60008060006060848603121561076757600080fd5b6107708461070c565b925061077e6020850161070c565b9150604084013590509250925092565b6000602082840312156107a057600080fd5b6107a98261070c565b9392505050565b600080604083850312156107c357600080fd5b6107cc8361070c565b91506107da6020840161070c565b90509250929050565b600181811c908216806107f757607f821691505b60208210810361081757634e487b7160e01b600052602260045260246000fd5b50919050565b8082018082111561026857634e487b7160e01b600052601160045260246000fdfea2646970667358221220257f3d763bae7b8c0189ed676531d85a1046e0bea68722f67c2616d46f01c02964736f6c63430008100033";
const abi = ["constructor(uint8 decimals_, address mintTo)"];
const tokenFactory = new ContractFactory(abi, erc20TokenBytecode, deployerWallet);
const token = await tokenFactory.deploy(decimals, deployerWallet.address);
await token.deployTransaction.wait();

return token.address;
}

export const bridgeFundsCommand = {
command: "bridge-funds",
Expand Down Expand Up @@ -192,14 +225,15 @@ export const createERC20Command = {
string: true,
describe: "account (see general help)"
},
mintTo: {
string: true,
describe: "account (see general help)",
},
bridgeable: {
boolean: true,
describe: "if true, deploy on L1 and bridge to L2",
},
decimals: {
string: true,
describe: "number of decimals for token",
default: "18",
},
},
handler: async (argv: any) => {
console.log("create-erc20");
Expand All @@ -220,20 +254,15 @@ export const createERC20Command = {
l1provider
);

const tokenFactory = new ContractFactory(
ERC20PresetFixedSupplyArtifact.abi,
ERC20PresetFixedSupplyArtifact.bytecode,
deployerWallet
);
const token = await tokenFactory.deploy("AppTestToken", "APP", ethers.utils.parseEther("1000000000"), deployerWallet.address);
await token.deployTransaction.wait();
const tokenAddress = await deployERC20Contract(deployerWallet, argv.decimals);
const token = new ethers.Contract(tokenAddress, ERC20.abi, deployerWallet);
console.log("Contract deployed at L1 address:", token.address);
await (await token.functions.transfer(namedAccount(argv.mintTo).address, ethers.utils.parseEther("100000000"))).wait();

const l1GatewayRouter = new ethers.Contract(l1l2tokenbridge.l2Network.tokenBridge.l1GatewayRouter, L1GatewayRouter.abi, deployerWallet);
await (await token.functions.approve(l1l2tokenbridge.l2Network.tokenBridge.l1ERC20Gateway, ethers.constants.MaxUint256)).wait();
const supply = await token.totalSupply();
await (await l1GatewayRouter.functions.outboundTransfer(
token.address, namedAccount(argv.mintTo).address, ethers.utils.parseEther("100000000"), 100000000, 1000000000, "0x000000000000000000000000000000000000000000000000000fffffffffff0000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000", {
token.address, deployerWallet.address, supply, 100000000, 1000000000, "0x000000000000000000000000000000000000000000000000000fffffffffff0000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000000", {
value: ethers.utils.parseEther("1"),
}
)).wait();
Expand Down Expand Up @@ -264,16 +293,8 @@ export const createERC20Command = {
ethers.utils.sha256(ethers.utils.toUtf8Bytes(argv.deployer)),
argv.provider
);

const contractFactory = new ContractFactory(
ERC20PresetFixedSupplyArtifact.abi,
ERC20PresetFixedSupplyArtifact.bytecode,
deployerWallet
);
const contract = await contractFactory.deploy("AppTestToken", "APP", ethers.utils.parseEther("1000000000"), namedAccount(argv.mintTo).address);
await contract.deployTransaction.wait();

console.log("Contract deployed at address:", contract.address);
const tokenAddress = await deployERC20Contract(deployerWallet, argv.decimals);
console.log("Contract deployed at address:", tokenAddress);

argv.provider.destroy();
},
Expand Down Expand Up @@ -306,8 +327,9 @@ export const transferERC20Command = {
argv.provider = new ethers.providers.WebSocketProvider(argv.l2url);
const account = namedAccount(argv.from).connect(argv.provider);
const tokenContract = new ethers.Contract(argv.token, ERC20.abi, account);
const decimals = await tokenContract.decimals();
await(await tokenContract.transfer(namedAccount(argv.to).address, ethers.utils.parseUnits(argv.amount, decimals))).wait();
const tokenDecimals = await tokenContract.decimals();
const amountToTransfer = BigNumber.from(argv.amount).mul(BigNumber.from('10').pow(tokenDecimals));
await(await tokenContract.transfer(namedAccount(argv.to).address, amountToTransfer)).wait();
argv.provider.destroy();
},
};
Expand Down
26 changes: 21 additions & 5 deletions test-node.bash
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ dev_build_nitro=false
dev_build_blockscout=false
l3_custom_fee_token=false
l3_token_bridge=false
l3_custom_fee_token_decimals=18
batchposters=1
devprivkey=b6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659
l1chainid=1337
Expand Down Expand Up @@ -146,6 +147,19 @@ while [[ $# -gt 0 ]]; do
l3_custom_fee_token=true
shift
;;
--l3-fee-token-decimals)
if ! $l3_custom_fee_token; then
echo "Error: --l3-fee-token-decimals requires --l3-fee-token to be provided."
exit 1
fi
l3_custom_fee_token_decimals=$2
if [[ $l3_custom_fee_token_decimals -lt 0 || $l3_custom_fee_token_decimals -gt 36 ]]; then
echo "l3-fee-token-decimals must be in range [0,36], value: $l3_custom_fee_token_decimals."
exit 1
fi
shift
shift
;;
--l3-token-bridge)
if ! $l3node; then
echo "Error: --l3-token-bridge requires --l3node to be provided."
Expand Down Expand Up @@ -184,6 +198,7 @@ while [[ $# -gt 0 ]]; do
echo --validate heavy computation, validating all blocks in WASM
echo --l3node deploys an L3 node on top of the L2
echo --l3-fee-token L3 chain is set up to use custom fee token. Only valid if also '--l3node' is provided
echo --l3-fee-token-decimals Number of decimals to use for custom fee token. Only valid if also '--l3-fee-token' is provided
echo --l3-token-bridge Deploy L2-L3 token bridge. Only valid if also '--l3node' is provided
echo --batchposters batch posters [0-3]
echo --redundantsequencers redundant sequencers [0-3]
Expand Down Expand Up @@ -421,8 +436,9 @@ if $force_init; then

if $l3_custom_fee_token; then
echo == Deploying custom fee token
nativeTokenAddress=`docker compose run scripts create-erc20 --deployer user_fee_token_deployer --mintTo user_token_bridge_deployer --bridgeable $tokenbridge | tail -n 1 | awk '{ print $NF }'`
docker compose run scripts transfer-erc20 --token $nativeTokenAddress --amount 100 --from user_token_bridge_deployer --to l3owner
nativeTokenAddress=`docker compose run scripts create-erc20 --deployer user_fee_token_deployer --bridgeable $tokenbridge --decimals $l3_custom_fee_token_decimals | tail -n 1 | awk '{ print $NF }'`
docker compose run scripts transfer-erc20 --token $nativeTokenAddress --amount 10000 --from user_fee_token_deployer --to l3owner
docker compose run scripts transfer-erc20 --token $nativeTokenAddress --amount 10000 --from user_fee_token_deployer --to user_token_bridge_deployer
EXTRA_L3_DEPLOY_FLAG="-e FEE_TOKEN_ADDRESS=$nativeTokenAddress"
fi

Expand Down Expand Up @@ -453,9 +469,9 @@ if $force_init; then

echo == Fund L3 accounts
if $l3_custom_fee_token; then
docker compose run scripts bridge-native-token-to-l3 --amount 50000 --from user_token_bridge_deployer --wait
docker compose run scripts send-l3 --ethamount 500 --from user_token_bridge_deployer --wait
docker compose run scripts send-l3 --ethamount 500 --from user_token_bridge_deployer --to "key_0x$devprivkey" --wait
docker compose run scripts bridge-native-token-to-l3 --amount 5000 --from user_fee_token_deployer --wait
docker compose run scripts send-l3 --ethamount 5 --from user_fee_token_deployer --wait
docker compose run scripts send-l3 --ethamount 5 --from user_fee_token_deployer --to "key_0x$devprivkey" --wait
else
docker compose run scripts bridge-to-l3 --ethamount 50000 --wait
fi
Expand Down
Loading