ran prettier
This commit is contained in:
parent
c6e53ed3ab
commit
980f08e22a
|
@ -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.
|
||||
|
|
230
index.js
230
index.js
|
@ -1,48 +1,43 @@
|
|||
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
|
||||
}
|
||||
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
|
||||
// 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
|
||||
|
@ -56,7 +51,7 @@ const erc20ABI = [
|
|||
name: "balanceOf",
|
||||
outputs: [{ name: "balance", type: "uint256" }],
|
||||
type: "function",
|
||||
}
|
||||
},
|
||||
]; // erc20ABI
|
||||
const faucetABI = [
|
||||
// faucet
|
||||
|
@ -65,136 +60,113 @@ const faucetABI = [
|
|||
{
|
||||
internalType: "uint256",
|
||||
name: "_amount",
|
||||
type: "uint256"
|
||||
}
|
||||
type: "uint256",
|
||||
},
|
||||
],
|
||||
name: "mint",
|
||||
outputs: [],
|
||||
stateMutability: "nonpayable",
|
||||
type: "function"
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
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);
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
|
@ -29,7 +35,9 @@ 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} `
|
||||
)
|
||||
`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()}`
|
||||
);
|
||||
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
|
||||
|
|
|
@ -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",
|
||||
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()}`,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue