diff --git a/README.md b/README.md index 2acdf21..cd4b824 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ The MiniMeToken used in this repository is a fork of [the original](https://gith the differences between the fork and the upstream repository, head over to its [documentation](https://github.com/vacp2p/minime#readme). -1. MiniMeToken uses generateTokens and destroyTokens operated by controller for mint and burn. OptimismMintableMiniMeToken uses mint and burn operated by bridge. +1. MiniMeToken uses generateTokens and destroyTokens operated by controller for mint and burn. + OptimismMintableMiniMeToken uses mint and burn operated by bridge. 2. MiniMeToken `version()` had to be renamed to `token_version()` due a conflict on inheritance and requirements of Optimism. Semver inheritance uses version() and this seems a requirement for Optimism. diff --git a/index.js b/index.js index 3544fd0..756d23c 100644 --- a/index.js +++ b/index.js @@ -1,52 +1,47 @@ const contracts = require("./contracts.json"); -const ethers = require("ethers") -const optimismSDK = require("@eth-optimism/sdk") -require('dotenv').config() +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 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] +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) + 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; + 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. -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 +// 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 - + 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 +// 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 @@ -56,145 +51,122 @@ const erc20ABI = [ name: "balanceOf", outputs: [{ name: "balance", type: "uint256" }], type: "function", - } -]; // erc20ABI + }, +]; // erc20ABI const faucetABI = [ - // faucet - { - inputs: [ - { - internalType: "uint256", - name: "_amount", - type: "uint256" - } - ], - name: "mint", - outputs: [], - stateMutability: "nonpayable", - type: "function" - } + // 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 +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) -} - - + 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}`) + 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 + 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) + 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() + 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 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(); - 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 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 + await setup(); + await depositERC20(); + await withdrawERC20(); +}; // main - - -main().then(() => process.exit(0)) +main() + .then(() => process.exit(0)) .catch((error) => { - console.error(error) - process.exit(1) - }) + console.error(error); + process.exit(1); + }); diff --git a/scripts/deployOptimism.js b/scripts/deployOptimism.js index df4153d..ac5d7ed 100644 --- a/scripts/deployOptimism.js +++ b/scripts/deployOptimism.js @@ -1,20 +1,26 @@ const hre = require("hardhat"); -require('dotenv').config() +require("dotenv").config(); async function main() { const mainnet = network.config.chainId == 10; - const bridgeAddress = "0x4200000000000000000000000000000000000010";//standard bridge + const bridgeAddress = "0x4200000000000000000000000000000000000010"; //standard bridge const remoteToken = mainnet ? process.env.MAINNET_L1_ADDRESS : "0xd55245e63bDafAabac2530F70074A36D7899Ed72"; const [deployer] = await ethers.getSigners(); - console.log(`Deploying contracts to ${network.name} (${network.config.chainId}) with the account: ${deployer.address}`); - + console.log( + `Deploying contracts to ${network.name} (${network.config.chainId}) with the account: ${deployer.address}`, + ); + // - const miniMeTokenFactory = await (await ethers.getContractFactory("MiniMeTokenFactory")).attach("0x86e5c5c884740894644dad30021aaaade2b7babd") /*ethers.deployContract("MiniMeTokenFactory"); + const miniMeTokenFactory = await ( + await ethers.getContractFactory("MiniMeTokenFactory") + ).attach("0x86e5c5c884740894644dad30021aaaade2b7babd"); /*ethers.deployContract("MiniMeTokenFactory"); await miniMeTokenFactory.waitForDeployment();*/ - + //0x2b3845b982b147a0436e3766eae06936f4e271a0 - const miniMeToken = await (await ethers.getContractFactory("OptimismMintableMiniMeToken")).attach("0x2b3845b982b147a0436e3766eae06936f4e271a0");/*ethers.deployContract( + const miniMeToken = await ( + await ethers.getContractFactory("OptimismMintableMiniMeToken") + ).attach("0x2b3845b982b147a0436e3766eae06936f4e271a0"); /*ethers.deployContract( "OptimismMintableMiniMeToken", [ bridgeAddress, remoteToken, @@ -28,8 +34,10 @@ async function main() { ]); await miniMeToken.waitForDeployment();*/ - // - const tokenController = await (await ethers.getContractFactory("SNTPlaceHolder")).attach("0x4Ef81bDfcbb003442869B53Bf81168c12e1746A8")/*ethers.deployContract( + // + const tokenController = await ( + await ethers.getContractFactory("SNTPlaceHolder") + ).attach("0x4Ef81bDfcbb003442869B53Bf81168c12e1746A8"); /*ethers.deployContract( "SNTPlaceHolder", //we should never mint STT on optimism, that should be done by bridge only [ deployer.address, @@ -38,22 +46,16 @@ async function main() { ); await tokenController.waitForDeployment();*/ await miniMeToken.changeController(tokenController.target); + console.log(`npx hardhat verify ${miniMeTokenFactory.target} `); console.log( - `npx hardhat verify ${miniMeTokenFactory.target} ` - ) - console.log( - `npx hardhat verify ${miniMeToken.target} ${bridgeAddress} ${remoteToken} ${miniMeTokenFactory.target} ${ethers.ZeroAddress} ${0} ${mainnet ? "Status Network Token" : "Status Test Token"} ${18} ${mainnet ? "SNT" : "STT"} ${true}` - ) - console.log( - `npx hardhat verify ${tokenController.target} ${deployer.address} ${miniMeToken.target} ` - ) - - console.log( - `${mainnet ? "SNT" : "STT"} ${miniMeToken.target} controlled by ${await miniMeToken.controller()}` - ); - console.log( - `SNTPlaceHolder ${tokenController.target} owned by ${await tokenController.owner()}` + `npx hardhat verify ${miniMeToken.target} ${bridgeAddress} ${remoteToken} ${miniMeTokenFactory.target} ${ + ethers.ZeroAddress + } ${0} ${mainnet ? "Status Network Token" : "Status Test Token"} ${18} ${mainnet ? "SNT" : "STT"} ${true}`, ); + console.log(`npx hardhat verify ${tokenController.target} ${deployer.address} ${miniMeToken.target} `); + + console.log(`${mainnet ? "SNT" : "STT"} ${miniMeToken.target} controlled by ${await miniMeToken.controller()}`); + console.log(`SNTPlaceHolder ${tokenController.target} owned by ${await tokenController.owner()}`); } // We recommend this pattern to be able to use async/await everywhere diff --git a/scripts/deploySNT.js b/scripts/deploySNT.js index 3676ebc..7522929 100644 --- a/scripts/deploySNT.js +++ b/scripts/deploySNT.js @@ -7,38 +7,37 @@ const hre = require("hardhat"); async function main() { - const mainnet = network.config.chainId == 1 + const mainnet = network.config.chainId == 1; const [deployer] = await ethers.getSigners(); - console.log(`Deploying contracts to ${network.name} (${network.config.chainId}) with the account: ${deployer.address}`); + console.log( + `Deploying contracts to ${network.name} (${network.config.chainId}) with the account: ${deployer.address}`, + ); const miniMeTokenFactory = await ethers.deployContract("MiniMeTokenFactory"); await miniMeTokenFactory.waitForDeployment(); const miniMeToken = await ethers.deployContract( - optimism ? "MintableMiniMeTokenOptimism" : "MiniMeToken", (optimism ? [] : []).concat([ + optimism ? "MintableMiniMeTokenOptimism" : "MiniMeToken", + (optimism ? [] : []).concat([ miniMeTokenFactory.target, ethers.ZeroAddress, 0, - mainnet ? "Status Network Token" : "Status Test Token", + mainnet ? "Status Network Token" : "Status Test Token", 18, mainnet ? "SNT" : "STT", - true - ])); - await miniMeToken.waitForDeployment(); - const tokenController = await ethers.deployContract( - mainnet ? "SNTPlaceHolder" : "SNTFaucet", - [ - deployer.address, - miniMeToken.target - ] + true, + ]), ); + await miniMeToken.waitForDeployment(); + const tokenController = await ethers.deployContract(mainnet ? "SNTPlaceHolder" : "SNTFaucet", [ + deployer.address, + miniMeToken.target, + ]); await tokenController.waitForDeployment(); await miniMeToken.changeController(tokenController.target); + console.log(`${mainnet ? "SNT" : "STT"} ${miniMeToken.target} controlled by ${await miniMeToken.controller()}`); console.log( - `${mainnet ? "SNT" : "STT"} ${miniMeToken.target} controlled by ${await miniMeToken.controller()}` - ); - console.log( - `${mainnet ? "SNTPlaceHolder" : "SNTFaucet"} ${tokenController.target} owned by ${await tokenController.owner()}` + `${mainnet ? "SNTPlaceHolder" : "SNTFaucet"} ${tokenController.target} owned by ${await tokenController.owner()}`, ); }