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

Deterministic Deploys #60

Merged
merged 14 commits into from
Apr 30, 2023
4 changes: 3 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
RPC_URL=
CENTRIFUGE_CHAIN_ORIGIN=0x7369626cef070000000000000000000000000000
MAINNET_RPC_URL=https://mainnet.infura.io/v3/...
POLYGON_RPC_URL=https://polygon-mainnet.infura.io/v3/...
PRIVATE_KEY=
ETHERSCAN_KEY=
5 changes: 4 additions & 1 deletion .github/workflows/pull-request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ jobs:
id: test

env:
FOUNDRY_PROFILE: pull_request
FOUNDRY_PROFILE: push_to_main
CENTRIFUGE_CHAIN_ORIGIN: ${{ secrets.CENTRIFUGE_CHAIN_ORIGIN }}
MAINNET_RPC_URL: ${{ secrets.MAINNET_RPC_URL }}
POLYGON_RPC_URL: ${{ secrets.POLYGON_RPC_URL }}

lint:
name: fmt
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/push-to-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,7 @@ jobs:
forge test -vvv
id: test
env:
FOUNDRY_PROFILE: push_to_main
FOUNDRY_PROFILE: push_to_main
CENTRIFUGE_CHAIN_ORIGIN: ${{ secrets.CENTRIFUGE_CHAIN_ORIGIN }}
MAINNET_RPC_URL: ${{ secrets.MAINNET_RPC_URL }}
POLYGON_RPC_URL: ${{ secrets.POLYGON_RPC_URL }}
48 changes: 0 additions & 48 deletions test/token/factory.sol

This file was deleted.

153 changes: 153 additions & 0 deletions test/token/factory.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.18;
pragma abicoder v2;

import {TrancheTokenFactory, MemberlistFactory} from "src/token/factory.sol";
import {RestrictedToken} from "src/token/restricted.sol";
import "forge-std/Test.sol";

contract FactoryTest is Test {
// address(0)[0:20] + keccak("Centrifuge")[21:32]
bytes32 SALT = 0x000000000000000000000000000000000000000075eb27011b69f002dc094d05;
uint256 mainnetFork;
uint256 polygonFork;

function setUp() public {
mainnetFork = vm.createFork(vm.envString("MAINNET_RPC_URL"));
polygonFork = vm.createFork(vm.envString("POLYGON_RPC_URL"));
}

function testTokenFactoryIsDeterministicAcrossChains(
address sender,
string memory name,
string memory symbol,
uint64 poolId,
bytes16 trancheId,
uint8 decimals
) public {
vm.selectFork(mainnetFork);
TrancheTokenFactory tokenFactory1 = new TrancheTokenFactory{ salt: SALT }();
address token1 = tokenFactory1.newTrancheToken(poolId, trancheId, name, symbol, decimals);

vm.selectFork(polygonFork);
TrancheTokenFactory tokenFactory2 = new TrancheTokenFactory{ salt: SALT }();
assertEq(address(tokenFactory1), address(tokenFactory2));
vm.prank(sender);
hieronx marked this conversation as resolved.
Show resolved Hide resolved
address token2 = tokenFactory2.newTrancheToken(poolId, trancheId, name, symbol, decimals);
assertEq(address(token1), address(token2));
}

function testTokenFactoryShouldBeDeterministic() public {
address predictedAddress = address(
uint160(
uint256(
keccak256(
abi.encodePacked(
bytes1(0xff),
address(this),
SALT,
keccak256(abi.encodePacked(type(TrancheTokenFactory).creationCode))
)
)
)
)
);
TrancheTokenFactory tokenFactory = new TrancheTokenFactory{ salt: SALT }();
assertEq(address(tokenFactory), predictedAddress);
}

function testTrancheTokenShouldBeDeterministic(
uint64 poolId,
bytes16 trancheId,
string memory name,
string memory symbol,
uint8 decimals
) public {
TrancheTokenFactory tokenFactory = new TrancheTokenFactory{ salt: SALT }();

bytes32 salt = keccak256(abi.encodePacked(poolId, trancheId));
address predictedAddress = address(
uint160(
uint256(
keccak256(
abi.encodePacked(
bytes1(0xff),
address(tokenFactory),
salt,
keccak256(abi.encodePacked(type(RestrictedToken).creationCode, abi.encode(decimals)))
)
)
)
)
);

address token = tokenFactory.newTrancheToken(poolId, trancheId, name, symbol, decimals);

assertEq(address(token), predictedAddress);
}

function testDeployingDeterministicAddressTwiceReverts(
uint64 poolId,
bytes16 trancheId,
string memory name,
string memory symbol,
uint8 decimals
) public {
address predictedAddress = address(
uint160(
uint256(
keccak256(
abi.encodePacked(
bytes1(0xff),
address(this),
SALT,
keccak256(abi.encodePacked(type(TrancheTokenFactory).creationCode))
)
)
)
)
);
TrancheTokenFactory tokenFactory = new TrancheTokenFactory{ salt: SALT }();
assertEq(address(tokenFactory), predictedAddress);
address token1 = tokenFactory.newTrancheToken(poolId, trancheId, name, symbol, decimals);
vm.expectRevert();
address token2 = tokenFactory.newTrancheToken(poolId, trancheId, name, symbol, decimals);
}

function testMemberlistFactoryIsDeterministicAcrossChains(
address sender,
uint64 poolId,
bytes16 trancheId,
uint256 threshold
) public {
vm.selectFork(mainnetFork);
MemberlistFactory memberlistFactory1 = new MemberlistFactory{ salt: SALT }();
address memberlist1 = memberlistFactory1.newMemberlist();

vm.selectFork(polygonFork);
MemberlistFactory memberlistFactory2 = new MemberlistFactory{ salt: SALT }();
assertEq(address(memberlistFactory1), address(memberlistFactory2));
vm.prank(sender);
hieronx marked this conversation as resolved.
Show resolved Hide resolved
address memberlist2 = memberlistFactory2.newMemberlist();
assertEq(address(memberlist1), address(memberlist2));
}

function testMemberlistShouldBeDeterministic() public {
address predictedAddress = address(
uint160(
uint256(
keccak256(
abi.encodePacked(
bytes1(0xff),
address(this),
SALT,
keccak256(abi.encodePacked(type(MemberlistFactory).creationCode))
)
)
)
)
);
MemberlistFactory memberlistFactory = new MemberlistFactory{ salt: SALT }();
assertEq(address(memberlistFactory), predictedAddress);
}
}
File renamed without changes.