瀏覽代碼

EVM deployment scripts and documentation (#141)

* Make Migrations contract Ownable

* Add prod deployment migrations

* Add BSC Testnet deployment configuration

* Add documentation for deploying contracts to production

* Fix test environment script for deployment to development network

* Fix tilt issues + refactor

Co-authored-by: Ali Behjati <bahjatia@gmail.com>
Tom Pointon 3 年之前
父節點
當前提交
4f4bec508e

+ 3 - 3
devnet/eth-devnet.yaml

@@ -73,9 +73,9 @@ spec:
           command:
             - /bin/sh
             - -c
-            - "npm run migrate &&
-            npx truffle test test/pyth.js 2>&1 &&
-            nc -lkp 2000 0.0.0.0"
+            - "npm run migrate --network development &&
+               npx truffle test test/pyth.js 2>&1 &&
+               nc -lkp 2000 0.0.0.0"
           readinessProbe:
             periodSeconds: 1
             failureThreshold: 300

+ 3 - 0
ethereum/.dockerignore

@@ -1,2 +1,5 @@
 node_modules
 build
+.openzeppelin
+networks
+.env

+ 7 - 0
ethereum/.env.prod.binance_testnet

@@ -0,0 +1,7 @@
+MIGRATIONS_DIR=./migrations/prod
+MIGRATIONS_NETWORK=binance_testnet
+
+#Pyth
+WORMHOME_BRIDGE_ADDRESS=0x68605AD7b15c732a30b1BbC62BE8F2A509D74b4D
+PYTH_TO_WORMHOLE_CHAIN_ID=0x1
+PYTH_TO_WORMHOLE_EMITTER=0xf346195ac02f37d60d4db8ffa6ef74cb1be3550047543a4a9ee9acf4d78697b0

+ 8 - 0
ethereum/.env.prod.development

@@ -0,0 +1,8 @@
+# Migrations Metadata
+MIGRATIONS_DIR=./migrations/prod
+MIGRATIONS_NETWORK=development
+
+#Pyth
+WORMHOME_BRIDGE_ADDRESS=0x68605AD7b15c732a30b1BbC62BE8F2A509D74b4D
+PYTH_TO_WORMHOLE_CHAIN_ID=0x1
+PYTH_TO_WORMHOLE_EMITTER=0x6bb14509a612f01fbbc4cffeebd4bbfb492a86df717ebe92eb6df432a3f00a25

+ 2 - 2
ethereum/.env.test

@@ -14,6 +14,6 @@ BRIDGE_INIT_GOV_CHAIN_ID=0x1
 BRIDGE_INIT_GOV_CONTRACT=0x0000000000000000000000000000000000000000000000000000000000000004
 BRIDGE_INIT_WETH=0xDDb64fE46a91D46ee29420539FC25FD07c5FEa3E
 
-#Pyth Migrations
+#Pyth
 PYTH_TO_WORMHOLE_CHAIN_ID=0x1
-PYTH_TO_WORMHOLE_EMITTER=8fuAZUxHecYLMC76ZNjYzwRybUiDv9LhkRQsAccEykLr
+PYTH_TO_WORMHOLE_EMITTER=0x71f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b

+ 30 - 0
ethereum/Deploying.md

@@ -0,0 +1,30 @@
+# Deploying Contracts to Production
+
+Running the Truffle migrations in [`migrations/prod`](migrations/prod) will deploy the contracts to production. Truffle stores the address of the deployed contracts in the build artifacts, which can make local development difficult. We use [`truffle-deploy-registry`](https://github.com/MedXProtocol/truffle-deploy-registry) to store the addresses separately from the artifacts, in the [`networks`](networks) directory. When we need to perform operations on the deployed contracts, such as performing additional migrations, we can run `apply-registry -n networks/$NETWORK` to populate the artifacts with the correct addresses.
+
+An example deployment process, for deploying to Binance Smart Chain Testnet:
+
+```bash
+# Load the configuration environment variables for deploying to BSC Testnet.
+rm -f .env; ln -s .env.prod.binance_testnet .env && set -o allexport && source .env set && set +o allexport
+
+# The Secret Recovery Phrase for the wallet the contract will be deployed from.
+export MNEMONIC=...
+
+# Ensure that we deploy a fresh build with up-to-date dependencies.
+rm -rf build .openzeppelin node_modules && npm install && npx truffle compile --all
+
+# Merge the network addresses into the artifacts, if some contracts are already deployed.
+npx apply-registry -n networks/$MIGRATIONS_NETWORK
+
+# Perform the migration
+npx truffle migrate --network $MIGRATIONS_NETWORK
+
+# Running the migration will cause a JSON file to be written to the networks/
+# directory, with a filename like 1648198934288.json (the Truffle network ID).
+# To make it more obvious which network this corresponds to, move this file
+# to networks/$MIGRATIONS_NETWORK.
+mkdir -p networks/$MIGRATIONS_NETWORK && mv networks/NETWORK__ID.json networks/$MIGRATIONS_NETWORK
+```
+
+As a sanity check, it is recommended to deploy the  migrations in `migrations/prod` to the Truffle `development` network first. You can do this by using the configuration values in [`.env.prod.development`](.env.prod.development).

+ 7 - 12
ethereum/contracts/Migrations.sol

@@ -1,19 +1,14 @@
 // SPDX-License-Identifier: MIT
 pragma solidity >=0.4.22 <0.9.0;
 
-contract Migrations {
-    address public owner = msg.sender;
-    uint public last_completed_migration;
+import "@openzeppelin/contracts/access/Ownable.sol";
 
-    modifier restricted() {
-        require(
-            msg.sender == owner,
-            "This function is restricted to the contract's owner"
-        );
-        _;
-    }
+// Needed for truffle migrate to work correctly.
+// Simply stores the last completed migration.
+contract Migrations is Ownable {
+    uint public last_completed_migration;
 
-    function setCompleted(uint completed) public restricted {
+    function setCompleted(uint completed) public onlyOwner  {
         last_completed_migration = completed;
     }
-}
+}

+ 12 - 0
ethereum/migrations/prod/1_initial_migration.js

@@ -0,0 +1,12 @@
+const Migrations = artifacts.require("Migrations");
+
+const tdr = require('truffle-deploy-registry');
+
+module.exports = async function (deployer, network) {
+  await deployer.deploy(Migrations);
+  let migrationsInstance = await Migrations.deployed();
+
+  if (!tdr.isDryRunNetworkName(network)) {
+      await tdr.appendInstance(migrationsInstance);
+  }
+};

+ 35 - 0
ethereum/migrations/prod/2_deploy_pyth.js

@@ -0,0 +1,35 @@
+require('dotenv').config({ path: "../.env" });
+const bs58 = require("bs58");
+
+const PythUpgradable = artifacts.require("PythUpgradable");
+
+const wormholeBridgeAddress = process.env.WORMHOME_BRIDGE_ADDRESS;
+const pyth2WormholeChainId = process.env.PYTH_TO_WORMHOLE_CHAIN_ID;
+const pyth2WormholeEmitter = process.env.PYTH_TO_WORMHOLE_EMITTER;
+
+const { deployProxy } = require("@openzeppelin/truffle-upgrades");
+const tdr = require('truffle-deploy-registry');
+
+console.log("Wormhole bridge address: " + wormholeBridgeAddress)
+console.log("pyth2WormholeEmitter: " + pyth2WormholeEmitter)
+console.log("pyth2WormholeChainId: " + pyth2WormholeChainId)
+
+module.exports = async function (deployer, network) {
+    // Deploy the proxy. This will return an instance of PythUpgradable,
+    // with the address field corresponding to the fronting ERC1967Proxy.
+    let proxyInstance = await deployProxy(PythUpgradable,
+        [
+            wormholeBridgeAddress,
+            pyth2WormholeChainId,
+            pyth2WormholeEmitter
+        ],
+        { deployer });
+
+    // Add the ERC1967Proxy address to the PythUpgradable contract's 
+    // entry in the registry. This allows us to call upgradeProxy
+    // functions with the value of PythUpgradable.deployed().address:
+    // e.g. upgradeProxy(PythUpgradable.deployed().address, NewImplementation)
+    if (!tdr.isDryRunNetworkName(network)) {
+        await tdr.appendInstance(proxyInstance);
+    }
+};

+ 2 - 2
ethereum/migrations/test/5_deploy_pyth.js

@@ -5,7 +5,7 @@ const PythUpgradable = artifacts.require("PythUpgradable");
 const Wormhole = artifacts.require("Wormhole");
 
 const pyth2WormholeChainId = process.env.PYTH_TO_WORMHOLE_CHAIN_ID;
-const pyth2WormholeEmitter = bs58.decode(process.env.PYTH_TO_WORMHOLE_EMITTER); // base58, must fit into bytes32
+const pyth2WormholeEmitter = process.env.PYTH_TO_WORMHOLE_EMITTER;
 
 const { deployProxy } = require("@openzeppelin/truffle-upgrades");
 
@@ -17,7 +17,7 @@ module.exports = async function (deployer) {
         [
             (await Wormhole.deployed()).address,
             pyth2WormholeChainId,
-            "0x" + pyth2WormholeEmitter.toString("hex")
+            pyth2WormholeEmitter
         ],
         { deployer });
 };

+ 54 - 8
ethereum/package-lock.json

@@ -9,7 +9,9 @@
       "version": "1.0.0",
       "license": "ISC",
       "dependencies": {
+        "@openzeppelin/contracts": "^4.5.0",
         "@openzeppelin/contracts-upgradeable": "^4.5.2",
+        "@pythnetwork/pyth-sdk-solidity": "0.0.3",
         "dotenv": "^10.0.0",
         "elliptic": "^6.5.2",
         "ganache-cli": "^6.12.1",
@@ -19,7 +21,6 @@
       "devDependencies": {
         "@chainsafe/truffle-plugin-abigen": "0.0.1",
         "@openzeppelin/cli": "^2.8.2",
-        "@openzeppelin/contracts": "^4.5.0",
         "@openzeppelin/test-environment": "^0.1.9",
         "@openzeppelin/test-helpers": "^0.5.15",
         "@openzeppelin/truffle-upgrades": "^1.14.0",
@@ -29,6 +30,7 @@
         "mocha": "^8.2.1",
         "truffle": "^5.5.5",
         "truffle-assertions": "^0.9.2",
+        "truffle-deploy-registry": "^0.5.1",
         "truffle-plugin-verify": "^0.5.11"
       }
     },
@@ -2030,8 +2032,7 @@
     "node_modules/@openzeppelin/contracts": {
       "version": "4.5.0",
       "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.5.0.tgz",
-      "integrity": "sha512-fdkzKPYMjrRiPK6K4y64e6GzULR7R7RwxSigHS8DDp7aWDeoReqsQI+cxHV1UuhAqX69L1lAaWDxenfP+xiqzA==",
-      "dev": true
+      "integrity": "sha512-fdkzKPYMjrRiPK6K4y64e6GzULR7R7RwxSigHS8DDp7aWDeoReqsQI+cxHV1UuhAqX69L1lAaWDxenfP+xiqzA=="
     },
     "node_modules/@openzeppelin/contracts-upgradeable": {
       "version": "4.5.2",
@@ -3672,6 +3673,11 @@
       "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=",
       "dev": true
     },
+    "node_modules/@pythnetwork/pyth-sdk-solidity": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/@pythnetwork/pyth-sdk-solidity/-/pyth-sdk-solidity-0.0.3.tgz",
+      "integrity": "sha512-4FaoGD4Sizvdm0+8KuQsYzARHa+myNwmFE2GBYxmMcw3zMtwvD7PH0RWM8nK9UyExJ+vyAAth7SbVzyLfN4Yow=="
+    },
     "node_modules/@redux-saga/core": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/@redux-saga/core/-/core-1.1.3.tgz",
@@ -32672,6 +32678,27 @@
         "node": ">=4"
       }
     },
+    "node_modules/truffle-deploy-registry": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/truffle-deploy-registry/-/truffle-deploy-registry-0.5.1.tgz",
+      "integrity": "sha512-ZvyRpn67J67cWTfN5Z+sIryOirzxOuvpS7WO1WJYx3V1GcJR0gWWEM38KTBoPQ5L98/n7XxWC8+TtkOypKEp6A==",
+      "dev": true,
+      "dependencies": {
+        "chalk": "^2.4.2",
+        "commander": "^2.19.0",
+        "mkdirp": "^0.5.1",
+        "rimraf": "^2.6.3"
+      },
+      "bin": {
+        "apply-registry": "lib/command.js"
+      }
+    },
+    "node_modules/truffle-deploy-registry/node_modules/commander": {
+      "version": "2.20.3",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+      "dev": true
+    },
     "node_modules/truffle-error": {
       "version": "0.0.5",
       "resolved": "https://registry.npmjs.org/truffle-error/-/truffle-error-0.0.5.tgz",
@@ -38580,8 +38607,7 @@
     "@openzeppelin/contracts": {
       "version": "4.5.0",
       "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.5.0.tgz",
-      "integrity": "sha512-fdkzKPYMjrRiPK6K4y64e6GzULR7R7RwxSigHS8DDp7aWDeoReqsQI+cxHV1UuhAqX69L1lAaWDxenfP+xiqzA==",
-      "dev": true
+      "integrity": "sha512-fdkzKPYMjrRiPK6K4y64e6GzULR7R7RwxSigHS8DDp7aWDeoReqsQI+cxHV1UuhAqX69L1lAaWDxenfP+xiqzA=="
     },
     "@openzeppelin/contracts-upgradeable": {
       "version": "4.5.2",
@@ -39972,9 +39998,9 @@
       "dev": true
     },
     "@pythnetwork/pyth-sdk-solidity": {
-      "version": "0.0.2",
-      "resolved": "https://registry.npmjs.org/@pythnetwork/pyth-sdk-solidity/-/pyth-sdk-solidity-0.0.2.tgz",
-      "integrity": "sha512-/nxo+HqikNFDbi2dW/GQFJQYLjbe/PZL9x3ILxT4AdM1Nd76ykd+AK5aq08Giz6H4FGGrjsjJH4TtmgPJsUpDw=="
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/@pythnetwork/pyth-sdk-solidity/-/pyth-sdk-solidity-0.0.3.tgz",
+      "integrity": "sha512-4FaoGD4Sizvdm0+8KuQsYzARHa+myNwmFE2GBYxmMcw3zMtwvD7PH0RWM8nK9UyExJ+vyAAth7SbVzyLfN4Yow=="
     },
     "@redux-saga/core": {
       "version": "1.1.3",
@@ -66512,6 +66538,26 @@
         }
       }
     },
+    "truffle-deploy-registry": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/truffle-deploy-registry/-/truffle-deploy-registry-0.5.1.tgz",
+      "integrity": "sha512-ZvyRpn67J67cWTfN5Z+sIryOirzxOuvpS7WO1WJYx3V1GcJR0gWWEM38KTBoPQ5L98/n7XxWC8+TtkOypKEp6A==",
+      "dev": true,
+      "requires": {
+        "chalk": "^2.4.2",
+        "commander": "^2.19.0",
+        "mkdirp": "^0.5.1",
+        "rimraf": "^2.6.3"
+      },
+      "dependencies": {
+        "commander": {
+          "version": "2.20.3",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+          "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+          "dev": true
+        }
+      }
+    },
     "truffle-error": {
       "version": "0.0.5",
       "resolved": "https://registry.npmjs.org/truffle-error/-/truffle-error-0.0.5.tgz",

+ 2 - 1
ethereum/package.json

@@ -6,7 +6,6 @@
   "devDependencies": {
     "@chainsafe/truffle-plugin-abigen": "0.0.1",
     "@openzeppelin/cli": "^2.8.2",
-    "@openzeppelin/contracts": "^4.5.0",
     "@openzeppelin/test-environment": "^0.1.9",
     "@openzeppelin/test-helpers": "^0.5.15",
     "@openzeppelin/truffle-upgrades": "^1.14.0",
@@ -16,6 +15,7 @@
     "mocha": "^8.2.1",
     "truffle": "^5.5.5",
     "truffle-assertions": "^0.9.2",
+    "truffle-deploy-registry": "^0.5.1",
     "truffle-plugin-verify": "^0.5.11"
   },
   "scripts": {
@@ -30,6 +30,7 @@
   "author": "",
   "license": "ISC",
   "dependencies": {
+    "@openzeppelin/contracts": "^4.5.0",
     "@openzeppelin/contracts-upgradeable": "^4.5.2",
     "@pythnetwork/pyth-sdk-solidity": "0.0.3",
     "dotenv": "^10.0.0",

+ 5 - 3
ethereum/truffle-config.js

@@ -59,11 +59,13 @@ module.exports = {
     binance_testnet: {
       provider: () => new HDWalletProvider(
         process.env.MNEMONIC,
-        "https://data-seed-prebsc-1-s1.binance.org:8545/"
+        "https://data-seed-prebsc-1-s1.binance.org:8545"
       ),
       network_id: "97",
-      gas: 70000000,
-      gasPrice: 8000000000,
+      confirmations: 10,
+      networkCheckTimeout: 1000000,
+      timeoutBlocks: 1000,
+      skipDryRun: true,
     },
     polygon: {
       provider: () => {