Skip to content

Commit

Permalink
Update L2ClaimTokens script (#52)
Browse files Browse the repository at this point in the history
* Update L2ClaimTokens script

* Update comments on L2ClaimTokens.s.sol

* Add assertions to L2ClaimTokens.s.sol

* Add demoClaim.sh script

* Update comment

* add publicity of Utils utils

* Rearrange devnet files

* Have 2 set of independent merkle trees for scripts and test
  • Loading branch information
Phanco authored Mar 5, 2024
1 parent ba69b7d commit e39b665
Show file tree
Hide file tree
Showing 9 changed files with 2,019 additions and 30 deletions.
File renamed without changes.
902 changes: 902 additions & 0 deletions script/data/devnet/signatures.json

Large diffs are not rendered by default.

33 changes: 33 additions & 0 deletions script/example/DemoTransferFunds.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.23;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Script, console2 } from "forge-std/Script.sol";
import { StdUtils } from "forge-std/StdUtils.sol";
import "script/Utils.sol";

/// @title DemoTransferFundsScript - Demo Transferring LSK to Claim contract
/// @notice In Demo environment, after Claim contract is deployed, this script is used to send LSK tokens to Claim
/// contract.
contract DemoTransferFundsScript is Script {
/// @notice Utils contract which provides functions to read and write JSON files containing L1 and L2 addresses.
Utils internal utils;

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

/// @notice Transfer LSK Tokens to Claim contract
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");

// read L2LiskToken address from l2addresses.json
Utils.L2AddressesConfig memory l2AddressesConfig = utils.readL2AddressesFile();
IERC20 lsk = IERC20(l2AddressesConfig.L2LiskToken);

vm.startBroadcast(deployerPrivateKey);
lsk.transfer(l2AddressesConfig.L2ClaimContract, 10000 ether);
vm.stopBroadcast();
}
}
127 changes: 100 additions & 27 deletions script/example/L2ClaimTokens.s.sol
Original file line number Diff line number Diff line change
@@ -1,54 +1,127 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.23;

import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Script, console2 } from "forge-std/Script.sol";
import { L2Claim } from "src/L2/L2Claim.sol";
import { stdJson } from "forge-std/StdJson.sol";
import { L2Claim, ED25519Signature, MultisigKeys } from "src/L2/L2Claim.sol";
import { Signature, MerkleTreeLeaf, MerkleLeaves } from "test/L2/L2Claim.t.sol";
import { MockERC20 } from "../../test/mock/MockERC20.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.
/// @notice This contract is used to claim L2 Lisk tokens from the L2 Claim contract for a demonstration purpose. This
/// contract works independently without interacting with previously-deployed contracts and only works when `NETWORK` is
/// set as `devnet`.
contract L2ClaimTokensScript is Script {
using stdJson for string;

/// @notice Utils contract which provides functions to read and write JSON files containing L1 and L2 addresses.
Utils utils;
Utils internal utils;

function setUp() public {
utils = new Utils();
/// @notice LSK Token in L2.
IERC20 internal lsk;

/// @notice L2Claim Contract, with address pointing to Proxy.
L2Claim internal l2Claim;

/// @notice signatures.json in string format.
string public signatureJson;

/// @notice merkle-leaves.json in string format.
string public merkleLeavesJson;

/// @notice The contract address created by default mnemonic in Anvil/Ganache when nonce=0.
address public constant destination = address(0x34A1D3fff3958843C43aD80F30b94c510645C316);

/// @notice 1 Beddows in LSK Chain = 10 * 10 Beddows in L2 Chain
uint256 public constant MULTIPLIER = 10 ** 10;

function getSignature(uint256 _index) internal view returns (Signature memory) {
return abi.decode(signatureJson.parseRaw(string(abi.encodePacked(".[", vm.toString(_index), "]"))), (Signature));
}

/// @notice This function claims L2 Lisk tokens from the L2 Claim contract for a demonstration purpose.
function run() public view {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
function getMerkleLeaves() internal view returns (MerkleLeaves memory) {
return abi.decode(merkleLeavesJson.parseRaw("."), (MerkleLeaves));
}

// print deployer address
console2.log("Deployer address: %s", vm.addr(deployerPrivateKey));
function setUp() public {
utils = new Utils();

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

// check L2Claim contract Lisk token balance
L2Claim l2Claim = L2Claim(address(l2AddressesConfig.L2ClaimContract));
console2.log(
"L2 Claim contract Lisk token balance before claim: %s", l2Claim.l2LiskToken().balanceOf(address(l2Claim))
);
// Get Merkle Root from /devnet/merkle-root.json
Utils.MerkleRoot memory merkleRoot = utils.readMerkleRootFile();
console2.log("MerkleRoot: %s", vm.toString(merkleRoot.merkleRoot));

// Read devnet Json files
string memory rootPath = string.concat(vm.projectRoot(), "/script/data/devnet");
signatureJson = vm.readFile(string.concat(rootPath, "/signatures.json"));
merkleLeavesJson = vm.readFile(string.concat(rootPath, "/merkle-leaves.json"));
}

// check deployer Lisk token balance
/// @notice This function submit request to `claimRegularAccount` and `claimMultisigAccount` once to demonstrate
/// claiming process of both regular account and multisig account
function run() public {
uint256 previousBalance = lsk.balanceOf(destination);
console2.log("Destination LSK Balance before Claim:", previousBalance, "Beddows");

// Claiming Regular Account
MerkleTreeLeaf memory regularAccountLeaf = getMerkleLeaves().leaves[0];
Signature memory regularAccountSignature = getSignature(0);
console2.log(
"Deployer's Lisk token balance before claim: %s",
l2Claim.l2LiskToken().balanceOf(vm.addr(deployerPrivateKey))
"Claiming Regular Account: id=0, LSK address(hex)=%s, Balance (Old Beddows): %s",
vm.toString(abi.encodePacked(bytes20(regularAccountLeaf.b32Address << 96))),
regularAccountLeaf.balanceBeddows
);
l2Claim.claimRegularAccount(
regularAccountLeaf.proof,
regularAccountSignature.sigs[0].pubKey,
regularAccountLeaf.balanceBeddows,
destination,
ED25519Signature(regularAccountSignature.sigs[0].r, regularAccountSignature.sigs[0].s)
);
assert(previousBalance + regularAccountLeaf.balanceBeddows * MULTIPLIER == lsk.balanceOf(destination));
console2.log("Destination LSK Balance After Regular Account Claim: %s Beddows", lsk.balanceOf(destination));

// Claiming Multisig Account
uint256 multisigAccountIndex = 0;
MerkleTreeLeaf memory multisigAccountLeaf = getMerkleLeaves().leaves[multisigAccountIndex];

// TODO: perform Claim Process for demonstration purpose
// A non-hardcode way to get the first Multisig Account from Merkle Tree
while (multisigAccountLeaf.numberOfSignatures == 0) {
multisigAccountIndex++;
multisigAccountLeaf = getMerkleLeaves().leaves[multisigAccountIndex];
}
Signature memory multisigAccountSignature = getSignature(multisigAccountIndex);

// check that L2Claim contract has less Lisk tokens than before
console2.log(
"L2 Claim contract Lisk token balance after claim: %s", l2Claim.l2LiskToken().balanceOf(address(l2Claim))
"Claiming Multisig Account: id=%s, LSK address(hex)=%s, Balance (Old Beddows): %s",
multisigAccountIndex,
vm.toString(abi.encodePacked(bytes20(multisigAccountLeaf.b32Address << 96))),
multisigAccountLeaf.balanceBeddows
);

// check that deployer has 5 Lisk tokens
console2.log(
"Deployer's Lisk token balance after claim: %s",
l2Claim.l2LiskToken().balanceOf(vm.addr(deployerPrivateKey))
// Gather just-right amount of signatures from signatures.json
ED25519Signature[] memory ed25519Signatures = new ED25519Signature[](multisigAccountLeaf.numberOfSignatures);
for (uint256 i; i < multisigAccountLeaf.numberOfSignatures; i++) {
ed25519Signatures[i] =
ED25519Signature(multisigAccountSignature.sigs[i].r, multisigAccountSignature.sigs[i].s);
}

previousBalance = lsk.balanceOf(destination);
l2Claim.claimMultisigAccount(
multisigAccountLeaf.proof,
bytes20(multisigAccountLeaf.b32Address << 96),
multisigAccountLeaf.balanceBeddows,
MultisigKeys(multisigAccountLeaf.mandatoryKeys, multisigAccountLeaf.optionalKeys),
destination,
ed25519Signatures
);
assert(previousBalance + multisigAccountLeaf.balanceBeddows * MULTIPLIER == lsk.balanceOf(destination));
console2.log("Destination LSK Balance After Multisig Account Claim: %s Beddows", lsk.balanceOf(destination));
}
}
48 changes: 48 additions & 0 deletions script/example/L2DemoToken.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.23;

import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { Script, console2 } from "forge-std/Script.sol";
import { StdUtils } from "forge-std/StdUtils.sol";
import "script/Utils.sol";

/// @title L2DemoToken - Demo L2 LSK Token
/// @notice In Demo environment, this contract will be used due to the lack of bridge.
contract L2DemoToken is ERC20 {
constructor(uint256 _totalSupply) ERC20("Demo Lisk Token", "dLSK") {
_mint(msg.sender, _totalSupply);
}
}

/// @title L2DemoTokenScript - Deploying Demo ERC20 as L2 LSK Token
/// @notice In Demo environment, this script will be used to deploy L2 LSK Token and mint LSK to deployer.
contract L2DemoTokenScript is Script {
/// @notice Utils contract which provides functions to read and write JSON files containing L1 and L2 addresses.
Utils internal utils;

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

function run() public {
// Deployer's private key. Owner of the Demo L2 Lisk token. PRIVATE_KEY is set in .env file.
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");

console2.log("Deploying Demo Lisk token...");

vm.startBroadcast(deployerPrivateKey);
ERC20 lsk = new L2DemoToken(10000 ether);
vm.stopBroadcast();

assert(lsk.decimals() == 18);
assert(lsk.totalSupply() == 10000 ether);

console2.log("L2 Demo Lisk Token successfully deployed!");
console2.log("L2 Demo Lisk Token address: %s", address(lsk));

// write L2LiskToken address to l2addresses.json
Utils.L2AddressesConfig memory l2AddressesConfig;
l2AddressesConfig.L2LiskToken = address(lsk);
utils.writeL2AddressesFile(l2AddressesConfig);
}
}
47 changes: 47 additions & 0 deletions script/example/demoClaim.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#!/usr/bin/env bash

echo "*** This script should only run at DEVNET ***"

echo "Instructing the shell to exit immediately if any command returns a non-zero exit status..."
set -e
echo "Done."

echo "Navigating to the root directory of the project..."
cd ../../
echo "Done."

echo "Setting environment variables..."
source .env
echo "Done."

if [ "$NETWORK" != "devnet" ]
then
echo "This script can only be running at devnet, please change your NETWORK at .env"
exit
fi

echo "Removing files inside deployment directory if they exists..."
rm -rf deployment/devnet
echo "Done."

echo "Creating devnet directory inside deployment directory..."
mkdir deployment/devnet
echo "Done."

echo "Deploying Demo L2LiskToken smart contract..."
forge script --rpc-url="$L2_RPC_URL" --broadcast -vvvv script/example/L2DemoToken.s.sol:L2DemoTokenScript
echo "Done."

echo "Deploying L2Claim smart contract..."
forge script --rpc-url="$L2_RPC_URL" --broadcast -vvvv script/L2Claim.s.sol:L2ClaimScript
echo "Done."

echo "Transferring funds to L2Claim smart contract..."
forge script --rpc-url="$L2_RPC_URL" --broadcast -vvvv script/example/DemoTransferFunds.s.sol:DemoTransferFundsScript
echo "Done."

echo "Submitting Claim..."
forge script --rpc-url="$L2_RPC_URL" --broadcast -vvvv script/example/L2ClaimTokens.s.sol:L2ClaimTokensScript
echo "Done."

echo "Completed."
6 changes: 3 additions & 3 deletions test/L2/L2Claim.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ contract L2ClaimTest is Test {
);
}

// get detailed MerkleTree, which only exists in devnet
// get detailed MerkleTree, which is located in `test/L2/data` and only being used by testing scripts
function getMerkleLeaves() internal view returns (MerkleLeaves memory) {
return abi.decode(MerkleLeavesJson.parseRaw("."), (MerkleLeaves));
}
Expand Down Expand Up @@ -117,8 +117,8 @@ contract L2ClaimTest is Test {
// read Pre-signed Signatures, Merkle Leaves and a Merkle Root in a json format from different files
string memory rootPath = string.concat(vm.projectRoot(), "/test/L2/data");
signatureJson = vm.readFile(string.concat(rootPath, "/signatures.json"));
MerkleLeavesJson = vm.readFile(string.concat(rootPath, "/merkleLeaves.json"));
MerkleRootJson = vm.readFile(string.concat(rootPath, "/merkleRoot.json"));
MerkleLeavesJson = vm.readFile(string.concat(rootPath, "/merkle-leaves.json"));
MerkleRootJson = vm.readFile(string.concat(rootPath, "/merkle-root.json"));

// get MerkleRoot struct
Utils.MerkleRoot memory merkleRoot = getMerkleRoot();
Expand Down
Loading

0 comments on commit e39b665

Please sign in to comment.