Skip to content

Commit

Permalink
feat: update universal router to use create3 deployment (#24)
Browse files Browse the repository at this point in the history
* forge install: pancake-create3-factory

* feat: deploy on eth sepolia and bsc testnet

* feat: updated remapping as auto_detect_remappings = false

* include acceptOwnership note
  • Loading branch information
ChefMist authored Nov 2, 2024
1 parent 0ffaeda commit 799d077
Show file tree
Hide file tree
Showing 20 changed files with 158 additions and 32 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@
[submodule "lib/pancake-v4-periphery"]
path = lib/pancake-v4-periphery
url = https://github.com/pancakeswap/pancake-v4-periphery
[submodule "lib/pancake-create3-factory"]
path = lib/pancake-create3-factory
url = https://github.com/pancakeswap/pancake-create3-factory
36 changes: 28 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
1. Install dependencies with `forge install`

2. Grab a RPC (eg. from nodereal) with history
```
```bash
// testnet fork test for v4, mainnet fork test for v2/v3
export FORK_URL=https://bsc-mainnet.nodereal.io/v1/xxx
export TESTNET_FORK_URL=https://bsc-testnet.nodereal.io/v1/xxx
Expand All @@ -21,21 +21,41 @@ export TESTNET_FORK_URL=https://bsc-testnet.nodereal.io/v1/xxx

Ensure `script/deployParameters/Deploy{chain}.s.sol` is updated

```
```bash
// set rpc url
export RPC_URL=https://

// private key need to be prefixed with 0x
export PRIVATE_KEY=0x

// optional. Only set if you want to verify contract on explorer
export ETHERSCAN_API_KEY=xx
// remove --verify flag if etherscan_api_key is not set
// replace with the respective chain eg. DeployArbitrum.s.sol:DeployArbitrum
forge script script/deployParameters/DeployArbitrum.s.sol:DeployArbitrum -vvv \
--rpc-url $RPC_URL \
--broadcast \
--slow \
--verify
--slow
```

Remember to call `.acceptOwnership()` to be the owner of universal router

## Verifying

Each script includes a verification command. Verification needs to be performed separately since the contract is deployed using the create3 method.

```bash
export ETHERSCAN_API_KEY=xx

forge verify-contract <address> UniversalRouter --watch --chain 97 --constructor-args-path example_args.txt
```

The file `example_args.txt` contains all the parameters specified in RouterParams.

Example
```solidity
params = RouterParameters({
permit2: 0x31c2F6fcFf4F8759b3Bd5Bf0e1084A055615c768,
weth9: 0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd,
...
})
```

then `example_args.txt` would be (0x31c2F6fcFf4F8759b3Bd5Bf0e1084A055615c768, 0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd,...)
2 changes: 1 addition & 1 deletion deploy-addresses/bsc-testnet.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"UniversalRouter": "0x7a7F562aCEb6e6352ee2FF93aeFb1051d4A1511A",
"UniversalRouter": "0x19Dbcfc815166633A393953B91115BF6D20E5c5a",
"UnsupportedProtocol": "0xe4da88F38C11C1450c720b8aDeDd94956610a4e5"
}
2 changes: 1 addition & 1 deletion deploy-addresses/ethereum-sepolia.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"UniversalRouter": "0x6A3d58cc25a92d90Fa3a8f0f5d5e75AD01ccd7a6",
"UniversalRouter": "0x19Dbcfc815166633A393953B91115BF6D20E5c5a",
"UnsupportedProtocol": "0x6879F5C1AdaDDF29892bf650F9C48350C12795D9"
}
1 change: 1 addition & 0 deletions example_args.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(0x31c2F6fcFf4F8759b3Bd5Bf0e1084A055615c768,0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd,0x6725F303b657a9451d8BA641348b6761A6CC7a17,0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865,0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9,0xd0d4c4cd0848c93cb4fd1f498d7013ee6bfb25783ea21593d5834f5d250ece66,0x6ce8eb472fa82df5469c6ab6d485f17c3ad13c8cd7af59b3d4a8026c5ce0f7e2,0xe6A00f8b819244e8Ab9Ea930e46449C2F20B6609,0x0A548d59D04096Bc01206D58C3D63c478e1e06dB,0x0a125Bb36e409957Ed951eF1FBe20e81D682EAb6,0x26Ca53c8C5CE90E22aA1FadDA68AB9a08f7BA06f,0x1DF0be383e9d17DA4448E57712849aBE5b3Fa33b,0x427bF5b37357632377eCbEC9de3626C71A5396c1,0x095bd2cf90ef113aa8c53904cE54C17f4583046d,0x26008c91a2D47147d6739db3fFd3598A27da859d)
3 changes: 3 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,7 @@ fs_permissions = [
solc_version = '0.8.26'
evm_version = "cancun"

# ref: https://github.com/foundry-rs/foundry/issues/4060 otherwise verification will have an issue
auto_detect_remappings = false

# See more config options https://github.com/foundry-rs/foundry/tree/master/config
1 change: 1 addition & 0 deletions lib/pancake-create3-factory
3 changes: 3 additions & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
forge-std/=lib/forge-std/src/
ds-test/=lib/forge-std/lib/ds-test/src/
@openzeppelin/=lib/pancake-v4-periphery/lib/pancake-v4-core/lib/openzeppelin-contracts/
openzeppelin-contracts/=lib/pancake-v4-periphery/lib/pancake-v4-core/lib/openzeppelin-contracts/
solmate/=lib/pancake-v4-periphery/lib/pancake-v4-core/lib/solmate/
pancake-v4-periphery/=lib/pancake-v4-periphery/
pancake-v4-core/=lib/pancake-v4-periphery/lib/pancake-v4-core
permit2/=lib/pancake-v4-periphery/lib/permit2/
pancake-create3-factory/=lib/pancake-create3-factory/
forge-gas-snapshot/=lib/forge-gas-snapshot/src/
32 changes: 27 additions & 5 deletions script/DeployUniversalRouter.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import "forge-std/Script.sol";
import {RouterParameters} from "../src/base/RouterImmutables.sol";
import {UnsupportedProtocol} from "../src/deploy/UnsupportedProtocol.sol";
import {UniversalRouter} from "../src/UniversalRouter.sol";

bytes32 constant SALT = bytes32(uint256(0x00000000000000000000000000000000000000005eb67581652632000a6cbedf));
import {Create3Factory} from "pancake-create3-factory/src/Create3Factory.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

abstract contract DeployUniversalRouter is Script {
RouterParameters internal params;
address internal unsupported;
address internal create3Factory; // from https://github.com/pancakeswap/pancake-create3-factory

address constant UNSUPPORTED_PROTOCOL = address(0);
bytes32 constant BYTES32_ZERO = bytes32(0);
Expand All @@ -21,7 +22,15 @@ abstract contract DeployUniversalRouter is Script {
// set values for params and unsupported
function setUp() public virtual;

function run() external returns (UniversalRouter router) {
/// @notice must be implemented by the inheriting contract to make sure eth deployment salt is unique
/// since the deployment salt will be the only factor to decide the address of the newly deployed contract
function getDeploymentSalt() public view virtual returns (bytes32);

function run() external returns (address router) {
/// @dev address from https://github.com/pancakeswap/pancake-create3-factory
Create3Factory factory = Create3Factory(0x38Ab3f2CE00973A51d3A2A04d634C9bcbf20e4e1);

// deployer will the the initial owner of universal router
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);

Expand Down Expand Up @@ -53,8 +62,21 @@ abstract contract DeployUniversalRouter is Script {

logParams();

router = new UniversalRouter(params);
console2.log("Universal Router Deployed:", address(router));
/// Prepare the payload to transfer ownership to deployer.
/// @dev deployer must call acceptOwnership after to be the owner
address owner = vm.addr(deployerPrivateKey);
console.log("universal router owner:", owner);

bytes memory afterDeploymentExecutionPayload = abi.encodeWithSelector(Ownable.transferOwnership.selector, owner);

bytes memory creationCode = abi.encodePacked(type(UniversalRouter).creationCode, abi.encode(params));

router = factory.deploy(
getDeploymentSalt(), creationCode, keccak256(creationCode), 0, afterDeploymentExecutionPayload, 0
);

console.log("UniversalRouter contract deployed at ", router);

vm.stopBroadcast();
}

Expand Down
12 changes: 10 additions & 2 deletions script/deployParameters/mainnet/DeployArbitrum.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,21 @@ import {DeployUniversalRouter} from "../../DeployUniversalRouter.s.sol";
import {RouterParameters} from "../../../src/base/RouterImmutables.sol";

/**
* Step 1: Deploy
* forge script script/deployParameters/mainnet/DeployArbitrum.s.sol:DeployArbitrum -vvv \
* --rpc-url $RPC_URL \
* --broadcast \
* --slow \
* --verify
* --slow
*
* Step 2: Verify - example_args.txt is the constructor arguments in the form of (args1, args2, args)
* forge verify-contract <address> UniversalRouter --watch --chain 42161 --constructor-args-path example_args.txt
*/
contract DeployArbitrum is DeployUniversalRouter {
/// @notice contract address will be based on deployment salt
function getDeploymentSalt() public pure override returns (bytes32) {
return keccak256("PANCAKE-V4-UNIVERSAL-ROUTER/UniversalRouter/0.0001");
}

function setUp() public override {
params = RouterParameters({
permit2: 0x31c2F6fcFf4F8759b3Bd5Bf0e1084A055615c768,
Expand Down
12 changes: 10 additions & 2 deletions script/deployParameters/mainnet/DeployBase.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,21 @@ import {DeployUniversalRouter} from "../../DeployUniversalRouter.s.sol";
import {RouterParameters} from "../../../src/base/RouterImmutables.sol";

/**
* Step 1: Deploy
* forge script script/deployParameters/mainnet/DeployBase.s.sol:DeployBase -vvv \
* --rpc-url $RPC_URL \
* --broadcast \
* --slow \
* --verify
* --slow
*
* Step 2: Verify - example_args.txt is the constructor arguments in the form of (args1, args2, args)
* forge verify-contract <address> UniversalRouter --watch --chain 8453 --constructor-args-path example_args.txt
*/
contract DeployBase is DeployUniversalRouter {
/// @notice contract address will be based on deployment salt
function getDeploymentSalt() public pure override returns (bytes32) {
return keccak256("PANCAKE-V4-UNIVERSAL-ROUTER/UniversalRouter/0.0001");
}

function setUp() public override {
params = RouterParameters({
permit2: 0x31c2F6fcFf4F8759b3Bd5Bf0e1084A055615c768,
Expand Down
12 changes: 10 additions & 2 deletions script/deployParameters/mainnet/DeployBsc.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,21 @@ import {DeployUniversalRouter} from "../../DeployUniversalRouter.s.sol";
import {RouterParameters} from "../../../src/base/RouterImmutables.sol";

/**
* Step 1: Deploy
* forge script script/deployParameters/mainnet/DeployBsc.s.sol:DeployBsc -vvv \
* --rpc-url $RPC_URL \
* --broadcast \
* --slow \
* --verify
* --slow
*
* Step 2: Verify - example_args.txt is the constructor arguments in the form of (args1, args2, args)
* forge verify-contract <address> UniversalRouter --watch --chain 56 --constructor-args-path example_args.txt
*/
contract DeployBsc is DeployUniversalRouter {
/// @notice contract address will be based on deployment salt
function getDeploymentSalt() public pure override returns (bytes32) {
return keccak256("PANCAKE-V4-UNIVERSAL-ROUTER/UniversalRouter/0.0001");
}

function setUp() public override {
params = RouterParameters({
permit2: 0x31c2F6fcFf4F8759b3Bd5Bf0e1084A055615c768,
Expand Down
12 changes: 10 additions & 2 deletions script/deployParameters/mainnet/DeployEth.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,21 @@ import {DeployUniversalRouter} from "../../DeployUniversalRouter.s.sol";
import {RouterParameters} from "../../../src/base/RouterImmutables.sol";

/**
* Step 1: Deploy
* forge script script/deployParameters/mainnet/DeployEth.s.sol:DeployEth -vvv \
* --rpc-url $RPC_URL \
* --broadcast \
* --slow \
* --verify
* --slow
*
* Step 2: Verify - example_args.txt is the constructor arguments in the form of (args1, args2, args)
* forge verify-contract <address> UniversalRouter --watch --chain 1 --constructor-args-path example_args.txt
*/
contract DeployEth is DeployUniversalRouter {
/// @notice contract address will be based on deployment salt
function getDeploymentSalt() public pure override returns (bytes32) {
return keccak256("PANCAKE-V4-UNIVERSAL-ROUTER/UniversalRouter/0.0001");
}

function setUp() public override {
params = RouterParameters({
permit2: 0x31c2F6fcFf4F8759b3Bd5Bf0e1084A055615c768,
Expand Down
12 changes: 10 additions & 2 deletions script/deployParameters/mainnet/DeployLinea.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,21 @@ import {DeployUniversalRouter} from "../../DeployUniversalRouter.s.sol";
import {RouterParameters} from "../../../src/base/RouterImmutables.sol";

/**
* Step 1: Deploy
* forge script script/deployParameters/mainnet/DeployLinea.s.sol:DeployLinea -vvv \
* --rpc-url $RPC_URL \
* --broadcast \
* --slow \
* --verify
* --slow
*
* Step 2: Verify - example_args.txt is the constructor arguments in the form of (args1, args2, args)
* forge verify-contract <address> UniversalRouter --watch --chain 59144 --constructor-args-path example_args.txt
*/
contract DeployLinea is DeployUniversalRouter {
/// @notice contract address will be based on deployment salt
function getDeploymentSalt() public pure override returns (bytes32) {
return keccak256("PANCAKE-V4-UNIVERSAL-ROUTER/UniversalRouter/0.0001");
}

function setUp() public override {
params = RouterParameters({
permit2: 0x31c2F6fcFf4F8759b3Bd5Bf0e1084A055615c768,
Expand Down
9 changes: 8 additions & 1 deletion script/deployParameters/mainnet/DeployOpBnb.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,20 @@ import {DeployUniversalRouter} from "../../DeployUniversalRouter.s.sol";
import {RouterParameters} from "../../../src/base/RouterImmutables.sol";

/**
* Note: As of now, opbnb does not support verify via foundry, so skipping --verify flag
* Step 1: Deploy
* forge script script/deployParameters/mainnet/DeployOpBnb.s.sol:DeployOpbnb -vvv \
* --rpc-url $RPC_URL \
* --broadcast \
* --slow
*
* Step 2: Verify. As of now, opbnb does not support verify via foundry, so skipping verify
*/
contract DeployOpbnb is DeployUniversalRouter {
/// @notice contract address will be based on deployment salt
function getDeploymentSalt() public pure override returns (bytes32) {
return keccak256("PANCAKE-V4-UNIVERSAL-ROUTER/UniversalRouter/0.0001");
}

function setUp() public override {
params = RouterParameters({
permit2: 0x31c2F6fcFf4F8759b3Bd5Bf0e1084A055615c768,
Expand Down
12 changes: 10 additions & 2 deletions script/deployParameters/mainnet/DeployzkEvm.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,21 @@ import {DeployUniversalRouter} from "../../DeployUniversalRouter.s.sol";
import {RouterParameters} from "../../../src/base/RouterImmutables.sol";

/**
* Step 1: Deploy
* forge script script/deployParameters/mainnet/DeployzkEvm.s.sol:DeployZkEvm -vvv \
* --rpc-url $RPC_URL \
* --broadcast \
* --slow \
* --verify
* --slow
*
* Step 2: Verify - example_args.txt is the constructor arguments in the form of (args1, args2, args)
* forge verify-contract <address> UniversalRouter --watch --chain 1101 --constructor-args-path example_args.txt
*/
contract DeployZkEvm is DeployUniversalRouter {
/// @notice contract address will be based on deployment salt
function getDeploymentSalt() public pure override returns (bytes32) {
return keccak256("PANCAKE-V4-UNIVERSAL-ROUTER/UniversalRouter/0.0001");
}

function setUp() public override {
params = RouterParameters({
permit2: 0x31c2F6fcFf4F8759b3Bd5Bf0e1084A055615c768,
Expand Down
12 changes: 10 additions & 2 deletions script/deployParameters/testnet/DeployBscTestnet.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,21 @@ import {DeployUniversalRouter} from "../../DeployUniversalRouter.s.sol";
import {RouterParameters} from "../../../src/base/RouterImmutables.sol";

/**
* Step 1: Deploy
* forge script script/deployParameters/testnet/DeployBscTestnet.s.sol:DeployBscTestnet -vvv \
* --rpc-url $RPC_URL \
* --broadcast \
* --slow \
* --verify
* --slow
*
* Step 2: Verify - example_args.txt is the constructor arguments in the form of (args1, args2, args)
* forge verify-contract <address> UniversalRouter --watch --chain 97 --constructor-args-path ./script/deployParameters/testnet/args/bsc_testnet.txt
*/
contract DeployBscTestnet is DeployUniversalRouter {
/// @notice contract address will be based on deployment salt
function getDeploymentSalt() public pure override returns (bytes32) {
return keccak256("PANCAKE-V4-UNIVERSAL-ROUTER/UniversalRouter/0.0001");
}

// ref from v3 universal router: https://testnet.bscscan.com/tx/0xdfab014e4f5df56d5a8b16375028ad0340f80070bd848eb57c4e0baf41210487
function setUp() public override {
params = RouterParameters({
Expand Down
12 changes: 10 additions & 2 deletions script/deployParameters/testnet/DeployEthSepolia.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,21 @@ import {DeployUniversalRouter} from "../../DeployUniversalRouter.s.sol";
import {RouterParameters} from "../../../src/base/RouterImmutables.sol";

/**
* Step 1: Deploy
* forge script script/deployParameters/testnet/DeployEthSepolia.s.sol:DeployEthSepolia -vvv \
* --rpc-url $RPC_URL \
* --broadcast \
* --slow \
* --verify
* --slow
*
* Step 2: Verify - example_args.txt is the constructor arguments in the form of (args1, args2, args)
* forge verify-contract <address> UniversalRouter --watch --chain 11155111 --constructor-args-path ./script/deployParameters/testnet/args/eth_sepolia.txt
*/
contract DeployEthSepolia is DeployUniversalRouter {
/// @notice contract address will be based on deployment salt
function getDeploymentSalt() public pure override returns (bytes32) {
return keccak256("PANCAKE-V4-UNIVERSAL-ROUTER/UniversalRouter/0.0001");
}

// ref from v3 universal router: https://sepolia.etherscan.io/tx/0xb4610521d3fc61f4837edbd899acb6c33a5fe0f3bb32ab84745ac0a8b1859906
// and from pancake-frontend config
function setUp() public override {
Expand Down
1 change: 1 addition & 0 deletions script/deployParameters/testnet/args/bsc_testnet.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(0x31c2F6fcFf4F8759b3Bd5Bf0e1084A055615c768,0xae13d989daC2f0dEbFf460aC112a837C89BAa7cd,0x6725F303b657a9451d8BA641348b6761A6CC7a17,0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865,0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9,0xd0d4c4cd0848c93cb4fd1f498d7013ee6bfb25783ea21593d5834f5d250ece66,0x6ce8eb472fa82df5469c6ab6d485f17c3ad13c8cd7af59b3d4a8026c5ce0f7e2,0xe6A00f8b819244e8Ab9Ea930e46449C2F20B6609,0x0A548d59D04096Bc01206D58C3D63c478e1e06dB,0x0a125Bb36e409957Ed951eF1FBe20e81D682EAb6,0x26Ca53c8C5CE90E22aA1FadDA68AB9a08f7BA06f,0x1DF0be383e9d17DA4448E57712849aBE5b3Fa33b,0x427bF5b37357632377eCbEC9de3626C71A5396c1,0x095bd2cf90ef113aa8c53904cE54C17f4583046d,0x26008c91a2D47147d6739db3fFd3598A27da859d)
1 change: 1 addition & 0 deletions script/deployParameters/testnet/args/eth_sepolia.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(0x31c2F6fcFf4F8759b3Bd5Bf0e1084A055615c768,0xfFf9976782d46CC05630D1f6eBAb18b2324d6B14,0x1bdc540dEB9Ed1fA29964DeEcCc524A8f5e2198e,0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865,0x41ff9AA7e16B8B1a8a8dc4f0eFacd93D02d071c9,0xd0d4c4cd0848c93cb4fd1f498d7013ee6bfb25783ea21593d5834f5d250ece66,0x6ce8eb472fa82df5469c6ab6d485f17c3ad13c8cd7af59b3d4a8026c5ce0f7e2,0x6879F5C1AdaDDF29892bf650F9C48350C12795D9,0x6879F5C1AdaDDF29892bf650F9C48350C12795D9,0x4670F769Daa625FF5F89719AE5295E9824f5805f,0xD4EAc75ee0E76EAD6AC6995DF30CA14b38549682,0x0Ca8430E263A098B998E47e0544C2C82B30CbDB1,0x46A15B0b27311cedF172AB29E4f4766fbE7F4364,0x53C9802F47295979c0E154779eD10fa6af27D7cA,0x21015eF9927e06b7Fc19D986A214e449Aa22FF7d)

0 comments on commit 799d077

Please sign in to comment.