Compare commits
No commits in common. "main" and "arbitrum" have entirely different histories.
|
@ -1,14 +1,10 @@
|
||||||
node_modules
|
node_modules
|
||||||
.env
|
.env
|
||||||
.env.*
|
|
||||||
coverage
|
coverage
|
||||||
coverage.json
|
coverage.json
|
||||||
typechain
|
typechain
|
||||||
typechain-types
|
typechain-types
|
||||||
|
|
||||||
.l1-token-address.*
|
|
||||||
.l2-token-address.*
|
|
||||||
|
|
||||||
# Hardhat files
|
# Hardhat files
|
||||||
cache
|
cache
|
||||||
artifacts
|
artifacts
|
||||||
|
|
28
README.md
28
README.md
|
@ -1,25 +1,11 @@
|
||||||
# Optimism-Compatible Token
|
# 10grans-NG
|
||||||
|
|
||||||
EVM-compatible ERC-20 token with extra features, capable of being bridged to an
|
10grans on Arbitrum chain, with a bridge to Ethereum.
|
||||||
Optimism-compatible L2. Built for Base blockchain and the 10Grans token,
|
|
||||||
but general enough to be reused.
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
npx hardhat compile
|
npx hardhat help
|
||||||
|
npx hardhat test
|
||||||
cp env.example .env.dev
|
REPORT_GAS=true npx hardhat test
|
||||||
# (customize your env file here first)
|
npx hardhat node
|
||||||
|
npx hardhat run scripts/deploy.ts
|
||||||
scripts/deploy-dev.sh
|
|
||||||
|
|
||||||
# optionally verify your contract on L2, customize the following:
|
|
||||||
NODE_ENV=dev npx hardhat verify --network base-goerli 0xl2tokenaddress MrTestToken TEST 0xbridgeonl2 0xl1tokenaddress
|
|
||||||
```
|
```
|
||||||
|
|
||||||
# Depositing to L2
|
|
||||||
|
|
||||||
Use this tool: https://git.shipoclu.com/moon/l2-base-bridging
|
|
||||||
|
|
||||||
# License
|
|
||||||
|
|
||||||
MIT license.
|
|
|
@ -1,11 +0,0 @@
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
pragma solidity ^0.8.19;
|
|
||||||
|
|
||||||
import "./AbstractToken.sol";
|
|
||||||
|
|
||||||
contract L1Token is AbstractToken {
|
|
||||||
|
|
||||||
constructor(string memory _name, string memory _symbol, uint256 _fixedMint) AbstractToken(_name, _symbol) {
|
|
||||||
_mint(msg.sender, _fixedMint * 10 ** 18);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,116 +0,0 @@
|
||||||
// SPDX-License-Identifier: MIT
|
|
||||||
pragma solidity ^0.8.19;
|
|
||||||
|
|
||||||
import "./AbstractToken.sol";
|
|
||||||
import "@eth-optimism/contracts-bedrock/contracts/universal/IOptimismMintableERC20.sol";
|
|
||||||
|
|
||||||
contract L2Token is AbstractToken, IOptimismMintableERC20 {
|
|
||||||
/// @notice Address of the corresponding version of this token on the remote chain.
|
|
||||||
address public immutable REMOTE_TOKEN;
|
|
||||||
|
|
||||||
/// @notice Address of the StandardBridge on this network.
|
|
||||||
address public immutable BRIDGE;
|
|
||||||
|
|
||||||
/// @notice Emitted whenever tokens are minted for an account.
|
|
||||||
/// @param account Address of the account tokens are being minted for.
|
|
||||||
/// @param amount Amount of tokens minted.
|
|
||||||
event Mint(address indexed account, uint256 amount);
|
|
||||||
|
|
||||||
/// @notice Emitted whenever tokens are burned from an account.
|
|
||||||
/// @param account Address of the account tokens are being burned from.
|
|
||||||
/// @param amount Amount of tokens burned.
|
|
||||||
event Burn(address indexed account, uint256 amount);
|
|
||||||
|
|
||||||
/// @notice A modifier that only allows the bridge to call
|
|
||||||
modifier onlyBridge() {
|
|
||||||
require(_msgSender() == BRIDGE, "OptimismMintableERC20: only bridge can mint and burn");
|
|
||||||
_;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(string memory _name, string memory _symbol, address _bridge, address _remoteToken) AbstractToken(_name, _symbol) {
|
|
||||||
BRIDGE = _bridge;
|
|
||||||
REMOTE_TOKEN = _remoteToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
function balanceOf(address account) public view virtual override(AbstractToken) returns (uint256) {
|
|
||||||
return AbstractToken.balanceOf(account);
|
|
||||||
}
|
|
||||||
|
|
||||||
function transferFrom(address sender, address recipient, uint256 amount) public virtual override(AbstractToken) returns (bool) {
|
|
||||||
return AbstractToken.transferFrom(sender, recipient, amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @notice Allows the StandardBridge on this network to mint tokens.
|
|
||||||
/// @param _to Address to mint tokens to.
|
|
||||||
/// @param _amount Amount of tokens to mint.
|
|
||||||
// ONLY bridge is allowed to mint, for security reasons.
|
|
||||||
function mint(
|
|
||||||
address _to,
|
|
||||||
uint256 _amount
|
|
||||||
)
|
|
||||||
external
|
|
||||||
virtual
|
|
||||||
override(IOptimismMintableERC20)
|
|
||||||
onlyBridge
|
|
||||||
{
|
|
||||||
_mint(_to, _amount);
|
|
||||||
emit Mint(_to, _amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @notice Allows the StandardBridge on this network to burn tokens.
|
|
||||||
/// @param _from Address to burn tokens from.
|
|
||||||
/// @param _amount Amount of tokens to burn.
|
|
||||||
// ONLY bridge is allowed to burn, for security reasons.
|
|
||||||
function burn(
|
|
||||||
address _from,
|
|
||||||
uint256 _amount
|
|
||||||
)
|
|
||||||
external
|
|
||||||
virtual
|
|
||||||
override(IOptimismMintableERC20)
|
|
||||||
onlyBridge
|
|
||||||
{
|
|
||||||
_burn(_from, _amount);
|
|
||||||
emit Burn(_from, _amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @notice ERC165 interface check function.
|
|
||||||
/// @param _interfaceId Interface ID to check.
|
|
||||||
/// @return Whether or not the interface is supported by this contract.
|
|
||||||
function supportsInterface(bytes4 _interfaceId) public pure virtual override(AbstractToken, IERC165) returns (bool) {
|
|
||||||
bytes4 iface1 = type(IERC165).interfaceId;
|
|
||||||
// Interface corresponding to the legacy L2StandardERC20.
|
|
||||||
bytes4 iface2 = type(ILegacyMintableERC20).interfaceId;
|
|
||||||
// Interface corresponding to the updated OptimismMintableERC20 (this contract).
|
|
||||||
bytes4 iface3 = type(IOptimismMintableERC20).interfaceId;
|
|
||||||
return _interfaceId == iface1 || _interfaceId == iface2 || _interfaceId == iface3;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @custom:legacy
|
|
||||||
/// @notice Legacy getter for REMOTE_TOKEN.
|
|
||||||
// Is this needed?
|
|
||||||
function remoteToken() public view virtual override(IOptimismMintableERC20) returns (address) {
|
|
||||||
return REMOTE_TOKEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @custom:legacy
|
|
||||||
/// @notice Legacy getter for BRIDGE.
|
|
||||||
// Is this needed?
|
|
||||||
function bridge() public view virtual override(IOptimismMintableERC20) returns (address) {
|
|
||||||
return BRIDGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @custom:legacy
|
|
||||||
/// @notice Legacy getter for the remote token. Use REMOTE_TOKEN going forward.
|
|
||||||
// Turns out Base blockchain needed this.
|
|
||||||
function l1Token() public virtual view returns (address) {
|
|
||||||
return REMOTE_TOKEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @custom:legacy
|
|
||||||
/// @notice Legacy getter for the bridge. Use BRIDGE going forward.
|
|
||||||
// Turns out Base blockchain needed this.
|
|
||||||
function l2Bridge() public virtual view returns (address) {
|
|
||||||
return BRIDGE;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +1,10 @@
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
pragma solidity ^0.8.19;
|
pragma solidity ^0.8.9;
|
||||||
|
|
||||||
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
||||||
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
|
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
|
||||||
import "@openzeppelin/contracts/security/Pausable.sol";
|
import "@openzeppelin/contracts/security/Pausable.sol";
|
||||||
import "@openzeppelin/contracts/access/Ownable.sol";
|
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||||
// Without paying gas, token holders will be able to allow third parties to transfer from their account. (EIP2612)
|
|
||||||
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
|
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
|
||||||
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
|
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Votes.sol";
|
||||||
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20FlashMint.sol";
|
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20FlashMint.sol";
|
||||||
|
@ -14,10 +13,10 @@ import "erc-payable-token/contracts/token/ERC1363/IERC1363.sol";
|
||||||
import "erc-payable-token/contracts/token/ERC1363/IERC1363Spender.sol";
|
import "erc-payable-token/contracts/token/ERC1363/IERC1363Spender.sol";
|
||||||
import "erc-payable-token/contracts/token/ERC1363/IERC1363Receiver.sol";
|
import "erc-payable-token/contracts/token/ERC1363/IERC1363Receiver.sol";
|
||||||
|
|
||||||
abstract contract AbstractToken is IERC1363, ERC20, ERC20Burnable, Pausable, Ownable, ERC20Permit, ERC20Votes, ERC20FlashMint {
|
abstract contract AbstractGrans is IERC1363, ERC20, ERC20Burnable, Pausable, Ownable, ERC20Permit, ERC20Votes, ERC20FlashMint {
|
||||||
using Address for address;
|
using Address for address;
|
||||||
|
|
||||||
constructor(string memory _name, string memory _symbol) ERC20(_name, _symbol) ERC20Permit(_name) {}
|
constructor() ERC20("10Grans", "GRANS") ERC20Permit("10Grans") {}
|
||||||
|
|
||||||
function pause() public onlyOwner {
|
function pause() public onlyOwner {
|
||||||
_pause();
|
_pause();
|
|
@ -0,0 +1,30 @@
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.8.9;
|
||||||
|
|
||||||
|
import "./TenGransAbstractToken.sol";
|
||||||
|
import "@arbitrum/token-bridge-contracts/contracts/tokenbridge/arbitrum/IArbToken.sol";
|
||||||
|
|
||||||
|
contract TenGransArbToken is AbstractGrans, IArbToken {
|
||||||
|
uint256 public immutable cap = 15_000 * 10 ** 18;
|
||||||
|
address public immutable l2Gateway;
|
||||||
|
address public immutable override l1Address;
|
||||||
|
|
||||||
|
modifier onlyGateway() {
|
||||||
|
require(msg.sender == l2Gateway, "ONLY_l2GATEWAY");
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(address _l2Gateway, address _l1Address) AbstractGrans() {
|
||||||
|
l2Gateway = _l2Gateway;
|
||||||
|
l1Address = _l1Address;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bridgeMint(address account, uint256 amount) external virtual override onlyGateway {
|
||||||
|
require(amount + totalSupply() <= cap, "CAP_EXCEEDED");
|
||||||
|
_mint(account, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function bridgeBurn(address account, uint256 amount) external virtual override onlyGateway {
|
||||||
|
_burn(account, amount);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
pragma solidity ^0.8.9;
|
||||||
|
|
||||||
|
import "./TenGransAbstractToken.sol";
|
||||||
|
import "@arbitrum/token-bridge-contracts/contracts/tokenbridge/ethereum/ICustomToken.sol";
|
||||||
|
|
||||||
|
interface IL1CustomGateway {
|
||||||
|
function registerTokenToL2(
|
||||||
|
address _l2Address,
|
||||||
|
uint256 _maxGas,
|
||||||
|
uint256 _gasPriceBid,
|
||||||
|
uint256 _maxSubmissionCost,
|
||||||
|
address _creditBackAddress
|
||||||
|
) external payable returns (uint256);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IGatewayRouter2 {
|
||||||
|
function setGateway(
|
||||||
|
address _gateway,
|
||||||
|
uint256 _maxGas,
|
||||||
|
uint256 _gasPriceBid,
|
||||||
|
uint256 _maxSubmissionCost,
|
||||||
|
address _creditBackAddress
|
||||||
|
) external payable returns (uint256);
|
||||||
|
}
|
||||||
|
|
||||||
|
contract TenGransEthToken is AbstractGrans, ICustomToken {
|
||||||
|
address public immutable gateway;
|
||||||
|
address public immutable router;
|
||||||
|
bool private shouldRegisterGateway;
|
||||||
|
|
||||||
|
constructor(address _gateway, address _router) AbstractGrans() {
|
||||||
|
gateway = _gateway;
|
||||||
|
router = _router;
|
||||||
|
_mint(msg.sender, 15_000 * 10 ** 18);
|
||||||
|
}
|
||||||
|
|
||||||
|
function balanceOf(address account) public view virtual override(AbstractGrans, ICustomToken) returns (uint256) {
|
||||||
|
return AbstractGrans.balanceOf(account);
|
||||||
|
}
|
||||||
|
|
||||||
|
function transferFrom(address sender, address recipient, uint256 amount) public virtual override(AbstractGrans, ICustomToken) returns (bool) {
|
||||||
|
return AbstractGrans.transferFrom(sender, recipient, amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @dev we only set shouldRegisterGateway to true when in `registerTokenOnL2`
|
||||||
|
function isArbitrumEnabled() external view override returns (uint8) {
|
||||||
|
require(shouldRegisterGateway, "NOT_EXPECTED_CALL");
|
||||||
|
return uint8(uint16(uint32(uint64(uint128(0xa4b1)))));
|
||||||
|
}
|
||||||
|
|
||||||
|
function registerTokenOnL2(
|
||||||
|
address l2CustomTokenAddress,
|
||||||
|
uint256 maxSubmissionCostForCustomGateway,
|
||||||
|
uint256 maxSubmissionCostForRouter,
|
||||||
|
uint256 maxGasForCustomGateway,
|
||||||
|
uint256 maxGasForRouter,
|
||||||
|
uint256 gasPriceBid,
|
||||||
|
uint256 valueForGateway,
|
||||||
|
uint256 valueForRouter,
|
||||||
|
address creditBackAddress
|
||||||
|
) public payable override onlyOwner {
|
||||||
|
// we temporarily set `shouldRegisterGateway` to true for the callback in registerTokenToL2 to succeed
|
||||||
|
bool prev = shouldRegisterGateway;
|
||||||
|
shouldRegisterGateway = true;
|
||||||
|
|
||||||
|
IL1CustomGateway(gateway).registerTokenToL2{value: valueForGateway}(
|
||||||
|
l2CustomTokenAddress,
|
||||||
|
maxGasForCustomGateway,
|
||||||
|
gasPriceBid,
|
||||||
|
maxSubmissionCostForCustomGateway,
|
||||||
|
creditBackAddress
|
||||||
|
);
|
||||||
|
|
||||||
|
IGatewayRouter2(router).setGateway{value: valueForRouter}(
|
||||||
|
gateway,
|
||||||
|
maxGasForRouter,
|
||||||
|
gasPriceBid,
|
||||||
|
maxSubmissionCostForRouter,
|
||||||
|
creditBackAddress
|
||||||
|
);
|
||||||
|
|
||||||
|
shouldRegisterGateway = prev;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
# rename to .env.{environment}
|
|
||||||
TOKEN_NAME=MrTestToken
|
|
||||||
TOKEN_SYMBOL=TEST
|
|
||||||
TOKEN_AMOUNT=1000
|
|
||||||
BRIDGE_ADDRESS=0x4200000000000000000000000000000000000010
|
|
||||||
MNEMONIC="special person entering our world egg yolks"
|
|
||||||
INFURA_KEY=bananaphone
|
|
|
@ -1,67 +1,17 @@
|
||||||
import { HardhatUserConfig } from "hardhat/config";
|
import { HardhatUserConfig } from "hardhat/config";
|
||||||
import "@nomicfoundation/hardhat-toolbox";
|
import "@nomicfoundation/hardhat-toolbox";
|
||||||
import "hardhat-contract-sizer";
|
import "hardhat-contract-sizer";
|
||||||
import dotenv from "dotenv";
|
|
||||||
|
|
||||||
const env = process.env.NODE_ENV || "local";
|
|
||||||
dotenv.config({ path: `.env.${env}` });
|
|
||||||
|
|
||||||
const config: HardhatUserConfig = {
|
const config: HardhatUserConfig = {
|
||||||
solidity: {
|
solidity: {
|
||||||
version: "0.8.19", // don"t make this higher
|
version: "0.8.19",
|
||||||
settings: {
|
settings: {
|
||||||
optimizer: {
|
optimizer: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
runs: 200
|
runs: 200
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
networks: {
|
|
||||||
"base-mainnet": {
|
|
||||||
url: "https://mainnet.base.org",
|
|
||||||
accounts: {
|
|
||||||
mnemonic: process.env.MNEMONIC as string
|
|
||||||
},
|
|
||||||
gasPrice: 1000000000,
|
|
||||||
},
|
|
||||||
"base-goerli": {
|
|
||||||
url: "https://goerli.base.org",
|
|
||||||
accounts: {
|
|
||||||
mnemonic: process.env.MNEMONIC as string
|
|
||||||
},
|
|
||||||
gasPrice: 1000000000,
|
|
||||||
},
|
|
||||||
"base-local": {
|
|
||||||
url: "http://localhost:8545",
|
|
||||||
accounts: {
|
|
||||||
mnemonic: process.env.MNEMONIC as string
|
|
||||||
},
|
|
||||||
gasPrice: 1000000000,
|
|
||||||
},
|
|
||||||
"eth-goerli": {
|
|
||||||
url: `https://goerli.infura.io/v3/${process.env.INFURA_KEY}`,
|
|
||||||
accounts: {
|
|
||||||
mnemonic: process.env.MNEMONIC as string
|
|
||||||
},
|
|
||||||
gasPrice: 1000000000,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultNetwork: "base-local",
|
|
||||||
etherscan: {
|
|
||||||
apiKey: {
|
|
||||||
"base-goerli": "PLACEHOLDER_STRING" // yes this is really the string
|
|
||||||
},
|
|
||||||
customChains: [
|
|
||||||
{
|
|
||||||
network: "base-goerli",
|
|
||||||
chainId: 84531,
|
|
||||||
urls: {
|
|
||||||
apiURL: "https://api-goerli.basescan.org/api",
|
|
||||||
browserURL: "https://goerli.basescan.org"
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default config;
|
export default config;
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
11
package.json
11
package.json
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "10grans-ng",
|
"name": "10grans-ng",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "10grans on Base",
|
"description": "10grans on Arbitrum",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
@ -14,8 +14,7 @@
|
||||||
"cryptocurrency",
|
"cryptocurrency",
|
||||||
"erc-20",
|
"erc-20",
|
||||||
"token",
|
"token",
|
||||||
"solidity",
|
"solidity"
|
||||||
"coinbase"
|
|
||||||
],
|
],
|
||||||
"author": "moon@shipoclu.com",
|
"author": "moon@shipoclu.com",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
@ -27,10 +26,8 @@
|
||||||
"prettier-plugin-solidity": "^1.1.3"
|
"prettier-plugin-solidity": "^1.1.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@eth-optimism/contracts-bedrock": "^0.16.0",
|
"@arbitrum/token-bridge-contracts": "^1.0.0-beta.0",
|
||||||
"@openzeppelin/contracts": "^4.9.3",
|
"@openzeppelin/contracts": "^4.9.3",
|
||||||
"dotenv": "^16.3.1",
|
"erc-payable-token": "^4.9.3"
|
||||||
"erc-payable-token": "^4.9.3",
|
|
||||||
"ethers": "^6.7.1"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
NODE_ENV=dev
|
|
||||||
export NODE_ENV
|
|
||||||
|
|
||||||
npx hardhat compile
|
|
||||||
|
|
||||||
npx hardhat --network eth-goerli run scripts/deploy-l1.ts
|
|
||||||
|
|
||||||
if [ "$?" -eq 0 ]
|
|
||||||
then
|
|
||||||
npx hardhat --network base-goerli run scripts/deploy-l2.ts
|
|
||||||
else
|
|
||||||
echo "deploy to L1 failed so aborting L2" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
|
@ -1,34 +0,0 @@
|
||||||
import { ethers } from "hardhat";
|
|
||||||
import fs from "fs/promises";
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
const env = process.env.NODE_ENV || "local";
|
|
||||||
|
|
||||||
if (!process.env.TOKEN_NAME) throw "Token name not defined";
|
|
||||||
if (!process.env.TOKEN_SYMBOL) throw "Token symbol not defined";
|
|
||||||
if (!process.env.TOKEN_AMOUNT) throw "Token amount not defined";
|
|
||||||
|
|
||||||
const tokenAmount = parseInt(process.env.TOKEN_AMOUNT);
|
|
||||||
|
|
||||||
const L1Token = await ethers.deployContract(
|
|
||||||
"L1Token",
|
|
||||||
[
|
|
||||||
process.env.TOKEN_NAME,
|
|
||||||
process.env.TOKEN_SYMBOL,
|
|
||||||
tokenAmount,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
await L1Token.waitForDeployment();
|
|
||||||
const deployedAddress = await L1Token.getAddress();
|
|
||||||
|
|
||||||
console.log(`L1 token deployed to: ${deployedAddress}`);
|
|
||||||
await fs.writeFile(`.l1-token-address.${env}`, deployedAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We recommend this pattern to be able to use async/await everywhere
|
|
||||||
// and properly handle errors.
|
|
||||||
main().catch((error) => {
|
|
||||||
console.error(error);
|
|
||||||
process.exitCode = 1;
|
|
||||||
});
|
|
|
@ -1,36 +0,0 @@
|
||||||
import { ethers } from "hardhat";
|
|
||||||
import fs from "fs/promises";
|
|
||||||
import dotenv from "dotenv";
|
|
||||||
const env = process.env.NODE_ENV || "local";
|
|
||||||
dotenv.config({ path: `.env.${env}` });
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
if (!process.env.BRIDGE_ADDRESS) throw "Bridge address not defined";
|
|
||||||
if (!process.env.TOKEN_NAME) throw "Token name not defined";
|
|
||||||
if (!process.env.TOKEN_SYMBOL) throw "Token symbol not defined";
|
|
||||||
|
|
||||||
const L1TokenAddress = (await fs.readFile(`.l1-token-address.${env}`, "utf-8")).trim();
|
|
||||||
|
|
||||||
const L2Token = await ethers.deployContract(
|
|
||||||
"L2Token",
|
|
||||||
[
|
|
||||||
process.env.TOKEN_NAME,
|
|
||||||
process.env.TOKEN_SYMBOL,
|
|
||||||
process.env.BRIDGE_ADDRESS,
|
|
||||||
L1TokenAddress,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
await L2Token.waitForDeployment();
|
|
||||||
const deployedAddress = await L2Token.getAddress();
|
|
||||||
|
|
||||||
console.log(`L2 token deployed to: ${deployedAddress}`);
|
|
||||||
await fs.writeFile(`.l2-token-address.${env}`, deployedAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We recommend this pattern to be able to use async/await everywhere
|
|
||||||
// and properly handle errors.
|
|
||||||
main().catch((error) => {
|
|
||||||
console.error(error);
|
|
||||||
process.exitCode = 1;
|
|
||||||
});
|
|
|
@ -1,17 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
NODE_ENV=local
|
|
||||||
export NODE_ENV
|
|
||||||
|
|
||||||
nc -z "127.0.0.1" 8545 >/dev/null 2>&1
|
|
||||||
if [ "$?" -ne 0 ]
|
|
||||||
then
|
|
||||||
echo "local node not running" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
npx hardhat compile
|
|
||||||
|
|
||||||
# Same network, for testing reasons only
|
|
||||||
npx hardhat --network base-local run scripts/deploy-l1.ts
|
|
||||||
npx hardhat --network base-local run scripts/deploy-l2.ts
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { ethers } from "hardhat";
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
// Do stuff here.
|
||||||
|
}
|
||||||
|
|
||||||
|
// We recommend this pattern to be able to use async/await everywhere
|
||||||
|
// and properly handle errors.
|
||||||
|
main().catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
process.exitCode = 1;
|
||||||
|
});
|
Loading…
Reference in New Issue