diff --git a/.env.example b/.env.example index 2525a67a..8a266b33 100644 --- a/.env.example +++ b/.env.example @@ -2,4 +2,10 @@ ETHERSCAN_API_KEY="" PRIVATE_KEY="" TENDERLY_PROJECT="" TENDERLY_USERNAME="" -ALCHEMY_ID="" \ No newline at end of file +ALCHEMY_ID="" +MAIN_ETHSCAN_KEY= +OPT_ETHSCAN_KEY= +POLY_ETHSCAN_KEY= +ARB_ETHSCAN_KEY= +AVAX_ETHSCAN_KEY= +FTM_ETHSCAN_KEY= \ No newline at end of file diff --git a/README.md b/README.md index a3701741..0b36d125 100644 --- a/README.md +++ b/README.md @@ -47,8 +47,27 @@ Run all the tests: ```sh $ npm run test ``` + (Striclty use this envirnment to test, or otherwise make suitable changes in config file before testing). +### Deploy + +To deploy a connector using interactive CLI + +```sh +$ npm run deploy:runner +``` + +(To deploy script manually use `scripts/deployment/deployManually.ts` script) + +### checks + +To check that code is compatible with github actions + +```sh +$ npm run check +``` + ## How to add a new connector You can create a new PR to add a new connector. To get the PR merged, certain requirements needs to be met which will be explained here. @@ -57,39 +76,39 @@ You can create a new PR to add a new connector. To get the PR merged, certain re Common files for all connectors are in `contracts/common` directory. -* `math.sol` has methods for mathematical operations (`DSMath`) -* `interfaces.sol` contains the common interfaces - * `TokenInterface` for ERC-20 interface including WETH -* `stores.sol` contains the global constants as well as methods `getId` & `setId` (`Stores`) -* `basic.sol` inherits `DSMath` & `Stores` contracts. This contains few details explained below - * Wrapping & unwrapping ETH (`convertEthToWeth` & `convertWethToEth`) - * Getting token & ETH balance of DSA +- `math.sol` has methods for mathematical operations (`DSMath`) +- `interfaces.sol` contains the common interfaces + - `TokenInterface` for ERC-20 interface including WETH +- `stores.sol` contains the global constants as well as methods `getId` & `setId` (`Stores`) +- `basic.sol` inherits `DSMath` & `Stores` contracts. This contains few details explained below + - Wrapping & unwrapping ETH (`convertEthToWeth` & `convertWethToEth`) + - Getting token & ETH balance of DSA Connectors are under `contracts/connectors` directory, and should be formatted as follows: -* Connector events should be in a separate contract: `events.sol` -* Interfaces should be defined in a seperate file: `interface.sol` -* If the connector has helper methods & constants (including interface instances), this should be defined in a separate file: `helpers.sol` - * `Helpers` contract should inherit `Basic` contract from common directory - * If the connector doesn't have any helper methods, the main contract should inherit `Basic` contract -* The main logic of the contract should be under `main.sol`, and the contract should inherit `Helpers` (if exists, otherwise `Basic`) & `Events` +- Connector events should be in a separate contract: `events.sol` +- Interfaces should be defined in a seperate file: `interface.sol` +- If the connector has helper methods & constants (including interface instances), this should be defined in a separate file: `helpers.sol` + - `Helpers` contract should inherit `Basic` contract from common directory + - If the connector doesn't have any helper methods, the main contract should inherit `Basic` contract +- The main logic of the contract should be under `main.sol`, and the contract should inherit `Helpers` (if exists, otherwise `Basic`) & `Events` Few things to consider while writing the connector: -* Connector should have a public constant string declared `name`, which will be the name of the connector. This will be versioned. Ex: `Compound-v1` -* Contract name should start with `ConnectV2` appended with protocol name. Eg: `ConnectV2Compound` -* User interacting methods (`external` methods) will not be emitting events, rather the methods will be returning 2 variables: - * `_eventName` of `string` type: This will be the event signture defined in the `Events` contract. Ex: `LogDeposit(address,address,uint256,uint256,uint256)` - * `_eventParam` of `bytes` type: This will be the abi encoded event parameters -* The contracts should not have `selfdestruct()` -* The contracts should not have `delegatecall()` -* Use `uint(-1)` of `type(uint256).max` for maximum amount everywhere -* Use `ethAddr` (declared in `Stores`) to denote Ethereum (non-ERC20) -* Use `address(this)` instead of `msg.sender` for fetching balance on-chain, etc -* Only `approve()` (declared in `Basic`) limited amount while giving ERC20 allowance, which strictly needs to be 0 by the end of the spell. -* User interacting functions should have natspec comments(@dev, @notice, @param). -* Use `getUint()` (declared in `Stores`) for getting value that saved from previous spell -* Use `setUint()` (declared in `Stores`) for setting value to save for the future spell +- Connector should have a public constant string declared `name`, which will be the name of the connector. This will be versioned. Ex: `Compound-v1` +- Contract name should start with `ConnectV2` appended with protocol name. Eg: `ConnectV2Compound` +- User interacting methods (`external` methods) will not be emitting events, rather the methods will be returning 2 variables: + - `_eventName` of `string` type: This will be the event signture defined in the `Events` contract. Ex: `LogDeposit(address,address,uint256,uint256,uint256)` + - `_eventParam` of `bytes` type: This will be the abi encoded event parameters +- The contracts should not have `selfdestruct()` +- The contracts should not have `delegatecall()` +- Use `uint(-1)` of `type(uint256).max` for maximum amount everywhere +- Use `ethAddr` (declared in `Stores`) to denote Ethereum (non-ERC20) +- Use `address(this)` instead of `msg.sender` for fetching balance on-chain, etc +- Only `approve()` (declared in `Basic`) limited amount while giving ERC20 allowance, which strictly needs to be 0 by the end of the spell. +- User interacting functions should have natspec comments(@dev, @notice, @param). +- Use `getUint()` (declared in `Stores`) for getting value that saved from previous spell +- Use `setUint()` (declared in `Stores`) for setting value to save for the future spell ### Support diff --git a/hardhat.config.ts b/hardhat.config.ts index a9ee4f9b..e679a097 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -14,6 +14,7 @@ import { NetworkUserConfig } from "hardhat/types"; import { utils } from "ethers"; import Web3 from "web3"; import { network } from "hardhat"; +import bigNumber from "bignumber.js"; dotenvConfig({ path: resolve(__dirname, "./.env") }); @@ -34,26 +35,22 @@ if (!alchemyApiKey) { } const PRIVATE_KEY = process.env.PRIVATE_KEY; -const ETHERSCAN_API = process.env.ETHERSCAN_API_KEY; -const POLYGONSCAN_API = process.env.POLYGON_API_KEY; -const ARBISCAN_API = process.env.ARBISCAN_API_KEY; -const SNOWTRACE_API = process.env.SNOWTRACE_API_KEY; -const FANTOMSCAN_API = process.env.FANTOM_API_KEY; -const OPTIMISM_API = process.env.OPTIMISM_API_KEY; const mnemonic = process.env.MNEMONIC ?? "test test test test test test test test test test test junk"; -const networkGasPriceConfig: Record = { - mainnet: "160", - polygon: "50", - avalanche: "50", - arbitrum: "2" +const networkGasPriceConfig: Record = { + mainnet: 100, + polygon: 50, + avalanche: 30, + arbitrum: 1, + optimism: 0.001, + fantom: 300 }; function createConfig(network: string) { return { url: getNetworkUrl(network), accounts: !!PRIVATE_KEY ? [`0x${PRIVATE_KEY}`] : { mnemonic }, - gasPrice: 35 * 1e9 // 0.0001 GWEI + gasPrice: new bigNumber(networkGasPriceConfig[network]).multipliedBy(1e9).toNumber() // Update the mapping above }; } @@ -66,16 +63,6 @@ function getNetworkUrl(networkType: string) { else return `https://eth-mainnet.alchemyapi.io/v2/${alchemyApiKey}`; } -function getScanApiKey(networkType: string) { - if (networkType === "avalanche") return SNOWTRACE_API; - else if (networkType === "polygon") return POLYGONSCAN_API; - else if (networkType === "arbitrum") return ARBISCAN_API; - else if (networkType === "fantom") return FANTOMSCAN_API; - else if (networkType === "fantom") return FANTOMSCAN_API; - else if (networkType === "optimism") return OPTIMISM_API; - else return ETHERSCAN_API; -} - /** * @type import('hardhat/config').HardhatUserConfig */ @@ -126,7 +113,14 @@ const config: HardhatUserConfig = { tests: "./test" }, etherscan: { - apiKey: "CZ1YB396AX4XAQ489Y4NJ33SJBCYZZD1VS" + apiKey: { + mainnet: String(process.env.MAIN_ETHSCAN_KEY), + optimisticEthereum: String(process.env.OPT_ETHSCAN_KEY), + polygon: String(process.env.POLY_ETHSCAN_KEY), + arbitrumOne: String(process.env.ARB_ETHSCAN_KEY), + avalanche: String(process.env.AVAX_ETHSCAN_KEY), + opera: String(process.env.FTM_ETHSCAN_KEY) + } }, typechain: { outDir: "typechain", diff --git a/package-lock.json b/package-lock.json index aadbddd5..bc7ad39e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,7 @@ }, "devDependencies": { "@nomiclabs/hardhat-ethers": "^2.0.3", - "@nomiclabs/hardhat-etherscan": "^2.1.8", + "@nomiclabs/hardhat-etherscan": "^3.0.3", "@nomiclabs/hardhat-waffle": "^2.0.1", "@nomiclabs/hardhat-web3": "^2.0.0", "@openzeppelin/test-helpers": "^0.5.15", @@ -2121,9 +2121,9 @@ } }, "node_modules/@nomiclabs/hardhat-etherscan": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-2.1.8.tgz", - "integrity": "sha512-0+rj0SsZotVOcTLyDOxnOc3Gulo8upo0rsw/h+gBPcmtj91YqYJNhdARHoBxOhhE8z+5IUQPx+Dii04lXT14PA==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-3.0.3.tgz", + "integrity": "sha512-OfNtUKc/ZwzivmZnnpwWREfaYncXteKHskn3yDnz+fPBZ6wfM4GR+d5RwjREzYFWE+o5iR9ruXhWw/8fejWM9g==", "dev": true, "dependencies": { "@ethersproject/abi": "^5.1.2", @@ -2131,8 +2131,8 @@ "cbor": "^5.0.2", "debug": "^4.1.1", "fs-extra": "^7.0.1", - "node-fetch": "^2.6.0", - "semver": "^6.3.0" + "semver": "^6.3.0", + "undici": "^4.14.1" }, "peerDependencies": { "hardhat": "^2.0.4" @@ -28736,6 +28736,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/undici": { + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-4.16.0.tgz", + "integrity": "sha512-tkZSECUYi+/T1i4u+4+lwZmQgLXd4BLGlrc7KZPcLIW7Jpq99+Xpc30ONv7nS6F5UNOxp/HBZSSL9MafUrvJbw==", + "dev": true, + "engines": { + "node": ">=12.18" + } + }, "node_modules/union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", @@ -32499,9 +32508,9 @@ "requires": {} }, "@nomiclabs/hardhat-etherscan": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-2.1.8.tgz", - "integrity": "sha512-0+rj0SsZotVOcTLyDOxnOc3Gulo8upo0rsw/h+gBPcmtj91YqYJNhdARHoBxOhhE8z+5IUQPx+Dii04lXT14PA==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-3.0.3.tgz", + "integrity": "sha512-OfNtUKc/ZwzivmZnnpwWREfaYncXteKHskn3yDnz+fPBZ6wfM4GR+d5RwjREzYFWE+o5iR9ruXhWw/8fejWM9g==", "dev": true, "requires": { "@ethersproject/abi": "^5.1.2", @@ -32509,8 +32518,8 @@ "cbor": "^5.0.2", "debug": "^4.1.1", "fs-extra": "^7.0.1", - "node-fetch": "^2.6.0", - "semver": "^6.3.0" + "semver": "^6.3.0", + "undici": "^4.14.1" } }, "@nomiclabs/hardhat-waffle": { @@ -53586,6 +53595,12 @@ "which-boxed-primitive": "^1.0.2" } }, + "undici": { + "version": "4.16.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-4.16.0.tgz", + "integrity": "sha512-tkZSECUYi+/T1i4u+4+lwZmQgLXd4BLGlrc7KZPcLIW7Jpq99+Xpc30ONv7nS6F5UNOxp/HBZSSL9MafUrvJbw==", + "dev": true + }, "union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", diff --git a/package.json b/package.json index 84f57114..f312326d 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ }, "devDependencies": { "@nomiclabs/hardhat-ethers": "^2.0.3", - "@nomiclabs/hardhat-etherscan": "^2.1.8", + "@nomiclabs/hardhat-etherscan": "^3.0.3", "@nomiclabs/hardhat-waffle": "^2.0.1", "@nomiclabs/hardhat-web3": "^2.0.0", "@openzeppelin/test-helpers": "^0.5.15", diff --git a/scripts/deployment/deployManually.ts b/scripts/deployment/deployManually.ts new file mode 100644 index 00000000..d60f73d5 --- /dev/null +++ b/scripts/deployment/deployManually.ts @@ -0,0 +1,23 @@ +import { Contract } from "@ethersproject/contracts"; +import hre, { ethers } from "hardhat"; + +import { Greeter__factory } from "../../typechain"; + +async function main(): Promise { + const Greeter: Greeter__factory = await ethers.getContractFactory("Greeter"); + const greeter: Contract = await Greeter.deploy("Hello, Buidler!"); + await greeter.deployed(); + + console.log("Greeter deployed to: ", greeter.address); + + await hre.run("verify:verify", { + address: greeter.address + }); +} + +main() + .then(() => process.exit(0)) + .catch((error: Error) => { + console.error(error); + process.exit(1); + });