Merge pull request #81 from propeller-heads/router/dc/ENG-4101-deployment-script
feat: Add deployment scripts
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"ethereum": {
|
"ethereum": {
|
||||||
"uniswap_v2": "0x5C2F5a71f67c01775180ADc06909288B4C329308",
|
"uniswap_v2": "0x00C1b81e3C8f6347E69e2DDb90454798A6Be975E",
|
||||||
"uniswap_v3": "0x5C2F5a71f67c01775180ADc06909288B4C329308",
|
"uniswap_v3": "0x5C2F5a71f67c01775180ADc06909288B4C329308",
|
||||||
"uniswap_v4": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a",
|
"uniswap_v4": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a",
|
||||||
"vm:balancer_v2": "0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"
|
"vm:balancer_v2": "0x543778987b293C7E8Cf0722BB2e935ba6f4068D4"
|
||||||
@@ -15,7 +15,7 @@ libs = ['lib']
|
|||||||
auto_detect_sol = true
|
auto_detect_sol = true
|
||||||
evm_version = 'cancun'
|
evm_version = 'cancun'
|
||||||
optimizer = true
|
optimizer = true
|
||||||
optimizer_runs = 44444444
|
optimizer_runs = 1000
|
||||||
via_ir = true
|
via_ir = true
|
||||||
|
|
||||||
[rpc_endpoints]
|
[rpc_endpoints]
|
||||||
|
|||||||
35
foundry/hardhat.config.js
Normal file
35
foundry/hardhat.config.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/** @type import('hardhat/config').HardhatUserConfig */
|
||||||
|
require("@tenderly/hardhat-tenderly");
|
||||||
|
require("@nomiclabs/hardhat-ethers");
|
||||||
|
require("@nomicfoundation/hardhat-foundry");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
solidity: {
|
||||||
|
version: "0.8.26",
|
||||||
|
settings: {
|
||||||
|
evmVersion: "cancun",
|
||||||
|
viaIR: true,
|
||||||
|
optimizer: {
|
||||||
|
enabled: true,
|
||||||
|
runs: 1000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
networks: {
|
||||||
|
tenderly: {
|
||||||
|
url: process.env.RPC_URL,
|
||||||
|
accounts: [process.env.PRIVATE_KEY]
|
||||||
|
},
|
||||||
|
mainnet: {
|
||||||
|
url: process.env.RPC_URL,
|
||||||
|
accounts: [process.env.PRIVATE_KEY]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
tenderly: {
|
||||||
|
project: "project",
|
||||||
|
username: "tvinagre",
|
||||||
|
privateVerification: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
7869
foundry/package-lock.json
generated
Normal file
7869
foundry/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
14
foundry/package.json
Normal file
14
foundry/package.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "hardhat-project",
|
||||||
|
"devDependencies": {
|
||||||
|
"@nomiclabs/hardhat-ethers": "^2.2.3",
|
||||||
|
"@tenderly/hardhat-tenderly": "^2.5.2",
|
||||||
|
"dotenv": "^16.4.7",
|
||||||
|
"hardhat": "^2.22.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nomicfoundation/hardhat-foundry": "^1.1.3",
|
||||||
|
"ethers": "^5.0.0",
|
||||||
|
"prompt-sync": "^4.2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
47
foundry/scripts/README.md
Normal file
47
foundry/scripts/README.md
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# How to deploy
|
||||||
|
|
||||||
|
- Install dependencies `npm install`
|
||||||
|
- `cd foundry`
|
||||||
|
|
||||||
|
## Deploy on a Tenderly fork
|
||||||
|
|
||||||
|
1. Make a new [fork](https://dashboard.tenderly.co/) in tenderly dashboard.
|
||||||
|
2. Set the following environment variables:
|
||||||
|
|
||||||
|
```
|
||||||
|
export RPC_URL=<fork-rpc-from-tenderly>
|
||||||
|
export DEPLOY_WALLET=<wallet-address>
|
||||||
|
export PRIVATE_KEY=<private-key>
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Fund wallet: `npx hardhat run scripts/fund-wallet-tenderly-fork.js --network tenderly`
|
||||||
|
|
||||||
|
## Deploy on mainnet
|
||||||
|
|
||||||
|
1. Set the following environment variables:
|
||||||
|
|
||||||
|
```
|
||||||
|
export RPC_URL=<mainnet-rpc-url>
|
||||||
|
export DEPLOY_WALLET=<wallet-address>
|
||||||
|
export PRIVATE_KEY=<private-key>
|
||||||
|
```
|
||||||
|
|
||||||
|
Make sure to run `unset HISTFILE` in your terminal before setting the private key. This will prevent the private key
|
||||||
|
from being stored in the shell history.
|
||||||
|
|
||||||
|
## Deploy Tycho Router
|
||||||
|
|
||||||
|
1. Deploy router: `npx hardhat run scripts/deploy-router.js --network tenderly/mainnet`
|
||||||
|
2. Define the accounts to grant roles to in `scripts/roles.json`
|
||||||
|
3. Export the router address to the environment variable `export ROUTER=<router-address>`
|
||||||
|
4. Grant roles: `npx hardhat run scripts/set-roles.js --network tenderly/mainnet`
|
||||||
|
5. Set executors: `npx hardhat run scripts/set-executors.js --network tenderly/mainnet`. Make sure you change the
|
||||||
|
DEPLOY_WALLET
|
||||||
|
to the executor deployer wallet. If you need to deploy executors, follow the instructions below.
|
||||||
|
|
||||||
|
### Deploy executors
|
||||||
|
|
||||||
|
1. In `scripts/deploy-executors.js` define the executors to be deployed
|
||||||
|
2. Deploy executors: `npx hardhat run scripts/deploy-executors.js --network tenderly/mainnet`
|
||||||
|
3. Fill in the executor addresses in `config/executor_addresses.json`. Note that the naming there needs to match the one
|
||||||
|
from tycho-indexer.
|
||||||
45
foundry/scripts/deploy-executors.js
Normal file
45
foundry/scripts/deploy-executors.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
require('dotenv').config();
|
||||||
|
const {ethers} = require("hardhat");
|
||||||
|
const hre = require("hardhat");
|
||||||
|
|
||||||
|
// Comment out the executors you don't want to deploy
|
||||||
|
const executors_to_deploy = [
|
||||||
|
{exchange: "UniswapV2Executor", args: []},
|
||||||
|
// {exchange: "UniswapV3Executor", args: ["0x1F98431c8aD98523631AE4a59f267346ea31F984"]},
|
||||||
|
// {exchange: "UniswapV4Executor", args: ["0x000000000004444c5dc75cB358380D2e3dE08A90"]},
|
||||||
|
// {exchange: "BalancerV2Executor", args: []},
|
||||||
|
]
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const network = hre.network.name;
|
||||||
|
console.log(`Deploying executors to ${network}`);
|
||||||
|
|
||||||
|
const [deployer] = await ethers.getSigners();
|
||||||
|
console.log(`Deploying with account: ${deployer.address}`);
|
||||||
|
console.log(`Account balance: ${ethers.utils.formatEther(await deployer.getBalance())} ETH`);
|
||||||
|
|
||||||
|
for (const executor of executors_to_deploy) {
|
||||||
|
const {exchange, args} = executor;
|
||||||
|
const Executor = await ethers.getContractFactory(exchange);
|
||||||
|
const deployedExecutor = await Executor.deploy(...args);
|
||||||
|
await deployedExecutor.deployed();
|
||||||
|
console.log(`${exchange} deployed to: ${deployedExecutor.address}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await hre.tenderly.verify({
|
||||||
|
name: exchange,
|
||||||
|
address: deployedExecutor.address,
|
||||||
|
});
|
||||||
|
console.log("Contract verified successfully on Tenderly");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error during contract verification:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
.then(() => process.exit(0))
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("Deployment failed:", error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
41
foundry/scripts/deploy-router.js
Normal file
41
foundry/scripts/deploy-router.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
require('dotenv').config();
|
||||||
|
const {ethers} = require("hardhat");
|
||||||
|
const hre = require("hardhat");
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const network = hre.network.name;
|
||||||
|
const permit2 = "0x000000000022D473030F116dDEE9F6B43aC78BA3";
|
||||||
|
const weth = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2";
|
||||||
|
|
||||||
|
console.log(`Deploying TychoRouter to ${network} with:`);
|
||||||
|
console.log(`- permit2: ${permit2}`);
|
||||||
|
console.log(`- weth: ${weth}`);
|
||||||
|
|
||||||
|
const [deployer] = await ethers.getSigners();
|
||||||
|
console.log(`Deploying with account: ${deployer.address}`);
|
||||||
|
console.log(`Account balance: ${ethers.utils.formatEther(await deployer.getBalance())} ETH`);
|
||||||
|
|
||||||
|
const TychoRouter = await ethers.getContractFactory("TychoRouter");
|
||||||
|
const router = await TychoRouter.deploy(permit2, weth);
|
||||||
|
|
||||||
|
await router.deployed();
|
||||||
|
console.log(`TychoRouter deployed to: ${router.address}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log("Verifying contract on Tenderly...");
|
||||||
|
await hre.tenderly.verify({
|
||||||
|
name: "TychoRouter",
|
||||||
|
address: router.address,
|
||||||
|
});
|
||||||
|
console.log("Contract verified successfully on Tenderly");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error during contract verification:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
.then(() => process.exit(0))
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("Deployment failed:", error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
29
foundry/scripts/fund-wallet-tenderly-fork.js
Normal file
29
foundry/scripts/fund-wallet-tenderly-fork.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
require("dotenv").config();
|
||||||
|
const {ethers} = require("hardhat");
|
||||||
|
|
||||||
|
const TENDERLY_RPC_URL = process.env.TENDERLY_RPC_URL;
|
||||||
|
const DEPLOY_WALLET = process.env.DEPLOY_WALLET;
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
if (!TENDERLY_RPC_URL || !DEPLOY_WALLET) {
|
||||||
|
console.error("Missing TENDERLY_RPC_URL or DEPLOY_WALLET in environment variables.");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const provider = ethers.provider; // Use Hardhat's provider
|
||||||
|
const balanceHex = ethers.utils.hexValue(ethers.utils.parseUnits("10", "ether")); // Convert 10 ETH to hex
|
||||||
|
console.log(`Funding wallet ${DEPLOY_WALLET} with 10 ETH on Tenderly...`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await provider.send("tenderly_setBalance", [[DEPLOY_WALLET], balanceHex]);
|
||||||
|
console.log(`Successfully funded wallet: ${DEPLOY_WALLET}`);
|
||||||
|
console.log(result);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error funding wallet:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
15
foundry/scripts/roles.json
Normal file
15
foundry/scripts/roles.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"EXECUTOR_SETTER_ROLE": [
|
||||||
|
"0x58Dc7Bf9eD1f4890A7505D5bE4E4252978eAF655"
|
||||||
|
],
|
||||||
|
"FEE_SETTER_ROLE": [],
|
||||||
|
"PAUSER_ROLE": [
|
||||||
|
"0x58Dc7Bf9eD1f4890A7505D5bE4E4252978eAF655"
|
||||||
|
],
|
||||||
|
"UNPAUSER_ROLE": [
|
||||||
|
"0x58Dc7Bf9eD1f4890A7505D5bE4E4252978eAF655"
|
||||||
|
],
|
||||||
|
"FUND_RESCUER_ROLE": [
|
||||||
|
"0x58Dc7Bf9eD1f4890A7505D5bE4E4252978eAF655"
|
||||||
|
]
|
||||||
|
}
|
||||||
65
foundry/scripts/set-executors.js
Normal file
65
foundry/scripts/set-executors.js
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
require('dotenv').config();
|
||||||
|
const {ethers} = require("hardhat");
|
||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
const hre = require("hardhat");
|
||||||
|
const prompt = require('prompt-sync')();
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const network = hre.network.name;
|
||||||
|
const routerAddress = process.env.ROUTER_ADDRESS;
|
||||||
|
console.log(`Setting executors on TychoRouter at ${routerAddress} on ${network}`);
|
||||||
|
|
||||||
|
const [deployer] = await ethers.getSigners();
|
||||||
|
console.log(`Setting executors with account: ${deployer.address}`);
|
||||||
|
console.log(`Account balance: ${ethers.utils.formatEther(await deployer.getBalance())} ETH`);
|
||||||
|
|
||||||
|
const TychoRouter = await ethers.getContractFactory("TychoRouter");
|
||||||
|
const router = TychoRouter.attach(routerAddress);
|
||||||
|
|
||||||
|
const executorsFilePath = path.join(__dirname, "../../config/executor_addresses.json");
|
||||||
|
const executors = Object.entries(JSON.parse(fs.readFileSync(executorsFilePath, "utf8"))["ethereum"]);
|
||||||
|
|
||||||
|
|
||||||
|
// Filter out executors that are already set
|
||||||
|
const executorsToSet = [];
|
||||||
|
for (const [name, executor] of executors) {
|
||||||
|
const isExecutorSet = await router.executors(executor);
|
||||||
|
if (!isExecutorSet) {
|
||||||
|
executorsToSet.push({name: name, executor: executor});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (executorsToSet.length === 0) {
|
||||||
|
console.log("All executors are already set. No changes needed.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`The following ${executorsToSet.length} executor(s) will be set:`);
|
||||||
|
executorsToSet.forEach(executor => {
|
||||||
|
console.log(`Name: ${executor.name}`);
|
||||||
|
console.log(`Address: ${executor.executor}`);
|
||||||
|
console.log("———");
|
||||||
|
});
|
||||||
|
|
||||||
|
const userConfirmation = prompt("Do you want to proceed with setting these executors? (yes/no): ");
|
||||||
|
if (userConfirmation.toLowerCase() !== 'yes') {
|
||||||
|
console.log("Operation cancelled by user.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set executors
|
||||||
|
const executorAddresses = executorsToSet.map(executor => executor.executor);
|
||||||
|
const tx = await router.setExecutors(executorAddresses, {
|
||||||
|
gasLimit: 100000
|
||||||
|
});
|
||||||
|
await tx.wait(); // Wait for the transaction to be mined
|
||||||
|
console.log(`Executors set at transaction: ${tx.hash}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
.then(() => process.exit(0))
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("Error setting executors:", error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
48
foundry/scripts/set-roles.js
Normal file
48
foundry/scripts/set-roles.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
require('dotenv').config();
|
||||||
|
const {ethers} = require("hardhat");
|
||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
const hre = require("hardhat");
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const network = hre.network.name;
|
||||||
|
const routerAddress = process.env.ROUTER_ADDRESS;
|
||||||
|
console.log(`Setting roles on TychoRouter at ${routerAddress} on ${network}`);
|
||||||
|
|
||||||
|
const [deployer] = await ethers.getSigners();
|
||||||
|
console.log(`Setting roles with account: ${deployer.address}`);
|
||||||
|
console.log(`Account balance: ${ethers.utils.formatEther(await deployer.getBalance())} ETH`);
|
||||||
|
const TychoRouter = await ethers.getContractFactory("TychoRouter");
|
||||||
|
const router = TychoRouter.attach(routerAddress);
|
||||||
|
|
||||||
|
const rolesFilePath = path.join(__dirname, "roles.json");
|
||||||
|
const rolesDict = JSON.parse(fs.readFileSync(rolesFilePath, "utf8"));
|
||||||
|
|
||||||
|
const roles = {
|
||||||
|
EXECUTOR_SETTER_ROLE: "0x6a1dd52dcad5bd732e45b6af4e7344fa284e2d7d4b23b5b09cb55d36b0685c87",
|
||||||
|
FEE_SETTER_ROLE: "0xe6ad9a47fbda1dc18de1eb5eeb7d935e5e81b4748f3cfc61e233e64f88182060",
|
||||||
|
PAUSER_ROLE: "0x65d7a28e3265b37a6474929f336521b332c1681b933f6cb9f3376673440d862a",
|
||||||
|
UNPAUSER_ROLE: "0x427da25fe773164f88948d3e215c94b6554e2ed5e5f203a821c9f2f6131cf75a",
|
||||||
|
FUND_RESCUER_ROLE: "0x912e45d663a6f4cc1d0491d8f046e06c616f40352565ea1cdb86a0e1aaefa41b"
|
||||||
|
};
|
||||||
|
|
||||||
|
// Iterate through roles and grant them to the corresponding addresses
|
||||||
|
for (const [roleName, roleHash] of Object.entries(roles)) {
|
||||||
|
const addresses = rolesDict[roleName];
|
||||||
|
if (addresses && addresses.length > 0) {
|
||||||
|
console.log(`Granting ${roleName} to the following addresses:`, addresses);
|
||||||
|
const tx = await router.batchGrantRole(roleHash, addresses);
|
||||||
|
await tx.wait(); // Wait for the transaction to be mined
|
||||||
|
console.log(`Role ${roleName} granted at transaction: ${tx.hash}`);
|
||||||
|
} else {
|
||||||
|
console.log(`No addresses found for role ${roleName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
.then(() => process.exit(0))
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("Error setting roles:", error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
use std::{collections::HashSet, sync::LazyLock};
|
use std::{collections::HashSet, sync::LazyLock};
|
||||||
|
|
||||||
pub const DEFAULT_EXECUTORS_JSON: &str =
|
pub const DEFAULT_EXECUTORS_JSON: &str = include_str!("../../../config/executor_addresses.json");
|
||||||
include_str!("../../../src/encoding/config/executor_addresses.json");
|
|
||||||
|
|
||||||
/// These protocols support the optimization of grouping swaps.
|
/// These protocols support the optimization of grouping swaps.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -400,7 +400,7 @@ mod tests {
|
|||||||
let hex_protocol_data = encode(&protocol_data);
|
let hex_protocol_data = encode(&protocol_data);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
executor_address,
|
executor_address,
|
||||||
Bytes::from_str("0x5c2f5a71f67c01775180adc06909288b4c329308").unwrap()
|
Bytes::from_str("0x00C1b81e3C8f6347E69e2DDb90454798A6Be975E").unwrap()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
hex_protocol_data,
|
hex_protocol_data,
|
||||||
@@ -669,7 +669,7 @@ mod tests {
|
|||||||
"01", // token out index
|
"01", // token out index
|
||||||
"000000", // split
|
"000000", // split
|
||||||
// Swap data
|
// Swap data
|
||||||
"5c2f5a71f67c01775180adc06909288b4c329308", // executor address
|
"00c1b81e3c8f6347e69e2ddb90454798a6be975e", // executor address
|
||||||
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
|
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
|
||||||
"a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id
|
"a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id
|
||||||
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
|
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
|
||||||
@@ -1076,7 +1076,7 @@ mod tests {
|
|||||||
"01", // token out index
|
"01", // token out index
|
||||||
"000000", // split
|
"000000", // split
|
||||||
// Swap data
|
// Swap data
|
||||||
"5c2f5a71f67c01775180adc06909288b4c329308", // executor address
|
"00c1b81e3c8f6347e69e2ddb90454798a6be975e", // executor address
|
||||||
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
|
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", // token in
|
||||||
"a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id
|
"a478c2975ab1ea89e8196811f51a7b7ade33eb11", // component id
|
||||||
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
|
"3ede3eca2a72b3aecc820e955b36f38437d01395", // receiver
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
Reference in New Issue
Block a user