Compare commits
23 Commits
Author | SHA1 | Date |
---|---|---|
Moon Man | 2b6f14aa16 | |
Moon Man | 6a99fda774 | |
Moon Man | b9fc687822 | |
Moon Man | e8ab27e7c9 | |
Moon Man | bb7dcc42b8 | |
Moon Man | aff7031a2f | |
Moon Man | 0246e47a58 | |
Moon Man | 68af3cb3f0 | |
Moon Man | 1b8ecb5c37 | |
Moon Man | f19428e409 | |
Moon Man | c952ccc38d | |
Moon Man | c69b64994a | |
Moon Man | 725f30100c | |
Moon Man | a03053efa1 | |
Moon Man | 5f95b7d3ac | |
Moon Man | 2d0702934f | |
Moon Man | fa4ede8fbc | |
Moon Man | 98af8dccad | |
Moon Man | ffae5e74d3 | |
Moon Man | d8bc375d4c | |
Moon Man | 0fab17085f | |
Moon Man | 6e7f451080 | |
Moon Man | 091be423aa |
|
@ -1,10 +1,14 @@
|
||||||
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,11 +1,25 @@
|
||||||
# 10grans-NG
|
# Optimism-Compatible Token
|
||||||
|
|
||||||
10grans on Arbitrum chain, with a bridge to Ethereum.
|
EVM-compatible ERC-20 token with extra features, capable of being bridged to an
|
||||||
|
Optimism-compatible L2. Built for Base blockchain and the 10Grans token,
|
||||||
|
but general enough to be reused.
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
npx hardhat help
|
npx hardhat compile
|
||||||
npx hardhat test
|
|
||||||
REPORT_GAS=true npx hardhat test
|
cp env.example .env.dev
|
||||||
npx hardhat node
|
# (customize your env file here first)
|
||||||
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,10 +1,11 @@
|
||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
pragma solidity ^0.8.9;
|
pragma solidity ^0.8.19;
|
||||||
|
|
||||||
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";
|
||||||
|
@ -13,10 +14,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 AbstractGrans is IERC1363, ERC20, ERC20Burnable, Pausable, Ownable, ERC20Permit, ERC20Votes, ERC20FlashMint {
|
abstract contract AbstractToken is IERC1363, ERC20, ERC20Burnable, Pausable, Ownable, ERC20Permit, ERC20Votes, ERC20FlashMint {
|
||||||
using Address for address;
|
using Address for address;
|
||||||
|
|
||||||
constructor() ERC20("10Grans", "GRANS") ERC20Permit("10Grans") {}
|
constructor(string memory _name, string memory _symbol) ERC20(_name, _symbol) ERC20Permit(_name) {}
|
||||||
|
|
||||||
function pause() public onlyOwner {
|
function pause() public onlyOwner {
|
||||||
_pause();
|
_pause();
|
|
@ -0,0 +1,11 @@
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,116 @@
|
||||||
|
// 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,30 +0,0 @@
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,85 +0,0 @@
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
# 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,17 +1,67 @@
|
||||||
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",
|
version: "0.8.19", // don"t make this higher
|
||||||
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 Arbitrum",
|
"description": "10grans on Base",
|
||||||
"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,7 +14,8 @@
|
||||||
"cryptocurrency",
|
"cryptocurrency",
|
||||||
"erc-20",
|
"erc-20",
|
||||||
"token",
|
"token",
|
||||||
"solidity"
|
"solidity",
|
||||||
|
"coinbase"
|
||||||
],
|
],
|
||||||
"author": "moon@shipoclu.com",
|
"author": "moon@shipoclu.com",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
@ -26,8 +27,10 @@
|
||||||
"prettier-plugin-solidity": "^1.1.3"
|
"prettier-plugin-solidity": "^1.1.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@arbitrum/token-bridge-contracts": "^1.0.0-beta.0",
|
"@eth-optimism/contracts-bedrock": "^0.16.0",
|
||||||
"@openzeppelin/contracts": "^4.9.3",
|
"@openzeppelin/contracts": "^4.9.3",
|
||||||
"erc-payable-token": "^4.9.3"
|
"dotenv": "^16.3.1",
|
||||||
|
"erc-payable-token": "^4.9.3",
|
||||||
|
"ethers": "^6.7.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/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
|
|
@ -0,0 +1,34 @@
|
||||||
|
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;
|
||||||
|
});
|
|
@ -0,0 +1,36 @@
|
||||||
|
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;
|
||||||
|
});
|
|
@ -0,0 +1,17 @@
|
||||||
|
#!/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
|
|
@ -1,12 +0,0 @@
|
||||||
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