Skip to content

Commit

Permalink
setConfig task
Browse files Browse the repository at this point in the history
  • Loading branch information
0xIryna committed Feb 9, 2023
1 parent 2e1f967 commit 480c1e2
Show file tree
Hide file tree
Showing 9 changed files with 4,170 additions and 1 deletion.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,7 @@ dist

# TernJS port file
.tern-port

.DS_Store
package-lock.json
.idea
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,21 @@
# ua-tasks
# ua-tasks

A set of common tasks for contracts integrating LayerZero

## Installation

```sh
$ npm install @layerzerolabs/ua-tasks
```

Import the plugin in your `hardhat.config.js`:

```js
require("@layerzerolabs/ua-tasks");
```

Or if you are using TypeScript, in your `hardhat.config.ts`:

```ts
import "@layerzerolabs/ua-tasks";
```
39 changes: 39 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "ua-tasks",
"version": "0.0.1",
"repository": "https://github.com/LayerZero-Labs/ua-tasks.git",
"license": "MIT",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"clean": "rimraf dist",
"build": "tsc"
},
"files": [
"dist/",
"src/",
"LICENSE",
"README.md"
],
"devDependencies": {
"@gnosis.pm/safe-core-sdk": "^2.0.0",
"@gnosis.pm/safe-core-sdk-types": "^1.0.0",
"@gnosis.pm/safe-ethers-lib": "^1.0.0",
"@gnosis.pm/safe-service-client": "1.1.1",
"@nomiclabs/hardhat-ethers": "^2.2.1",
"@types/mocha": "^10.0.1",
"cli-ux": "^6.0.9",
"dotenv": "^16.0.3",
"ethers": "^5.7.2",
"hardhat": "^2.12.5",
"hardhat-deploy": "^0.11.22",
"mocha": "^10.2.0",
"rimraf": "^3.0.2",
"tiny-invariant": "^1.3.1",
"ts-node": "^10.9.1",
"typescript": "^4.9.4"
},
"dependencies": {
"@layerzerolabs/lz-sdk": "^0.0.11"
}
}
6 changes: 6 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { task, types } from "hardhat/config";

task("setConfig", "sets send and receive messaging library versions and a custom config for contracts inheriting LzApp", require("./setConfig"))
.addParam("networks", "comma separated list of networks where contracts are deployed")
.addOptionalParam("contract", "name of the deployed contract")
.addOptionalParam("gnosis", "send to gnosis", false, types.boolean);
242 changes: 242 additions & 0 deletions src/setConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
import { executeTransaction, executeGnosisTransactions, getContractAt, getWalletContractAt, Transaction } from "./utils/crossChainHelper";
import { promptToProceed, writeToCsv } from "./utils/helpers";
import { utils, constants } from "ethers";

const fs = require("fs").promises;
const { LZ_ADDRESS, CHAIN_ID } = require("@layerzerolabs/lz-sdk");
const ENVIRONMENTS = require("../constants/environments.json");
const MAINNET_CONFIG = require("../constants/uaMainnetConfig.json");
const TESTNET_CONFIG = require("../constants/uaTestnetConfig.json");
const UA_ADDRESSES = require("../constants/uaAddresses.json");
const LZ_ENDPOINTS = require("../constants/layerzeroEndpoints.json");

const VERSION = 2;

// Application config types from UltraLightNodeV2 contract
const CONFIG_TYPE_INBOUND_PROOF_LIBRARY_VERSION = 1;
const CONFIG_TYPE_INBOUND_BLOCK_CONFIRMATIONS = 2;
const CONFIG_TYPE_RELAYER = 3;
const CONFIG_TYPE_OUTBOUND_PROOF_TYPE = 4;
const CONFIG_TYPE_OUTBOUND_BLOCK_CONFIRMATIONS = 5;
const CONFIG_TYPE_ORACLE = 6;

const endpointAbi = [
"function uaConfigLookup(address) view returns (tuple(uint16, uint16, address, address))",
"function getConfig(uint16 _version, uint16 _chainId, address _userApplication, uint _configType) external view returns (bytes memory)"
]

const uaAbi = [
"function setConfig(uint16 _version, uint16 _chainId, uint _configType, bytes calldata _config)",
"function setSendVersion(uint16 _version)",
"function setReceiveVersion(uint16 _version)"
]

module.exports = async (taskArgs: any, hre: any) => {
// const environment = taskArgs.e
// let networks
let config = TESTNET_CONFIG;
// if (environment === "mainnet") {
// networks = ENVIRONMENTS[environment]
// config = MAINNET_CONFIG
// } else if (environment === "testnet") {
// networks = ENVIRONMENTS[environment]
// config = TESTNET_CONFIG
// } else {
// console.log("Invalid environment")
// return
// }

const networks = ["goerli", "optimism-goerli", "bsc-testnet", "fuji", "arbitrum-goerli"];
const localNetworks = networks;
const remoteNetworks = networks;

const transactionByNetwork: any = (
await Promise.all(
localNetworks.map(async (localNetwork) => {
const transactions: Transaction[] = [];
const uaAddress = UA_ADDRESSES[localNetwork];

if (uaAddress === undefined) return;

const endpoint = await getContractAt(hre, localNetwork, "Endpoint", endpointAbi, LZ_ENDPOINTS[localNetwork]);
const ua = await getContractAt(hre, localNetwork, "UserApplication", uaAbi, uaAddress);
const chainId = CHAIN_ID[localNetwork];
const localNetworkConfig = config[localNetwork];

if (localNetworkConfig === undefined) return;
const uaConfig = await endpoint.uaConfigLookup(ua.address);

if (localNetworkConfig.sendVersion !== undefined) {
transactions.push(...(await setSendVersion(chainId, ua, uaConfig[0], localNetworkConfig.sendVersion)));
}

if (localNetworkConfig.receiveVersion !== undefined) {
transactions.push(...(await setReceiveVersion(chainId, ua, uaConfig[1], localNetworkConfig.receiveVersion)));
}

await Promise.all(
remoteNetworks.map(async (remoteNetwork) => {
if (localNetwork === remoteNetwork || UA_ADDRESSES[remoteNetwork] === undefined) return;
const remoteChainId = CHAIN_ID[remoteNetwork];

if (localNetworkConfig.inboundProofLibraryVersion !== undefined) {
transactions.push(...(await setConfig(chainId, remoteChainId, endpoint, ua, CONFIG_TYPE_INBOUND_PROOF_LIBRARY_VERSION, "uint16", localNetworkConfig.inboundProofLibraryVersion)));
}

if (localNetworkConfig.inboundBlockConfirmations !== undefined) {
transactions.push(...(await setConfig(chainId, remoteChainId, endpoint, ua, CONFIG_TYPE_INBOUND_BLOCK_CONFIRMATIONS, "uint64", localNetworkConfig.inboundBlockConfirmations)));
}

if (localNetworkConfig.relayer !== undefined) {
transactions.push(...(await setConfig(chainId, remoteChainId, endpoint, ua, CONFIG_TYPE_RELAYER, "address", localNetworkConfig.relayer)));
}

if (localNetworkConfig.outboundProofType !== undefined) {
transactions.push(...(await setConfig(chainId, remoteChainId, endpoint, ua, CONFIG_TYPE_OUTBOUND_PROOF_TYPE, "uint16", localNetworkConfig.outboundProofType)));
}

if (localNetworkConfig.outboundBlockConfirmations !== undefined) {
transactions.push(...(await setConfig(chainId, remoteChainId, endpoint, ua, CONFIG_TYPE_OUTBOUND_BLOCK_CONFIRMATIONS, "uint64", localNetworkConfig.outboundBlockConfirmations)));
}

if (localNetworkConfig.oracle !== undefined) {
transactions.push(...(await setConfig(chainId, remoteChainId, endpoint, ua, CONFIG_TYPE_ORACLE, "address", localNetworkConfig.oracle)));
}
})
);
return {
network: localNetwork,
transactions,
};
})
)
).filter((x) => x);

let totalTransactionsNeedingChange: number = 0;
const columns = ["needChange", "chainId", "remoteChainId", "contractAddress", "methodName", "args", "diff"];

transactionByNetwork.forEach(({ network, transaction }) => {
console.log(`================================================`);
console.log(`${network} transactions`);
console.log(`================================================`);
const transactionsNeedingChange = transactions.filter((tx) => tx.needChange);
totalTransactionsNeedingChange += transactionsNeedingChange.length;

if (!transactionsNeedingChange.length) {
console.log("No change needed\n");
} else {
console.table(transactionsNeedingChange, columns);
}
});

writeToCsv("./setConfigTxs.csv", columns, transactionByNetwork);

if (totalTransactionsNeedingChange === 0) return;

await promptToProceed(taskArgs.gnosis
? "Would you like to proceed with above instructions in Gnosis?"
: "Would you like to proceed with above instruction?");

const errs: any[] = [];
const print: any = {};
let previousPrintLine = 0;
const printResult = () => {
if (previousPrintLine) {
process.stdout.moveCursor(0, -previousPrintLine);
}
if (Object.keys(print)) {
previousPrintLine = Object.keys(print).length + 4;
console.table(Object.keys(print).map((network) => ({ network, ...print[network] })));
}
};

if (taskArgs.gnosis) {
await Promise.all(
transactionByNetwork.map(async ({ network, transactions }) => {
const transactionToCommit = transactions.filter((transaction) => transaction.needChange);

print[network] = print[network] || { requests: "1/1" };
print[network].current = `executeGnosisTransactions: ${transactionToCommit}`;
try {
await executeGnosisTransactions(hre, network, transactionToCommit);
print[network].requests = "1/1";
printResult();
} catch (err: any) {
console.log(`Failing calling executeGnosisTransactions for network ${network} with err ${err}`);
errs.push({ network, err });
print[network].current = err.message;
print[network].err = true;
printResult();
}
})
);
}
else {
await Promise.all(
transactionByNetwork.map(async ({ network, transactions }) => {
const transactionToCommit = transactions.filter((transaction) => transaction.needChange);
const contract = await getWalletContractAt(hre, network, "UserApplication", uaAbi, UA_ADDRESSES[network]);

let successTx = 0;
print[network] = print[network] || { requests: `${successTx}/${transactionToCommit.length}` };
for (let transaction of transactionToCommit) {
print[network].current = `${transaction.methodName}(${transaction.args})`;
printResult();
try {
const tx = await executeTransaction(hre, network, transaction, contract);
print[network].past = `${transaction.methodName}(${transaction.args}) (${tx.transactionHash})`;
successTx++;
print[network].requests = `${successTx}/${transactionToCommit.length}`;
printResult();
} catch (err: any) {
console.log(`Failing calling ${transaction.contractName}.${transaction.methodName} for network ${network} with err ${err}`);
console.log(err);
errs.push({ network, err });
print[network].current = err;
print[network].err = true;
printResult();
break;
}
}
})
);
}
};

const setSendVersion = async (chainId: string, ua: any, currentSendVersion: any, newSendVersion: any): Promise<Transaction[]> => {
const needChange = currentSendVersion !== newSendVersion;
const contractAddress = ua.address;
const methodName = "setSendVersion";
//const remoteChainId = undefined
const args = [newSendVersion];
const calldata = ua.interface.encodeFunctionData(methodName, args);
const diff = needChange ? { oldValue: currentSendVersion, newValue: newSendVersion } : undefined;

return [{ needChange, chainId, contractAddress, methodName, args, calldata, diff }];
};

const setReceiveVersion = async (chainId: string, ua: any, currentReceiveVersion: any, newReceiveVersion: any): Promise<Transaction[]> => {
const needChange = currentReceiveVersion !== newReceiveVersion;
const contractAddress = ua.address;
const methodName = "setReceiveVersion";
//const remoteChainId = undefined
const args = [newReceiveVersion];
const calldata = ua.interface.encodeFunctionData(methodName, args);
const diff = needChange ? { oldValue: currentReceiveVersion, newValue: newReceiveVersion } : undefined;

return [{ needChange, chainId, contractAddress, methodName, args, calldata, diff }];
};

const setConfig = async (chainId: string, remoteChainId: string, endpoint: any, ua: any, configType: number, configValueType: string, newValue: any): Promise<Transaction[]> => {
const currentConfig = await endpoint.getConfig(VERSION, remoteChainId, ua.address, configType);
const [oldValue] = utils.defaultAbiCoder.decode([configValueType], currentConfig) as any;
const newConfig = utils.defaultAbiCoder.encode([configValueType], [newValue]);
const contractAddress = ua.address;
const methodName = "setConfig";
const args = [VERSION, remoteChainId, configType, newConfig];
const needChange = oldValue !== newValue;
const calldata = ua.interface.encodeFunctionData(methodName, args);
const diff = needChange ? { oldValue, newValue } : undefined;

return [{ needChange, chainId, remoteChainId, contractAddress, methodName, args, calldata, diff }];
};
Loading

0 comments on commit 480c1e2

Please sign in to comment.