optimism-bridge-snt/index.js

173 lines
6.4 KiB
JavaScript

const contracts = require("./contracts.json");
const ethers = require("ethers");
const optimismSDK = require("@eth-optimism/sdk");
require("dotenv").config();
const l1Url = `https://eth-goerli.g.alchemy.com/v2/${process.env.GOERLI_ALCHEMY_KEY}`;
const l2Url = `https://opt-goerli.g.alchemy.com/v2/${process.env.GOERLI_OPT_ALCHEMY_KEY}`;
const mnemonic = process.env.GOERLI_MNEMONIC;
const words = mnemonic.match(/[a-zA-Z]+/g).length;
validLength = [12, 15, 18, 24];
if (!validLength.includes(words)) {
console.log(`The mnemonic (${mnemonic}) is the wrong number of words`);
process.exit(-1);
}
const erc20Addrs = {
l1Addr: contracts.goerli.l1.token,
l2Addr: contracts.goerli.l2.token,
};
const faucetAddr = contracts.goerli.l2.controller;
let crossChainMessenger;
let l1ERC20, l2ERC20; // OUTb contracts to show ERC-20
let faucet;
let ourAddr; // The address of the signer we use.
// Get signers on L1 and L2 (for the same address). Note that
// this address needs to have ETH on it, both on Optimism and
// Optimism Georli
const getSigners = async () => {
const l1RpcProvider = new ethers.providers.JsonRpcProvider(l1Url);
const l2RpcProvider = new ethers.providers.JsonRpcProvider(l2Url);
const hdNode = ethers.utils.HDNode.fromMnemonic(mnemonic);
const privateKey = hdNode.derivePath(ethers.utils.defaultPath).privateKey;
const l1Wallet = new ethers.Wallet(privateKey, l1RpcProvider);
const l2Wallet = new ethers.Wallet(privateKey, l2RpcProvider);
return [l1Wallet, l2Wallet];
}; // getSigners
// The ABI fragment for the contract. We only need to know how to do two things:
// 1. Get an account's balance
// 2. Call the faucet to get more (only works on L1). Of course, production
// ERC-20 tokens tend to be a bit harder to acquire.
const erc20ABI = [
// balanceOf
{
constant: true,
inputs: [{ name: "_owner", type: "address" }],
name: "balanceOf",
outputs: [{ name: "balance", type: "uint256" }],
type: "function",
},
]; // erc20ABI
const faucetABI = [
// faucet
{
inputs: [
{
internalType: "uint256",
name: "_amount",
type: "uint256",
},
],
name: "mint",
outputs: [],
stateMutability: "nonpayable",
type: "function",
},
];
const setup = async () => {
const [l1Signer, l2Signer] = await getSigners();
ourAddr = l1Signer.address;
crossChainMessenger = new optimismSDK.CrossChainMessenger({
l1ChainId: 5, // Goerli value, 1 for mainnet
l2ChainId: 420, // Goerli value, 10 for mainnet
l1SignerOrProvider: l1Signer,
l2SignerOrProvider: l2Signer,
});
l1ERC20 = new ethers.Contract(erc20Addrs.l1Addr, erc20ABI, l1Signer);
l2ERC20 = new ethers.Contract(erc20Addrs.l2Addr, erc20ABI, l2Signer);
faucet = new ethers.Contract(faucetAddr, faucetABI, l1Signer);
};
const reportERC20Balances = async () => {
const l1Balance = (await l1ERC20.balanceOf(ourAddr)).toString().slice(0, -18);
const l2Balance = (await l2ERC20.balanceOf(ourAddr)).toString().slice(0, -18);
console.log(`OUTb on L1:${l1Balance} OUTb on L2:${l2Balance}`);
if (l1Balance != 0) {
return;
}
console.log(`You don't have enough OUTb on L1. Let's call the faucet to fix that`);
const tx = await faucet.mint("10000000000000000000000");
console.log(`Faucet tx: ${tx.hash}`);
console.log(`\tMore info: https://goerli.etherscan.io/tx/${tx.hash}`);
await tx.wait();
const newBalance = (await l1ERC20.balanceOf(ourAddr)).toString().slice(0, -18);
console.log(`New L1 OUTb balance: ${newBalance}`);
};
const oneToken = BigInt(1e18);
const depositERC20 = async () => {
console.log("Deposit ERC20");
await reportERC20Balances();
const start = new Date();
// Need the l2 address to know which bridge is responsible
const allowanceResponse = await crossChainMessenger.approveERC20(erc20Addrs.l1Addr, erc20Addrs.l2Addr, oneToken);
await allowanceResponse.wait();
console.log(`Allowance given by tx ${allowanceResponse.hash}`);
console.log(`\tMore info: https://goerli.etherscan.io/tx/${allowanceResponse.hash}`);
console.log(`Time so far ${(new Date() - start) / 1000} seconds`);
const response = await crossChainMessenger.depositERC20(erc20Addrs.l1Addr, erc20Addrs.l2Addr, oneToken);
console.log(`Deposit transaction hash (on L1): ${response.hash}`);
console.log(`\tMore info: https://goerli.etherscan.io/tx/${response.hash}`);
await response.wait();
console.log("Waiting for status to change to RELAYED");
console.log(`Time so far ${(new Date() - start) / 1000} seconds`);
await crossChainMessenger.waitForMessageStatus(response.hash, optimismSDK.MessageStatus.RELAYED);
await reportERC20Balances();
console.log(`depositERC20 took ${(new Date() - start) / 1000} seconds\n\n`);
}; // depositERC20()
const withdrawERC20 = async () => {
console.log("Withdraw ERC20");
const start = new Date();
await reportERC20Balances();
const response = await crossChainMessenger.withdrawERC20(erc20Addrs.l1Addr, erc20Addrs.l2Addr, oneToken);
console.log(`Transaction hash (on L2): ${response.hash}`);
console.log(`\tFor more information: https://goerli-optimism.etherscan.io/tx/${response.hash}`);
await response.wait();
console.log("Waiting for status to be READY_TO_PROVE");
console.log(`Time so far ${(new Date() - start) / 1000} seconds`);
await crossChainMessenger.waitForMessageStatus(response.hash, optimismSDK.MessageStatus.READY_TO_PROVE);
console.log(`Time so far ${(new Date() - start) / 1000} seconds`);
await crossChainMessenger.proveMessage(response.hash);
console.log("In the challenge period, waiting for status READY_FOR_RELAY");
console.log(`Time so far ${(new Date() - start) / 1000} seconds`);
await crossChainMessenger.waitForMessageStatus(response.hash, optimismSDK.MessageStatus.READY_FOR_RELAY);
console.log("Ready for relay, finalizing message now");
console.log(`Time so far ${(new Date() - start) / 1000} seconds`);
await crossChainMessenger.finalizeMessage(response.hash);
console.log("Waiting for status to change to RELAYED");
console.log(`Time so far ${(new Date() - start) / 1000} seconds`);
await crossChainMessenger.waitForMessageStatus(response, optimismSDK.MessageStatus.RELAYED);
await reportERC20Balances();
console.log(`withdrawERC20 took ${(new Date() - start) / 1000} seconds\n\n\n`);
}; // withdrawERC20()
const main = async () => {
await setup();
await depositERC20();
await withdrawERC20();
}; // main
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});