Skip to content

Commit

Permalink
Address for Lisk L2 token is unified across different networks (#17)
Browse files Browse the repository at this point in the history
* Lisk L2 token address is unified across different networks

* Add unit tests for unified L2 token address
  • Loading branch information
matjazv authored Dec 29, 2023
1 parent f7c8b10 commit 84764c8
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 2 deletions.
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Deployer private key
PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

# Salt for deterministic Lisk L2 token address generation
L2_TOKEN_SALT="test_l2_token_salt"

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

Expand Down
17 changes: 15 additions & 2 deletions script/L2LiskToken.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity 0.8.21;

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

Expand Down Expand Up @@ -29,12 +30,24 @@ contract L2LiskTokenScript is Script {
Utils.L1AddressesConfig memory l1AddressesConfig = utils.readL1AddressesFile();
console2.log("Simulation: L1 Lisk token address: %s", l1AddressesConfig.L1LiskToken);

// get salt for L2LiskToken contract
bytes32 salt = keccak256(bytes(vm.envString("L2_TOKEN_SALT")));
console2.log("Simulation: L2 Lisk token address salt: %s", vm.envString("L2_TOKEN_SALT"));

// calculate L2LiskToken contract address
address l2LiskTokenAddressCalculated = computeCreate2Address(
salt,
hashInitCode(type(L2LiskToken).creationCode, abi.encode(L2_STANDARD_BRIDGE, l1AddressesConfig.L1LiskToken)),
CREATE2_FACTORY
);
console2.log("Simulation: Calculated L2 Lisk token address: %s", l2LiskTokenAddressCalculated);

// deploy L2LiskToken contract
vm.startBroadcast(deployerPrivateKey);
L2LiskToken l2LiskToken = new L2LiskToken(L2_STANDARD_BRIDGE, l1AddressesConfig.L1LiskToken);
L2LiskToken l2LiskToken = new L2LiskToken{ salt: salt }(L2_STANDARD_BRIDGE, l1AddressesConfig.L1LiskToken);
vm.stopBroadcast();

assert(address(l2LiskToken) != address(0));
assert(address(l2LiskToken) == l2LiskTokenAddressCalculated);
assert(keccak256(bytes(l2LiskToken.name())) == keccak256(bytes("Lisk")));
assert(keccak256(bytes(l2LiskToken.symbol())) == keccak256(bytes("LSK")));
assert(l2LiskToken.decimals() == 18);
Expand Down
47 changes: 47 additions & 0 deletions test/L2/L2LiskToken.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ contract L2LiskTokenTest is Test {
address public alice;
address public bob;

// salt for testing unified address
bytes32 public salt;

function setUp() public {
bridge = vm.addr(1);
remoteToken = vm.addr(2);
Expand All @@ -26,6 +29,8 @@ contract L2LiskTokenTest is Test {

(alice, alicePrivateKey) = makeAddrAndKey("alice");
(bob, bobPrivateKey) = makeAddrAndKey("bob");

salt = keccak256(bytes("test_salt"));
}

function test_Initialize() public {
Expand All @@ -43,6 +48,48 @@ contract L2LiskTokenTest is Test {
assertEq(l2LiskToken.supportsInterface(type(IOptimismMintableERC20).interfaceId), true);
}

function test_UnifiedTokenAddress() public {
// calculate L2LiskToken contract address
address l2LiskTokenAddressCalculated = computeCreate2Address(
salt, hashInitCode(type(L2LiskToken).creationCode, abi.encode(bridge, remoteToken)), alice
);

// use the same salt and the same deployer as in calculated address of L2LiskToken contract
vm.prank(alice);
L2LiskToken l2LiskTokenSalted = new L2LiskToken{ salt: salt }(bridge, remoteToken);

// check that both token contracts and the calculated address have the same address
assertEq(address(l2LiskTokenSalted), l2LiskTokenAddressCalculated);
}

function test_UnifiedTokenAddressFail_DifferentDeployers() public {
// calculate L2LiskToken contract address
address l2LiskTokenAddressCalculated = computeCreate2Address(
salt, hashInitCode(type(L2LiskToken).creationCode, abi.encode(bridge, remoteToken)), alice
);

// use the same salt but different deployer as in calculated address of L2LiskToken contract
vm.prank(bob);
L2LiskToken l2LiskTokenSalted = new L2LiskToken{ salt: salt }(bridge, remoteToken);

// check that token contracts and the calculated address have different addresses
assertNotEq(address(l2LiskTokenSalted), l2LiskTokenAddressCalculated);
}

function test_UnifiedTokenAddressFail_DifferentSalt() public {
// calculate L2LiskToken contract address
address l2LiskTokenAddressCalculated = computeCreate2Address(
salt, hashInitCode(type(L2LiskToken).creationCode, abi.encode(bridge, remoteToken)), alice
);

// use different salt but the same deployer as in calculated address of L2LiskToken contract
vm.prank(alice);
L2LiskToken l2LiskTokenSalted = new L2LiskToken{ salt: keccak256(bytes("different_salt")) }(bridge, remoteToken);

// check that token contracts and the calculated address have different addresses
assertNotEq(address(l2LiskTokenSalted), l2LiskTokenAddressCalculated);
}

function test_GetBridge() public {
assertEq(l2LiskToken.bridge(), bridge);
assertEq(l2LiskToken.BRIDGE(), bridge);
Expand Down

0 comments on commit 84764c8

Please sign in to comment.