Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement deployment scripts for Lisk token and Claim process smart contracts #6

Merged
merged 23 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
# Deployer private key
PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

# L1 RPC URL, e.g. Infura, Alchemy, or your own node
L1_RPC_URL=https://sepolia.infura.io/v3/YOUR_INFURA_KEY

# RPC URL of the L1 network from which a private L1 test network is forked if a private test network is used, e.g. Infura, Alchemy
L1_FORK_RPC_URL=https://sepolia.infura.io/v3/YOUR_INFURA_KEY
AndreasKendziorra marked this conversation as resolved.
Show resolved Hide resolved

# L2 RPC URL, e.g. Infura, Alchemy, or your own node
L2_RPC_URL=https://optimism-sepolia.infura.io/v3/YOUR_INFURA_KEY

# RPC URL of the L2 network from which a private L2 test network is forked if a private test network is used, e.g. Infura, Alchemy
L2_FORK_RPC_URL=https://optimism-sepolia.infura.io/v3/YOUR_INFURA_KEY
AndreasKendziorra marked this conversation as resolved.
Show resolved Hide resolved

# L1 Etherscan API key (needed for contract verification, if not provided, verification will be skipped)
L1_ETHERSCAN_API_KEY=

# L2 Etherscan API key (needed for contract verification, if not provided, verification will be skipped)
L2_ETHERSCAN_API_KEY=

# L1 standard bridge address (Mainnet, Goerli, Sepolia or custom)
L1_STANDARD_BRIDGE_ADDR=0xFBb0621E0B23b5478B630BD55a5f21f67730B0F1

# Test network default mnemonic
TEST_NETWORK_MNEMONIC="test test test test test test test test test test test junk"
6 changes: 2 additions & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@
cache/
out/

# Ignores development broadcast logs
!/broadcast
/broadcast/*/31337/
/broadcast/**/dry-run/
# Ignores broadcast logs
broadcast/

# Dotenv file
.env
38 changes: 32 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ This package contains the smart contracts for the Lisk L2 network. In order for

### Contracts deployed to L2

| Name | Description |
| ---- | ----------- |
| Name | Description |
| --------------------------------------- | ------------------------------------------------------------------- |
| [`L2LiskToken`](src/L2/L2LiskToken.sol) | Bridged Lisk token (LSK) deployed on Lisk L2 network |
| [`L2Claim`](src/L2/L2Claim.sol) | Smart contract responsible for a claiming process of the LSK tokens |

## Installation

Expand All @@ -25,14 +27,38 @@ curl -L https://foundry.paradigm.xyz | bash

This will install `Foundryup`, then simply follow the instructions on-screen, which will make the `foundryup` command available in your CLI. Running `foundryup` by itself will install the latest (nightly) precompiled binaries: `forge`, `cast`, `anvil`, and `chisel`.

### Cloning the Lisk Smart Contracts Repository
To download all the necessary project files and libraries, execute the following commands:
```shell
git clone https://github.com/LiskHQ/lisk-contracts.git
```
Inside newly created `lisk-contracts` directory:
```shell
git submodule update --init --recursive
```

## Deployment on Private Test Network

A private test network is established using the `anvil` tool, and the smart contracts are deployed using the `forge script` tool. To run a private network and deploy the smart contracts, follow these steps:
1. Create `.env` file and set the vars `PRIVATE_KEY` and `TEST_NETWORK_MNEMONIC`. You can copy and rename the `.env.example` file if the default values provided in `.env.example` are satisfactory.
**NOTE**: On a private test network, the deployment of smart contracts is feasible on both L1 and L2 networks. However, the transfer of tokens between these networks is not possible as it requires the operation of the Sequencer.

**NOTE**: To successfully deploy all smart contracts and execute the required transactions, the deployer (specified by `PRIVATE_KEY` in the `.env` file) must have funds available in its address on the respective networks. For a private test network, you can use a any private key from the list provided by `anvil` when the network is created, or choose another private key with sufficient funds on both forked networks.

Private L1 and L2 test networks are established using the `anvil` tool, and the smart contracts are deployed using the `forge script` tool. To run private networks and deploy the smart contracts, follow these steps:
1. Create `.env` file and set the vars `PRIVATE_KEY`, `L1_RPC_URL`, `L1_FORK_RPC_URL`, `L2_RPC_URL`, `L2_FORK_RPC_URL`, `L1_STANDARD_BRIDGE_ADDR` and `TEST_NETWORK_MNEMONIC`. You can copy and rename the `.env.example` file if the default values provided in `.env.example` are satisfactory. `L1_RPC_URL` should be set to `http://127.0.0.1:8545` and `L2_RPC_URL` should be set to `http://127.0.0.1:8546` if no changes are made in the `./runL1TestNetwork.sh` or `./runL2TestNetwork.sh` script files.
AndreasKendziorra marked this conversation as resolved.
Show resolved Hide resolved
2. Navigate to the `script` directory.
3. To create and launch a private test network, execute the script: `./runTestNetwork.sh`
4. To deploy all smart contracts, execute the script: `./deployContracts.sh`
3. To create and launch a private test L1 network, execute the script: `./runL1TestNetwork.sh`
4. To create and launch a private test L2 network, execute the script: `./runL2TestNetwork.sh`
5. To deploy all smart contracts, execute the script: `./deployContracts.sh`
AndreasKendziorra marked this conversation as resolved.
Show resolved Hide resolved

## Deployment on Public Test Network

**NOTE**: To successfully deploy all smart contracts and execute the required transactions, the deployer (specified by `PRIVATE_KEY` in the `.env` file) must have funds available in its address. This implies that a private key with a sufficient balance on both public test networks is required.

To deploy smart contracts on both L1 and L2 public networks, you will need to provide for each network an URL for a public node from a RPC provider, such as Alchemy or Infura. Additionally, in order to verify smart contracts during the deployment process, it is necessary to provide an Etherscan API key. Follow these steps to deploy the smart contracts:
1. Create `.env` file and set the vars `PRIVATE_KEY`, `L1_RPC_URL`, `L2_RPC_URL`, `L1_ETHERSCAN_API_KEY`, `L2_ETHERSCAN_API_KEY` and `L1_STANDARD_BRIDGE_ADDR`. You can copy and rename the `.env.example` file if the default values provided in `.env.example` are satisfactory. `L1_ETHERSCAN_API_KEY` and `L2_ETHERSCAN_API_KEY` may be empty to skip smart contracts verification process.
AndreasKendziorra marked this conversation as resolved.
Show resolved Hide resolved
2. Navigate to the `script` directory.
3. To deploy all smart contracts, execute the script: `./deployContracts.sh`
AndreasKendziorra marked this conversation as resolved.
Show resolved Hide resolved

## Tips & Tricks

**WARNING**: Foundry installs the latest versions of `openzeppelin-contracts` and `openzeppelin-contracts-upgradeable` initially, but subsequent `forge update` commands will use the `master` branch which is a development branch that should be avoided in favor of tagged releases. The release process involves security measures that the `master` branch does not guarantee.
5 changes: 5 additions & 0 deletions deployment/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Ignore everything in this directory
*

# Except for this file
!.gitignore
shuse2 marked this conversation as resolved.
Show resolved Hide resolved
2 changes: 2 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
src = "src"
out = "out"
libs = ["lib"]
fs_permissions = [{ access = "read-write", path = "./" }]
solc_version = "0.8.21"
optimizer = true
optimizer_runs = 999999
remappings = [
Expand Down
24 changes: 22 additions & 2 deletions script/L1LiskToken.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,25 @@ pragma solidity 0.8.21;

import { Script, console2 } from "forge-std/Script.sol";
import { L1LiskToken, UUPSProxy } from "src/L1/L1LiskToken.sol";
import "script/Utils.sol";

contract L1LiskTokencript is Script {
function setUp() public { }
/// @title L1LiskTokenScript - L1 Lisk token deployment script
/// @notice This contract is used to deploy L1 Lisk token contract and write its address to JSON file.
contract L1LiskTokenScript is Script {
/// @notice Utils contract which provides functions to read and write JSON files containing L1 and L2 addresses.
Utils utils;

function setUp() public {
utils = new Utils();
}

/// @notice This function deploys L1 Lisk token contract and writes its address to JSON file.
function run() public {
// Deployer's private key. Owner of the L1 Lisk token. PRIVATE_KEY is set in .env file.
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");

console2.log("Simulation: Deploying L1 Lisk token...");

// deploy L1LiskToken contract
vm.startBroadcast(deployerPrivateKey);
L1LiskToken l1LiskToken = new L1LiskToken();
Expand Down Expand Up @@ -39,5 +51,13 @@ contract L1LiskTokencript is Script {
assert(wrappedProxy.totalSupply() == 200000000 * 10 ** 18);
assert(wrappedProxy.balanceOf(vm.addr(deployerPrivateKey)) == 200000000 * 10 ** 18);
assert(wrappedProxy.owner() == vm.addr(deployerPrivateKey));

console2.log("Simulation: L1 Lisk token successfully deployed!");
console2.log("Simulation: L1 Lisk token address: %s", address(wrappedProxy));
shuse2 marked this conversation as resolved.
Show resolved Hide resolved

// write L1LiskToken address to l1addresses.json
Utils.L1AddressesConfig memory l1AddressesConfig;
l1AddressesConfig.L1LiskToken = address(wrappedProxy);
utils.writeL1AddressesFile(l1AddressesConfig);
}
}
46 changes: 46 additions & 0 deletions script/L2Claim.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.21;

import { Script, console2 } from "forge-std/Script.sol";
import { L2Claim } from "src/L2/L2Claim.sol";
import "script/Utils.sol";

/// @title L2ClaimScript - L2 Claim contract deployment script
/// @notice This contract is used to deploy L2 Claim contract and write its address to JSON file.
contract L2ClaimScript is Script {
/// @notice Utils contract which provides functions to read and write JSON files containing L1 and L2 addresses.
Utils utils;

function setUp() public {
utils = new Utils();
}

/// @notice This function deploys L2 Claim contract and writes its address to JSON file.
function run() public {
// Deployer's private key. Owner of the Claim contract which can perform upgrades. PRIVATE_KEY is set in .env
// file.
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");

console2.log("Simulation: Deploying L2 Claim contract...");

// get L2LiskToken contract address
Utils.L2AddressesConfig memory l2AddressesConfig = utils.readL2AddressesFile();
console2.log("Simulation: L2 Lisk token address: %s", l2AddressesConfig.L2LiskToken);

// deploy L2Claim contract
vm.startBroadcast(deployerPrivateKey);
L2Claim l2Claim = new L2Claim(address(l2AddressesConfig.L2LiskToken));
vm.stopBroadcast();

assert(address(l2Claim) != address(0));
assert(keccak256(bytes(l2Claim.name())) == keccak256(bytes("Claim process")));
assert(address(l2Claim.l2LiskToken()) == address(l2AddressesConfig.L2LiskToken));

console2.log("Simulation: L2 Claim contract successfully deployed!");
console2.log("Simulation: L2 Claim contract address: %s", address(l2Claim));

// write L2ClaimContract address to l2addresses.json
l2AddressesConfig.L2ClaimContract = address(l2Claim);
utils.writeL2AddressesFile(l2AddressesConfig);
}
}
59 changes: 59 additions & 0 deletions script/L2ClaimTokens.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.21;

import { Script, console2 } from "forge-std/Script.sol";
import { L2Claim } from "src/L2/L2Claim.sol";
import "script/Utils.sol";

/// @title L2ClaimTokensScript - L2 Claim Lisk tokens script
/// @notice This contract is used to claim L2 Lisk tokens from the L2 Claim contract for a demonstration purpose.
contract L2ClaimTokensScript is Script {
/// @notice Utils contract which provides functions to read and write JSON files containing L1 and L2 addresses.
Utils utils;

function setUp() public {
utils = new Utils();
}

/// @notice This function claims L2 Lisk tokens from the L2 Claim contract for a demonstration purpose.
function run() public {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");

// print deployer address
console2.log("Simulation: Deployer address: %s", vm.addr(deployerPrivateKey));

// get L2Claim contract address
Utils.L2AddressesConfig memory l2AddressesConfig = utils.readL2AddressesFile();
console2.log("Simulation: L2 Claim contract address: %s", l2AddressesConfig.L2ClaimContract);

// check L2Claim contract Lisk token balance
L2Claim l2Claim = L2Claim(address(l2AddressesConfig.L2ClaimContract));
console2.log(
"Simulation: L2 Claim contract Lisk token balance before claim: %s",
l2Claim.l2LiskToken().balanceOf(address(l2Claim))
);

// check deployer Lisk token balance
console2.log(
"Simulation: Deployer's Lisk token balance before claim: %s",
l2Claim.l2LiskToken().balanceOf(vm.addr(deployerPrivateKey))
);

// claim 5 Lisk tokens for a demonstration purpose
vm.startBroadcast(deployerPrivateKey);
l2Claim.claim();
vm.stopBroadcast();

// check that L2Claim contract has less Lisk tokens than before
console2.log(
"Simulation: L2 Claim contract Lisk token balance after claim: %s",
l2Claim.l2LiskToken().balanceOf(address(l2Claim))
);

// check that deployer has 5 Lisk tokens
console2.log(
"Simulation: Deployer's Lisk token balance after claim: %s",
l2Claim.l2LiskToken().balanceOf(vm.addr(deployerPrivateKey))
);
}
}
52 changes: 52 additions & 0 deletions script/L2LiskToken.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.21;

import { Script, console2 } from "forge-std/Script.sol";
import { L2LiskToken } from "src/L2/L2LiskToken.sol";
import "script/Utils.sol";

/// @title L2LiskTokenScript - L2 Lisk token deployment script
/// @notice This contract is used to deploy L2 Lisk token contract and write its address to JSON file.
contract L2LiskTokenScript is Script {
/// @notice Utils contract which provides functions to read and write JSON files containing L1 and L2 addresses.
Utils utils;

/// @notice L2 Standard Bridge address.
address private constant L2_STANDARD_BRIDGE = 0x4200000000000000000000000000000000000010;

function setUp() public {
utils = new Utils();
}

/// @notice This function deploys L2 Lisk token contract and writes its address to JSON file.
function run() public {
// Deployer's private key. Owner of the L2 Lisk token. PRIVATE_KEY is set in .env file.
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");

console2.log("Simulation: Deploying L2 Lisk token...");

// get L1LiskToken contract address
Utils.L1AddressesConfig memory l1AddressesConfig = utils.readL1AddressesFile();
console2.log("Simulation: L1 Lisk token address: %s", l1AddressesConfig.L1LiskToken);

// deploy L2LiskToken contract
vm.startBroadcast(deployerPrivateKey);
L2LiskToken l2LiskToken = new L2LiskToken(L2_STANDARD_BRIDGE, l1AddressesConfig.L1LiskToken, "Lisk", "LSK", 18);
AndreasKendziorra marked this conversation as resolved.
Show resolved Hide resolved
vm.stopBroadcast();

assert(address(l2LiskToken) != address(0));
assert(keccak256(bytes(l2LiskToken.name())) == keccak256(bytes("Lisk")));
assert(keccak256(bytes(l2LiskToken.symbol())) == keccak256(bytes("LSK")));
assert(l2LiskToken.decimals() == 18);
assert(l2LiskToken.REMOTE_TOKEN() == l1AddressesConfig.L1LiskToken);
assert(l2LiskToken.BRIDGE() == L2_STANDARD_BRIDGE);

console2.log("Simulation: L2 Lisk token successfully deployed!");
console2.log("Simulation: L2 Lisk token address: %s", address(l2LiskToken));

// write L2LiskToken address to l2addresses.json
Utils.L2AddressesConfig memory l2AddressesConfig;
l2AddressesConfig.L2LiskToken = address(l2LiskToken);
utils.writeL2AddressesFile(l2AddressesConfig);
}
}
Loading