Skip to content

Commit

Permalink
Merge pull request #24 from OffchainLabs/non18-decimal-token
Browse files Browse the repository at this point in the history
Support for non-18 decimals in custom fee token
  • Loading branch information
gvladika authored Jul 9, 2024
2 parents b162ff9 + 3af86a0 commit e4ff0cf
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 39 deletions.
108 changes: 74 additions & 34 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,32 +58,84 @@ async function bridgeNativeToken(argv: any, parentChainUrl: string, chainUrl: st

argv.to = "address_" + inboxAddr;

/// approve inbox to use fee token
// snapshot balance before deposit
const childProvider = new ethers.providers.WebSocketProvider(chainUrl);
const bridger = namedAccount(argv.from, argv.threadId).connect(childProvider)
const bridgerBalanceBefore = await bridger.getBalance()

// 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);

argv.provider.destroy();
if (argv.wait) {
const childProvider = new ethers.providers.WebSocketProvider(chainUrl);
const bridger = namedAccount(argv.from, argv.threadId).connect(childProvider)
const sleep = (ms: number) => new Promise(r => setTimeout(r, ms));

// calculate amount being minted on child chain
let expectedMintedAmount = depositAmount
if(decimals < 18) {
// inflate up to 18 decimals
expectedMintedAmount = depositAmount.mul(BigNumber.from('10').pow(18 - decimals))
} else if(decimals > 18) {
// deflate down to 18 decimals, rounding up
const quotient = BigNumber.from('10').pow(decimals - 18)
expectedMintedAmount = depositAmount.div(quotient)
if(expectedMintedAmount.mul(quotient).lt(depositAmount)) {
expectedMintedAmount = expectedMintedAmount.add(1)
}
}

while (true) {
const balance = await bridger.getBalance()
if (balance.gte(ethers.utils.parseEther(argv.amount))) {
const bridgerBalanceAfter = await bridger.getBalance()
if (bridgerBalanceAfter.sub(bridgerBalanceBefore).eq(expectedMintedAmount)) {
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 +243,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 +272,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 +311,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 +345,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

0 comments on commit e4ff0cf

Please sign in to comment.