Skip to content

Commit

Permalink
chore(smart-contracts): publish to tenderly (#1048)
Browse files Browse the repository at this point in the history
  • Loading branch information
alexandre-abrioux authored Mar 7, 2023
1 parent 4d414a3 commit 27aaa1f
Show file tree
Hide file tree
Showing 15 changed files with 178 additions and 2 deletions.
7 changes: 7 additions & 0 deletions packages/currency/src/chains/ChainsAbstract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,11 @@ export abstract class ChainsAbstract<
public getChainName(chainId: CHAIN_ID): CHAIN_NAME | undefined {
return this.chainNames.find((chainName) => this.chains[chainName].chainId === chainId);
}

/**
* Returns true is the chain is a testnet chain
*/
public isTestnet(chainName: CHAIN_NAME): boolean {
return Boolean(this.chains[chainName].testnet);
}
}
1 change: 1 addition & 0 deletions packages/currency/src/chains/btc/data/testnet.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export const chainId = '000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943';
export const testnet = true;
1 change: 1 addition & 0 deletions packages/currency/src/chains/evm/data/arbitrum-rinkeby.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export const chainId = 421611;
export const testnet = true;
1 change: 1 addition & 0 deletions packages/currency/src/chains/evm/data/goerli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { TokenMap } from '../../../types';
import { supportedGoerliERC20 } from '../../../erc20/chains/goerli';

export const chainId = 5;
export const testnet = true;
export const currencies: TokenMap = {
...supportedGoerliERC20,
};
1 change: 1 addition & 0 deletions packages/currency/src/chains/evm/data/mumbai.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export const chainId = 80001;
export const testnet = true;
1 change: 1 addition & 0 deletions packages/currency/src/chains/evm/data/rinkeby.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { supportedRinkebyERC20 } from '../../../erc20/chains/rinkeby';
import { supportedRinkebyERC777 } from '../../../erc777/chains/rinkeby';

export const chainId = 4;
export const testnet = true;
export const currencies: TokenMap = {
...supportedRinkebyERC20,
...supportedRinkebyERC777,
Expand Down
1 change: 1 addition & 0 deletions packages/currency/src/chains/near/data/near-testnet.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export const chainId = 'testnet';
export const testnet = true;
1 change: 1 addition & 0 deletions packages/currency/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export type TokenMap = Record<TokenAddress, TokenDefinition>;
*/
export type Chain = {
chainId: number | string;
testnet?: boolean;
currencies?: TokenMap;
};

Expand Down
12 changes: 11 additions & 1 deletion packages/smart-contracts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ The deployer contract should be deployed at `0xE99Ab70a5FAE59551544FA326fA048f7B

Be sure to run `yarn build:sol` before deploying the deployer or a contract.

The contracts implemented are listed in the array `create2ContractDeploymentList` in [Utils](./scripts-create2/utils.ts).
The contracts implemented are listed in the array `create2ContractDeploymentList` in [Utils](scripts-create2/utils.ts).

### Deploy the request deployer (once per chain)

Expand Down Expand Up @@ -166,6 +166,16 @@ See `hardhat.config.ts`.
yarn hardhat verify-contract-from-deployer --network <NETWORK>
```

### Add the contracts to Tenderly

Once the contract has been added to the artifacts (`./src/lib/artifacts`), run the following command to synchronize
contracts with the Tenderly account.
Environment variables needed: `TENDERLY_...` (see `hardhat.config.ts`).

```bash
yarn hardhat tenderly-monitor-contracts
```

#### Verify the contracts manually With Hardhat (legacy)

A more generic way to verify any contract by setting constructor argments manually:
Expand Down
12 changes: 12 additions & 0 deletions packages/smart-contracts/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { VerifyCreate2FromList } from './scripts-create2/verify';
import { deployWithCreate2FromList } from './scripts-create2/deploy';
import { NUMBER_ERRORS } from './scripts/utils';
import { networkRpcs } from '@requestnetwork/utils';
import { tenderlyImportAll } from './scripts-create2/tenderly';

config();

Expand Down Expand Up @@ -185,6 +186,11 @@ export default {
},
],
},
tenderly: {
project: process.env.TENDERLY_PROJECT,
username: process.env.TENDERLY_USERNAME,
accessKey: process.env.TENDERLY_ACCESS_KEY,
},
typechain: {
outDir: 'src/types',
target: 'ethers-v5',
Expand Down Expand Up @@ -272,6 +278,12 @@ task(
await VerifyCreate2FromList(hre as HardhatRuntimeEnvironmentExtended);
});

task('tenderly-monitor-contracts', 'Import all contracts to a Tenderly account').setAction(
async (_args, hre) => {
await tenderlyImportAll(hre as HardhatRuntimeEnvironmentExtended);
},
);

subtask(DEPLOYER_KEY_GUARD, 'prevent usage of the deployer master key').setAction(async () => {
if (accounts && accounts[0] === process.env.DEPLOYER_MASTER_KEY) {
throw new Error('The deployer master key should not be used for this action');
Expand Down
1 change: 1 addition & 0 deletions packages/smart-contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"@types/chai": "4.2.21",
"@types/mocha": "8.2.3",
"@types/node": "16.11.7",
"axios": "0.27.2",
"chai": "4.3.4",
"dotenv": "10.0.0",
"ethereum-waffle": "3.4.0",
Expand Down
109 changes: 109 additions & 0 deletions packages/smart-contracts/scripts-create2/tenderly.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { HardhatRuntimeEnvironmentExtended } from './types';
import * as artifacts from '../src/lib/artifacts';
import { ContractArtifact } from '../src/lib';
import { Contract } from 'ethers';
import * as console from 'console';
import axios from 'axios';
import { EvmChains } from '@requestnetwork/currency';
import { CurrencyTypes } from '@requestnetwork/types';

const getTenderlyAxiosInstance = (hre: HardhatRuntimeEnvironmentExtended) => {
return axios.create({
baseURL: 'https://api.tenderly.co',
headers: {
'X-Access-Key': hre.config.tenderly.accessKey,
},
});
};

const capitalizeFirstLetter = (string: string) => string.charAt(0).toUpperCase() + string.slice(1);

/**
* Chains supported by Tenderly.
* Supported testnet chains are commented out.
*/
const supportedTenderlyChains: CurrencyTypes.EvmChainName[] = [
'arbitrum-one',
'arbitrum-rinkeby',
'avalanche',
'bsc',
'fantom',
'goerli',
'mainnet',
'matic',
'moonbeam',
'mumbai',
'optimism',
'rinkeby',
'xdai',
];

type TenderlyContract = { address: string; chainId: number };

const getTenderlyContractId = (c: TenderlyContract) =>
`eth:${c.chainId}:${c.address.toLowerCase()}`;

export const tenderlyImportAll = async (hre: HardhatRuntimeEnvironmentExtended): Promise<void> => {
try {
const { username, project } = hre.config.tenderly;
const contracts: Record<string, { name: string } & TenderlyContract> = {};
const mainnetContracts: Set<string> = new Set();
const testnetContracts: Set<string> = new Set();
const versions: Record<string, Set<string>> = {};
for (const artifactName in artifacts) {
const artifact = (artifacts as any)[artifactName] as ContractArtifact<Contract>;
const deployments = artifact.getAllAddressesFromAllNetworks();
for (const deployment of deployments) {
const { networkName, address, version } = deployment;
if (!supportedTenderlyChains.includes(networkName)) continue;
const chainId = EvmChains.getChainId(networkName);
const contract: TenderlyContract = {
address,
chainId,
};
const contractId = getTenderlyContractId(contract);
contracts[contractId] = {
name: capitalizeFirstLetter(artifactName.replace(/Artifact/i, '')),
...contract,
};
versions[version] ??= new Set();
versions[version].add(contractId);
(EvmChains.isTestnet(networkName) ? testnetContracts : mainnetContracts).add(contractId);
}
}
console.log(`> Retrieved ${Object.keys(contracts).length} contracts from protocol artifacts`);

console.log(`> Syncing contracts with Tenderly...`);
const axiosInstance = getTenderlyAxiosInstance(hre);
await axiosInstance.post(`/api/v2/accounts/${username}/projects/${project}/contracts`, {
contracts: Object.values(contracts).map((contract) => ({
address: contract.address,
display_name: contract.name,
network_id: contract.chainId.toString(),
})),
});
console.log(' ✔ done');

console.log(`> Adding version tags to contracts...`);
for (const version in versions) {
await axiosInstance.post(`/api/v1/account/${username}/project/${project}/tag`, {
contract_ids: Array.from(versions[version]),
tag: `v${version}`,
});
}
console.log(' ✔ done');

console.log(`> Adding mainnet/testnet tags to contracts...`);
await axiosInstance.post(`/api/v1/account/${username}/project/${project}/tag`, {
contract_ids: Array.from(mainnetContracts),
tag: 'mainnet',
});
await axiosInstance.post(`/api/v1/account/${username}/project/${project}/tag`, {
contract_ids: Array.from(testnetContracts),
tag: 'testnet',
});
console.log(' ✔ done');
} catch (err) {
console.error('Error while adding contract(s) to Tenderly', err.response?.data || err);
}
};
5 changes: 5 additions & 0 deletions packages/smart-contracts/scripts-create2/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ export type HardhatRuntimeEnvironmentExtended = HardhatRuntimeEnvironment & {
deployerAddress: string;
gasLimit?: number;
};
tenderly: {
project: string;
username: string;
accessKey: string;
};
};
};

Expand Down
2 changes: 1 addition & 1 deletion packages/smart-contracts/scripts-create2/verify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export async function VerifyCreate2FromList(hre: HardhatRuntimeEnvironmentExtend
// Other cases to add when necessary
default:
throw new Error(
`The contrat ${contract} is not to be deployed using the CREATE2 scheme`,
`The contract ${contract} is not to be deployed using the CREATE2 scheme`,
);
}
} catch (err) {
Expand Down
25 changes: 25 additions & 0 deletions packages/smart-contracts/src/lib/ContractArtifact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,31 @@ export class ContractArtifact<TContract extends Contract> {
}));
}

/**
* Retrieve all addresses for all versions for all networks
* @returns the addresses of the deployed contract and the associated network and version.
*/
getAllAddressesFromAllNetworks(): {
version: string;
address: string;
networkName: CurrencyTypes.EvmChainName;
}[] {
const deployments = [];
for (const version in this.info) {
let networkName: CurrencyTypes.EvmChainName;
for (networkName in this.info[version].deployment) {
const address = this.info[version].deployment[networkName]?.address;
if (!address) continue;
deployments.push({
version,
address,
networkName,
});
}
}
return deployments;
}

/**
* Retrieve the block creation number from the artifact of the used version
* deployed into the specified network
Expand Down

0 comments on commit 27aaa1f

Please sign in to comment.