diff --git a/solidity/hardhat.config.ts b/solidity/hardhat.config.ts index 5d226afa6..cf728456a 100644 --- a/solidity/hardhat.config.ts +++ b/solidity/hardhat.config.ts @@ -8,8 +8,8 @@ const config: HardhatUserConfig = { defaultNetwork: "localhost", networks: { hardhat: { - mining: { - interval: 1000 + forking: { + url: `${process.env.MAINNET_URL || "https://mainnet.infura.io/v3/infura-key"}`, } }, localhost: { diff --git a/solidity/package.json b/solidity/package.json index 8addc99d2..3c022efe1 100644 --- a/solidity/package.json +++ b/solidity/package.json @@ -34,6 +34,9 @@ "dependencies": { "@openzeppelin/contracts": "^4.8.3", "@openzeppelin/contracts-upgradeable": "4.8.3", + "@types/chai": "^4.3.9", + "@types/mocha": "^10.0.3", + "chai": "^4.3.10", "inquirer": "^8.1.0" }, "devDependencies": { diff --git a/solidity/test/fx_bridge.ts b/solidity/test/fx_bridge.ts new file mode 100644 index 000000000..1e0a6518f --- /dev/null +++ b/solidity/test/fx_bridge.ts @@ -0,0 +1,104 @@ +import {ethers, network} from "hardhat"; +import {TransactionRequest} from "@ethersproject/abstract-provider/src.ts"; +import {expect} from "chai"; + +describe("fork mainnet fx bridge test", function () { + it("update bridge contract", async function () { + const gasAddress = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + const adminAddress = "0x0F413055AdEF9b61e9507928c6856F438d690882" + const ownerAddress = "0xE77A7EA2F1DC25968b5941a456d99D37b80E98B5" + const bridgeContractAddress = "0x6f1D09Fed11115d65E1071CD2109eDb300D80A27" + + await network.provider.request({ + method: "hardhat_impersonateAccount", + params: [gasAddress], + }); + const gasSigner = ethers.provider.getSigner(gasAddress) + + await gasSigner.sendTransaction({ + to: adminAddress, + value: ethers.utils.parseEther("100") + }) + + await gasSigner.sendTransaction({ + to: ownerAddress, + value: ethers.utils.parseEther("100") + }) + + await network.provider.request({ + method: "hardhat_impersonateAccount", + params: [adminAddress], + }); + const adminSigner = ethers.provider.getSigner(adminAddress) + + await network.provider.request({ + method: "hardhat_impersonateAccount", + params: [ownerAddress], + }); + + const ownerSigner = ethers.provider.getSigner(ownerAddress) + const bridgeFactory = await ethers.getContractFactory("FxBridgeLogicETH") + + const bridgeContractV1 = bridgeFactory.attach(bridgeContractAddress) + const oldFxBridgeId = await bridgeContractV1.state_fxBridgeId() + const oldPowerThreshold = await bridgeContractV1.state_powerThreshold() + const fxAddress = await bridgeContractV1.state_fxOriginatedToken() + const oldLastEventNonce = await bridgeContractV1.state_lastEventNonce() + const oldCheckpoint = await bridgeContractV1.state_lastOracleSetCheckpoint() + const oldOracleSetNonce = await bridgeContractV1.state_lastOracleSetNonce() + const oldBridgeTokens = await bridgeContractV1.getBridgeTokenList() + + let oldBatchNonce = new Map(); + + for (const bridgeToken of oldBridgeTokens) { + const batchNonce = await bridgeContractV1.state_lastBatchNonces(bridgeToken.addr) + oldBatchNonce.set(bridgeToken.addr.toString(), batchNonce) + } + + const bridgeContract = await bridgeFactory.deploy() + await bridgeContract.deployed() + + // 0x3659cfe6 is the signature of the upgradeTo(address) function + const data = ethers.utils.hexConcat([ + '0x3659cfe6', + ethers.utils.defaultAbiCoder.encode(['address'], [bridgeContract.address]) + ]) + + const transaction: TransactionRequest = { + to: bridgeContractAddress, + data: data, + } + + const upgradeTx = await adminSigner.sendTransaction(transaction) + await upgradeTx.wait() + + const migrateTx = await bridgeContractV1.connect(ownerSigner).migrate() + await migrateTx.wait() + + const fxBridgeId = await bridgeContractV1.state_fxBridgeId() + const powerThreshold = await bridgeContractV1.state_powerThreshold() + const lastEventNonce = await bridgeContractV1.state_lastEventNonce() + const checkpoint = await bridgeContractV1.state_lastOracleSetCheckpoint() + const oracleSetNonce = await bridgeContractV1.state_lastOracleSetNonce() + const bridgeTokens = await bridgeContractV1.getBridgeTokenList() + + expect(fxBridgeId).to.equal(oldFxBridgeId) + expect(powerThreshold.toString()).to.equal(oldPowerThreshold.toString()) + expect(lastEventNonce.toString()).to.equal(oldLastEventNonce.toString()) + expect(checkpoint).to.equal(oldCheckpoint) + expect(oracleSetNonce.toString()).to.equal(oldOracleSetNonce.toString()) + + for (const bridgeToken of bridgeTokens) { + const status = await bridgeContractV1.tokenStatus(bridgeToken.addr) + if (bridgeToken.addr.toString() === fxAddress.toString()) { + expect(status.isOriginated).to.equal(true) + } else { + expect(status.isOriginated).to.equal(false) + } + expect(status.isActive).to.equal(true) + expect(status.isExist).to.equal(true) + const batchNonce = await bridgeContractV1.state_lastBatchNonces(bridgeToken.addr) + expect(batchNonce.toString()).to.equal(oldBatchNonce.get(bridgeToken.addr).toString()) + } + }); +}); \ No newline at end of file diff --git a/solidity/upgrade.md b/solidity/upgrade.md new file mode 100644 index 000000000..70e99e8bf --- /dev/null +++ b/solidity/upgrade.md @@ -0,0 +1,15 @@ +# Fx Bridge Contract Upgrade Process + +``` +1. Deploy new bridge logic contract + +npx hardhat deploy-contract --contract-name FxBridgeLogicETH --is-ledger --driver-path "m/44'/60'/0'/0/0" --network + +2. send upgradeTo + +npx hardhat send 0x6f1D09Fed11115d65E1071CD2109eDb300D80A27 "upgradeTo(address)" --driver-path "m/44'/60'/0'/0/0" --network + +3. send migrate + +npx hardhat send 0x6f1D09Fed11115d65E1071CD2109eDb300D80A27 "migrate()" --driver-path "m/44'/60'/0'/0/0" --network +``` \ No newline at end of file diff --git a/solidity/yarn.lock b/solidity/yarn.lock index 2e87b0668..fdfaad397 100644 --- a/solidity/yarn.lock +++ b/solidity/yarn.lock @@ -896,6 +896,11 @@ dependencies: "@types/node" "*" +"@types/chai@^4.3.9": + version "4.3.9" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.9.tgz#144d762491967db8c6dea38e03d2206c2623feec" + integrity sha512-69TtiDzu0bcmKQv3yg1Zx409/Kd7r0b5F1PfpYJfSHzLGtB53547V4u+9iqKYsTu/O2ai6KTb0TInNpvuQ3qmg== + "@types/inquirer@^7.3.1": version "7.3.3" resolved "https://registry.yarnpkg.com/@types/inquirer/-/inquirer-7.3.3.tgz#92e6676efb67fa6925c69a2ee638f67a822952ac" @@ -909,6 +914,11 @@ resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.1.tgz#c48c2e27b65d2a153b19bfc1a317e30872e01eef" integrity sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw== +"@types/mocha@^10.0.3": + version "10.0.3" + resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-10.0.3.tgz#4804fe9cd39da26eb62fa65c15ea77615a187812" + integrity sha512-RsOPImTriV/OE4A9qKjMtk2MnXiuLLbcO3nCXK+kvq4nr0iMfFgpjaX3MPLb6f7+EL1FGSelYvuJMV6REH+ZPQ== + "@types/node@*": version "18.15.11" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.11.tgz#b3b790f09cb1696cffcec605de025b088fa4225f" @@ -1100,6 +1110,11 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + ast-parents@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/ast-parents/-/ast-parents-0.0.1.tgz#508fd0f05d0c48775d9eccda2e174423261e8dd3" @@ -1332,6 +1347,19 @@ catering@^2.1.0, catering@^2.1.1: resolved "https://registry.yarnpkg.com/catering/-/catering-2.1.1.tgz#66acba06ed5ee28d5286133982a927de9a04b510" integrity sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w== +chai@^4.3.10: + version "4.3.10" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.10.tgz#d784cec635e3b7e2ffb66446a63b4e33bd390384" + integrity sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.3" + deep-eql "^4.1.3" + get-func-name "^2.0.2" + loupe "^2.3.6" + pathval "^1.1.1" + type-detect "^4.0.8" + chalk@^2.0.0, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -1354,6 +1382,13 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== +check-error@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694" + integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg== + dependencies: + get-func-name "^2.0.2" + chokidar@3.5.3, chokidar@^3.4.0: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" @@ -1567,6 +1602,13 @@ decompress-response@^4.2.0: dependencies: mimic-response "^2.0.0" +deep-eql@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d" + integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== + dependencies: + type-detect "^4.0.0" + deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -1938,6 +1980,11 @@ get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-func-name@^2.0.1, get-func-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" + integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== + get-intrinsic@^1.0.2: version "1.2.0" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f" @@ -2429,6 +2476,13 @@ loose-envify@^1.0.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" +loupe@^2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697" + integrity sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA== + dependencies: + get-func-name "^2.0.1" + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -2820,6 +2874,11 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pathval@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== + pbkdf2@^3.0.17: version "3.1.2" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" @@ -3462,6 +3521,11 @@ tweetnacl@^1.0.3: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== +type-detect@^4.0.0, type-detect@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + type-fest@^0.21.3: version "0.21.3" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"