diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c4ed99cf7..55b984f12 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: - name: Run tests run: yarn test:coverage - name: Upload coverage report - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v3 with: token: ${{ secrets.CODECOV_TOKEN }} files: ./coverage.json diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 85864221b..c76b7260b 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -30,4 +30,10 @@ jobs: - name: Install packages run: yarn install --non-interactive --frozen-lockfile - name: Run e2e tests - run: ADDRESS_BOOK=addresses.json yarn test:e2e + run: | + git clone https://github.com/edgeandnode/nitro + pushd nitro + git submodule update --init --recursive + ./test-node.bash --init --no-blockscout --detach + popd + L1_NETWORK=localnitrol1 L2_NETWORK=localnitrol2 yarn test:e2e \ No newline at end of file diff --git a/.gitignore b/.gitignore index 892a901cf..96fb37310 100644 --- a/.gitignore +++ b/.gitignore @@ -20,5 +20,11 @@ bin/ /reports coverage.json +# Local test files +addresses-local.json +localNetwork.json +arbitrum-addresses-local.json tx-*.log + +# Keys .keystore diff --git a/.solcover.js b/.solcover.js index 747f078e5..b10738c1f 100644 --- a/.solcover.js +++ b/.solcover.js @@ -1,4 +1,4 @@ -const skipFiles = ['bancor', 'ens', 'erc1056'] +const skipFiles = ['bancor', 'ens', 'erc1056', 'arbitrum', 'tests/arbitrum'] module.exports = { providerOptions: { diff --git a/TESTING.md b/TESTING.md index e9ac1b0e9..08b89c1a5 100644 --- a/TESTING.md +++ b/TESTING.md @@ -33,23 +33,38 @@ There are several types of e2e tests which can be run separately: - Read and write interactions with the blockchain. _Requires an account with sufficient balance!_ - Example: a test validating that a user can add signal to a subgraph. -### Hardhat local node +### Hardhat local node (L1) -To run all e2e tests against a hardhat local node run: +It can be useful to run E2E tests against a fresh protocol deployment on L1, this can be done with the following: ```bash -yarn test:e2e +L1_NETWORK=localhost yarn test:e2e ``` -The command will perform the following actions: +The command will: +- start a hardhat local node +- deploy the L1 protocol +- configure the new L1 deployment +- Run all L1 e2e tests -- Start a hardhat node (localhost) -- Run `migrate:accounts` hardhat task to create keys for all protocol roles (deployer, governor, arbiter, etc). This currently doesn't support multisig accounts. -- Run `migrate` hardhat task to deploy the protocol -- Run `migrate:ownership` hardhat task to transfer ownership of governed contracts to the governor -- Run `migrate:unpause` to unpause the protocol -- Run `e2e` hardhat task to run all deployment tests (config and init) -- Run `e2e:scenario` hardhat task to run a scenario +### Arbitrum Nitro testnodes (L1/L2) + +If you want to test the protocol on an L1/L2 setup, you can run: + +```bash +L1_NETWORK=localnitrol1 L2_NETWORK=localnitrol2 yarn test:e2e +``` + +In this case the command will: +- deploy the L1 protocol +- configure the new L1 deployment +- deploy the L2 protocol +- configure the new L2 deployment +- configure the L1/L2 bridge +- Run all L1 e2e tests +- Run all L2 e2e tests + +Note that you'll need to setup the testnodes before running the tests. See [Quick Setup](https://github.com/edgeandnode/nitro#quick-setup) for details on how to do this. ### Other networks @@ -57,13 +72,13 @@ To run tests against a live testnet or even mainnet run: ```bash # All e2e tests -npx hardhat e2e --network --graph-config config/graph..yml +ARBITRUM_ADDRESS_BOOK= npx hardhat e2e --network --l1-graph-config config/graph..yml --l2-graph-config config/graph..yml # Only deployment config tests -npx hardhat e2e:config --network --graph-config config/graph..yml +ARBITRUM_ADDRESS_BOOK= npx hardhat e2e:config --network --l1-graph-config config/graph..yml --l2-graph-config config/graph..yml # Only deployment init tests -npx hardhat e2e:init --network --graph-config config/graph..yml +ARBITRUM_ADDRESS_BOOK= npx hardhat e2e:init --network --l1-graph-config config/graph..yml --l2-graph-config config/graph..yml # Only a specific scenario npx hardhat e2e:scenario --network --graph-config config/graph..yml diff --git a/addresses.json b/addresses.json index a89a55f08..24307ea8d 100644 --- a/addresses.json +++ b/addresses.json @@ -496,6 +496,34 @@ }, "IEthereumDIDRegistry": { "address": "0xdCa7EF03e98e0DC2B855bE647C39ABe984fcF21B" + }, + "BridgeEscrow": { + "address": "0x8e4145358af77516B886D865e2EcacC0Fd832B75", + "initArgs": ["0x48eD7AfbaB432d1Fc6Ea84EEC70E745d9DAcaF3B"], + "creationCodeHash": "0xcdd28bb3db05f1267ca0f5ea29536c61841be5937ce711b813924f8ff38918cc", + "runtimeCodeHash": "0x4ca8c37c807bdfda1d6dcf441324b7ea14c6ddec5db37c20c2bf05aeae49bc0d", + "txHash": "0x190ea3c8f731a77a8fd1cbce860f9561f233adeafe559b33201b7d21ccd298cf", + "proxy": true, + "implementation": { + "address": "0xDD569E05D54fBF5d02fE4a26aC03Ea00317A0A2e", + "creationCodeHash": "0x6a1fc897c0130a1c99221cde1938d247de13a0861111ac47ad81c691f323df1a", + "runtimeCodeHash": "0xc8e31a4ebea0c3e43ceece974071ba0b6db2bed6725190795e07a2d369d2a8ab", + "txHash": "0x369038dcc8d8e70d40782dd761a82cc453c7a4f1939284c724a5a72119e3e566" + } + }, + "L1GraphTokenGateway": { + "address": "0xc82fF7b51c3e593D709BA3dE1b3a0d233D1DEca1", + "initArgs": ["0x48eD7AfbaB432d1Fc6Ea84EEC70E745d9DAcaF3B"], + "creationCodeHash": "0xcdd28bb3db05f1267ca0f5ea29536c61841be5937ce711b813924f8ff38918cc", + "runtimeCodeHash": "0x4ca8c37c807bdfda1d6dcf441324b7ea14c6ddec5db37c20c2bf05aeae49bc0d", + "txHash": "0x4a06731591df5c5f77c11bf8df7851234873eb6727fbbc93f5595a223f7cf3fc", + "proxy": true, + "implementation": { + "address": "0x06A7A68d0D0D496693508ad3f50A8EA962333B7D", + "creationCodeHash": "0x9dac8130793923c7f35f3943b755b7a88e2de9a25d0ae5c0b8cb020b6479151a", + "runtimeCodeHash": "0xcd798129b77d26c0b29369138d2d8dd413fcf6cb9b3838c5f95f50d9839a388a", + "txHash": "0xa4d75169094cd8601ec507234695d83042e888ec2ab49b0ce150d7aae908d895" + } } }, "1337": { @@ -692,5 +720,215 @@ "runtimeCodeHash": "0x07012b5692ec6cbeb7a6986e061fc5026a2f76545b07bfd9182985de002fa281", "txHash": "0xe3d870434e38ee37142a86e0fc54063df59c02c3b70135f070c3a1025c5e8246" } + }, + "421613": { + "GraphProxyAdmin": { + "address": "0x4037466bb242f51575d32E8B1be693b3E5Cd1386", + "creationCodeHash": "0x68b304ac6bce7380d5e0f6b14a122f628bffebcc75f8205cb60f0baf578b79c3", + "runtimeCodeHash": "0x8d9ba87a745cf82ab407ebabe6c1490197084d320efb6c246d94bcc80e804417", + "txHash": "0x9c4d5f8c0ab5a5bc36b0a063ab1ff04372ce7d917c0b200b94544b5da4f0230d" + }, + "BancorFormula": { + "address": "0x71319060b9fdeD6174b6368bE04F9A1b7c9aCe48", + "creationCodeHash": "0x7ae36017eddb326ddd79c7363781366121361d42fdb201cf57c57424eede46f4", + "runtimeCodeHash": "0xed6701e196ad93718e28c2a2a44d110d9e9906085bcfe4faf4f6604b08f0116c", + "txHash": "0x7fe8cabb7a4fe56311591aa8d68d6c82cb0d5c232fc5aaf28bed4d1ece0e42e5" + }, + "Controller": { + "address": "0x7f734E995010Aa8d28b912703093d532C37b6EAb", + "creationCodeHash": "0x798f913fbaa1b2547c917e3dc31679089ab27cba442c511c159803acdba28c15", + "runtimeCodeHash": "0x00ae0824f79c4e48d2d23a8d4e6d075f04f44f3ea30a4f4305c345bb98117c62", + "txHash": "0x6213da3e6367ef47cd6e1fe23e4d83296f16153a64236a5c91f865f2ec84c089" + }, + "EpochManager": { + "address": "0x8ECedc7631f4616D7f4074f9fC9D0368674794BE", + "initArgs": ["0x7f734E995010Aa8d28b912703093d532C37b6EAb", "554"], + "creationCodeHash": "0xcdd28bb3db05f1267ca0f5ea29536c61841be5937ce711b813924f8ff38918cc", + "runtimeCodeHash": "0x4ca8c37c807bdfda1d6dcf441324b7ea14c6ddec5db37c20c2bf05aeae49bc0d", + "txHash": "0x62b0d6b8556be9443397ad1f6030fdc47b1a4a3ebcc63f34cdf4091420aec84b", + "proxy": true, + "implementation": { + "address": "0xAaB195Ed1B445A2A0E357494d9036bC746227AE2", + "creationCodeHash": "0x83bc0b08dbe1a9259666ec209f06223863f7bb9cfbf917a2d4b795c771a727fe", + "runtimeCodeHash": "0xed60261c6dc84ebc16830c36f3ee370a92802601d5a2fe1c3c19f5120dcbc2eb", + "txHash": "0xd4f8780490f63432580e3dd5b2b4d9b39e904e8b4ac5cfd23540658cbafe449d" + } + }, + "L2GraphToken": { + "address": "0x18C924BD5E8b83b47EFaDD632b7178E2Fd36073D", + "initArgs": ["0xEfc519BEd6a43a14f1BBBbA9e796C4931f7A5540"], + "creationCodeHash": "0xcdd28bb3db05f1267ca0f5ea29536c61841be5937ce711b813924f8ff38918cc", + "runtimeCodeHash": "0x4ca8c37c807bdfda1d6dcf441324b7ea14c6ddec5db37c20c2bf05aeae49bc0d", + "txHash": "0x7ec14b524141af953959b537c1acbea9b49b12ee906563a6172123b09ab3d1f6", + "proxy": true, + "implementation": { + "address": "0x5dcAcF820D7b9F0640e8a23a5a857675A774C34a", + "creationCodeHash": "0x6c4146427aafa7375a569154be95c8c931bf83aab0315706dd78bdf79c889e4c", + "runtimeCodeHash": "0x004371d1d80011906953dcba17c648503fc94b94e1e0365c8d8c706ff91f93e9", + "txHash": "0xb748498a2ebc90e20dc8da981be832f4e00f08ea9ff289880738705e45d6aeca" + } + }, + "GraphCurationToken": { + "address": "0x2B757ad83e4ed51ecaE8D4dC9AdE8E3Fa29F7BdC", + "creationCodeHash": "0x1ee42ee271cefe20c33c0de904501e618ac4b56debca67c634d0564cecea9ff2", + "runtimeCodeHash": "0x340e8f378c0117b300f3ec255bc5c3a273f9ab5bd2940fa8eb3b5065b21f86dc", + "txHash": "0x1aa753cd01fa4505c71f6866dae35faee723d181141ed91b6e5cf3082ee90f9b" + }, + "ServiceRegistry": { + "address": "0x07ECDD4278D83Cd2425cA86256634f666b659e53", + "initArgs": ["0x7f734E995010Aa8d28b912703093d532C37b6EAb"], + "creationCodeHash": "0xcdd28bb3db05f1267ca0f5ea29536c61841be5937ce711b813924f8ff38918cc", + "runtimeCodeHash": "0x4ca8c37c807bdfda1d6dcf441324b7ea14c6ddec5db37c20c2bf05aeae49bc0d", + "txHash": "0x8a13420fdc91139297ab1497fbf5b443c156bbc7b9d2a1ac97fb9f23abde2723", + "proxy": true, + "implementation": { + "address": "0xd18D4B4e84eA4713E04060c93bD079A974BE6C4a", + "creationCodeHash": "0x50808e8cce93cf78a23c9e6dd7984356bd2bd93be30b358982909140dd61f6ff", + "runtimeCodeHash": "0xaef79c87f7e80107c0dc568cf1f8950459b5174ee3aa565ec487556a655e71db", + "txHash": "0x2d6043d89a5f5c4f3d0df0f50264ab7efebc898be0b5d358a00715ba9f657a89" + } + }, + "Curation": { + "address": "0x7080AAcC4ADF4b1E72615D6eb24CDdE40a04f6Ca", + "initArgs": [ + "0x7f734E995010Aa8d28b912703093d532C37b6EAb", + "0x71319060b9fdeD6174b6368bE04F9A1b7c9aCe48", + "0x2B757ad83e4ed51ecaE8D4dC9AdE8E3Fa29F7BdC", + "1000000", + "10000", + "1000000000000000000" + ], + "creationCodeHash": "0xcdd28bb3db05f1267ca0f5ea29536c61841be5937ce711b813924f8ff38918cc", + "runtimeCodeHash": "0x4ca8c37c807bdfda1d6dcf441324b7ea14c6ddec5db37c20c2bf05aeae49bc0d", + "txHash": "0x2e5744fa4eca56cf6902e27fcc0509487f39bdb0d29b9eb0181db986235289a0", + "proxy": true, + "implementation": { + "address": "0xDA6c9d39b49c3d41CaC2030c6B75b40Efea09817", + "creationCodeHash": "0xa5fa77df71a72c5aadba812345978c291c5fa1a3a23129b6eba3a38ac85d8b5d", + "runtimeCodeHash": "0x1d265e9f658778b48a0247cfef79bfc9304d1faa1f1e085f2fea85629f68e2d5", + "txHash": "0x815eda87a2599d6f2c7458c7b164e7307d05018f0dd72073a50971d424313377" + } + }, + "SubgraphNFTDescriptor": { + "address": "0x30545f313bD2eb0F85E4f808Ae4D2C016efE78b2", + "creationCodeHash": "0xf16e8ff11d852eea165195ac9e0dfa00f98e48f6ce3c77c469c7df9bf195b651", + "runtimeCodeHash": "0x39583196f2bcb85789b6e64692d8c0aa56f001c46f0ca3d371abbba2c695860f", + "txHash": "0x060839a09e89cbd47adbb8c04cc76b21a00785600a4e8b44939dd928391777e1" + }, + "SubgraphNFT": { + "address": "0x5571D8FE183AD1367dF21eE9968690f0Eabdc593", + "constructorArgs": ["0xEfc519BEd6a43a14f1BBBbA9e796C4931f7A5540"], + "creationCodeHash": "0xc1e58864302084de282dffe54c160e20dd96c6cfff45e00e6ebfc15e04136982", + "runtimeCodeHash": "0x7216e736a8a8754e88688fbf5c0c7e9caf35c55ecc3a0c5a597b951c56cf7458", + "txHash": "0xc11917ffedda6867648fa2cb62cca1df3c0ed485a0a0885284e93a2c5d33455c" + }, + "GNS": { + "address": "0x6bf9104e054537301cC23A1023Ca30A6Df79eB21", + "initArgs": [ + "0x7f734E995010Aa8d28b912703093d532C37b6EAb", + "0x71319060b9fdeD6174b6368bE04F9A1b7c9aCe48", + "0x5571D8FE183AD1367dF21eE9968690f0Eabdc593" + ], + "creationCodeHash": "0xcdd28bb3db05f1267ca0f5ea29536c61841be5937ce711b813924f8ff38918cc", + "runtimeCodeHash": "0x4ca8c37c807bdfda1d6dcf441324b7ea14c6ddec5db37c20c2bf05aeae49bc0d", + "txHash": "0x3c2509730e06249d970818319bb507185d4fdea13d5600cef87928a718950c19", + "proxy": true, + "implementation": { + "address": "0x7eCb82A9Cf9B370d3fC2Ef66E38F38EDFAeaa125", + "creationCodeHash": "0xb0be24e926bb24420bb5a8d3f7bd0b70a545fdddbf8cb177a42478adf4435aae", + "runtimeCodeHash": "0x4cb62b9def5b691e43ed06808b18efe682fcefb7739909be0d6c87f1eda724cd", + "txHash": "0xf1d41fc99ed716a0c890ea62e13ee108ddcb4ecfc74efb715a4ef05605ce449b" + } + }, + "Staking": { + "address": "0xcd549d0C43d915aEB21d3a331dEaB9B7aF186D26", + "initArgs": [ + "0x7f734E995010Aa8d28b912703093d532C37b6EAb", + "100000000000000000000000", + "6646", + "10000", + "100000", + "2", + "4", + "12", + "16", + "77", + "100" + ], + "creationCodeHash": "0xcdd28bb3db05f1267ca0f5ea29536c61841be5937ce711b813924f8ff38918cc", + "runtimeCodeHash": "0x4ca8c37c807bdfda1d6dcf441324b7ea14c6ddec5db37c20c2bf05aeae49bc0d", + "txHash": "0xc98ebdd0a80b97ef8f6305903ef6496a7781db76a5b1b3c3c3b2b10dbd9a7af5", + "proxy": true, + "implementation": { + "address": "0x8E56ee65Ed613f2AecA8898D19497D288601bdeb", + "creationCodeHash": "0x75b63ef816627315c635cae7f95917764e2cb797496280cdeaa9b3230bf7f7bc", + "runtimeCodeHash": "0x461ccf91c7c6188c94c6df430b6954dfd9c5cc2a79a5e4db21422e11b663d319", + "txHash": "0xb9ce53dafab3dcaad25b24d9f998888225103265bd2d84cb1545b4e06e96e3b6", + "libraries": { + "LibCobbDouglas": "0x86f0f6cd9a38A851E3AB8f110be06B77C199eC1F" + } + } + }, + "RewardsManager": { + "address": "0x5F06ABd1CfAcF7AE99530D7Fed60E085f0B15e8D", + "initArgs": ["0x7f734E995010Aa8d28b912703093d532C37b6EAb"], + "creationCodeHash": "0xcdd28bb3db05f1267ca0f5ea29536c61841be5937ce711b813924f8ff38918cc", + "runtimeCodeHash": "0x4ca8c37c807bdfda1d6dcf441324b7ea14c6ddec5db37c20c2bf05aeae49bc0d", + "txHash": "0xd4cfa95475e9e867fb24babd6a00a5b6b01d2267533e2412986aa1ff94d51c02", + "proxy": true, + "implementation": { + "address": "0x80b54Ba64d8a207785969d9ae0dA984EfE8D10dF", + "creationCodeHash": "0x98aaabec491a17401ca37209db0613c91285de061e859574526f841a4dd60c4a", + "runtimeCodeHash": "0x2795a83531898957014373bd4595f1f9a381ecfaf787bdfc64380563af06f06a", + "txHash": "0xb4bc7ae32ec98394c448f8773bdd3049ab83e236acb6823a7a322d88ecfbfd99" + } + }, + "DisputeManager": { + "address": "0x16DEF7E0108A5467A106dbD7537f8591f470342E", + "initArgs": [ + "0x7f734E995010Aa8d28b912703093d532C37b6EAb", + "0xF89688d5d44d73cc4dE880857A3940487076e5A4", + "10000000000000000000000", + "500000", + "25000", + "25000" + ], + "creationCodeHash": "0xcdd28bb3db05f1267ca0f5ea29536c61841be5937ce711b813924f8ff38918cc", + "runtimeCodeHash": "0x4ca8c37c807bdfda1d6dcf441324b7ea14c6ddec5db37c20c2bf05aeae49bc0d", + "txHash": "0x70188c9243c2226ac793ac8c0a9eecd76c9b44e53f7f6f97fa177a34808421a0", + "proxy": true, + "implementation": { + "address": "0x39aEdA1d6ea3B62b76C7c439beBfFCb5369a175C", + "creationCodeHash": "0x2e77ad7a1627b6e04bece0fe18b3ab543ef4a2d6914f2e5e640b2c8175aca3a8", + "runtimeCodeHash": "0x0186afe711eff4ceea28620d091e3c6034fd15be05894119c74a38b020e3a554", + "txHash": "0x4efbd28e55866c0292309964f47bd805922ad417e5980e14e055ad693024582d" + } + }, + "AllocationExchange": { + "address": "0x61809D6Cde07f27D2fcDCb67a42d0Af1988Be5e8", + "constructorArgs": [ + "0x18C924BD5E8b83b47EFaDD632b7178E2Fd36073D", + "0xcd549d0C43d915aEB21d3a331dEaB9B7aF186D26", + "0x05F359b1319f1Ca9b799CB6386F31421c2c49dBA", + "0xD06f366678AE139a94b2AaC2913608De568F1D03" + ], + "creationCodeHash": "0x96c5b59557c161d80f1617775a7b9537a89b0ecf2258598b3a37724be91ae80a", + "runtimeCodeHash": "0xed3d9cce65ddfa8a237d4d7d294ffdb13a082e0adcda3bbd313029cfae1365f3", + "txHash": "0x1df63329a21dca69d20e03c076dd89c350970d35319eeefab028cebbc78d29dc" + }, + "L2GraphTokenGateway": { + "address": "0xef2757855d2802bA53733901F90C91645973f743", + "initArgs": ["0x7f734E995010Aa8d28b912703093d532C37b6EAb"], + "creationCodeHash": "0xcdd28bb3db05f1267ca0f5ea29536c61841be5937ce711b813924f8ff38918cc", + "runtimeCodeHash": "0x4ca8c37c807bdfda1d6dcf441324b7ea14c6ddec5db37c20c2bf05aeae49bc0d", + "txHash": "0x47bde4e3ad0bc077897a3de65058c4b7dd710aa447ec25942f716321cbdc590d", + "proxy": true, + "implementation": { + "address": "0xc68cd0d2ca533232Fd86D6e48b907338B2E0a74A", + "creationCodeHash": "0xbd52455bd8b14bfc27af623388fe2f9e06ddd4c4be3fc06c51558a912de91770", + "runtimeCodeHash": "0x29e47f693053f978d6b2ac0a327319591bf5b5e8a6e6c0744b8afcc0250bf667", + "txHash": "0xf68a5e1e516ee9a646f19bbe4d58336fdfcf5fc859f84cdac5e68b00bcd3a09a" + } + } } } diff --git a/arbitrum-addresses.json b/arbitrum-addresses.json new file mode 100644 index 000000000..4249270ad --- /dev/null +++ b/arbitrum-addresses.json @@ -0,0 +1,42 @@ +{ + "source": "https://developer.offchainlabs.com/docs/useful_addresses", + "1": { + "L1GatewayRouter": { + "address": "0x72Ce9c846789fdB6fC1f34aC4AD25Dd9ef7031ef" + }, + "IInbox": { + "address": "0x4Dbd4fc535Ac27206064B68FfCf827b0A60BAB3f" + } + }, + "4": { + "L1GatewayRouter": { + "address": "0x70C143928eCfFaf9F5b406f7f4fC28Dc43d68380" + }, + "IInbox": { + "address": "0x578BAde599406A8fE3d24Fd7f7211c0911F5B29e" + } + }, + "5": { + "L1GatewayRouter": { + "address": "0x4c7708168395aEa569453Fc36862D2ffcDaC588c" + }, + "IInbox": { + "address": "0x6BEbC4925716945D46F0Ec336D5C2564F419682C" + } + }, + "42161": { + "L2GatewayRouter": { + "address": "0x5288c571Fd7aD117beA99bF60FE0846C4E84F933" + } + }, + "421611": { + "L2GatewayRouter": { + "address": "0x9413AD42910c1eA60c737dB5f58d1C504498a3cD" + } + }, + "421613": { + "L2GatewayRouter": { + "address": "0xE5B9d8d42d656d1DcB8065A6c012FE3780246041" + } + } +} diff --git a/cli/address-book.ts b/cli/address-book.ts index 39818c82a..5c938fe2a 100644 --- a/cli/address-book.ts +++ b/cli/address-book.ts @@ -28,7 +28,7 @@ export interface AddressBook { } export const getAddressBook = (path: string, chainId: string): AddressBook => { - if (!path) throw new Error(`A path the the address book file is required.`) + if (!path) throw new Error(`A path to the address book file is required.`) if (!chainId) throw new Error(`A chainId is required.`) const addressBook = JSON.parse(fs.readFileSync(path, 'utf8') || '{}') as AddressBookJson diff --git a/cli/cli.ts b/cli/cli.ts index a9e754536..ab2449679 100755 --- a/cli/cli.ts +++ b/cli/cli.ts @@ -8,6 +8,7 @@ import { proxyCommand } from './commands/proxy' import { protocolCommand } from './commands/protocol' import { contractsCommand } from './commands/contracts' import { airdropCommand } from './commands/airdrop' +import { bridgeCommand } from './commands/bridge' import { cliOpts } from './defaults' @@ -28,11 +29,13 @@ yargs .option('p', cliOpts.providerUrl) .option('n', cliOpts.accountNumber) .option('s', cliOpts.skipConfirmation) + .option('r', cliOpts.arbitrumAddressBook) .command(deployCommand) .command(migrateCommand) .command(proxyCommand) .command(protocolCommand) .command(contractsCommand) .command(airdropCommand) + .command(bridgeCommand) .demandCommand(1, 'Choose a command from the above list') .help().argv diff --git a/cli/commands/bridge/index.ts b/cli/commands/bridge/index.ts new file mode 100644 index 000000000..fcdf1bcc1 --- /dev/null +++ b/cli/commands/bridge/index.ts @@ -0,0 +1,22 @@ +import yargs, { Argv } from 'yargs' + +import { redeemSendToL2Command, sendToL2Command } from './to-l2' +import { startSendToL1Command, finishSendToL1Command, waitFinishSendToL1Command } from './to-l1' +import { cliOpts } from '../../defaults' + +export const bridgeCommand = { + command: 'bridge', + describe: 'Graph token bridge actions.', + builder: (yargs: Argv): yargs.Argv => { + return yargs + .option('-l', cliOpts.l2ProviderUrl) + .command(sendToL2Command) + .command(redeemSendToL2Command) + .command(startSendToL1Command) + .command(finishSendToL1Command) + .command(waitFinishSendToL1Command) + }, + handler: (): void => { + yargs.showHelp() + }, +} diff --git a/cli/commands/bridge/to-l1.ts b/cli/commands/bridge/to-l1.ts new file mode 100644 index 000000000..0913aa2e1 --- /dev/null +++ b/cli/commands/bridge/to-l1.ts @@ -0,0 +1,216 @@ +import { loadEnv, CLIArgs, CLIEnvironment } from '../../env' +import { logger } from '../../logging' +import { getAddressBook } from '../../address-book' +import { getProvider, sendTransaction, toGRT } from '../../network' +import { chainIdIsL2 } from '../../cross-chain' +import { loadAddressBookContract } from '../../contracts' +import { L2TransactionReceipt, L2ToL1MessageStatus, L2ToL1MessageWriter } from '@arbitrum/sdk' +import { L2GraphTokenGateway } from '../../../build/types/L2GraphTokenGateway' +import { BigNumber } from 'ethers' +import { JsonRpcProvider } from '@ethersproject/providers' +import { providers } from 'ethers' +import { L2GraphToken } from '../../../build/types/L2GraphToken' + +const FOURTEEN_DAYS_IN_SECONDS = 24 * 3600 * 14 + +const BLOCK_SEARCH_THRESHOLD = 6 * 3600 +const searchForArbBlockByTimestamp = async ( + l2Provider: JsonRpcProvider, + timestamp: number, +): Promise => { + let step = 131072 + let block = await l2Provider.getBlock('latest') + while (block.timestamp > timestamp) { + while (block.number - step < 0) { + step = Math.round(step / 2) + } + block = await l2Provider.getBlock(block.number - step) + } + while (step > 1 && Math.abs(block.timestamp - timestamp) > BLOCK_SEARCH_THRESHOLD) { + step = Math.round(step / 2) + if (block.timestamp - timestamp > 0) { + block = await l2Provider.getBlock(block.number - step) + } else { + block = await l2Provider.getBlock(block.number + step) + } + } + return block.number +} + +const wait = (ms: number): Promise => { + return new Promise((res) => setTimeout(res, ms)) +} + +const waitUntilOutboxEntryCreatedWithCb = async ( + msg: L2ToL1MessageWriter, + provider: providers.Provider, + retryDelay: number, + callback: () => void, +) => { + let done = false + while (!done) { + const status = await msg.status(provider) + if (status == L2ToL1MessageStatus.CONFIRMED || status == L2ToL1MessageStatus.EXECUTED) { + done = true + } else { + callback() + await wait(retryDelay) + } + } +} + +export const startSendToL1 = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise => { + logger.info(`>>> Sending tokens to L1 <<<\n`) + const l2Provider = getProvider(cliArgs.l2ProviderUrl) + const l2ChainId = (await l2Provider.getNetwork()).chainId + + if (chainIdIsL2(cli.chainId) || !chainIdIsL2(l2ChainId)) { + throw new Error( + 'Please use an L1 provider in --provider-url, and an L2 provider in --l2-provider-url', + ) + } + + const l1GRT = cli.contracts['GraphToken'] + const l1GRTAddress = l1GRT.address + const amount = toGRT(cliArgs.amount) + const recipient = cliArgs.recipient ? cliArgs.recipient : cli.wallet.address + const l2Wallet = cli.wallet.connect(l2Provider) + const l2AddressBook = getAddressBook(cliArgs.addressBook, l2ChainId.toString()) + + const gateway = loadAddressBookContract('L2GraphTokenGateway', l2AddressBook, l2Wallet) + const l2GRT = loadAddressBookContract('L2GraphToken', l2AddressBook, l2Wallet) as L2GraphToken + + const l1Gateway = cli.contracts['L1GraphTokenGateway'] + logger.info(`Will send ${cliArgs.amount} GRT to ${recipient}`) + logger.info(`Using L2 gateway ${gateway.address} and L1 gateway ${l1Gateway.address}`) + + const senderBalance = await l2GRT.balanceOf(cli.wallet.address) + if (senderBalance.lt(amount)) { + throw new Error('Sender balance is insufficient for the transfer') + } + + const params = [l1GRTAddress, recipient, amount, '0x'] + logger.info('Approving token transfer') + await sendTransaction(l2Wallet, l2GRT, 'approve', [gateway.address, amount]) + logger.info('Sending outbound transfer transaction') + const receipt = await sendTransaction( + l2Wallet, + gateway, + 'outboundTransfer(address,address,uint256,bytes)', + params, + ) + const l2Receipt = new L2TransactionReceipt(receipt) + const l2ToL1Message = (await l2Receipt.getL2ToL1Messages(cli.wallet))[0] + + const ethBlockNum = l2ToL1Message.getFirstExecutableBlock(l2Provider) + if (ethBlockNum === null) { + logger.info(`L2 to L1 message can or already has been executed. If not finalized call`) + } else { + logger.info(`The transaction generated an L2 to L1 message in outbox with eth block number:`) + logger.info(ethBlockNum.toString()) + logger.info( + `After the dispute period is finalized (in ~1 week), you can finalize this by calling`, + ) + } + logger.info(`finish-send-to-l1 with the following txhash:`) + logger.info(l2Receipt.transactionHash) +} + +export const finishSendToL1 = async ( + cli: CLIEnvironment, + cliArgs: CLIArgs, + wait: boolean, +): Promise => { + logger.info(`>>> Finishing transaction sending tokens to L1 <<<\n`) + const l2Provider = getProvider(cliArgs.l2ProviderUrl) + const l2ChainId = (await l2Provider.getNetwork()).chainId + + if (chainIdIsL2(cli.chainId) || !chainIdIsL2(l2ChainId)) { + throw new Error( + 'Please use an L1 provider in --provider-url, and an L2 provider in --l2-provider-url', + ) + } + + const l2AddressBook = getAddressBook(cliArgs.addressBook, l2ChainId.toString()) + + const gateway = loadAddressBookContract( + 'L2GraphTokenGateway', + l2AddressBook, + l2Provider, + ) as L2GraphTokenGateway + let txHash: string + if (cliArgs.txHash) { + txHash = cliArgs.txHash + } else { + logger.info( + `Looking for withdrawals initiated by ${cli.wallet.address} in roughly the last 14 days`, + ) + const fromBlock = await searchForArbBlockByTimestamp( + l2Provider, + Math.round(Date.now() / 1000) - FOURTEEN_DAYS_IN_SECONDS, + ) + const filt = gateway.filters.WithdrawalInitiated(null, cli.wallet.address) + const allEvents = await gateway.queryFilter(filt, BigNumber.from(fromBlock).toHexString()) + if (allEvents.length == 0) { + throw new Error('No withdrawals found') + } + txHash = allEvents[allEvents.length - 1].transactionHash + } + logger.info(`Getting receipt from transaction ${txHash}`) + const receipt = await l2Provider.getTransactionReceipt(txHash) + + const l2Receipt = new L2TransactionReceipt(receipt) + logger.info(`Getting L2 to L1 message...`) + const l2ToL1Message = (await l2Receipt.getL2ToL1Messages(cli.wallet))[0] + + if (wait) { + const retryDelayMs = cliArgs.retryDelaySeconds ? cliArgs.retryDelaySeconds * 1000 : 60000 + logger.info('Waiting for outbox entry to be created, this can take a full week...') + await waitUntilOutboxEntryCreatedWithCb(l2ToL1Message, l2Provider, retryDelayMs, () => { + logger.info('Still waiting...') + }) + } else { + const status = await l2ToL1Message.status(l2Provider) + if (status == L2ToL1MessageStatus.EXECUTED) { + throw new Error('Message already executed!') + } else if (status != L2ToL1MessageStatus.CONFIRMED) { + throw new Error( + `Transaction is not confirmed, status is ${status} when it should be ${L2ToL1MessageStatus.CONFIRMED}. Has the dispute period passed?`, + ) + } + } + + logger.info('Executing outbox transaction') + const tx = await l2ToL1Message.execute(l2Provider) + const outboxExecuteReceipt = await tx.wait() + logger.info('Transaction succeeded! tx hash:') + logger.info(outboxExecuteReceipt.transactionHash) +} + +export const startSendToL1Command = { + command: 'start-send-to-l1 [recipient]', + describe: 'Start an L2-to-L1 Graph Token transaction', + handler: async (argv: CLIArgs): Promise => { + return startSendToL1(await loadEnv(argv), argv) + }, +} + +export const finishSendToL1Command = { + command: 'finish-send-to-l1 [txHash]', + describe: + 'Finish an L2-to-L1 Graph Token transaction. L2 dispute period must have completed. ' + + 'If txHash is not specified, the last withdrawal from the main account in the past 14 days will be redeemed.', + handler: async (argv: CLIArgs): Promise => { + return finishSendToL1(await loadEnv(argv), argv, false) + }, +} + +export const waitFinishSendToL1Command = { + command: 'wait-finish-send-to-l1 [txHash] [retryDelaySeconds]', + describe: + "Wait for an L2-to-L1 Graph Token transaction's dispute period to complete (which takes about a week), and then finalize it. " + + 'If txHash is not specified, the last withdrawal from the main account in the past 14 days will be redeemed.', + handler: async (argv: CLIArgs): Promise => { + return finishSendToL1(await loadEnv(argv), argv, true) + }, +} diff --git a/cli/commands/bridge/to-l2.ts b/cli/commands/bridge/to-l2.ts new file mode 100644 index 000000000..01b3f65bc --- /dev/null +++ b/cli/commands/bridge/to-l2.ts @@ -0,0 +1,187 @@ +import { Argv } from 'yargs' +import { utils } from 'ethers' +import { L1TransactionReceipt, L1ToL2MessageStatus, L1ToL2MessageWriter } from '@arbitrum/sdk' + +import { loadEnv, CLIArgs, CLIEnvironment } from '../../env' +import { logger } from '../../logging' +import { getProvider, sendTransaction, toGRT, ensureAllowance, toBN } from '../../network' +import { chainIdIsL2, estimateRetryableTxGas } from '../../cross-chain' + +const logAutoRedeemReason = (autoRedeemRec) => { + if (autoRedeemRec == null) { + logger.info(`Auto redeem was not attempted.`) + return + } + logger.info(`Auto redeem reverted.`) +} + +const checkAndRedeemMessage = async (l1ToL2Message: L1ToL2MessageWriter) => { + logger.info(`Waiting for status of ${l1ToL2Message.retryableCreationId}`) + const res = await l1ToL2Message.waitForStatus() + logger.info('Getting auto redeem attempt') + const autoRedeemRec = await l1ToL2Message.getAutoRedeemAttempt() + const l2TxReceipt = res.status === L1ToL2MessageStatus.REDEEMED ? res.l2TxReceipt : autoRedeemRec + let l2TxHash = l2TxReceipt ? l2TxReceipt.transactionHash : 'null' + if (res.status === L1ToL2MessageStatus.FUNDS_DEPOSITED_ON_L2) { + /** Message wasn't auto-redeemed! */ + logger.warn('Funds were deposited on L2 but the retryable ticket was not redeemed') + logAutoRedeemReason(autoRedeemRec) + logger.info('Attempting to redeem...') + await l1ToL2Message.redeem(process.env.CI ? { gasLimit: 2_000_000 } : {}) + const redeemAttempt = await l1ToL2Message.getSuccessfulRedeem() + if (redeemAttempt.status == L1ToL2MessageStatus.REDEEMED) { + l2TxHash = redeemAttempt.l2TxReceipt ? redeemAttempt.l2TxReceipt.transactionHash : 'null' + } else { + throw new Error(`Unexpected L1ToL2MessageStatus after redeem attempt: ${res.status}`) + } + } else if (res.status != L1ToL2MessageStatus.REDEEMED) { + throw new Error(`Unexpected L1ToL2MessageStatus ${res.status}`) + } + logger.info(`Transfer successful: ${l2TxHash}`) +} + +export const sendToL2 = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise => { + logger.info(`>>> Sending tokens to L2 <<<\n`) + + // parse provider + const l1Provider = cli.wallet.provider + // TODO: fix this hack for usage with hardhat + const l2Provider = cliArgs.l2Provider ? cliArgs.l2Provider : getProvider(cliArgs.l2ProviderUrl) + const l1ChainId = cli.chainId + const l2ChainId = (await l2Provider.getNetwork()).chainId + if (chainIdIsL2(l1ChainId) || !chainIdIsL2(l2ChainId)) { + throw new Error( + 'Please use an L1 provider in --provider-url, and an L2 provider in --l2-provider-url', + ) + } + + // parse params + const { L1GraphTokenGateway: l1Gateway, GraphToken: l1GRT } = cli.contracts + const amount = toGRT(cliArgs.amount) + const recipient = cliArgs.recipient ?? cli.wallet.address + const l1GatewayAddress = l1Gateway.address + const l2GatewayAddress = await l1Gateway.l2Counterpart() + const calldata = cliArgs.calldata ?? '0x' + + // transport tokens + logger.info(`Will send ${cliArgs.amount} GRT to ${recipient}`) + logger.info(`Using L1 gateway ${l1GatewayAddress} and L2 gateway ${l2GatewayAddress}`) + await ensureAllowance(cli.wallet, l1GatewayAddress, l1GRT, amount) + + // estimate L2 ticket + // See https://github.com/OffchainLabs/arbitrum/blob/master/packages/arb-ts/src/lib/bridge.ts + const depositCalldata = await l1Gateway.getOutboundCalldata( + l1GRT.address, + cli.wallet.address, + recipient, + amount, + calldata, + ) + const { maxGas, gasPriceBid, maxSubmissionCost } = await estimateRetryableTxGas( + l1Provider, + l2Provider, + l1GatewayAddress, + l2GatewayAddress, + depositCalldata, + { + maxGas: cliArgs.maxGas, + gasPriceBid: cliArgs.gasPriceBid, + maxSubmissionCost: cliArgs.maxSubmissionCost, + }, + ) + const ethValue = maxSubmissionCost.add(gasPriceBid.mul(maxGas)) + logger.info( + `Using maxGas:${maxGas}, gasPriceBid:${gasPriceBid}, maxSubmissionCost:${maxSubmissionCost} = tx value: ${ethValue}`, + ) + + // build transaction + logger.info('Sending outbound transfer transaction') + const txData = utils.defaultAbiCoder.encode(['uint256', 'bytes'], [maxSubmissionCost, calldata]) + const txParams = [l1GRT.address, recipient, amount, maxGas, gasPriceBid, txData] + const txReceipt = await sendTransaction(cli.wallet, l1Gateway, 'outboundTransfer', txParams, { + value: ethValue, + }) + + // get l2 ticket status + if (txReceipt.status == 1) { + logger.info('Waiting for message to propagate to L2...') + const l1Receipt = new L1TransactionReceipt(txReceipt) + const l1ToL2Messages = await l1Receipt.getL1ToL2Messages(cli.wallet.connect(l2Provider)) + const l1ToL2Message = l1ToL2Messages[0] + try { + await checkAndRedeemMessage(l1ToL2Message) + } catch (e) { + logger.error('Auto redeem failed') + logger.error(e) + logger.error('You can re-attempt using redeem-send-to-l2 with the following txHash:') + logger.error(txReceipt.transactionHash) + } + } +} + +export const redeemSendToL2 = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise => { + logger.info(`>>> Redeeming pending tokens on L2 <<<\n`) + const l2Provider = getProvider(cliArgs.l2ProviderUrl) + const l2ChainId = (await l2Provider.getNetwork()).chainId + + if (chainIdIsL2(cli.chainId) || !chainIdIsL2(l2ChainId)) { + throw new Error( + 'Please use an L1 provider in --provider-url, and an L2 provider in --l2-provider-url', + ) + } + const l1Provider = cli.wallet.provider + + const receipt = await l1Provider.getTransactionReceipt(cliArgs.txHash) + const l1Receipt = new L1TransactionReceipt(receipt) + const l1ToL2Messages = await l1Receipt.getL1ToL2Messages(cli.wallet.connect(l2Provider)) + const l1ToL2Message = l1ToL2Messages[0] + + logger.info('Checking message status in L2...') + await checkAndRedeemMessage(l1ToL2Message) +} + +export const sendToL2Command = { + command: 'send-to-l2 [recipient] [calldata]', + describe: 'Perform an L1-to-L2 Graph Token transaction', + builder: (yargs: Argv): Argv => { + return yargs + .option('max-gas', { + description: 'Max gas for the L2 redemption attempt', + requiresArg: true, + type: 'string', + }) + .option('gas-price-bid', { + description: 'Gas price for the L2 redemption attempt', + requiresArg: true, + type: 'string', + }) + .option('max-submission-cost', { + description: 'Max submission cost for the retryable ticket', + requiresArg: true, + type: 'string', + }) + .positional('amount', { description: 'Amount to send (will be converted to wei)' }) + .positional('recipient', { + description: 'Receiving address in L2. Same to L1 address if empty', + }) + .positional('calldata', { + description: 'Calldata to pass to the recipient. Must be allowlisted in the bridge', + }) + .coerce({ + maxGas: toBN, + gasPriceBid: toBN, + maxSubmissionCost: toBN, + }) + }, + handler: async (argv: CLIArgs): Promise => { + return sendToL2(await loadEnv(argv), argv) + }, +} + +export const redeemSendToL2Command = { + command: 'redeem-send-to-l2 ', + describe: 'Finish an L1-to-L2 Graph Token transaction if it failed to auto-redeem', + handler: async (argv: CLIArgs): Promise => { + return redeemSendToL2(await loadEnv(argv), argv) + }, +} diff --git a/cli/commands/contracts/disputeManager.ts b/cli/commands/contracts/disputeManager.ts index 957df9554..88d0abce1 100644 --- a/cli/commands/contracts/disputeManager.ts +++ b/cli/commands/contracts/disputeManager.ts @@ -3,8 +3,9 @@ import { constants, utils, Wallet } from 'ethers' import { createAttestation, Attestation, Receipt } from '@graphprotocol/common-ts' import { logger } from '../../logging' -import { sendTransaction, getChainID, getProvider, toGRT, randomHexBytes } from '../../network' +import { sendTransaction, getProvider, toGRT, randomHexBytes } from '../../network' import { loadEnv, CLIArgs, CLIEnvironment } from '../../env' +import { getChainID } from '../../network' const { HashZero } = constants const { defaultAbiCoder: abi, arrayify, concat, hexlify } = utils diff --git a/cli/commands/migrate.ts b/cli/commands/migrate.ts index b893c6e63..335b6fa32 100644 --- a/cli/commands/migrate.ts +++ b/cli/commands/migrate.ts @@ -11,13 +11,14 @@ import { sendTransaction, } from '../network' import { loadEnv, CLIArgs, CLIEnvironment } from '../env' +import { chainIdIsL2 } from '../cross-chain' import { confirm } from '../helpers' const { EtherSymbol } = constants const { formatEther } = utils // Contracts are deployed in the order defined in this list -const allContracts = [ +let allContracts = [ 'GraphProxyAdmin', 'BancorFormula', 'Controller', @@ -33,6 +34,27 @@ const allContracts = [ 'RewardsManager', 'DisputeManager', 'AllocationExchange', + 'L1GraphTokenGateway', + 'BridgeEscrow', +] + +const l2Contracts = [ + 'GraphProxyAdmin', + 'BancorFormula', + 'Controller', + 'EpochManager', + 'L2GraphToken', + 'GraphCurationToken', + 'ServiceRegistry', + 'Curation', + 'SubgraphNFTDescriptor', + 'SubgraphNFT', + 'GNS', + 'Staking', + 'RewardsManager', + 'DisputeManager', + 'AllocationExchange', + 'L2GraphTokenGateway', ] export const migrate = async ( @@ -51,7 +73,10 @@ export const migrate = async ( if (!sure) return if (chainId == 1337) { - await (cli.wallet.provider as providers.JsonRpcProvider).send('evm_setAutomine', [true]) + allContracts = ['EthereumDIDRegistry', ...allContracts] + await setAutoMine(cli.wallet.provider as providers.JsonRpcProvider, true) + } else if (chainIdIsL2(chainId)) { + allContracts = l2Contracts } logger.info(`>>> Migrating contracts <<<\n`) @@ -144,7 +169,15 @@ export const migrate = async ( logger.info(`Sent ${nTx} transaction${nTx === 1 ? '' : 's'} & spent ${EtherSymbol} ${spent}`) if (chainId == 1337) { - await (cli.wallet.provider as providers.JsonRpcProvider).send('evm_setAutomine', [autoMine]) + await setAutoMine(cli.wallet.provider as providers.JsonRpcProvider, autoMine) + } +} + +const setAutoMine = async (provider: providers.JsonRpcProvider, automine: boolean) => { + try { + await provider.send('evm_setAutomine', [automine]) + } catch (error) { + logger.warn('The method evm_setAutomine does not exist/is not available!') } } diff --git a/cli/commands/protocol/configure-bridge.ts b/cli/commands/protocol/configure-bridge.ts new file mode 100644 index 000000000..057ebc653 --- /dev/null +++ b/cli/commands/protocol/configure-bridge.ts @@ -0,0 +1,89 @@ +import { loadEnv, CLIArgs, CLIEnvironment } from '../../env' +import { logger } from '../../logging' +import { getAddressBook } from '../../address-book' +import { sendTransaction } from '../../network' +import { chainIdIsL2, l1ToL2ChainIdMap, l2ToL1ChainIdMap } from '../../cross-chain' + +export const configureL1Bridge = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise => { + logger.info(`>>> Setting L1 Bridge Configuration <<<\n`) + + if (chainIdIsL2(cli.chainId)) { + throw new Error('Cannot set L1 configuration on an L2 network!') + } + const l2ChainId = cliArgs.l2ChainId ? cliArgs.l2ChainId : l1ToL2ChainIdMap[cli.chainId] + logger.info('Connecting with the contracts on L2 chainId ' + l2ChainId) + const l2AddressBook = getAddressBook(cliArgs.addressBook, l2ChainId) + const arbAddressBook = getAddressBook(cliArgs.arbAddressBook, cli.chainId.toString()) + + const gateway = cli.contracts['L1GraphTokenGateway'] + + const l2GRT = l2AddressBook.getEntry('L2GraphToken') + logger.info('L2 GRT address: ' + l2GRT.address) + await sendTransaction(cli.wallet, gateway, 'setL2TokenAddress', [l2GRT.address]) + + const l2Counterpart = l2AddressBook.getEntry('L2GraphTokenGateway') + logger.info('L2 Gateway address: ' + l2Counterpart.address) + await sendTransaction(cli.wallet, gateway, 'setL2CounterpartAddress', [l2Counterpart.address]) + + const bridgeEscrow = cli.contracts.BridgeEscrow + logger.info('Escrow address: ' + bridgeEscrow.address) + await sendTransaction(cli.wallet, gateway, 'setEscrowAddress', [bridgeEscrow.address]) + await sendTransaction(cli.wallet, bridgeEscrow, 'approveAll', [gateway.address]) + + const l1Inbox = arbAddressBook.getEntry('IInbox') + const l1Router = arbAddressBook.getEntry('L1GatewayRouter') + logger.info( + 'L1 Inbox address: ' + l1Inbox.address + ' and L1 Router address: ' + l1Router.address, + ) + await sendTransaction(cli.wallet, gateway, 'setArbitrumAddresses', [ + l1Inbox.address, + l1Router.address, + ]) +} + +export const configureL2Bridge = async (cli: CLIEnvironment, cliArgs: CLIArgs): Promise => { + logger.info(`>>> Setting L2 Bridge Configuration <<<\n`) + + if (!chainIdIsL2(cli.chainId)) { + throw new Error('Cannot set L2 configuration on an L1 network!') + } + const l1ChainId = cliArgs.l1ChainId ? cliArgs.l1ChainId : l2ToL1ChainIdMap[cli.chainId] + logger.info('Connecting with the contracts on L1 chainId ' + l1ChainId) + const l1AddressBook = getAddressBook(cliArgs.addressBook, l1ChainId) + const arbAddressBook = getAddressBook(cliArgs.arbAddressBook, cli.chainId.toString()) + + const gateway = cli.contracts['L2GraphTokenGateway'] + const token = cli.contracts['L2GraphToken'] + + const l1GRT = l1AddressBook.getEntry('GraphToken') + logger.info('L1 GRT address: ' + l1GRT.address) + await sendTransaction(cli.wallet, gateway, 'setL1TokenAddress', [l1GRT.address]) + await sendTransaction(cli.wallet, token, 'setL1Address', [l1GRT.address]) + + const l1Counterpart = l1AddressBook.getEntry('L1GraphTokenGateway') + logger.info('L1 Gateway address: ' + l1Counterpart.address) + await sendTransaction(cli.wallet, gateway, 'setL1CounterpartAddress', [l1Counterpart.address]) + + const l2Router = arbAddressBook.getEntry('L2GatewayRouter') + logger.info('L2 Router address: ' + l2Router.address) + await sendTransaction(cli.wallet, gateway, 'setL2Router', [l2Router.address]) + + logger.info('L2 Gateway address: ' + gateway.address) + await sendTransaction(cli.wallet, token, 'setGateway', [gateway.address]) +} + +export const configureL1BridgeCommand = { + command: 'configure-l1-bridge [l2ChainId]', + describe: 'Configure L1/L2 bridge parameters (L1 side) using the address book', + handler: async (argv: CLIArgs): Promise => { + return configureL1Bridge(await loadEnv(argv), argv) + }, +} + +export const configureL2BridgeCommand = { + command: 'configure-l2-bridge [l1ChainId]', + describe: 'Configure L1/L2 bridge parameters (L2 side) using the address book', + handler: async (argv: CLIArgs): Promise => { + return configureL2Bridge(await loadEnv(argv), argv) + }, +} diff --git a/cli/commands/protocol/get.ts b/cli/commands/protocol/get.ts index 27932d209..439b539b4 100644 --- a/cli/commands/protocol/get.ts +++ b/cli/commands/protocol/get.ts @@ -51,6 +51,18 @@ export const gettersList = { 'controller-get-paused': { contract: 'Controller', name: 'paused' }, 'controller-get-partial-paused': { contract: 'Controller', name: 'partialPaused' }, 'controller-get-pause-guardian': { contract: 'Controller', name: 'pauseGuardian' }, + 'l1-gateway-l2-grt': { contract: 'L1GraphTokenGateway', name: 'l2GRT' }, + 'l1-gateway-inbox': { contract: 'L1GraphTokenGateway', name: 'inbox' }, + 'l1-gateway-escrow': { contract: 'L1GraphTokenGateway', name: 'escrow' }, + 'l1-gateway-l1-router': { contract: 'L1GraphTokenGateway', name: 'l1Router' }, + 'l1-gateway-l2-counterpart': { contract: 'L1GraphTokenGateway', name: 'l2Counterpart' }, + 'l1-gateway-paused': { contract: 'L1GraphTokenGateway', name: 'paused' }, + 'l2-gateway-l1-grt': { contract: 'L2GraphTokenGateway', name: 'l1GRT' }, + 'l2-gateway-l2-router': { contract: 'L2GraphTokenGateway', name: 'l2Router' }, + 'l2-gateway-l1-counterpart': { contract: 'L2GraphTokenGateway', name: 'l1Counterpart' }, + 'l2-gateway-paused': { contract: 'L2GraphTokenGateway', name: 'paused' }, + 'l2-token-gateway': { contract: 'L2GraphToken', name: 'gateway' }, + 'l2-token-l1-address': { contract: 'L2GraphToken', name: 'l1Address' }, } const buildHelp = () => { diff --git a/cli/commands/protocol/index.ts b/cli/commands/protocol/index.ts index 69ca4e4c2..acab2be90 100644 --- a/cli/commands/protocol/index.ts +++ b/cli/commands/protocol/index.ts @@ -3,6 +3,7 @@ import yargs, { Argv } from 'yargs' import { listCommand } from './list' import { getCommand } from './get' import { setCommand } from './set' +import { configureL1BridgeCommand, configureL2BridgeCommand } from './configure-bridge' export interface ProtocolFunction { contract: string @@ -17,7 +18,12 @@ export const protocolCommand = { command: 'protocol', describe: 'Graph protocol configuration', builder: (yargs: Argv): yargs.Argv => { - return yargs.command(getCommand).command(setCommand).command(listCommand) + return yargs + .command(getCommand) + .command(setCommand) + .command(listCommand) + .command(configureL1BridgeCommand) + .command(configureL2BridgeCommand) }, handler: (): void => { yargs.showHelp() diff --git a/cli/commands/protocol/list.ts b/cli/commands/protocol/list.ts index 228151e04..c82ca1d81 100644 --- a/cli/commands/protocol/list.ts +++ b/cli/commands/protocol/list.ts @@ -15,6 +15,9 @@ const contractNames = [ 'DisputeManager', 'RewardsManager', 'GNS', + 'L1GraphTokenGateway', + 'L2GraphToken', + 'L2GraphTokenGateway', ] export const listProtocolParams = async (cli: CLIEnvironment): Promise => { @@ -26,13 +29,15 @@ export const listProtocolParams = async (cli: CLIEnvironment): Promise => colWidths: [30, 50], }) + if (!(contractName in cli.contracts)) { + continue + } const contract = cli.contracts[contractName] table.push(['* address', contract.address]) const req = [] for (const fn of Object.values(gettersList)) { if (fn.contract != contractName) continue - const contract = cli.contracts[fn.contract] if (contract.interface.getFunction(fn.name).inputs.length == 0) { const contractFn: ContractFunction = contract.functions[fn.name] @@ -56,7 +61,7 @@ export const listProtocolParams = async (cli: CLIEnvironment): Promise => const controller = cli.contracts['Controller'] for (const contractName of contractNames) { - if (contractName === 'Controller') continue + if (contractName === 'Controller' || !(contractName in cli.contracts)) continue const contract = cli.contracts[contractName] const contractFn = contract.functions['controller'] diff --git a/cli/commands/protocol/set.ts b/cli/commands/protocol/set.ts index 4fc05f877..966d07897 100644 --- a/cli/commands/protocol/set.ts +++ b/cli/commands/protocol/set.ts @@ -59,6 +59,31 @@ export const settersList = { 'controller-set-paused': { contract: 'Controller', name: 'setPaused' }, 'controller-set-partial-paused': { contract: 'Controller', name: 'setPartialPaused' }, 'controller-set-pause-guardian': { contract: 'Controller', name: 'setPauseGuardian' }, + 'l1-gateway-set-l2-grt': { contract: 'L1GraphTokenGateway', name: 'setL2TokenAddress' }, + 'l1-gateway-set-arbitrum-addresses': { + contract: 'L1GraphTokenGateway', + name: 'setArbitrumAddresses', + }, + 'l1-gateway-set-l2-counterpart': { + contract: 'L1GraphTokenGateway', + name: 'setL2CounterpartAddress', + }, + 'l1-gateway-set-escrow-address': { + contract: 'L1GraphTokenGateway', + name: 'setEscrowAddress', + }, + 'l1-gateway-set-paused': { contract: 'L1GraphTokenGateway', name: 'setPaused' }, + 'bridge-escrow-approve-all': { contract: 'BridgeEscrow', name: 'approveAll' }, + 'bridge-escrow-revoke-all': { contract: 'BridgeEscrow', name: 'revokeAll' }, + 'l2-gateway-set-l1-grt': { contract: 'L2GraphTokenGateway', name: 'setL1TokenAddress' }, + 'l2-gateway-set-l2-router': { contract: 'L2GraphTokenGateway', name: 'setL2Router' }, + 'l2-gateway-set-l1-counterpart': { + contract: 'L2GraphTokenGateway', + name: 'setL1CounterpartAddress', + }, + 'l2-gateway-set-paused': { contract: 'L2GraphTokenGateway', name: 'setPaused' }, + 'l2-token-set-gateway': { contract: 'L2GraphToken', name: 'setGateway' }, + 'l2-token-set-l1-address': { contract: 'L2GraphToken', name: 'setL1Address' }, } const buildHelp = () => { diff --git a/cli/contracts.ts b/cli/contracts.ts index 57e49d21c..c36e83567 100644 --- a/cli/contracts.ts +++ b/cli/contracts.ts @@ -1,4 +1,5 @@ import { + BaseContract, Contract, ContractFunction, ContractReceipt, @@ -11,6 +12,7 @@ import lodash from 'lodash' import fs from 'fs' import { AddressBook } from './address-book' +import { chainIdIsL2 } from './cross-chain' import { logger } from './logging' import { getContractAt } from './network' @@ -31,6 +33,10 @@ import { AllocationExchange } from '../build/types/AllocationExchange' import { SubgraphNFT } from '../build/types/SubgraphNFT' import { GraphCurationToken } from '../build/types/GraphCurationToken' import { SubgraphNFTDescriptor } from '../build/types/SubgraphNFTDescriptor' +import { L1GraphTokenGateway } from '../build/types/L1GraphTokenGateway' +import { L2GraphToken } from '../build/types/L2GraphToken' +import { L2GraphTokenGateway } from '../build/types/L2GraphTokenGateway' +import { BridgeEscrow } from '../build/types/BridgeEscrow' export interface NetworkContracts { EpochManager: EpochManager @@ -50,16 +56,35 @@ export interface NetworkContracts { SubgraphNFT: SubgraphNFT SubgraphNFTDescriptor: SubgraphNFTDescriptor GraphCurationToken: GraphCurationToken + L1GraphTokenGateway: L1GraphTokenGateway + BridgeEscrow: BridgeEscrow + L2GraphToken: L2GraphToken + L2GraphTokenGateway: L2GraphTokenGateway +} + +export const loadAddressBookContract = ( + contractName: string, + addressBook: AddressBook, + signerOrProvider?: Signer | providers.Provider, +): BaseContract => { + const contractEntry = addressBook.getEntry(contractName) + let contract = getContractAt(contractName, contractEntry.address) + if (signerOrProvider) { + contract = contract.connect(signerOrProvider) + } + return contract } export const loadContracts = ( addressBook: AddressBook, + chainId: number | string, signerOrProvider?: Signer | providers.Provider, enableTXLogging = false, ): NetworkContracts => { const contracts = {} for (const contractName of addressBook.listEntries()) { const contractEntry = addressBook.getEntry(contractName) + try { let contract = getContractAt(contractName, contractEntry.address) if (enableTXLogging) { @@ -67,9 +92,15 @@ export const loadContracts = ( contract = wrapCalls(contract, contractName) } contracts[contractName] = contract + if (signerOrProvider) { contracts[contractName] = contracts[contractName].connect(signerOrProvider) } + + // On L2 networks, we alias L2GraphToken as GraphToken + if (chainIdIsL2(chainId) && contractName == 'L2GraphToken') { + contracts['GraphToken'] = contracts[contractName] + } } catch (err) { logger.warn(`Could not load contract ${contractName} - ${err.message}`) } diff --git a/cli/cross-chain.ts b/cli/cross-chain.ts new file mode 100644 index 000000000..a0b674624 --- /dev/null +++ b/cli/cross-chain.ts @@ -0,0 +1,76 @@ +import { L1ToL2MessageGasEstimator } from '@arbitrum/sdk' +import { L1ToL2MessageNoGasParams } from '@arbitrum/sdk/dist/lib/message/L1ToL2MessageCreator' +import { GasOverrides } from '@arbitrum/sdk/dist/lib/message/L1ToL2MessageGasEstimator' +import { BigNumber, providers } from 'ethers' +import { parseEther } from 'ethers/lib/utils' + +import { logger } from './logging' + +export const l1ToL2ChainIdMap = { + '1': '42161', + '4': '421611', + '5': '421613', + '1337': '412346', +} + +export const l2ChainIds = Object.values(l1ToL2ChainIdMap).map(Number) +export const l2ToL1ChainIdMap = Object.fromEntries( + Object.entries(l1ToL2ChainIdMap).map(([k, v]) => [v, k]), +) + +export const chainIdIsL2 = (chainId: number | string): boolean => { + return l2ChainIds.includes(Number(chainId)) +} + +interface L2GasParams { + maxGas: BigNumber + gasPriceBid: BigNumber + maxSubmissionCost: BigNumber +} + +export const estimateRetryableTxGas = async ( + l1Provider: providers.Provider, + l2Provider: providers.Provider, + gatewayAddress: string, + l2Dest: string, + depositCalldata: string, + opts: L2GasParams, +): Promise => { + const autoEstimate = opts && (!opts.maxGas || !opts.gasPriceBid || !opts.maxSubmissionCost) + if (!autoEstimate) { + return opts + } + + // Comment from Offchain Labs' implementation: + // we add a 0.05 ether "deposit" buffer to pay for execution in the gas estimation + logger.info('Estimating retryable ticket gas:') + const baseFee = (await l1Provider.getBlock('latest')).baseFeePerGas + const gasEstimator = new L1ToL2MessageGasEstimator(l2Provider) + const retryableEstimateData: L1ToL2MessageNoGasParams = { + from: gatewayAddress, + to: l2Dest, + data: depositCalldata, + l2CallValue: parseEther('0'), + excessFeeRefundAddress: gatewayAddress, + callValueRefundAddress: gatewayAddress, + } + + const estimateOpts: GasOverrides = {} + if (opts.maxGas) estimateOpts.gasLimit = { base: opts.maxGas } + if (opts.maxSubmissionCost) estimateOpts.maxSubmissionFee = { base: opts.maxSubmissionCost } + if (opts.gasPriceBid) estimateOpts.maxFeePerGas = { base: opts.gasPriceBid } + + const gasParams = await gasEstimator.estimateAll( + retryableEstimateData, + baseFee as BigNumber, + l1Provider, + estimateOpts, + ) + + // override fixed values + return { + maxGas: opts.maxGas ?? gasParams.gasLimit, + gasPriceBid: opts.gasPriceBid ?? gasParams.maxFeePerGas, + maxSubmissionCost: opts.maxSubmissionCost ?? gasParams.maxSubmissionCost, + } +} diff --git a/cli/defaults.ts b/cli/defaults.ts index 5c0a1dd6a..ed7ecb0af 100644 --- a/cli/defaults.ts +++ b/cli/defaults.ts @@ -7,6 +7,8 @@ export const local = { addressBookPath: './addresses.json', graphConfigPath: './config/graph.mainnet.yml', accountNumber: '0', + arbitrumAddressBookPath: './arbitrum-addresses.json', + arbProviderUrl: 'https://rinkeby.arbitrum.io/rpc', } export const defaultOverrides: Overrides = { @@ -62,4 +64,18 @@ export const cliOpts = { type: 'boolean', default: false, }, + arbitrumAddressBook: { + alias: 'arb-address-book', + description: 'The path to the address book file for Arbitrum deployments', + type: 'string', + group: 'Config', + default: local.arbitrumAddressBookPath, + }, + l2ProviderUrl: { + alias: 'l2-provider-url', + description: 'The URL of an Arbitrum provider (only for bridge commands)', + type: 'string', + group: 'Arbitrum', + default: local.arbProviderUrl, + }, } as { [key: string]: Options } diff --git a/cli/env.ts b/cli/env.ts index d889fcdc6..d607e4be5 100644 --- a/cli/env.ts +++ b/cli/env.ts @@ -4,7 +4,7 @@ import { Argv } from 'yargs' import { logger } from './logging' import { getAddressBook, AddressBook } from './address-book' import { defaultOverrides } from './defaults' -import { getProvider } from './utils' +import { getProvider } from './network' import { loadContracts, NetworkContracts } from './contracts' const { formatEther } = utils @@ -43,7 +43,7 @@ export const loadEnv = async (argv: CLIArgs, wallet?: Wallet): Promise hexlify(randomBytes(n)) +export const toBN = (value: string | number | BigNumber): BigNumber => BigNumber.from(value) export const toGRT = (value: string | number): BigNumber => { return parseUnits(typeof value === 'number' ? value.toString() : value, '18') } export const getProvider = (providerUrl: string, network?: number): providers.JsonRpcProvider => new providers.JsonRpcProvider(providerUrl, network) +export const getChainID = (): number => { + return 4 // Only works for rinkeby right now +} + export const hashHexString = (input: string): string => keccak256(`0x${input.replace(/^0x/, '')}`) type ContractParam = string | BigNumber | number @@ -103,7 +111,7 @@ export const sendTransaction = async ( fn: string, // eslint-disable-next-line @typescript-eslint/no-explicit-any params?: Array, - overrides?: Overrides, + overrides?: PayableOverrides, ): Promise => { // Setup overrides if (overrides) { @@ -363,3 +371,26 @@ export const linkLibraries = ( } return bytecode } + +export const ensureAllowance = async ( + sender: Wallet, + spenderAddress: string, + token: GraphToken, + amount: BigNumber, +) => { + // check balance + const senderBalance = await token.balanceOf(sender.address) + if (senderBalance.lt(amount)) { + throw new Error('Sender balance is insufficient for the transfer') + } + + // check allowance + const allowance = await token.allowance(sender.address, spenderAddress) + if (allowance.gte(amount)) { + return + } + + // approve + logger.info('Approving token transfer') + return sendTransaction(sender, token, 'approve', [spenderAddress, amount]) +} diff --git a/cli/utils.ts b/cli/utils.ts deleted file mode 100644 index a11e40f4e..000000000 --- a/cli/utils.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Contract, Wallet, providers } from 'ethers' - -import { loadArtifact } from './artifacts' - -export const contractAt = ( - contractName: string, - contractAddress: string, - wallet: Wallet, -): Contract => { - return new Contract(contractAddress, loadArtifact(contractName).abi, wallet.provider) -} - -export const getProvider = (providerUrl: string, network?: number): providers.JsonRpcProvider => - new providers.JsonRpcProvider(providerUrl, network) diff --git a/config/graph.arbitrum-goerli.yml b/config/graph.arbitrum-goerli.yml new file mode 100644 index 000000000..5b41e4382 --- /dev/null +++ b/config/graph.arbitrum-goerli.yml @@ -0,0 +1,151 @@ +general: + arbitrator: &arbitrator "0xF89688d5d44d73cc4dE880857A3940487076e5A4" # Arbitration Council (TODO: update) + governor: &governor "0x5CeeeE16F30357d49c50bcd7F520ca6527cf388a" # Graph Council (TODO: update) + authority: &authority "0xD06f366678AE139a94b2AaC2913608De568F1D03" # Authority that signs payment vouchers (TODO: update) + availabilityOracle: &availabilityOracle "0xA99A56fA38a6B9553853c84E11458AeCcdad509B" # Subgraph Availability Oracle (TODO: update) + pauseGuardian: &pauseGuardian "0x4B6C90B9fE29dfa521188B6547989C23d613b79B" # Protocol pause guardian (TODO: update) + allocationExchangeOwner: &allocationExchangeOwner "0x05F359b1319f1Ca9b799CB6386F31421c2c49dBA" # Allocation Exchange owner (TODO: update) + +contracts: + Controller: + calls: + - fn: "setContractProxy" + id: "0xe6876326c1291dfcbbd3864a6816d698cd591defc7aa2153d7f9c4c04016c89f" # keccak256('Curation') + contractAddress: "${{Curation.address}}" + - fn: "setContractProxy" + id: "0x39605a6c26a173774ca666c67ef70cf491880e5d3d6d0ca66ec0a31034f15ea3" # keccak256('GNS') + contractAddress: "${{GNS.address}}" + - fn: "setContractProxy" + id: "0xf942813d07d17b56de9a9afc8de0ced6e8c053bbfdcc87b7badea4ddcf27c307" # keccak256('DisputeManager') + contractAddress: "${{DisputeManager.address}}" + - fn: "setContractProxy" + id: "0xc713c3df6d14cdf946460395d09af88993ee2b948b1a808161494e32c5f67063" # keccak256('EpochManager') + contractAddress: "${{EpochManager.address}}" + - fn: "setContractProxy" + id: "0x966f1e8d8d8014e05f6ec4a57138da9be1f7c5a7f802928a18072f7c53180761" # keccak256('RewardsManager') + contractAddress: "${{RewardsManager.address}}" + - fn: "setContractProxy" + id: "0x1df41cd916959d1163dc8f0671a666ea8a3e434c13e40faef527133b5d167034" # keccak256('Staking') + contractAddress: "${{Staking.address}}" + - fn: "setContractProxy" + id: "0x45fc200c7e4544e457d3c5709bfe0d520442c30bbcbdaede89e8d4a4bbc19247" # keccak256('GraphToken') + contractAddress: "${{L2GraphToken.address}}" + - fn: "setContractProxy" + id: "0xd362cac9cb75c10d67bcc0b7eeb0b1ef48bb5420b556c092d4fd7f758816fcf0" # keccak256('GraphTokenGateway') + contractAddress: "${{L2GraphTokenGateway.address}}" + - fn: "setPauseGuardian" + pauseGuardian: *pauseGuardian + - fn: "transferOwnership" + owner: *governor + GraphProxyAdmin: + calls: + - fn: "transferOwnership" + owner: *governor + ServiceRegistry: + proxy: true + init: + controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" + EpochManager: + proxy: true + init: + controller: "${{Controller.address}}" + lengthInBlocks: 554 # length in hours = lengthInBlocks*13/60/60 (~13 second blocks) + L2GraphToken: + proxy: true + init: + owner: "${{Env.deployer}}" + calls: + - fn: "renounceMinter" + - fn: "transferOwnership" + owner: *governor + Curation: + proxy: true + init: + controller: "${{Controller.address}}" + bondingCurve: "${{BancorFormula.address}}" + curationTokenMaster: "${{GraphCurationToken.address}}" + reserveRatio: 1000000 # in parts per million + curationTaxPercentage: 10000 # in parts per million + minimumCurationDeposit: "1000000000000000000" # in wei + calls: + - fn: "syncAllContracts" + DisputeManager: + proxy: true + init: + controller: "${{Controller.address}}" + arbitrator: *arbitrator + minimumDeposit: "10000000000000000000000" # in wei + fishermanRewardPercentage: 500000 # in parts per million + idxSlashingPercentage: 25000 # in parts per million + qrySlashingPercentage: 25000 # in parts per million + calls: + - fn: "syncAllContracts" + GNS: + proxy: true + init: + controller: "${{Controller.address}}" + bondingCurve: "${{BancorFormula.address}}" + subgraphNFT: "${{SubgraphNFT.address}}" + calls: + - fn: "approveAll" + - fn: "syncAllContracts" + SubgraphNFT: + init: + governor: "${{Env.deployer}}" + calls: + - fn: "setTokenDescriptor" + tokenDescriptor: "${{SubgraphNFTDescriptor.address}}" + - fn: "setMinter" + minter: "${{GNS.address}}" + - fn: "transferOwnership" + owner: *governor + Staking: + proxy: true + init: + controller: "${{Controller.address}}" + minimumIndexerStake: "100000000000000000000000" # in wei + thawingPeriod: 6646 # in blocks + protocolPercentage: 10000 # in parts per million + curationPercentage: 100000 # in parts per million + channelDisputeEpochs: 2 # in epochs + maxAllocationEpochs: 4 # in epochs + delegationUnbondingPeriod: 12 # in epochs + delegationRatio: 16 # delegated stake to indexer stake multiplier + rebateAlphaNumerator: 77 # rebateAlphaNumerator / rebateAlphaDenominator + rebateAlphaDenominator: 100 # rebateAlphaNumerator / rebateAlphaDenominator + calls: + - fn: "setDelegationTaxPercentage" + delegationTaxPercentage: 5000 # parts per million + - fn: "setSlasher" + slasher: "${{DisputeManager.address}}" + allowed: true + - fn: "setAssetHolder" + assetHolder: "${{AllocationExchange.address}}" + allowed: true + - fn: "syncAllContracts" + RewardsManager: + proxy: true + init: + controller: "${{Controller.address}}" + calls: + - fn: "setSubgraphAvailabilityOracle" + subgraphAvailabilityOracle: *availabilityOracle + - fn: "syncAllContracts" + AllocationExchange: + init: + graphToken: "${{L2GraphToken.address}}" + staking: "${{Staking.address}}" + governor: *allocationExchangeOwner + authority: *authority + calls: + - fn: "approveAll" + L2GraphTokenGateway: + proxy: true + init: + controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" + - fn: "setPauseGuardian" + pauseGuardian: *pauseGuardian diff --git a/config/graph.arbitrum-localhost.yml b/config/graph.arbitrum-localhost.yml new file mode 100644 index 000000000..a17755fbc --- /dev/null +++ b/config/graph.arbitrum-localhost.yml @@ -0,0 +1,151 @@ +general: + arbitrator: &arbitrator "0x4237154FE0510FdE3575656B60c68a01B9dCDdF8" # Arbitration Council + governor: &governor "0x1257227a2ECA34834940110f7B5e341A5143A2c4" # Graph Council + authority: &authority "0x12B8D08b116E1E3cc29eE9Cf42bB0AA8129C3215" # Authority that signs payment vouchers + availabilityOracle: &availabilityOracle "0x7694a48065f063a767a962610C6717c59F36b445" # Subgraph Availability Oracle + pauseGuardian: &pauseGuardian "0x601060e0DC5349AA55EC73df5A58cB0FC1cD2e3C" # Protocol pause guardian + allocationExchangeOwner: &allocationExchangeOwner "0xbD38F7b67a591A5cc7D642e1026E5095B819d952" # Allocation Exchange owner + +contracts: + Controller: + calls: + - fn: "setContractProxy" + id: "0xe6876326c1291dfcbbd3864a6816d698cd591defc7aa2153d7f9c4c04016c89f" # keccak256('Curation') + contractAddress: "${{Curation.address}}" + - fn: "setContractProxy" + id: "0x39605a6c26a173774ca666c67ef70cf491880e5d3d6d0ca66ec0a31034f15ea3" # keccak256('GNS') + contractAddress: "${{GNS.address}}" + - fn: "setContractProxy" + id: "0xf942813d07d17b56de9a9afc8de0ced6e8c053bbfdcc87b7badea4ddcf27c307" # keccak256('DisputeManager') + contractAddress: "${{DisputeManager.address}}" + - fn: "setContractProxy" + id: "0xc713c3df6d14cdf946460395d09af88993ee2b948b1a808161494e32c5f67063" # keccak256('EpochManager') + contractAddress: "${{EpochManager.address}}" + - fn: "setContractProxy" + id: "0x966f1e8d8d8014e05f6ec4a57138da9be1f7c5a7f802928a18072f7c53180761" # keccak256('RewardsManager') + contractAddress: "${{RewardsManager.address}}" + - fn: "setContractProxy" + id: "0x1df41cd916959d1163dc8f0671a666ea8a3e434c13e40faef527133b5d167034" # keccak256('Staking') + contractAddress: "${{Staking.address}}" + - fn: "setContractProxy" + id: "0x45fc200c7e4544e457d3c5709bfe0d520442c30bbcbdaede89e8d4a4bbc19247" # keccak256('GraphToken') + contractAddress: "${{L2GraphToken.address}}" + - fn: "setContractProxy" + id: "0xd362cac9cb75c10d67bcc0b7eeb0b1ef48bb5420b556c092d4fd7f758816fcf0" # keccak256('GraphTokenGateway') + contractAddress: "${{L2GraphTokenGateway.address}}" + - fn: "setPauseGuardian" + pauseGuardian: *pauseGuardian + - fn: "transferOwnership" + owner: *governor + GraphProxyAdmin: + calls: + - fn: "transferOwnership" + owner: *governor + ServiceRegistry: + proxy: true + init: + controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" + EpochManager: + proxy: true + init: + controller: "${{Controller.address}}" + lengthInBlocks: 554 # length in hours = lengthInBlocks*13/60/60 (~13 second blocks) + L2GraphToken: + proxy: true + init: + owner: "${{Env.deployer}}" + calls: + - fn: "renounceMinter" + - fn: "transferOwnership" + owner: *governor + Curation: + proxy: true + init: + controller: "${{Controller.address}}" + bondingCurve: "${{BancorFormula.address}}" + curationTokenMaster: "${{GraphCurationToken.address}}" + reserveRatio: 1000000 # in parts per million + curationTaxPercentage: 10000 # in parts per million + minimumCurationDeposit: "1000000000000000000" # in wei + calls: + - fn: "syncAllContracts" + DisputeManager: + proxy: true + init: + controller: "${{Controller.address}}" + arbitrator: *arbitrator + minimumDeposit: "10000000000000000000000" # in wei + fishermanRewardPercentage: 500000 # in parts per million + idxSlashingPercentage: 25000 # in parts per million + qrySlashingPercentage: 25000 # in parts per million + calls: + - fn: "syncAllContracts" + GNS: + proxy: true + init: + controller: "${{Controller.address}}" + bondingCurve: "${{BancorFormula.address}}" + subgraphNFT: "${{SubgraphNFT.address}}" + calls: + - fn: "approveAll" + - fn: "syncAllContracts" + SubgraphNFT: + init: + governor: "${{Env.deployer}}" + calls: + - fn: "setTokenDescriptor" + tokenDescriptor: "${{SubgraphNFTDescriptor.address}}" + - fn: "setMinter" + minter: "${{GNS.address}}" + - fn: "transferOwnership" + owner: *governor + Staking: + proxy: true + init: + controller: "${{Controller.address}}" + minimumIndexerStake: "100000000000000000000000" # in wei + thawingPeriod: 6646 # in blocks + protocolPercentage: 10000 # in parts per million + curationPercentage: 100000 # in parts per million + channelDisputeEpochs: 2 # in epochs + maxAllocationEpochs: 4 # in epochs + delegationUnbondingPeriod: 12 # in epochs + delegationRatio: 16 # delegated stake to indexer stake multiplier + rebateAlphaNumerator: 77 # rebateAlphaNumerator / rebateAlphaDenominator + rebateAlphaDenominator: 100 # rebateAlphaNumerator / rebateAlphaDenominator + calls: + - fn: "setDelegationTaxPercentage" + delegationTaxPercentage: 5000 # parts per million + - fn: "setSlasher" + slasher: "${{DisputeManager.address}}" + allowed: true + - fn: "setAssetHolder" + assetHolder: "${{AllocationExchange.address}}" + allowed: true + - fn: "syncAllContracts" + RewardsManager: + proxy: true + init: + controller: "${{Controller.address}}" + calls: + - fn: "setSubgraphAvailabilityOracle" + subgraphAvailabilityOracle: *availabilityOracle + - fn: "syncAllContracts" + AllocationExchange: + init: + graphToken: "${{L2GraphToken.address}}" + staking: "${{Staking.address}}" + governor: *allocationExchangeOwner + authority: *authority + calls: + - fn: "approveAll" + L2GraphTokenGateway: + proxy: true + init: + controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" + - fn: "setPauseGuardian" + pauseGuardian: *pauseGuardian diff --git a/config/graph.arbitrum-one.yml b/config/graph.arbitrum-one.yml new file mode 100644 index 000000000..49c7b0b13 --- /dev/null +++ b/config/graph.arbitrum-one.yml @@ -0,0 +1,151 @@ +general: + arbitrator: &arbitrator "0x113DC95e796836b8F0Fa71eE7fB42f221740c3B0" # Arbitration Council + governor: &governor "0x8C6de8F8D562f3382417340A6994601eE08D3809" # Graph Council + authority: &authority "0x79f2212de27912bCb25a452fC102C85c142E3eE3" # Authority that signs payment vouchers + availabilityOracle: &availabilityOracle "0xbCAEE36Ce38Ec534c7078db1f90118E72173645B" # Subgraph Availability Oracle + pauseGuardian: &pauseGuardian "0xB0aD33a21b98bCA1761729A105e2E34e27153aAE" # Protocol pause guardian + allocationExchangeOwner: &allocationExchangeOwner "0x270Ea4ea9e8A699f8fE54515E3Bb2c418952623b" # Allocation Exchange owner + +contracts: + Controller: + calls: + - fn: "setContractProxy" + id: "0xe6876326c1291dfcbbd3864a6816d698cd591defc7aa2153d7f9c4c04016c89f" # keccak256('Curation') + contractAddress: "${{Curation.address}}" + - fn: "setContractProxy" + id: "0x39605a6c26a173774ca666c67ef70cf491880e5d3d6d0ca66ec0a31034f15ea3" # keccak256('GNS') + contractAddress: "${{GNS.address}}" + - fn: "setContractProxy" + id: "0xf942813d07d17b56de9a9afc8de0ced6e8c053bbfdcc87b7badea4ddcf27c307" # keccak256('DisputeManager') + contractAddress: "${{DisputeManager.address}}" + - fn: "setContractProxy" + id: "0xc713c3df6d14cdf946460395d09af88993ee2b948b1a808161494e32c5f67063" # keccak256('EpochManager') + contractAddress: "${{EpochManager.address}}" + - fn: "setContractProxy" + id: "0x966f1e8d8d8014e05f6ec4a57138da9be1f7c5a7f802928a18072f7c53180761" # keccak256('RewardsManager') + contractAddress: "${{RewardsManager.address}}" + - fn: "setContractProxy" + id: "0x1df41cd916959d1163dc8f0671a666ea8a3e434c13e40faef527133b5d167034" # keccak256('Staking') + contractAddress: "${{Staking.address}}" + - fn: "setContractProxy" + id: "0x45fc200c7e4544e457d3c5709bfe0d520442c30bbcbdaede89e8d4a4bbc19247" # keccak256('GraphToken') + contractAddress: "${{L2GraphToken.address}}" + - fn: "setContractProxy" + id: "0xd362cac9cb75c10d67bcc0b7eeb0b1ef48bb5420b556c092d4fd7f758816fcf0" # keccak256('GraphTokenGateway') + contractAddress: "${{L2GraphTokenGateway.address}}" + - fn: "setPauseGuardian" + pauseGuardian: *pauseGuardian + - fn: "transferOwnership" + owner: *governor + GraphProxyAdmin: + calls: + - fn: "transferOwnership" + owner: *governor + ServiceRegistry: + proxy: true + init: + controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" + EpochManager: + proxy: true + init: + controller: "${{Controller.address}}" + lengthInBlocks: 6646 # length in hours = lengthInBlocks*13/60/60 (~13 second blocks) + L2GraphToken: + proxy: true + init: + owner: "${{Env.deployer}}" + calls: + - fn: "renounceMinter" + - fn: "transferOwnership" + owner: *governor + Curation: + proxy: true + init: + controller: "${{Controller.address}}" + bondingCurve: "${{BancorFormula.address}}" + curationTokenMaster: "${{GraphCurationToken.address}}" + reserveRatio: 1000000 # in parts per million + curationTaxPercentage: 10000 # in parts per million + minimumCurationDeposit: "1000000000000000000" # in wei + calls: + - fn: "syncAllContracts" + DisputeManager: + proxy: true + init: + controller: "${{Controller.address}}" + arbitrator: *arbitrator + minimumDeposit: "10000000000000000000000" # in wei + fishermanRewardPercentage: 500000 # in parts per million + idxSlashingPercentage: 25000 # in parts per million + qrySlashingPercentage: 25000 # in parts per million + calls: + - fn: "syncAllContracts" + GNS: + proxy: true + init: + controller: "${{Controller.address}}" + bondingCurve: "${{BancorFormula.address}}" + subgraphNFT: "${{SubgraphNFT.address}}" + calls: + - fn: "approveAll" + - fn: "syncAllContracts" + SubgraphNFT: + init: + governor: "${{Env.deployer}}" + calls: + - fn: "setTokenDescriptor" + tokenDescriptor: "${{SubgraphNFTDescriptor.address}}" + - fn: "setMinter" + minter: "${{GNS.address}}" + - fn: "transferOwnership" + owner: *governor + Staking: + proxy: true + init: + controller: "${{Controller.address}}" + minimumIndexerStake: "100000000000000000000000" # in wei + thawingPeriod: 186092 # in blocks + protocolPercentage: 10000 # in parts per million + curationPercentage: 100000 # in parts per million + channelDisputeEpochs: 7 # in epochs + maxAllocationEpochs: 28 # in epochs + delegationUnbondingPeriod: 28 # in epochs + delegationRatio: 16 # delegated stake to indexer stake multiplier + rebateAlphaNumerator: 77 # rebateAlphaNumerator / rebateAlphaDenominator + rebateAlphaDenominator: 100 # rebateAlphaNumerator / rebateAlphaDenominator + calls: + - fn: "setDelegationTaxPercentage" + delegationTaxPercentage: 5000 # parts per million + - fn: "setSlasher" + slasher: "${{DisputeManager.address}}" + allowed: true + - fn: "setAssetHolder" + assetHolder: "${{AllocationExchange.address}}" + allowed: true + - fn: "syncAllContracts" + RewardsManager: + proxy: true + init: + controller: "${{Controller.address}}" + calls: + - fn: "setSubgraphAvailabilityOracle" + subgraphAvailabilityOracle: *availabilityOracle + - fn: "syncAllContracts" + AllocationExchange: + init: + graphToken: "${{L2GraphToken.address}}" + staking: "${{Staking.address}}" + governor: *allocationExchangeOwner + authority: *authority + calls: + - fn: "approveAll" + L2GraphTokenGateway: + proxy: true + init: + controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" + - fn: "setPauseGuardian" + pauseGuardian: *pauseGuardian diff --git a/config/graph.goerli.yml b/config/graph.goerli.yml index b79890149..b4b735b4e 100644 --- a/config/graph.goerli.yml +++ b/config/graph.goerli.yml @@ -30,6 +30,9 @@ contracts: - fn: "setContractProxy" id: "0x45fc200c7e4544e457d3c5709bfe0d520442c30bbcbdaede89e8d4a4bbc19247" # keccak256('GraphToken') contractAddress: "${{GraphToken.address}}" + - fn: "setContractProxy" + id: "0xd362cac9cb75c10d67bcc0b7eeb0b1ef48bb5420b556c092d4fd7f758816fcf0" # keccak256('GraphTokenGateway') + contractAddress: "${{L1GraphTokenGateway.address}}" - fn: "setPauseGuardian" pauseGuardian: *pauseGuardian - fn: "transferOwnership" @@ -42,6 +45,8 @@ contracts: proxy: true init: controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" EpochManager: proxy: true init: @@ -65,6 +70,8 @@ contracts: reserveRatio: 500000 # in parts per million curationTaxPercentage: 10000 # in parts per million minimumCurationDeposit: "1000000000000000000" # in wei + calls: + - fn: "syncAllContracts" DisputeManager: proxy: true init: @@ -74,6 +81,8 @@ contracts: fishermanRewardPercentage: 500000 # in parts per million idxSlashingPercentage: 25000 # in parts per million qrySlashingPercentage: 25000 # in parts per million + calls: + - fn: "syncAllContracts" GNS: proxy: true init: @@ -82,6 +91,7 @@ contracts: subgraphNFT: "${{SubgraphNFT.address}}" calls: - fn: "approveAll" + - fn: "syncAllContracts" SubgraphNFT: init: governor: "${{Env.deployer}}" @@ -115,6 +125,7 @@ contracts: - fn: "setAssetHolder" assetHolder: "${{AllocationExchange.address}}" allowed: true + - fn: "syncAllContracts" RewardsManager: proxy: true init: @@ -124,6 +135,7 @@ contracts: issuanceRate: "1000000012184945188" # per block increase of total supply, blocks in a year = 365*60*60*24/13 - fn: "setSubgraphAvailabilityOracle" subgraphAvailabilityOracle: *availabilityOracle + - fn: "syncAllContracts" AllocationExchange: init: graphToken: "${{GraphToken.address}}" @@ -132,3 +144,17 @@ contracts: authority: *authority calls: - fn: "approveAll" + L1GraphTokenGateway: + proxy: true + init: + controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" + - fn: "setPauseGuardian" + pauseGuardian: *pauseGuardian + BridgeEscrow: + proxy: true + init: + controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" diff --git a/config/graph.localhost.yml b/config/graph.localhost.yml index bb039f405..4a90dfd20 100644 --- a/config/graph.localhost.yml +++ b/config/graph.localhost.yml @@ -30,6 +30,9 @@ contracts: - fn: "setContractProxy" id: "0x45fc200c7e4544e457d3c5709bfe0d520442c30bbcbdaede89e8d4a4bbc19247" # keccak256('GraphToken') contractAddress: "${{GraphToken.address}}" + - fn: "setContractProxy" + id: "0xd362cac9cb75c10d67bcc0b7eeb0b1ef48bb5420b556c092d4fd7f758816fcf0" # keccak256('GraphTokenGateway') + contractAddress: "${{L1GraphTokenGateway.address}}" - fn: "setPauseGuardian" pauseGuardian: *pauseGuardian - fn: "transferOwnership" @@ -42,6 +45,8 @@ contracts: proxy: true init: controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" EpochManager: proxy: true init: @@ -65,6 +70,8 @@ contracts: reserveRatio: 500000 # in parts per million curationTaxPercentage: 10000 # in parts per million minimumCurationDeposit: "1000000000000000000" # in wei + calls: + - fn: "syncAllContracts" DisputeManager: proxy: true init: @@ -74,6 +81,8 @@ contracts: fishermanRewardPercentage: 500000 # in parts per million idxSlashingPercentage: 25000 # in parts per million qrySlashingPercentage: 25000 # in parts per million + calls: + - fn: "syncAllContracts" GNS: proxy: true init: @@ -82,6 +91,7 @@ contracts: subgraphNFT: "${{SubgraphNFT.address}}" calls: - fn: "approveAll" + - fn: "syncAllContracts" SubgraphNFT: init: governor: "${{Env.deployer}}" @@ -115,6 +125,7 @@ contracts: - fn: "setAssetHolder" assetHolder: "${{AllocationExchange.address}}" allowed: true + - fn: "syncAllContracts" RewardsManager: proxy: true init: @@ -124,6 +135,7 @@ contracts: issuanceRate: "1000000012184945188" # per block increase of total supply, blocks in a year = 365*60*60*24/13 - fn: "setSubgraphAvailabilityOracle" subgraphAvailabilityOracle: *availabilityOracle + - fn: "syncAllContracts" AllocationExchange: init: graphToken: "${{GraphToken.address}}" @@ -132,3 +144,17 @@ contracts: authority: *authority calls: - fn: "approveAll" + L1GraphTokenGateway: + proxy: true + init: + controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" + - fn: "setPauseGuardian" + pauseGuardian: *pauseGuardian + BridgeEscrow: + proxy: true + init: + controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" diff --git a/config/graph.mainnet.yml b/config/graph.mainnet.yml index df24ef7d6..6eb08b233 100644 --- a/config/graph.mainnet.yml +++ b/config/graph.mainnet.yml @@ -30,6 +30,9 @@ contracts: - fn: "setContractProxy" id: "0x45fc200c7e4544e457d3c5709bfe0d520442c30bbcbdaede89e8d4a4bbc19247" # keccak256('GraphToken') contractAddress: "${{GraphToken.address}}" + - fn: "setContractProxy" + id: "0xd362cac9cb75c10d67bcc0b7eeb0b1ef48bb5420b556c092d4fd7f758816fcf0" # keccak256('GraphTokenGateway') + contractAddress: "${{L1GraphTokenGateway.address}}" - fn: "setPauseGuardian" pauseGuardian: *pauseGuardian - fn: "transferOwnership" @@ -42,6 +45,8 @@ contracts: proxy: true init: controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" EpochManager: proxy: true init: @@ -65,6 +70,8 @@ contracts: reserveRatio: 500000 # in parts per million curationTaxPercentage: 10000 # in parts per million minimumCurationDeposit: "1000000000000000000" # in wei + calls: + - fn: "syncAllContracts" DisputeManager: proxy: true init: @@ -74,6 +81,8 @@ contracts: fishermanRewardPercentage: 500000 # in parts per million idxSlashingPercentage: 25000 # in parts per million qrySlashingPercentage: 25000 # in parts per million + calls: + - fn: "syncAllContracts" GNS: proxy: true init: @@ -82,6 +91,7 @@ contracts: subgraphNFT: "${{SubgraphNFT.address}}" calls: - fn: "approveAll" + - fn: "syncAllContracts" SubgraphNFT: init: governor: "${{Env.deployer}}" @@ -115,6 +125,7 @@ contracts: - fn: "setAssetHolder" assetHolder: "${{AllocationExchange.address}}" allowed: true + - fn: "syncAllContracts" RewardsManager: proxy: true init: @@ -124,6 +135,7 @@ contracts: issuanceRate: "1000000012184945188" # per block increase of total supply, blocks in a year = 365*60*60*24/13 - fn: "setSubgraphAvailabilityOracle" subgraphAvailabilityOracle: *availabilityOracle + - fn: "syncAllContracts" AllocationExchange: init: graphToken: "${{GraphToken.address}}" @@ -132,3 +144,17 @@ contracts: authority: *authority calls: - fn: "approveAll" + L1GraphTokenGateway: + proxy: true + init: + controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" + - fn: "setPauseGuardian" + pauseGuardian: *pauseGuardian + BridgeEscrow: + proxy: true + init: + controller: "${{Controller.address}}" + calls: + - fn: "syncAllContracts" diff --git a/contracts/arbitrum/AddressAliasHelper.sol b/contracts/arbitrum/AddressAliasHelper.sol new file mode 100644 index 000000000..740b70361 --- /dev/null +++ b/contracts/arbitrum/AddressAliasHelper.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2019-2021, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Originally copied from: + * https://github.com/OffchainLabs/arbitrum/tree/84e64dee6ee82adbf8ec34fd4b86c207a61d9007/packages/arb-bridge-eth + * + * MODIFIED from Offchain Labs' implementation: + * - Changed solidity version to 0.7.6 (pablo@edgeandnode.com) + * + */ + +pragma solidity ^0.7.6; + +library AddressAliasHelper { + uint160 constant offset = uint160(0x1111000000000000000000000000000000001111); + + /// @notice Utility function that converts the address in the L1 that submitted a tx to + /// the inbox to the msg.sender viewed in the L2 + /// @param l1Address the address in the L1 that triggered the tx to L2 + /// @return l2Address L2 address as viewed in msg.sender + function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) { + l2Address = address(uint160(l1Address) + offset); + } + + /// @notice Utility function that converts the msg.sender viewed in the L2 to the + /// address in the L1 that submitted a tx to the inbox + /// @param l2Address L2 address as viewed in msg.sender + /// @return l1Address the address in the L1 that triggered the tx to L2 + function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) { + l1Address = address(uint160(l2Address) - offset); + } +} diff --git a/contracts/arbitrum/IArbToken.sol b/contracts/arbitrum/IArbToken.sol new file mode 100644 index 000000000..d7d5a2d8c --- /dev/null +++ b/contracts/arbitrum/IArbToken.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2020, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Originally copied from: + * https://github.com/OffchainLabs/arbitrum/tree/e3a6307ad8a2dc2cad35728a2a9908cfd8dd8ef9/packages/arb-bridge-peripherals + * + * MODIFIED from Offchain Labs' implementation: + * - Changed solidity version to 0.7.6 (pablo@edgeandnode.com) + * + */ + +/** + * @title Minimum expected interface for L2 token that interacts with the L2 token bridge (this is the interface necessary + * for a custom token that interacts with the bridge, see TestArbCustomToken.sol for an example implementation). + */ +pragma solidity ^0.7.6; + +interface IArbToken { + /** + * @notice should increase token supply by amount, and should (probably) only be callable by the L1 bridge. + */ + function bridgeMint(address account, uint256 amount) external; + + /** + * @notice should decrease token supply by amount, and should (probably) only be callable by the L1 bridge. + */ + function bridgeBurn(address account, uint256 amount) external; + + /** + * @return address of layer 1 token + */ + function l1Address() external view returns (address); +} diff --git a/contracts/arbitrum/IBridge.sol b/contracts/arbitrum/IBridge.sol new file mode 100644 index 000000000..ff78253fc --- /dev/null +++ b/contracts/arbitrum/IBridge.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2021, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Originally copied from: + * https://github.com/OffchainLabs/arbitrum/tree/e3a6307ad8a2dc2cad35728a2a9908cfd8dd8ef9/packages/arb-bridge-eth + * + * MODIFIED from Offchain Labs' implementation: + * - Changed solidity version to 0.7.6 (pablo@edgeandnode.com) + * + */ + +pragma solidity ^0.7.6; + +interface IBridge { + event MessageDelivered( + uint256 indexed messageIndex, + bytes32 indexed beforeInboxAcc, + address inbox, + uint8 kind, + address sender, + bytes32 messageDataHash + ); + + event BridgeCallTriggered( + address indexed outbox, + address indexed destAddr, + uint256 amount, + bytes data + ); + + event InboxToggle(address indexed inbox, bool enabled); + + event OutboxToggle(address indexed outbox, bool enabled); + + function deliverMessageToInbox( + uint8 kind, + address sender, + bytes32 messageDataHash + ) external payable returns (uint256); + + function executeCall( + address destAddr, + uint256 amount, + bytes calldata data + ) external returns (bool success, bytes memory returnData); + + // These are only callable by the admin + function setInbox(address inbox, bool enabled) external; + + function setOutbox(address inbox, bool enabled) external; + + // View functions + + function activeOutbox() external view returns (address); + + function allowedInboxes(address inbox) external view returns (bool); + + function allowedOutboxes(address outbox) external view returns (bool); + + function inboxAccs(uint256 index) external view returns (bytes32); + + function messageCount() external view returns (uint256); +} diff --git a/contracts/arbitrum/IInbox.sol b/contracts/arbitrum/IInbox.sol new file mode 100644 index 000000000..a9315bbf8 --- /dev/null +++ b/contracts/arbitrum/IInbox.sol @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2021, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Originally copied from: + * https://github.com/OffchainLabs/arbitrum/tree/e3a6307ad8a2dc2cad35728a2a9908cfd8dd8ef9/packages/arb-bridge-eth + * + * MODIFIED from Offchain Labs' implementation: + * - Changed solidity version to 0.7.6 (pablo@edgeandnode.com) + * + */ + +pragma solidity ^0.7.6; + +import "./IBridge.sol"; +import "./IMessageProvider.sol"; + +interface IInbox is IMessageProvider { + function sendL2Message(bytes calldata messageData) external returns (uint256); + + function sendUnsignedTransaction( + uint256 maxGas, + uint256 gasPriceBid, + uint256 nonce, + address destAddr, + uint256 amount, + bytes calldata data + ) external returns (uint256); + + function sendContractTransaction( + uint256 maxGas, + uint256 gasPriceBid, + address destAddr, + uint256 amount, + bytes calldata data + ) external returns (uint256); + + function sendL1FundedUnsignedTransaction( + uint256 maxGas, + uint256 gasPriceBid, + uint256 nonce, + address destAddr, + bytes calldata data + ) external payable returns (uint256); + + function sendL1FundedContractTransaction( + uint256 maxGas, + uint256 gasPriceBid, + address destAddr, + bytes calldata data + ) external payable returns (uint256); + + function createRetryableTicket( + address destAddr, + uint256 arbTxCallValue, + uint256 maxSubmissionCost, + address submissionRefundAddress, + address valueRefundAddress, + uint256 maxGas, + uint256 gasPriceBid, + bytes calldata data + ) external payable returns (uint256); + + function depositEth(uint256 maxSubmissionCost) external payable returns (uint256); + + function bridge() external view returns (IBridge); + + function pauseCreateRetryables() external; + + function unpauseCreateRetryables() external; + + function startRewriteAddress() external; + + function stopRewriteAddress() external; +} diff --git a/contracts/arbitrum/IMessageProvider.sol b/contracts/arbitrum/IMessageProvider.sol new file mode 100644 index 000000000..8fbfdb171 --- /dev/null +++ b/contracts/arbitrum/IMessageProvider.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2021, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Originally copied from: + * https://github.com/OffchainLabs/arbitrum/tree/e3a6307ad8a2dc2cad35728a2a9908cfd8dd8ef9/packages/arb-bridge-eth + * + * MODIFIED from Offchain Labs' implementation: + * - Changed solidity version to 0.7.6 (pablo@edgeandnode.com) + * + */ + +pragma solidity ^0.7.6; + +interface IMessageProvider { + event InboxMessageDelivered(uint256 indexed messageNum, bytes data); + + event InboxMessageDeliveredFromOrigin(uint256 indexed messageNum); +} diff --git a/contracts/arbitrum/IOutbox.sol b/contracts/arbitrum/IOutbox.sol new file mode 100644 index 000000000..687c86abf --- /dev/null +++ b/contracts/arbitrum/IOutbox.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2021, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Originally copied from: + * https://github.com/OffchainLabs/arbitrum/tree/e3a6307ad8a2dc2cad35728a2a9908cfd8dd8ef9/packages/arb-bridge-eth + * + * MODIFIED from Offchain Labs' implementation: + * - Changed solidity version to 0.7.6 (pablo@edgeandnode.com) + * + */ + +pragma solidity ^0.7.6; + +interface IOutbox { + event OutboxEntryCreated( + uint256 indexed batchNum, + uint256 outboxEntryIndex, + bytes32 outputRoot, + uint256 numInBatch + ); + event OutBoxTransactionExecuted( + address indexed destAddr, + address indexed l2Sender, + uint256 indexed outboxEntryIndex, + uint256 transactionIndex + ); + + function l2ToL1Sender() external view returns (address); + + function l2ToL1Block() external view returns (uint256); + + function l2ToL1EthBlock() external view returns (uint256); + + function l2ToL1Timestamp() external view returns (uint256); + + function l2ToL1BatchNum() external view returns (uint256); + + function l2ToL1OutputId() external view returns (bytes32); + + function processOutgoingMessages(bytes calldata sendsData, uint256[] calldata sendLengths) + external; + + function outboxEntryExists(uint256 batchNum) external view returns (bool); +} diff --git a/contracts/arbitrum/ITokenGateway.sol b/contracts/arbitrum/ITokenGateway.sol new file mode 100644 index 000000000..977fe07f2 --- /dev/null +++ b/contracts/arbitrum/ITokenGateway.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2020, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Originally copied from: + * https://github.com/OffchainLabs/arbitrum/tree/e3a6307ad8a2dc2cad35728a2a9908cfd8dd8ef9/packages/arb-bridge-peripherals + * + * MODIFIED from Offchain Labs' implementation: + * - Changed solidity version to 0.7.6 (pablo@edgeandnode.com) + * + */ + +pragma solidity ^0.7.6; + +interface ITokenGateway { + /// @notice event deprecated in favor of DepositInitiated and WithdrawalInitiated + // event OutboundTransferInitiated( + // address token, + // address indexed _from, + // address indexed _to, + // uint256 indexed _transferId, + // uint256 _amount, + // bytes _data + // ); + + /// @notice event deprecated in favor of DepositFinalized and WithdrawalFinalized + // event InboundTransferFinalized( + // address token, + // address indexed _from, + // address indexed _to, + // uint256 indexed _transferId, + // uint256 _amount, + // bytes _data + // ); + + function outboundTransfer( + address _token, + address _to, + uint256 _amount, + uint256 _maxGas, + uint256 _gasPriceBid, + bytes calldata _data + ) external payable returns (bytes memory); + + function finalizeInboundTransfer( + address _token, + address _from, + address _to, + uint256 _amount, + bytes calldata _data + ) external payable; + + /** + * @notice Calculate the address used when bridging an ERC20 token + * @dev the L1 and L2 address oracles may not always be in sync. + * For example, a custom token may have been registered but not deployed or the contract self destructed. + * @param l1ERC20 address of L1 token + * @return L2 address of a bridged ERC20 token + */ + function calculateL2TokenAddress(address l1ERC20) external view returns (address); +} diff --git a/contracts/arbitrum/L1ArbitrumMessenger.sol b/contracts/arbitrum/L1ArbitrumMessenger.sol new file mode 100644 index 000000000..b893fa262 --- /dev/null +++ b/contracts/arbitrum/L1ArbitrumMessenger.sol @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2020, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Originally copied from: + * https://github.com/OffchainLabs/arbitrum/tree/e3a6307ad8a2dc2cad35728a2a9908cfd8dd8ef9/packages/arb-bridge-peripherals + * + * MODIFIED from Offchain Labs' implementation: + * - Changed solidity version to 0.7.6 (pablo@edgeandnode.com) + * + */ + +pragma solidity ^0.7.6; + +import "./IInbox.sol"; +import "./IOutbox.sol"; + +/// @notice L1 utility contract to assist with L1 <=> L2 interactions +/// @dev this is an abstract contract instead of library so the functions can be easily overriden when testing +abstract contract L1ArbitrumMessenger { + event TxToL2(address indexed _from, address indexed _to, uint256 indexed _seqNum, bytes _data); + + struct L2GasParams { + uint256 _maxSubmissionCost; + uint256 _maxGas; + uint256 _gasPriceBid; + } + + function sendTxToL2( + address _inbox, + address _to, + address _user, + uint256 _l1CallValue, + uint256 _l2CallValue, + L2GasParams memory _l2GasParams, + bytes memory _data + ) internal virtual returns (uint256) { + // alternative function entry point when struggling with the stack size + return + sendTxToL2( + _inbox, + _to, + _user, + _l1CallValue, + _l2CallValue, + _l2GasParams._maxSubmissionCost, + _l2GasParams._maxGas, + _l2GasParams._gasPriceBid, + _data + ); + } + + function sendTxToL2( + address _inbox, + address _to, + address _user, + uint256 _l1CallValue, + uint256 _l2CallValue, + uint256 _maxSubmissionCost, + uint256 _maxGas, + uint256 _gasPriceBid, + bytes memory _data + ) internal virtual returns (uint256) { + uint256 seqNum = IInbox(_inbox).createRetryableTicket{ value: _l1CallValue }( + _to, + _l2CallValue, + _maxSubmissionCost, + _user, + _user, + _maxGas, + _gasPriceBid, + _data + ); + emit TxToL2(_user, _to, seqNum, _data); + return seqNum; + } + + function getBridge(address _inbox) internal view virtual returns (IBridge) { + return IInbox(_inbox).bridge(); + } + + /// @dev the l2ToL1Sender behaves as the tx.origin, the msg.sender should be validated to protect against reentrancies + function getL2ToL1Sender(address _inbox) internal view virtual returns (address) { + IOutbox outbox = IOutbox(getBridge(_inbox).activeOutbox()); + address l2ToL1Sender = outbox.l2ToL1Sender(); + + require(l2ToL1Sender != address(0), "NO_SENDER"); + return l2ToL1Sender; + } +} diff --git a/contracts/arbitrum/L2ArbitrumMessenger.sol b/contracts/arbitrum/L2ArbitrumMessenger.sol new file mode 100644 index 000000000..e03985bef --- /dev/null +++ b/contracts/arbitrum/L2ArbitrumMessenger.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: Apache-2.0 + +/* + * Copyright 2020, Offchain Labs, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Originally copied from: + * https://github.com/OffchainLabs/arbitrum/tree/e3a6307ad8a2dc2cad35728a2a9908cfd8dd8ef9/packages/arb-bridge-peripherals + * + * MODIFIED from Offchain Labs' implementation: + * - Changed solidity version to 0.7.6 (pablo@edgeandnode.com) + * + */ + +pragma solidity ^0.7.6; + +import "arbos-precompiles/arbos/builtin/ArbSys.sol"; + +/// @notice L2 utility contract to assist with L1 <=> L2 interactions +/// @dev this is an abstract contract instead of library so the functions can be easily overriden when testing +abstract contract L2ArbitrumMessenger { + address internal constant ARB_SYS_ADDRESS = address(100); + + event TxToL1(address indexed _from, address indexed _to, uint256 indexed _id, bytes _data); + + function sendTxToL1( + uint256 _l1CallValue, + address _from, + address _to, + bytes memory _data + ) internal virtual returns (uint256) { + uint256 _id = ArbSys(ARB_SYS_ADDRESS).sendTxToL1{ value: _l1CallValue }(_to, _data); + emit TxToL1(_from, _to, _id, _data); + return _id; + } +} diff --git a/contracts/arbitrum/README.md b/contracts/arbitrum/README.md new file mode 100644 index 000000000..abc87553e --- /dev/null +++ b/contracts/arbitrum/README.md @@ -0,0 +1,5 @@ +# Arbitrum contracts + +These contracts have been copied from the [Arbitrum repo](https://github.com/OffchainLabs/arbitrum). + +They are also available as part of the npm packages [arb-bridge-eth](https://www.npmjs.com/package/arb-bridge-eth) and [arb-bridge-peripherals](https://www.npmjs.com/package/arb-bridge-peripherals). The reason for copying them rather than installing those packages is the contracts only support Solidity `^0.6.11`, so we had to change the version to `^0.7.6` for it to be compatible with our other contracts. diff --git a/contracts/curation/Curation.sol b/contracts/curation/Curation.sol index fc59b33d1..565a51a89 100644 --- a/contracts/curation/Curation.sol +++ b/contracts/curation/Curation.sol @@ -2,17 +2,20 @@ pragma solidity ^0.7.6; -import "@openzeppelin/contracts/utils/Address.sol"; -import "@openzeppelin/contracts/math/SafeMath.sol"; -import "@openzeppelin/contracts/proxy/Clones.sol"; - -import "../bancor/BancorFormula.sol"; -import "../upgrades/GraphUpgradeable.sol"; -import "../utils/TokenUtils.sol"; - -import "./CurationStorage.sol"; -import "./ICuration.sol"; -import "./GraphCurationToken.sol"; +import { Address } from "@openzeppelin/contracts/utils/Address.sol"; +import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol"; +import { Clones } from "@openzeppelin/contracts/proxy/Clones.sol"; + +import { BancorFormula } from "../bancor/BancorFormula.sol"; +import { GraphUpgradeable } from "../upgrades/GraphUpgradeable.sol"; +import { TokenUtils } from "../utils/TokenUtils.sol"; +import { IRewardsManager } from "../rewards/IRewardsManager.sol"; +import { Managed } from "../governance/Managed.sol"; +import { IGraphToken } from "../token/IGraphToken.sol"; +import { CurationV1Storage } from "./CurationStorage.sol"; +import { ICuration } from "./ICuration.sol"; +import { IGraphCurationToken } from "./IGraphCurationToken.sol"; +import { GraphCurationToken } from "./GraphCurationToken.sol"; /** * @title Curation contract diff --git a/contracts/curation/CurationStorage.sol b/contracts/curation/CurationStorage.sol index dd2edd18b..a530d9199 100644 --- a/contracts/curation/CurationStorage.sol +++ b/contracts/curation/CurationStorage.sol @@ -2,7 +2,9 @@ pragma solidity ^0.7.6; -import "../governance/Managed.sol"; +import { ICuration } from "./ICuration.sol"; +import { IGraphCurationToken } from "./IGraphCurationToken.sol"; +import { Managed } from "../governance/Managed.sol"; abstract contract CurationV1Storage is Managed, ICuration { // -- Pool -- diff --git a/contracts/gateway/BridgeEscrow.sol b/contracts/gateway/BridgeEscrow.sol new file mode 100644 index 000000000..3c0fa5c1a --- /dev/null +++ b/contracts/gateway/BridgeEscrow.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.7.6; + +import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; + +import { GraphUpgradeable } from "../upgrades/GraphUpgradeable.sol"; +import { Managed } from "../governance/Managed.sol"; +import { IGraphToken } from "../token/IGraphToken.sol"; + +/** + * @title Bridge Escrow + * @dev This contracts acts as a gateway for an L2 bridge (or several). It simply holds GRT and has + * a set of spenders that can transfer the tokens; the L1 side of each L2 bridge has to be + * approved as a spender. + */ +contract BridgeEscrow is Initializable, GraphUpgradeable, Managed { + /** + * @notice Initialize the BridgeEscrow contract. + * @param _controller Address of the Controller that manages this contract + */ + function initialize(address _controller) external onlyImpl initializer { + Managed._initialize(_controller); + } + + /** + * @notice Approve a spender (i.e. a bridge that manages the GRT funds held by the escrow) + * @param _spender Address of the spender that will be approved + */ + function approveAll(address _spender) external onlyGovernor { + graphToken().approve(_spender, type(uint256).max); + } + + /** + * @notice Revoke a spender (i.e. a bridge that will no longer manage the GRT funds held by the escrow) + * @param _spender Address of the spender that will be revoked + */ + function revokeAll(address _spender) external onlyGovernor { + graphToken().approve(_spender, 0); + } +} diff --git a/contracts/gateway/GraphTokenGateway.sol b/contracts/gateway/GraphTokenGateway.sol new file mode 100644 index 000000000..ca2ad4c95 --- /dev/null +++ b/contracts/gateway/GraphTokenGateway.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.7.6; + +import { GraphUpgradeable } from "../upgrades/GraphUpgradeable.sol"; +import { ITokenGateway } from "../arbitrum/ITokenGateway.sol"; +import { Pausable } from "../governance/Pausable.sol"; +import { Managed } from "../governance/Managed.sol"; + +/** + * @title L1/L2 Graph Token Gateway + * @dev This includes everything that's shared between the L1 and L2 sides of the bridge. + */ +abstract contract GraphTokenGateway is GraphUpgradeable, Pausable, Managed, ITokenGateway { + /// @dev Storage gap added in case we need to add state variables to this contract + uint256[50] private __gap; + + /** + * @dev Check if the caller is the Controller's governor or this contract's pause guardian. + */ + modifier onlyGovernorOrGuardian() { + require( + msg.sender == controller.getGovernor() || msg.sender == pauseGuardian, + "Only Governor or Guardian" + ); + _; + } + + /** + * @notice Change the Pause Guardian for this contract + * @param _newPauseGuardian The address of the new Pause Guardian + */ + function setPauseGuardian(address _newPauseGuardian) external onlyGovernor { + require(_newPauseGuardian != address(0), "PauseGuardian must be set"); + _setPauseGuardian(_newPauseGuardian); + } + + /** + * @notice Change the paused state of the contract + * @param _newPaused New value for the pause state (true means the transfers will be paused) + */ + function setPaused(bool _newPaused) external onlyGovernorOrGuardian { + if (!_newPaused) { + _checksBeforeUnpause(); + } + _setPaused(_newPaused); + } + + /** + * @notice Getter to access paused state of this contract + * @return True if the contract is paused, false otherwise + */ + function paused() external view returns (bool) { + return _paused; + } + + /** + * @dev Override the default pausing from Managed to allow pausing this + * particular contract instead of pausing from the Controller. + */ + function _notPaused() internal view override { + require(!_paused, "Paused (contract)"); + } + + /** + * @dev Runs state validation before unpausing, reverts if + * something is not set properly + */ + function _checksBeforeUnpause() internal view virtual; +} diff --git a/contracts/gateway/ICallhookReceiver.sol b/contracts/gateway/ICallhookReceiver.sol new file mode 100644 index 000000000..ff0fbfab1 --- /dev/null +++ b/contracts/gateway/ICallhookReceiver.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +/** + * @title Interface for contracts that can receive callhooks through the Arbitrum GRT bridge + * @dev Any contract that can receive a callhook on L2, sent through the bridge from L1, must + * be allowlisted by the governor, but also implement this interface that contains + * the function that will actually be called by the L2GraphTokenGateway. + */ +pragma solidity ^0.7.6; + +interface ICallhookReceiver { + /** + * @notice Receive tokens with a callhook from the bridge + * @param _from Token sender in L1 + * @param _amount Amount of tokens that were transferred + * @param _data ABI-encoded callhook data + */ + function onTokenTransfer( + address _from, + uint256 _amount, + bytes calldata _data + ) external; +} diff --git a/contracts/gateway/L1GraphTokenGateway.sol b/contracts/gateway/L1GraphTokenGateway.sol new file mode 100644 index 000000000..094d99611 --- /dev/null +++ b/contracts/gateway/L1GraphTokenGateway.sol @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.7.6; +pragma abicoder v2; + +import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/Initializable.sol"; +import { AddressUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; +import { SafeMathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; + +import { L1ArbitrumMessenger } from "../arbitrum/L1ArbitrumMessenger.sol"; +import { IBridge } from "../arbitrum/IBridge.sol"; +import { IInbox } from "../arbitrum/IInbox.sol"; +import { IOutbox } from "../arbitrum/IOutbox.sol"; +import { ITokenGateway } from "../arbitrum/ITokenGateway.sol"; +import { Managed } from "../governance/Managed.sol"; +import { GraphTokenGateway } from "./GraphTokenGateway.sol"; +import { IGraphToken } from "../token/IGraphToken.sol"; + +/** + * @title L1 Graph Token Gateway Contract + * @dev Provides the L1 side of the Ethereum-Arbitrum GRT bridge. Sends GRT to the L2 chain + * by escrowing them and sending a message to the L2 gateway, and receives tokens from L2 by + * releasing them from escrow. + * Based on Offchain Labs' reference implementation and Livepeer's arbitrum-lpt-bridge + * (See: https://github.com/OffchainLabs/arbitrum/tree/master/packages/arb-bridge-peripherals/contracts/tokenbridge + * and https://github.com/livepeer/arbitrum-lpt-bridge) + */ +contract L1GraphTokenGateway is Initializable, GraphTokenGateway, L1ArbitrumMessenger { + using SafeMathUpgradeable for uint256; + + /// Address of the Graph Token contract on L2 + address public l2GRT; + /// Address of the Arbitrum Inbox + address public inbox; + /// Address of the Arbitrum Gateway Router on L1 + address public l1Router; + /// Address of the L2GraphTokenGateway on L2 that is the counterpart of this gateway + address public l2Counterpart; + /// Address of the BridgeEscrow contract that holds the GRT in the bridge + address public escrow; + /// Addresses for which this mapping is true are allowed to send callhooks in outbound transfers + mapping(address => bool) public callhookAllowlist; + + /// Emitted when an outbound transfer is initiated, i.e. tokens are deposited from L1 to L2 + event DepositInitiated( + address l1Token, + address indexed from, + address indexed to, + uint256 indexed sequenceNumber, + uint256 amount + ); + + /// Emitted when an incoming transfer is finalized, i.e tokens are withdrawn from L2 to L1 + event WithdrawalFinalized( + address l1Token, + address indexed from, + address indexed to, + uint256 indexed exitNum, + uint256 amount + ); + + /// Emitted when the Arbitrum Inbox and Gateway Router addresses have been updated + event ArbitrumAddressesSet(address inbox, address l1Router); + /// Emitted when the L2 GRT address has been updated + event L2TokenAddressSet(address l2GRT); + /// Emitted when the counterpart L2GraphTokenGateway address has been updated + event L2CounterpartAddressSet(address l2Counterpart); + /// Emitted when the escrow address has been updated + event EscrowAddressSet(address escrow); + /// Emitted when an address is added to the callhook allowlist + event AddedToCallhookAllowlist(address newAllowlisted); + /// Emitted when an address is removed from the callhook allowlist + event RemovedFromCallhookAllowlist(address notAllowlisted); + + /** + * @dev Allows a function to be called only by the gateway's L2 counterpart. + * The message will actually come from the Arbitrum Bridge, but the Outbox + * can tell us who the sender from L2 is. + */ + modifier onlyL2Counterpart() { + require(inbox != address(0), "INBOX_NOT_SET"); + require(l2Counterpart != address(0), "L2_COUNTERPART_NOT_SET"); + + // a message coming from the counterpart gateway was executed by the bridge + IBridge bridge = IInbox(inbox).bridge(); + require(msg.sender == address(bridge), "NOT_FROM_BRIDGE"); + + // and the outbox reports that the L2 address of the sender is the counterpart gateway + address l2ToL1Sender = IOutbox(bridge.activeOutbox()).l2ToL1Sender(); + require(l2ToL1Sender == l2Counterpart, "ONLY_COUNTERPART_GATEWAY"); + _; + } + + /** + * @notice Initialize the L1GraphTokenGateway contract. + * @dev The contract will be paused. + * Note some parameters have to be set separately as they are generally + * not expected to be available at initialization time: + * - inbox and l1Router using setArbitrumAddresses + * - l2GRT using setL2TokenAddress + * - l2Counterpart using setL2CounterpartAddress + * - escrow using setEscrowAddress + * - allowlisted callhook callers using addToCallhookAllowlist + * - pauseGuardian using setPauseGuardian + * @param _controller Address of the Controller that manages this contract + */ + function initialize(address _controller) external onlyImpl initializer { + Managed._initialize(_controller); + _paused = true; + } + + /** + * @notice Sets the addresses for L1 contracts provided by Arbitrum + * @param _inbox Address of the Inbox that is part of the Arbitrum Bridge + * @param _l1Router Address of the Gateway Router + */ + function setArbitrumAddresses(address _inbox, address _l1Router) external onlyGovernor { + require(_inbox != address(0), "INVALID_INBOX"); + require(_l1Router != address(0), "INVALID_L1_ROUTER"); + require(!callhookAllowlist[_l1Router], "ROUTER_CANT_BE_ALLOWLISTED"); + require(AddressUpgradeable.isContract(_inbox), "INBOX_MUST_BE_CONTRACT"); + require(AddressUpgradeable.isContract(_l1Router), "ROUTER_MUST_BE_CONTRACT"); + inbox = _inbox; + l1Router = _l1Router; + emit ArbitrumAddressesSet(_inbox, _l1Router); + } + + /** + * @notice Sets the address of the L2 Graph Token + * @param _l2GRT Address of the GRT contract on L2 + */ + function setL2TokenAddress(address _l2GRT) external onlyGovernor { + require(_l2GRT != address(0), "INVALID_L2_GRT"); + l2GRT = _l2GRT; + emit L2TokenAddressSet(_l2GRT); + } + + /** + * @notice Sets the address of the counterpart gateway on L2 + * @param _l2Counterpart Address of the corresponding L2GraphTokenGateway on Arbitrum + */ + function setL2CounterpartAddress(address _l2Counterpart) external onlyGovernor { + require(_l2Counterpart != address(0), "INVALID_L2_COUNTERPART"); + l2Counterpart = _l2Counterpart; + emit L2CounterpartAddressSet(_l2Counterpart); + } + + /** + * @notice Sets the address of the escrow contract on L1 + * @param _escrow Address of the BridgeEscrow + */ + function setEscrowAddress(address _escrow) external onlyGovernor { + require(_escrow != address(0), "INVALID_ESCROW"); + require(AddressUpgradeable.isContract(_escrow), "MUST_BE_CONTRACT"); + escrow = _escrow; + emit EscrowAddressSet(_escrow); + } + + /** + * @notice Adds an address to the callhook allowlist. + * This address will be allowed to include callhooks when transferring tokens. + * @param _newAllowlisted Address to add to the allowlist + */ + function addToCallhookAllowlist(address _newAllowlisted) external onlyGovernor { + require(_newAllowlisted != address(0), "INVALID_ADDRESS"); + require(_newAllowlisted != l1Router, "CANT_ALLOW_ROUTER"); + require(AddressUpgradeable.isContract(_newAllowlisted), "MUST_BE_CONTRACT"); + require(!callhookAllowlist[_newAllowlisted], "ALREADY_ALLOWLISTED"); + callhookAllowlist[_newAllowlisted] = true; + emit AddedToCallhookAllowlist(_newAllowlisted); + } + + /** + * @notice Removes an address from the callhook allowlist. + * This address will no longer be allowed to include callhooks when transferring tokens. + * @param _notAllowlisted Address to remove from the allowlist + */ + function removeFromCallhookAllowlist(address _notAllowlisted) external onlyGovernor { + require(_notAllowlisted != address(0), "INVALID_ADDRESS"); + require(callhookAllowlist[_notAllowlisted], "NOT_ALLOWLISTED"); + callhookAllowlist[_notAllowlisted] = false; + emit RemovedFromCallhookAllowlist(_notAllowlisted); + } + + /** + * @notice Creates and sends a retryable ticket to transfer GRT to L2 using the Arbitrum Inbox. + * The tokens are escrowed by the gateway until they are withdrawn back to L1. + * The ticket must be redeemed on L2 to receive tokens at the specified address. + * Note that the caller must previously allow the gateway to spend the specified amount of GRT. + * @dev maxGas and gasPriceBid must be set using Arbitrum's NodeInterface.estimateRetryableTicket method. + * Also note that allowlisted senders (some protocol contracts) can include additional calldata + * for a callhook to be executed on the L2 side when the tokens are received. In this case, the L2 transaction + * can revert if the callhook reverts, potentially locking the tokens on the bridge if the callhook + * never succeeds. This requires extra care when adding contracts to the allowlist, but is necessary to ensure that + * the tickets can be retried in the case of a temporary failure, and to ensure the atomicity of callhooks + * with token transfers. + * @param _l1Token L1 Address of the GRT contract (needed for compatibility with Arbitrum Gateway Router) + * @param _to Recipient address on L2 + * @param _amount Amount of tokens to transfer + * @param _maxGas Gas limit for L2 execution of the ticket + * @param _gasPriceBid Price per gas on L2 + * @param _data Encoded maxSubmissionCost and sender address along with additional calldata + * @return Sequence number of the retryable ticket created by Inbox + */ + function outboundTransfer( + address _l1Token, + address _to, + uint256 _amount, + uint256 _maxGas, + uint256 _gasPriceBid, + bytes calldata _data + ) external payable override notPaused returns (bytes memory) { + IGraphToken token = graphToken(); + require(_amount != 0, "INVALID_ZERO_AMOUNT"); + require(_l1Token == address(token), "TOKEN_NOT_GRT"); + require(_to != address(0), "INVALID_DESTINATION"); + + // nested scopes to avoid stack too deep errors + address from; + uint256 seqNum; + { + uint256 maxSubmissionCost; + bytes memory outboundCalldata; + { + bytes memory extraData; + (from, maxSubmissionCost, extraData) = _parseOutboundData(_data); + require( + extraData.length == 0 || callhookAllowlist[msg.sender] == true, + "CALL_HOOK_DATA_NOT_ALLOWED" + ); + require(maxSubmissionCost != 0, "NO_SUBMISSION_COST"); + outboundCalldata = getOutboundCalldata(_l1Token, from, _to, _amount, extraData); + } + { + L2GasParams memory gasParams = L2GasParams( + maxSubmissionCost, + _maxGas, + _gasPriceBid + ); + // transfer tokens to escrow + token.transferFrom(from, escrow, _amount); + seqNum = sendTxToL2( + inbox, + l2Counterpart, + from, + msg.value, + 0, + gasParams, + outboundCalldata + ); + } + } + emit DepositInitiated(_l1Token, from, _to, seqNum, _amount); + + return abi.encode(seqNum); + } + + /** + * @notice Receives withdrawn tokens from L2 + * The equivalent tokens are released from escrow and sent to the destination. + * @dev can only accept transactions coming from the L2 GRT Gateway. + * The last parameter is unused but kept for compatibility with Arbitrum gateways, + * and the encoded exitNum is assumed to be 0. + * @param _l1Token L1 Address of the GRT contract (needed for compatibility with Arbitrum Gateway Router) + * @param _from Address of the sender + * @param _to Recipient address on L1 + * @param _amount Amount of tokens transferred + */ + function finalizeInboundTransfer( + address _l1Token, + address _from, + address _to, + uint256 _amount, + bytes calldata // _data, contains exitNum, unused by this contract + ) external payable override notPaused onlyL2Counterpart { + IGraphToken token = graphToken(); + require(_l1Token == address(token), "TOKEN_NOT_GRT"); + + uint256 escrowBalance = token.balanceOf(escrow); + // If the bridge doesn't have enough tokens, something's very wrong! + require(_amount <= escrowBalance, "BRIDGE_OUT_OF_FUNDS"); + token.transferFrom(escrow, _to, _amount); + + emit WithdrawalFinalized(_l1Token, _from, _to, 0, _amount); + } + + /** + * @notice Calculate the L2 address of a bridged token + * @dev In our case, this would only work for GRT. + * @param _l1ERC20 address of L1 GRT contract + * @return L2 address of the bridged GRT token + */ + function calculateL2TokenAddress(address _l1ERC20) external view override returns (address) { + IGraphToken token = graphToken(); + if (_l1ERC20 != address(token)) { + return address(0); + } + return l2GRT; + } + + /** + * @notice Get the address of the L2GraphTokenGateway + * @dev This is added for compatibility with the Arbitrum Router's + * gateway registration process. + * @return Address of the L2 gateway connected to this gateway + */ + function counterpartGateway() external view returns (address) { + return l2Counterpart; + } + + /** + * @notice Creates calldata required to create a retryable ticket + * @dev encodes the target function with its params which + * will be called on L2 when the retryable ticket is redeemed + * @param _l1Token Address of the Graph token contract on L1 + * @param _from Address on L1 from which we're transferring tokens + * @param _to Address on L2 to which we're transferring tokens + * @param _amount Amount of GRT to transfer + * @param _data Additional call data for the L2 transaction, which must be empty unless the caller is allowlisted + * @return Encoded calldata (including function selector) for the L2 transaction + */ + function getOutboundCalldata( + address _l1Token, + address _from, + address _to, + uint256 _amount, + bytes memory _data + ) public pure returns (bytes memory) { + return + abi.encodeWithSelector( + ITokenGateway.finalizeInboundTransfer.selector, + _l1Token, + _from, + _to, + _amount, + _data + ); + } + + /** + * @dev Runs state validation before unpausing, reverts if + * something is not set properly + */ + function _checksBeforeUnpause() internal view override { + require(inbox != address(0), "INBOX_NOT_SET"); + require(l1Router != address(0), "ROUTER_NOT_SET"); + require(l2Counterpart != address(0), "L2_COUNTERPART_NOT_SET"); + require(escrow != address(0), "ESCROW_NOT_SET"); + } + + /** + * @notice Decodes calldata required for migration of tokens + * @dev Data must include maxSubmissionCost, extraData can be left empty. When the router + * sends an outbound message, data also contains the from address. + * @param _data encoded callhook data + * @return Sender of the tx + * @return Base ether value required to keep retryable ticket alive + * @return Additional data sent to L2 + */ + function _parseOutboundData(bytes calldata _data) + private + view + returns ( + address, + uint256, + bytes memory + ) + { + address from; + uint256 maxSubmissionCost; + bytes memory extraData; + if (msg.sender == l1Router) { + // Data encoded by the Gateway Router includes the sender address + (from, extraData) = abi.decode(_data, (address, bytes)); + } else { + from = msg.sender; + extraData = _data; + } + // User-encoded data contains the max retryable ticket submission cost + // and additional L2 calldata + (maxSubmissionCost, extraData) = abi.decode(extraData, (uint256, bytes)); + return (from, maxSubmissionCost, extraData); + } +} diff --git a/contracts/governance/Controller.sol b/contracts/governance/Controller.sol index 2e4418a59..bc287d2be 100644 --- a/contracts/governance/Controller.sol +++ b/contracts/governance/Controller.sol @@ -2,10 +2,10 @@ pragma solidity ^0.7.6; -import "./IController.sol"; -import "./IManaged.sol"; -import "./Governed.sol"; -import "./Pausable.sol"; +import { IController } from "./IController.sol"; +import { IManaged } from "./IManaged.sol"; +import { Governed } from "./Governed.sol"; +import { Pausable } from "./Pausable.sol"; /** * @title Graph Controller contract @@ -13,13 +13,14 @@ import "./Pausable.sol"; * https://github.com/livepeer/protocol/blob/streamflow/contracts/Controller.sol */ contract Controller is Governed, Pausable, IController { - // Track contract ids to contract proxy address - mapping(bytes32 => address) private registry; + /// @dev Track contract ids to contract proxy address + mapping(bytes32 => address) private _registry; + /// Emitted when the proxy address for a protocol contract has been set event SetContractProxy(bytes32 indexed id, address contractAddress); /** - * @dev Contract constructor. + * @notice Controller contract constructor. */ constructor() { Governed._initialize(msg.sender); @@ -58,7 +59,7 @@ contract Controller is Governed, Pausable, IController { onlyGovernor { require(_contractAddress != address(0), "Contract address must be set"); - registry[_id] = _contractAddress; + _registry[_id] = _contractAddress; emit SetContractProxy(_id, _contractAddress); } @@ -67,16 +68,17 @@ contract Controller is Governed, Pausable, IController { * @param _id Contract id (keccak256 hash of contract name) */ function unsetContractProxy(bytes32 _id) external override onlyGovernor { - registry[_id] = address(0); + _registry[_id] = address(0); emit SetContractProxy(_id, address(0)); } /** * @notice Get contract proxy address by its id * @param _id Contract id + * @return Address of the proxy contract for the provided id */ - function getContractProxy(bytes32 _id) public view override returns (address) { - return registry[_id]; + function getContractProxy(bytes32 _id) external view override returns (address) { + return _registry[_id]; } /** @@ -86,7 +88,7 @@ contract Controller is Governed, Pausable, IController { */ function updateController(bytes32 _id, address _controller) external override onlyGovernor { require(_controller != address(0), "Controller must be set"); - return IManaged(registry[_id]).setController(_controller); + return IManaged(_registry[_id]).setController(_controller); } // -- Pausing -- @@ -94,17 +96,19 @@ contract Controller is Governed, Pausable, IController { /** * @notice Change the partial paused state of the contract * Partial pause is intended as a partial pause of the protocol + * @param _toPause True if the contracts should be (partially) paused, false otherwise */ - function setPartialPaused(bool _partialPaused) external override onlyGovernorOrGuardian { - _setPartialPaused(_partialPaused); + function setPartialPaused(bool _toPause) external override onlyGovernorOrGuardian { + _setPartialPaused(_toPause); } /** * @notice Change the paused state of the contract * Full pause most of protocol functions + * @param _toPause True if the contracts should be paused, false otherwise */ - function setPaused(bool _paused) external override onlyGovernorOrGuardian { - _setPaused(_paused); + function setPaused(bool _toPause) external override onlyGovernorOrGuardian { + _setPaused(_toPause); } /** @@ -118,6 +122,7 @@ contract Controller is Governed, Pausable, IController { /** * @notice Getter to access paused + * @return True if the contracts are paused, false otherwise */ function paused() external view override returns (bool) { return _paused; @@ -125,6 +130,7 @@ contract Controller is Governed, Pausable, IController { /** * @notice Getter to access partial pause status + * @return True if the contracts are partially paused, false otherwise */ function partialPaused() external view override returns (bool) { return _partialPaused; diff --git a/contracts/governance/Governed.sol b/contracts/governance/Governed.sol index 2a87389d9..f692b2d19 100644 --- a/contracts/governance/Governed.sol +++ b/contracts/governance/Governed.sol @@ -6,15 +6,19 @@ pragma solidity ^0.7.6; * @title Graph Governance contract * @dev All contracts that will be owned by a Governor entity should extend this contract. */ -contract Governed { +abstract contract Governed { // -- State -- + /// Address of the governor address public governor; + /// Address of the new governor that is pending acceptance address public pendingGovernor; // -- Events -- + /// Emitted when a new owner/governor has been set, but is pending acceptance event NewPendingOwnership(address indexed from, address indexed to); + /// Emitted when a new owner/governor has accepted their role event NewOwnership(address indexed from, address indexed to); /** @@ -26,14 +30,15 @@ contract Governed { } /** - * @dev Initialize the governor to the contract caller. + * @dev Initialize the governor for this contract + * @param _initGovernor Address of the governor */ function _initialize(address _initGovernor) internal { governor = _initGovernor; } /** - * @dev Admin function to begin change of governor. The `_newGovernor` must call + * @notice Admin function to begin change of governor. The `_newGovernor` must call * `acceptOwnership` to finalize the transfer. * @param _newGovernor Address of new `governor` */ @@ -47,19 +52,20 @@ contract Governed { } /** - * @dev Admin function for pending governor to accept role and update governor. + * @notice Admin function for pending governor to accept role and update governor. * This function must called by the pending governor. */ function acceptOwnership() external { + address oldPendingGovernor = pendingGovernor; + require( - pendingGovernor != address(0) && msg.sender == pendingGovernor, + oldPendingGovernor != address(0) && msg.sender == oldPendingGovernor, "Caller must be pending governor" ); address oldGovernor = governor; - address oldPendingGovernor = pendingGovernor; - governor = pendingGovernor; + governor = oldPendingGovernor; pendingGovernor = address(0); emit NewOwnership(oldGovernor, governor); diff --git a/contracts/governance/Managed.sol b/contracts/governance/Managed.sol index 716403111..6b8fe624e 100644 --- a/contracts/governance/Managed.sol +++ b/contracts/governance/Managed.sol @@ -2,13 +2,16 @@ pragma solidity ^0.7.6; -import "./IController.sol"; +import { IController } from "./IController.sol"; -import "../curation/ICuration.sol"; -import "../epochs/IEpochManager.sol"; -import "../rewards/IRewardsManager.sol"; -import "../staking/IStaking.sol"; -import "../token/IGraphToken.sol"; +import { ICuration } from "../curation/ICuration.sol"; +import { IEpochManager } from "../epochs/IEpochManager.sol"; +import { IRewardsManager } from "../rewards/IRewardsManager.sol"; +import { IStaking } from "../staking/IStaking.sol"; +import { IGraphToken } from "../token/IGraphToken.sol"; +import { ITokenGateway } from "../arbitrum/ITokenGateway.sol"; + +import { IManaged } from "./IManaged.sol"; /** * @title Graph Managed contract @@ -19,60 +22,92 @@ import "../token/IGraphToken.sol"; * Inspired by Livepeer: * https://github.com/livepeer/protocol/blob/streamflow/contracts/Controller.sol */ -contract Managed { +abstract contract Managed is IManaged { // -- State -- - // Controller that contract is registered with + /// Controller that contract is registered with IController public controller; - mapping(bytes32 => address) private addressCache; + /// @dev Cache for the addresses of the contracts retrieved from the controller + mapping(bytes32 => address) private _addressCache; + /// @dev Gap for future storage variables uint256[10] private __gap; + // Immutables + bytes32 private immutable CURATION = keccak256("Curation"); + bytes32 private immutable EPOCH_MANAGER = keccak256("EpochManager"); + bytes32 private immutable REWARDS_MANAGER = keccak256("RewardsManager"); + bytes32 private immutable STAKING = keccak256("Staking"); + bytes32 private immutable GRAPH_TOKEN = keccak256("GraphToken"); + bytes32 private immutable GRAPH_TOKEN_GATEWAY = keccak256("GraphTokenGateway"); + // -- Events -- + /// Emitted when a contract parameter has been updated event ParameterUpdated(string param); + /// Emitted when the controller address has been set event SetController(address controller); - /** - * @dev Emitted when contract with `nameHash` is synced to `contractAddress`. - */ + /// Emitted when contract with `nameHash` is synced to `contractAddress`. event ContractSynced(bytes32 indexed nameHash, address contractAddress); // -- Modifiers -- + /** + * @dev Revert if the controller is paused or partially paused + */ function _notPartialPaused() internal view { require(!controller.paused(), "Paused"); require(!controller.partialPaused(), "Partial-paused"); } - function _notPaused() internal view { + /** + * @dev Revert if the controller is paused + */ + function _notPaused() internal view virtual { require(!controller.paused(), "Paused"); } + /** + * @dev Revert if the caller is not the governor + */ function _onlyGovernor() internal view { - require(msg.sender == controller.getGovernor(), "Caller must be Controller governor"); + require(msg.sender == controller.getGovernor(), "Only Controller governor"); } + /** + * @dev Revert if the caller is not the Controller + */ function _onlyController() internal view { require(msg.sender == address(controller), "Caller must be Controller"); } + /** + * @dev Revert if the controller is paused or partially paused + */ modifier notPartialPaused() { _notPartialPaused(); _; } + /** + * @dev Revert if the controller is paused + */ modifier notPaused() { _notPaused(); _; } - // Check if sender is controller. + /** + * @dev Revert if the caller is not the Controller + */ modifier onlyController() { _onlyController(); _; } - // Check if sender is the governor. + /** + * @dev Revert if the caller is not the governor + */ modifier onlyGovernor() { _onlyGovernor(); _; @@ -81,7 +116,8 @@ contract Managed { // -- Functions -- /** - * @dev Initialize the controller. + * @dev Initialize a Managed contract + * @param _controller Address for the Controller that manages this contract */ function _initialize(address _controller) internal { _setController(_controller); @@ -91,7 +127,7 @@ contract Managed { * @notice Set Controller. Only callable by current controller. * @param _controller Controller contract address */ - function setController(address _controller) external onlyController { + function setController(address _controller) external override onlyController { _setController(_controller); } @@ -106,51 +142,60 @@ contract Managed { } /** - * @dev Return Curation interface. + * @dev Return Curation interface * @return Curation contract registered with Controller */ function curation() internal view returns (ICuration) { - return ICuration(_resolveContract(keccak256("Curation"))); + return ICuration(_resolveContract(CURATION)); } /** - * @dev Return EpochManager interface. + * @dev Return EpochManager interface * @return Epoch manager contract registered with Controller */ function epochManager() internal view returns (IEpochManager) { - return IEpochManager(_resolveContract(keccak256("EpochManager"))); + return IEpochManager(_resolveContract(EPOCH_MANAGER)); } /** - * @dev Return RewardsManager interface. + * @dev Return RewardsManager interface * @return Rewards manager contract registered with Controller */ function rewardsManager() internal view returns (IRewardsManager) { - return IRewardsManager(_resolveContract(keccak256("RewardsManager"))); + return IRewardsManager(_resolveContract(REWARDS_MANAGER)); } /** - * @dev Return Staking interface. + * @dev Return Staking interface * @return Staking contract registered with Controller */ function staking() internal view returns (IStaking) { - return IStaking(_resolveContract(keccak256("Staking"))); + return IStaking(_resolveContract(STAKING)); } /** - * @dev Return GraphToken interface. + * @dev Return GraphToken interface * @return Graph token contract registered with Controller */ function graphToken() internal view returns (IGraphToken) { - return IGraphToken(_resolveContract(keccak256("GraphToken"))); + return IGraphToken(_resolveContract(GRAPH_TOKEN)); + } + + /** + * @dev Return GraphTokenGateway (L1 or L2) interface + * @return Graph token gateway contract registered with Controller + */ + function graphTokenGateway() internal view returns (ITokenGateway) { + return ITokenGateway(_resolveContract(GRAPH_TOKEN_GATEWAY)); } /** - * @dev Resolve a contract address from the cache or the Controller if not found. + * @dev Resolve a contract address from the cache or the Controller if not found + * @param _nameHash keccak256 hash of the contract name * @return Address of the contract */ function _resolveContract(bytes32 _nameHash) internal view returns (address) { - address contractAddress = addressCache[_nameHash]; + address contractAddress = _addressCache[_nameHash]; if (contractAddress == address(0)) { contractAddress = controller.getContractProxy(_nameHash); } @@ -164,15 +209,15 @@ contract Managed { function _syncContract(string memory _name) internal { bytes32 nameHash = keccak256(abi.encodePacked(_name)); address contractAddress = controller.getContractProxy(nameHash); - if (addressCache[nameHash] != contractAddress) { - addressCache[nameHash] = contractAddress; + if (_addressCache[nameHash] != contractAddress) { + _addressCache[nameHash] = contractAddress; emit ContractSynced(nameHash, contractAddress); } } /** - * @dev Sync protocol contract addresses from the Controller registry. - * This function will cache all the contracts using the latest addresses + * @notice Sync protocol contract addresses from the Controller registry + * @dev This function will cache all the contracts using the latest addresses * Anyone can call the function whenever a Proxy contract change in the * controller to ensure the protocol is using the latest version */ @@ -182,5 +227,6 @@ contract Managed { _syncContract("RewardsManager"); _syncContract("Staking"); _syncContract("GraphToken"); + _syncContract("GraphTokenGateway"); } } diff --git a/contracts/governance/Pausable.sol b/contracts/governance/Pausable.sol index b1ecbdf30..552b0aa15 100644 --- a/contracts/governance/Pausable.sol +++ b/contracts/governance/Pausable.sol @@ -2,26 +2,36 @@ pragma solidity ^0.7.6; -contract Pausable { - // Partial paused paused exit and enter functions for GRT, but not internal - // functions, such as allocating +abstract contract Pausable { + /** + * @dev "Partial paused" pauses exit and enter functions for GRT, but not internal + * functions, such as allocating + */ bool internal _partialPaused; - // Paused will pause all major protocol functions + /** + * @dev Paused will pause all major protocol functions + */ bool internal _paused; - // Time last paused for both pauses + /// Timestamp for the last time the partial pause was set uint256 public lastPausePartialTime; + /// Timestamp for the last time the full pause was set uint256 public lastPauseTime; - // Pause guardian is a separate entity from the governor that can pause + /// Pause guardian is a separate entity from the governor that can + /// pause and unpause the protocol, fully or partially address public pauseGuardian; + /// Emitted when the partial pause state changed event PartialPauseChanged(bool isPaused); + /// Emitted when the full pause state changed event PauseChanged(bool isPaused); + /// Emitted when the pause guardian is changed event NewPauseGuardian(address indexed oldPauseGuardian, address indexed pauseGuardian); /** - * @notice Change the partial paused state of the contract + * @dev Change the partial paused state of the contract + * @param _toPause New value for the partial pause state (true means the contracts will be partially paused) */ function _setPartialPaused(bool _toPause) internal { if (_toPause == _partialPaused) { @@ -35,7 +45,8 @@ contract Pausable { } /** - * @notice Change the paused state of the contract + * @dev Change the paused state of the contract + * @param _toPause New value for the pause state (true means the contracts will be paused) */ function _setPaused(bool _toPause) internal { if (_toPause == _paused) { @@ -49,7 +60,7 @@ contract Pausable { } /** - * @notice Change the Pause Guardian + * @dev Change the Pause Guardian * @param newPauseGuardian The address of the new Pause Guardian */ function _setPauseGuardian(address newPauseGuardian) internal { diff --git a/contracts/l2/gateway/L2GraphTokenGateway.sol b/contracts/l2/gateway/L2GraphTokenGateway.sol new file mode 100644 index 000000000..7af102fb4 --- /dev/null +++ b/contracts/l2/gateway/L2GraphTokenGateway.sol @@ -0,0 +1,304 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.7.6; +pragma abicoder v2; + +import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; +import { SafeMathUpgradeable } from "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; + +import { L2ArbitrumMessenger } from "../../arbitrum/L2ArbitrumMessenger.sol"; +import { AddressAliasHelper } from "../../arbitrum/AddressAliasHelper.sol"; +import { ITokenGateway } from "../../arbitrum/ITokenGateway.sol"; +import { Managed } from "../../governance/Managed.sol"; +import { GraphTokenGateway } from "../../gateway/GraphTokenGateway.sol"; +import { ICallhookReceiver } from "../../gateway/ICallhookReceiver.sol"; +import { L2GraphToken } from "../token/L2GraphToken.sol"; + +/** + * @title L2 Graph Token Gateway Contract + * @dev Provides the L2 side of the Ethereum-Arbitrum GRT bridge. Receives GRT from the L1 chain + * and mints them on the L2 side. Sends GRT back to L1 by burning them on the L2 side. + * Based on Offchain Labs' reference implementation and Livepeer's arbitrum-lpt-bridge + * (See: https://github.com/OffchainLabs/arbitrum/tree/master/packages/arb-bridge-peripherals/contracts/tokenbridge + * and https://github.com/livepeer/arbitrum-lpt-bridge) + */ +contract L2GraphTokenGateway is GraphTokenGateway, L2ArbitrumMessenger, ReentrancyGuardUpgradeable { + using SafeMathUpgradeable for uint256; + + /// Address of the Graph Token contract on L1 + address public l1GRT; + /// Address of the L1GraphTokenGateway that is the counterpart of this gateway on L1 + address public l1Counterpart; + /// Address of the Arbitrum Gateway Router on L2 + address public l2Router; + + /// @dev Calldata included in an outbound transfer, stored as a structure for convenience and stack depth + struct OutboundCalldata { + address from; + bytes extraData; + } + + /// Emitted when an incoming transfer is finalized, i.e. tokens were deposited from L1 to L2 + event DepositFinalized( + address indexed l1Token, + address indexed from, + address indexed to, + uint256 amount + ); + + /// Emitted when an outbound transfer is initiated, i.e. tokens are being withdrawn from L2 back to L1 + event WithdrawalInitiated( + address l1Token, + address indexed from, + address indexed to, + uint256 indexed l2ToL1Id, + uint256 exitNum, + uint256 amount + ); + + /// Emitted when the Arbitrum Gateway Router address on L2 has been updated + event L2RouterSet(address l2Router); + /// Emitted when the L1 Graph Token address has been updated + event L1TokenAddressSet(address l1GRT); + /// Emitted when the address of the counterpart gateway on L1 has been updated + event L1CounterpartAddressSet(address l1Counterpart); + + /** + * @dev Checks that the sender is the L2 alias of the counterpart + * gateway on L1. + */ + modifier onlyL1Counterpart() { + require( + msg.sender == AddressAliasHelper.applyL1ToL2Alias(l1Counterpart), + "ONLY_COUNTERPART_GATEWAY" + ); + _; + } + + /** + * @notice Initialize the L2GraphTokenGateway contract. + * @dev The contract will be paused. + * Note some parameters have to be set separately as they are generally + * not expected to be available at initialization time: + * - l2Router using setL2Router + * - l1GRT using setL1TokenAddress + * - l1Counterpart using setL1CounterpartAddress + * - pauseGuardian using setPauseGuardian + * @param _controller Address of the Controller that manages this contract + */ + function initialize(address _controller) external onlyImpl initializer { + Managed._initialize(_controller); + _paused = true; + __ReentrancyGuard_init(); + } + + /** + * @notice Sets the address of the Arbitrum Gateway Router on L2 + * @param _l2Router Address of the L2 Router (provided by Arbitrum) + */ + function setL2Router(address _l2Router) external onlyGovernor { + require(_l2Router != address(0), "INVALID_L2_ROUTER"); + l2Router = _l2Router; + emit L2RouterSet(_l2Router); + } + + /** + * @notice Sets the address of the Graph Token on L1 + * @param _l1GRT L1 address of the Graph Token contract + */ + function setL1TokenAddress(address _l1GRT) external onlyGovernor { + require(_l1GRT != address(0), "INVALID_L1_GRT"); + l1GRT = _l1GRT; + emit L1TokenAddressSet(_l1GRT); + } + + /** + * @notice Sets the address of the counterpart gateway on L1 + * @param _l1Counterpart Address of the L1GraphTokenGateway on L1 + */ + function setL1CounterpartAddress(address _l1Counterpart) external onlyGovernor { + require(_l1Counterpart != address(0), "INVALID_L1_COUNTERPART"); + l1Counterpart = _l1Counterpart; + emit L1CounterpartAddressSet(_l1Counterpart); + } + + /** + * @notice Burns L2 tokens and initiates a transfer to L1. + * The tokens will be received on L1 only after the wait period (7 days) is over, + * and will require an Outbox.executeTransaction to finalize. + * @dev no additional callhook data is allowed + * @param _l1Token L1 Address of GRT (needed for compatibility with Arbitrum Gateway Router) + * @param _to Recipient address on L1 + * @param _amount Amount of tokens to burn + * @param _data Contains sender and additional data to send to L1 + * @return ID of the withdraw tx + */ + function outboundTransfer( + address _l1Token, + address _to, + uint256 _amount, + bytes calldata _data + ) external returns (bytes memory) { + return outboundTransfer(_l1Token, _to, _amount, 0, 0, _data); + } + + /** + * @notice Receives token amount from L1 and mints the equivalent tokens to the receiving address + * @dev Only accepts transactions from the L1 GRT Gateway. + * The function is payable for ITokenGateway compatibility, but msg.value must be zero. + * Note that allowlisted senders (some protocol contracts) can include additional calldata + * for a callhook to be executed on the L2 side when the tokens are received. In this case, the L2 transaction + * can revert if the callhook reverts, potentially locking the tokens on the bridge if the callhook + * never succeeds. This requires extra care when adding contracts to the allowlist, but is necessary to ensure that + * the tickets can be retried in the case of a temporary failure, and to ensure the atomicity of callhooks + * with token transfers. + * @param _l1Token L1 Address of GRT + * @param _from Address of the sender on L1 + * @param _to Recipient address on L2 + * @param _amount Amount of tokens transferred + * @param _data Extra callhook data, only used when the sender is allowlisted + */ + function finalizeInboundTransfer( + address _l1Token, + address _from, + address _to, + uint256 _amount, + bytes calldata _data + ) external payable override nonReentrant notPaused onlyL1Counterpart { + require(_l1Token == l1GRT, "TOKEN_NOT_GRT"); + require(msg.value == 0, "INVALID_NONZERO_VALUE"); + + L2GraphToken(calculateL2TokenAddress(l1GRT)).bridgeMint(_to, _amount); + + if (_data.length > 0) { + ICallhookReceiver(_to).onTokenTransfer(_from, _amount, _data); + } + + emit DepositFinalized(_l1Token, _from, _to, _amount); + } + + /** + * @notice Burns L2 tokens and initiates a transfer to L1. + * The tokens will be available on L1 only after the wait period (7 days) is over, + * and will require an Outbox.executeTransaction to finalize. + * Note that the caller must previously allow the gateway to spend the specified amount of GRT. + * @dev no additional callhook data is allowed. The two unused params are needed + * for compatibility with Arbitrum's gateway router. + * The function is payable for ITokenGateway compatibility, but msg.value must be zero. + * @param _l1Token L1 Address of GRT (needed for compatibility with Arbitrum Gateway Router) + * @param _to Recipient address on L1 + * @param _amount Amount of tokens to burn + * @param _data Contains sender and additional data (always empty) to send to L1 + * @return ID of the withdraw transaction + */ + function outboundTransfer( + address _l1Token, + address _to, + uint256 _amount, + uint256, // unused on L2 + uint256, // unused on L2 + bytes calldata _data + ) public payable override nonReentrant notPaused returns (bytes memory) { + require(_l1Token == l1GRT, "TOKEN_NOT_GRT"); + require(_amount != 0, "INVALID_ZERO_AMOUNT"); + require(msg.value == 0, "INVALID_NONZERO_VALUE"); + require(_to != address(0), "INVALID_DESTINATION"); + + OutboundCalldata memory outboundCalldata; + + (outboundCalldata.from, outboundCalldata.extraData) = _parseOutboundData(_data); + require(outboundCalldata.extraData.length == 0, "CALL_HOOK_DATA_NOT_ALLOWED"); + + // from needs to approve this contract to burn the amount first + L2GraphToken(calculateL2TokenAddress(l1GRT)).bridgeBurn(outboundCalldata.from, _amount); + + uint256 id = sendTxToL1( + 0, + outboundCalldata.from, + l1Counterpart, + getOutboundCalldata( + _l1Token, + outboundCalldata.from, + _to, + _amount, + outboundCalldata.extraData + ) + ); + + // we don't need to track exitNums (b/c we have no fast exits) so we always use 0 + emit WithdrawalInitiated(_l1Token, outboundCalldata.from, _to, id, 0, _amount); + + return abi.encode(id); + } + + /** + * @notice Calculate the L2 address of a bridged token + * @dev In our case, this would only work for GRT. + * @param l1ERC20 address of L1 GRT contract + * @return L2 address of the bridged GRT token + */ + function calculateL2TokenAddress(address l1ERC20) public view override returns (address) { + if (l1ERC20 != l1GRT) { + return address(0); + } + return address(graphToken()); + } + + /** + * @notice Creates calldata required to send tx to L1 + * @dev encodes the target function with its params which + * will be called on L1 when the message is received on L1 + * @param _token Address of the token on L1 + * @param _from Address of the token sender on L2 + * @param _to Address to which we're sending tokens on L1 + * @param _amount Amount of GRT to transfer + * @param _data Additional calldata for the transaction + * @return Calldata for a transaction sent to L1 + */ + function getOutboundCalldata( + address _token, + address _from, + address _to, + uint256 _amount, + bytes memory _data + ) public pure returns (bytes memory) { + return + abi.encodeWithSelector( + ITokenGateway.finalizeInboundTransfer.selector, + _token, + _from, + _to, + _amount, + abi.encode(0, _data) // we don't need to track exitNums (b/c we have no fast exits) so we always use 0 + ); + } + + /** + * @dev Runs state validation before unpausing, reverts if + * something is not set properly + */ + function _checksBeforeUnpause() internal view override { + require(l2Router != address(0), "L2_ROUTER_NOT_SET"); + require(l1Counterpart != address(0), "L1_COUNTERPART_NOT_SET"); + require(l1GRT != address(0), "L1_GRT_NOT_SET"); + } + + /** + * @notice Decodes calldata required for migration of tokens + * @dev extraData can be left empty + * @param _data Encoded callhook data + * @return Sender of the tx + * @return Any other data sent to L1 + */ + function _parseOutboundData(bytes calldata _data) private view returns (address, bytes memory) { + address from; + bytes memory extraData; + if (msg.sender == l2Router) { + (from, extraData) = abi.decode(_data, (address, bytes)); + } else { + from = msg.sender; + extraData = _data; + } + return (from, extraData); + } +} diff --git a/contracts/l2/token/GraphTokenUpgradeable.sol b/contracts/l2/token/GraphTokenUpgradeable.sol new file mode 100644 index 000000000..3f7882b5c --- /dev/null +++ b/contracts/l2/token/GraphTokenUpgradeable.sol @@ -0,0 +1,212 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.7.6; + +import { ERC20BurnableUpgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20BurnableUpgradeable.sol"; +import { ECDSAUpgradeable } from "@openzeppelin/contracts-upgradeable/cryptography/ECDSAUpgradeable.sol"; + +import { GraphUpgradeable } from "../../upgrades/GraphUpgradeable.sol"; +import { Governed } from "../../governance/Governed.sol"; + +/** + * @title GraphTokenUpgradeable contract + * @dev This is the implementation of the ERC20 Graph Token. + * The implementation exposes a permit() function to allow for a spender to send a signed message + * and approve funds to a spender following EIP2612 to make integration with other contracts easier. + * + * The token is initially owned by the deployer address that can mint tokens to create the initial + * distribution. For convenience, an initial supply can be passed in the constructor that will be + * assigned to the deployer. + * + * The governor can add contracts allowed to mint indexing rewards. + * + * Note this is an exact copy of the original GraphToken contract, but using + * initializer functions and upgradeable OpenZeppelin contracts instead of + * the original's constructor + non-upgradeable approach. + */ +abstract contract GraphTokenUpgradeable is GraphUpgradeable, Governed, ERC20BurnableUpgradeable { + // -- EIP712 -- + // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md#definition-of-domainseparator + + /// @dev Hash of the EIP-712 Domain type + bytes32 private immutable DOMAIN_TYPE_HASH = + keccak256( + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)" + ); + /// @dev Hash of the EIP-712 Domain name + bytes32 private immutable DOMAIN_NAME_HASH = keccak256("Graph Token"); + /// @dev Hash of the EIP-712 Domain version + bytes32 private immutable DOMAIN_VERSION_HASH = keccak256("0"); + /// @dev EIP-712 Domain salt + bytes32 private immutable DOMAIN_SALT = + 0xe33842a7acd1d5a1d28f25a931703e5605152dc48d64dc4716efdae1f5659591; // Randomly generated salt + /// @dev Hash of the EIP-712 permit type + bytes32 private immutable PERMIT_TYPEHASH = + keccak256( + "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" + ); + + // -- State -- + + /// @dev EIP-712 Domain separator + bytes32 private DOMAIN_SEPARATOR; // solhint-disable-line var-name-mixedcase + /// @dev Addresses for which this mapping is true are allowed to mint tokens + mapping(address => bool) private _minters; + /// Nonces for permit signatures for each token holder + mapping(address => uint256) public nonces; + /// @dev Storage gap added in case we need to add state variables to this contract + uint256[47] private __gap; + + // -- Events -- + + /// Emitted when a new minter is added + event MinterAdded(address indexed account); + /// Emitted when a minter is removed + event MinterRemoved(address indexed account); + + /// @dev Reverts if the caller is not an authorized minter + modifier onlyMinter() { + require(isMinter(msg.sender), "Only minter can call"); + _; + } + + /** + * @notice Approve token allowance by validating a message signed by the holder. + * @param _owner Address of the token holder + * @param _spender Address of the approved spender + * @param _value Amount of tokens to approve the spender + * @param _deadline Expiration time of the signed permit (if zero, the permit will never expire, so use with caution) + * @param _v Signature recovery id + * @param _r Signature r value + * @param _s Signature s value + */ + function permit( + address _owner, + address _spender, + uint256 _value, + uint256 _deadline, + uint8 _v, + bytes32 _r, + bytes32 _s + ) external { + require(_deadline == 0 || block.timestamp <= _deadline, "GRT: expired permit"); + bytes32 digest = keccak256( + abi.encodePacked( + "\x19\x01", + DOMAIN_SEPARATOR, + keccak256( + abi.encode(PERMIT_TYPEHASH, _owner, _spender, _value, nonces[_owner], _deadline) + ) + ) + ); + + address recoveredAddress = ECDSAUpgradeable.recover(digest, _v, _r, _s); + require(_owner == recoveredAddress, "GRT: invalid permit"); + + nonces[_owner] = nonces[_owner] + 1; + _approve(_owner, _spender, _value); + } + + /** + * @notice Add a new minter. + * @param _account Address of the minter + */ + function addMinter(address _account) external onlyGovernor { + require(_account != address(0), "INVALID_MINTER"); + _addMinter(_account); + } + + /** + * @notice Remove a minter. + * @param _account Address of the minter + */ + function removeMinter(address _account) external onlyGovernor { + require(isMinter(_account), "NOT_A_MINTER"); + _removeMinter(_account); + } + + /** + * @notice Renounce being a minter. + */ + function renounceMinter() external { + require(isMinter(msg.sender), "NOT_A_MINTER"); + _removeMinter(msg.sender); + } + + /** + * @notice Mint new tokens. + * @param _to Address to send the newly minted tokens + * @param _amount Amount of tokens to mint + */ + function mint(address _to, uint256 _amount) external onlyMinter { + _mint(_to, _amount); + } + + /** + * @notice Return if the `_account` is a minter or not. + * @param _account Address to check + * @return True if the `_account` is minter + */ + function isMinter(address _account) public view returns (bool) { + return _minters[_account]; + } + + /** + * @dev Graph Token Contract initializer. + * @param _owner Owner of this contract, who will hold the initial supply and will be a minter + * @param _initialSupply Initial supply of GRT + */ + function _initialize(address _owner, uint256 _initialSupply) internal { + __ERC20_init("Graph Token", "GRT"); + Governed._initialize(_owner); + + // The Governor has the initial supply of tokens + _mint(_owner, _initialSupply); + + // The Governor is the default minter + _addMinter(_owner); + + // EIP-712 domain separator + DOMAIN_SEPARATOR = keccak256( + abi.encode( + DOMAIN_TYPE_HASH, + DOMAIN_NAME_HASH, + DOMAIN_VERSION_HASH, + _getChainID(), + address(this), + DOMAIN_SALT + ) + ); + } + + /** + * @dev Add a new minter. + * @param _account Address of the minter + */ + function _addMinter(address _account) private { + _minters[_account] = true; + emit MinterAdded(_account); + } + + /** + * @dev Remove a minter. + * @param _account Address of the minter + */ + function _removeMinter(address _account) private { + _minters[_account] = false; + emit MinterRemoved(_account); + } + + /** + * @dev Get the running network chain ID. + * @return The chain ID + */ + function _getChainID() private pure returns (uint256) { + uint256 id; + // solhint-disable-next-line no-inline-assembly + assembly { + id := chainid() + } + return id; + } +} diff --git a/contracts/l2/token/L2GraphToken.sol b/contracts/l2/token/L2GraphToken.sol new file mode 100644 index 000000000..639444870 --- /dev/null +++ b/contracts/l2/token/L2GraphToken.sol @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.7.6; + +import { GraphTokenUpgradeable } from "./GraphTokenUpgradeable.sol"; +import { IArbToken } from "../../arbitrum/IArbToken.sol"; + +/** + * @title L2 Graph Token Contract + * @dev Provides the L2 version of the GRT token, meant to be minted/burned + * through the L2GraphTokenGateway. + */ +contract L2GraphToken is GraphTokenUpgradeable, IArbToken { + /// Address of the gateway (on L2) that is allowed to mint tokens + address public gateway; + /// Address of the corresponding Graph Token contract on L1 + address public override l1Address; + + /// Emitted when the bridge / gateway has minted new tokens, i.e. tokens were transferred to L2 + event BridgeMinted(address indexed account, uint256 amount); + /// Emitted when the bridge / gateway has burned tokens, i.e. tokens were transferred back to L1 + event BridgeBurned(address indexed account, uint256 amount); + /// Emitted when the address of the gateway has been updated + event GatewaySet(address gateway); + /// Emitted when the address of the Graph Token contract on L1 has been updated + event L1AddressSet(address l1Address); + + /** + * @dev Checks that the sender is the L2 gateway from the L1/L2 token bridge + */ + modifier onlyGateway() { + require(msg.sender == gateway, "NOT_GATEWAY"); + _; + } + + /** + * @notice L2 Graph Token Contract initializer. + * @dev Note some parameters have to be set separately as they are generally + * not expected to be available at initialization time: + * - gateway using setGateway + * - l1Address using setL1Address + * @param _owner Governance address that owns this contract + */ + function initialize(address _owner) external onlyImpl initializer { + require(_owner != address(0), "Owner must be set"); + // Initial supply hard coded to 0 as tokens are only supposed + // to be minted through the bridge. + GraphTokenUpgradeable._initialize(_owner, 0); + } + + /** + * @notice Sets the address of the L2 gateway allowed to mint tokens + * @param _gw Address for the L2GraphTokenGateway that will be allowed to mint tokens + */ + function setGateway(address _gw) external onlyGovernor { + require(_gw != address(0), "INVALID_GATEWAY"); + gateway = _gw; + emit GatewaySet(_gw); + } + + /** + * @notice Sets the address of the counterpart token on L1 + * @param _addr Address for the GraphToken contract on L1 + */ + function setL1Address(address _addr) external onlyGovernor { + require(_addr != address(0), "INVALID_L1_ADDRESS"); + l1Address = _addr; + emit L1AddressSet(_addr); + } + + /** + * @notice Increases token supply, only callable by the L1/L2 bridge (when tokens are transferred to L2) + * @param _account Address to credit with the new tokens + * @param _amount Number of tokens to mint + */ + function bridgeMint(address _account, uint256 _amount) external override onlyGateway { + _mint(_account, _amount); + emit BridgeMinted(_account, _amount); + } + + /** + * @notice Decreases token supply, only callable by the L1/L2 bridge (when tokens are transferred to L1). + * @param _account Address from which to extract the tokens + * @param _amount Number of tokens to burn + */ + function bridgeBurn(address _account, uint256 _amount) external override onlyGateway { + burnFrom(_account, _amount); + emit BridgeBurned(_account, _amount); + } +} diff --git a/contracts/tests/CallhookReceiverMock.sol b/contracts/tests/CallhookReceiverMock.sol new file mode 100644 index 000000000..1e71cf86f --- /dev/null +++ b/contracts/tests/CallhookReceiverMock.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.7.6; + +import "../gateway/ICallhookReceiver.sol"; + +/** + * @title GovernedMock contract + */ +contract CallhookReceiverMock is ICallhookReceiver { + event TransferReceived(address from, uint256 amount, uint256 foo, uint256 bar); + + /** + * @dev Receive tokens with a callhook from the bridge + * Expects two uint256 values encoded in _data. + * Reverts if the first of these values is zero. + * @param _from Token sender in L1 + * @param _amount Amount of tokens that were transferred + * @param _data ABI-encoded callhook data + */ + function onTokenTransfer( + address _from, + uint256 _amount, + bytes calldata _data + ) external override { + uint256 foo; + uint256 bar; + (foo, bar) = abi.decode(_data, (uint256, uint256)); + require(foo != 0, "FOO_IS_ZERO"); + emit TransferReceived(_from, _amount, foo, bar); + } +} diff --git a/contracts/tests/arbitrum/BridgeMock.sol b/contracts/tests/arbitrum/BridgeMock.sol new file mode 100644 index 000000000..4f2848288 --- /dev/null +++ b/contracts/tests/arbitrum/BridgeMock.sol @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.7.6; + +import "../../arbitrum/IBridge.sol"; + +/** + * @title Arbitrum Bridge mock contract + * @dev This contract implements Arbitrum's IBridge interface for testing purposes + */ +contract BridgeMock is IBridge { + // Address of the (mock) Arbitrum Inbox + address public inbox; + // Address of the (mock) Arbitrum Outbox + address public outbox; + // Index of the next message on the inbox messages array + uint256 public messageIndex; + // Inbox messages array + bytes32[] public override inboxAccs; + + /** + * @dev Deliver a message to the inbox. The encoded message will be + * added to the inbox array, and messageIndex will be incremented. + * @param _kind Type of the message + * @param _sender Address that is sending the message + * @param _messageDataHash keccak256 hash of the message data + * @return The next index for the inbox array + */ + function deliverMessageToInbox( + uint8 _kind, + address _sender, + bytes32 _messageDataHash + ) external payable override returns (uint256) { + messageIndex = messageIndex + 1; + inboxAccs.push(keccak256(abi.encodePacked(inbox, _kind, _sender, _messageDataHash))); + emit MessageDelivered( + messageIndex, + inboxAccs[messageIndex - 1], + msg.sender, + _kind, + _sender, + _messageDataHash + ); + return messageIndex; + } + + /** + * @dev Executes an L1 function call incoing from L2. This can only be called + * by the Outbox. + * @param _destAddr Contract to call + * @param _amount ETH value to send + * @param _data Calldata for the function call + * @return True if the call was successful, false otherwise + * @return Return data from the call + */ + function executeCall( + address _destAddr, + uint256 _amount, + bytes calldata _data + ) external override returns (bool, bytes memory) { + require(outbox == msg.sender, "NOT_FROM_OUTBOX"); + bool success; + bytes memory returnData; + + // solhint-disable-next-line avoid-low-level-calls + (success, returnData) = _destAddr.call{ value: _amount }(_data); + emit BridgeCallTriggered(msg.sender, _destAddr, _amount, _data); + return (success, returnData); + } + + /** + * @dev Set the address of the inbox. Anyone can call this, because it's a mock. + * @param _inbox Address of the inbox + * @param _enabled Enable the inbox (ignored) + */ + function setInbox(address _inbox, bool _enabled) external override { + inbox = _inbox; + emit InboxToggle(inbox, _enabled); + } + + /** + * @dev Set the address of the outbox. Anyone can call this, because it's a mock. + * @param _outbox Address of the outbox + * @param _enabled Enable the outbox (ignored) + */ + function setOutbox(address _outbox, bool _enabled) external override { + outbox = _outbox; + emit OutboxToggle(outbox, _enabled); + } + + // View functions + + /** + * @dev Getter for the active outbox (in this case there's only one) + */ + function activeOutbox() external view override returns (address) { + return outbox; + } + + /** + * @dev Getter for whether an address is an allowed inbox (in this case there's only one) + * @param _inbox Address to check + * @return True if the address is the allowed inbox, false otherwise + */ + function allowedInboxes(address _inbox) external view override returns (bool) { + return _inbox == inbox; + } + + /** + * @dev Getter for whether an address is an allowed outbox (in this case there's only one) + * @param _outbox Address to check + * @return True if the address is the allowed outbox, false otherwise + */ + function allowedOutboxes(address _outbox) external view override returns (bool) { + return _outbox == outbox; + } + + /** + * @dev Getter for the count of messages in the inboxAccs + * @return Number of messages in inboxAccs + */ + function messageCount() external view override returns (uint256) { + return inboxAccs.length; + } +} diff --git a/contracts/tests/arbitrum/InboxMock.sol b/contracts/tests/arbitrum/InboxMock.sol new file mode 100644 index 000000000..b600ec3ac --- /dev/null +++ b/contracts/tests/arbitrum/InboxMock.sol @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.7.6; + +import "../../arbitrum/IInbox.sol"; +import "../../arbitrum/AddressAliasHelper.sol"; + +/** + * @title Arbitrum Inbox mock contract + * @dev This contract implements (a subset of) Arbitrum's IInbox interface for testing purposes + */ +contract InboxMock is IInbox { + // Type indicator for a standard L2 message + uint8 internal constant L2_MSG = 3; + // Type indicator for a retryable ticket message + // solhint-disable-next-line const-name-snakecase + uint8 internal constant L1MessageType_submitRetryableTx = 9; + // Address of the Bridge (mock) contract + IBridge public override bridge; + + /** + * @dev Send a message to L2 (by delivering it to the Bridge) + * @param _messageData Encoded data to send in the message + * @return message number returned by the inbox + */ + function sendL2Message(bytes calldata _messageData) external override returns (uint256) { + uint256 msgNum = deliverToBridge(L2_MSG, msg.sender, keccak256(_messageData)); + emit InboxMessageDelivered(msgNum, _messageData); + return msgNum; + } + + /** + * @dev Set the address of the (mock) bridge + * @param _bridge Address of the bridge + */ + function setBridge(address _bridge) external { + bridge = IBridge(_bridge); + } + + /** + * @dev Unimplemented in this mock + */ + function sendUnsignedTransaction( + uint256, + uint256, + uint256, + address, + uint256, + bytes calldata + ) external pure override returns (uint256) { + revert("Unimplemented"); + } + + /** + * @dev Unimplemented in this mock + */ + function sendContractTransaction( + uint256, + uint256, + address, + uint256, + bytes calldata + ) external pure override returns (uint256) { + revert("Unimplemented"); + } + + /** + * @dev Unimplemented in this mock + */ + function sendL1FundedUnsignedTransaction( + uint256, + uint256, + uint256, + address, + bytes calldata + ) external payable override returns (uint256) { + revert("Unimplemented"); + } + + /** + * @dev Unimplemented in this mock + */ + function sendL1FundedContractTransaction( + uint256, + uint256, + address, + bytes calldata + ) external payable override returns (uint256) { + revert("Unimplemented"); + } + + /** + * @dev Creates a retryable ticket for an L2 transaction + * @param _destAddr Address of the contract to call in L2 + * @param _arbTxCallValue Callvalue to use in the L2 transaction + * @param _maxSubmissionCost Max cost of submitting the ticket, in Wei + * @param _submissionRefundAddress L2 address to refund for any remaining value from the submission cost + * @param _valueRefundAddress L2 address to refund if the ticket times out or gets cancelled + * @param _maxGas Max gas for the L2 transcation + * @param _gasPriceBid Gas price bid on L2 + * @param _data Encoded calldata for the L2 transaction (including function selector) + * @return message number returned by the bridge + */ + function createRetryableTicket( + address _destAddr, + uint256 _arbTxCallValue, + uint256 _maxSubmissionCost, + address _submissionRefundAddress, + address _valueRefundAddress, + uint256 _maxGas, + uint256 _gasPriceBid, + bytes calldata _data + ) external payable override returns (uint256) { + _submissionRefundAddress = AddressAliasHelper.applyL1ToL2Alias(_submissionRefundAddress); + _valueRefundAddress = AddressAliasHelper.applyL1ToL2Alias(_valueRefundAddress); + return + _deliverMessage( + L1MessageType_submitRetryableTx, + msg.sender, + abi.encodePacked( + uint256(uint160(bytes20(_destAddr))), + _arbTxCallValue, + msg.value, + _maxSubmissionCost, + uint256(uint160(bytes20(_submissionRefundAddress))), + uint256(uint160(bytes20(_valueRefundAddress))), + _maxGas, + _gasPriceBid, + _data.length, + _data + ) + ); + } + + function depositEth(uint256) external payable override returns (uint256) { + revert("Unimplemented"); + } + + /** + * @dev Unimplemented in this mock + */ + function pauseCreateRetryables() external pure override { + revert("Unimplemented"); + } + + /** + * @dev Unimplemented in this mock + */ + function unpauseCreateRetryables() external pure override { + revert("Unimplemented"); + } + + /** + * @dev Unimplemented in this mock + */ + function startRewriteAddress() external pure override { + revert("Unimplemented"); + } + + /** + * @dev Unimplemented in this mock + */ + function stopRewriteAddress() external pure override { + revert("Unimplemented"); + } + + /** + * @dev Deliver a message to the bridge + * @param _kind Type of the message + * @param _sender Address that is sending the message + * @param _messageData Encoded message data + * @return Message number returned by the bridge + */ + function _deliverMessage( + uint8 _kind, + address _sender, + bytes memory _messageData + ) internal returns (uint256) { + uint256 msgNum = deliverToBridge(_kind, _sender, keccak256(_messageData)); + emit InboxMessageDelivered(msgNum, _messageData); + return msgNum; + } + + /** + * @dev Deliver a message to the bridge + * @param _kind Type of the message + * @param _sender Address that is sending the message + * @param _messageDataHash keccak256 hash of the encoded message data + * @return Message number returned by the bridge + */ + function deliverToBridge( + uint8 _kind, + address _sender, + bytes32 _messageDataHash + ) internal returns (uint256) { + return bridge.deliverMessageToInbox{ value: msg.value }(_kind, _sender, _messageDataHash); + } +} diff --git a/contracts/tests/arbitrum/OutboxMock.sol b/contracts/tests/arbitrum/OutboxMock.sol new file mode 100644 index 000000000..a529a975a --- /dev/null +++ b/contracts/tests/arbitrum/OutboxMock.sol @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.7.6; + +import "../../arbitrum/IOutbox.sol"; +import "../../arbitrum/IBridge.sol"; + +/** + * @title Arbitrum Outbox mock contract + * @dev This contract implements (a subset of) Arbitrum's IOutbox interface for testing purposes + */ +contract OutboxMock is IOutbox { + // Context of an L2-to-L1 function call + struct L2ToL1Context { + uint128 l2Block; + uint128 l1Block; + uint128 timestamp; + uint128 batchNum; + bytes32 outputId; + address sender; + } + // Context of the current L2-to-L1 function call (set and cleared in each transaction) + L2ToL1Context internal context; + + // Address of the (mock) Arbitrum Bridge + IBridge public bridge; + + /** + * @dev Set the address of the (mock) bridge + * @param _bridge Address of the bridge + */ + function setBridge(address _bridge) external { + bridge = IBridge(_bridge); + } + + /** + * @dev Getter for the L2 sender of the current incoming message + */ + function l2ToL1Sender() external view override returns (address) { + return context.sender; + } + + /** + * @dev Getter for the L2 block of the current incoming message + */ + function l2ToL1Block() external view override returns (uint256) { + return context.l2Block; + } + + /** + * @dev Getter for the L1 block of the current incoming message + */ + function l2ToL1EthBlock() external view override returns (uint256) { + return context.l1Block; + } + + /** + * @dev Getter for the L1 timestamp of the current incoming message + */ + function l2ToL1Timestamp() external view override returns (uint256) { + return context.timestamp; + } + + /** + * @dev Getter for the L2 batch number of the current incoming message + */ + function l2ToL1BatchNum() external view override returns (uint256) { + return context.batchNum; + } + + /** + * @dev Getter for the output ID of the current incoming message + */ + function l2ToL1OutputId() external view override returns (bytes32) { + return context.outputId; + } + + /** + * @dev Unimplemented in this mock + */ + function processOutgoingMessages(bytes calldata, uint256[] calldata) external pure override { + revert("Unimplemented"); + } + + /** + * @dev Check whether an outbox entry for a message exists. + * This mock returns always true. + */ + function outboxEntryExists(uint256) external pure override returns (bool) { + return true; + } + + /** + * @notice (Mock) Executes a messages in an Outbox entry. + * @dev This mocks what has to be called when finalizing an L2 to L1 transfer. + * In our mock scenario, we don't validate and execute unconditionally. + * @param _batchNum Index of OutboxEntry in outboxEntries array + * @param _l2Sender sender of original message (i.e., caller of ArbSys.sendTxToL1) + * @param _destAddr destination address for L1 contract call + * @param _l2Block l2 block number at which sendTxToL1 call was made + * @param _l1Block l1 block number at which sendTxToL1 call was made + * @param _l2Timestamp l2 Timestamp at which sendTxToL1 call was made + * @param _amount value in L1 message in wei + * @param _calldataForL1 abi-encoded L1 message data + */ + function executeTransaction( + uint256 _batchNum, + bytes32[] calldata, // proof + uint256, // index + address _l2Sender, + address _destAddr, + uint256 _l2Block, + uint256 _l1Block, + uint256 _l2Timestamp, + uint256 _amount, + bytes calldata _calldataForL1 + ) external virtual { + bytes32 outputId; + + context = L2ToL1Context({ + sender: _l2Sender, + l2Block: uint128(_l2Block), + l1Block: uint128(_l1Block), + timestamp: uint128(_l2Timestamp), + batchNum: uint128(_batchNum), + outputId: outputId + }); + + // set and reset vars around execution so they remain valid during call + executeBridgeCall(_destAddr, _amount, _calldataForL1); + } + + /** + * @dev Execute an L2-to-L1 function call by calling the bridge + * @param _destAddr Address of the contract to call + * @param _amount Callvalue for the function call + * @param _data Calldata for the function call + */ + function executeBridgeCall( + address _destAddr, + uint256 _amount, + bytes memory _data + ) internal { + (bool success, bytes memory returndata) = bridge.executeCall(_destAddr, _amount, _data); + if (!success) { + if (returndata.length > 0) { + // solhint-disable-next-line no-inline-assembly + assembly { + let returndata_size := mload(returndata) + revert(add(32, returndata), returndata_size) + } + } else { + revert("BRIDGE_CALL_FAILED"); + } + } + } +} diff --git a/contracts/token/IGraphToken.sol b/contracts/token/IGraphToken.sol index 2b7dbaa20..8255e18d5 100644 --- a/contracts/token/IGraphToken.sol +++ b/contracts/token/IGraphToken.sol @@ -9,6 +9,8 @@ interface IGraphToken is IERC20 { function burn(uint256 amount) external; + function burnFrom(address _from, uint256 amount) external; + function mint(address _to, uint256 _amount) external; // -- Mint Admin -- @@ -32,4 +34,10 @@ interface IGraphToken is IERC20 { bytes32 _r, bytes32 _s ) external; + + // -- Allowance -- + + function increaseAllowance(address spender, uint256 addedValue) external returns (bool); + + function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool); } diff --git a/contracts/upgrades/GraphProxy.sol b/contracts/upgrades/GraphProxy.sol index 69c88fd28..d3f6eacec 100644 --- a/contracts/upgrades/GraphProxy.sol +++ b/contracts/upgrades/GraphProxy.sol @@ -2,9 +2,11 @@ pragma solidity ^0.7.6; -import "@openzeppelin/contracts/utils/Address.sol"; +import { Address } from "@openzeppelin/contracts/utils/Address.sol"; -import "./GraphProxyStorage.sol"; +import { GraphProxyStorage } from "./GraphProxyStorage.sol"; + +import { IGraphProxy } from "./IGraphProxy.sol"; /** * @title Graph Proxy @@ -13,13 +15,13 @@ import "./GraphProxyStorage.sol"; * This contract implements a proxy that is upgradeable by an admin. * https://docs.openzeppelin.com/upgrades-plugins/1.x/proxies#transparent-proxies-and-function-clashes */ -contract GraphProxy is GraphProxyStorage { +contract GraphProxy is GraphProxyStorage, IGraphProxy { /** * @dev Modifier used internally that will delegate the call to the implementation unless * the sender is the admin. */ modifier ifAdmin() { - if (msg.sender == _admin()) { + if (msg.sender == _getAdmin()) { _; } else { _fallback(); @@ -31,7 +33,7 @@ contract GraphProxy is GraphProxyStorage { * the sender is the admin or pending implementation. */ modifier ifAdminOrPendingImpl() { - if (msg.sender == _admin() || msg.sender == _pendingImplementation()) { + if (msg.sender == _getAdmin() || msg.sender == _getPendingImplementation()) { _; } else { _fallback(); @@ -39,7 +41,7 @@ contract GraphProxy is GraphProxyStorage { } /** - * @dev Contract constructor. + * @notice GraphProxy contract constructor. * @param _impl Address of the initial implementation * @param _admin Address of the proxy admin */ @@ -58,91 +60,114 @@ contract GraphProxy is GraphProxyStorage { } /** - * @dev Returns the current admin. + * @notice Fallback function that delegates calls to implementation. Will run if call data + * is empty. + */ + receive() external payable { + _fallback(); + } + + /** + * @notice Fallback function that delegates calls to implementation. Will run if no other + * function in the contract matches the call data. + */ + fallback() external payable { + _fallback(); + } + + /** + * @notice Get the current admin * - * NOTE: Only the admin and implementation can call this function. + * @dev NOTE: Only the admin and implementation can call this function. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` + * + * @return The address of the current admin */ - function admin() external ifAdminOrPendingImpl returns (address) { - return _admin(); + function admin() external override ifAdminOrPendingImpl returns (address) { + return _getAdmin(); } /** - * @dev Returns the current implementation. + * @notice Get the current implementation. * - * NOTE: Only the admin can call this function. + * @dev NOTE: Only the admin can call this function. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` + * + * @return The address of the current implementation for this proxy */ - function implementation() external ifAdminOrPendingImpl returns (address) { - return _implementation(); + function implementation() external override ifAdminOrPendingImpl returns (address) { + return _getImplementation(); } /** - * @dev Returns the current pending implementation. + * @notice Get the current pending implementation. * - * NOTE: Only the admin can call this function. + * @dev NOTE: Only the admin can call this function. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0x9e5eddc59e0b171f57125ab86bee043d9128098c3a6b9adb4f2e86333c2f6f8c` + * + * @return The address of the current pending implementation for this proxy */ - function pendingImplementation() external ifAdminOrPendingImpl returns (address) { - return _pendingImplementation(); + function pendingImplementation() external override ifAdminOrPendingImpl returns (address) { + return _getPendingImplementation(); } /** - * @dev Changes the admin of the proxy. + * @notice Changes the admin of the proxy. + * + * @dev NOTE: Only the admin can call this function. * - * NOTE: Only the admin can call this function. + * @param _newAdmin Address of the new admin */ - function setAdmin(address _newAdmin) external ifAdmin { - require(_newAdmin != address(0), "Cannot change the admin of a proxy to the zero address"); + function setAdmin(address _newAdmin) external override ifAdmin { + require(_newAdmin != address(0), "Admin cant be the zero address"); _setAdmin(_newAdmin); } /** - * @dev Upgrades to a new implementation contract. + * @notice Upgrades to a new implementation contract. + * @dev NOTE: Only the admin can call this function. * @param _newImplementation Address of implementation contract - * - * NOTE: Only the admin can call this function. */ - function upgradeTo(address _newImplementation) external ifAdmin { + function upgradeTo(address _newImplementation) external override ifAdmin { _setPendingImplementation(_newImplementation); } /** - * @dev Admin function for new implementation to accept its role as implementation. + * @notice Admin function for new implementation to accept its role as implementation. */ - function acceptUpgrade() external ifAdminOrPendingImpl { + function acceptUpgrade() external override ifAdminOrPendingImpl { _acceptUpgrade(); } /** - * @dev Admin function for new implementation to accept its role as implementation. + * @notice Admin function for new implementation to accept its role as implementation, + * calling a function on the new implementation. + * @param data Calldata (including selector) for the function to delegatecall into the implementation */ - function acceptUpgradeAndCall(bytes calldata data) external ifAdminOrPendingImpl { + function acceptUpgradeAndCall(bytes calldata data) external override ifAdminOrPendingImpl { _acceptUpgrade(); // solhint-disable-next-line avoid-low-level-calls - (bool success, ) = _implementation().delegatecall(data); - require(success); + (bool success, ) = _getImplementation().delegatecall(data); + require(success, "Impl call failed"); } /** * @dev Admin function for new implementation to accept its role as implementation. */ function _acceptUpgrade() internal { - address _pendingImplementation = _pendingImplementation(); - require(Address.isContract(_pendingImplementation), "Implementation must be a contract"); - require( - _pendingImplementation != address(0) && msg.sender == _pendingImplementation, - "Caller must be the pending implementation" - ); + address _pendingImplementation = _getPendingImplementation(); + require(Address.isContract(_pendingImplementation), "Impl must be a contract"); + require(_pendingImplementation != address(0), "Impl cannot be zero address"); + require(msg.sender == _pendingImplementation, "Only pending implementation"); _setImplementation(_pendingImplementation); _setPendingImplementation(address(0)); @@ -154,8 +179,9 @@ contract GraphProxy is GraphProxyStorage { * external caller. */ function _fallback() internal { - require(msg.sender != _admin(), "Cannot fallback to proxy target"); + require(msg.sender != _getAdmin(), "Cannot fallback to proxy target"); + // solhint-disable-next-line no-inline-assembly assembly { // (a) get free memory pointer let ptr := mload(0x40) @@ -183,20 +209,4 @@ contract GraphProxy is GraphProxyStorage { } } } - - /** - * @dev Fallback function that delegates calls to implementation. Will run if no other - * function in the contract matches the call data. - */ - fallback() external payable { - _fallback(); - } - - /** - * @dev Fallback function that delegates calls to implementation. Will run if call data - * is empty. - */ - receive() external payable { - _fallback(); - } } diff --git a/contracts/upgrades/GraphProxyAdmin.sol b/contracts/upgrades/GraphProxyAdmin.sol index 3775b9df1..d96dbd449 100644 --- a/contracts/upgrades/GraphProxyAdmin.sol +++ b/contracts/upgrades/GraphProxyAdmin.sol @@ -2,10 +2,10 @@ pragma solidity ^0.7.6; -import "../governance/Governed.sol"; +import { Governed } from "../governance/Governed.sol"; -import "./IGraphProxy.sol"; -import "./GraphUpgradeable.sol"; +import { IGraphProxy } from "./IGraphProxy.sol"; +import { GraphUpgradeable } from "./GraphUpgradeable.sol"; /** * @title GraphProxyAdmin @@ -16,79 +16,85 @@ import "./GraphUpgradeable.sol"; */ contract GraphProxyAdmin is Governed { /** - * @dev Contract constructor. + * @notice Contract constructor. */ constructor() { Governed._initialize(msg.sender); } /** - * @dev Returns the current implementation of a proxy. - * This is needed because only the proxy admin can query it. + * @notice Returns the current implementation of a proxy. + * @dev This is needed because only the proxy admin can query it. + * @param _proxy Address of the proxy for which to get the implementation. * @return The address of the current implementation of the proxy. */ - function getProxyImplementation(IGraphProxy _proxy) public view returns (address) { + function getProxyImplementation(IGraphProxy _proxy) external view returns (address) { // We need to manually run the static call since the getter cannot be flagged as view // bytes4(keccak256("implementation()")) == 0x5c60da1b (bool success, bytes memory returndata) = address(_proxy).staticcall(hex"5c60da1b"); - require(success); + require(success, "Proxy impl call failed"); return abi.decode(returndata, (address)); } /** - * @dev Returns the pending implementation of a proxy. - * This is needed because only the proxy admin can query it. + * @notice Returns the pending implementation of a proxy. + * @dev This is needed because only the proxy admin can query it. + * @param _proxy Address of the proxy for which to get the pending implementation. * @return The address of the pending implementation of the proxy. */ - function getProxyPendingImplementation(IGraphProxy _proxy) public view returns (address) { + function getProxyPendingImplementation(IGraphProxy _proxy) external view returns (address) { // We need to manually run the static call since the getter cannot be flagged as view // bytes4(keccak256("pendingImplementation()")) == 0x396f7b23 (bool success, bytes memory returndata) = address(_proxy).staticcall(hex"396f7b23"); - require(success); + require(success, "Proxy pendingImpl call failed"); return abi.decode(returndata, (address)); } /** - * @dev Returns the admin of a proxy. Only the admin can query it. + * @notice Returns the admin of a proxy. Only the admin can query it. + * @param _proxy Address of the proxy for which to get the admin. * @return The address of the current admin of the proxy. */ - function getProxyAdmin(IGraphProxy _proxy) public view returns (address) { + function getProxyAdmin(IGraphProxy _proxy) external view returns (address) { // We need to manually run the static call since the getter cannot be flagged as view // bytes4(keccak256("admin()")) == 0xf851a440 (bool success, bytes memory returndata) = address(_proxy).staticcall(hex"f851a440"); - require(success); + require(success, "Proxy admin call failed"); return abi.decode(returndata, (address)); } /** - * @dev Changes the admin of a proxy. + * @notice Changes the admin of a proxy. * @param _proxy Proxy to change admin. * @param _newAdmin Address to transfer proxy administration to. */ - function changeProxyAdmin(IGraphProxy _proxy, address _newAdmin) public onlyGovernor { + function changeProxyAdmin(IGraphProxy _proxy, address _newAdmin) external onlyGovernor { _proxy.setAdmin(_newAdmin); } /** - * @dev Upgrades a proxy to the newest implementation of a contract. + * @notice Upgrades a proxy to the newest implementation of a contract. * @param _proxy Proxy to be upgraded. * @param _implementation the address of the Implementation. */ - function upgrade(IGraphProxy _proxy, address _implementation) public onlyGovernor { + function upgrade(IGraphProxy _proxy, address _implementation) external onlyGovernor { _proxy.upgradeTo(_implementation); } /** - * @dev Accepts a proxy. + * @notice Accepts a proxy. * @param _implementation Address of the implementation accepting the proxy. * @param _proxy Address of the proxy being accepted. */ - function acceptProxy(GraphUpgradeable _implementation, IGraphProxy _proxy) public onlyGovernor { + function acceptProxy(GraphUpgradeable _implementation, IGraphProxy _proxy) + external + onlyGovernor + { _implementation.acceptProxy(_proxy); } /** - * @dev Accepts a proxy and call a function on the implementation. + * @notice Accepts a proxy and call a function on the implementation. * @param _implementation Address of the implementation accepting the proxy. * @param _proxy Address of the proxy being accepted. * @param _data Encoded function to call on the implementation after accepting the proxy. diff --git a/contracts/upgrades/GraphProxyStorage.sol b/contracts/upgrades/GraphProxyStorage.sol index 950f9776b..b308c0a0c 100644 --- a/contracts/upgrades/GraphProxyStorage.sol +++ b/contracts/upgrades/GraphProxyStorage.sol @@ -8,7 +8,7 @@ pragma solidity ^0.7.6; * This contract does not actually define state variables managed by the compiler * but uses fixed slot locations. */ -contract GraphProxyStorage { +abstract contract GraphProxyStorage { /** * @dev Storage slot with the address of the current implementation. * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is @@ -59,15 +59,16 @@ contract GraphProxyStorage { * @dev Modifier to check whether the `msg.sender` is the admin. */ modifier onlyAdmin() { - require(msg.sender == _admin(), "Caller must be admin"); + require(msg.sender == _getAdmin(), "Caller must be admin"); _; } /** * @return adm The admin slot. */ - function _admin() internal view returns (address adm) { + function _getAdmin() internal view returns (address adm) { bytes32 slot = ADMIN_SLOT; + // solhint-disable-next-line no-inline-assembly assembly { adm := sload(slot) } @@ -78,20 +79,23 @@ contract GraphProxyStorage { * @param _newAdmin Address of the new proxy admin */ function _setAdmin(address _newAdmin) internal { + address oldAdmin = _getAdmin(); bytes32 slot = ADMIN_SLOT; + // solhint-disable-next-line no-inline-assembly assembly { sstore(slot, _newAdmin) } - emit AdminUpdated(_admin(), _newAdmin); + emit AdminUpdated(oldAdmin, _newAdmin); } /** * @dev Returns the current implementation. * @return impl Address of the current implementation */ - function _implementation() internal view returns (address impl) { + function _getImplementation() internal view returns (address impl) { bytes32 slot = IMPLEMENTATION_SLOT; + // solhint-disable-next-line no-inline-assembly assembly { impl := sload(slot) } @@ -101,8 +105,9 @@ contract GraphProxyStorage { * @dev Returns the current pending implementation. * @return impl Address of the current pending implementation */ - function _pendingImplementation() internal view returns (address impl) { + function _getPendingImplementation() internal view returns (address impl) { bytes32 slot = PENDING_IMPLEMENTATION_SLOT; + // solhint-disable-next-line no-inline-assembly assembly { impl := sload(slot) } @@ -113,9 +118,10 @@ contract GraphProxyStorage { * @param _newImplementation Address of the new implementation */ function _setImplementation(address _newImplementation) internal { - address oldImplementation = _implementation(); + address oldImplementation = _getImplementation(); bytes32 slot = IMPLEMENTATION_SLOT; + // solhint-disable-next-line no-inline-assembly assembly { sstore(slot, _newImplementation) } @@ -128,9 +134,10 @@ contract GraphProxyStorage { * @param _newImplementation Address of the new pending implementation */ function _setPendingImplementation(address _newImplementation) internal { - address oldPendingImplementation = _pendingImplementation(); + address oldPendingImplementation = _getPendingImplementation(); bytes32 slot = PENDING_IMPLEMENTATION_SLOT; + // solhint-disable-next-line no-inline-assembly assembly { sstore(slot, _newImplementation) } diff --git a/contracts/upgrades/GraphUpgradeable.sol b/contracts/upgrades/GraphUpgradeable.sol index 2331cae27..92ce80ad7 100644 --- a/contracts/upgrades/GraphUpgradeable.sol +++ b/contracts/upgrades/GraphUpgradeable.sol @@ -2,13 +2,13 @@ pragma solidity ^0.7.6; -import "./IGraphProxy.sol"; +import { IGraphProxy } from "./IGraphProxy.sol"; /** * @title Graph Upgradeable * @dev This contract is intended to be inherited from upgradeable contracts. */ -contract GraphUpgradeable { +abstract contract GraphUpgradeable { /** * @dev Storage slot with the address of the current implementation. * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is @@ -29,7 +29,7 @@ contract GraphUpgradeable { * @dev Check if the caller is the implementation. */ modifier onlyImpl() { - require(msg.sender == _implementation(), "Caller must be the implementation"); + require(msg.sender == _implementation(), "Only implementation"); _; } @@ -39,22 +39,26 @@ contract GraphUpgradeable { */ function _implementation() internal view returns (address impl) { bytes32 slot = IMPLEMENTATION_SLOT; + // solhint-disable-next-line no-inline-assembly assembly { impl := sload(slot) } } /** - * @dev Accept to be an implementation of proxy. + * @notice Accept to be an implementation of proxy. + * @param _proxy Proxy to accept */ function acceptProxy(IGraphProxy _proxy) external onlyProxyAdmin(_proxy) { _proxy.acceptUpgrade(); } /** - * @dev Accept to be an implementation of proxy and then call a function from the new + * @notice Accept to be an implementation of proxy and then call a function from the new * implementation as specified by `_data`, which should be an encoded function call. This is * useful to initialize new storage variables in the proxied contract. + * @param _proxy Proxy to accept + * @param _data Calldata for the initialization function call (including selector) */ function acceptProxyAndCall(IGraphProxy _proxy, bytes calldata _data) external diff --git a/e2e/deployment/config/controller.test.ts b/e2e/deployment/config/controller.test.ts index 9bde5d85e..647cb19f5 100644 --- a/e2e/deployment/config/controller.test.ts +++ b/e2e/deployment/config/controller.test.ts @@ -1,12 +1,13 @@ import { expect } from 'chai' import hre, { ethers } from 'hardhat' import { NamedAccounts } from '../../../gre/type-extensions' +import GraphChain from '../../../gre/helpers/chain' describe('Controller configuration', () => { - const { contracts, getNamedAccounts } = hre.graph() - const { Controller } = contracts + const graph = hre.graph() + const { Controller } = graph.contracts - const proxyContracts = [ + const l1ProxyContracts = [ 'Curation', 'GNS', 'DisputeManager', @@ -14,21 +15,41 @@ describe('Controller configuration', () => { 'RewardsManager', 'Staking', 'GraphToken', + 'L1GraphTokenGateway', + ] + + const l2ProxyContracts = [ + 'Curation', + 'GNS', + 'DisputeManager', + 'EpochManager', + 'RewardsManager', + 'Staking', + 'L2GraphToken', + 'L2GraphTokenGateway', ] let namedAccounts: NamedAccounts before(async () => { - namedAccounts = await getNamedAccounts() + namedAccounts = await graph.getNamedAccounts() }) const proxyShouldMatchDeployed = async (contractName: string) => { - const curationAddress = await Controller.getContractProxy( - ethers.utils.solidityKeccak256(['string'], [contractName]), + // remove L1/L2 prefix, contracts are not registered as L1/L2 on controller + const name = contractName.replace(/(^L1|L2)/gi, '') + + const address = await Controller.getContractProxy( + ethers.utils.solidityKeccak256(['string'], [name]), ) - expect(curationAddress).eq(contracts[contractName].address) + expect(address).eq(graph.contracts[contractName].address) } + it('protocol should be unpaused', async function () { + const paused = await Controller.paused() + expect(paused).eq(false) + }) + it('should be owned by governor', async function () { const owner = await Controller.governor() expect(owner).eq(namedAccounts.governor.address) @@ -40,6 +61,7 @@ describe('Controller configuration', () => { }) describe('proxy contract', async function () { + const proxyContracts = GraphChain.isL1(graph.chainId) ? l1ProxyContracts : l2ProxyContracts for (const contract of proxyContracts) { it(`${contract} should match deployed`, async function () { await proxyShouldMatchDeployed(contract) diff --git a/e2e/deployment/config/graphToken.test.ts b/e2e/deployment/config/graphToken.test.ts index cc5e60618..5ae1e3207 100644 --- a/e2e/deployment/config/graphToken.test.ts +++ b/e2e/deployment/config/graphToken.test.ts @@ -5,7 +5,7 @@ import { NamedAccounts } from '../../../gre/type-extensions' describe('GraphToken configuration', () => { const { getNamedAccounts, - contracts: { GraphToken, RewardsManager }, + contracts: { GraphToken }, getDeployer, } = hre.graph() @@ -23,11 +23,6 @@ describe('GraphToken configuration', () => { it('deployer should not be minter', async function () { const deployer = await getDeployer() const deployerIsMinter = await GraphToken.isMinter(deployer.address) - hre.network.config.chainId === 1337 ? this.skip() : expect(deployerIsMinter).eq(false) - }) - - it('RewardsManager should be minter', async function () { - const deployerIsMinter = await GraphToken.isMinter(RewardsManager.address) - expect(deployerIsMinter).eq(true) + expect(deployerIsMinter).eq(false) }) }) diff --git a/e2e/deployment/config/l1/bridgeEscrow.test.ts b/e2e/deployment/config/l1/bridgeEscrow.test.ts new file mode 100644 index 000000000..1aeba6357 --- /dev/null +++ b/e2e/deployment/config/l1/bridgeEscrow.test.ts @@ -0,0 +1,17 @@ +import { expect } from 'chai' +import hre from 'hardhat' +import GraphChain from '../../../../gre/helpers/chain' + +describe('[L1] BridgeEscrow configuration', function () { + const graph = hre.graph() + const { Controller, BridgeEscrow } = graph.contracts + + before(async function () { + if (GraphChain.isL2(graph.chainId)) this.skip() + }) + + it('should be controlled by Controller', async function () { + const controller = await BridgeEscrow.controller() + expect(controller).eq(Controller.address) + }) +}) diff --git a/e2e/deployment/config/l1/graphToken.test.ts b/e2e/deployment/config/l1/graphToken.test.ts new file mode 100644 index 000000000..0d222c263 --- /dev/null +++ b/e2e/deployment/config/l1/graphToken.test.ts @@ -0,0 +1,31 @@ +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' +import { expect } from 'chai' +import hre from 'hardhat' +import GraphChain from '../../../../gre/helpers/chain' + +describe('[L1] GraphToken', () => { + const graph = hre.graph() + const { GraphToken, RewardsManager } = graph.contracts + + let unauthorized: SignerWithAddress + + before(async function () { + if (GraphChain.isL2(graph.chainId)) this.skip() + unauthorized = (await graph.getTestAccounts())[0] + }) + + describe('calls with unauthorized user', () => { + it('mint should revert', async function () { + const tx = GraphToken.connect(unauthorized).mint( + unauthorized.address, + '1000000000000000000000', + ) + await expect(tx).revertedWith('Only minter can call') + }) + + it('RewardsManager should be minter', async function () { + const rewardsMgrIsMinter = await GraphToken.isMinter(RewardsManager.address) + expect(rewardsMgrIsMinter).eq(true) + }) + }) +}) diff --git a/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts b/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts new file mode 100644 index 000000000..a957f6882 --- /dev/null +++ b/e2e/deployment/config/l1/l1GraphTokenGateway.test.ts @@ -0,0 +1,121 @@ +import { expect } from 'chai' +import hre from 'hardhat' +import GraphChain from '../../../../gre/helpers/chain' +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' +import { getAddressBook } from '../../../../cli/address-book' + +describe('[L1] L1GraphTokenGateway configuration', function () { + const graph = hre.graph() + const { Controller, L1GraphTokenGateway } = graph.contracts + + let unauthorized: SignerWithAddress + before(async function () { + if (GraphChain.isL2(graph.chainId)) this.skip() + unauthorized = (await graph.getTestAccounts())[0] + }) + + it('bridge should not be paused', async function () { + const paused = await L1GraphTokenGateway.paused() + expect(paused).eq(false) + }) + + it('should be controlled by Controller', async function () { + const controller = await L1GraphTokenGateway.controller() + expect(controller).eq(Controller.address) + }) + + it('l2GRT should match the L2 GraphToken deployed address', async function () { + const l2GRT = await L1GraphTokenGateway.l2GRT() + expect(l2GRT).eq(graph.l2.contracts.GraphToken.address) + }) + + it('l2Counterpart should match the deployed L2 GraphTokenGateway address', async function () { + const l2Counterpart = await L1GraphTokenGateway.l2Counterpart() + expect(l2Counterpart).eq(graph.l2.contracts.L2GraphTokenGateway.address) + }) + + it('escrow should match the deployed L1 BridgeEscrow address', async function () { + const escrow = await L1GraphTokenGateway.escrow() + expect(escrow).eq(graph.l1.contracts.BridgeEscrow.address) + }) + + it("inbox should match Arbitrum's Inbox address", async function () { + const inbox = await L1GraphTokenGateway.inbox() + + // TODO: is there a cleaner way to get the router address? + const arbitrumAddressBook = process.env.ARBITRUM_ADDRESS_BOOK ?? 'arbitrum-addresses-local.json' + const arbAddressBook = getAddressBook(arbitrumAddressBook, graph.l1.chainId.toString()) + const arbIInbox = arbAddressBook.getEntry('IInbox') + + expect(inbox.toLowerCase()).eq(arbIInbox.address.toLowerCase()) + }) + + it("l1Router should match Arbitrum's router address", async function () { + const l1Router = await L1GraphTokenGateway.l1Router() + + // TODO: is there a cleaner way to get the router address? + const arbitrumAddressBook = process.env.ARBITRUM_ADDRESS_BOOK ?? 'arbitrum-addresses-local.json' + const arbAddressBook = getAddressBook(arbitrumAddressBook, graph.l1.chainId.toString()) + const arbL2Router = arbAddressBook.getEntry('L1GatewayRouter') + + expect(l1Router).eq(arbL2Router.address) + }) + + describe('calls with unauthorized user', () => { + it('initialize should revert', async function () { + const tx = L1GraphTokenGateway.connect(unauthorized).initialize(unauthorized.address) + await expect(tx).revertedWith('Only implementation') + }) + + it('setArbitrumAddresses should revert', async function () { + const tx = L1GraphTokenGateway.connect(unauthorized).setArbitrumAddresses( + unauthorized.address, + unauthorized.address, + ) + await expect(tx).revertedWith('Only Controller governor') + }) + + it('setL2TokenAddress should revert', async function () { + const tx = L1GraphTokenGateway.connect(unauthorized).setL2TokenAddress(unauthorized.address) + await expect(tx).revertedWith('Only Controller governor') + }) + + it('setL2CounterpartAddress should revert', async function () { + const tx = L1GraphTokenGateway.connect(unauthorized).setL2CounterpartAddress( + unauthorized.address, + ) + await expect(tx).revertedWith('Only Controller governor') + }) + + it('setEscrowAddress should revert', async function () { + const tx = L1GraphTokenGateway.connect(unauthorized).setEscrowAddress(unauthorized.address) + await expect(tx).revertedWith('Only Controller governor') + }) + + it('addToCallhookAllowlist should revert', async function () { + const tx = L1GraphTokenGateway.connect(unauthorized).addToCallhookAllowlist( + unauthorized.address, + ) + await expect(tx).revertedWith('Only Controller governor') + }) + + it('removeFromCallhookAllowlist should revert', async function () { + const tx = L1GraphTokenGateway.connect(unauthorized).removeFromCallhookAllowlist( + unauthorized.address, + ) + await expect(tx).revertedWith('Only Controller governor') + }) + + it('finalizeInboundTransfer should revert', async function () { + const tx = L1GraphTokenGateway.connect(unauthorized).finalizeInboundTransfer( + unauthorized.address, + unauthorized.address, + unauthorized.address, + '100', + '0x00', + ) + + await expect(tx).revertedWith('NOT_FROM_BRIDGE') + }) + }) +}) diff --git a/e2e/deployment/config/l1/rewardsManager.test.ts b/e2e/deployment/config/l1/rewardsManager.test.ts new file mode 100644 index 000000000..cf7cbb09c --- /dev/null +++ b/e2e/deployment/config/l1/rewardsManager.test.ts @@ -0,0 +1,17 @@ +import { expect } from 'chai' +import hre from 'hardhat' +import GraphChain from '../../../../gre/helpers/chain' + +describe('[L1] RewardsManager configuration', () => { + const graph = hre.graph() + const { RewardsManager } = graph.contracts + + before(async function () { + if (GraphChain.isL2(graph.chainId)) this.skip() + }) + + it('issuanceRate should match "issuanceRate" in the config file', async function () { + const value = await RewardsManager.issuanceRate() + expect(value).eq('1000000012184945188') // hardcoded as it's set with a function call rather than init parameter + }) +}) diff --git a/e2e/deployment/config/l2/l2GraphToken.test.ts b/e2e/deployment/config/l2/l2GraphToken.test.ts new file mode 100644 index 000000000..a4ccdaec8 --- /dev/null +++ b/e2e/deployment/config/l2/l2GraphToken.test.ts @@ -0,0 +1,54 @@ +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' +import { expect } from 'chai' +import hre from 'hardhat' +import GraphChain from '../../../../gre/helpers/chain' + +describe('[L2] L2GraphToken', () => { + const graph = hre.graph() + const { L2GraphToken, RewardsManager } = graph.contracts + + let unauthorized: SignerWithAddress + + before(async function () { + if (GraphChain.isL1(graph.chainId)) this.skip() + unauthorized = (await graph.getTestAccounts())[0] + }) + + it('l1Address should match the L1 GraphToken deployed address', async function () { + const l1Address = await L2GraphToken.l1Address() + expect(l1Address).eq(graph.l1.contracts.GraphToken.address) + }) + + it('gateway should match the L2 GraphTokenGateway deployed address', async function () { + const gateway = await L2GraphToken.gateway() + expect(gateway).eq(graph.l2.contracts.L2GraphTokenGateway.address) + }) + + describe('calls with unauthorized user', () => { + it('mint should revert', async function () { + const tx = L2GraphToken.connect(unauthorized).mint( + unauthorized.address, + '1000000000000000000000', + ) + await expect(tx).revertedWith('Only minter can call') + }) + + it('bridgeMint should revert', async function () { + const tx = L2GraphToken.connect(unauthorized).bridgeMint( + unauthorized.address, + '1000000000000000000000', + ) + await expect(tx).revertedWith('NOT_GATEWAY') + }) + + it('setGateway should revert', async function () { + const tx = L2GraphToken.connect(unauthorized).setGateway(unauthorized.address) + await expect(tx).revertedWith('Only Governor can call') + }) + + it('RewardsManager should not be minter (for now)', async function () { + const rewardsMgrIsMinter = await L2GraphToken.isMinter(RewardsManager.address) + expect(rewardsMgrIsMinter).eq(false) + }) + }) +}) diff --git a/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts b/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts new file mode 100644 index 000000000..46df97686 --- /dev/null +++ b/e2e/deployment/config/l2/l2GraphTokenGateway.test.ts @@ -0,0 +1,83 @@ +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' +import { expect } from 'chai' +import hre from 'hardhat' +import { getAddressBook } from '../../../../cli/address-book' +import GraphChain from '../../../../gre/helpers/chain' + +describe('[L2] L2GraphTokenGateway configuration', function () { + const graph = hre.graph() + const { Controller, L2GraphTokenGateway } = graph.contracts + + let unauthorized: SignerWithAddress + before(async function () { + if (GraphChain.isL1(graph.chainId)) this.skip() + unauthorized = (await graph.getTestAccounts())[0] + }) + + it('bridge should not be paused', async function () { + const paused = await L2GraphTokenGateway.paused() + expect(paused).eq(false) + }) + + it('should be controlled by Controller', async function () { + const controller = await L2GraphTokenGateway.controller() + expect(controller).eq(Controller.address) + }) + + it('l1GRT should match the L1 GraphToken deployed address', async function () { + const l1GRT = await L2GraphTokenGateway.l1GRT() + expect(l1GRT).eq(graph.l1.contracts.GraphToken.address) + }) + + it('l1Counterpart should match the deployed L1 GraphTokenGateway address', async function () { + const l1Counterpart = await L2GraphTokenGateway.l1Counterpart() + expect(l1Counterpart).eq(graph.l1.contracts.L1GraphTokenGateway.address) + }) + + it("l2Router should match Arbitrum's router address", async function () { + const l2Router = await L2GraphTokenGateway.l2Router() + + // TODO: is there a cleaner way to get the router address? + const arbitrumAddressBook = process.env.ARBITRUM_ADDRESS_BOOK ?? 'arbitrum-addresses-local.json' + const arbAddressBook = getAddressBook(arbitrumAddressBook, graph.l2.chainId.toString()) + const arbL2Router = arbAddressBook.getEntry('L2GatewayRouter') + + expect(l2Router).eq(arbL2Router.address) + }) + + describe('calls with unauthorized user', () => { + it('initialize should revert', async function () { + const tx = L2GraphTokenGateway.connect(unauthorized).initialize(unauthorized.address) + await expect(tx).revertedWith('Only implementation') + }) + + it('setL2Router should revert', async function () { + const tx = L2GraphTokenGateway.connect(unauthorized).setL2Router(unauthorized.address) + await expect(tx).revertedWith('Only Controller governor') + }) + + it('setL1TokenAddress should revert', async function () { + const tx = L2GraphTokenGateway.connect(unauthorized).setL1TokenAddress(unauthorized.address) + await expect(tx).revertedWith('Only Controller governor') + }) + + it('setL1CounterpartAddress should revert', async function () { + const tx = L2GraphTokenGateway.connect(unauthorized).setL1CounterpartAddress( + unauthorized.address, + ) + await expect(tx).revertedWith('Only Controller governor') + }) + + it('finalizeInboundTransfer should revert', async function () { + const tx = L2GraphTokenGateway.connect(unauthorized).finalizeInboundTransfer( + unauthorized.address, + unauthorized.address, + unauthorized.address, + '1000000000000', + '0x00', + ) + + await expect(tx).revertedWith('ONLY_COUNTERPART_GATEWAY') + }) + }) +}) diff --git a/e2e/deployment/config/l2/rewardsManager.test.ts b/e2e/deployment/config/l2/rewardsManager.test.ts new file mode 100644 index 000000000..a5e2e7cbf --- /dev/null +++ b/e2e/deployment/config/l2/rewardsManager.test.ts @@ -0,0 +1,17 @@ +import { expect } from 'chai' +import hre from 'hardhat' +import GraphChain from '../../../../gre/helpers/chain' + +describe('[L2] RewardsManager configuration', () => { + const graph = hre.graph() + const { RewardsManager } = graph.contracts + + before(async function () { + if (GraphChain.isL1(graph.chainId)) this.skip() + }) + + it('issuanceRate should be zero', async function () { + const value = await RewardsManager.issuanceRate() + expect(value).eq('0') + }) +}) diff --git a/e2e/deployment/config/protocol.test.ts b/e2e/deployment/config/protocol.test.ts deleted file mode 100644 index 4e12f5088..000000000 --- a/e2e/deployment/config/protocol.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { expect } from 'chai' -import hre from 'hardhat' - -describe('Protocol configuration', () => { - const { contracts } = hre.graph() - - it('should be unpaused', async function () { - const paused = await contracts.Controller.paused() - expect(paused).eq(false) - }) -}) diff --git a/e2e/deployment/config/rewardsManager.test.ts b/e2e/deployment/config/rewardsManager.test.ts index 475abd9f4..ddbcbb835 100644 --- a/e2e/deployment/config/rewardsManager.test.ts +++ b/e2e/deployment/config/rewardsManager.test.ts @@ -19,11 +19,6 @@ describe('RewardsManager configuration', () => { expect(controller).eq(Controller.address) }) - it('issuanceRate should match "issuanceRate" in the config file', async function () { - const value = await RewardsManager.issuanceRate() - expect(value).eq('1000000012184945188') // hardcoded as it's set with a function call rather than init parameter - }) - it('should allow subgraph availability oracle to deny rewards', async function () { const availabilityOracle = await RewardsManager.subgraphAvailabilityOracle() expect(availabilityOracle).eq(namedAccounts.availabilityOracle.address) diff --git a/e2e/deployment/init/graphToken.test.ts b/e2e/deployment/init/graphToken.test.ts deleted file mode 100644 index 8c2230adb..000000000 --- a/e2e/deployment/init/graphToken.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { expect } from 'chai' -import hre from 'hardhat' -import { getItemValue } from '../../../cli/config' - -describe('GraphToken initialization', () => { - const { - graphConfig, - contracts: { GraphToken }, - } = hre.graph() - - it('total supply should match "initialSupply" on the config file', async function () { - const value = await GraphToken.totalSupply() - const expected = getItemValue(graphConfig, 'contracts/GraphToken/init/initialSupply') - hre.network.config.chainId === 1337 ? expect(value).eq(expected) : expect(value).gte(expected) - }) -}) diff --git a/e2e/deployment/init/l1/bridgeEscrow.test.ts b/e2e/deployment/init/l1/bridgeEscrow.test.ts new file mode 100644 index 000000000..2f0333eb2 --- /dev/null +++ b/e2e/deployment/init/l1/bridgeEscrow.test.ts @@ -0,0 +1,17 @@ +import { expect } from 'chai' +import hre from 'hardhat' +import GraphChain from '../../../../gre/helpers/chain' + +describe('BridgeEscrow initialization', () => { + const graph = hre.graph() + const { BridgeEscrow, GraphToken, L1GraphTokenGateway } = graph.contracts + + before(async function () { + if (GraphChain.isL2(graph.chainId)) this.skip() + }) + + it("should allow L1GraphTokenGateway contract to spend MAX_UINT256 tokens on BridgeEscrow's behalf", async function () { + const allowance = await GraphToken.allowance(BridgeEscrow.address, L1GraphTokenGateway.address) + expect(allowance).eq(hre.ethers.constants.MaxUint256) + }) +}) diff --git a/e2e/deployment/init/l1/graphToken.test.ts b/e2e/deployment/init/l1/graphToken.test.ts new file mode 100644 index 000000000..39766b479 --- /dev/null +++ b/e2e/deployment/init/l1/graphToken.test.ts @@ -0,0 +1,19 @@ +import { expect } from 'chai' +import hre from 'hardhat' +import { getItemValue } from '../../../../cli/config' +import GraphChain from '../../../../gre/helpers/chain' + +describe('[L1] GraphToken initialization', () => { + const graph = hre.graph() + const { GraphToken } = graph.contracts + + before(async function () { + if (GraphChain.isL2(graph.chainId)) this.skip() + }) + + it('total supply should match "initialSupply" on the config file', async function () { + const value = await GraphToken.totalSupply() + const expected = getItemValue(graph.graphConfig, 'contracts/GraphToken/init/initialSupply') + expect(value).eq(expected) + }) +}) diff --git a/e2e/deployment/init/l2/graphToken.test.ts b/e2e/deployment/init/l2/graphToken.test.ts new file mode 100644 index 000000000..048531283 --- /dev/null +++ b/e2e/deployment/init/l2/graphToken.test.ts @@ -0,0 +1,17 @@ +import { expect } from 'chai' +import hre from 'hardhat' +import GraphChain from '../../../../gre/helpers/chain' + +describe('[L2] GraphToken initialization', () => { + const graph = hre.graph() + const { GraphToken } = graph.contracts + + before(async function () { + if (GraphChain.isL1(graph.chainId)) this.skip() + }) + + it('total supply should be zero', async function () { + const value = await GraphToken.totalSupply() + expect(value).eq(0) + }) +}) diff --git a/e2e/scenarios/fixtures/bridge.ts b/e2e/scenarios/fixtures/bridge.ts new file mode 100644 index 000000000..a1441ec89 --- /dev/null +++ b/e2e/scenarios/fixtures/bridge.ts @@ -0,0 +1,29 @@ +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' +import { BigNumber } from 'ethers' +import { toGRT } from '../../../cli/network' + +export interface BridgeFixture { + deploymentFile: string + funder: SignerWithAddress + accountsToFund: { + signer: SignerWithAddress + amount: BigNumber + }[] +} + +// Signers +// 0: l1Deployer +// 1: l2Deployer + +export const getBridgeFixture = (signers: SignerWithAddress[]): BridgeFixture => { + return { + deploymentFile: 'localNetwork.json', + funder: signers[0], + accountsToFund: [ + { + signer: signers[1], + amount: toGRT(10_000_000), + }, + ], + } +} diff --git a/e2e/scenarios/lib/accounts.ts b/e2e/scenarios/lib/accounts.ts index 6796a7aea..45c1a0e5b 100644 --- a/e2e/scenarios/lib/accounts.ts +++ b/e2e/scenarios/lib/accounts.ts @@ -12,7 +12,9 @@ const checkBalance = async ( const balance = await getBalanceFn(address) if (balance.lt(amount)) { throw new Error( - `Sender does not have enough funds to distribute! Required ${amount} - Balance ${balance}`, + `Sender does not have enough funds to distribute! Required ${amount} - Balance ${ethers.utils.formatEther( + balance, + )}`, ) } } @@ -20,6 +22,7 @@ const checkBalance = async ( const ensureBalance = async ( beneficiary: string, amount: BigNumberish, + symbol: string, getBalanceFn: (address: string) => Promise, transferFn: ( address: string, @@ -30,7 +33,7 @@ const ensureBalance = async ( const balanceDif = BigNumber.from(amount).sub(balance) if (balanceDif.gt(0)) { - console.log(`Funding ${beneficiary} with ${balanceDif}...`) + console.log(`Funding ${beneficiary} with ${ethers.utils.formatEther(balanceDif)} ${symbol}...`) const tx = await transferFn(beneficiary, balanceDif) await tx.wait() } @@ -48,6 +51,7 @@ export const ensureETHBalance = async ( await ensureBalance( beneficiaries[index], amounts[index], + 'ETH', ethers.provider.getBalance, (address: string, amount: BigNumber) => { return sender.sendTransaction({ to: address, value: amount }) @@ -66,9 +70,12 @@ export const ensureGRTAllowance = async ( const allowTokens = BigNumber.from(amount).sub(allowance) if (allowTokens.gt(0)) { console.log( - `\nApproving ${spender} to spend ${allowTokens} tokens on ${owner.address} behalf...`, + `\nApproving ${spender} to spend ${ethers.utils.formatEther(allowTokens)} GRT on ${ + owner.address + } behalf...`, ) - await grt.connect(owner).approve(spender, amount) + const tx = await grt.connect(owner).approve(spender, amount) + await tx.wait() } } @@ -112,6 +119,7 @@ export const fundAccountsGRT = async ( await ensureBalance( beneficiaries[index], amounts[index], + 'GRT', grt.balanceOf, grt.connect(sender).transfer, ) diff --git a/e2e/scenarios/lib/curation.ts b/e2e/scenarios/lib/curation.ts index 1dc35cbb2..b0c2d927b 100644 --- a/e2e/scenarios/lib/curation.ts +++ b/e2e/scenarios/lib/curation.ts @@ -16,6 +16,6 @@ export const signal = async ( // Add signal console.log(`\nAdd ${amount} in signal to subgraphId ${subgraphId}..`) await sendTransaction(curator, contracts.GNS, 'mintSignal', [subgraphId, amount, 0], { - gasLimit: 2000000, + gasLimit: 4_000_000, }) } diff --git a/e2e/scenarios/lib/helpers.ts b/e2e/scenarios/lib/helpers.ts index 110fca9bf..e9a1eca0e 100644 --- a/e2e/scenarios/lib/helpers.ts +++ b/e2e/scenarios/lib/helpers.ts @@ -1,6 +1,8 @@ export function getGraphOptsFromArgv(): { - graphConfig: string | undefined addressBook: string | undefined + graphConfig: string | undefined + l1GraphConfig: string | undefined + l2GraphConfig: string | undefined disableSecureAccounts?: boolean | undefined } { const argv = process.argv.slice(2) @@ -9,8 +11,10 @@ export function getGraphOptsFromArgv(): { argv[index] && argv[index] !== 'undefined' ? argv[index] : undefined return { - graphConfig: getArgv(0), - addressBook: getArgv(1), - disableSecureAccounts: getArgv(2), + addressBook: getArgv(0), + graphConfig: getArgv(1), + l1GraphConfig: getArgv(2), + l2GraphConfig: getArgv(3), + disableSecureAccounts: getArgv(4), } } diff --git a/e2e/scenarios/lib/staking.ts b/e2e/scenarios/lib/staking.ts index 234d6b1ce..53465cfb8 100644 --- a/e2e/scenarios/lib/staking.ts +++ b/e2e/scenarios/lib/staking.ts @@ -11,9 +11,11 @@ export const stake = async ( ): Promise => { // Approve await ensureGRTAllowance(indexer, contracts.Staking.address, amount, contracts.GraphToken) + const allowance = await contracts.GraphToken.allowance(indexer.address, contracts.Staking.address) + console.log(`Allowance: ${ethers.utils.formatEther(allowance)}`) // Stake - console.log(`\nStaking ${amount} tokens...`) + console.log(`\nStaking ${ethers.utils.formatEther(amount)} tokens...`) await sendTransaction(indexer, contracts.Staking, 'stake', [amount]) } @@ -40,7 +42,7 @@ export const allocateFrom = async ( 'allocateFrom', [indexer.address, subgraphDeploymentID, amount, allocationId, metadata, proof], { - gasLimit: 2000000, + gasLimit: 4_000_000, }, ) } @@ -54,6 +56,6 @@ export const closeAllocation = async ( console.log(`\nClosing ${allocationId}...`) await sendTransaction(indexer, contracts.Staking, 'closeAllocation', [allocationId, poi], { - gasLimit: 2000000, + gasLimit: 4_000_000, }) } diff --git a/e2e/scenarios/lib/subgraph.ts b/e2e/scenarios/lib/subgraph.ts index c17c4e543..f97f1d76b 100644 --- a/e2e/scenarios/lib/subgraph.ts +++ b/e2e/scenarios/lib/subgraph.ts @@ -26,10 +26,14 @@ export const publishNewSubgraph = async ( publisher.address, await contracts.GNS.nextAccountSeqID(publisher.address), ) - await sendTransaction(publisher, contracts.GNS, 'publishNewSubgraph', [ - deploymentId, - randomHexBytes(), - randomHexBytes(), - ]) + await sendTransaction( + publisher, + contracts.GNS, + 'publishNewSubgraph', + [deploymentId, randomHexBytes(), randomHexBytes()], + { + gasLimit: 4_000_000, + }, + ) return subgraphId } diff --git a/e2e/scenarios/send-grt-to-l2.test.ts b/e2e/scenarios/send-grt-to-l2.test.ts new file mode 100644 index 000000000..9b81371b6 --- /dev/null +++ b/e2e/scenarios/send-grt-to-l2.test.ts @@ -0,0 +1,23 @@ +import { expect } from 'chai' +import hre from 'hardhat' +import { getBridgeFixture, BridgeFixture } from './fixtures/bridge' + +describe('Bridge GRT to L2', () => { + const graph = hre.graph() + let bridgeFixture: BridgeFixture + + before(async () => { + const l1Deployer = await graph.l1.getDeployer() + const l2Deployer = await graph.l2.getDeployer() + bridgeFixture = getBridgeFixture([l1Deployer, l2Deployer]) + }) + + describe('GRT balances', () => { + it(`L2 balances should match bridged amount`, async function () { + for (const account of bridgeFixture.accountsToFund) { + const l2GrtBalance = await graph.l2.contracts.GraphToken.balanceOf(account.signer.address) + expect(l2GrtBalance).eq(account.amount) + } + }) + }) +}) diff --git a/e2e/scenarios/send-grt-to-l2.ts b/e2e/scenarios/send-grt-to-l2.ts new file mode 100644 index 000000000..9884c9816 --- /dev/null +++ b/e2e/scenarios/send-grt-to-l2.ts @@ -0,0 +1,40 @@ +// ### Scenario description ### +// Bridge action > Bridge GRT tokens from L1 to L2 +// This scenario will bridge GRT tokens from L1 to L2. See fixtures for details. +// Run with: +// npx hardhat e2e:scenario send-grt-to-l2 --network --graph-config config/graph..yml + +import hre from 'hardhat' +import { TASK_BRIDGE_TO_L2 } from '../../tasks/bridge/to-l2' +import { getGraphOptsFromArgv } from './lib/helpers' +import { getBridgeFixture } from './fixtures/bridge' + +async function main() { + const graphOpts = getGraphOptsFromArgv() + const graph = hre.graph(graphOpts) + + const l1Deployer = await graph.l1.getDeployer() + const l2Deployer = await graph.l2.getDeployer() + + const bridgeFixture = getBridgeFixture([l1Deployer, l2Deployer]) + + // == Send GRT to L2 accounts + for (const account of bridgeFixture.accountsToFund) { + await hre.run(TASK_BRIDGE_TO_L2, { + ...graphOpts, + amount: account.amount.toString(), + sender: bridgeFixture.funder.address, + recipient: account.signer.address, + deploymentFile: bridgeFixture.deploymentFile, + }) + } +} + +// We recommend this pattern to be able to use async/await everywhere +// and properly handle errors. +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error) + process.exitCode = 1 + }) diff --git a/gre/gre.ts b/gre/gre.ts index b0da8cffc..d7e18e594 100644 --- a/gre/gre.ts +++ b/gre/gre.ts @@ -180,7 +180,7 @@ function buildGraphNetworkEnvironment( addressBook: lazyObject(() => getAddressBook(addressBookPath, chainId.toString())), graphConfig: lazyObject(() => readConfig(graphConfigPath, true)), contracts: lazyObject(() => - loadContracts(getAddressBook(addressBookPath, chainId.toString()), provider, enableTxLogging), + loadContracts(getAddressBook(addressBookPath, chainId.toString()), chainId, provider), ), getWallets: lazyFunction(() => () => getWallets()), getWallet: lazyFunction(() => (address: string) => getWallet(address)), diff --git a/hardhat.config.ts b/hardhat.config.ts index a2c4856d7..3b989dcd0 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -29,7 +29,7 @@ const SKIP_LOAD = process.env.SKIP_LOAD === 'true' function loadTasks() { require('./gre/gre') - ;['contracts', 'deployment', 'actions', 'verify', 'e2e'].forEach((folder) => { + ;['contracts', 'bridge', 'deployment', 'actions', 'verify', 'e2e'].forEach((folder) => { const tasksPath = path.join(__dirname, 'tasks', folder) fs.readdirSync(tasksPath) .filter((pth) => pth.includes('.ts')) @@ -59,7 +59,22 @@ interface NetworkConfig { const networkConfigs: NetworkConfig[] = [ { network: 'mainnet', chainId: 1, graphConfig: 'config/graph.mainnet.yml' }, + { network: 'rinkeby', chainId: 4, graphConfig: 'config/graph.rinkeby.yml' }, { network: 'goerli', chainId: 5, graphConfig: 'config/graph.goerli.yml' }, + { network: 'kovan', chainId: 42 }, + { network: 'arbitrum-rinkeby', chainId: 421611, url: 'https://rinkeby.arbitrum.io/rpc' }, + { + network: 'arbitrum-one', + chainId: 42161, + url: 'https://arb1.arbitrum.io/rpc', + graphConfig: 'config/graph.arbitrum-one.yml', + }, + { + network: 'arbitrum-goerli', + chainId: 421613, + url: 'https://goerli-rollup.arbitrum.io/rpc', + graphConfig: 'config/graph.arbitrum-goerli.yml', + }, ] function getAccountsKeys() { @@ -92,6 +107,9 @@ function setupNetworkProviders(hardhatConfig) { const DEFAULT_TEST_MNEMONIC = 'myth like bonus scare over problem client lizard pioneer submit female collect' +const DEFAULT_L2_TEST_MNEMONIC = + 'urge never interest human any economy gentle canvas anxiety pave unlock find' + const config: HardhatUserConfig = { paths: { sources: './contracts', @@ -145,11 +163,23 @@ const config: HardhatUserConfig = { process.env.FORK === 'true' ? getAccountsKeys() : { mnemonic: DEFAULT_TEST_MNEMONIC }, graphConfig: 'config/graph.localhost.yml', }, + localnitrol1: { + chainId: 1337, + url: 'http://localhost:8545', + accounts: { mnemonic: DEFAULT_TEST_MNEMONIC }, + graphConfig: 'config/graph.localhost.yml', + }, + localnitrol2: { + chainId: 412346, + url: 'http://localhost:8547', + accounts: { mnemonic: DEFAULT_L2_TEST_MNEMONIC }, + graphConfig: 'config/graph.arbitrum-localhost.yml', + }, }, graph: { addressBook: process.env.ADDRESS_BOOK ?? 'addresses.json', l1GraphConfig: process.env.L1_GRAPH_CONFIG ?? 'config/graph.localhost.yml', - l2GraphConfig: process.env.L2_GRAPH_CONFIG, + l2GraphConfig: process.env.L2_GRAPH_CONFIG ?? 'config/graph.arbitrum-localhost.yml', }, etherscan: { apiKey: process.env.ETHERSCAN_API_KEY, diff --git a/package.json b/package.json index 8b378285b..ef123981e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@graphprotocol/contracts", - "version": "1.17.0", + "version": "2.0.0", "description": "Contracts for the Graph Protocol", "directories": { "test": "test" @@ -12,13 +12,15 @@ "addresses.json" ], "dependencies": { - "ethers": "^5.4.4" + "ethers": "^5.6.0" }, "devDependencies": { + "@arbitrum/sdk": "^3.0.0", "@commitlint/cli": "^13.2.1", "@commitlint/config-conventional": "^13.2.0", - "@ethersproject/experimental": "^5.4.0", - "@graphprotocol/common-ts": "^1.6.0", + "@defi-wonderland/smock": "^2.0.7", + "@ethersproject/experimental": "^5.6.0", + "@graphprotocol/common-ts": "^1.8.3", "@nomiclabs/hardhat-ethers": "^2.0.2", "@nomiclabs/hardhat-etherscan": "^2.1.1", "@nomiclabs/hardhat-waffle": "^2.0.1", @@ -41,6 +43,7 @@ "@typescript-eslint/eslint-plugin": "^4.0.0", "@typescript-eslint/parser": "^4.0.0", "@urql/core": "^2.1.3", + "arbos-precompiles": "^1.0.2", "bignumber.js": "^9.0.0", "chai": "^4.3.4", "chai-as-promised": "^7.1.1", @@ -88,6 +91,7 @@ "deploy": "yarn build && yarn predeploy && hardhat migrate", "deploy-localhost": "yarn deploy --force --network localhost --graph-config config/graph.localhost.yml", "deploy-goerli": "yarn deploy --force --network goerli --graph-config config/graph.goerli.yml", + "deploy-arbitrum-goerli": "yarn deploy --force --network arbitrum-goerli --graph-config config/graph.arbitrum-goerli.yml", "predeploy": "scripts/predeploy", "test": "scripts/test", "test:e2e": "scripts/e2e", diff --git a/scripts/e2e b/scripts/e2e index a6847cbd0..70aae7e63 100755 --- a/scripts/e2e +++ b/scripts/e2e @@ -3,45 +3,232 @@ set -eo pipefail source $(pwd)/scripts/evm +### > SCRIPT CONFIG < # Allow overriding config -GRAPH_CONFIG=${1:-"config/graph.localhost.yml"} -ADDRESS_BOOK=${2:-"addresses.json"} +ADDRESS_BOOK=${ADDRESS_BOOK:-"addresses-local.json"} +ARBITRUM_ADDRESS_BOOK=${ARBITRUM_ADDRESS_BOOK:-"arbitrum-addresses-local.json"} +ARBITRUM_DEPLOYMENT_FILE=${ARBITRUM_DEPLOYMENT_FILE:-"localNetwork.json"} -### Setup +L1_NETWORK=${L1_NETWORK} +L2_NETWORK=${L2_NETWORK} + +L1_GRAPH_CONFIG=${L1_GRAPH_CONFIG:-"config/graph.localhost.yml"} +L2_GRAPH_CONFIG=${L2_GRAPH_CONFIG:-"config/graph.arbitrum-localhost.yml"} + +echo "Running e2e tests" +echo "- Using address book: $ADDRESS_BOOK" + +if [[ -n "$L1_NETWORK" ]]; then + echo "- Using L1 network: $L1_NETWORK" + echo "- Using L1 config: $L1_GRAPH_CONFIG" +else + echo "- No L1_NETWORK provided, skipping L1 tests" +fi + +if [[ -n "$L2_NETWORK" ]]; then + echo "- Using L2 network: $L2_NETWORK" + echo "- Using L2 config: $L2_GRAPH_CONFIG" + echo "- Using arbitrum address book: $ARBITRUM_ADDRESS_BOOK" + echo "- Using arbitrum deployment file: $ARBITRUM_DEPLOYMENT_FILE" +else + echo "- No L2_NETWORK provided, skipping L2 tests" +fi + +if [[ -z "$L1_NETWORK" ]] && [[ -z "$L2_NETWORK" ]]; then + echo "Must specify one of L1_NETWORK or L2_NETWORK!" + exit 0 +fi + +if [[ "$L1_NETWORK" == "$L2_NETWORK" ]]; then + echo "L1_NETWORK and L2_NETWORK must be different networks!" + exit 0 +fi + +### > SCRIPT AUX FUNCTIONS < +function pre_deploy() { + local NETWORK=$1 + local GRAPH_CONFIG=$2 + + # Create named accounts + npx hardhat migrate:accounts --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --disable-secure-accounts + + # Fund accounts if using nitro test nodes + if [[ "$NETWORK" == *"localnitro"* ]]; then + npx hardhat nitro:fund-accounts --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --disable-secure-accounts + fi +} + +function deploy() { + local NETWORK=$1 + local GRAPH_CONFIG=$2 + local ADDRESS_BOOK=$3 + + # Deploy protocol + npx hardhat migrate \ + --network "$NETWORK" \ + --skip-confirmation \ + --auto-mine \ + --force \ + --graph-config "$GRAPH_CONFIG" \ + --address-book "$ADDRESS_BOOK" +} + +function post_deploy () { + local NETWORK=$1 + local GRAPH_CONFIG=$2 + local ADDRESS_BOOK=$3 + + # Governor to accept contracts ownership + npx hardhat migrate:ownership --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" --disable-secure-accounts + + # Unpause the protocol + npx hardhat migrate:unpause:protocol --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" --disable-secure-accounts +} + +function configure_bridge () { + local L1_NETWORK=$1 + local L1_GRAPH_CONFIG=$2 + local L2_NETWORK=$3 + local L2_GRAPH_CONFIG=$4 + local ADDRESS_BOOK=$5 + local ARBITRUM_ADDRESS_BOOK=$6 + local ARBITRUM_DEPLOYMENT_FILE=$7 + + # These settings are only used for CLI bridge commands + # so we keep them here to avoid confusion with hardhat based tasks + local L1_CHAIN_ID=${L1_CHAIN_ID:-"1337"} + local L2_CHAIN_ID=${L2_CHAIN_ID:-"412346"} + + local L1_RPC=${L1_RPC:-"http://localhost:8545"} + local L2_RPC=${L2_RPC:-"http://localhost:8547"} + + local L1_MNEMONIC=${L1_MNEMONIC:-"myth like bonus scare over problem client lizard pioneer submit female collect"} + local L2_MNEMONIC=${L2_MNEMONIC:-"urge never interest human any economy gentle canvas anxiety pave unlock find"} + + # Copy required arbitrum contract addresses to the local arbitrum address book + if [[ "$L1_NETWORK" == *"localnitro"* ]]; then + npx hardhat nitro:address-book-setup --deployment-file "$ARBITRUM_DEPLOYMENT_FILE" --arbitrum-address-book "$ARBITRUM_ADDRESS_BOOK" + fi + + # Configure the bridge + ./cli/cli.ts -a "$ADDRESS_BOOK" -p "$L2_RPC" -m "$L2_MNEMONIC" -n 2 -r "$ARBITRUM_ADDRESS_BOOK" protocol configure-l2-bridge "$L1_CHAIN_ID" + ./cli/cli.ts -a "$ADDRESS_BOOK" -p "$L1_RPC" -m "$L1_MNEMONIC" -n 2 -r "$ARBITRUM_ADDRESS_BOOK" protocol configure-l1-bridge "$L2_CHAIN_ID" + + # Unpause the bridge + npx hardhat migrate:unpause:bridge --network "$L2_NETWORK" --graph-config "$L2_GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" --disable-secure-accounts + npx hardhat migrate:unpause:bridge --network "$L1_NETWORK" --graph-config "$L1_GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" --disable-secure-accounts +} + +function test_e2e () { + local NETWORK=$1 + local L1_GRAPH_CONFIG=$2 + local L2_GRAPH_CONFIG=$3 + local ADDRESS_BOOK=$4 + local SKIP_BRIDGE_TESTS=$5 + + if [[ -z "$SKIP_BRIDGE_TESTS" ]]; then + npx hardhat e2e --network "$NETWORK" --l1-graph-config "$L1_GRAPH_CONFIG" --l2-graph-config "$L2_GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" + else + npx hardhat e2e --network "$NETWORK" --l1-graph-config "$L1_GRAPH_CONFIG" --l2-graph-config "$L2_GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" --skip-bridge + fi +} + +function test_e2e_scenarios () { + local NETWORK=$1 + local L1_GRAPH_CONFIG=$2 + local L2_GRAPH_CONFIG=$3 + local ADDRESS_BOOK=$4 + + npx hardhat e2e:scenario create-subgraphs --network "$NETWORK" --l1-graph-config "$L1_GRAPH_CONFIG" --l2-graph-config "$L2_GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" --disable-secure-accounts + npx hardhat e2e:scenario open-allocations --network "$NETWORK" --l1-graph-config "$L1_GRAPH_CONFIG" --l2-graph-config "$L2_GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" --disable-secure-accounts + + # skip close-allocations for arbitrum testnodes as we can't advance epoch + if [[ "$NETWORK" != *"localnitro"* ]]; then + npx hardhat e2e:scenario close-allocations --network "$NETWORK" --l1-graph-config "$L1_GRAPH_CONFIG" --l2-graph-config "$L2_GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" --disable-secure-accounts + fi +} + +function test_e2e_scenarios_bridge () { + local NETWORK=$1 + local L1_GRAPH_CONFIG=$2 + local L2_GRAPH_CONFIG=$3 + local ADDRESS_BOOK=$4 + + npx hardhat e2e:scenario send-grt-to-l2 --network "$NETWORK" --l1-graph-config "$L1_GRAPH_CONFIG" --l2-graph-config "$L2_GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" --disable-secure-accounts +} + + +### > SCRIPT START < ### +## SETUP # Compile contracts yarn build # Start evm -evm_kill -evm_start -sleep 5 - -# Pre-deploy actions -npx hardhat migrate:accounts --network localhost --graph-config "$GRAPH_CONFIG" --disable-secure-accounts - -# Deploy protocol -npx hardhat migrate \ - --network localhost \ - --skip-confirmation \ - --auto-mine \ - --graph-config "$GRAPH_CONFIG" \ - --address-book "$ADDRESS_BOOK" - -# Post deploy actions -npx hardhat migrate:ownership --network localhost --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" --disable-secure-accounts -npx hardhat migrate:unpause --network localhost --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" --disable-secure-accounts - -### Test -# Run tests using the localhost evm instance and the localhost graph config -npx hardhat e2e --network localhost --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" --disable-secure-accounts -npx hardhat e2e:scenario create-subgraphs --network localhost --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" --disable-secure-accounts -npx hardhat e2e:scenario open-allocations --network localhost --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" --disable-secure-accounts -npx hardhat e2e:scenario close-allocations --network localhost --graph-config "$GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" --disable-secure-accounts - -### Cleanup +if [[ "$L1_NETWORK" == "localhost" || "$L2_NETWORK" == "localhost" ]]; then + evm_kill + evm_start +fi + +# Create address book if needed +if [[ ! -f "$ADDRESS_BOOK" ]]; then + echo '{}' > "$ADDRESS_BOOK" +fi + +# Reset arbitrum address book (just in case the deployment changed) +if [[ -f "$ARBITRUM_ADDRESS_BOOK" ]]; then + rm "$ARBITRUM_ADDRESS_BOOK" +fi +echo '{}' > "$ARBITRUM_ADDRESS_BOOK" + +# Reset arbitrum address book (just in case the deployment changed) +if [[ -f "$ARBITRUM_DEPLOYMENT_FILE" ]]; then + rm "$ARBITRUM_DEPLOYMENT_FILE" +fi + +## DEPLOY +# Deploy L1 +if [[ -n "$L1_NETWORK" ]]; then + echo "Deploying L1 protocol" + pre_deploy "$L1_NETWORK" "$L1_GRAPH_CONFIG" + deploy "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$ADDRESS_BOOK" + post_deploy "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$ADDRESS_BOOK" +fi + +# Deploy L2 +if [[ -n "$L2_NETWORK" ]]; then + echo "Deploying L2 protocol" + pre_deploy "$L2_NETWORK" "$L2_GRAPH_CONFIG" + deploy "$L2_NETWORK" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" + post_deploy "$L2_NETWORK" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" +fi + +# Configure bridge +if [[ -n "$L1_NETWORK" ]] && [[ -n "$L2_NETWORK" ]]; then + configure_bridge "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$L2_NETWORK" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" "$ARBITRUM_ADDRESS_BOOK" "$ARBITRUM_DEPLOYMENT_FILE" +fi + +## TEST +# Run e2e tests +if [[ -z "$L2_NETWORK" ]]; then + test_e2e "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" true +else + test_e2e "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" + test_e2e "$L2_NETWORK" "$L1_GRAPH_CONFIG" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" +fi + +# Run scenario tests +test_e2e_scenarios "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" +if [[ -n "$L2_NETWORK" ]]; then + test_e2e_scenarios_bridge "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" + test_e2e_scenarios "$L2_NETWORK" "$L1_GRAPH_CONFIG" "$L2_GRAPH_CONFIG" "$ADDRESS_BOOK" +fi + +## Cleanup # Exit error mode so the evm instance always gets killed -set +e -result=0 +if [[ "$L1_NETWORK" == "localhost" || "$L2_NETWORK" == "localhost" ]]; then + set +e + result=0 -evm_kill -exit $result + evm_kill + exit $result +fi diff --git a/scripts/evm b/scripts/evm index cead9acc3..62bab58fb 100644 --- a/scripts/evm +++ b/scripts/evm @@ -1,14 +1,36 @@ #!/bin/bash TESTRPC_PORT=${TESTRPC_PORT:-8545} +MAX_RETRIES=120 evm_running() { - nc -z localhost "$TESTRPC_PORT" > /dev/null + lsof -i:$TESTRPC_PORT -t > /dev/null +} + +evm_ping() { + PORT=$1 + curl --location --request POST "localhost:$PORT/" \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "jsonrpc":"2.0", + "method":"web3_clientVersion", + "params":[], + "id":1 + }' } evm_start() { echo "Starting our own evm instance at port $TESTRPC_PORT" npx hardhat node --port "$TESTRPC_PORT" > /dev/null & + retries=0 + while ! evm_ping $TESTRPC_PORT; do + ((retries=retries+1)) + if [ $retries -gt $MAX_RETRIES ]; then + echo "Timeout waiting for evm instance" + exit 1 + fi + sleep 1 + done } evm_kill() { @@ -16,4 +38,4 @@ evm_kill() { echo "Killing evm instance at port $TESTRPC_PORT" kill -9 $(lsof -i:$TESTRPC_PORT -t) fi -} \ No newline at end of file +} diff --git a/tasks/bridge/to-l2.ts b/tasks/bridge/to-l2.ts new file mode 100644 index 000000000..ce12d1a57 --- /dev/null +++ b/tasks/bridge/to-l2.ts @@ -0,0 +1,61 @@ +import { task } from 'hardhat/config' +import { cliOpts } from '../../cli/defaults' +import { sendToL2 } from '../../cli/commands/bridge/to-l2' +import { loadEnv } from '../../cli/env' +import { TASK_NITRO_SETUP_SDK } from '../deployment/nitro' +import { BigNumber } from 'ethers' + +export const TASK_BRIDGE_TO_L2 = 'bridge:send-to-l2' + +task(TASK_BRIDGE_TO_L2, 'Bridge GRT tokens from L1 to L2') + .addParam('amount', 'Amount of tokens to bridge') + .addOptionalParam('sender', 'Address of the sender. L1 deployer if empty.') + .addOptionalParam('recipient', 'Receiving address in L2. Same to L1 address if empty.') + .addOptionalParam('addressBook', cliOpts.addressBook.description) + .addOptionalParam( + 'arbitrumAddressBook', + cliOpts.arbitrumAddressBook.description, + cliOpts.arbitrumAddressBook.default, + ) + .addOptionalParam('l1GraphConfig', cliOpts.graphConfig.description) + .addOptionalParam('l2GraphConfig', cliOpts.graphConfig.description) + .addOptionalParam( + 'deploymentFile', + 'Nitro testnode deployment file. Must specify if using nitro test nodes.', + ) + .setAction(async (taskArgs, hre) => { + console.log('> Sending GRT to L2') + const graph = hre.graph(taskArgs) + + // If local, add nitro test node networks to sdk + if (taskArgs.deploymentFile) { + console.log('> Adding nitro test node network to sdk') + await hre.run(TASK_NITRO_SETUP_SDK, { deploymentFile: taskArgs.deploymentFile }) + } + + // Get the sender, use L1 deployer if not provided + const l1Deployer = await graph.l1.getDeployer() + const sender: string = taskArgs.sender ?? l1Deployer.address + + let wallet = await graph.l1.getWallet(sender) + + if (!wallet) { + throw new Error(`No wallet found for address ${sender}`) + } else { + console.log(`> Using wallet ${wallet.address}`) + wallet = wallet.connect(graph.l1.provider) + } + + // Patch sendToL2 opts + taskArgs.l2Provider = graph.l2.provider + taskArgs.amount = hre.ethers.utils.formatEther(taskArgs.amount) // sendToL2 expects amount in GRT + + // L2 provider gas limit estimation has been hit or miss in CI, 400k should be more than enough + if (process.env.CI) { + taskArgs.maxGas = BigNumber.from('400000') + } + + await sendToL2(await loadEnv(taskArgs, wallet), taskArgs) + + console.log('Done!') + }) diff --git a/tasks/deployment/nitro.ts b/tasks/deployment/nitro.ts new file mode 100644 index 000000000..8cd3feb0c --- /dev/null +++ b/tasks/deployment/nitro.ts @@ -0,0 +1,141 @@ +import { BigNumber, ContractTransaction } from 'ethers' +import { subtask, task } from 'hardhat/config' +import { cliOpts } from '../../cli/defaults' +import { addCustomNetwork } from '@arbitrum/sdk/dist/lib/dataEntities/networks' +import fs from 'fs' +import { execSync } from 'child_process' + +export const TASK_NITRO_FUND_ACCOUNTS = 'nitro:fund-accounts' +export const TASK_NITRO_SETUP_SDK = 'nitro:sdk-setup' +export const TASK_NITRO_SETUP_ADDRESS_BOOK = 'nitro:address-book-setup' +export const TASK_NITRO_FETCH_DEPLOYMENT_FILE = 'nitro:fetch-deployment-file' + +task(TASK_NITRO_FUND_ACCOUNTS, 'Funds protocol accounts on Arbitrum Nitro testnodes') + .addFlag('disableSecureAccounts', 'Disable secure accounts on GRE') + .addOptionalParam('graphConfig', cliOpts.graphConfig.description) + .addOptionalParam('privateKey', 'The private key for Arbitrum testnode genesis account') + .addOptionalParam('amount', 'The amount to fund each account with') + .setAction(async (taskArgs, hre) => { + // Arbitrum Nitro testnodes have a pre-funded genesis account whose private key is hardcoded here: + // - L1 > https://github.com/OffchainLabs/nitro/blob/01c558c06ad9cbaa083bebe3e51960e195c3fd6b/test-node.bash#L136 + // - L2 > https://github.com/OffchainLabs/nitro/blob/01c558c06ad9cbaa083bebe3e51960e195c3fd6b/testnode-scripts/config.ts#L22 + const genesisAccountPrivateKey = + taskArgs.privateKey ?? 'e887f7d17d07cc7b8004053fb8826f6657084e88904bb61590e498ca04704cf2' + const genesisAccount = new hre.ethers.Wallet(genesisAccountPrivateKey) + + // Get protocol accounts + const { getDeployer, getNamedAccounts, getTestAccounts, provider } = hre.graph(taskArgs) + const deployer = await getDeployer() + const testAccounts = await getTestAccounts() + const namedAccounts = await getNamedAccounts() + const accounts = [ + deployer, + ...testAccounts, + ...Object.keys(namedAccounts).map((k) => namedAccounts[k]), + ] + + // Amount to fund + // - If amount is specified, use that + // - Otherwise, use 95% of genesis account balance with a maximum of 100 Eth + let amount: BigNumber + const maxAmount = hre.ethers.utils.parseEther('100') + const genesisAccountBalance = await provider.getBalance(genesisAccount.address) + + if (taskArgs.amount) { + amount = hre.ethers.BigNumber.from(taskArgs.amount) + } else { + const splitGenesisBalance = genesisAccountBalance.mul(95).div(100).div(accounts.length) + if (splitGenesisBalance.gt(maxAmount)) { + amount = maxAmount + } else { + amount = splitGenesisBalance + } + } + + // Check genesis account balance + const requiredFunds = amount.mul(accounts.length) + if (genesisAccountBalance.lt(requiredFunds)) { + throw new Error('Insufficient funds in genesis account') + } + + // Fund accounts + console.log('> Funding protocol addresses') + console.log(`Genesis account: ${genesisAccount.address}`) + console.log(`Total accounts: ${accounts.length}`) + console.log(`Amount per account: ${hre.ethers.utils.formatEther(amount)}`) + console.log(`Required funds: ${hre.ethers.utils.formatEther(requiredFunds)}`) + + const txs: ContractTransaction[] = [] + for (const account of accounts) { + const tx = await genesisAccount.connect(provider).sendTransaction({ + value: amount, + to: account.address, + }) + txs.push(tx) + } + await Promise.all(txs.map((tx) => tx.wait())) + console.log('Done!') + }) + +// Arbitrum SDK does not support Nitro testnodes out of the box +// This adds the testnodes to the SDK configuration +subtask(TASK_NITRO_SETUP_SDK, 'Adds nitro testnodes to SDK config') + .addParam('deploymentFile', 'The testnode deployment file to use', 'localNetwork.json') + .setAction(async (taskArgs) => { + if (!fs.existsSync(taskArgs.deploymentFile)) { + throw new Error(`Deployment file not found: ${taskArgs.deploymentFile}`) + } + const deployment = JSON.parse(fs.readFileSync(taskArgs.deploymentFile, 'utf-8')) + addCustomNetwork({ + customL1Network: deployment.l1Network, + customL2Network: deployment.l2Network, + }) + }) + +subtask(TASK_NITRO_FETCH_DEPLOYMENT_FILE, 'Fetches nitro deployment file from a local testnode') + .addParam( + 'deploymentFile', + 'Path to the file where to deployment file will be saved', + 'localNetwork.json', + ) + .setAction(async (taskArgs) => { + console.log(`Attempting to fetch deployment file from testnode...`) + + const command = `docker exec $(docker ps -qf "name=sequencer") cat /workspace/localNetwork.json > ${taskArgs.deploymentFile}` + const stdOut = execSync(command) + console.log(stdOut.toString()) + + if (!fs.existsSync(taskArgs.deploymentFile)) { + throw new Error(`Unable to fetch deployment file: ${taskArgs.deploymentFile}`) + } + console.log(`Deployment file saved to ${taskArgs.deploymentFile}`) + }) + +// Read arbitrum contract addresses from deployment file and write them to the address book +task(TASK_NITRO_SETUP_ADDRESS_BOOK, 'Write arbitrum addresses to address book') + .addParam('deploymentFile', 'The testnode deployment file to use') + .addParam('arbitrumAddressBook', 'Arbitrum address book file') + .setAction(async (taskArgs, hre) => { + if (!fs.existsSync(taskArgs.deploymentFile)) { + await hre.run(TASK_NITRO_FETCH_DEPLOYMENT_FILE, taskArgs) + } + const deployment = JSON.parse(fs.readFileSync(taskArgs.deploymentFile, 'utf-8')) + + const addressBook = { + '1337': { + L1GatewayRouter: { + address: deployment.l2Network.tokenBridge.l1GatewayRouter, + }, + IInbox: { + address: deployment.l2Network.ethBridge.inbox, + }, + }, + '412346': { + L2GatewayRouter: { + address: deployment.l2Network.tokenBridge.l2GatewayRouter, + }, + }, + } + + fs.writeFileSync(taskArgs.arbitrumAddressBook, JSON.stringify(addressBook)) + }) diff --git a/tasks/deployment/ownership.ts b/tasks/deployment/ownership.ts index 40d133d79..9f8e318fc 100644 --- a/tasks/deployment/ownership.ts +++ b/tasks/deployment/ownership.ts @@ -1,4 +1,5 @@ -import { ContractTransaction } from 'ethers' +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' +import { Contract, ContractTransaction, ethers } from 'ethers' import { task } from 'hardhat/config' import { cliOpts } from '../../cli/defaults' @@ -7,18 +8,43 @@ task('migrate:ownership', 'Accepts ownership of protocol contracts on behalf of .addOptionalParam('addressBook', cliOpts.addressBook.description) .addOptionalParam('graphConfig', cliOpts.graphConfig.description) .setAction(async (taskArgs, hre) => { - const { contracts, getNamedAccounts } = hre.graph(taskArgs) - const { governor } = await getNamedAccounts() + const graph = hre.graph(taskArgs) + const { GraphToken, Controller, GraphProxyAdmin, SubgraphNFT } = graph.contracts + const { governor } = await graph.getNamedAccounts() console.log('> Accepting ownership of contracts') console.log(`- Governor: ${governor.address}`) + const governedContracts = [GraphToken, Controller, GraphProxyAdmin, SubgraphNFT] const txs: ContractTransaction[] = [] - txs.push(await contracts.GraphToken.connect(governor).acceptOwnership()) - txs.push(await contracts.Controller.connect(governor).acceptOwnership()) - txs.push(await contracts.GraphProxyAdmin.connect(governor).acceptOwnership()) - txs.push(await contracts.SubgraphNFT.connect(governor).acceptOwnership()) + for (const contract of governedContracts) { + const tx = await acceptOwnershipIfPending(contract, governor) + if (tx) { + txs.push() + } + } await Promise.all(txs.map((tx) => tx.wait())) console.log('Done!') }) + +async function acceptOwnershipIfPending( + contract: Contract, + signer: SignerWithAddress, +): Promise { + const pendingGovernor = await contract.connect(signer).pendingGovernor() + + if (pendingGovernor === ethers.constants.AddressZero) { + console.log(`No pending governor for ${contract.address}`) + return + } + + if (pendingGovernor === signer.address) { + console.log(`Accepting ownership of ${contract.address}`) + return contract.connect(signer).acceptOwnership() + } else { + console.log( + `Signer ${signer.address} is not the pending governor of ${contract.address}, it is ${pendingGovernor}`, + ) + } +} diff --git a/tasks/deployment/sync.ts b/tasks/deployment/sync.ts new file mode 100644 index 000000000..429f5aba6 --- /dev/null +++ b/tasks/deployment/sync.ts @@ -0,0 +1,71 @@ +import { ContractTransaction } from 'ethers' +import { task } from 'hardhat/config' +import { cliOpts } from '../../cli/defaults' +import { chainIdIsL2 } from '../../cli/cross-chain' + +task('migrate:sync', 'Sync controller contracts') + .addParam('addressBook', cliOpts.addressBook.description, cliOpts.addressBook.default) + .addParam('graphConfig', cliOpts.graphConfig.description, cliOpts.graphConfig.default) + .setAction(async (taskArgs, hre) => { + const { contracts, getDeployer } = hre.graph({ + addressBook: taskArgs.addressBook, + graphConfig: taskArgs.graphConfig, + }) + const deployer = await getDeployer() + + const chainId = hre.network.config.chainId?.toString() ?? '1337' + const isL2 = chainIdIsL2(chainId) + + // Sync contracts + console.log( + `Syncing cache for contract addresses on chainId ${chainId} (${isL2 ? 'L2' : 'L1'})`, + ) + const txs: ContractTransaction[] = [] + console.log('> Syncing cache on Curation') + txs.push(await contracts['Curation'].connect(deployer).syncAllContracts()) + console.log('> Syncing cache on GNS') + txs.push(await contracts['GNS'].connect(deployer).syncAllContracts()) + console.log('> Syncing cache on ServiceRegistry') + txs.push(await contracts['ServiceRegistry'].connect(deployer).syncAllContracts()) + console.log('> Syncing cache on DisputeManager') + txs.push(await contracts['DisputeManager'].connect(deployer).syncAllContracts()) + console.log('> Syncing cache on RewardsManager') + txs.push(await contracts['RewardsManager'].connect(deployer).syncAllContracts()) + console.log('> Syncing cache on Staking') + txs.push(await contracts['Staking'].connect(deployer).syncAllContracts()) + if (isL2) { + console.log('> Syncing cache on L2GraphTokenGateway') + txs.push(await contracts['L2GraphTokenGateway'].connect(deployer).syncAllContracts()) + if (contracts['L2Reservoir']) { + console.log('> Syncing cache on L2Reservoir') + txs.push(await contracts['L2Reservoir'].connect(deployer).syncAllContracts()) + } + } else { + // L1 chains might not have these contracts deployed yet... + if (contracts['L1GraphTokenGateway']) { + console.log('> Syncing cache on L1GraphTokenGateway') + txs.push(await contracts['L1GraphTokenGateway'].connect(deployer).syncAllContracts()) + } else { + console.log('Skipping L1GraphTokenGateway as it does not seem to be deployed yet') + } + if (contracts['BridgeEscrow']) { + console.log('> Syncing cache on BridgeEscrow') + txs.push(await contracts['BridgeEscrow'].connect(deployer).syncAllContracts()) + } else { + console.log('Skipping BridgeEscrow as it does not seem to be deployed yet') + } + if (contracts['L1Reservoir']) { + console.log('> Syncing cache on L1Reservoir') + txs.push(await contracts['L1Reservoir'].connect(deployer).syncAllContracts()) + } else { + console.log('Skipping L1Reservoir as it does not seem to be deployed yet') + } + } + await Promise.all( + txs.map((tx) => { + console.log(tx.hash) + return tx.wait() + }), + ) + console.log('Done!') + }) diff --git a/tasks/deployment/unpause.ts b/tasks/deployment/unpause.ts index 6534fd9bf..b7bc29b5d 100644 --- a/tasks/deployment/unpause.ts +++ b/tasks/deployment/unpause.ts @@ -1,16 +1,38 @@ import { task } from 'hardhat/config' import { cliOpts } from '../../cli/defaults' +import GraphChain from '../../gre/helpers/chain' -task('migrate:unpause', 'Unpause protocol') +task('migrate:unpause:protocol', 'Unpause protocol (except bridge)') .addFlag('disableSecureAccounts', 'Disable secure accounts on GRE') .addOptionalParam('addressBook', cliOpts.addressBook.description) .addOptionalParam('graphConfig', cliOpts.graphConfig.description) .setAction(async (taskArgs, hre) => { - const { contracts, getNamedAccounts } = hre.graph(taskArgs) - const { governor } = await getNamedAccounts() + const graph = hre.graph(taskArgs) + const { governor } = await graph.getNamedAccounts() + const { Controller } = graph.contracts console.log('> Unpausing protocol') - const tx = await contracts.Controller.connect(governor).setPaused(false) + const tx = await Controller.connect(governor).setPaused(false) await tx.wait() + + console.log('Done!') + }) + +task('migrate:unpause:bridge', 'Unpause bridge') + .addFlag('disableSecureAccounts', 'Disable secure accounts on GRE') + .addOptionalParam('addressBook', cliOpts.addressBook.description) + .addOptionalParam('graphConfig', cliOpts.graphConfig.description) + .setAction(async (taskArgs, hre) => { + const graph = hre.graph(taskArgs) + const { governor } = await graph.getNamedAccounts() + const { L1GraphTokenGateway, L2GraphTokenGateway } = graph.contracts + + console.log('> Unpausing bridge') + const GraphTokenGateway = GraphChain.isL2(graph.chainId) + ? L2GraphTokenGateway + : L1GraphTokenGateway + const tx = await GraphTokenGateway.connect(governor).setPaused(false) + await tx.wait() + console.log('Done!') }) diff --git a/tasks/e2e/e2e.ts b/tasks/e2e/e2e.ts index be792eda9..aa712380c 100644 --- a/tasks/e2e/e2e.ts +++ b/tasks/e2e/e2e.ts @@ -34,15 +34,24 @@ const setGraphConfig = async (args: TaskArguments, hre: HardhatRuntimeEnvironmen } task('e2e', 'Run all e2e tests') - .addFlag('disableSecureAccounts', 'Disable secure accounts on GRE') .addOptionalParam('graphConfig', cliOpts.graphConfig.description) + .addOptionalParam('l1GraphConfig', cliOpts.graphConfig.description) + .addOptionalParam('l2GraphConfig', cliOpts.graphConfig.description) .addOptionalParam('addressBook', cliOpts.addressBook.description) + .addFlag('skipBridge', 'Skip bridge tests') .setAction(async (args, hre: HardhatRuntimeEnvironment) => { - const testFiles = [ + let testFiles = [ ...new glob.GlobSync(CONFIG_TESTS).found, ...new glob.GlobSync(INIT_TESTS).found, ] + if (args.skipBridge) { + testFiles = testFiles.filter((file) => !['l1', 'l2'].includes(file.split('/')[3])) + } + + // Disable secure accounts, we don't need them for this task + hre.config.graph.disableSecureAccounts = true + setGraphConfig(args, hre) await hre.run(TASK_TEST, { testFiles: testFiles, @@ -50,11 +59,16 @@ task('e2e', 'Run all e2e tests') }) task('e2e:config', 'Run deployment configuration e2e tests') - .addFlag('disableSecureAccounts', 'Disable secure accounts on GRE') .addOptionalParam('graphConfig', cliOpts.graphConfig.description) + .addOptionalParam('l1GraphConfig', cliOpts.graphConfig.description) + .addOptionalParam('l2GraphConfig', cliOpts.graphConfig.description) .addOptionalParam('addressBook', cliOpts.addressBook.description) .setAction(async (args, hre: HardhatRuntimeEnvironment) => { const files = new glob.GlobSync(CONFIG_TESTS).found + + // Disable secure accounts, we don't need them for this task + hre.config.graph.disableSecureAccounts = true + setGraphConfig(args, hre) await hre.run(TASK_TEST, { testFiles: files, @@ -62,11 +76,16 @@ task('e2e:config', 'Run deployment configuration e2e tests') }) task('e2e:init', 'Run deployment initialization e2e tests') - .addFlag('disableSecureAccounts', 'Disable secure accounts on GRE') .addOptionalParam('graphConfig', cliOpts.graphConfig.description) + .addOptionalParam('l1GraphConfig', cliOpts.graphConfig.description) + .addOptionalParam('l2GraphConfig', cliOpts.graphConfig.description) .addOptionalParam('addressBook', cliOpts.addressBook.description) .setAction(async (args, hre: HardhatRuntimeEnvironment) => { const files = new glob.GlobSync(INIT_TESTS).found + + // Disable secure accounts, we don't need them for this task + hre.config.graph.disableSecureAccounts = true + setGraphConfig(args, hre) await hre.run(TASK_TEST, { testFiles: files, @@ -76,8 +95,10 @@ task('e2e:init', 'Run deployment initialization e2e tests') task('e2e:scenario', 'Run scenario scripts and e2e tests') .addPositionalParam('scenario', 'Name of the scenario to run') .addFlag('disableSecureAccounts', 'Disable secure accounts on GRE') - .addOptionalParam('graphConfig', cliOpts.graphConfig.description) .addOptionalParam('addressBook', cliOpts.addressBook.description) + .addOptionalParam('graphConfig', cliOpts.graphConfig.description) + .addOptionalParam('l1GraphConfig', cliOpts.graphConfig.description) + .addOptionalParam('l2GraphConfig', cliOpts.graphConfig.description) .addFlag('skipScript', "Don't run scenario script") .setAction(async (args, hre: HardhatRuntimeEnvironment) => { setGraphConfig(args, hre) @@ -92,8 +113,10 @@ task('e2e:scenario', 'Run scenario scripts and e2e tests') if (!args.skipScript) { if (fs.existsSync(script)) { await runScriptWithHardhat(hre.hardhatArguments, script, [ - args.graphConfig, args.addressBook, + args.graphConfig, + args.l1GraphConfig, + args.l2GraphConfig, args.disableSecureAccounts, ]) } else { diff --git a/test/curation/configuration.test.ts b/test/curation/configuration.test.ts index b2424c784..f475d8f30 100644 --- a/test/curation/configuration.test.ts +++ b/test/curation/configuration.test.ts @@ -55,7 +55,7 @@ describe('Curation:Config', () => { it('reject set `defaultReserveRatio` if not allowed', async function () { const tx = curation.connect(me.signer).setDefaultReserveRatio(defaults.curation.reserveRatio) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) @@ -79,7 +79,7 @@ describe('Curation:Config', () => { const tx = curation .connect(me.signer) .setMinimumCurationDeposit(defaults.curation.minimumCurationDeposit) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) @@ -99,7 +99,7 @@ describe('Curation:Config', () => { it('reject set `curationTaxPercentage` if not allowed', async function () { const tx = curation.connect(me.signer).setCurationTaxPercentage(0) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) @@ -124,7 +124,7 @@ describe('Curation:Config', () => { it('reject set `curationTokenMaster` if not allowed', async function () { const newCurationTokenMaster = curation.address const tx = curation.connect(me.signer).setCurationTokenMaster(newCurationTokenMaster) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) }) diff --git a/test/disputes/configuration.test.ts b/test/disputes/configuration.test.ts index 28bc92ed5..c663c31d7 100644 --- a/test/disputes/configuration.test.ts +++ b/test/disputes/configuration.test.ts @@ -49,7 +49,7 @@ describe('DisputeManager:Config', () => { it('reject set `arbitrator` if not allowed', async function () { const tx = disputeManager.connect(me.signer).setArbitrator(arbitrator.address) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('reject set `arbitrator` to address zero', async function () { @@ -74,7 +74,7 @@ describe('DisputeManager:Config', () => { it('reject set `minimumDeposit` if not allowed', async function () { const newValue = toBN('1') const tx = disputeManager.connect(me.signer).setMinimumDeposit(newValue) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) @@ -97,7 +97,7 @@ describe('DisputeManager:Config', () => { it('reject set `fishermanRewardPercentage` if not allowed', async function () { const tx = disputeManager.connect(me.signer).setFishermanRewardPercentage(50) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) @@ -127,7 +127,7 @@ describe('DisputeManager:Config', () => { it('reject set `slashingPercentage` if not allowed', async function () { const tx = disputeManager.connect(me.signer).setSlashingPercentage(50, 50) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) }) diff --git a/test/epochs.test.ts b/test/epochs.test.ts index 2ff87084a..1e807c053 100644 --- a/test/epochs.test.ts +++ b/test/epochs.test.ts @@ -12,6 +12,7 @@ import { getAccounts, toBN, Account, + initNetwork, } from './lib/testHelpers' describe('EpochManager', () => { @@ -23,6 +24,7 @@ describe('EpochManager', () => { const epochLength: BigNumber = toBN('3') before(async function () { + await initNetwork() ;[me, governor] = await getAccounts() }) diff --git a/test/gateway/bridgeEscrow.test.ts b/test/gateway/bridgeEscrow.test.ts new file mode 100644 index 000000000..fabe9de27 --- /dev/null +++ b/test/gateway/bridgeEscrow.test.ts @@ -0,0 +1,77 @@ +import { expect } from 'chai' +import { BigNumber } from 'ethers' + +import { GraphToken } from '../../build/types/GraphToken' +import { BridgeEscrow } from '../../build/types/BridgeEscrow' + +import { NetworkFixture } from '../lib/fixtures' + +import { getAccounts, toGRT, Account } from '../lib/testHelpers' + +describe('BridgeEscrow', () => { + let governor: Account + let tokenReceiver: Account + let spender: Account + + let fixture: NetworkFixture + + let grt: GraphToken + let bridgeEscrow: BridgeEscrow + + const nTokens = toGRT('1000') + + before(async function () { + ;[governor, tokenReceiver, spender] = await getAccounts() + + fixture = new NetworkFixture() + ;({ grt, bridgeEscrow } = await fixture.load(governor.signer)) + + // Give some funds to the Escrow + await grt.connect(governor.signer).mint(bridgeEscrow.address, nTokens) + }) + + beforeEach(async function () { + await fixture.setUp() + }) + + afterEach(async function () { + await fixture.tearDown() + }) + + describe('approveAll', function () { + it('cannot be called by someone other than the governor', async function () { + const tx = bridgeEscrow.connect(tokenReceiver.signer).approveAll(spender.address) + await expect(tx).revertedWith('Only Controller governor') + }) + it('allows a spender to transfer GRT held by the contract', async function () { + expect(await grt.allowance(bridgeEscrow.address, spender.address)).eq(0) + const tx = grt + .connect(spender.signer) + .transferFrom(bridgeEscrow.address, tokenReceiver.address, nTokens) + await expect(tx).revertedWith('ERC20: transfer amount exceeds allowance') + await bridgeEscrow.connect(governor.signer).approveAll(spender.address) + await expect( + grt + .connect(spender.signer) + .transferFrom(bridgeEscrow.address, tokenReceiver.address, nTokens), + ).to.emit(grt, 'Transfer') + expect(await grt.balanceOf(tokenReceiver.address)).to.eq(nTokens) + }) + }) + + describe('revokeAll', function () { + it('cannot be called by someone other than the governor', async function () { + const tx = bridgeEscrow.connect(tokenReceiver.signer).revokeAll(spender.address) + await expect(tx).revertedWith('Only Controller governor') + }) + it("revokes a spender's permission to transfer GRT held by the contract", async function () { + await bridgeEscrow.connect(governor.signer).approveAll(spender.address) + await bridgeEscrow.connect(governor.signer).revokeAll(spender.address) + // We shouldn't be able to transfer _anything_ + const tx = grt + .connect(spender.signer) + .transferFrom(bridgeEscrow.address, tokenReceiver.address, BigNumber.from('1')) + await expect(tx).revertedWith('ERC20: transfer amount exceeds allowance') + }) + }) +}) diff --git a/test/gateway/l1GraphTokenGateway.test.ts b/test/gateway/l1GraphTokenGateway.test.ts new file mode 100644 index 000000000..76b00ee6b --- /dev/null +++ b/test/gateway/l1GraphTokenGateway.test.ts @@ -0,0 +1,688 @@ +import { expect } from 'chai' +import { constants, Signer, utils } from 'ethers' + +import { GraphToken } from '../../build/types/GraphToken' +import { BridgeMock } from '../../build/types/BridgeMock' +import { InboxMock } from '../../build/types/InboxMock' +import { OutboxMock } from '../../build/types/OutboxMock' +import { L1GraphTokenGateway } from '../../build/types/L1GraphTokenGateway' + +import { NetworkFixture, ArbitrumL1Mocks, L1FixtureContracts } from '../lib/fixtures' + +import { + getAccounts, + latestBlock, + toBN, + toGRT, + Account, + applyL1ToL2Alias, + provider, +} from '../lib/testHelpers' +import { BridgeEscrow } from '../../build/types/BridgeEscrow' + +const { AddressZero } = constants + +describe('L1GraphTokenGateway', () => { + let governor: Account + let tokenSender: Account + let l2Receiver: Account + let mockRouter: Account + let mockL2GRT: Account + let mockL2Gateway: Account + let pauseGuardian: Account + let fixture: NetworkFixture + + let grt: GraphToken + let l1GraphTokenGateway: L1GraphTokenGateway + let bridgeEscrow: BridgeEscrow + let bridgeMock: BridgeMock + let inboxMock: InboxMock + let outboxMock: OutboxMock + + let arbitrumMocks: ArbitrumL1Mocks + let fixtureContracts: L1FixtureContracts + + const senderTokens = toGRT('1000') + const maxGas = toBN('1000000') + const maxSubmissionCost = toBN('7') + const gasPriceBid = toBN('2') + const defaultEthValue = maxSubmissionCost.add(maxGas.mul(gasPriceBid)) + const emptyCallHookData = '0x' + const defaultData = utils.defaultAbiCoder.encode( + ['uint256', 'bytes'], + [maxSubmissionCost, emptyCallHookData], + ) + const defaultDataNoSubmissionCost = utils.defaultAbiCoder.encode( + ['uint256', 'bytes'], + [toBN(0), emptyCallHookData], + ) + const notEmptyCallHookData = '0x12' + const defaultDataWithNotEmptyCallHookData = utils.defaultAbiCoder.encode( + ['uint256', 'bytes'], + [maxSubmissionCost, notEmptyCallHookData], + ) + + before(async function () { + ;[governor, tokenSender, l2Receiver, mockRouter, mockL2GRT, mockL2Gateway, pauseGuardian] = + await getAccounts() + + // Dummy code on the mock router so that it appears as a contract + await provider().send('hardhat_setCode', [mockRouter.address, '0x1234']) + fixture = new NetworkFixture() + fixtureContracts = await fixture.load(governor.signer) + ;({ grt, l1GraphTokenGateway, bridgeEscrow } = fixtureContracts) + + // Give some funds to the token sender + await grt.connect(governor.signer).mint(tokenSender.address, senderTokens) + // Deploy contracts that mock Arbitrum's bridge contracts + arbitrumMocks = await fixture.loadArbitrumL1Mocks(governor.signer) + ;({ bridgeMock, inboxMock, outboxMock } = arbitrumMocks) + }) + + beforeEach(async function () { + await fixture.setUp() + }) + + afterEach(async function () { + await fixture.tearDown() + }) + + context('> immediately after deploy', function () { + describe('calculateL2TokenAddress', function () { + it('should return address zero as it was not set', async function () { + expect(await l1GraphTokenGateway.calculateL2TokenAddress(grt.address)).eq(AddressZero) + }) + }) + + describe('outboundTransfer', function () { + it('reverts because it is paused', async function () { + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .outboundTransfer( + grt.address, + l2Receiver.address, + toGRT('10'), + maxGas, + gasPriceBid, + defaultData, + { + value: defaultEthValue, + }, + ) + await expect(tx).revertedWith('Paused (contract)') + }) + }) + + describe('finalizeInboundTransfer', function () { + it('revert because it is paused', async function () { + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .finalizeInboundTransfer( + grt.address, + l2Receiver.address, + tokenSender.address, + toGRT('10'), + defaultData, + ) + await expect(tx).revertedWith('Paused (contract)') + }) + }) + + describe('setArbitrumAddresses', function () { + it('is not callable by addreses that are not the governor', async function () { + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .setArbitrumAddresses(inboxMock.address, mockRouter.address) + await expect(tx).revertedWith('Only Controller governor') + }) + it('rejects setting an EOA as router or inbox', async function () { + let tx = l1GraphTokenGateway + .connect(governor.signer) + .setArbitrumAddresses(tokenSender.address, mockRouter.address) + await expect(tx).revertedWith('INBOX_MUST_BE_CONTRACT') + tx = l1GraphTokenGateway + .connect(governor.signer) + .setArbitrumAddresses(inboxMock.address, tokenSender.address) + await expect(tx).revertedWith('ROUTER_MUST_BE_CONTRACT') + }) + it('sets inbox and router address', async function () { + const tx = l1GraphTokenGateway + .connect(governor.signer) + .setArbitrumAddresses(inboxMock.address, mockRouter.address) + await expect(tx) + .emit(l1GraphTokenGateway, 'ArbitrumAddressesSet') + .withArgs(inboxMock.address, mockRouter.address) + expect(await l1GraphTokenGateway.l1Router()).eq(mockRouter.address) + expect(await l1GraphTokenGateway.inbox()).eq(inboxMock.address) + }) + }) + + describe('setL2TokenAddress', function () { + it('is not callable by addreses that are not the governor', async function () { + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .setL2TokenAddress(mockL2GRT.address) + await expect(tx).revertedWith('Only Controller governor') + }) + it('sets l2GRT', async function () { + const tx = l1GraphTokenGateway.connect(governor.signer).setL2TokenAddress(mockL2GRT.address) + await expect(tx).emit(l1GraphTokenGateway, 'L2TokenAddressSet').withArgs(mockL2GRT.address) + expect(await l1GraphTokenGateway.l2GRT()).eq(mockL2GRT.address) + }) + }) + + describe('setL2CounterpartAddress', function () { + it('is not callable by addreses that are not the governor', async function () { + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .setL2CounterpartAddress(mockL2Gateway.address) + await expect(tx).revertedWith('Only Controller governor') + }) + it('sets l2Counterpart which can be queried with counterpartGateway()', async function () { + const tx = l1GraphTokenGateway + .connect(governor.signer) + .setL2CounterpartAddress(mockL2Gateway.address) + await expect(tx) + .emit(l1GraphTokenGateway, 'L2CounterpartAddressSet') + .withArgs(mockL2Gateway.address) + expect(await l1GraphTokenGateway.l2Counterpart()).eq(mockL2Gateway.address) + expect(await l1GraphTokenGateway.counterpartGateway()).eq(mockL2Gateway.address) + }) + }) + describe('setEscrowAddress', function () { + it('is not callable by addreses that are not the governor', async function () { + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .setEscrowAddress(bridgeEscrow.address) + await expect(tx).revertedWith('Only Controller governor') + }) + it('sets escrow', async function () { + const tx = l1GraphTokenGateway + .connect(governor.signer) + .setEscrowAddress(bridgeEscrow.address) + await expect(tx) + .emit(l1GraphTokenGateway, 'EscrowAddressSet') + .withArgs(bridgeEscrow.address) + expect(await l1GraphTokenGateway.escrow()).eq(bridgeEscrow.address) + }) + }) + describe('addToCallhookAllowlist', function () { + it('is not callable by addreses that are not the governor', async function () { + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .addToCallhookAllowlist(fixtureContracts.rewardsManager.address) + await expect(tx).revertedWith('Only Controller governor') + expect( + await l1GraphTokenGateway.callhookAllowlist(fixtureContracts.rewardsManager.address), + ).eq(false) + }) + it('rejects adding an EOA to the callhook allowlist', async function () { + const tx = l1GraphTokenGateway + .connect(governor.signer) + .addToCallhookAllowlist(tokenSender.address) + await expect(tx).revertedWith('MUST_BE_CONTRACT') + }) + it('adds an address to the callhook allowlist', async function () { + const tx = l1GraphTokenGateway + .connect(governor.signer) + .addToCallhookAllowlist(fixtureContracts.rewardsManager.address) + await expect(tx) + .emit(l1GraphTokenGateway, 'AddedToCallhookAllowlist') + .withArgs(fixtureContracts.rewardsManager.address) + expect( + await l1GraphTokenGateway.callhookAllowlist(fixtureContracts.rewardsManager.address), + ).eq(true) + }) + }) + describe('removeFromCallhookAllowlist', function () { + it('is not callable by addreses that are not the governor', async function () { + await l1GraphTokenGateway + .connect(governor.signer) + .addToCallhookAllowlist(fixtureContracts.rewardsManager.address) + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .removeFromCallhookAllowlist(fixtureContracts.rewardsManager.address) + await expect(tx).revertedWith('Only Controller governor') + expect( + await l1GraphTokenGateway.callhookAllowlist(fixtureContracts.rewardsManager.address), + ).eq(true) + }) + it('removes an address from the callhook allowlist', async function () { + await l1GraphTokenGateway + .connect(governor.signer) + .addToCallhookAllowlist(fixtureContracts.rewardsManager.address) + const tx = l1GraphTokenGateway + .connect(governor.signer) + .removeFromCallhookAllowlist(fixtureContracts.rewardsManager.address) + await expect(tx) + .emit(l1GraphTokenGateway, 'RemovedFromCallhookAllowlist') + .withArgs(fixtureContracts.rewardsManager.address) + expect( + await l1GraphTokenGateway.callhookAllowlist(fixtureContracts.rewardsManager.address), + ).eq(false) + }) + }) + describe('Pausable behavior', () => { + it('cannot be paused or unpaused by someone other than governor or pauseGuardian', async () => { + let tx = l1GraphTokenGateway.connect(tokenSender.signer).setPaused(false) + await expect(tx).revertedWith('Only Governor or Guardian') + tx = l1GraphTokenGateway.connect(tokenSender.signer).setPaused(true) + await expect(tx).revertedWith('Only Governor or Guardian') + }) + it('cannot be unpaused if some state variables are not set', async function () { + let tx = l1GraphTokenGateway.connect(governor.signer).setPaused(false) + await expect(tx).revertedWith('INBOX_NOT_SET') + await l1GraphTokenGateway + .connect(governor.signer) + .setArbitrumAddresses(arbitrumMocks.inboxMock.address, mockRouter.address) + tx = l1GraphTokenGateway.connect(governor.signer).setPaused(false) + await expect(tx).revertedWith('L2_COUNTERPART_NOT_SET') + await l1GraphTokenGateway + .connect(governor.signer) + .setL2CounterpartAddress(mockL2Gateway.address) + tx = l1GraphTokenGateway.connect(governor.signer).setPaused(false) + await expect(tx).revertedWith('ESCROW_NOT_SET') + }) + it('can be paused and unpaused by the governor', async function () { + await fixture.configureL1Bridge( + governor.signer, + arbitrumMocks, + fixtureContracts, + mockRouter.address, + mockL2GRT.address, + mockL2Gateway.address, + ) + let tx = l1GraphTokenGateway.connect(governor.signer).setPaused(true) + await expect(tx).emit(l1GraphTokenGateway, 'PauseChanged').withArgs(true) + await expect(await l1GraphTokenGateway.paused()).eq(true) + tx = l1GraphTokenGateway.connect(governor.signer).setPaused(false) + await expect(tx).emit(l1GraphTokenGateway, 'PauseChanged').withArgs(false) + await expect(await l1GraphTokenGateway.paused()).eq(false) + }) + describe('setPauseGuardian', function () { + it('cannot be called by someone other than governor', async function () { + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .setPauseGuardian(pauseGuardian.address) + await expect(tx).revertedWith('Only Controller governor') + }) + it('sets a new pause guardian', async function () { + const tx = l1GraphTokenGateway + .connect(governor.signer) + .setPauseGuardian(pauseGuardian.address) + await expect(tx) + .emit(l1GraphTokenGateway, 'NewPauseGuardian') + .withArgs(AddressZero, pauseGuardian.address) + }) + it('allows a pause guardian to pause and unpause', async function () { + await fixture.configureL1Bridge( + governor.signer, + arbitrumMocks, + fixtureContracts, + mockRouter.address, + mockL2GRT.address, + mockL2Gateway.address, + ) + await l1GraphTokenGateway.connect(governor.signer).setPauseGuardian(pauseGuardian.address) + let tx = l1GraphTokenGateway.connect(pauseGuardian.signer).setPaused(true) + await expect(tx).emit(l1GraphTokenGateway, 'PauseChanged').withArgs(true) + await expect(await l1GraphTokenGateway.paused()).eq(true) + tx = l1GraphTokenGateway.connect(pauseGuardian.signer).setPaused(false) + await expect(tx).emit(l1GraphTokenGateway, 'PauseChanged').withArgs(false) + await expect(await l1GraphTokenGateway.paused()).eq(false) + }) + }) + }) + }) + + context('> after configuring and unpausing', function () { + const createMsgData = function (callHookData: string) { + const selector = l1GraphTokenGateway.interface.getSighash('finalizeInboundTransfer') + const params = utils.defaultAbiCoder.encode( + ['address', 'address', 'address', 'uint256', 'bytes'], + [grt.address, tokenSender.address, l2Receiver.address, toGRT('10'), callHookData], + ) + const outboundData = utils.hexlify(utils.concat([selector, params])) + + const msgData = utils.solidityPack( + [ + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'uint256', + 'bytes', + ], + [ + toBN(mockL2Gateway.address), + toBN('0'), + defaultEthValue, + maxSubmissionCost, + applyL1ToL2Alias(tokenSender.address), + applyL1ToL2Alias(tokenSender.address), + maxGas, + gasPriceBid, + utils.hexDataLength(outboundData), + outboundData, + ], + ) + return msgData + } + const createInboxAccsEntry = function (msgDataHash: string) { + // The real bridge would emit the InboxAccs entry that came before this one, but our mock + // emits this, making it easier for us to validate here that all the parameters we sent are correct + const expectedInboxAccsEntry = utils.keccak256( + utils.solidityPack( + ['address', 'uint8', 'address', 'bytes32'], + [inboxMock.address, 9, l1GraphTokenGateway.address, msgDataHash], + ), + ) + return expectedInboxAccsEntry + } + const testValidOutboundTransfer = async function ( + signer: Signer, + data: string, + callHookData: string, + ) { + const tx = l1GraphTokenGateway + .connect(signer) + .outboundTransfer(grt.address, l2Receiver.address, toGRT('10'), maxGas, gasPriceBid, data, { + value: defaultEthValue, + }) + // Our bridge mock returns an incrementing seqNum starting at 1 + const expectedSeqNum = 1 + await expect(tx) + .emit(l1GraphTokenGateway, 'DepositInitiated') + .withArgs(grt.address, tokenSender.address, l2Receiver.address, expectedSeqNum, toGRT('10')) + + const msgData = createMsgData(callHookData) + const msgDataHash = utils.keccak256(msgData) + const expectedInboxAccsEntry = createInboxAccsEntry(msgDataHash) + + await expect(tx).emit(inboxMock, 'InboxMessageDelivered').withArgs(1, msgData) + await expect(tx) + .emit(bridgeMock, 'MessageDelivered') + .withArgs( + expectedSeqNum, + expectedInboxAccsEntry, + inboxMock.address, + 9, + l1GraphTokenGateway.address, + msgDataHash, + ) + const escrowBalance = await grt.balanceOf(bridgeEscrow.address) + const senderBalance = await grt.balanceOf(tokenSender.address) + await expect(escrowBalance).eq(toGRT('10')) + await expect(senderBalance).eq(toGRT('990')) + } + before(async function () { + await fixture.configureL1Bridge( + governor.signer, + arbitrumMocks, + fixtureContracts, + mockRouter.address, + mockL2GRT.address, + mockL2Gateway.address, + ) + }) + + describe('calculateL2TokenAddress', function () { + it('returns the L2 token address', async function () { + expect(await l1GraphTokenGateway.calculateL2TokenAddress(grt.address)).eq(mockL2GRT.address) + }) + it('returns the zero address if the input is any other address', async function () { + expect(await l1GraphTokenGateway.calculateL2TokenAddress(tokenSender.address)).eq( + AddressZero, + ) + }) + }) + + describe('outboundTransfer', function () { + it('reverts when called with the wrong token address', async function () { + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .outboundTransfer( + tokenSender.address, + l2Receiver.address, + toGRT('10'), + maxGas, + gasPriceBid, + defaultData, + { + value: defaultEthValue, + }, + ) + await expect(tx).revertedWith('TOKEN_NOT_GRT') + }) + it('puts tokens in escrow and creates a retryable ticket', async function () { + await grt.connect(tokenSender.signer).approve(l1GraphTokenGateway.address, toGRT('10')) + await testValidOutboundTransfer(tokenSender.signer, defaultData, emptyCallHookData) + }) + it('decodes the sender address from messages sent by the router', async function () { + await grt.connect(tokenSender.signer).approve(l1GraphTokenGateway.address, toGRT('10')) + const routerEncodedData = utils.defaultAbiCoder.encode( + ['address', 'bytes'], + [tokenSender.address, defaultData], + ) + await testValidOutboundTransfer(mockRouter.signer, routerEncodedData, emptyCallHookData) + }) + it('reverts when called with no submission cost', async function () { + await grt.connect(tokenSender.signer).approve(l1GraphTokenGateway.address, toGRT('10')) + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .outboundTransfer( + grt.address, + l2Receiver.address, + toGRT('10'), + maxGas, + gasPriceBid, + defaultDataNoSubmissionCost, + { + value: defaultEthValue, + }, + ) + await expect(tx).revertedWith('NO_SUBMISSION_COST') + }) + it('reverts when called with nonempty calldata, if the sender is not allowlisted', async function () { + await grt.connect(tokenSender.signer).approve(l1GraphTokenGateway.address, toGRT('10')) + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .outboundTransfer( + grt.address, + l2Receiver.address, + toGRT('10'), + maxGas, + gasPriceBid, + defaultDataWithNotEmptyCallHookData, + { + value: defaultEthValue, + }, + ) + await expect(tx).revertedWith('CALL_HOOK_DATA_NOT_ALLOWED') + }) + it('allows sending nonempty calldata, if the sender is allowlisted', async function () { + // Make the sender a contract so that it can be allowed to send callhooks + await provider().send('hardhat_setCode', [tokenSender.address, '0x1234']) + await l1GraphTokenGateway + .connect(governor.signer) + .addToCallhookAllowlist(tokenSender.address) + await grt.connect(tokenSender.signer).approve(l1GraphTokenGateway.address, toGRT('10')) + await testValidOutboundTransfer( + tokenSender.signer, + defaultDataWithNotEmptyCallHookData, + notEmptyCallHookData, + ) + }) + it('reverts when the sender does not have enough GRT', async function () { + await grt.connect(tokenSender.signer).approve(l1GraphTokenGateway.address, toGRT('1001')) + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .outboundTransfer( + grt.address, + l2Receiver.address, + toGRT('1001'), + maxGas, + gasPriceBid, + defaultData, + { + value: defaultEthValue, + }, + ) + await expect(tx).revertedWith('ERC20: transfer amount exceeds balance') + }) + }) + + describe('finalizeInboundTransfer', function () { + it('reverts when called by an account that is not the bridge', async function () { + const tx = l1GraphTokenGateway + .connect(tokenSender.signer) + .finalizeInboundTransfer( + grt.address, + l2Receiver.address, + tokenSender.address, + toGRT('10'), + defaultData, + ) + await expect(tx).revertedWith('NOT_FROM_BRIDGE') + }) + it('reverts when called by the bridge, but the tx was not started by the L2 gateway', async function () { + const encodedCalldata = l1GraphTokenGateway.interface.encodeFunctionData( + 'finalizeInboundTransfer', + [ + grt.address, + l2Receiver.address, + tokenSender.address, + toGRT('10'), + utils.defaultAbiCoder.encode(['uint256', 'bytes'], [0, []]), + ], + ) + // The real outbox would require a proof, which would + // validate that the tx was initiated by the L2 gateway but our mock + // just executes unconditionally + const tx = outboxMock.connect(tokenSender.signer).executeTransaction( + toBN('0'), + [], + toBN('0'), + l2Receiver.address, // Note this is not mockL2Gateway + l1GraphTokenGateway.address, + toBN('1337'), + await latestBlock(), + toBN('133701337'), + toBN('0'), + encodedCalldata, + ) + await expect(tx).revertedWith('ONLY_COUNTERPART_GATEWAY') + }) + it('reverts if the gateway does not have tokens', async function () { + // This scenario should never really happen, but we still + // test that the gateway reverts in this case + const encodedCalldata = l1GraphTokenGateway.interface.encodeFunctionData( + 'finalizeInboundTransfer', + [ + grt.address, + l2Receiver.address, + tokenSender.address, + toGRT('10'), + utils.defaultAbiCoder.encode(['uint256', 'bytes'], [0, []]), + ], + ) + // The real outbox would require a proof, which would + // validate that the tx was initiated by the L2 gateway but our mock + // just executes unconditionally + const tx = outboxMock + .connect(tokenSender.signer) + .executeTransaction( + toBN('0'), + [], + toBN('0'), + mockL2Gateway.address, + l1GraphTokenGateway.address, + toBN('1337'), + await latestBlock(), + toBN('133701337'), + toBN('0'), + encodedCalldata, + ) + await expect(tx).revertedWith('BRIDGE_OUT_OF_FUNDS') + }) + it('reverts if the gateway is revoked from escrow', async function () { + await grt.connect(tokenSender.signer).approve(l1GraphTokenGateway.address, toGRT('10')) + await testValidOutboundTransfer(tokenSender.signer, defaultData, emptyCallHookData) + // At this point, the gateway holds 10 GRT in escrow + // But we revoke the gateway's permission to move the funds: + await bridgeEscrow.connect(governor.signer).revokeAll(l1GraphTokenGateway.address) + const encodedCalldata = l1GraphTokenGateway.interface.encodeFunctionData( + 'finalizeInboundTransfer', + [ + grt.address, + l2Receiver.address, + tokenSender.address, + toGRT('8'), + utils.defaultAbiCoder.encode(['uint256', 'bytes'], [0, []]), + ], + ) + // The real outbox would require a proof, which would + // validate that the tx was initiated by the L2 gateway but our mock + // just executes unconditionally + const tx = outboxMock + .connect(tokenSender.signer) + .executeTransaction( + toBN('0'), + [], + toBN('0'), + mockL2Gateway.address, + l1GraphTokenGateway.address, + toBN('1337'), + await latestBlock(), + toBN('133701337'), + toBN('0'), + encodedCalldata, + ) + await expect(tx).revertedWith('ERC20: transfer amount exceeds allowance') + }) + it('sends tokens out of escrow', async function () { + await grt.connect(tokenSender.signer).approve(l1GraphTokenGateway.address, toGRT('10')) + await testValidOutboundTransfer(tokenSender.signer, defaultData, emptyCallHookData) + // At this point, the gateway holds 10 GRT in escrow + const encodedCalldata = l1GraphTokenGateway.interface.encodeFunctionData( + 'finalizeInboundTransfer', + [ + grt.address, + l2Receiver.address, + tokenSender.address, + toGRT('8'), + utils.defaultAbiCoder.encode(['uint256', 'bytes'], [0, []]), + ], + ) + // The real outbox would require a proof, which would + // validate that the tx was initiated by the L2 gateway but our mock + // just executes unconditionally + const tx = outboxMock + .connect(tokenSender.signer) + .executeTransaction( + toBN('0'), + [], + toBN('0'), + mockL2Gateway.address, + l1GraphTokenGateway.address, + toBN('1337'), + await latestBlock(), + toBN('133701337'), + toBN('0'), + encodedCalldata, + ) + await expect(tx) + .emit(l1GraphTokenGateway, 'WithdrawalFinalized') + .withArgs(grt.address, l2Receiver.address, tokenSender.address, toBN('0'), toGRT('8')) + const escrowBalance = await grt.balanceOf(bridgeEscrow.address) + const senderBalance = await grt.balanceOf(tokenSender.address) + await expect(escrowBalance).eq(toGRT('2')) + await expect(senderBalance).eq(toGRT('998')) + }) + }) + }) +}) diff --git a/test/gns.test.ts b/test/gns.test.ts index 298d95ab0..1ee46002a 100644 --- a/test/gns.test.ts +++ b/test/gns.test.ts @@ -546,7 +546,7 @@ describe('GNS', () => { it('reject set `ownerTaxPercentage` if not allowed', async function () { const tx = gns.connect(me.signer).setOwnerTaxPercentage(newValue) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) @@ -1034,7 +1034,7 @@ describe('GNS', () => { // Batch send transaction const tx = gns.connect(me.signer).multicall([tx1.data, tx2.data]) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('should revert if batching a call to initialize', async function () { @@ -1050,7 +1050,7 @@ describe('GNS', () => { // Batch send transaction const tx = gns.connect(me.signer).multicall([tx1.data, tx2.data]) - await expect(tx).revertedWith('Caller must be the implementation') + await expect(tx).revertedWith('Only implementation') }) it('should revert if trying to call a private function', async function () { diff --git a/test/graphToken.test.ts b/test/graphToken.test.ts index 5f53e6866..b42274ac4 100644 --- a/test/graphToken.test.ts +++ b/test/graphToken.test.ts @@ -1,275 +1,5 @@ -import { expect } from 'chai' -import { constants, utils, BytesLike, BigNumber, Signature } from 'ethers' -import { eip712 } from '@graphprotocol/common-ts/dist/attestations' - -import { GraphToken } from '../build/types/GraphToken' - -import * as deployment from './lib/deployment' -import { getAccounts, getChainID, toBN, toGRT, Account } from './lib/testHelpers' - -const { AddressZero, MaxUint256 } = constants -const { keccak256, SigningKey } = utils - -const PERMIT_TYPE_HASH = eip712.typeHash( - 'Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)', -) -const SALT = '0x51f3d585afe6dfeb2af01bba0889a36c1db03beec88c6a4d0c53817069026afa' - -interface Permit { - owner: string - spender: string - value: BigNumber - nonce: BigNumber - deadline: BigNumber -} - -function hashEncodePermit(permit: Permit) { - return eip712.hashStruct( - PERMIT_TYPE_HASH, - ['address', 'address', 'uint256', 'uint256', 'uint256'], - [permit.owner, permit.spender, permit.value, permit.nonce, permit.deadline], - ) -} - -function signPermit( - signer: BytesLike, - chainId: number, - contractAddress: string, - permit: Permit, -): Signature { - const domainSeparator = eip712.domainSeparator({ - name: 'Graph Token', - version: '0', - chainId, - verifyingContract: contractAddress, - salt: SALT, - }) - const hashEncodedPermit = hashEncodePermit(permit) - const message = eip712.encode(domainSeparator, hashEncodedPermit) - const messageHash = keccak256(message) - const signingKey = new SigningKey(signer) - return signingKey.signDigest(messageHash) -} +import { grtTests } from './lib/graphTokenTests' describe('GraphToken', () => { - let me: Account - let other: Account - let governor: Account - - const mePrivateKey = '0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d' - const otherPrivateKey = '0x6cbed15c793ce57650b9877cf6fa156fbef513c4e6134f022a85b1ffdd59b2a1' - - let grt: GraphToken - - async function permitMaxOK(): Promise { - return permitOK(MaxUint256) - } - - async function permitOK(value: BigNumber): Promise { - const nonce = await grt.nonces(me.address) - return { - owner: me.address, - spender: other.address, - value: value, - nonce: nonce, - deadline: toBN('0'), - } - } - - async function permitExpired(): Promise { - const permit = await permitMaxOK() - permit.deadline = toBN('1') - return permit - } - - async function permitDeny(): Promise { - const permit = await permitMaxOK() - permit.value = toBN('0') - return permit - } - - async function createPermitTransaction(permit: Permit, signer: string) { - const chainID = await getChainID() - const signature: Signature = signPermit(signer, chainID, grt.address, permit) - return grt.permit( - permit.owner, - permit.spender, - permit.value, - permit.deadline, - signature.v, - signature.r, - signature.s, - ) - } - - before(async function () { - ;[me, other, governor] = await getAccounts() - }) - - beforeEach(async function () { - // Deploy graph token - grt = await deployment.deployGRT(governor.signer) - - // Mint some tokens - const tokens = toGRT('10000') - await grt.connect(governor.signer).mint(me.address, tokens) - }) - - describe('permit', function () { - it('should permit max token allowance', async function () { - // Allow to transfer tokens - const tokensToApprove = toGRT('1000') - const permit = await permitOK(tokensToApprove) - const tx = createPermitTransaction(permit, mePrivateKey) - await expect(tx).emit(grt, 'Approval').withArgs(permit.owner, permit.spender, tokensToApprove) - - // Allowance updated - const allowance = await grt.allowance(me.address, other.address) - expect(allowance).eq(tokensToApprove) - - // Transfer tokens should work - const tokens = toGRT('100') - await grt.connect(other.signer).transferFrom(me.address, other.address, tokens) - }) - - it('should permit max token allowance', async function () { - // Allow to transfer tokens - const permit = await permitMaxOK() - const tx = createPermitTransaction(permit, mePrivateKey) - await expect(tx).emit(grt, 'Approval').withArgs(permit.owner, permit.spender, MaxUint256) - - // Allowance updated - const allowance = await grt.allowance(me.address, other.address) - expect(allowance).eq(MaxUint256) - - // Transfer tokens should work - const tokens = toGRT('100') - await grt.connect(other.signer).transferFrom(me.address, other.address, tokens) - }) - - it('reject to transfer more tokens than approved by permit', async function () { - // Allow to transfer tokens - const tokensToApprove = toGRT('1000') - const permit = await permitOK(tokensToApprove) - await createPermitTransaction(permit, mePrivateKey) - - // Should not transfer more than approved - const tooManyTokens = toGRT('1001') - const tx = grt.connect(other.signer).transferFrom(me.address, other.address, tooManyTokens) - await expect(tx).revertedWith('ERC20: transfer amount exceeds allowance') - - // Should transfer up to the approved amount - await grt.connect(other.signer).transferFrom(me.address, other.address, tokensToApprove) - }) - - it('reject use two permits with same nonce', async function () { - // Allow to transfer tokens - const permit = await permitMaxOK() - await createPermitTransaction(permit, mePrivateKey) - - // Try to re-use the permit - const tx = createPermitTransaction(permit, mePrivateKey) - await expect(tx).revertedWith('GRT: invalid permit') - }) - - it('reject use expired permit', async function () { - const permit = await permitExpired() - const tx = createPermitTransaction(permit, mePrivateKey) - await expect(tx).revertedWith('GRT: expired permit') - }) - - it('reject permit if holder address does not match', async function () { - const permit = await permitMaxOK() - const tx = createPermitTransaction(permit, otherPrivateKey) - await expect(tx).revertedWith('GRT: invalid permit') - }) - - it('should deny transfer from if permit was denied', async function () { - // Allow to transfer tokens - const permit1 = await permitMaxOK() - await createPermitTransaction(permit1, mePrivateKey) - - // Deny transfer tokens - const permit2 = await permitDeny() - await createPermitTransaction(permit2, mePrivateKey) - - // Allowance updated - const allowance = await grt.allowance(me.address, other.address) - expect(allowance).eq(toBN('0')) - - // Try to transfer without permit should fail - const tokens = toGRT('100') - const tx = grt.connect(other.signer).transferFrom(me.address, other.address, tokens) - await expect(tx).revertedWith('ERC20: transfer amount exceeds allowance') - }) - }) - - describe('mint', function () { - describe('addMinter', function () { - it('reject add a new minter if not allowed', async function () { - const tx = grt.connect(me.signer).addMinter(me.address) - await expect(tx).revertedWith('Only Governor can call') - }) - - it('should add a new minter', async function () { - expect(await grt.isMinter(me.address)).eq(false) - const tx = grt.connect(governor.signer).addMinter(me.address) - await expect(tx).emit(grt, 'MinterAdded').withArgs(me.address) - expect(await grt.isMinter(me.address)).eq(true) - }) - }) - - describe('mint', async function () { - it('reject mint if not minter', async function () { - const tx = grt.connect(me.signer).mint(me.address, toGRT('100')) - await expect(tx).revertedWith('Only minter can call') - }) - }) - - context('> when is minter', function () { - beforeEach(async function () { - await grt.connect(governor.signer).addMinter(me.address) - expect(await grt.isMinter(me.address)).eq(true) - }) - - describe('mint', async function () { - it('should mint', async function () { - const beforeTokens = await grt.balanceOf(me.address) - - const tokensToMint = toGRT('100') - const tx = grt.connect(me.signer).mint(me.address, tokensToMint) - await expect(tx).emit(grt, 'Transfer').withArgs(AddressZero, me.address, tokensToMint) - - const afterTokens = await grt.balanceOf(me.address) - expect(afterTokens).eq(beforeTokens.add(tokensToMint)) - }) - - it('should mint if governor', async function () { - const tokensToMint = toGRT('100') - await grt.connect(governor.signer).mint(me.address, tokensToMint) - }) - }) - - describe('removeMinter', function () { - it('reject remove a minter if not allowed', async function () { - const tx = grt.connect(me.signer).removeMinter(me.address) - await expect(tx).revertedWith('Only Governor can call') - }) - - it('should remove a minter', async function () { - const tx = grt.connect(governor.signer).removeMinter(me.address) - await expect(tx).emit(grt, 'MinterRemoved').withArgs(me.address) - expect(await grt.isMinter(me.address)).eq(false) - }) - }) - - describe('renounceMinter', function () { - it('should renounce to be a minter', async function () { - const tx = grt.connect(me.signer).renounceMinter() - await expect(tx).emit(grt, 'MinterRemoved').withArgs(me.address) - expect(await grt.isMinter(me.address)).eq(false) - }) - }) - }) - }) + grtTests.bind(this)(false) }) diff --git a/test/l2/l2GraphToken.test.ts b/test/l2/l2GraphToken.test.ts new file mode 100644 index 000000000..56d8e5ed6 --- /dev/null +++ b/test/l2/l2GraphToken.test.ts @@ -0,0 +1,107 @@ +import { expect } from 'chai' + +import { getAccounts, toGRT, Account, initNetwork } from '../lib/testHelpers' + +import { L2GraphToken } from '../../build/types/L2GraphToken' + +import { grtTests } from '../lib/graphTokenTests' +import { NetworkFixture } from '../lib/fixtures' + +describe('L2GraphToken', () => { + describe('Base GRT behavior', () => { + grtTests.bind(this)(true) + }) + describe('Extended L2 behavior', () => { + let mockL2Gateway: Account + let mockL1GRT: Account + let governor: Account + let user: Account + + let fixture: NetworkFixture + let grt: L2GraphToken + + before(async function () { + await initNetwork() + ;[mockL1GRT, mockL2Gateway, governor, user] = await getAccounts() + fixture = new NetworkFixture() + ;({ grt } = await fixture.loadL2(governor.signer)) + }) + + beforeEach(async function () { + await fixture.setUp() + }) + + afterEach(async function () { + await fixture.tearDown() + }) + + describe('setGateway', async function () { + it('cannot be called by someone who is not the governor', async function () { + const tx = grt.connect(mockL2Gateway.signer).setGateway(mockL2Gateway.address) + await expect(tx).revertedWith('Only Governor can call') + }) + it('sets the L2 Gateway address when called by the governor', async function () { + const tx = grt.connect(governor.signer).setGateway(mockL2Gateway.address) + await expect(tx).emit(grt, 'GatewaySet').withArgs(mockL2Gateway.address) + await expect(await grt.gateway()).eq(mockL2Gateway.address) + }) + }) + describe('setL1Address', async function () { + it('cannot be called by someone who is not the governor', async function () { + const tx = grt.connect(mockL2Gateway.signer).setL1Address(mockL1GRT.address) + await expect(tx).revertedWith('Only Governor can call') + }) + it('sets the L1 GRT address when called by the governor', async function () { + const tx = grt.connect(governor.signer).setL1Address(mockL1GRT.address) + await expect(tx).emit(grt, 'L1AddressSet').withArgs(mockL1GRT.address) + await expect(await grt.l1Address()).eq(mockL1GRT.address) + }) + }) + describe('bridge minting and burning', async function () { + beforeEach(async function () { + // Configure the l1Address and gateway + await grt.connect(governor.signer).setL1Address(mockL1GRT.address) + await grt.connect(governor.signer).setGateway(mockL2Gateway.address) + }) + describe('bridgeMint', async function () { + it('cannot be called by someone who is not the gateway', async function () { + const tx = grt.connect(governor.signer).bridgeMint(user.address, toGRT('100')) + await expect(tx).revertedWith('NOT_GATEWAY') + }) + it('mints GRT into a destination account', async function () { + const tx = grt.connect(mockL2Gateway.signer).bridgeMint(user.address, toGRT('100')) + await expect(tx).emit(grt, 'BridgeMinted').withArgs(user.address, toGRT('100')) + await expect(await grt.balanceOf(user.address)).eq(toGRT('100')) + }) + }) + describe('bridgeBurn', async function () { + it('cannot be called by someone who is not the gateway', async function () { + const tx = grt.connect(governor.signer).bridgeBurn(user.address, toGRT('100')) + await expect(tx).revertedWith('NOT_GATEWAY') + }) + it('requires approval for burning', async function () { + await grt.connect(mockL2Gateway.signer).bridgeMint(user.address, toGRT('100')) + const tx = grt.connect(mockL2Gateway.signer).bridgeBurn(user.address, toGRT('20')) + await expect(tx).revertedWith('ERC20: burn amount exceeds allowance') + }) + it('fails if the user does not have enough funds', async function () { + await grt.connect(mockL2Gateway.signer).bridgeMint(user.address, toGRT('10')) + await grt.connect(user.signer).approve(mockL2Gateway.address, toGRT('20')) + const tx = grt.connect(mockL2Gateway.signer).bridgeBurn(user.address, toGRT('20')) + await expect(tx).revertedWith('ERC20: burn amount exceeds balance') + }) + it('burns GRT from an account when approved', async function () { + await grt.connect(mockL2Gateway.signer).bridgeMint(user.address, toGRT('100')) + await grt.connect(user.signer).approve(mockL2Gateway.address, toGRT('20')) + const tx = grt.connect(mockL2Gateway.signer).bridgeBurn(user.address, toGRT('20')) + await expect(tx).emit(grt, 'BridgeBurned').withArgs(user.address, toGRT('20')) + await expect(await grt.balanceOf(user.address)).eq(toGRT('80')) + }) + }) + it('does not allow the bridge to mint as a regular minter', async function () { + const tx = grt.connect(mockL2Gateway.signer).mint(user.address, toGRT('100')) + await expect(tx).revertedWith('Only minter can call') + }) + }) + }) +}) diff --git a/test/l2/l2GraphTokenGateway.test.ts b/test/l2/l2GraphTokenGateway.test.ts new file mode 100644 index 000000000..236817afd --- /dev/null +++ b/test/l2/l2GraphTokenGateway.test.ts @@ -0,0 +1,457 @@ +import { expect, use } from 'chai' +import { constants, ContractTransaction, Signer, utils } from 'ethers' + +import { L2GraphToken } from '../../build/types/L2GraphToken' +import { L2GraphTokenGateway } from '../../build/types/L2GraphTokenGateway' +import { CallhookReceiverMock } from '../../build/types/CallhookReceiverMock' + +import { L2FixtureContracts, NetworkFixture } from '../lib/fixtures' + +import { FakeContract, smock } from '@defi-wonderland/smock' + +use(smock.matchers) + +import { getAccounts, toGRT, Account, toBN, getL2SignerFromL1 } from '../lib/testHelpers' +import { Interface } from 'ethers/lib/utils' +import { deployContract } from '../lib/deployment' +import { RewardsManager } from '../../build/types/RewardsManager' + +const { AddressZero } = constants + +describe('L2GraphTokenGateway', () => { + let me: Account + let governor: Account + let tokenSender: Account + let l1Receiver: Account + let l2Receiver: Account + let mockRouter: Account + let mockL1GRT: Account + let mockL1Gateway: Account + let pauseGuardian: Account + let fixture: NetworkFixture + let arbSysMock: FakeContract + + let fixtureContracts: L2FixtureContracts + let grt: L2GraphToken + let l2GraphTokenGateway: L2GraphTokenGateway + let callhookReceiverMock: CallhookReceiverMock + let rewardsManager: RewardsManager + + const senderTokens = toGRT('1000') + const defaultData = '0x' + const defaultDataWithNotEmptyCallHookData = utils.defaultAbiCoder.encode( + ['uint256', 'uint256'], + [toBN('1337'), toBN('42')], + ) + + before(async function () { + ;[ + me, + governor, + tokenSender, + l1Receiver, + mockRouter, + mockL1GRT, + mockL1Gateway, + l2Receiver, + pauseGuardian, + ] = await getAccounts() + + fixture = new NetworkFixture() + fixtureContracts = await fixture.loadL2(governor.signer) + ;({ grt, l2GraphTokenGateway, rewardsManager } = fixtureContracts) + + callhookReceiverMock = (await deployContract( + 'CallhookReceiverMock', + governor.signer, + )) as unknown as CallhookReceiverMock + + // Give some funds to the token sender + await grt.connect(governor.signer).mint(tokenSender.address, senderTokens) + }) + + beforeEach(async function () { + await fixture.setUp() + // Thanks to Livepeer: https://github.com/livepeer/arbitrum-lpt-bridge/blob/main/test/unit/L2/l2LPTGateway.test.ts#L86 + arbSysMock = await smock.fake('ArbSys', { + address: '0x0000000000000000000000000000000000000064', + }) + arbSysMock.sendTxToL1.returns(1) + }) + + afterEach(async function () { + await fixture.tearDown() + }) + + context('> immediately after deploy', function () { + describe('calculateL2TokenAddress', function () { + it('should return the zero address', async function () { + expect(await l2GraphTokenGateway.calculateL2TokenAddress(grt.address)).eq(AddressZero) + }) + }) + + describe('outboundTransfer', function () { + it('reverts because it is paused', async function () { + const tx = l2GraphTokenGateway + .connect(tokenSender.signer) + ['outboundTransfer(address,address,uint256,bytes)']( + grt.address, + l1Receiver.address, + toGRT('10'), + defaultData, + ) + await expect(tx).revertedWith('Paused (contract)') + }) + }) + + describe('finalizeInboundTransfer', function () { + it('revert because it is paused', async function () { + const tx = l2GraphTokenGateway + .connect(tokenSender.signer) + .finalizeInboundTransfer( + grt.address, + tokenSender.address, + l1Receiver.address, + toGRT('10'), + defaultData, + ) + await expect(tx).revertedWith('Paused (contract)') + }) + }) + + describe('setL2Router', function () { + it('is not callable by addreses that are not the governor', async function () { + const tx = l2GraphTokenGateway.connect(tokenSender.signer).setL2Router(mockRouter.address) + await expect(tx).revertedWith('Only Controller governor') + }) + it('sets router address', async function () { + const tx = l2GraphTokenGateway.connect(governor.signer).setL2Router(mockRouter.address) + await expect(tx).emit(l2GraphTokenGateway, 'L2RouterSet').withArgs(mockRouter.address) + expect(await l2GraphTokenGateway.l2Router()).eq(mockRouter.address) + }) + }) + + describe('setL1TokenAddress', function () { + it('is not callable by addreses that are not the governor', async function () { + const tx = l2GraphTokenGateway + .connect(tokenSender.signer) + .setL1TokenAddress(mockL1GRT.address) + await expect(tx).revertedWith('Only Controller governor') + }) + it('sets l2GRT', async function () { + const tx = l2GraphTokenGateway.connect(governor.signer).setL1TokenAddress(mockL1GRT.address) + await expect(tx).emit(l2GraphTokenGateway, 'L1TokenAddressSet').withArgs(mockL1GRT.address) + expect(await l2GraphTokenGateway.l1GRT()).eq(mockL1GRT.address) + }) + }) + + describe('setL1CounterpartAddress', function () { + it('is not callable by addreses that are not the governor', async function () { + const tx = l2GraphTokenGateway + .connect(tokenSender.signer) + .setL1CounterpartAddress(mockL1Gateway.address) + await expect(tx).revertedWith('Only Controller governor') + }) + it('sets L1Counterpart', async function () { + const tx = l2GraphTokenGateway + .connect(governor.signer) + .setL1CounterpartAddress(mockL1Gateway.address) + await expect(tx) + .emit(l2GraphTokenGateway, 'L1CounterpartAddressSet') + .withArgs(mockL1Gateway.address) + expect(await l2GraphTokenGateway.l1Counterpart()).eq(mockL1Gateway.address) + }) + }) + describe('Pausable behavior', () => { + it('cannot be paused or unpaused by someone other than governor or pauseGuardian', async () => { + let tx = l2GraphTokenGateway.connect(tokenSender.signer).setPaused(false) + await expect(tx).revertedWith('Only Governor or Guardian') + tx = l2GraphTokenGateway.connect(tokenSender.signer).setPaused(true) + await expect(tx).revertedWith('Only Governor or Guardian') + }) + it('cannot be paused if some state variables are not set', async function () { + let tx = l2GraphTokenGateway.connect(governor.signer).setPaused(false) + await expect(tx).revertedWith('L2_ROUTER_NOT_SET') + await l2GraphTokenGateway.connect(governor.signer).setL2Router(mockRouter.address) + tx = l2GraphTokenGateway.connect(governor.signer).setPaused(false) + await expect(tx).revertedWith('L1_COUNTERPART_NOT_SET') + await l2GraphTokenGateway + .connect(governor.signer) + .setL1CounterpartAddress(mockL1Gateway.address) + tx = l2GraphTokenGateway.connect(governor.signer).setPaused(false) + await expect(tx).revertedWith('L1_GRT_NOT_SET') + }) + it('can be paused and unpaused by the governor', async function () { + await fixture.configureL2Bridge( + governor.signer, + fixtureContracts, + mockRouter.address, + mockL1GRT.address, + mockL1Gateway.address, + ) + let tx = l2GraphTokenGateway.connect(governor.signer).setPaused(true) + await expect(tx).emit(l2GraphTokenGateway, 'PauseChanged').withArgs(true) + await expect(await l2GraphTokenGateway.paused()).eq(true) + tx = l2GraphTokenGateway.connect(governor.signer).setPaused(false) + await expect(tx).emit(l2GraphTokenGateway, 'PauseChanged').withArgs(false) + await expect(await l2GraphTokenGateway.paused()).eq(false) + }) + describe('setPauseGuardian', function () { + it('cannot be called by someone other than governor', async function () { + const tx = l2GraphTokenGateway + .connect(tokenSender.signer) + .setPauseGuardian(pauseGuardian.address) + await expect(tx).revertedWith('Only Controller governor') + }) + it('sets a new pause guardian', async function () { + const tx = l2GraphTokenGateway + .connect(governor.signer) + .setPauseGuardian(pauseGuardian.address) + await expect(tx) + .emit(l2GraphTokenGateway, 'NewPauseGuardian') + .withArgs(AddressZero, pauseGuardian.address) + }) + it('allows a pause guardian to pause and unpause', async function () { + await fixture.configureL2Bridge( + governor.signer, + fixtureContracts, + mockRouter.address, + mockL1GRT.address, + mockL1Gateway.address, + ) + await l2GraphTokenGateway.connect(governor.signer).setPauseGuardian(pauseGuardian.address) + let tx = l2GraphTokenGateway.connect(pauseGuardian.signer).setPaused(true) + await expect(tx).emit(l2GraphTokenGateway, 'PauseChanged').withArgs(true) + await expect(await l2GraphTokenGateway.paused()).eq(true) + tx = l2GraphTokenGateway.connect(pauseGuardian.signer).setPaused(false) + await expect(tx).emit(l2GraphTokenGateway, 'PauseChanged').withArgs(false) + await expect(await l2GraphTokenGateway.paused()).eq(false) + }) + }) + }) + }) + + context('> after configuring and unpausing', function () { + const testValidOutboundTransfer = async function (signer: Signer, data: string) { + const tx = l2GraphTokenGateway + .connect(signer) + ['outboundTransfer(address,address,uint256,bytes)']( + mockL1GRT.address, + l1Receiver.address, + toGRT('10'), + data, + ) + const expectedId = 1 + await expect(tx) + .emit(l2GraphTokenGateway, 'WithdrawalInitiated') + .withArgs( + mockL1GRT.address, + tokenSender.address, + l1Receiver.address, + expectedId, + 0, + toGRT('10'), + ) + + // Should use the L1 Gateway's interface, but both come from ITokenGateway + const calldata = l2GraphTokenGateway.interface.encodeFunctionData('finalizeInboundTransfer', [ + mockL1GRT.address, + tokenSender.address, + l1Receiver.address, + toGRT('10'), + utils.defaultAbiCoder.encode(['uint256', 'bytes'], [0, []]), + ]) + await expect(tx) + .emit(l2GraphTokenGateway, 'TxToL1') + .withArgs(tokenSender.address, mockL1Gateway.address, 1, calldata) + + // For some reason the call count doesn't work properly, + // and each function call is counted 12 times. + // Possibly related to https://github.com/defi-wonderland/smock/issues/85 ? + //expect(arbSysMock.sendTxToL1).to.have.been.calledOnce + expect(arbSysMock.sendTxToL1).to.have.been.calledWith(mockL1Gateway.address, calldata) + const senderBalance = await grt.balanceOf(tokenSender.address) + await expect(senderBalance).eq(toGRT('990')) + } + before(async function () { + await fixture.configureL2Bridge( + governor.signer, + fixtureContracts, + mockRouter.address, + mockL1GRT.address, + mockL1Gateway.address, + ) + }) + + describe('calculateL2TokenAddress', function () { + it('returns the L2 token address', async function () { + expect(await l2GraphTokenGateway.calculateL2TokenAddress(mockL1GRT.address)).eq(grt.address) + }) + it('returns the zero address if the input is any other address', async function () { + expect(await l2GraphTokenGateway.calculateL2TokenAddress(tokenSender.address)).eq( + AddressZero, + ) + }) + }) + + describe('outboundTransfer', function () { + it('reverts when called with the wrong token address', async function () { + const tx = l2GraphTokenGateway + .connect(tokenSender.signer) + ['outboundTransfer(address,address,uint256,bytes)']( + tokenSender.address, + l1Receiver.address, + toGRT('10'), + defaultData, + ) + await expect(tx).revertedWith('TOKEN_NOT_GRT') + }) + it('burns tokens and triggers an L1 call', async function () { + await grt.connect(tokenSender.signer).approve(l2GraphTokenGateway.address, toGRT('10')) + await testValidOutboundTransfer(tokenSender.signer, defaultData) + }) + it('decodes the sender address from messages sent by the router', async function () { + await grt.connect(tokenSender.signer).approve(l2GraphTokenGateway.address, toGRT('10')) + const routerEncodedData = utils.defaultAbiCoder.encode( + ['address', 'bytes'], + [tokenSender.address, defaultData], + ) + await testValidOutboundTransfer(mockRouter.signer, routerEncodedData) + }) + it('reverts when called with nonempty calldata', async function () { + await grt.connect(tokenSender.signer).approve(l2GraphTokenGateway.address, toGRT('10')) + const tx = l2GraphTokenGateway + .connect(tokenSender.signer) + ['outboundTransfer(address,address,uint256,bytes)']( + mockL1GRT.address, + l1Receiver.address, + toGRT('10'), + defaultDataWithNotEmptyCallHookData, + ) + await expect(tx).revertedWith('CALL_HOOK_DATA_NOT_ALLOWED') + }) + it('reverts when the sender does not have enough GRT', async function () { + await grt.connect(tokenSender.signer).approve(l2GraphTokenGateway.address, toGRT('1001')) + const tx = l2GraphTokenGateway + .connect(tokenSender.signer) + ['outboundTransfer(address,address,uint256,bytes)']( + mockL1GRT.address, + l1Receiver.address, + toGRT('1001'), + defaultData, + ) + await expect(tx).revertedWith('ERC20: burn amount exceeds balance') + }) + }) + + describe('finalizeInboundTransfer', function () { + const testValidFinalizeTransfer = async function ( + data: string, + to?: string, + ): Promise { + to = to ?? l2Receiver.address + const mockL1GatewayL2Alias = await getL2SignerFromL1(mockL1Gateway.address) + await me.signer.sendTransaction({ + to: await mockL1GatewayL2Alias.getAddress(), + value: utils.parseUnits('1', 'ether'), + }) + const tx = l2GraphTokenGateway + .connect(mockL1GatewayL2Alias) + .finalizeInboundTransfer(mockL1GRT.address, tokenSender.address, to, toGRT('10'), data) + await expect(tx) + .emit(l2GraphTokenGateway, 'DepositFinalized') + .withArgs(mockL1GRT.address, tokenSender.address, to, toGRT('10')) + + await expect(tx).emit(grt, 'BridgeMinted').withArgs(to, toGRT('10')) + + // Unchanged + const senderBalance = await grt.balanceOf(tokenSender.address) + await expect(senderBalance).eq(toGRT('1000')) + // 10 newly minted GRT + const receiverBalance = await grt.balanceOf(to) + await expect(receiverBalance).eq(toGRT('10')) + return tx + } + it('reverts when called by an account that is not the gateway', async function () { + const tx = l2GraphTokenGateway + .connect(tokenSender.signer) + .finalizeInboundTransfer( + mockL1GRT.address, + tokenSender.address, + l2Receiver.address, + toGRT('10'), + defaultData, + ) + await expect(tx).revertedWith('ONLY_COUNTERPART_GATEWAY') + }) + it('reverts when called by an account that is the gateway but without the L2 alias', async function () { + const tx = l2GraphTokenGateway + .connect(mockL1Gateway.signer) + .finalizeInboundTransfer( + mockL1GRT.address, + tokenSender.address, + l2Receiver.address, + toGRT('10'), + defaultData, + ) + await expect(tx).revertedWith('ONLY_COUNTERPART_GATEWAY') + }) + it('mints and sends tokens when called by the aliased gateway', async function () { + await testValidFinalizeTransfer(defaultData) + }) + it('calls a callhook if the transfer includes calldata', async function () { + const tx = await testValidFinalizeTransfer( + defaultDataWithNotEmptyCallHookData, + callhookReceiverMock.address, + ) + // Emitted by the callhook: + await expect(tx) + .emit(callhookReceiverMock, 'TransferReceived') + .withArgs(tokenSender.address, toGRT('10'), toBN('1337'), toBN('42')) + }) + it('reverts if a callhook reverts', async function () { + // The 0 will make the callhook revert (see CallhookReceiverMock.sol) + const callHookData = utils.defaultAbiCoder.encode( + ['uint256', 'uint256'], + [toBN('0'), toBN('42')], + ) + const mockL1GatewayL2Alias = await getL2SignerFromL1(mockL1Gateway.address) + await me.signer.sendTransaction({ + to: await mockL1GatewayL2Alias.getAddress(), + value: utils.parseUnits('1', 'ether'), + }) + const tx = l2GraphTokenGateway + .connect(mockL1GatewayL2Alias) + .finalizeInboundTransfer( + mockL1GRT.address, + tokenSender.address, + callhookReceiverMock.address, + toGRT('10'), + callHookData, + ) + await expect(tx).revertedWith('FOO_IS_ZERO') + }) + it('reverts if trying to call a callhook in a contract that does not implement onTokenTransfer', async function () { + const callHookData = utils.defaultAbiCoder.encode(['uint256'], [toBN('0')]) + const mockL1GatewayL2Alias = await getL2SignerFromL1(mockL1Gateway.address) + await me.signer.sendTransaction({ + to: await mockL1GatewayL2Alias.getAddress(), + value: utils.parseUnits('1', 'ether'), + }) + // RewardsManager does not implement onTokenTransfer, so this will fail + const tx = l2GraphTokenGateway + .connect(mockL1GatewayL2Alias) + .finalizeInboundTransfer( + mockL1GRT.address, + tokenSender.address, + rewardsManager.address, + toGRT('10'), + callHookData, + ) + await expect(tx).revertedWith( + "function selector was not recognized and there's no fallback function", + ) + }) + }) + }) +}) diff --git a/test/lib/deployment.ts b/test/lib/deployment.ts index 68f0eb18a..a6afe9dbb 100644 --- a/test/lib/deployment.ts +++ b/test/lib/deployment.ts @@ -18,6 +18,10 @@ import { Staking } from '../../build/types/Staking' import { RewardsManager } from '../../build/types/RewardsManager' import { GraphGovernance } from '../../build/types/GraphGovernance' import { SubgraphNFT } from '../../build/types/SubgraphNFT' +import { L1GraphTokenGateway } from '../../build/types/L1GraphTokenGateway' +import { L2GraphTokenGateway } from '../../build/types/L2GraphTokenGateway' +import { L2GraphToken } from '../../build/types/L2GraphToken' +import { BridgeEscrow } from '../../build/types/BridgeEscrow' // Disable logging for tests logger.pause() @@ -53,6 +57,7 @@ export const defaults = { }, rewards: { issuanceRate: toGRT('1.000000023206889619'), // 5% annual rate + dripInterval: toBN('50400'), // 1 week in blocks (post-Merge) }, } @@ -243,3 +248,54 @@ export async function deployGraphGovernance( deployer, ) as unknown as GraphGovernance } + +export async function deployL1GraphTokenGateway( + deployer: Signer, + controller: string, + proxyAdmin: GraphProxyAdmin, +): Promise { + return network.deployContractWithProxy( + proxyAdmin, + 'L1GraphTokenGateway', + [controller], + deployer, + ) as unknown as L1GraphTokenGateway +} + +export async function deployBridgeEscrow( + deployer: Signer, + controller: string, + proxyAdmin: GraphProxyAdmin, +): Promise { + return network.deployContractWithProxy( + proxyAdmin, + 'BridgeEscrow', + [controller], + deployer, + ) as unknown as BridgeEscrow +} + +export async function deployL2GraphTokenGateway( + deployer: Signer, + controller: string, + proxyAdmin: GraphProxyAdmin, +): Promise { + return network.deployContractWithProxy( + proxyAdmin, + 'L2GraphTokenGateway', + [controller], + deployer, + ) as unknown as L2GraphTokenGateway +} + +export async function deployL2GRT( + deployer: Signer, + proxyAdmin: GraphProxyAdmin, +): Promise { + return network.deployContractWithProxy( + proxyAdmin, + 'L2GraphToken', + [await deployer.getAddress()], + deployer, + ) as unknown as L2GraphToken +} diff --git a/test/lib/fixtures.ts b/test/lib/fixtures.ts index 86541cf6d..8375a86a4 100644 --- a/test/lib/fixtures.ts +++ b/test/lib/fixtures.ts @@ -2,7 +2,60 @@ import { utils, Wallet, Signer } from 'ethers' import * as deployment from './deployment' -import { evmSnapshot, evmRevert, provider } from './testHelpers' +import { evmSnapshot, evmRevert, initNetwork, toBN } from './testHelpers' +import { BridgeMock } from '../../build/types/BridgeMock' +import { InboxMock } from '../../build/types/InboxMock' +import { OutboxMock } from '../../build/types/OutboxMock' +import { deployContract } from './deployment' +import { Controller } from '../../build/types/Controller' +import { DisputeManager } from '../../build/types/DisputeManager' +import { EpochManager } from '../../build/types/EpochManager' +import { GraphToken } from '../../build/types/GraphToken' +import { Curation } from '../../build/types/Curation' +import { GNS } from '../../build/types/GNS' +import { Staking } from '../../build/types/Staking' +import { RewardsManager } from '../../build/types/RewardsManager' +import { ServiceRegistry } from '../../build/types/ServiceRegistry' +import { GraphProxyAdmin } from '../../build/types/GraphProxyAdmin' +import { L1GraphTokenGateway } from '../../build/types/L1GraphTokenGateway' +import { BridgeEscrow } from '../../build/types/BridgeEscrow' +import { L2GraphTokenGateway } from '../../build/types/L2GraphTokenGateway' +import { L2GraphToken } from '../../build/types/L2GraphToken' + +export interface L1FixtureContracts { + controller: Controller + disputeManager: DisputeManager + epochManager: EpochManager + grt: GraphToken + curation: Curation + gns: GNS + staking: Staking + rewardsManager: RewardsManager + serviceRegistry: ServiceRegistry + proxyAdmin: GraphProxyAdmin + l1GraphTokenGateway: L1GraphTokenGateway + bridgeEscrow: BridgeEscrow +} + +export interface L2FixtureContracts { + controller: Controller + disputeManager: DisputeManager + epochManager: EpochManager + grt: L2GraphToken + curation: Curation + gns: GNS + staking: Staking + rewardsManager: RewardsManager + serviceRegistry: ServiceRegistry + proxyAdmin: GraphProxyAdmin + l2GraphTokenGateway: L2GraphTokenGateway +} + +export interface ArbitrumL1Mocks { + bridgeMock: BridgeMock + inboxMock: InboxMock + outboxMock: OutboxMock +} export class NetworkFixture { lastSnapshotId: number @@ -11,16 +64,13 @@ export class NetworkFixture { this.lastSnapshotId = 0 } - async load( + async _loadLayer( deployer: Signer, slasher: Signer = Wallet.createRandom() as Signer, arbitrator: Signer = Wallet.createRandom() as Signer, - ): Promise { - // Enable automining with each transaction, and disable - // the mining interval. Individual tests may modify this - // behavior as needed. - provider().send('evm_setIntervalMining', [0]) - provider().send('evm_setAutomine', [true]) + isL2: boolean, + ): Promise { + await initNetwork() // Roles const arbitratorAddress = await arbitrator.getAddress() @@ -34,7 +84,13 @@ export class NetworkFixture { controller.address, proxyAdmin, ) - const grt = await deployment.deployGRT(deployer) + let grt: GraphToken | L2GraphToken + if (isL2) { + grt = await deployment.deployL2GRT(deployer, proxyAdmin) + } else { + grt = await deployment.deployGRT(deployer) + } + const curation = await deployment.deployCuration(deployer, controller.address, proxyAdmin) const gns = await deployment.deployGNS(deployer, controller.address, proxyAdmin) const staking = await deployment.deployStaking(deployer, controller.address, proxyAdmin) @@ -55,6 +111,24 @@ export class NetworkFixture { proxyAdmin, ) + let l1GraphTokenGateway: L1GraphTokenGateway + let l2GraphTokenGateway: L2GraphTokenGateway + let bridgeEscrow: BridgeEscrow + if (isL2) { + l2GraphTokenGateway = await deployment.deployL2GraphTokenGateway( + deployer, + controller.address, + proxyAdmin, + ) + } else { + l1GraphTokenGateway = await deployment.deployL1GraphTokenGateway( + deployer, + controller.address, + proxyAdmin, + ) + bridgeEscrow = await deployment.deployBridgeEscrow(deployer, controller.address, proxyAdmin) + } + // Setup controller await controller.setContractProxy(utils.id('EpochManager'), epochManager.address) await controller.setContractProxy(utils.id('GraphToken'), grt.address) @@ -63,6 +137,11 @@ export class NetworkFixture { await controller.setContractProxy(utils.id('DisputeManager'), staking.address) await controller.setContractProxy(utils.id('RewardsManager'), rewardsManager.address) await controller.setContractProxy(utils.id('ServiceRegistry'), serviceRegistry.address) + if (isL2) { + await controller.setContractProxy(utils.id('GraphTokenGateway'), l2GraphTokenGateway.address) + } else { + await controller.setContractProxy(utils.id('GraphTokenGateway'), l1GraphTokenGateway.address) + } // Setup contracts await curation.connect(deployer).syncAllContracts() @@ -71,32 +150,143 @@ export class NetworkFixture { await disputeManager.connect(deployer).syncAllContracts() await rewardsManager.connect(deployer).syncAllContracts() await staking.connect(deployer).syncAllContracts() + if (isL2) { + await l2GraphTokenGateway.connect(deployer).syncAllContracts() + } else { + await l1GraphTokenGateway.connect(deployer).syncAllContracts() + await bridgeEscrow.connect(deployer).syncAllContracts() + } await staking.connect(deployer).setSlasher(slasherAddress, true) - await grt.connect(deployer).addMinter(rewardsManager.address) await gns.connect(deployer).approveAll() - await rewardsManager.connect(deployer).setIssuanceRate(deployment.defaults.rewards.issuanceRate) + if (!isL2) { + await grt.connect(deployer).addMinter(rewardsManager.address) + } // Unpause the protocol await controller.connect(deployer).setPaused(false) + if (isL2) { + return { + controller, + disputeManager, + epochManager, + grt: grt as L2GraphToken, + curation, + gns, + staking, + rewardsManager, + serviceRegistry, + proxyAdmin, + l2GraphTokenGateway, + } as L2FixtureContracts + } else { + return { + controller, + disputeManager, + epochManager, + grt: grt as GraphToken, + curation, + gns, + staking, + rewardsManager, + serviceRegistry, + proxyAdmin, + l1GraphTokenGateway, + bridgeEscrow, + } as L1FixtureContracts + } + } + + async load( + deployer: Signer, + slasher: Signer = Wallet.createRandom() as Signer, + arbitrator: Signer = Wallet.createRandom() as Signer, + ): Promise { + return this._loadLayer(deployer, slasher, arbitrator, false) as unknown as L1FixtureContracts + } + + async loadL2( + deployer: Signer, + slasher: Signer = Wallet.createRandom() as Signer, + arbitrator: Signer = Wallet.createRandom() as Signer, + ): Promise { + return this._loadLayer(deployer, slasher, arbitrator, true) as unknown as L2FixtureContracts + } + + async loadArbitrumL1Mocks(deployer: Signer): Promise { + const bridgeMock = (await deployContract('BridgeMock', deployer)) as unknown as BridgeMock + const inboxMock = (await deployContract('InboxMock', deployer)) as unknown as InboxMock + const outboxMock = (await deployContract('OutboxMock', deployer)) as unknown as OutboxMock return { - controller, - disputeManager, - epochManager, - grt, - curation, - gns, - staking, - rewardsManager, - serviceRegistry, - proxyAdmin, + bridgeMock, + inboxMock, + outboxMock, } } + async configureL1Bridge( + deployer: Signer, + arbitrumMocks: ArbitrumL1Mocks, + l1FixtureContracts: L1FixtureContracts, + mockRouterAddress: string, + mockL2GRTAddress: string, + mockL2GatewayAddress: string, + ): Promise { + // First configure the Arbitrum bridge mocks + await arbitrumMocks.bridgeMock.connect(deployer).setInbox(arbitrumMocks.inboxMock.address, true) + await arbitrumMocks.bridgeMock + .connect(deployer) + .setOutbox(arbitrumMocks.outboxMock.address, true) + await arbitrumMocks.inboxMock.connect(deployer).setBridge(arbitrumMocks.bridgeMock.address) + await arbitrumMocks.outboxMock.connect(deployer).setBridge(arbitrumMocks.bridgeMock.address) + + // Configure the gateway + await l1FixtureContracts.l1GraphTokenGateway + .connect(deployer) + .setArbitrumAddresses(arbitrumMocks.inboxMock.address, mockRouterAddress) + await l1FixtureContracts.l1GraphTokenGateway + .connect(deployer) + .setL2TokenAddress(mockL2GRTAddress) + await l1FixtureContracts.l1GraphTokenGateway + .connect(deployer) + .setL2CounterpartAddress(mockL2GatewayAddress) + await l1FixtureContracts.l1GraphTokenGateway + .connect(deployer) + .setEscrowAddress(l1FixtureContracts.bridgeEscrow.address) + await l1FixtureContracts.bridgeEscrow + .connect(deployer) + .approveAll(l1FixtureContracts.l1GraphTokenGateway.address) + await l1FixtureContracts.l1GraphTokenGateway.connect(deployer).setPaused(false) + } + + async configureL2Bridge( + deployer: Signer, + l2FixtureContracts: L2FixtureContracts, + mockRouterAddress: string, + mockL1GRTAddress: string, + mockL1GatewayAddress: string, + ): Promise { + // Configure the L2 GRT + // Configure the gateway + await l2FixtureContracts.grt + .connect(deployer) + .setGateway(l2FixtureContracts.l2GraphTokenGateway.address) + await l2FixtureContracts.grt.connect(deployer).setL1Address(mockL1GRTAddress) + // Configure the gateway + await l2FixtureContracts.l2GraphTokenGateway.connect(deployer).setL2Router(mockRouterAddress) + await l2FixtureContracts.l2GraphTokenGateway + .connect(deployer) + .setL1TokenAddress(mockL1GRTAddress) + await l2FixtureContracts.l2GraphTokenGateway + .connect(deployer) + .setL1CounterpartAddress(mockL1GatewayAddress) + await l2FixtureContracts.l2GraphTokenGateway.connect(deployer).setPaused(false) + } + async setUp(): Promise { this.lastSnapshotId = await evmSnapshot() - provider().send('evm_setAutomine', [true]) + await initNetwork() } async tearDown(): Promise { diff --git a/test/lib/graphTokenTests.ts b/test/lib/graphTokenTests.ts new file mode 100644 index 000000000..b0a1b7c7f --- /dev/null +++ b/test/lib/graphTokenTests.ts @@ -0,0 +1,287 @@ +import { expect } from 'chai' +import { constants, utils, BytesLike, BigNumber, Signature } from 'ethers' +import { eip712 } from '@graphprotocol/common-ts/dist/attestations' + +import * as deployment from './deployment' +import { getAccounts, getChainID, toBN, toGRT, Account, initNetwork } from './testHelpers' + +import { L2GraphToken } from '../../build/types/L2GraphToken' +import { GraphToken } from '../../build/types/GraphToken' + +const { AddressZero, MaxUint256 } = constants +const { keccak256, SigningKey } = utils + +const PERMIT_TYPE_HASH = eip712.typeHash( + 'Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)', +) +const L1SALT = '0x51f3d585afe6dfeb2af01bba0889a36c1db03beec88c6a4d0c53817069026afa' +const L2SALT = '0xe33842a7acd1d5a1d28f25a931703e5605152dc48d64dc4716efdae1f5659591' + +interface Permit { + owner: string + spender: string + value: BigNumber + nonce: BigNumber + deadline: BigNumber +} + +function hashEncodePermit(permit: Permit) { + return eip712.hashStruct( + PERMIT_TYPE_HASH, + ['address', 'address', 'uint256', 'uint256', 'uint256'], + [permit.owner, permit.spender, permit.value, permit.nonce, permit.deadline], + ) +} + +function signPermit( + signer: BytesLike, + chainId: number, + contractAddress: string, + permit: Permit, + salt: string, +): Signature { + const domainSeparator = eip712.domainSeparator({ + name: 'Graph Token', + version: '0', + chainId, + verifyingContract: contractAddress, + salt: salt, + }) + const hashEncodedPermit = hashEncodePermit(permit) + const message = eip712.encode(domainSeparator, hashEncodedPermit) + const messageHash = keccak256(message) + const signingKey = new SigningKey(signer) + return signingKey.signDigest(messageHash) +} + +export function grtTests(isL2: boolean): void { + let me: Account + let other: Account + let governor: Account + let salt: string + + const mePrivateKey = '0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d' + const otherPrivateKey = '0x6cbed15c793ce57650b9877cf6fa156fbef513c4e6134f022a85b1ffdd59b2a1' + + let grt: GraphToken | L2GraphToken + + async function permitMaxOK(): Promise { + return permitOK(MaxUint256) + } + + async function permitOK(value: BigNumber): Promise { + const nonce = await grt.nonces(me.address) + return { + owner: me.address, + spender: other.address, + value: value, + nonce: nonce, + deadline: toBN('0'), + } + } + + async function permitExpired(): Promise { + const permit = await permitMaxOK() + permit.deadline = toBN('1') + return permit + } + + async function permitDeny(): Promise { + const permit = await permitMaxOK() + permit.value = toBN('0') + return permit + } + + async function createPermitTransaction(permit: Permit, signer: string, salt: string) { + const chainID = await getChainID() + const signature: Signature = signPermit(signer, chainID, grt.address, permit, salt) + return grt.permit( + permit.owner, + permit.spender, + permit.value, + permit.deadline, + signature.v, + signature.r, + signature.s, + ) + } + + before(async function () { + await initNetwork() + ;[me, other, governor] = await getAccounts() + }) + + beforeEach(async function () { + // Deploy graph token + if (isL2) { + const proxyAdmin = await deployment.deployProxyAdmin(governor.signer) + grt = await deployment.deployL2GRT(governor.signer, proxyAdmin) + salt = L2SALT + } else { + grt = await deployment.deployGRT(governor.signer) + salt = L1SALT + } + + // Mint some tokens + const tokens = toGRT('10000') + await grt.connect(governor.signer).mint(me.address, tokens) + }) + + describe('permit', function () { + it('should permit max token allowance', async function () { + // Allow to transfer tokens + const tokensToApprove = toGRT('1000') + const permit = await permitOK(tokensToApprove) + const tx = createPermitTransaction(permit, mePrivateKey, salt) + await expect(tx).emit(grt, 'Approval').withArgs(permit.owner, permit.spender, tokensToApprove) + + // Allowance updated + const allowance = await grt.allowance(me.address, other.address) + expect(allowance).eq(tokensToApprove) + + // Transfer tokens should work + const tokens = toGRT('100') + await grt.connect(other.signer).transferFrom(me.address, other.address, tokens) + }) + + it('should permit max token allowance', async function () { + // Allow to transfer tokens + const permit = await permitMaxOK() + const tx = createPermitTransaction(permit, mePrivateKey, salt) + await expect(tx).emit(grt, 'Approval').withArgs(permit.owner, permit.spender, MaxUint256) + + // Allowance updated + const allowance = await grt.allowance(me.address, other.address) + expect(allowance).eq(MaxUint256) + + // Transfer tokens should work + const tokens = toGRT('100') + await grt.connect(other.signer).transferFrom(me.address, other.address, tokens) + }) + + it('reject to transfer more tokens than approved by permit', async function () { + // Allow to transfer tokens + const tokensToApprove = toGRT('1000') + const permit = await permitOK(tokensToApprove) + await createPermitTransaction(permit, mePrivateKey, salt) + + // Should not transfer more than approved + const tooManyTokens = toGRT('1001') + const tx = grt.connect(other.signer).transferFrom(me.address, other.address, tooManyTokens) + await expect(tx).revertedWith('ERC20: transfer amount exceeds allowance') + + // Should transfer up to the approved amount + await grt.connect(other.signer).transferFrom(me.address, other.address, tokensToApprove) + }) + + it('reject use two permits with same nonce', async function () { + // Allow to transfer tokens + const permit = await permitMaxOK() + await createPermitTransaction(permit, mePrivateKey, salt) + + // Try to re-use the permit + const tx = createPermitTransaction(permit, mePrivateKey, salt) + await expect(tx).revertedWith('GRT: invalid permit') + }) + + it('reject use expired permit', async function () { + const permit = await permitExpired() + const tx = createPermitTransaction(permit, mePrivateKey, salt) + await expect(tx).revertedWith('GRT: expired permit') + }) + + it('reject permit if holder address does not match', async function () { + const permit = await permitMaxOK() + const tx = createPermitTransaction(permit, otherPrivateKey, salt) + await expect(tx).revertedWith('GRT: invalid permit') + }) + + it('should deny transfer from if permit was denied', async function () { + // Allow to transfer tokens + const permit1 = await permitMaxOK() + await createPermitTransaction(permit1, mePrivateKey, salt) + + // Deny transfer tokens + const permit2 = await permitDeny() + await createPermitTransaction(permit2, mePrivateKey, salt) + + // Allowance updated + const allowance = await grt.allowance(me.address, other.address) + expect(allowance).eq(toBN('0')) + + // Try to transfer without permit should fail + const tokens = toGRT('100') + const tx = grt.connect(other.signer).transferFrom(me.address, other.address, tokens) + await expect(tx).revertedWith('ERC20: transfer amount exceeds allowance') + }) + }) + + describe('mint', function () { + describe('addMinter', function () { + it('reject add a new minter if not allowed', async function () { + const tx = grt.connect(me.signer).addMinter(me.address) + await expect(tx).revertedWith('Only Governor can call') + }) + + it('should add a new minter', async function () { + expect(await grt.isMinter(me.address)).eq(false) + const tx = grt.connect(governor.signer).addMinter(me.address) + await expect(tx).emit(grt, 'MinterAdded').withArgs(me.address) + expect(await grt.isMinter(me.address)).eq(true) + }) + }) + + describe('mint', async function () { + it('reject mint if not minter', async function () { + const tx = grt.connect(me.signer).mint(me.address, toGRT('100')) + await expect(tx).revertedWith('Only minter can call') + }) + }) + + context('> when is minter', function () { + beforeEach(async function () { + await grt.connect(governor.signer).addMinter(me.address) + expect(await grt.isMinter(me.address)).eq(true) + }) + + describe('mint', async function () { + it('should mint', async function () { + const beforeTokens = await grt.balanceOf(me.address) + + const tokensToMint = toGRT('100') + const tx = grt.connect(me.signer).mint(me.address, tokensToMint) + await expect(tx).emit(grt, 'Transfer').withArgs(AddressZero, me.address, tokensToMint) + + const afterTokens = await grt.balanceOf(me.address) + expect(afterTokens).eq(beforeTokens.add(tokensToMint)) + }) + + it('should mint if governor', async function () { + const tokensToMint = toGRT('100') + await grt.connect(governor.signer).mint(me.address, tokensToMint) + }) + }) + + describe('removeMinter', function () { + it('reject remove a minter if not allowed', async function () { + const tx = grt.connect(me.signer).removeMinter(me.address) + await expect(tx).revertedWith('Only Governor can call') + }) + + it('should remove a minter', async function () { + const tx = grt.connect(governor.signer).removeMinter(me.address) + await expect(tx).emit(grt, 'MinterRemoved').withArgs(me.address) + expect(await grt.isMinter(me.address)).eq(false) + }) + }) + + describe('renounceMinter', function () { + it('should renounce to be a minter', async function () { + const tx = grt.connect(me.signer).renounceMinter() + await expect(tx).emit(grt, 'MinterRemoved').withArgs(me.address) + expect(await grt.isMinter(me.address)).eq(false) + }) + }) + }) + }) +} diff --git a/test/lib/testHelpers.ts b/test/lib/testHelpers.ts index ee7c87322..87e04beb1 100644 --- a/test/lib/testHelpers.ts +++ b/test/lib/testHelpers.ts @@ -2,7 +2,8 @@ import hre from 'hardhat' import '@nomiclabs/hardhat-ethers' import '@nomiclabs/hardhat-waffle' import { providers, utils, BigNumber, Signer, Wallet } from 'ethers' -import { formatUnits, getAddress } from 'ethers/lib/utils' +import { formatUnits, getAddress, hexValue } from 'ethers/lib/utils' +import { BigNumber as BN } from 'bignumber.js' import { EpochManager } from '../../build/types/EpochManager' @@ -25,6 +26,14 @@ export interface Account { export const provider = (): providers.JsonRpcProvider => hre.waffle.provider +// Enable automining with each transaction, and disable +// the mining interval. Individual tests may modify this +// behavior as needed. +export async function initNetwork(): Promise { + await provider().send('evm_setIntervalMining', [0]) + await provider().send('evm_setAutomine', [true]) +} + export const getAccounts = async (): Promise => { const accounts = [] const signers: Signer[] = await hre.ethers.getSigners() @@ -57,24 +66,17 @@ export const advanceBlockTo = async (blockNumber: string | number | BigNumber): ? toBN(blockNumber) : blockNumber const currentBlock = await latestBlock() - const start = Date.now() - let notified - if (target.lt(currentBlock)) + if (target.lt(currentBlock)) { throw Error(`Target block #(${target}) is lower than current block #(${currentBlock})`) - while ((await latestBlock()).lt(target)) { - if (!notified && Date.now() - start >= 5000) { - notified = true - console.log(`advanceBlockTo: Advancing too ` + 'many blocks is causing this test to be slow.') - } - await advanceBlock() + } else if (target.eq(currentBlock)) { + return + } else { + await advanceBlocks(target.sub(currentBlock)) } } export const advanceBlocks = async (blocks: string | number | BigNumber): Promise => { - const steps = typeof blocks === 'number' || typeof blocks === 'string' ? toBN(blocks) : blocks - const currentBlock = await latestBlock() - const toBlock = currentBlock.add(steps) - return advanceBlockTo(toBlock) + await provider().send('hardhat_mine', [hexValue(BigNumber.from(blocks))]) } export const advanceToNextEpoch = async (epochManager: EpochManager): Promise => { @@ -119,3 +121,24 @@ export const deriveChannelKey = (): ChannelKey => { }, } } + +// Adapted from: +// https://github.com/livepeer/arbitrum-lpt-bridge/blob/e1a81edda3594e434dbcaa4f1ebc95b7e67ecf2a/utils/arbitrum/messaging.ts#L118 +export const applyL1ToL2Alias = (l1Address: string): string => { + const offset = toBN('0x1111000000000000000000000000000000001111') + const l1AddressAsNumber = toBN(l1Address) + const l2AddressAsNumber = l1AddressAsNumber.add(offset) + + const mask = toBN(2).pow(160) + return l2AddressAsNumber.mod(mask).toHexString() +} + +// Adapted from: +// https://github.com/livepeer/arbitrum-lpt-bridge/blob/e1a81edda3594e434dbcaa4f1ebc95b7e67ecf2a/test/utils/messaging.ts#L5 +export async function getL2SignerFromL1(l1Address: string): Promise { + const l2Address = applyL1ToL2Alias(l1Address) + await provider().send('hardhat_impersonateAccount', [l2Address]) + const l2Signer = await hre.ethers.getSigner(l2Address) + + return l2Signer +} diff --git a/test/rewards/rewards.test.ts b/test/rewards/rewards.test.ts index 23aee0dfa..beed73a73 100644 --- a/test/rewards/rewards.test.ts +++ b/test/rewards/rewards.test.ts @@ -171,7 +171,7 @@ describe('Rewards', () => { describe('issuance rate update', function () { it('reject set issuance rate if unauthorized', async function () { const tx = rewardsManager.connect(indexer1.signer).setIssuanceRate(toGRT('1.025')) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('reject set issuance rate to less than minimum allowed', async function () { @@ -199,7 +199,7 @@ describe('Rewards', () => { const tx = rewardsManager .connect(indexer1.signer) .setSubgraphAvailabilityOracle(oracle.address) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('should set subgraph oracle if governor', async function () { diff --git a/test/staking/configuration.test.ts b/test/staking/configuration.test.ts index 39ed30502..52aeaa843 100644 --- a/test/staking/configuration.test.ts +++ b/test/staking/configuration.test.ts @@ -52,7 +52,7 @@ describe('Staking:Config', () => { it('reject set `minimumIndexerStake` if not allowed', async function () { const newValue = toGRT('100') const tx = staking.connect(me.signer).setMinimumIndexerStake(newValue) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('reject set `minimumIndexerStake` to zero', async function () { @@ -74,7 +74,7 @@ describe('Staking:Config', () => { it('reject set `slasher` if not allowed', async function () { const tx = staking.connect(other.signer).setSlasher(me.address, true) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('reject set `slasher` for zero', async function () { @@ -103,7 +103,7 @@ describe('Staking:Config', () => { it('reject set `assetHolder` if not allowed', async function () { const tx = staking.connect(other.signer).setAssetHolder(me.address, true) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('reject set `assetHolder` to address zero', async function () { @@ -122,7 +122,7 @@ describe('Staking:Config', () => { it('reject set `channelDisputeEpochs` if not allowed', async function () { const newValue = toBN('5') const tx = staking.connect(other.signer).setChannelDisputeEpochs(newValue) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('reject set `channelDisputeEpochs` to zero', async function () { @@ -146,7 +146,7 @@ describe('Staking:Config', () => { it('reject set `curationPercentage` if not allowed', async function () { const tx = staking.connect(other.signer).setCurationPercentage(50) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) @@ -166,7 +166,7 @@ describe('Staking:Config', () => { it('reject set `protocolPercentage` if not allowed', async function () { const tx = staking.connect(other.signer).setProtocolPercentage(50) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) @@ -180,7 +180,7 @@ describe('Staking:Config', () => { it('reject set `maxAllocationEpochs` if not allowed', async function () { const newValue = toBN('5') const tx = staking.connect(other.signer).setMaxAllocationEpochs(newValue) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) @@ -194,7 +194,7 @@ describe('Staking:Config', () => { it('reject set `thawingPeriod` if not allowed', async function () { const newValue = toBN('5') const tx = staking.connect(other.signer).setThawingPeriod(newValue) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) it('reject set `thawingPeriod` to zero', async function () { @@ -225,7 +225,7 @@ describe('Staking:Config', () => { it('reject set `rebateRatio` if not allowed', async function () { const tx = staking.connect(other.signer).setRebateRatio(1, 1) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) }) diff --git a/test/staking/delegation.test.ts b/test/staking/delegation.test.ts index cad46a063..e097dfd9f 100644 --- a/test/staking/delegation.test.ts +++ b/test/staking/delegation.test.ts @@ -217,7 +217,7 @@ describe('Staking::Delegation', () => { it('reject set `delegationRatio` if not allowed', async function () { const tx = staking.connect(me.signer).setDelegationRatio(delegationRatio) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) @@ -231,7 +231,7 @@ describe('Staking::Delegation', () => { it('reject set `delegationParametersCooldown` if not allowed', async function () { const tx = staking.connect(me.signer).setDelegationParametersCooldown(cooldown) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) @@ -251,7 +251,7 @@ describe('Staking::Delegation', () => { it('reject set `delegationTaxPercentage` if not allowed', async function () { const tx = staking.connect(me.signer).setDelegationTaxPercentage(50) - await expect(tx).revertedWith('Caller must be Controller governor') + await expect(tx).revertedWith('Only Controller governor') }) }) diff --git a/test/staking/rebate.test.ts b/test/staking/rebate.test.ts index 2668b29f4..d6db5948b 100644 --- a/test/staking/rebate.test.ts +++ b/test/staking/rebate.test.ts @@ -4,7 +4,7 @@ import { BigNumber } from 'ethers' import { deployContract } from '../lib/deployment' import { RebatePoolMock } from '../../build/types/RebatePoolMock' -import { getAccounts, toBN, toGRT, formatGRT, Account } from '../lib/testHelpers' +import { getAccounts, toBN, toGRT, formatGRT, Account, initNetwork } from '../lib/testHelpers' const toFloat = (n: BigNumber) => parseFloat(formatGRT(n)) const toFixed = (n: number | BigNumber, precision = 12) => { @@ -194,6 +194,7 @@ describe('Staking:Rebate', () => { } beforeEach(async function () { + await initNetwork() ;[deployer] = await getAccounts() rebatePoolMock = (await deployContract( 'RebatePoolMock', diff --git a/yarn.lock b/yarn.lock index 18353874e..42084d9cd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,16 @@ # yarn lockfile v1 +"@arbitrum/sdk@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@arbitrum/sdk/-/sdk-3.0.0.tgz#a5dc48a00cb8c6e230a2c696c0e880a7f80c637d" + integrity sha512-Mws5WAxxirp3vk8JH3vyQ5H6q1NNUIAAGEd9oEnQYDMyTBHLKU293GA3s9w4w6ZfIq/RZq8YCexhy4D1R+mQng== + dependencies: + "@ethersproject/address" "^5.0.8" + "@ethersproject/bignumber" "^5.1.1" + "@ethersproject/bytes" "^5.0.8" + ethers "^5.1.0" + "@babel/code-frame@7.12.11": version "7.12.11" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" @@ -10,23 +20,23 @@ "@babel/highlight" "^7.10.4" "@babel/code-frame@^7.0.0": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" - integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" + integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== dependencies: - "@babel/highlight" "^7.18.6" + "@babel/highlight" "^7.16.7" -"@babel/helper-validator-identifier@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" - integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== +"@babel/helper-validator-identifier@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" + integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== -"@babel/highlight@^7.10.4", "@babel/highlight@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" - integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== +"@babel/highlight@^7.10.4", "@babel/highlight@^7.16.7": + version "7.17.9" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.17.9.tgz#61b2ee7f32ea0454612def4fccdae0de232b73e3" + integrity sha512-J9PfEKCbFIv2X5bjTMiZu6Vf341N05QIY+d6FvVKynkG1S7G0j3I0QoRtWIrXhZ+/Nlb5Q0MzqL7TokEJ5BNHg== dependencies: - "@babel/helper-validator-identifier" "^7.18.6" + "@babel/helper-validator-identifier" "^7.16.7" chalk "^2.0.0" js-tokens "^4.0.0" @@ -191,6 +201,18 @@ enabled "2.0.x" kuler "^2.0.0" +"@defi-wonderland/smock@^2.0.7": + version "2.0.7" + resolved "https://registry.yarnpkg.com/@defi-wonderland/smock/-/smock-2.0.7.tgz#59d5fc93e175ad120c5dcdd8294e07525606c855" + integrity sha512-RVpODLKZ/Cr0C1bCbhJ2aXbAr2Ll/K2WO7hDL96tqhMzCsA7ToWdDIgiNpV5Vtqqvpftu5ddO7v3TAurQNSU0w== + dependencies: + "@nomiclabs/ethereumjs-vm" "^4.2.2" + diff "^5.0.0" + lodash.isequal "^4.5.0" + lodash.isequalwith "^4.4.0" + rxjs "^7.2.0" + semver "^7.3.5" + "@endemolshinegroup/cosmiconfig-typescript-loader@^3.0.2": version "3.0.2" resolved "https://registry.yarnpkg.com/@endemolshinegroup/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-3.0.2.tgz#eea4635828dde372838b0909693ebd9aafeec22d" @@ -285,7 +307,17 @@ patch-package "^6.2.2" postinstall-postinstall "^2.1.0" -"@ethereumjs/block@^3.5.0", "@ethereumjs/block@^3.6.2", "@ethereumjs/block@^3.6.3": +"@ethereumjs/block@^3.5.0", "@ethereumjs/block@^3.6.0", "@ethereumjs/block@^3.6.2": + version "3.6.2" + resolved "https://registry.yarnpkg.com/@ethereumjs/block/-/block-3.6.2.tgz#63d1e26d0b7a7a3684fce920de6ebabec1e5b674" + integrity sha512-mOqYWwMlAZpYUEOEqt7EfMFuVL2eyLqWWIzcf4odn6QgXY8jBI2NhVuJncrMCKeMZrsJAe7/auaRRB6YcdH+Qw== + dependencies: + "@ethereumjs/common" "^2.6.3" + "@ethereumjs/tx" "^3.5.1" + ethereumjs-util "^7.1.4" + merkle-patricia-tree "^4.2.4" + +"@ethereumjs/block@^3.6.3": version "3.6.3" resolved "https://registry.yarnpkg.com/@ethereumjs/block/-/block-3.6.3.tgz#d96cbd7af38b92ebb3424223dbf773f5ccd27f84" integrity sha512-CegDeryc2DVKnDkg5COQrE0bJfw/p0v3GBk2W5/Dj5dOVfEmb50Ux0GLnSPypooLnfqjwFaorGuT9FokWB3GRg== @@ -295,7 +327,21 @@ ethereumjs-util "^7.1.5" merkle-patricia-tree "^4.2.4" -"@ethereumjs/blockchain@^5.5.2", "@ethereumjs/blockchain@^5.5.3": +"@ethereumjs/blockchain@^5.5.0", "@ethereumjs/blockchain@^5.5.2": + version "5.5.2" + resolved "https://registry.yarnpkg.com/@ethereumjs/blockchain/-/blockchain-5.5.2.tgz#1848abd9dc1ee56acf8cec4c84304d7f4667d027" + integrity sha512-Jz26iJmmsQtngerW6r5BDFaew/f2mObLrRZo3rskLOx1lmtMZ8+TX/vJexmivrnWgmAsTdNWhlKUYY4thPhPig== + dependencies: + "@ethereumjs/block" "^3.6.2" + "@ethereumjs/common" "^2.6.3" + "@ethereumjs/ethash" "^1.1.0" + debug "^4.3.3" + ethereumjs-util "^7.1.4" + level-mem "^5.0.1" + lru-cache "^5.1.1" + semaphore-async-await "^1.5.1" + +"@ethereumjs/blockchain@^5.5.3": version "5.5.3" resolved "https://registry.yarnpkg.com/@ethereumjs/blockchain/-/blockchain-5.5.3.tgz#aa49a6a04789da6b66b5bcbb0d0b98efc369f640" integrity sha512-bi0wuNJ1gw4ByNCV56H0Z4Q7D+SxUbwyG12Wxzbvqc89PXLRNR20LBcSUZRKpN0+YCPo6m0XZL/JLio3B52LTw== @@ -309,7 +355,7 @@ lru-cache "^5.1.1" semaphore-async-await "^1.5.1" -"@ethereumjs/common@^2.5.0", "@ethereumjs/common@^2.6.4", "@ethereumjs/common@^2.6.5": +"@ethereumjs/common@^2.3.0", "@ethereumjs/common@^2.6.5": version "2.6.5" resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.6.5.tgz#0a75a22a046272579d91919cb12d84f2756e8d30" integrity sha512-lRyVQOeCDaIVtgfbowla32pzeDv2Obr8oR8Put5RdUBNRGr1VGPGQNGP6elWIpgK3YdpzqTOh4GyUGOureVeeA== @@ -317,6 +363,22 @@ crc-32 "^1.2.0" ethereumjs-util "^7.1.5" +"@ethereumjs/common@^2.4.0", "@ethereumjs/common@^2.6.0", "@ethereumjs/common@^2.6.3": + version "2.6.3" + resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.6.3.tgz#39ddece7300b336276bad6c02f6a9f1a082caa05" + integrity sha512-mQwPucDL7FDYIg9XQ8DL31CnIYZwGhU5hyOO5E+BMmT71G0+RHvIT5rIkLBirJEKxV6+Rcf9aEIY0kXInxUWpQ== + dependencies: + crc-32 "^1.2.0" + ethereumjs-util "^7.1.4" + +"@ethereumjs/common@^2.6.4": + version "2.6.4" + resolved "https://registry.yarnpkg.com/@ethereumjs/common/-/common-2.6.4.tgz#1b3cdd3aa4ee3b0ca366756fc35e4a03022a01cc" + integrity sha512-RDJh/R/EAr+B7ZRg5LfJ0BIpf/1LydFgYdvZEuTraojCbVypO2sQ+QnpP5u2wJf9DASyooKqu8O4FJEWUV6NXw== + dependencies: + crc-32 "^1.2.0" + ethereumjs-util "^7.1.4" + "@ethereumjs/ethash@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@ethereumjs/ethash/-/ethash-1.1.0.tgz#7c5918ffcaa9cb9c1dc7d12f77ef038c11fb83fb" @@ -328,7 +390,7 @@ ethereumjs-util "^7.1.1" miller-rabin "^4.0.0" -"@ethereumjs/tx@^3.3.2", "@ethereumjs/tx@^3.5.1", "@ethereumjs/tx@^3.5.2": +"@ethereumjs/tx@^3.2.1", "@ethereumjs/tx@^3.5.2": version "3.5.2" resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-3.5.2.tgz#197b9b6299582ad84f9527ca961466fce2296c1c" integrity sha512-gQDNJWKrSDGu2w7w0PzVXVBNMzb7wwdDOmOqczmhNjqFxFuIbhVJDwiGEnxFNC2/b8ifcZzY7MLcluizohRzNw== @@ -336,6 +398,32 @@ "@ethereumjs/common" "^2.6.4" ethereumjs-util "^7.1.5" +"@ethereumjs/tx@^3.4.0", "@ethereumjs/tx@^3.5.1": + version "3.5.1" + resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-3.5.1.tgz#8d941b83a602b4a89949c879615f7ea9a90e6671" + integrity sha512-xzDrTiu4sqZXUcaBxJ4n4W5FrppwxLxZB4ZDGVLtxSQR4lVuOnFR6RcUHdg1mpUhAPVrmnzLJpxaeXnPxIyhWA== + dependencies: + "@ethereumjs/common" "^2.6.3" + ethereumjs-util "^7.1.4" + +"@ethereumjs/vm@^5.6.0": + version "5.8.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/vm/-/vm-5.8.0.tgz#c9055f96afc13dd7b72893b57fa20027effea6fe" + integrity sha512-mn2G2SX79QY4ckVvZUfxlNUpzwT2AEIkvgJI8aHoQaNYEHhH8rmdVDIaVVgz6//PjK52BZsK23afz+WvSR0Qqw== + dependencies: + "@ethereumjs/block" "^3.6.2" + "@ethereumjs/blockchain" "^5.5.2" + "@ethereumjs/common" "^2.6.3" + "@ethereumjs/tx" "^3.5.1" + async-eventemitter "^0.2.4" + core-js-pure "^3.0.1" + debug "^4.3.3" + ethereumjs-util "^7.1.4" + functional-red-black-tree "^1.0.1" + mcl-wasm "^0.7.1" + merkle-patricia-tree "^4.2.4" + rustbn.js "~0.2.0" + "@ethereumjs/vm@^5.9.0": version "5.9.3" resolved "https://registry.yarnpkg.com/@ethereumjs/vm/-/vm-5.9.3.tgz#6d69202e4c132a4a1e1628ac246e92062e230823" @@ -369,7 +457,22 @@ "@ethersproject/properties" ">=5.0.0-beta.131" "@ethersproject/strings" ">=5.0.0-beta.130" -"@ethersproject/abi@5.6.0": +"@ethersproject/abi@5.0.7": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.7.tgz#79e52452bd3ca2956d0e1c964207a58ad1a0ee7b" + integrity sha512-Cqktk+hSIckwP/W8O47Eef60VwmoSC/L3lY0+dIBhQPCNn9E4V7rwmm2aFrNRRDJfFlGuZ1khkQUOc3oBX+niw== + dependencies: + "@ethersproject/address" "^5.0.4" + "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bytes" "^5.0.4" + "@ethersproject/constants" "^5.0.4" + "@ethersproject/hash" "^5.0.4" + "@ethersproject/keccak256" "^5.0.3" + "@ethersproject/logger" "^5.0.5" + "@ethersproject/properties" "^5.0.3" + "@ethersproject/strings" "^5.0.4" + +"@ethersproject/abi@5.6.0", "@ethersproject/abi@^5.0.0-beta.146", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.5.0", "@ethersproject/abi@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.6.0.tgz#ea07cbc1eec2374d32485679c12408005895e9f3" integrity sha512-AhVByTwdXCc2YQ20v300w6KVHle9g2OFc28ZAFCPnJyEpkv1xKXjZcSTgWOlv1i+0dqlgF8RCF2Rn2KC1t+1Vg== @@ -384,22 +487,7 @@ "@ethersproject/properties" "^5.6.0" "@ethersproject/strings" "^5.6.0" -"@ethersproject/abi@5.6.4", "@ethersproject/abi@^5.0.0-beta.146", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.5.0", "@ethersproject/abi@^5.6.0", "@ethersproject/abi@^5.6.3": - version "5.6.4" - resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.6.4.tgz#f6e01b6ed391a505932698ecc0d9e7a99ee60362" - integrity sha512-TTeZUlCeIHG6527/2goZA6gW5F8Emoc7MrZDC7hhP84aRGvW3TEdTnZR08Ls88YXM1m2SuK42Osw/jSi3uO8gg== - dependencies: - "@ethersproject/address" "^5.6.1" - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/constants" "^5.6.1" - "@ethersproject/hash" "^5.6.1" - "@ethersproject/keccak256" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/strings" "^5.6.1" - -"@ethersproject/abstract-provider@5.6.0": +"@ethersproject/abstract-provider@5.6.0", "@ethersproject/abstract-provider@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.6.0.tgz#0c4ac7054650dbd9c476cf5907f588bbb6ef3061" integrity sha512-oPMFlKLN+g+y7a79cLK3WiLcjWFnZQtXWgnLAbHZcN3s7L4v90UHpTOrLk+m3yr0gt+/h9STTM6zrr7PM8uoRw== @@ -412,20 +500,7 @@ "@ethersproject/transactions" "^5.6.0" "@ethersproject/web" "^5.6.0" -"@ethersproject/abstract-provider@5.6.1", "@ethersproject/abstract-provider@^5.6.0", "@ethersproject/abstract-provider@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.6.1.tgz#02ddce150785caf0c77fe036a0ebfcee61878c59" - integrity sha512-BxlIgogYJtp1FS8Muvj8YfdClk3unZH0vRMVX791Z9INBNT/kuACZ9GzaY1Y4yFq+YSy6/w4gzj3HCRKrK9hsQ== - dependencies: - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/networks" "^5.6.3" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/transactions" "^5.6.2" - "@ethersproject/web" "^5.6.1" - -"@ethersproject/abstract-signer@5.6.0": +"@ethersproject/abstract-signer@5.6.0", "@ethersproject/abstract-signer@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.6.0.tgz#9cd7ae9211c2b123a3b29bf47aab17d4d016e3e7" integrity sha512-WOqnG0NJKtI8n0wWZPReHtaLkDByPL67tn4nBaDAhmVq8sjHTPbCdz4DRhVu/cfTOvfy9w3iq5QZ7BX7zw56BQ== @@ -436,18 +511,7 @@ "@ethersproject/logger" "^5.6.0" "@ethersproject/properties" "^5.6.0" -"@ethersproject/abstract-signer@5.6.2", "@ethersproject/abstract-signer@^5.6.0", "@ethersproject/abstract-signer@^5.6.2": - version "5.6.2" - resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.6.2.tgz#491f07fc2cbd5da258f46ec539664713950b0b33" - integrity sha512-n1r6lttFBG0t2vNiI3HoWaS/KdOt8xyDjzlP2cuevlWLG6EX0OwcKLyG/Kp/cuwNxdy/ous+R/DEMdTUwWQIjQ== - dependencies: - "@ethersproject/abstract-provider" "^5.6.1" - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - -"@ethersproject/address@5.6.0": +"@ethersproject/address@5.6.0", "@ethersproject/address@>=5.0.0-beta.128", "@ethersproject/address@^5.0.2", "@ethersproject/address@^5.0.4", "@ethersproject/address@^5.0.8", "@ethersproject/address@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.6.0.tgz#13c49836d73e7885fc148ad633afad729da25012" integrity sha512-6nvhYXjbXsHPS+30sHZ+U4VMagFC/9zAk6Gd/h3S21YW4+yfb0WfRtaAIZ4kfM4rrVwqiy284LP0GtL5HXGLxQ== @@ -458,32 +522,14 @@ "@ethersproject/logger" "^5.6.0" "@ethersproject/rlp" "^5.6.0" -"@ethersproject/address@5.6.1", "@ethersproject/address@>=5.0.0-beta.128", "@ethersproject/address@^5.0.2", "@ethersproject/address@^5.6.0", "@ethersproject/address@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.6.1.tgz#ab57818d9aefee919c5721d28cd31fd95eff413d" - integrity sha512-uOgF0kS5MJv9ZvCz7x6T2EXJSzotiybApn4XlOgoTX0xdtyVIJ7pF+6cGPxiEq/dpBiTfMiw7Yc81JcwhSYA0Q== - dependencies: - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/keccak256" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/rlp" "^5.6.1" - -"@ethersproject/base64@5.6.0": +"@ethersproject/base64@5.6.0", "@ethersproject/base64@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.6.0.tgz#a12c4da2a6fb86d88563216b0282308fc15907c9" integrity sha512-2Neq8wxJ9xHxCF9TUgmKeSh9BXJ6OAxWfeGWvbauPh8FuHEjamgHilllx8KkSd5ErxyHIX7Xv3Fkcud2kY9ezw== dependencies: "@ethersproject/bytes" "^5.6.0" -"@ethersproject/base64@5.6.1", "@ethersproject/base64@^5.6.0", "@ethersproject/base64@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.6.1.tgz#2c40d8a0310c9d1606c2c37ae3092634b41d87cb" - integrity sha512-qB76rjop6a0RIYYMiB4Eh/8n+Hxu2NIZm8S/Q7kNo5pmZfXhHGHmS4MinUainiBC54SCyRnwzL+KZjj8zbsSsw== - dependencies: - "@ethersproject/bytes" "^5.6.1" - -"@ethersproject/basex@5.6.0": +"@ethersproject/basex@5.6.0", "@ethersproject/basex@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.6.0.tgz#9ea7209bf0a1c3ddc2a90f180c3a7f0d7d2e8a69" integrity sha512-qN4T+hQd/Md32MoJpc69rOwLYRUXwjTlhHDIeUkUmiN/JyWkkLLMoG0TqvSQKNqZOMgN5stbUYN6ILC+eD7MEQ== @@ -491,15 +537,7 @@ "@ethersproject/bytes" "^5.6.0" "@ethersproject/properties" "^5.6.0" -"@ethersproject/basex@5.6.1", "@ethersproject/basex@^5.6.0", "@ethersproject/basex@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.6.1.tgz#badbb2f1d4a6f52ce41c9064f01eab19cc4c5305" - integrity sha512-a52MkVz4vuBXR06nvflPMotld1FJWSj2QT0985v7P/emPZO00PucFAkbcmq2vpVU7Ts7umKiSI6SppiLykVWsA== - dependencies: - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/properties" "^5.6.0" - -"@ethersproject/bignumber@5.6.0": +"@ethersproject/bignumber@5.6.0", "@ethersproject/bignumber@>=5.0.0-beta.130", "@ethersproject/bignumber@^5.0.7", "@ethersproject/bignumber@^5.1.1", "@ethersproject/bignumber@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.6.0.tgz#116c81b075c57fa765a8f3822648cf718a8a0e26" integrity sha512-VziMaXIUHQlHJmkv1dlcd6GY2PmT0khtAqaMctCIDogxkrarMzA9L94KN1NeXqqOfFD6r0sJT3vCTOFSmZ07DA== @@ -508,36 +546,20 @@ "@ethersproject/logger" "^5.6.0" bn.js "^4.11.9" -"@ethersproject/bignumber@5.6.2", "@ethersproject/bignumber@>=5.0.0-beta.130", "@ethersproject/bignumber@^5.6.0", "@ethersproject/bignumber@^5.6.2": - version "5.6.2" - resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.6.2.tgz#72a0717d6163fab44c47bcc82e0c550ac0315d66" - integrity sha512-v7+EEUbhGqT3XJ9LMPsKvXYHFc8eHxTowFCG/HgJErmq4XHJ2WR7aeyICg3uTOAQ7Icn0GFHAohXEhxQHq4Ubw== - dependencies: - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - bn.js "^5.2.1" - -"@ethersproject/bytes@5.6.1", "@ethersproject/bytes@>=5.0.0-beta.129", "@ethersproject/bytes@^5.6.0", "@ethersproject/bytes@^5.6.1": +"@ethersproject/bytes@5.6.1", "@ethersproject/bytes@>=5.0.0-beta.129", "@ethersproject/bytes@^5.0.4", "@ethersproject/bytes@^5.0.8", "@ethersproject/bytes@^5.6.0": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.6.1.tgz#24f916e411f82a8a60412344bf4a813b917eefe7" integrity sha512-NwQt7cKn5+ZE4uDn+X5RAXLp46E1chXoaMmrxAyA0rblpxz8t58lVkrHXoRIn0lz1joQElQ8410GqhTqMOwc6g== dependencies: "@ethersproject/logger" "^5.6.0" -"@ethersproject/constants@5.6.0": +"@ethersproject/constants@5.6.0", "@ethersproject/constants@>=5.0.0-beta.128", "@ethersproject/constants@^5.0.4", "@ethersproject/constants@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.6.0.tgz#55e3eb0918584d3acc0688e9958b0cedef297088" integrity sha512-SrdaJx2bK0WQl23nSpV/b1aq293Lh0sUaZT/yYKPDKn4tlAbkH96SPJwIhwSwTsoQQZxuh1jnqsKwyymoiBdWA== dependencies: "@ethersproject/bignumber" "^5.6.0" -"@ethersproject/constants@5.6.1", "@ethersproject/constants@>=5.0.0-beta.128", "@ethersproject/constants@^5.6.0", "@ethersproject/constants@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.6.1.tgz#e2e974cac160dd101cf79fdf879d7d18e8cb1370" - integrity sha512-QSq9WVnZbxXYFftrjSjZDUshp6/eKp6qrtdBtUCm0QxCV5z1fG/w3kdlcsjMCQuQHUnAclKoK7XpXMezhRDOLg== - dependencies: - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/contracts@5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.6.0.tgz#60f2cfc7addd99a865c6c8cfbbcec76297386067" @@ -554,32 +576,16 @@ "@ethersproject/properties" "^5.6.0" "@ethersproject/transactions" "^5.6.0" -"@ethersproject/contracts@5.6.2": - version "5.6.2" - resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.6.2.tgz#20b52e69ebc1b74274ff8e3d4e508de971c287bc" - integrity sha512-hguUA57BIKi6WY0kHvZp6PwPlWF87MCeB4B7Z7AbUpTxfFXFdn/3b0GmjZPagIHS+3yhcBJDnuEfU4Xz+Ks/8g== - dependencies: - "@ethersproject/abi" "^5.6.3" - "@ethersproject/abstract-provider" "^5.6.1" - "@ethersproject/abstract-signer" "^5.6.2" - "@ethersproject/address" "^5.6.1" - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/constants" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/transactions" "^5.6.2" - -"@ethersproject/experimental@^5.4.0": - version "5.6.3" - resolved "https://registry.yarnpkg.com/@ethersproject/experimental/-/experimental-5.6.3.tgz#d1cd8f3b886cbab86430fb2954eb65ddb7c75ffd" - integrity sha512-yMymv32XMr9sXvHc3S1On2wD0JMT6n4X9uKpfZ8jFFw5rEcI99yfovcCZ0tpUedh1b3IvReSain+RobeNQmmEg== +"@ethersproject/experimental@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/experimental/-/experimental-5.6.0.tgz#c72ef00a79b746c522eb79736712169d71c55f64" + integrity sha512-lSEM/6t+BicbeyRxat5meoQhXZLoBEziVrxZqeCIhsPntvq4DlMobPBKXF0Iz3m0dMvl9uga7fHEO4YD9SgCgw== dependencies: - "@ethersproject/web" "^5.6.1" - ethers "^5.6.8" + "@ethersproject/web" "^5.6.0" + ethers "^5.6.0" scrypt-js "3.0.1" -"@ethersproject/hash@5.6.0": +"@ethersproject/hash@5.6.0", "@ethersproject/hash@>=5.0.0-beta.128", "@ethersproject/hash@^5.0.4", "@ethersproject/hash@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.6.0.tgz#d24446a5263e02492f9808baa99b6e2b4c3429a2" integrity sha512-fFd+k9gtczqlr0/BruWLAu7UAOas1uRRJvOR84uDf4lNZ+bTkGl366qvniUZHKtlqxBRU65MkOobkmvmpHU+jA== @@ -593,21 +599,7 @@ "@ethersproject/properties" "^5.6.0" "@ethersproject/strings" "^5.6.0" -"@ethersproject/hash@5.6.1", "@ethersproject/hash@>=5.0.0-beta.128", "@ethersproject/hash@^5.6.0", "@ethersproject/hash@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.6.1.tgz#224572ea4de257f05b4abf8ae58b03a67e99b0f4" - integrity sha512-L1xAHurbaxG8VVul4ankNX5HgQ8PNCTrnVXEiFnE9xoRnaUcgfD12tZINtDinSllxPLCtGwguQxJ5E6keE84pA== - dependencies: - "@ethersproject/abstract-signer" "^5.6.2" - "@ethersproject/address" "^5.6.1" - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/keccak256" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/strings" "^5.6.1" - -"@ethersproject/hdnode@5.6.0": +"@ethersproject/hdnode@5.6.0", "@ethersproject/hdnode@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.6.0.tgz#9dcbe8d629bbbcf144f2cae476337fe92d320998" integrity sha512-61g3Jp3nwDqJcL/p4nugSyLrpl/+ChXIOtCEM8UDmWeB3JCAt5FoLdOMXQc3WWkc0oM2C0aAn6GFqqMcS/mHTw== @@ -625,25 +617,7 @@ "@ethersproject/transactions" "^5.6.0" "@ethersproject/wordlists" "^5.6.0" -"@ethersproject/hdnode@5.6.2", "@ethersproject/hdnode@^5.6.0", "@ethersproject/hdnode@^5.6.2": - version "5.6.2" - resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.6.2.tgz#26f3c83a3e8f1b7985c15d1db50dc2903418b2d2" - integrity sha512-tERxW8Ccf9CxW2db3WsN01Qao3wFeRsfYY9TCuhmG0xNpl2IO8wgXU3HtWIZ49gUWPggRy4Yg5axU0ACaEKf1Q== - dependencies: - "@ethersproject/abstract-signer" "^5.6.2" - "@ethersproject/basex" "^5.6.1" - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/pbkdf2" "^5.6.1" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/sha2" "^5.6.1" - "@ethersproject/signing-key" "^5.6.2" - "@ethersproject/strings" "^5.6.1" - "@ethersproject/transactions" "^5.6.2" - "@ethersproject/wordlists" "^5.6.1" - -"@ethersproject/json-wallets@5.6.0": +"@ethersproject/json-wallets@5.6.0", "@ethersproject/json-wallets@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.6.0.tgz#4c2fc27f17e36c583e7a252fb938bc46f98891e5" integrity sha512-fmh86jViB9r0ibWXTQipxpAGMiuxoqUf78oqJDlCAJXgnJF024hOOX7qVgqsjtbeoxmcLwpPsXNU0WEe/16qPQ== @@ -662,26 +636,7 @@ aes-js "3.0.0" scrypt-js "3.0.1" -"@ethersproject/json-wallets@5.6.1", "@ethersproject/json-wallets@^5.6.0", "@ethersproject/json-wallets@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.6.1.tgz#3f06ba555c9c0d7da46756a12ac53483fe18dd91" - integrity sha512-KfyJ6Zwz3kGeX25nLihPwZYlDqamO6pfGKNnVMWWfEVVp42lTfCZVXXy5Ie8IZTN0HKwAngpIPi7gk4IJzgmqQ== - dependencies: - "@ethersproject/abstract-signer" "^5.6.2" - "@ethersproject/address" "^5.6.1" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/hdnode" "^5.6.2" - "@ethersproject/keccak256" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/pbkdf2" "^5.6.1" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/random" "^5.6.1" - "@ethersproject/strings" "^5.6.1" - "@ethersproject/transactions" "^5.6.2" - aes-js "3.0.0" - scrypt-js "3.0.1" - -"@ethersproject/keccak256@5.6.0": +"@ethersproject/keccak256@5.6.0", "@ethersproject/keccak256@>=5.0.0-beta.127", "@ethersproject/keccak256@^5.0.3", "@ethersproject/keccak256@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.6.0.tgz#fea4bb47dbf8f131c2e1774a1cecbfeb9d606459" integrity sha512-tk56BJ96mdj/ksi7HWZVWGjCq0WVl/QvfhFQNeL8fxhBlGoP+L80uDCiQcpJPd+2XxkivS3lwRm3E0CXTfol0w== @@ -689,34 +644,19 @@ "@ethersproject/bytes" "^5.6.0" js-sha3 "0.8.0" -"@ethersproject/keccak256@5.6.1", "@ethersproject/keccak256@>=5.0.0-beta.127", "@ethersproject/keccak256@^5.6.0", "@ethersproject/keccak256@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.6.1.tgz#b867167c9b50ba1b1a92bccdd4f2d6bd168a91cc" - integrity sha512-bB7DQHCTRDooZZdL3lk9wpL0+XuG3XLGHLh3cePnybsO3V0rdCAOQGpn/0R3aODmnTOOkCATJiD2hnL+5bwthA== - dependencies: - "@ethersproject/bytes" "^5.6.1" - js-sha3 "0.8.0" - -"@ethersproject/logger@5.6.0", "@ethersproject/logger@>=5.0.0-beta.129", "@ethersproject/logger@^5.6.0": +"@ethersproject/logger@5.6.0", "@ethersproject/logger@>=5.0.0-beta.129", "@ethersproject/logger@^5.0.5", "@ethersproject/logger@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.6.0.tgz#d7db1bfcc22fd2e4ab574cba0bb6ad779a9a3e7a" integrity sha512-BiBWllUROH9w+P21RzoxJKzqoqpkyM1pRnEKG69bulE9TSQD8SAIvTQqIMZmmCO8pUNkgLP1wndX1gKghSpBmg== -"@ethersproject/networks@5.6.1": +"@ethersproject/networks@5.6.1", "@ethersproject/networks@^5.6.0": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.6.1.tgz#7a21ed1f83e86121737b16841961ec99ccf5c9c7" integrity sha512-b2rrupf3kCTcc3jr9xOWBuHylSFtbpJf79Ga7QR98ienU2UqGimPGEsYMgbI29KHJfA5Us89XwGVmxrlxmSrMg== dependencies: "@ethersproject/logger" "^5.6.0" -"@ethersproject/networks@5.6.4", "@ethersproject/networks@^5.6.0", "@ethersproject/networks@^5.6.3": - version "5.6.4" - resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.6.4.tgz#51296d8fec59e9627554f5a8a9c7791248c8dc07" - integrity sha512-KShHeHPahHI2UlWdtDMn2lJETcbtaJge4k7XSjDR9h79QTd6yQJmv6Cp2ZA4JdqWnhszAOLSuJEd9C0PRw7hSQ== - dependencies: - "@ethersproject/logger" "^5.6.0" - -"@ethersproject/pbkdf2@5.6.0": +"@ethersproject/pbkdf2@5.6.0", "@ethersproject/pbkdf2@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.6.0.tgz#04fcc2d7c6bff88393f5b4237d906a192426685a" integrity sha512-Wu1AxTgJo3T3H6MIu/eejLFok9TYoSdgwRr5oGY1LTLfmGesDoSx05pemsbrPT2gG4cQME+baTSCp5sEo2erZQ== @@ -724,15 +664,7 @@ "@ethersproject/bytes" "^5.6.0" "@ethersproject/sha2" "^5.6.0" -"@ethersproject/pbkdf2@5.6.1", "@ethersproject/pbkdf2@^5.6.0", "@ethersproject/pbkdf2@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.6.1.tgz#f462fe320b22c0d6b1d72a9920a3963b09eb82d1" - integrity sha512-k4gRQ+D93zDRPNUfmduNKq065uadC2YjMP/CqwwX5qG6R05f47boq6pLZtV/RnC4NZAYOPH1Cyo54q0c9sshRQ== - dependencies: - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/sha2" "^5.6.1" - -"@ethersproject/properties@5.6.0", "@ethersproject/properties@>=5.0.0-beta.131", "@ethersproject/properties@^5.6.0": +"@ethersproject/properties@5.6.0", "@ethersproject/properties@>=5.0.0-beta.131", "@ethersproject/properties@^5.0.3", "@ethersproject/properties@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.6.0.tgz#38904651713bc6bdd5bdd1b0a4287ecda920fa04" integrity sha512-szoOkHskajKePTJSZ46uHUWWkbv7TzP2ypdEK6jGMqJaEt2sb0jCgfBo0gH0m2HBpRixMuJ6TBRaQCF7a9DoCg== @@ -764,33 +696,7 @@ bech32 "1.1.4" ws "7.4.6" -"@ethersproject/providers@5.6.8": - version "5.6.8" - resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.6.8.tgz#22e6c57be215ba5545d3a46cf759d265bb4e879d" - integrity sha512-Wf+CseT/iOJjrGtAOf3ck9zS7AgPmr2fZ3N97r4+YXN3mBePTG2/bJ8DApl9mVwYL+RpYbNxMEkEp4mPGdwG/w== - dependencies: - "@ethersproject/abstract-provider" "^5.6.1" - "@ethersproject/abstract-signer" "^5.6.2" - "@ethersproject/address" "^5.6.1" - "@ethersproject/base64" "^5.6.1" - "@ethersproject/basex" "^5.6.1" - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/constants" "^5.6.1" - "@ethersproject/hash" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/networks" "^5.6.3" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/random" "^5.6.1" - "@ethersproject/rlp" "^5.6.1" - "@ethersproject/sha2" "^5.6.1" - "@ethersproject/strings" "^5.6.1" - "@ethersproject/transactions" "^5.6.2" - "@ethersproject/web" "^5.6.1" - bech32 "1.1.4" - ws "7.4.6" - -"@ethersproject/random@5.6.0": +"@ethersproject/random@5.6.0", "@ethersproject/random@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.6.0.tgz#1505d1ab6a250e0ee92f436850fa3314b2cb5ae6" integrity sha512-si0PLcLjq+NG/XHSZz90asNf+YfKEqJGVdxoEkSukzbnBgC8rydbgbUgBbBGLeHN4kAJwUFEKsu3sCXT93YMsw== @@ -798,15 +704,7 @@ "@ethersproject/bytes" "^5.6.0" "@ethersproject/logger" "^5.6.0" -"@ethersproject/random@5.6.1", "@ethersproject/random@^5.6.0", "@ethersproject/random@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.6.1.tgz#66915943981bcd3e11bbd43733f5c3ba5a790255" - integrity sha512-/wtPNHwbmng+5yi3fkipA8YBT59DdkGRoC2vWk09Dci/q5DlgnMkhIycjHlavrvrjJBkFjO/ueLyT+aUDfc4lA== - dependencies: - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - -"@ethersproject/rlp@5.6.0": +"@ethersproject/rlp@5.6.0", "@ethersproject/rlp@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.6.0.tgz#55a7be01c6f5e64d6e6e7edb6061aa120962a717" integrity sha512-dz9WR1xpcTL+9DtOT/aDO+YyxSSdO8YIS0jyZwHHSlAmnxA6cKU3TrTd4Xc/bHayctxTgGLYNuVVoiXE4tTq1g== @@ -814,15 +712,7 @@ "@ethersproject/bytes" "^5.6.0" "@ethersproject/logger" "^5.6.0" -"@ethersproject/rlp@5.6.1", "@ethersproject/rlp@^5.6.0", "@ethersproject/rlp@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.6.1.tgz#df8311e6f9f24dcb03d59a2bac457a28a4fe2bd8" - integrity sha512-uYjmcZx+DKlFUk7a5/W9aQVaoEC7+1MOBgNtvNg13+RnuUwT4F0zTovC0tmay5SmRslb29V1B7Y5KCri46WhuQ== - dependencies: - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - -"@ethersproject/sha2@5.6.0": +"@ethersproject/sha2@5.6.0", "@ethersproject/sha2@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.6.0.tgz#364c4c11cc753bda36f31f001628706ebadb64d9" integrity sha512-1tNWCPFLu1n3JM9t4/kytz35DkuF9MxqkGGEHNauEbaARdm2fafnOyw1s0tIQDPKF/7bkP1u3dbrmjpn5CelyA== @@ -831,16 +721,7 @@ "@ethersproject/logger" "^5.6.0" hash.js "1.1.7" -"@ethersproject/sha2@5.6.1", "@ethersproject/sha2@^5.6.0", "@ethersproject/sha2@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.6.1.tgz#211f14d3f5da5301c8972a8827770b6fd3e51656" - integrity sha512-5K2GyqcW7G4Yo3uenHegbXRPDgARpWUiXc6RiF7b6i/HXUoWlb7uCARh7BAHg7/qT/Q5ydofNwiZcim9qpjB6g== - dependencies: - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - hash.js "1.1.7" - -"@ethersproject/signing-key@5.6.0": +"@ethersproject/signing-key@5.6.0", "@ethersproject/signing-key@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.6.0.tgz#4f02e3fb09e22b71e2e1d6dc4bcb5dafa69ce042" integrity sha512-S+njkhowmLeUu/r7ir8n78OUKx63kBdMCPssePS89So1TH4hZqnWFsThEd/GiXYp9qMxVrydf7KdM9MTGPFukA== @@ -852,18 +733,6 @@ elliptic "6.5.4" hash.js "1.1.7" -"@ethersproject/signing-key@5.6.2", "@ethersproject/signing-key@^5.6.0", "@ethersproject/signing-key@^5.6.2": - version "5.6.2" - resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.6.2.tgz#8a51b111e4d62e5a62aee1da1e088d12de0614a3" - integrity sha512-jVbu0RuP7EFpw82vHcL+GP35+KaNruVAZM90GxgQnGqB6crhBqW/ozBfFvdeImtmb4qPko0uxXjn8l9jpn0cwQ== - dependencies: - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - bn.js "^5.2.1" - elliptic "6.5.4" - hash.js "1.1.7" - "@ethersproject/solidity@5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.6.0.tgz#64657362a596bf7f5630bdc921c07dd78df06dc3" @@ -876,19 +745,7 @@ "@ethersproject/sha2" "^5.6.0" "@ethersproject/strings" "^5.6.0" -"@ethersproject/solidity@5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.6.1.tgz#5845e71182c66d32e6ec5eefd041fca091a473e2" - integrity sha512-KWqVLkUUoLBfL1iwdzUVlkNqAUIFMpbbeH0rgCfKmJp0vFtY4AsaN91gHKo9ZZLkC4UOm3cI3BmMV4N53BOq4g== - dependencies: - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/keccak256" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/sha2" "^5.6.1" - "@ethersproject/strings" "^5.6.1" - -"@ethersproject/strings@5.6.0": +"@ethersproject/strings@5.6.0", "@ethersproject/strings@>=5.0.0-beta.130", "@ethersproject/strings@^5.0.4", "@ethersproject/strings@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.6.0.tgz#9891b26709153d996bf1303d39a7f4bc047878fd" integrity sha512-uv10vTtLTZqrJuqBZR862ZQjTIa724wGPWQqZrofaPI/kUsf53TBG0I0D+hQ1qyNtllbNzaW+PDPHHUI6/65Mg== @@ -897,16 +754,7 @@ "@ethersproject/constants" "^5.6.0" "@ethersproject/logger" "^5.6.0" -"@ethersproject/strings@5.6.1", "@ethersproject/strings@>=5.0.0-beta.130", "@ethersproject/strings@^5.6.0", "@ethersproject/strings@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.6.1.tgz#dbc1b7f901db822b5cafd4ebf01ca93c373f8952" - integrity sha512-2X1Lgk6Jyfg26MUnsHiT456U9ijxKUybz8IM1Vih+NJxYtXhmvKBcHOmvGqpFSVJ0nQ4ZCoIViR8XlRw1v/+Cw== - dependencies: - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/constants" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - -"@ethersproject/transactions@5.6.0": +"@ethersproject/transactions@5.6.0", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.6.0.tgz#4b594d73a868ef6e1529a2f8f94a785e6791ae4e" integrity sha512-4HX+VOhNjXHZyGzER6E/LVI2i6lf9ejYeWD6l4g50AdmimyuStKc39kvKf1bXWQMg7QNVh+uC7dYwtaZ02IXeg== @@ -921,21 +769,6 @@ "@ethersproject/rlp" "^5.6.0" "@ethersproject/signing-key" "^5.6.0" -"@ethersproject/transactions@5.6.2", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.6.0", "@ethersproject/transactions@^5.6.2": - version "5.6.2" - resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.6.2.tgz#793a774c01ced9fe7073985bb95a4b4e57a6370b" - integrity sha512-BuV63IRPHmJvthNkkt9G70Ullx6AcM+SDc+a8Aw/8Yew6YwT51TcBKEp1P4oOQ/bP25I18JJr7rcFRgFtU9B2Q== - dependencies: - "@ethersproject/address" "^5.6.1" - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/constants" "^5.6.1" - "@ethersproject/keccak256" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/rlp" "^5.6.1" - "@ethersproject/signing-key" "^5.6.2" - "@ethersproject/units@5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.6.0.tgz#e5cbb1906988f5740254a21b9ded6bd51e826d9c" @@ -945,15 +778,6 @@ "@ethersproject/constants" "^5.6.0" "@ethersproject/logger" "^5.6.0" -"@ethersproject/units@5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.6.1.tgz#ecc590d16d37c8f9ef4e89e2005bda7ddc6a4e6f" - integrity sha512-rEfSEvMQ7obcx3KWD5EWWx77gqv54K6BKiZzKxkQJqtpriVsICrktIQmKl8ReNToPeIYPnFHpXvKpi068YFZXw== - dependencies: - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/constants" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/wallet@5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.6.0.tgz#33d11a806d783864208f348709a5a3badac8e22a" @@ -975,28 +799,7 @@ "@ethersproject/transactions" "^5.6.0" "@ethersproject/wordlists" "^5.6.0" -"@ethersproject/wallet@5.6.2": - version "5.6.2" - resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.6.2.tgz#cd61429d1e934681e413f4bc847a5f2f87e3a03c" - integrity sha512-lrgh0FDQPuOnHcF80Q3gHYsSUODp6aJLAdDmDV0xKCN/T7D99ta1jGVhulg3PY8wiXEngD0DfM0I2XKXlrqJfg== - dependencies: - "@ethersproject/abstract-provider" "^5.6.1" - "@ethersproject/abstract-signer" "^5.6.2" - "@ethersproject/address" "^5.6.1" - "@ethersproject/bignumber" "^5.6.2" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/hash" "^5.6.1" - "@ethersproject/hdnode" "^5.6.2" - "@ethersproject/json-wallets" "^5.6.1" - "@ethersproject/keccak256" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/random" "^5.6.1" - "@ethersproject/signing-key" "^5.6.2" - "@ethersproject/transactions" "^5.6.2" - "@ethersproject/wordlists" "^5.6.1" - -"@ethersproject/web@5.6.0": +"@ethersproject/web@5.6.0", "@ethersproject/web@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.6.0.tgz#4bf8b3cbc17055027e1a5dd3c357e37474eaaeb8" integrity sha512-G/XHj0hV1FxI2teHRfCGvfBUHFmU+YOSbCxlAMqJklxSa7QMiHFQfAxvwY2PFqgvdkxEKwRNr/eCjfAPEm2Ctg== @@ -1007,18 +810,7 @@ "@ethersproject/properties" "^5.6.0" "@ethersproject/strings" "^5.6.0" -"@ethersproject/web@5.6.1", "@ethersproject/web@^5.6.0", "@ethersproject/web@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.6.1.tgz#6e2bd3ebadd033e6fe57d072db2b69ad2c9bdf5d" - integrity sha512-/vSyzaQlNXkO1WV+RneYKqCJwualcUdx/Z3gseVovZP0wIlOFcCE1hkRhKBH8ImKbGQbMl9EAAyJFrJu7V0aqA== - dependencies: - "@ethersproject/base64" "^5.6.1" - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/strings" "^5.6.1" - -"@ethersproject/wordlists@5.6.0": +"@ethersproject/wordlists@5.6.0", "@ethersproject/wordlists@^5.6.0": version "5.6.0" resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.6.0.tgz#79e62c5276e091d8575f6930ba01a29218ded032" integrity sha512-q0bxNBfIX3fUuAo9OmjlEYxP40IB8ABgb7HjEZCL5IKubzV3j30CWi2rqQbjTS2HfoyQbfINoKcTVWP4ejwR7Q== @@ -1029,23 +821,12 @@ "@ethersproject/properties" "^5.6.0" "@ethersproject/strings" "^5.6.0" -"@ethersproject/wordlists@5.6.1", "@ethersproject/wordlists@^5.6.0", "@ethersproject/wordlists@^5.6.1": - version "5.6.1" - resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.6.1.tgz#1e78e2740a8a21e9e99947e47979d72e130aeda1" - integrity sha512-wiPRgBpNbNwCQFoCr8bcWO8o5I810cqO6mkdtKfLKFlLxeCWcnzDi4Alu8iyNzlhYuS9npCwivMbRWF19dyblw== - dependencies: - "@ethersproject/bytes" "^5.6.1" - "@ethersproject/hash" "^5.6.1" - "@ethersproject/logger" "^5.6.0" - "@ethersproject/properties" "^5.6.0" - "@ethersproject/strings" "^5.6.1" - -"@graphprotocol/common-ts@^1.6.0": - version "1.8.4" - resolved "https://registry.yarnpkg.com/@graphprotocol/common-ts/-/common-ts-1.8.4.tgz#a20e8126a7da34239904b0d3ad7c225d56d6cc17" - integrity sha512-qHAW4gNYBxD2SahGsKCzn1dmeVII+POws4dNQ5h8vvq4DWjdDzhUrb/qQWcmqmsZQ9MI4lQEoB/s5WdFMeRMNw== +"@graphprotocol/common-ts@^1.8.3": + version "1.8.6" + resolved "https://registry.yarnpkg.com/@graphprotocol/common-ts/-/common-ts-1.8.6.tgz#d12fe02d3f4ce544087cf9b57af26e95ca1b3b80" + integrity sha512-HSJ5hv1CZ1JSx0tGkEWrlG7G1QranrnipCmqkrl0U3gwfKss1XwCXuFtqMarE1NxMmYj46xKsrBm4/drlw7wLg== dependencies: - "@graphprotocol/contracts" "1.11.1" + "@graphprotocol/contracts" "1.13.0" "@graphprotocol/pino-sentry-simple" "0.7.1" "@urql/core" "2.4.4" "@urql/exchange-execute" "1.2.2" @@ -1067,10 +848,10 @@ prom-client "14.0.1" sequelize "6.19.0" -"@graphprotocol/contracts@1.11.1": - version "1.11.1" - resolved "https://registry.yarnpkg.com/@graphprotocol/contracts/-/contracts-1.11.1.tgz#22df943388b3349218b92d570722f663b890266e" - integrity sha512-z4klBGRf9X08iE+JQKHH5VfmPh/8btDVVof9pckDd5bSsnPG4TvvpDHJnPAw+TQRRtoiKmDIvNIGBajRTAtQzA== +"@graphprotocol/contracts@1.13.0": + version "1.13.0" + resolved "https://registry.yarnpkg.com/@graphprotocol/contracts/-/contracts-1.13.0.tgz#0289f9ef725f342ad1c69a9ac70bc5129c3eb0a3" + integrity sha512-1s6559hsvOQv6bbEGYOvkvuO4DkurwNKeHQ4wM3qT3j0v96sEd1xJSXL9fIOTPyg53BDfz8pZHe2+xaohXvVbg== dependencies: ethers "^5.4.4" @@ -1122,9 +903,9 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@metamask/eth-sig-util@^4.0.0": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz#3ad61f6ea9ad73ba5b19db780d40d9aae5157088" - integrity sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ== + version "4.0.0" + resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-4.0.0.tgz#11553ba06de0d1352332c1bde28c8edd00e0dcf6" + integrity sha512-LczOjjxY4A7XYloxzyxJIHONELmUxVZncpOLoClpEcTiebiVdM46KRPYXGuULro9oNNR2xdVx3yoKiQjdfWmoA== dependencies: ethereumjs-abi "^0.6.8" ethereumjs-util "^6.2.1" @@ -1137,15 +918,15 @@ resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" integrity sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw== -"@noble/hashes@1.1.2", "@noble/hashes@~1.1.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.2.tgz#e9e035b9b166ca0af657a7848eb2718f0f22f183" - integrity sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA== +"@noble/hashes@1.0.0", "@noble/hashes@~1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.0.0.tgz#d5e38bfbdaba174805a4e649f13be9a9ed3351ae" + integrity sha512-DZVbtY62kc3kkBtMHqwCOfXrT/hnoORy5BJ4+HU1IR59X0KWAOqsfzQPcUl/lQLlG7qXbe/fZ3r/emxtAl+sqg== -"@noble/secp256k1@1.6.3", "@noble/secp256k1@~1.6.0": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.6.3.tgz#7eed12d9f4404b416999d0c87686836c4c5c9b94" - integrity sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ== +"@noble/secp256k1@1.5.5", "@noble/secp256k1@~1.5.2": + version "1.5.5" + resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.5.5.tgz#315ab5745509d1a8c8e90d0bdf59823ccf9bcfc3" + integrity sha512-sZ1W6gQzYnu45wPrWx8D3kwI2/U29VYTx9OjbDAd7jwRItJ0cSTMPRL/C8AWZFn9kWFLQGqEXVEE86w4Z8LpIQ== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -1168,10 +949,31 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@nomiclabs/hardhat-ethers@^2.0.2", "@nomiclabs/hardhat-ethers@^2.0.6": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.1.0.tgz#9b7dc94d669ad9dc286b94f6f2f1513118c7027b" - integrity sha512-vlW90etB3675QWG7tMrHaDoTa7ymMB7irM4DAQ98g8zJoe9YqEggeDnbO6v5b+BLth/ty4vN6Ko/kaqRN1krHw== +"@nomiclabs/ethereumjs-vm@^4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@nomiclabs/ethereumjs-vm/-/ethereumjs-vm-4.2.2.tgz#2f8817113ca0fb6c44c1b870d0a809f0e026a6cc" + integrity sha512-8WmX94mMcJaZ7/m7yBbyuS6B+wuOul+eF+RY9fBpGhNaUpyMR/vFIcDojqcWQ4Yafe1tMKY5LDu2yfT4NZgV4Q== + dependencies: + async "^2.1.2" + async-eventemitter "^0.2.2" + core-js-pure "^3.0.1" + ethereumjs-account "^3.0.0" + ethereumjs-block "^2.2.2" + ethereumjs-blockchain "^4.0.3" + ethereumjs-common "^1.5.0" + ethereumjs-tx "^2.1.2" + ethereumjs-util "^6.2.0" + fake-merkle-patricia-tree "^1.0.1" + functional-red-black-tree "^1.0.1" + merkle-patricia-tree "3.0.0" + rustbn.js "~0.2.0" + safe-buffer "^5.1.1" + util.promisify "^1.0.0" + +"@nomiclabs/hardhat-ethers@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.0.5.tgz#131b0da1b71680d5a01569f916ae878229d326d3" + integrity sha512-A2gZAGB6kUvLx+kzM92HKuUF33F1FSe90L0TmkXkT2Hh0OKRpvWZURUSU2nghD2yC4DzfEZ3DftfeHGvZ2JTUw== "@nomiclabs/hardhat-etherscan@^2.1.1": version "2.1.8" @@ -1205,18 +1007,18 @@ integrity sha512-z0zMCjyhhp4y7XKAcDAi3Vgms4T2PstwBdahiO0+9NaGICQKjynK3wduSRplTgk4LXmoO1yfDGO5RbjKYxtuxA== "@openzeppelin/hardhat-upgrades@^1.6.0": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@openzeppelin/hardhat-upgrades/-/hardhat-upgrades-1.19.0.tgz#8f17210b924eb04c3ea4aa9795c1bb01d8a7dc3f" - integrity sha512-351QqDO3nZ96g0BO/bQnaTflix4Nw4ELWC/hZ8PwicsuVxRmxN/GZhxPrs62AOdiQdZ+xweawrVXa5knliRd4A== + version "1.17.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/hardhat-upgrades/-/hardhat-upgrades-1.17.0.tgz#24ea0f366c3b2df985263cf8b1b796afd04d7e13" + integrity sha512-GNxR3/3fCKQsFpBi/r+5ib6U81UM9KCypmcOQxuCkVp9JKJ80/3hQdg1R+AQku/dlnhutPsfkCokH2LZFc5mNA== dependencies: - "@openzeppelin/upgrades-core" "^1.16.0" + "@openzeppelin/upgrades-core" "^1.14.1" chalk "^4.1.0" proper-lockfile "^4.1.1" -"@openzeppelin/upgrades-core@^1.16.0": - version "1.16.1" - resolved "https://registry.yarnpkg.com/@openzeppelin/upgrades-core/-/upgrades-core-1.16.1.tgz#a4c383fc628cc9d37d5a276def99a093c951644b" - integrity sha512-+hejbeAfsZWIQL5Ih13gkdm2KO6kbERc1ektzcyb25/OtUwaRjIIHxW++LdC/3Hg5uzThVOzJBfiLdAbgwD+OA== +"@openzeppelin/upgrades-core@^1.14.1": + version "1.14.1" + resolved "https://registry.yarnpkg.com/@openzeppelin/upgrades-core/-/upgrades-core-1.14.1.tgz#a0e1c83f9811186ac49d286e6b43ae129097422b" + integrity sha512-iKlh1mbUxyfdjdEiUFyhMkqirfas+DMUu7ED53nZbHEyhcYsm+5Fl/g0Bv6bZA+a7k8kO8+22DNEKsqaDUBc2Q== dependencies: bn.js "^5.1.2" cbor "^8.0.0" @@ -1264,27 +1066,27 @@ path-browserify "^1.0.0" url "^0.11.0" -"@scure/base@~1.1.0": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" - integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== +"@scure/base@~1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.0.0.tgz#109fb595021de285f05a7db6806f2f48296fcee7" + integrity sha512-gIVaYhUsy+9s58m/ETjSJVKHhKTBMmcRb9cEV5/5dwvfDlfORjKrFsDeDHWRrm6RjcPvCLZFwGJjAjLj1gg4HA== -"@scure/bip32@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.1.0.tgz#dea45875e7fbc720c2b4560325f1cf5d2246d95b" - integrity sha512-ftTW3kKX54YXLCxH6BB7oEEoJfoE2pIgw7MINKAs5PsS6nqKPuKk1haTF/EuHmYqG330t5GSrdmtRuHaY1a62Q== +"@scure/bip32@1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.0.1.tgz#1409bdf9f07f0aec99006bb0d5827693418d3aa5" + integrity sha512-AU88KKTpQ+YpTLoicZ/qhFhRRIo96/tlb+8YmDDHR9yiKVjSsFZiefJO4wjS2PMTkz5/oIcw84uAq/8pleQURA== dependencies: - "@noble/hashes" "~1.1.1" - "@noble/secp256k1" "~1.6.0" - "@scure/base" "~1.1.0" + "@noble/hashes" "~1.0.0" + "@noble/secp256k1" "~1.5.2" + "@scure/base" "~1.0.0" -"@scure/bip39@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.0.tgz#92f11d095bae025f166bef3defcc5bf4945d419a" - integrity sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w== +"@scure/bip39@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.0.0.tgz#47504e58de9a56a4bbed95159d2d6829fa491bb0" + integrity sha512-HrtcikLbd58PWOkl02k9V6nXWQyoa7A0+Ek9VF7z17DDk9XZAFUcIdqfh0jJXLypmizc5/8P6OxoUeKliiWv4w== dependencies: - "@noble/hashes" "~1.1.1" - "@scure/base" "~1.1.0" + "@noble/hashes" "~1.0.0" + "@scure/base" "~1.0.0" "@sentry/core@5.30.0": version "5.30.0" @@ -1359,7 +1161,14 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== -"@solidity-parser/parser@^0.14.0", "@solidity-parser/parser@^0.14.1", "@solidity-parser/parser@^0.14.2", "@solidity-parser/parser@^0.14.3": +"@solidity-parser/parser@^0.14.0", "@solidity-parser/parser@^0.14.1": + version "0.14.1" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.14.1.tgz#179afb29f4e295a77cc141151f26b3848abc3c46" + integrity sha512-eLjj2L6AuQjBB6s/ibwCAc0DwrR5Ge+ys+wgWo+bviU7fV2nTMQhU63CGaDKXg9iTmMxwhkyoggdIR7ZGRfMgw== + dependencies: + antlr4ts "^0.5.0-alpha.4" + +"@solidity-parser/parser@^0.14.2": version "0.14.3" resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.14.3.tgz#0d627427b35a40d8521aaa933cc3df7d07bfa36f" integrity sha512-29g2SZ29HtsqA58pLCtopI1P/cPy5/UAzlcAXO6T/CNJimG6yA8kx4NaseMyJULiC+TEs02Y9/yeHzClqoA0hw== @@ -1374,15 +1183,12 @@ defer-to-connect "^1.0.1" "@tenderly/hardhat-tenderly@^1.0.11": - version "1.1.4" - resolved "https://registry.yarnpkg.com/@tenderly/hardhat-tenderly/-/hardhat-tenderly-1.1.4.tgz#cb84ebf05eaf75c6fffae4cee52d132b3eed986a" - integrity sha512-HqtJSaX/EVxUcNqoHSBtZzrwAF93hPXBV4buPU2UWCuL36na6wImqzjSVhrxghgrxP6MQv5BbU5THxOkKQ0gdw== + version "1.0.13" + resolved "https://registry.yarnpkg.com/@tenderly/hardhat-tenderly/-/hardhat-tenderly-1.0.13.tgz#6182a2d32bf12d110622f0b24263dc964ed7aa6d" + integrity sha512-XsrF2QIUh8YmzCcWHmPnSNQjZNBQkyrHER8bcrWsFIgL7ub49hmPbpGVB2Isb6gUV/IGBpBm7R69jpmoTvJ17g== dependencies: - "@nomiclabs/hardhat-ethers" "^2.0.6" axios "^0.21.1" - ethers "^5.6.8" fs-extra "^9.0.1" - hardhat-deploy "^0.11.10" js-yaml "^3.14.0" "@truffle/error@^0.1.0": @@ -1390,44 +1196,43 @@ resolved "https://registry.yarnpkg.com/@truffle/error/-/error-0.1.0.tgz#5e9fed79e6cda624c926d314b280a576f8b22a36" integrity sha512-RbUfp5VreNhsa2Q4YbBjz18rOQI909pG32bghl1hulO7IpvcqTS+C3Ge5cNbiWQ1WGzy1wIeKLW0tmQtHFB7qg== -"@truffle/interface-adapter@^0.5.19": - version "0.5.19" - resolved "https://registry.yarnpkg.com/@truffle/interface-adapter/-/interface-adapter-0.5.19.tgz#57529cacb09c72ebfd584ec003af55face18a3de" - integrity sha512-x7IZvsyx36DAJCJVZ9gUe1Lh8AhODhJoW7I+lJXIlGxj3EmZbao4/sHo+cN4u9i94yVTyGwYd78NzbP0a/LAog== +"@truffle/interface-adapter@^0.5.12": + version "0.5.12" + resolved "https://registry.yarnpkg.com/@truffle/interface-adapter/-/interface-adapter-0.5.12.tgz#8cc34e9a5363970bfec1001520ae55fad6a26bfd" + integrity sha512-Qrc5VARnvSILYqZNsAM0xsUHqGqphLXVdIvDnhUA1Xj1xyNz8iboTr8bXorMd+Uspw+PXmsW44BJ/Wioo/jL2A== dependencies: bn.js "^5.1.3" ethers "^4.0.32" - web3 "1.7.4" + web3 "1.5.3" "@truffle/provider@^0.2.24": - version "0.2.57" - resolved "https://registry.yarnpkg.com/@truffle/provider/-/provider-0.2.57.tgz#c6d079748c99427c1ce283a19921b450aa9920ee" - integrity sha512-O8VxF2uQwa+KB4HDg9lG7uhQ/+AOvchX+1STpQBSSAGfov1+EROM0iRZUNoPm71Pu0C9ji2WmXbNW/COjUMaMA== + version "0.2.50" + resolved "https://registry.yarnpkg.com/@truffle/provider/-/provider-0.2.50.tgz#5c8a8fd7724bacac15cf7396cecc51c0ee1e597d" + integrity sha512-GCoyX8SncfgizXYJfordv5kiysQS7S1311D3ewciixaoQpTkbqC3s0wxwHlPxU7m5wJOCw2K8rQtL3oIFfeHwA== dependencies: "@truffle/error" "^0.1.0" - "@truffle/interface-adapter" "^0.5.19" - debug "^4.3.1" - web3 "1.7.4" + "@truffle/interface-adapter" "^0.5.12" + web3 "1.5.3" "@tsconfig/node10@^1.0.7": - version "1.0.9" - resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" - integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== + version "1.0.8" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" + integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg== "@tsconfig/node12@^1.0.7": - version "1.0.11" - resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" - integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c" + integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw== "@tsconfig/node14@^1.0.0": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" - integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + version "1.0.1" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2" + integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== "@tsconfig/node16@^1.0.2": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" - integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== + version "1.0.2" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" + integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== "@typechain/ethers-v5@^2.0.0": version "2.0.0" @@ -1485,9 +1290,9 @@ "@types/chai" "*" "@types/chai@*": - version "4.3.1" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.1.tgz#e2c6e73e0bdeb2521d00756d099218e9f5d90a04" - integrity sha512-/zPMqDkzSZ8t3VtxOa4KPq7uzzW978M9Tvh+j7GHKuo6k6GTLxPJ4J5gE5cjfJ26pnXst0N5Hax8Sr0T2Mi9zQ== + version "4.3.0" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.0.tgz#23509ebc1fa32f1b4d50d6a66c4032d5b8eaabdc" + integrity sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw== "@types/concat-stream@^1.6.0": version "1.6.1" @@ -1585,17 +1390,17 @@ integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== "@types/node-fetch@^2.5.5": - version "2.6.2" - resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.2.tgz#d1a9c5fd049d9415dce61571557104dec3ec81da" - integrity sha512-DHqhlq5jeESLy19TYhLakJ07kNumXWjcDdxXsLUMJZ6ue8VZJj4kLPQVE/2mdHh3xZziNF1xppu5lwmS53HR+A== + version "2.6.1" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.1.tgz#8f127c50481db65886800ef496f20bbf15518975" + integrity sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA== dependencies: "@types/node" "*" form-data "^3.0.0" "@types/node@*": - version "18.0.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.6.tgz#0ba49ac517ad69abe7a1508bc9b3a5483df9d5d7" - integrity sha512-/xUq6H2aQm261exT6iZTMifUySEt4GR5KX8eYyY+C4MSNPqSh9oNIP7tz2GLKTlFaiBbgZNxffoR3CVRG+cljw== + version "17.0.23" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.23.tgz#3b41a6e643589ac6442bdbd7a4a3ded62f33f7da" + integrity sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw== "@types/node@^10.0.3": version "10.17.60" @@ -1603,9 +1408,9 @@ integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw== "@types/node@^12.12.6": - version "12.20.55" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" - integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== + version "12.20.47" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.47.tgz#ca9237d51f2a2557419688511dab1c8daf475188" + integrity sha512-BzcaRsnFuznzOItW1WpQrDHM7plAa7GIDMZ6b5pnMbkqEtM/6WCOhvZar39oeMQP79gwvFUWjjptE7/KGcNqFg== "@types/node@^15.0.1": version "15.14.9" @@ -1635,11 +1440,11 @@ "@types/node" "*" "@types/prettier@^2.1.1": - version "2.6.3" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.3.tgz#68ada76827b0010d0db071f739314fa429943d0a" - integrity sha512-ymZk3LEC/fsut+/Q5qejp6R9O1rMxz3XaRHDV6kX8MrGAhOSPqVARbDi+EZvInBpw+BnCX3TD240byVkOfQsHg== + version "2.4.4" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.4.4.tgz#5d9b63132df54d8909fce1c3f8ca260fdd693e17" + integrity sha512-ReVR2rLTV1kvtlWFyuot+d1pkpG2Fw/XKE3PDAdj57rbM97ttSp9JZ2UsP+2EHTylra9cUf6JA7tGwW1INzUrA== -"@types/qs@^6.2.31", "@types/qs@^6.9.7": +"@types/qs@^6.2.31": version "6.9.7" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== @@ -1667,9 +1472,9 @@ "@types/sinon" "*" "@types/sinon@*": - version "10.0.13" - resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-10.0.13.tgz#60a7a87a70d9372d0b7b38cc03e825f46981fb83" - integrity sha512-UVjDqJblVNQYvVNUsj0PuYYw0ELRmgt1Nt5Vk0pT5f16ROGfcKJY8o1HVuMOJOpD727RrGB9EGvoaTQE5tgxZQ== + version "10.0.11" + resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-10.0.11.tgz#8245827b05d3fc57a6601bd35aee1f7ad330fc42" + integrity sha512-dmZsHlBsKUtBpHriNjlK0ndlvEh8dcb9uV9Afsbt89QIyydpC7NcR+nWlAhASfy3GHnxTl4FX/aKE7XZUt/B4g== dependencies: "@types/sinonjs__fake-timers" "*" @@ -1691,9 +1496,9 @@ integrity sha512-uO4CD2ELOjw8tasUrAhvnn2W4A0ZECOvMjCivJr4gA9pGgjv+qxKWY9GLTMVEK8ej85BxQOocUyE7hImmSQYcg== "@types/validator@^13.7.1": - version "13.7.4" - resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.4.tgz#33cc949ee87dd47c63e35ba4ad94f6888852be04" - integrity sha512-uAaSWegu2lymY18l+s5nmcXu3sFeeTOl1zhSGoYzcr6T3wz1M+3OcW4UjfPhIhHGd13tIMRDsEpR+d8w/MexwQ== + version "13.7.6" + resolved "https://registry.yarnpkg.com/@types/validator/-/validator-13.7.6.tgz#631f1acd15cbac9cb0a114da7e87575f1c95b46a" + integrity sha512-uBsnWETsUagQ0n6G2wcXNIufpTNJir0zqzG4p62fhnwzs48d/iuOWEEo0d3iUxN7D+9R/8CSvWGKS+KmaD0mWA== "@types/web3@1.0.19": version "1.0.19" @@ -1806,9 +1611,9 @@ wonka "^4.0.14" "@urql/core@>=2.3.6", "@urql/core@^2.1.3": - version "2.6.0" - resolved "https://registry.yarnpkg.com/@urql/core/-/core-2.6.0.tgz#1ce8b3a471312986378ce42ea800478595239315" - integrity sha512-JuIbzDu2oASSe+m5u410ceqWQGTF6QK5W/UZaHuzQX+I2wX4aVdPN/NeZnaFFqG3UYw3+ttFHpQK0vrsU6QLFw== + version "2.4.3" + resolved "https://registry.yarnpkg.com/@urql/core/-/core-2.4.3.tgz#af35355cd2e3eeef4657f91616098e7cba8154dc" + integrity sha512-FpapxUKF0nLdzRLoB1QsudDjeLXJhBwzkzl8bSOJ6Cnj7LRRKJ+dYdqHfqGykswB/ILrkZks2Isp4a4BhqyUow== dependencies: "@graphql-typed-document-node/core" "^3.1.1" wonka "^4.0.14" @@ -1935,9 +1740,9 @@ acorn@^7.4.0: integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.4.1: - version "8.8.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" - integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== + version "8.7.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf" + integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ== address@^1.0.1: version "1.2.0" @@ -2090,6 +1895,13 @@ anymatch@~3.1.1, anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +arbos-precompiles@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/arbos-precompiles/-/arbos-precompiles-1.0.2.tgz#7bebd5963aef972cd259eb41f3116ea065013ea6" + integrity sha512-1dOFYFJUN0kKoofh6buZJ8qCqTs+oLGSsGzHI0trA/Pka/TCERflCRsNVxez2lihOvK7MT/a2RA8AepKtBXdPQ== + dependencies: + hardhat "^2.6.4" + arg@^4.1.0: version "4.1.3" resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" @@ -2154,24 +1966,13 @@ array-union@^2.1.0: array-uniq@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" - integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q== + integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== -array.prototype.reduce@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.4.tgz#8167e80089f78bff70a99e20bd4201d4663b0a6f" - integrity sha512-WnM+AjG/DvLRLo4DDl+r+SvCzYtD2Jd9oeBYMcEaI7t3fFrHY9M53/wdLcTvmZNQ70IU6Htj0emFkZ5TS+lrdw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.2" - es-array-method-boxes-properly "^1.0.0" - is-string "^1.0.7" - arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -2261,9 +2062,9 @@ async@^2.0.1, async@^2.1.2, async@^2.4.0, async@^2.5.0, async@^2.6.1: lodash "^4.17.14" async@^3.2.3: - version "3.2.4" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" - integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== + version "3.2.3" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9" + integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g== asynckit@^0.4.0: version "0.4.0" @@ -2944,10 +2745,10 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.10.0, bn.js@^4.11.0, bn.js@^4.11.6, bn.js@^ resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== -bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.1.3, bn.js@^5.2.0, bn.js@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" - integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== +bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.1.3, bn.js@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" + integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== body-parser@1.19.1: version "1.19.1" @@ -2981,7 +2782,7 @@ body-parser@1.19.2: raw-body "2.4.3" type-is "~1.6.18" -body-parser@1.20.0, body-parser@^1.16.0: +body-parser@^1.16.0: version "1.20.0" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.0.tgz#3de69bd89011c11573d7bfee6a64f11b6bd27cc5" integrity sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg== @@ -3303,9 +3104,9 @@ camelcase@^6.0.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30000844: - version "1.0.30001368" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001368.tgz#c5c06381c6051cd863c45021475434e81936f713" - integrity sha512-wgfRYa9DenEomLG/SdWgQxpIyvdtH3NW8Vq+tB6AwR9e56iOIcu1im5F/wNdDf04XlKHXqIx4N8Jo0PemeBenQ== + version "1.0.30001325" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001325.tgz#2b4ad19b77aa36f61f2eaf72e636d7481d55e606" + integrity sha512-sB1bZHjseSjDtijV1Hb7PB2Zd58Kyx+n/9EotvZ4Qcz2K3d0lWB8dB4nb8wN/TsOGFq3UuAm0zQZNQ4SoR7TrQ== caseless@^0.12.0, caseless@~0.12.0: version "0.12.0" @@ -3367,7 +3168,7 @@ chalk@^2.0.0, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: +chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -3412,7 +3213,7 @@ chokidar@3.3.0: optionalDependencies: fsevents "~2.1.1" -chokidar@3.5.3, chokidar@^3.4.0, chokidar@^3.5.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" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -3516,13 +3317,13 @@ cli-table3@^0.5.0: colors "^1.1.2" cli-table3@^0.6.0: - version "0.6.2" - resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.2.tgz#aaf5df9d8b5bf12634dc8b3040806a0c07120d2a" - integrity sha512-QyavHCaIC80cMivimWu4aWHilIpiDpfm3hGmqAmXVL1UsnbLuBSMd21hTX6VY4ZSDSM73ESLeF8TOYId3rBTbw== + version "0.6.1" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.1.tgz#36ce9b7af4847f288d3cdd081fbd09bf7bd237b8" + integrity sha512-w0q/enDHhPLq44ovMGdQeeDLvwxwavsJX7oQGYt/LrBlYsyaxyDnp6z3QzFut/6kLLKnlcUVJLrpB7KBfgG/RA== dependencies: string-width "^4.2.0" optionalDependencies: - "@colors/colors" "1.5.0" + colors "1.4.0" cli-table@^0.3.6: version "0.3.11" @@ -3631,9 +3432,9 @@ color-name@^1.0.0, color-name@~1.1.4: integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== color-string@^1.6.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" - integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== + version "1.9.0" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.0.tgz#63b6ebd1bec11999d1df3a79a7569451ac2be8aa" + integrity sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ== dependencies: color-name "^1.0.0" simple-swizzle "^0.2.2" @@ -3647,9 +3448,9 @@ color@^3.1.3: color-string "^1.6.0" colorette@^2.0.16: - version "2.0.19" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798" - integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ== + version "2.0.16" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.16.tgz#713b9af84fdb000139f04546bd4a93f62a5085da" + integrity sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g== colors@1.0.3: version "1.0.3" @@ -3817,11 +3618,6 @@ cookie@0.4.2, cookie@^0.4.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== -cookie@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" - integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== - cookiejar@^2.1.1: version "2.1.3" resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.3.tgz#fc7a6216e408e74414b90230050842dacda75acc" @@ -3833,9 +3629,9 @@ copy-descriptor@^0.1.0: integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== core-js-pure@^3.0.1: - version "3.23.5" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.23.5.tgz#23daaa9af9230e50f10b0fa4b8e6b87402be4c33" - integrity sha512-8t78LdpKSuCq4pJYCYk8hl7XEkAX+BP16yRIwL3AanTksxuEf7CM83vRyctmiEL8NDZ3jpUcv56fk9/zG3aIuw== + version "3.21.1" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.21.1.tgz#8c4d1e78839f5f46208de7230cebfb72bc3bdb51" + integrity sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ== core-js@^2.4.0, core-js@^2.5.0: version "2.6.12" @@ -4018,13 +3814,20 @@ debug@3.2.6: dependencies: ms "^2.1.1" -debug@4, debug@4.3.4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: +debug@4, debug@4.3.4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.3, debug@^4.3.4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" +debug@4.3.3: + version "4.3.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" + integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== + dependencies: + ms "2.1.2" + debug@^3.1.0: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -4189,7 +3992,7 @@ depd@2.0.0, depd@~2.0.0: depd@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= des.js@^1.0.0: version "1.0.1" @@ -4229,7 +4032,7 @@ diff@3.5.0: resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== -diff@5.0.0: +diff@5.0.0, diff@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== @@ -4284,9 +4087,9 @@ dot-prop@^5.1.0: is-obj "^2.0.0" dotenv@*: - version "16.0.1" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.1.tgz#8f8f9d94876c35dac989876a5d3a82a267fdce1d" - integrity sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ== + version "16.0.0" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.0.tgz#c619001253be89ebb638d027b609c75c26e47411" + integrity sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q== dotenv@^9.0.0: version "9.0.2" @@ -4334,9 +4137,9 @@ ee-first@1.1.1: integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== electron-to-chromium@^1.3.47: - version "1.4.196" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.196.tgz#e18cdc5c1c2c2ebf78da237d0c374cc3b244d4cb" - integrity sha512-uxMa/Dt7PQsLBVXwH+t6JvpHJnrsYBaxWKi/J6HE+/nBtoHENhwBoNkgkm226/Kfxeg0z1eMQLBRPPKcDH8xWA== + version "1.4.104" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.104.tgz#60973b0a7d398efa877196e8ccb0c93d48b918d8" + integrity sha512-2kjoAyiG7uMyGRM9mx25s3HAzmQG2ayuYXxsFmYugHSDcwxREgLtscZvbL1JcW9S/OemeQ3f/SG6JhDwpnCclQ== elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5.4: version "6.5.4" @@ -4351,7 +4154,7 @@ elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.2, elliptic@^6.5.3, elliptic@^6.5 minimalistic-assert "^1.0.1" minimalistic-crypto-utils "^1.0.1" -emoji-regex@^10.1.0: +emoji-regex@^10.0.0: version "10.1.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.1.0.tgz#d50e383743c0f7a5945c47087295afc112e3cf66" integrity sha512-xAEnNCT3w2Tg6MA7ly6QqYJvEoY1tm9iIjJ3yMKK9JPlWuRHAMoe5iETwQnx3M9TVbFMfsrBgWKR+IsmswwNjg== @@ -4371,11 +4174,6 @@ enabled@2.0.x: resolved "https://registry.yarnpkg.com/enabled/-/enabled-2.0.0.tgz#f9dd92ec2d6f4bbc0d5d1e64e21d61cd4665e7c2" integrity sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ== -encode-utf8@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/encode-utf8/-/encode-utf8-1.0.3.tgz#f30fdd31da07fb596f281beb2f6b027851994cda" - integrity sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw== - encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -4452,7 +4250,33 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.19.0, es-abstract@^1.19.2, es-abstract@^1.19.5, es-abstract@^1.20.0, es-abstract@^1.20.1: +es-abstract@^1.18.5, es-abstract@^1.19.1: + version "1.19.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.2.tgz#8f7b696d8f15b167ae3640b4060670f3d054143f" + integrity sha512-gfSBJoZdlL2xRiOCy0g8gLMryhoe1TlimjzU99L/31Z8QEGIhVQI+EWwt5lT+AuU9SnorVupXFqqOGqGfsyO6w== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + get-intrinsic "^1.1.1" + get-symbol-description "^1.0.0" + has "^1.0.3" + has-symbols "^1.0.3" + internal-slot "^1.0.3" + is-callable "^1.2.4" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.1" + is-string "^1.0.7" + is-weakref "^1.0.2" + object-inspect "^1.12.0" + object-keys "^1.1.1" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.4" + string.prototype.trimstart "^1.0.4" + unbox-primitive "^1.0.1" + +es-abstract@^1.19.0, es-abstract@^1.19.5: version "1.20.1" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814" integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA== @@ -4479,12 +4303,7 @@ es-abstract@^1.19.0, es-abstract@^1.19.2, es-abstract@^1.19.5, es-abstract@^1.20 regexp.prototype.flags "^1.4.3" string.prototype.trimend "^1.0.5" string.prototype.trimstart "^1.0.5" - unbox-primitive "^1.0.2" - -es-array-method-boxes-properly@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" - integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== + unbox-primitive "^1.0.2" es-to-primitive@^1.2.1: version "1.2.1" @@ -4496,9 +4315,9 @@ es-to-primitive@^1.2.1: is-symbol "^1.0.2" es5-ext@^0.10.35, es5-ext@^0.10.50: - version "0.10.61" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.61.tgz#311de37949ef86b6b0dcea894d1ffedb909d3269" - integrity sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA== + version "0.10.59" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.59.tgz#71038939730eb6f4f165f1421308fb60be363bc6" + integrity sha512-cOgyhW0tIJyQY1Kfw6Kr0viu9ZlUctVchRMZ7R0HiH3dxTSp5zJDLecwxUqPUrGKMsgBI1wd1FL+d9Jxfi4cLw== dependencies: es6-iterator "^2.0.3" es6-symbol "^3.1.3" @@ -4935,7 +4754,7 @@ ethereum-common@^0.0.18: resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f" integrity sha512-EoltVQTRNg2Uy4o84qpa2aXymXDJhxm7eos/ACOg0DG4baAbMjhbdAEsx9GeE8sC3XCxnYvrrzZDH8D8MtA2iQ== -ethereum-cryptography@^0.1.3: +ethereum-cryptography@^0.1.2, ethereum-cryptography@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== @@ -4957,14 +4776,14 @@ ethereum-cryptography@^0.1.3: setimmediate "^1.0.5" ethereum-cryptography@^1.0.3: - version "1.1.2" - resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-1.1.2.tgz#74f2ac0f0f5fe79f012c889b3b8446a9a6264e6d" - integrity sha512-XDSJlg4BD+hq9N2FjvotwUET9Tfxpxc3kWGE2AqUG5vcbeunnbImVk3cj6e/xT3phdW21mE8R5IugU4fspQDcQ== + version "1.0.3" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-1.0.3.tgz#b1f8f4e702434b2016248dbb2f9fdd60c54772d8" + integrity sha512-NQLTW0x0CosoVb/n79x/TRHtfvS3hgNUPTUSCu0vM+9k6IIhHFFrAOJReneexjZsoZxMjJHnJn4lrE8EbnSyqQ== dependencies: - "@noble/hashes" "1.1.2" - "@noble/secp256k1" "1.6.3" - "@scure/bip32" "1.1.0" - "@scure/bip39" "1.1.0" + "@noble/hashes" "1.0.0" + "@noble/secp256k1" "1.5.5" + "@scure/bip32" "1.0.1" + "@scure/bip39" "1.0.0" ethereum-waffle@^3.3.0: version "3.4.4" @@ -5119,7 +4938,18 @@ ethereumjs-util@^5.0.0, ethereumjs-util@^5.0.1, ethereumjs-util@^5.1.1, ethereum rlp "^2.0.0" safe-buffer "^5.1.1" -ethereumjs-util@^7.0.10, ethereumjs-util@^7.0.2, ethereumjs-util@^7.0.3, ethereumjs-util@^7.1.0, ethereumjs-util@^7.1.1, ethereumjs-util@^7.1.4, ethereumjs-util@^7.1.5: +ethereumjs-util@^7.0.10, ethereumjs-util@^7.0.2, ethereumjs-util@^7.0.3, ethereumjs-util@^7.1.0, ethereumjs-util@^7.1.1, ethereumjs-util@^7.1.3, ethereumjs-util@^7.1.4: + version "7.1.4" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.4.tgz#a6885bcdd92045b06f596c7626c3e89ab3312458" + integrity sha512-p6KmuPCX4mZIqsQzXfmSx9Y0l2hqf+VkAiwSisW3UKUFdk8ZkAt+AYaor83z2nSi6CU2zSsXMlD80hAbNEGM0A== + dependencies: + "@types/bn.js" "^5.1.0" + bn.js "^5.1.2" + create-hash "^1.1.2" + ethereum-cryptography "^0.1.3" + rlp "^2.2.4" + +ethereumjs-util@^7.1.5: version "7.1.5" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz#9ecf04861e4fbbeed7465ece5f23317ad1129181" integrity sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg== @@ -5183,7 +5013,7 @@ ethereumjs-wallet@0.6.5: utf8 "^3.0.0" uuid "^3.3.2" -ethers@5.6.2: +ethers@5.6.2, ethers@^5.0.1, ethers@^5.0.2, ethers@^5.0.24, ethers@^5.1.0, ethers@^5.4.4, ethers@^5.5.2, ethers@^5.6.0: version "5.6.2" resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.6.2.tgz#e75bac7f038c5e0fdde667dba62fc223924143a2" integrity sha512-EzGCbns24/Yluu7+ToWnMca3SXJ1Jk1BvWB7CCmVNxyOeM4LLvw2OLuIHhlkhQk1dtOcj9UMsdkxUh8RiG1dxQ== @@ -5234,42 +5064,6 @@ ethers@^4.0.32, ethers@^4.0.40: uuid "2.0.1" xmlhttprequest "1.8.0" -ethers@^5.0.1, ethers@^5.0.2, ethers@^5.0.24, ethers@^5.4.4, ethers@^5.5.2, ethers@^5.5.3, ethers@^5.6.8: - version "5.6.9" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.6.9.tgz#4e12f8dfcb67b88ae7a78a9519b384c23c576a4d" - integrity sha512-lMGC2zv9HC5EC+8r429WaWu3uWJUCgUCt8xxKCFqkrFuBDZXDYIdzDUECxzjf2BMF8IVBByY1EBoGSL3RTm8RA== - dependencies: - "@ethersproject/abi" "5.6.4" - "@ethersproject/abstract-provider" "5.6.1" - "@ethersproject/abstract-signer" "5.6.2" - "@ethersproject/address" "5.6.1" - "@ethersproject/base64" "5.6.1" - "@ethersproject/basex" "5.6.1" - "@ethersproject/bignumber" "5.6.2" - "@ethersproject/bytes" "5.6.1" - "@ethersproject/constants" "5.6.1" - "@ethersproject/contracts" "5.6.2" - "@ethersproject/hash" "5.6.1" - "@ethersproject/hdnode" "5.6.2" - "@ethersproject/json-wallets" "5.6.1" - "@ethersproject/keccak256" "5.6.1" - "@ethersproject/logger" "5.6.0" - "@ethersproject/networks" "5.6.4" - "@ethersproject/pbkdf2" "5.6.1" - "@ethersproject/properties" "5.6.0" - "@ethersproject/providers" "5.6.8" - "@ethersproject/random" "5.6.1" - "@ethersproject/rlp" "5.6.1" - "@ethersproject/sha2" "5.6.1" - "@ethersproject/signing-key" "5.6.2" - "@ethersproject/solidity" "5.6.1" - "@ethersproject/strings" "5.6.1" - "@ethersproject/transactions" "5.6.2" - "@ethersproject/units" "5.6.1" - "@ethersproject/wallet" "5.6.2" - "@ethersproject/web" "5.6.1" - "@ethersproject/wordlists" "5.6.1" - ethjs-unit@0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" @@ -5357,7 +5151,7 @@ expand-brackets@^2.1.4: snapdragon "^0.8.1" to-regex "^3.0.1" -express@4.17.3: +express@4.17.3, express@^4.14.0: version "4.17.3" resolved "https://registry.yarnpkg.com/express/-/express-4.17.3.tgz#f6c7302194a4fb54271b73a1fe7a06478c8f85a1" integrity sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg== @@ -5393,43 +5187,6 @@ express@4.17.3: utils-merge "1.0.1" vary "~1.1.2" -express@^4.14.0: - version "4.18.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.18.1.tgz#7797de8b9c72c857b9cd0e14a5eea80666267caf" - integrity sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q== - dependencies: - accepts "~1.3.8" - array-flatten "1.1.1" - body-parser "1.20.0" - content-disposition "0.5.4" - content-type "~1.0.4" - cookie "0.5.0" - cookie-signature "1.0.6" - debug "2.6.9" - depd "2.0.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "1.2.0" - fresh "0.5.2" - http-errors "2.0.0" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "2.4.1" - parseurl "~1.3.3" - path-to-regexp "0.1.7" - proxy-addr "~2.0.7" - qs "6.10.3" - range-parser "~1.2.1" - safe-buffer "5.2.1" - send "0.18.0" - serve-static "1.15.0" - setprototypeof "1.2.0" - statuses "2.0.1" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - ext@^1.1.2: version "1.6.0" resolved "https://registry.yarnpkg.com/ext/-/ext-1.6.0.tgz#3871d50641e874cc172e2b53f919842d19db4c52" @@ -5607,19 +5364,6 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -finalhandler@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" - integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "2.4.1" - parseurl "~1.3.3" - statuses "2.0.1" - unpipe "~1.0.0" - finalhandler@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" @@ -5729,31 +5473,24 @@ flatted@^2.0.0: integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== flatted@^3.1.0: - version "3.2.6" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.6.tgz#022e9218c637f9f3fc9c35ab9c9193f05add60b2" - integrity sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ== + version "3.2.5" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" + integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== flow-stoplight@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/flow-stoplight/-/flow-stoplight-1.0.0.tgz#4a292c5bcff8b39fa6cc0cb1a853d86f27eeff7b" integrity sha512-rDjbZUKpN8OYhB0IE/vY/I8UWO/602IIJEU/76Tv4LvYnwHCk0BCsvz4eRr9n+FQcri7L5cyaXOo0+/Kh4HisA== -fmix@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/fmix/-/fmix-0.1.0.tgz#c7bbf124dec42c9d191cfb947d0a9778dd986c0c" - integrity sha512-Y6hyofImk9JdzU8k5INtTXX1cu8LDlePWDFU5sftm9H+zKCr5SGrVjdhkvsim646cw5zD0nADj8oHyXMZmCZ9w== - dependencies: - imul "^1.0.0" - fn.name@1.x.x: version "1.1.0" resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== follow-redirects@^1.12.1, follow-redirects@^1.14.0: - version "1.15.1" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" - integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== + version "1.14.9" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" + integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== for-each@^0.3.3, for-each@~0.3.3: version "0.3.3" @@ -5767,6 +5504,11 @@ for-in@^1.0.2: resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== +foreach@^2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.6.tgz#87bcc8a1a0e74000ff2bf9802110708cfb02eb6e" + integrity sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg== + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -5847,9 +5589,9 @@ fs-extra@^0.30.0: rimraf "^2.2.8" fs-extra@^10.0.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" - integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== + version "10.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.1.tgz#27de43b4320e833f6867cc044bfce29fdf0ef3b8" + integrity sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag== dependencies: graceful-fs "^4.2.0" jsonfile "^6.0.1" @@ -6102,7 +5844,7 @@ glob@7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -glob@7.2.0: +glob@7.2.0, glob@^7.0.0, glob@^7.1.2, glob@^7.1.3, glob@^7.1.6, glob@~7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== @@ -6125,18 +5867,6 @@ glob@^5.0.15: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.1.2, glob@^7.1.3, glob@^7.1.6, glob@~7.2.0: - version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" - glob@^8.0.3: version "8.0.3" resolved "https://registry.yarnpkg.com/glob/-/glob-8.0.3.tgz#415c6eb2deed9e502c68fa44a272e6da6eeca42e" @@ -6185,9 +5915,9 @@ globals@^11.7.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.6.0, globals@^13.9.0: - version "13.17.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.17.0.tgz#902eb1e680a41da93945adbdcb5a9f361ba69bd4" - integrity sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw== + version "13.13.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.13.0.tgz#ac32261060d8070e2719dd6998406e27d2b5727b" + integrity sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A== dependencies: type-fest "^0.20.2" @@ -6312,40 +6042,21 @@ hard-rejection@^2.1.0: integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== hardhat-abi-exporter@^2.2.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/hardhat-abi-exporter/-/hardhat-abi-exporter-2.10.0.tgz#4498982bb8942178a3b6d9368b0def8d18b49cc8" - integrity sha512-ynYGUptkal3leyUNGP3aJ6GRYtSHed3lHcfMv6imawqEd7MERueEkeFy+X4kWH3Vf5w4k6xeeCJHJsegGBJUPA== + version "2.8.0" + resolved "https://registry.yarnpkg.com/hardhat-abi-exporter/-/hardhat-abi-exporter-2.8.0.tgz#e7ed6216c16acf84158909d856577f0a8832ec55" + integrity sha512-HQwd9Agr2O5znUg9Dzicw8grsXacoMSQsS5ZhBBNyaxKeVbvxL1Ubm9ss8sSVGr74511a8qiR2Ljm/lsLS9Mew== dependencies: "@ethersproject/abi" "^5.5.0" delete-empty "^3.0.0" hardhat-contract-sizer@^2.0.3: - version "2.6.1" - resolved "https://registry.yarnpkg.com/hardhat-contract-sizer/-/hardhat-contract-sizer-2.6.1.tgz#2b0046a55fa1ec96f19fdab7fde372377401c874" - integrity sha512-b8wS7DBvyo22kmVwpzstAQTdDCThpl/ySBqZh5ga9Yxjf61/uTL12TEg5nl7lDeWy73ntEUzxMwY6XxbQEc2wA== + version "2.5.1" + resolved "https://registry.yarnpkg.com/hardhat-contract-sizer/-/hardhat-contract-sizer-2.5.1.tgz#cb0b8dd32593b7a28c8d96ecde04841292bbd603" + integrity sha512-28yRb73e30aBVaZOOHTlHZFIdIasA/iFunIehrUviIJTubvdQjtSiQUo2wexHFtt71mQeMPP8qjw2sdbgatDnQ== dependencies: chalk "^4.0.0" cli-table3 "^0.6.0" -hardhat-deploy@^0.11.10: - version "0.11.11" - resolved "https://registry.yarnpkg.com/hardhat-deploy/-/hardhat-deploy-0.11.11.tgz#479040ba84d5b6d582d8fc719a3e77656ba8dc66" - integrity sha512-/37No1l6aFMMc4+t+a0/8gk6OwwD4tyW8bSQqV/0IZEiaHiNa7hOV4rTbC9B7277SDHVoFUrV/fwmk7IDiB8sQ== - dependencies: - "@types/qs" "^6.9.7" - axios "^0.21.1" - chalk "^4.1.2" - chokidar "^3.5.2" - debug "^4.3.2" - enquirer "^2.3.6" - ethers "^5.5.3" - form-data "^4.0.0" - fs-extra "^10.0.0" - match-all "^1.2.6" - murmur-128 "^0.2.1" - qs "^6.9.4" - zksync-web3 "^0.7.8" - hardhat-gas-reporter@^1.0.4: version "1.0.8" resolved "https://registry.yarnpkg.com/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.8.tgz#93ce271358cd748d9c4185dbb9d1d5525ec145e0" @@ -6379,6 +6090,60 @@ hardhat-tracer@^1.0.0-alpha.6: dependencies: ethers "^5.0.24" +hardhat@^2.6.4: + version "2.9.3" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.9.3.tgz#4759dc3c468c7d15f34334ca1be7d59b04e47b1e" + integrity sha512-7Vw99RbYbMZ15UzegOR/nqIYIqddZXvLwJGaX5sX4G5bydILnbjmDU6g3jMKJSiArEixS3vHAEaOs5CW1JQ3hg== + dependencies: + "@ethereumjs/block" "^3.6.0" + "@ethereumjs/blockchain" "^5.5.0" + "@ethereumjs/common" "^2.6.0" + "@ethereumjs/tx" "^3.4.0" + "@ethereumjs/vm" "^5.6.0" + "@ethersproject/abi" "^5.1.2" + "@metamask/eth-sig-util" "^4.0.0" + "@sentry/node" "^5.18.1" + "@solidity-parser/parser" "^0.14.1" + "@types/bn.js" "^5.1.0" + "@types/lru-cache" "^5.1.0" + abort-controller "^3.0.0" + adm-zip "^0.4.16" + aggregate-error "^3.0.0" + ansi-escapes "^4.3.0" + chalk "^2.4.2" + chokidar "^3.4.0" + ci-info "^2.0.0" + debug "^4.1.1" + enquirer "^2.3.0" + env-paths "^2.2.0" + ethereum-cryptography "^0.1.2" + ethereumjs-abi "^0.6.8" + ethereumjs-util "^7.1.3" + find-up "^2.1.0" + fp-ts "1.19.3" + fs-extra "^7.0.1" + glob "^7.1.3" + immutable "^4.0.0-rc.12" + io-ts "1.10.4" + lodash "^4.17.11" + merkle-patricia-tree "^4.2.2" + mnemonist "^0.38.0" + mocha "^9.2.0" + p-map "^4.0.0" + qs "^6.7.0" + raw-body "^2.4.1" + resolve "1.17.0" + semver "^6.3.0" + slash "^3.0.0" + solc "0.7.3" + source-map-support "^0.5.13" + stacktrace-parser "^0.1.10" + "true-case-path" "^2.2.1" + tsort "0.0.1" + undici "^4.14.1" + uuid "^8.3.2" + ws "^7.4.6" + hardhat@^2.9.5: version "2.10.1" resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.10.1.tgz#37fdc0c96d6a5d16b322269db2ad8f9f115c4046" @@ -6726,9 +6491,9 @@ immediate@~3.2.3: integrity sha512-RrGCXRm/fRVqMIhqXrGEX9rRADavPiDFSoMb/k64i9XMk8uH4r/Omi5Ctierj6XzNecwDbO4WuFbDD1zmpl3Tg== immutable@^4.0.0-rc.12: - version "4.1.0" - resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.1.0.tgz#f795787f0db780183307b9eb2091fcac1f6fafef" - integrity sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ== + version "4.0.0" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0.tgz#b86f78de6adef3608395efb269a91462797e2c23" + integrity sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw== import-fresh@^2.0.0: version "2.0.0" @@ -6746,11 +6511,6 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" -imul@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/imul/-/imul-1.0.1.tgz#9d5867161e8b3de96c2c38d5dc7cb102f35e2ac9" - integrity sha512-WFAgfwPLAjU66EKt6vRdTlKj4nAgIDQzh29JonLa4Bqtl6D8JrIMvWjCnx7xEjVNmP3U0fM5o8ZObk7d0f62bA== - imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -6804,9 +6564,9 @@ inquirer@^6.2.2: through "^2.3.6" inquirer@^8.0.0: - version "8.2.4" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.4.tgz#ddbfe86ca2f67649a67daa6f1051c128f684f0b4" - integrity sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg== + version "8.2.2" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.2.tgz#1310517a87a0814d25336c78a20b44c3d9b7629d" + integrity sha512-pG7I/si6K/0X7p1qU+rfWnpTE1UIkTONN1wxtzh0d+dHXtT/JG6qBgLxoyHVsQa8cFABxAPh0pD6uUUHiAoaow== dependencies: ansi-escapes "^4.2.1" chalk "^4.1.1" @@ -6822,7 +6582,6 @@ inquirer@^8.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" through "^2.3.6" - wrap-ansi "^7.0.0" internal-slot@^1.0.3: version "1.0.3" @@ -6848,7 +6607,7 @@ invariant@^2.2.2: invert-kv@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" - integrity sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ== + integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= io-ts@1.10.4: version "1.10.4" @@ -7060,10 +6819,10 @@ is-circular@^1.0.2: resolved "https://registry.yarnpkg.com/is-circular/-/is-circular-1.0.2.tgz#2e0ab4e9835f4c6b0ea2b9855a84acd501b8366c" integrity sha512-YttjnrswnUYRVJvxCvu8z+PGMUSzC2JttP0OEXezlAEdp3EXzhf7IZ3j0gRAybJBQupedIZFhY61Tga6E0qASA== -is-core-module@^2.5.0, is-core-module@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" - integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== +is-core-module@^2.5.0, is-core-module@^2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" + integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== dependencies: has "^1.0.3" @@ -7275,7 +7034,7 @@ is-retry-allowed@^1.0.0: resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== -is-shared-array-buffer@^1.0.2: +is-shared-array-buffer@^1.0.1, is-shared-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== @@ -7313,15 +7072,15 @@ is-text-path@^1.0.1: dependencies: text-extensions "^1.0.0" -is-typed-array@^1.1.3, is-typed-array@^1.1.9: - version "1.1.9" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.9.tgz#246d77d2871e7d9f5aeb1d54b9f52c71329ece67" - integrity sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A== +is-typed-array@^1.1.3, is-typed-array@^1.1.7: + version "1.1.8" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.8.tgz#cbaa6585dc7db43318bc5b89523ea384a6f65e79" + integrity sha512-HqH41TNZq2fgtGT8WHVFVJhBVGuY3AnP3Q36K8JKXUxSxRgk/d+7NjmwG2vo2mYmXK8UYZKu0qH8bVP5gEisjA== dependencies: available-typed-arrays "^1.0.5" call-bind "^1.0.2" - es-abstract "^1.20.0" - for-each "^0.3.3" + es-abstract "^1.18.5" + foreach "^2.0.5" has-tostringtag "^1.0.0" is-typedarray@^1.0.0, is-typedarray@~1.0.0: @@ -8054,7 +7813,7 @@ locate-path@^6.0.0: lodash.assign@^4.0.3, lodash.assign@^4.0.6: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" - integrity sha512-hFuH8TY+Yji7Eja3mGiuAxBqLagejScbG8GbG0j6o9vzn0YL14My+ktnqtZgFTosKymC9/44wP6s7xyuLfnClw== + integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc= lodash.clonedeep@^4.5.0: version "4.5.0" @@ -8066,6 +7825,16 @@ lodash.get@^4: resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= + +lodash.isequalwith@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.isequalwith/-/lodash.isequalwith-4.4.0.tgz#266726ddd528f854f21f4ea98a065606e0fbc6b0" + integrity sha1-Jmcm3dUo+FTyH06pigZWBuD7xrA= + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" @@ -8112,9 +7881,9 @@ log-update@^4.0.0: wrap-ansi "^6.2.0" logform@^2.3.2, logform@^2.4.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/logform/-/logform-2.4.2.tgz#a617983ac0334d0c3b942c34945380062795b47c" - integrity sha512-W4c9himeAwXEdZ05dQNerhFz2XG80P9Oj0loPUMV23VC2it0orMHQhJm4hdnnor3rd1HsGf6a2lPwBM1zeXHGw== + version "2.4.0" + resolved "https://registry.yarnpkg.com/logform/-/logform-2.4.0.tgz#131651715a17d50f09c2a2c1a524ff1a4164bcfe" + integrity sha512-CPSJw4ftjf517EhXZGGvTHHkYobo7ZCc0kvwUoOYcjfR2UVrI66RHj8MCrfAdEitdmFqbu2BYdYs8FHHZSb6iw== dependencies: "@colors/colors" "1.5.0" fecha "^4.2.0" @@ -8177,6 +7946,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru-cache@^7.4.0: + version "7.7.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.7.3.tgz#98cd19eef89ce6a4a3c4502c17c833888677c252" + integrity sha512-WY9wjJNQt9+PZilnLbuFKM+SwDull9+6IAguOrarOMoOHTcJ9GnXSO11+Gw6c7xtDkBkthR57OZMtZKYr+1CEw== + lru_map@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" @@ -8224,11 +7998,6 @@ markdown-table@^1.1.3: resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-1.1.3.tgz#9fcb69bcfdb8717bfd0398c6ec2d93036ef8de60" integrity sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q== -match-all@^1.2.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/match-all/-/match-all-1.2.6.tgz#66d276ad6b49655551e63d3a6ee53e8be0566f8d" - integrity sha512-0EESkXiTkWzrQQntBu2uzKvLu6vVkUGz40nGPbSZuegcfE5UuSzNjLaIu76zJWuaT/2I3Z/8M06OlUOZLGwLlQ== - mcl-wasm@^0.7.1: version "0.7.9" resolved "https://registry.yarnpkg.com/mcl-wasm/-/mcl-wasm-0.7.9.tgz#c1588ce90042a8700c3b60e40efb339fc07ab87f" @@ -8246,7 +8015,7 @@ md5.js@^1.3.4: media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= memdown@^1.0.0: version "1.4.1" @@ -8355,7 +8124,7 @@ merkle-patricia-tree@^2.1.2, merkle-patricia-tree@^2.3.2: rlp "^2.0.0" semaphore ">=1.0.1" -merkle-patricia-tree@^4.2.4: +merkle-patricia-tree@^4.2.2, merkle-patricia-tree@^4.2.4: version "4.2.4" resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-4.2.4.tgz#ff988d045e2bf3dfa2239f7fabe2d59618d57413" integrity sha512-eHbf/BG6eGNsqqfbLED9rIqbsF4+sykEaBn6OLNs71tjclbMcMOk1tEPmJKcNcNCLkvbpY/lwyOlizWsqPNo8w== @@ -8461,14 +8230,14 @@ minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== -minimatch@*, minimatch@^5.0.1: - version "5.1.0" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7" - integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg== +minimatch@*, minimatch@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== dependencies: brace-expansion "^2.0.1" -"minimatch@2 || 3", minimatch@^3.0.4, minimatch@^3.1.1: +"minimatch@2 || 3", minimatch@^3.0.4: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -8482,10 +8251,17 @@ minimatch@3.0.4: dependencies: brace-expansion "^1.1.7" -minimatch@5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" - integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== +minimatch@4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-4.2.1.tgz#40d9d511a46bdc4e563c22c3080cde9c0d8299b4" + integrity sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7" + integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg== dependencies: brace-expansion "^2.0.1" @@ -8498,7 +8274,7 @@ minimist-options@4.1.0: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6, minimist@~1.2.6: +minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6, minimist@~1.2.5: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== @@ -8617,6 +8393,36 @@ mocha@^7.1.1: yargs-parser "13.1.2" yargs-unparser "1.6.0" +mocha@^9.2.0: + version "9.2.2" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.2.2.tgz#d70db46bdb93ca57402c809333e5a84977a88fb9" + integrity sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g== + dependencies: + "@ungap/promise-all-settled" "1.1.2" + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.5.3" + debug "4.3.3" + diff "5.0.0" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.2.0" + growl "1.10.5" + he "1.2.0" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "4.2.1" + ms "2.1.3" + nanoid "3.3.1" + serialize-javascript "6.0.0" + strip-json-comments "3.1.1" + supports-color "8.1.1" + which "2.0.2" + workerpool "6.2.0" + yargs "16.2.0" + yargs-parser "20.2.4" + yargs-unparser "2.0.0" + mock-fs@^4.1.0: version "4.14.0" resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.14.0.tgz#ce5124d2c601421255985e6e94da80a7357b1b18" @@ -8630,9 +8436,9 @@ moment-timezone@^0.5.34: moment ">= 2.9.0" "moment@>= 2.9.0", moment@^2.29.1: - version "2.29.4" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" - integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== + version "2.29.2" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.2.tgz#00910c60b20843bcba52d37d58c628b47b1f20e4" + integrity sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg== morgan@1.10.0: version "1.10.0" @@ -8749,9 +8555,9 @@ multicodec@^3.0.1: varint "^6.0.0" multiformats@^9.4.2: - version "9.7.0" - resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.7.0.tgz#845799e8df70fbb6b15922500e45cb87cf12f7e5" - integrity sha512-uv/tcgwk0yN4DStopnBN4GTgvaAlYdy6KnZpuzEPFOYQd71DYFJjs0MN1ERElAflrZaYyGBWXyGxL5GgrxIx0Q== + version "9.6.4" + resolved "https://registry.yarnpkg.com/multiformats/-/multiformats-9.6.4.tgz#5dce1f11a407dbb69aa612cb7e5076069bb759ca" + integrity sha512-fCCB6XMrr6CqJiHNjfFNGT0v//dxOBMrOMqUIzpPc/mmITweLEyhvMpY9bF+jZ9z3vaMAau5E8B68DW77QMXkg== multihashes@^0.4.15, multihashes@~0.4.15: version "0.4.21" @@ -8792,15 +8598,6 @@ multihashing-async@^2.0.0: murmurhash3js-revisited "^3.0.0" uint8arrays "^3.0.0" -murmur-128@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/murmur-128/-/murmur-128-0.2.1.tgz#a9f6568781d2350ecb1bf80c14968cadbeaa4b4d" - integrity sha512-WseEgiRkI6aMFBbj8Cg9yBj/y+OdipwVC7zUo3W2W1JAJITwouUOtpqsmGSg67EQmwwSyod7hsVsWY5LsrfQVg== - dependencies: - encode-utf8 "^1.0.2" - fmix "^0.1.0" - imul "^1.0.0" - murmurhash3js-revisited@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/murmurhash3js-revisited/-/murmurhash3js-revisited-3.0.0.tgz#6bd36e25de8f73394222adc6e41fa3fac08a5869" @@ -8821,15 +8618,20 @@ nano-json-stream-parser@^0.1.2: resolved "https://registry.yarnpkg.com/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz#0cc8f6d0e2b622b479c40d499c46d64b755c6f5f" integrity sha512-9MqxMH/BSJC7dnLsEMPyfN5Dvoo49IsPFYMcHw3Bcfc2kN0lpHRBSzlMSVx4HGyJ7s9B31CyBTVehWJoQ8Ctew== +nanoid@3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" + integrity sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw== + nanoid@3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== nanoid@^3.0.2, nanoid@^3.1.3: - version "3.3.4" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" - integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== + version "3.3.2" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.2.tgz#c89622fafb4381cd221421c69ec58547a1eec557" + integrity sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA== nanomatch@^1.2.9: version "1.2.13" @@ -8919,9 +8721,9 @@ node-fetch@~1.7.1: is-stream "^1.0.1" node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" - integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== + version "4.4.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.4.0.tgz#42e99687ce87ddeaf3a10b99dc06abc11021f3f4" + integrity sha512-amJnQCcgtRVw9SvoebO3BKGESClrfXGCUTX9hSn1OuGQTQBOZmVd0Z0OlecpuRksKvbsUqALE8jls/ErClAPuQ== nofilter@^1.0.4: version "1.0.4" @@ -9010,9 +8812,9 @@ object-copy@^0.1.0: kind-of "^3.0.3" object-inspect@^1.12.0, object-inspect@^1.9.0, object-inspect@~1.12.0: - version "1.12.2" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" - integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== + version "1.12.0" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0" + integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g== object-is@^1.0.1: version "1.1.5" @@ -9060,14 +8862,13 @@ object.assign@^4.1.2: object-keys "^1.1.1" object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.1: - version "2.1.4" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.4.tgz#7965e6437a57278b587383831a9b829455a4bc37" - integrity sha512-sccv3L/pMModT6dJAYF3fzGMVcb38ysQ0tEE6ixv2yXJDtEIPph268OlAdJj5/qZMZDq2g/jqvwppt36uS/uQQ== + version "2.1.3" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.3.tgz#b223cf38e17fefb97a63c10c91df72ccb386df9e" + integrity sha512-VdDoCwvJI4QdC6ndjpqFmoL3/+HxffFBbcJzKi5hwLLqqx3mdbedRpfZDdK0SrOSauj8X4GzBvnDZl4vTN7dOw== dependencies: - array.prototype.reduce "^1.0.4" call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.1" + define-properties "^1.1.3" + es-abstract "^1.19.1" object.pick@^1.3.0: version "1.3.0" @@ -9077,9 +8878,9 @@ object.pick@^1.3.0: isobject "^3.0.1" obliterator@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-2.0.4.tgz#fa650e019b2d075d745e44f1effeb13a2adbe816" - integrity sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ== + version "2.0.2" + resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-2.0.2.tgz#25f50dc92e1181371b9d8209d11890f1a3c2fc21" + integrity sha512-g0TrA7SbUggROhDPK8cEu/qpItwH2LSKcNl4tlfBNT54XY+nOsqrs0Q68h1V9b3HOSpIWv15jb1lax2hAggdIg== oboe@2.1.4: version "2.1.4" @@ -9237,7 +9038,7 @@ p-fifo@^1.0.0: p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== + integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= p-limit@^1.1.0: version "1.3.0" @@ -9547,9 +9348,9 @@ pg-int8@1.0.1: integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw== pg-pool@^3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.5.1.tgz#f499ce76f9bf5097488b3b83b19861f28e4ed905" - integrity sha512-6iCR0wVrro6OOHFsyavV+i6KYL4lVNyYAB9RD18w66xSzN+d8b66HiwuP30Gp1SH5O9T82fckkzsRjlrhD0ioQ== + version "3.5.2" + resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.5.2.tgz#ed1bed1fb8d79f1c6fd5fb1c99e990fbf9ddf178" + integrity sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w== pg-protocol@^1.5.0: version "1.5.0" @@ -9651,11 +9452,10 @@ pino@7.6.0: thread-stream "^0.13.0" pino@^7.0.0: - version "7.11.0" - resolved "https://registry.yarnpkg.com/pino/-/pino-7.11.0.tgz#0f0ea5c4683dc91388081d44bff10c83125066f6" - integrity sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg== + version "7.9.2" + resolved "https://registry.yarnpkg.com/pino/-/pino-7.9.2.tgz#0519ac79b9db17f0c63bb4267f7e8133f5c517ad" + integrity sha512-c8wmB2PuhdJurYPRl/eo3+PosHe7Ep6GZvBJFIrp9oV1YRZQ3qm3MujaEolaKUfwX8cDL96WKCWWMedB2drXqw== dependencies: - atomic-sleep "^1.0.0" fast-redact "^3.0.0" on-exit-leak-free "^0.2.0" pino-abstract-transport v0.5.0 @@ -9665,7 +9465,7 @@ pino@^7.0.0: real-require "^0.1.0" safe-stable-stringify "^2.1.0" sonic-boom "^2.2.1" - thread-stream "^0.15.1" + thread-stream "^0.15.0" please-upgrade-node@^3.2.0: version "3.2.0" @@ -9739,14 +9539,14 @@ prettier-linter-helpers@^1.0.0: fast-diff "^1.1.2" prettier-plugin-solidity@^1.0.0-beta.9: - version "1.0.0-dev.23" - resolved "https://registry.yarnpkg.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-dev.23.tgz#e1edf0693d69fe1518519ab704d5e46ee4f842fc" - integrity sha512-440/jZzvtDJcqtoRCQiigo1DYTPAZ85pjNg7gvdd+Lds6QYgID8RyOdygmudzHdFmV2UfENt//A8tzx7iS58GA== + version "1.0.0-beta.19" + resolved "https://registry.yarnpkg.com/prettier-plugin-solidity/-/prettier-plugin-solidity-1.0.0-beta.19.tgz#7c3607fc4028f5e6a425259ff03e45eedf733df3" + integrity sha512-xxRQ5ZiiZyUoMFLE9h7HnUDXI/daf1tnmL1msEdcKmyh7ZGQ4YklkYLC71bfBpYU2WruTb5/SFLUaEb3RApg5g== dependencies: - "@solidity-parser/parser" "^0.14.3" - emoji-regex "^10.1.0" + "@solidity-parser/parser" "^0.14.0" + emoji-regex "^10.0.0" escape-string-regexp "^4.0.0" - semver "^7.3.7" + semver "^7.3.5" solidity-comments-extractor "^0.0.7" string-width "^4.2.3" @@ -9756,9 +9556,9 @@ prettier@^1.14.3: integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== prettier@^2.1.2, prettier@^2.2.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" - integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== + version "2.6.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.6.2.tgz#e26d71a18a74c3d0f0597f55f01fb6c06c206032" + integrity sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew== private@^0.1.6, private@^0.1.8: version "0.1.8" @@ -9958,7 +9758,7 @@ q@^1.5.1: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== -qs@6.10.3: +qs@6.10.3, qs@^6.4.0, qs@^6.7.0: version "6.10.3" resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== @@ -9975,13 +9775,6 @@ qs@6.9.7: resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.7.tgz#4610846871485e1e048f44ae3b94033f0e675afe" integrity sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw== -qs@^6.4.0, qs@^6.7.0, qs@^6.9.4: - version "6.11.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" - integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== - dependencies: - side-channel "^1.0.4" - qs@~6.5.2: version "6.5.3" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" @@ -10219,7 +10012,15 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" -regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.4.3: +regexp.prototype.flags@^1.2.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz#b3f4c0059af9e47eca9f3f660e51d81307e72307" + integrity sha512-pMR7hBVUUGI7PMA37m2ofIdQCsomVnas+Jn5UPGAHQ+/LlwKm/aTLJHdasmHRzlfeZwHiAOaRSo2rbBDm3nNUQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + +regexp.prototype.flags@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== @@ -10402,11 +10203,11 @@ resolve@1.17.0: path-parse "^1.0.6" resolve@^1.1.6, resolve@^1.10.0, resolve@^1.8.1, resolve@~1.22.0: - version "1.22.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + version "1.22.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" + integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== dependencies: - is-core-module "^2.9.0" + is-core-module "^2.8.1" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -10532,10 +10333,10 @@ rxjs@^6.4.0: dependencies: tslib "^1.9.0" -rxjs@^7.5.1, rxjs@^7.5.5: - version "7.5.6" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.6.tgz#0446577557862afd6903517ce7cae79ecb9662bc" - integrity sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw== +rxjs@^7.2.0, rxjs@^7.5.1, rxjs@^7.5.5: + version "7.5.5" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.5.tgz#2ebad89af0f560f460ad5cc4213219e1f7dd4e9f" + integrity sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw== dependencies: tslib "^2.1.0" @@ -10656,12 +10457,12 @@ semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.2.1, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== +semver@^7.2.1, semver@^7.3.4, semver@^7.3.5: + version "7.3.6" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.6.tgz#5d73886fb9c0c6602e79440b97165c29581cbb2b" + integrity sha512-HZWqcgwLsjaX1HBD31msI/rXktuIhS+lWvdE4kN9z+8IVT4Itc7vqU2WvYsyD6/sjYCt4dEKH/m1M3dwI9CC5w== dependencies: - lru-cache "^6.0.0" + lru-cache "^7.4.0" semver@~5.4.1: version "5.4.1" @@ -10687,25 +10488,6 @@ send@0.17.2: range-parser "~1.2.1" statuses "~1.5.0" -send@0.18.0: - version "0.18.0" - resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" - integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== - dependencies: - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "2.0.0" - mime "1.6.0" - ms "2.1.3" - on-finished "2.4.1" - range-parser "~1.2.1" - statuses "2.0.1" - sequelize-pool@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/sequelize-pool/-/sequelize-pool-7.1.0.tgz#210b391af4002762f823188fd6ecfc7413020768" @@ -10750,16 +10532,6 @@ serve-static@1.14.2: parseurl "~1.3.3" send "0.17.2" -serve-static@1.15.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" - integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.18.0" - servify@^0.1.12: version "0.1.12" resolved "https://registry.yarnpkg.com/servify/-/servify-0.1.12.tgz#142ab7bee1f1d033b66d0707086085b17c06db95" @@ -11044,9 +10816,9 @@ solhint@^3.3.6: prettier "^1.14.3" solidity-ast@^0.4.15: - version "0.4.35" - resolved "https://registry.yarnpkg.com/solidity-ast/-/solidity-ast-0.4.35.tgz#82e064b14dc989338123264bde2235cad751f128" - integrity sha512-F5bTDLh3rmDxRmLSrs3qt3nvxJprWSEkS7h2KmuXDx7XTfJ6ZKVTV1rtPIYCqJAuPsU/qa8YUeFn7jdOAZcTPA== + version "0.4.31" + resolved "https://registry.yarnpkg.com/solidity-ast/-/solidity-ast-0.4.31.tgz#c63e42f894cd047826a05dbb8d1e1dfc17282d39" + integrity sha512-kX6o4XE4ihaqENuRRTMJfwQNHoqWusPENZUlX4oVb19gQdfi7IswFWnThONHSW/61umgfWdKtCBgW45iuOTryQ== solidity-comments-extractor@^0.0.7: version "0.0.7" @@ -11054,9 +10826,9 @@ solidity-comments-extractor@^0.0.7: integrity sha512-wciNMLg/Irp8OKGrh3S2tfvZiZ0NEyILfcRCXCD4mp7SgK/i9gzLfhY2hY7VMCQJ3kH9UB9BzNdibIVMchzyYw== solidity-coverage@^0.7.16: - version "0.7.21" - resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.7.21.tgz#20c5615a3a543086b243c2ca36e2951a75316b40" - integrity sha512-O8nuzJ9yXiKUx3NdzVvHrUW0DxoNVcGzq/I7NzewNO9EZE3wYAQ4l8BwcnV64r4aC/HB6Vnw/q2sF0BQHv/3fg== + version "0.7.20" + resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.7.20.tgz#246e9b0dd62f698bb8ddeecdcc46cab26c48b637" + integrity sha512-edOXTugUYdqxrtEnIn4vgrGjLPxdexcL0WD8LzAvVA3d1dwgcfRO3k8xQR02ZQnOnWMBi8Cqs0F+kAQQp3JW8g== dependencies: "@solidity-parser/parser" "^0.14.0" "@truffle/provider" "^0.2.24" @@ -11078,9 +10850,9 @@ solidity-coverage@^0.7.16: web3-utils "^1.3.0" sonic-boom@^2.2.1: - version "2.8.0" - resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-2.8.0.tgz#c1def62a77425090e6ad7516aad8eb402e047611" - integrity sha512-kuonw1YOYYNOve5iHdSahXPOK49GqwA+LZhI6Wz/l0rP57iKyXXIHaRagOBHAPmGwJC6od2Z9zgvZ5loSgMlVg== + version "2.6.0" + resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-2.6.0.tgz#8786fc78be07c18a90381acd816d1d4afe3537a2" + integrity sha512-6xYZFRmDEtxGqfOKcDQ4cPLrNa0SPEDI+wlzDAHowXE6YV42NeXqg9mP2KkiM8JVu3lHfZ2iQKYlGOz+kTpphg== dependencies: atomic-sleep "^1.0.0" @@ -11311,15 +11083,15 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: strip-ansi "^6.0.1" string.prototype.trim@~1.2.5: - version "1.2.6" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.6.tgz#824960787db37a9e24711802ed0c1d1c0254f83e" - integrity sha512-8lMR2m+U0VJTPp6JjvJTtGyc4FIGq9CdRt7O9p6T0e6K4vjU+OP+SQJpbe/SBmRcCUIvNUnjsbmY6lnMp8MhsQ== + version "1.2.5" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.5.tgz#a587bcc8bfad8cb9829a577f5de30dd170c1682c" + integrity sha512-Lnh17webJVsD6ECeovpVN17RlAKjmz4rF9S+8Y45CkMc/ufVpTkU3vZIyIC7sllQ1FCvObZnnCdNs/HXTUOTlg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.19.5" + define-properties "^1.1.3" + es-abstract "^1.19.1" -string.prototype.trimend@^1.0.5: +string.prototype.trimend@^1.0.4, string.prototype.trimend@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog== @@ -11328,7 +11100,7 @@ string.prototype.trimend@^1.0.5: define-properties "^1.1.4" es-abstract "^1.19.5" -string.prototype.trimstart@^1.0.5: +string.prototype.trimstart@^1.0.4, string.prototype.trimstart@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg== @@ -11529,9 +11301,9 @@ table@^6.0.9: strip-ansi "^6.0.1" tape@^4.6.3: - version "4.15.1" - resolved "https://registry.yarnpkg.com/tape/-/tape-4.15.1.tgz#88fb662965a11f9be1bddb04c11662d7eceb129e" - integrity sha512-k7F5pyr91n9D/yjSJwbLLYDCrTWXxMSXbbmHX2n334lSIc2rxeXyFkaBv4UuUd2gBYMrAOalPutAiCxC6q1qbw== + version "4.15.0" + resolved "https://registry.yarnpkg.com/tape/-/tape-4.15.0.tgz#1b8a9563b4bc7e51302216c137732fb2ce6d1a99" + integrity sha512-SfRmG2I8QGGgJE/MCiLH8c11L5XxyUXxwK9xLRD0uiK5fehRkkSZGmR6Y1pxOt8vJ19m3sY+POTQpiaVv45/LQ== dependencies: call-bind "~1.0.2" deep-equal "~1.1.1" @@ -11542,7 +11314,7 @@ tape@^4.6.3: has "~1.0.3" inherits "~2.0.4" is-regex "~1.1.4" - minimist "~1.2.6" + minimist "~1.2.5" object-inspect "~1.12.0" resolve "~1.22.0" resumer "~0.0.0" @@ -11621,10 +11393,10 @@ thread-stream@^0.13.0: dependencies: real-require "^0.1.0" -thread-stream@^0.15.1: - version "0.15.2" - resolved "https://registry.yarnpkg.com/thread-stream/-/thread-stream-0.15.2.tgz#fb95ad87d2f1e28f07116eb23d85aba3bc0425f4" - integrity sha512-UkEhKIg2pD+fjkHQKyJO3yoIvAP3N6RlNFt2dUhcS1FGvCD1cQa1M/PGknCLFIyZdtJOWQjejp7bdNqmN7zwdA== +thread-stream@^0.15.0: + version "0.15.1" + resolved "https://registry.yarnpkg.com/thread-stream/-/thread-stream-0.15.1.tgz#5aba24a35aa5e9e4eb66173826443d7167b34b07" + integrity sha512-SCnuIT27gc2h/F/RY2peuC7brgLy+1oXU+7yOIAITz1z5stDpXCF5rAoFcykjuK6ifbTlKAHL7Ccq8oc5Btv1w== dependencies: real-require "^0.1.0" @@ -11827,9 +11599,9 @@ tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== tslib@^2, tslib@^2.1.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + version "2.3.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== tsort@0.0.1: version "0.0.1" @@ -11973,7 +11745,12 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== -typescript@^4.4.3, typescript@^4.7.4: +typescript@^4.4.3: + version "4.6.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.3.tgz#eefeafa6afdd31d725584c67a0eaba80f6fc6c6c" + integrity sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw== + +typescript@^4.7.4: version "4.7.4" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== @@ -12001,9 +11778,9 @@ typical@^2.6.0, typical@^2.6.1: integrity sha512-ofhi8kjIje6npGozTip9Fr8iecmYfEbS06i0JnIg+rh51KakryWF4+jX8lLKZVhy6N+ID45WYSFCxPOdTWCzNg== uglify-js@^3.1.4: - version "3.16.2" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.16.2.tgz#0481e1dbeed343ad1c2ddf3c6d42e89b7a6d4def" - integrity sha512-AaQNokTNgExWrkEYA24BTNMSjyqEXPSfhqoS0AxmHkCJ4U+Dyy5AvbGV/sqxuxficEfGGoX3zWw9R7QpLFfEsg== + version "3.15.3" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.15.3.tgz#9aa82ca22419ba4c0137642ba0df800cb06e0471" + integrity sha512-6iCVm2omGJbsu3JWac+p6kUiOpg3wFO2f8lIXjfEb8RrmLjzog1wTPMmwKB7swfzzqxj9YM+sGUM++u1qN4qJg== uint8arrays@1.1.0, uint8arrays@^1.0.0, uint8arrays@^1.1.0: version "1.1.0" @@ -12032,7 +11809,7 @@ ultron@~1.1.0: resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== -unbox-primitive@^1.0.2: +unbox-primitive@^1.0.1, unbox-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== @@ -12048,9 +11825,14 @@ underscore@1.9.1: integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg== underscore@^1.13.1: - version "1.13.4" - resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.4.tgz#7886b46bbdf07f768e0052f1828e1dcab40c0dee" - integrity sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ== + version "1.13.2" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.2.tgz#276cea1e8b9722a8dbed0100a407dda572125881" + integrity sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g== + +undici@^4.14.1: + version "4.16.0" + resolved "https://registry.yarnpkg.com/undici/-/undici-4.16.0.tgz#469bb87b3b918818d3d7843d91a1d08da357d5ff" + integrity sha512-tkZSECUYi+/T1i4u+4+lwZmQgLXd4BLGlrc7KZPcLIW7Jpq99+Xpc30ONv7nS6F5UNOxp/HBZSSL9MafUrvJbw== undici@^5.4.0: version "5.8.0" @@ -12282,10 +12064,10 @@ web3-bzz@1.2.11: swarm-js "^0.1.40" underscore "1.9.1" -web3-bzz@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.7.4.tgz#9419e606e38a9777443d4ce40506ebd796e06075" - integrity sha512-w9zRhyEqTK/yi0LGRHjZMcPCfP24LBjYXI/9YxFw9VqsIZ9/G0CRCnUt12lUx0A56LRAMpF7iQ8eA73aBcO29Q== +web3-bzz@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.5.3.tgz#e36456905ce051138f9c3ce3623cbc73da088c2b" + integrity sha512-SlIkAqG0eS6cBS9Q2eBOTI1XFzqh83RqGJWnyrNZMDxUwsTVHL+zNnaPShVPvrWQA1Ub5b0bx1Kc5+qJVxsTJg== dependencies: "@types/node" "^12.12.6" got "9.6.0" @@ -12300,13 +12082,13 @@ web3-core-helpers@1.2.11: web3-eth-iban "1.2.11" web3-utils "1.2.11" -web3-core-helpers@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.7.4.tgz#f8f808928560d3e64e0c8d7bdd163aa4766bcf40" - integrity sha512-F8PH11qIkE/LpK4/h1fF/lGYgt4B6doeMi8rukeV/s4ivseZHHslv1L6aaijLX/g/j4PsFmR42byynBI/MIzFg== +web3-core-helpers@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.5.3.tgz#099030235c477aadf39a94199ef40092151d563c" + integrity sha512-Ip1IjB3S8vN7Kf1PPjK41U5gskmMk6IJQlxIVuS8/1U7n/o0jC8krqtpRwiMfAgYyw3TXwBFtxSRTvJtnLyXZw== dependencies: - web3-eth-iban "1.7.4" - web3-utils "1.7.4" + web3-eth-iban "1.5.3" + web3-utils "1.5.3" web3-core-method@1.2.11: version "1.2.11" @@ -12320,16 +12102,17 @@ web3-core-method@1.2.11: web3-core-subscriptions "1.2.11" web3-utils "1.2.11" -web3-core-method@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.7.4.tgz#3873c6405e1a0a8a1efc1d7b28de8b7550b00c15" - integrity sha512-56K7pq+8lZRkxJyzf5MHQPI9/VL3IJLoy4L/+q8HRdZJ3CkB1DkXYaXGU2PeylG1GosGiSzgIfu1ljqS7CP9xQ== +web3-core-method@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.5.3.tgz#6cff97ed19fe4ea2e9183d6f703823a079f5132c" + integrity sha512-8wJrwQ2qD9ibWieF9oHXwrJsUGrv3XAtEkNeyvyNMpktNTIjxJ2jaFGQUuLiyUrMubD18XXgLk4JS6PJU4Loeg== dependencies: - "@ethersproject/transactions" "^5.6.2" - web3-core-helpers "1.7.4" - web3-core-promievent "1.7.4" - web3-core-subscriptions "1.7.4" - web3-utils "1.7.4" + "@ethereumjs/common" "^2.4.0" + "@ethersproject/transactions" "^5.0.0-beta.135" + web3-core-helpers "1.5.3" + web3-core-promievent "1.5.3" + web3-core-subscriptions "1.5.3" + web3-utils "1.5.3" web3-core-promievent@1.2.11: version "1.2.11" @@ -12338,10 +12121,10 @@ web3-core-promievent@1.2.11: dependencies: eventemitter3 "4.0.4" -web3-core-promievent@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.7.4.tgz#80a75633fdfe21fbaae2f1e38950edb2f134868c" - integrity sha512-o4uxwXKDldN7ER7VUvDfWsqTx9nQSP1aDssi1XYXeYC2xJbVo0n+z6ryKtmcoWoRdRj7uSpVzal3nEmlr480mA== +web3-core-promievent@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.5.3.tgz#3f11833c3dc6495577c274350b61144e0a4dba01" + integrity sha512-CFfgqvk3Vk6PIAxtLLuX+pOMozxkKCY+/GdGr7weMh033mDXEPvwyVjoSRO1PqIKj668/hMGQsVoIgbyxkJ9Mg== dependencies: eventemitter3 "4.0.4" @@ -12356,16 +12139,16 @@ web3-core-requestmanager@1.2.11: web3-providers-ipc "1.2.11" web3-providers-ws "1.2.11" -web3-core-requestmanager@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.7.4.tgz#2dc8a526dab8183dca3fef54658621801b1d0469" - integrity sha512-IuXdAm65BQtPL4aI6LZJJOrKAs0SM5IK2Cqo2/lMNvVMT9Kssq6qOk68Uf7EBDH0rPuINi+ReLP+uH+0g3AnPA== +web3-core-requestmanager@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.5.3.tgz#b339525815fd40e3a2a81813c864ddc413f7b6f7" + integrity sha512-9k/Bze2rs8ONix5IZR+hYdMNQv+ark2Ek2kVcrFgWO+LdLgZui/rn8FikPunjE+ub7x7pJaKCgVRbYFXjo3ZWg== dependencies: util "^0.12.0" - web3-core-helpers "1.7.4" - web3-providers-http "1.7.4" - web3-providers-ipc "1.7.4" - web3-providers-ws "1.7.4" + web3-core-helpers "1.5.3" + web3-providers-http "1.5.3" + web3-providers-ipc "1.5.3" + web3-providers-ws "1.5.3" web3-core-subscriptions@1.2.11: version "1.2.11" @@ -12376,13 +12159,13 @@ web3-core-subscriptions@1.2.11: underscore "1.9.1" web3-core-helpers "1.2.11" -web3-core-subscriptions@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.7.4.tgz#cfbd3fa71081a8c8c6f1a64577a1a80c5bd9826f" - integrity sha512-VJvKWaXRyxk2nFWumOR94ut9xvjzMrRtS38c4qj8WBIRSsugrZr5lqUwgndtj0qx4F+50JhnU++QEqUEAtKm3g== +web3-core-subscriptions@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.5.3.tgz#d7d69c4caad65074212028656e9dc56ca5c2159d" + integrity sha512-L2m9vG1iRN6thvmv/HQwO2YLhOQlmZU8dpLG6GSo9FBN14Uch868Swk0dYVr3rFSYjZ/GETevSXU+O+vhCummA== dependencies: eventemitter3 "4.0.4" - web3-core-helpers "1.7.4" + web3-core-helpers "1.5.3" web3-core@1.2.11: version "1.2.11" @@ -12397,18 +12180,18 @@ web3-core@1.2.11: web3-core-requestmanager "1.2.11" web3-utils "1.2.11" -web3-core@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.7.4.tgz#943fff99134baedafa7c65b4a0bbd424748429ff" - integrity sha512-L0DCPlIh9bgIED37tYbe7bsWrddoXYc897ANGvTJ6MFkSNGiMwDkTLWSgYd9Mf8qu8b4iuPqXZHMwIo4atoh7Q== +web3-core@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.5.3.tgz#59f8728b27c8305b349051326aa262b9b7e907bf" + integrity sha512-ACTbu8COCu+0eUNmd9pG7Q9EVsNkAg2w3Y7SqhDr+zjTgbSHZV01jXKlapm9z+G3AN/BziV3zGwudClJ4u4xXQ== dependencies: - "@types/bn.js" "^5.1.0" + "@types/bn.js" "^4.11.5" "@types/node" "^12.12.6" bignumber.js "^9.0.0" - web3-core-helpers "1.7.4" - web3-core-method "1.7.4" - web3-core-requestmanager "1.7.4" - web3-utils "1.7.4" + web3-core-helpers "1.5.3" + web3-core-method "1.5.3" + web3-core-requestmanager "1.5.3" + web3-utils "1.5.3" web3-eth-abi@1.2.11: version "1.2.11" @@ -12419,13 +12202,13 @@ web3-eth-abi@1.2.11: underscore "1.9.1" web3-utils "1.2.11" -web3-eth-abi@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.7.4.tgz#3fee967bafd67f06b99ceaddc47ab0970f2a614a" - integrity sha512-eMZr8zgTbqyL9MCTCAvb67RbVyN5ZX7DvA0jbLOqRWCiw+KlJKTGnymKO6jPE8n5yjk4w01e165Qb11hTDwHgg== +web3-eth-abi@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.5.3.tgz#5aea9394d797f99ca0d9bd40c3417eb07241c96c" + integrity sha512-i/qhuFsoNrnV130CSRYX/z4SlCfSQ4mHntti5yTmmQpt70xZKYZ57BsU0R29ueSQ9/P+aQrL2t2rqkQkAloUxg== dependencies: - "@ethersproject/abi" "^5.6.3" - web3-utils "1.7.4" + "@ethersproject/abi" "5.0.7" + web3-utils "1.5.3" web3-eth-accounts@1.2.11: version "1.2.11" @@ -12444,22 +12227,22 @@ web3-eth-accounts@1.2.11: web3-core-method "1.2.11" web3-utils "1.2.11" -web3-eth-accounts@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.7.4.tgz#7a24a4dfe947f7e9d1bae678529e591aa146167a" - integrity sha512-Y9vYLRKP7VU7Cgq6wG1jFaG2k3/eIuiTKAG8RAuQnb6Cd9k5BRqTm5uPIiSo0AP/u11jDomZ8j7+WEgkU9+Btw== +web3-eth-accounts@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.5.3.tgz#076c816ff4d68c9dffebdc7fd2bfaddcfc163d77" + integrity sha512-pdGhXgeBaEJENMvRT6W9cmji3Zz/46ugFSvmnLLw79qi5EH7XJhKISNVb41eWCrs4am5GhI67GLx5d2s2a72iw== dependencies: - "@ethereumjs/common" "^2.5.0" - "@ethereumjs/tx" "^3.3.2" + "@ethereumjs/common" "^2.3.0" + "@ethereumjs/tx" "^3.2.1" crypto-browserify "3.12.0" eth-lib "0.2.8" ethereumjs-util "^7.0.10" scrypt-js "^3.0.1" uuid "3.3.2" - web3-core "1.7.4" - web3-core-helpers "1.7.4" - web3-core-method "1.7.4" - web3-utils "1.7.4" + web3-core "1.5.3" + web3-core-helpers "1.5.3" + web3-core-method "1.5.3" + web3-utils "1.5.3" web3-eth-contract@1.2.11: version "1.2.11" @@ -12476,19 +12259,19 @@ web3-eth-contract@1.2.11: web3-eth-abi "1.2.11" web3-utils "1.2.11" -web3-eth-contract@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.7.4.tgz#e5761cfb43d453f57be4777b2e5e7e1082078ff7" - integrity sha512-ZgSZMDVI1pE9uMQpK0T0HDT2oewHcfTCv0osEqf5qyn5KrcQDg1GT96/+S0dfqZ4HKj4lzS5O0rFyQiLPQ8LzQ== +web3-eth-contract@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.5.3.tgz#12b03a4a16ce583a945f874bea2ff2fb4c5b81ad" + integrity sha512-Gdlt1L6cdHe83k7SdV6xhqCytVtOZkjD0kY/15x441AuuJ4JLubCHuqu69k2Dr3tWifHYVys/vG8QE/W16syGg== dependencies: - "@types/bn.js" "^5.1.0" - web3-core "1.7.4" - web3-core-helpers "1.7.4" - web3-core-method "1.7.4" - web3-core-promievent "1.7.4" - web3-core-subscriptions "1.7.4" - web3-eth-abi "1.7.4" - web3-utils "1.7.4" + "@types/bn.js" "^4.11.5" + web3-core "1.5.3" + web3-core-helpers "1.5.3" + web3-core-method "1.5.3" + web3-core-promievent "1.5.3" + web3-core-subscriptions "1.5.3" + web3-eth-abi "1.5.3" + web3-utils "1.5.3" web3-eth-ens@1.2.11: version "1.2.11" @@ -12505,19 +12288,19 @@ web3-eth-ens@1.2.11: web3-eth-contract "1.2.11" web3-utils "1.2.11" -web3-eth-ens@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.7.4.tgz#346720305379c0a539e226141a9602f1da7bc0c8" - integrity sha512-Gw5CVU1+bFXP5RVXTCqJOmHn71X2ghNk9VcEH+9PchLr0PrKbHTA3hySpsPco1WJAyK4t8SNQVlNr3+bJ6/WZA== +web3-eth-ens@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.5.3.tgz#ef6eee1ddf32b1ff9536fc7c599a74f2656bafe1" + integrity sha512-QmGFFtTGElg0E+3xfCIFhiUF+1imFi9eg/cdsRMUZU4F1+MZCC/ee+IAelYLfNTGsEslCqfAusliKOT9DdGGnw== dependencies: content-hash "^2.5.2" eth-ens-namehash "2.0.8" - web3-core "1.7.4" - web3-core-helpers "1.7.4" - web3-core-promievent "1.7.4" - web3-eth-abi "1.7.4" - web3-eth-contract "1.7.4" - web3-utils "1.7.4" + web3-core "1.5.3" + web3-core-helpers "1.5.3" + web3-core-promievent "1.5.3" + web3-eth-abi "1.5.3" + web3-eth-contract "1.5.3" + web3-utils "1.5.3" web3-eth-iban@1.2.11: version "1.2.11" @@ -12527,13 +12310,13 @@ web3-eth-iban@1.2.11: bn.js "^4.11.9" web3-utils "1.2.11" -web3-eth-iban@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.7.4.tgz#711fb2547fdf0f988060027331b2b6c430505753" - integrity sha512-XyrsgWlZQMv5gRcjXMsNvAoCRvV5wN7YCfFV5+tHUCqN8g9T/o4XUS20vDWD0k4HNiAcWGFqT1nrls02MGZ08w== +web3-eth-iban@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.5.3.tgz#91b1475893a877b10eac1de5cce6eb379fb81b5d" + integrity sha512-vMzmGqolYZvRHwP9P4Nf6G8uYM5aTLlQu2a34vz78p0KlDC+eV1th3+90Qeaupa28EG7OO0IT1F0BejiIauOPw== dependencies: - bn.js "^5.2.1" - web3-utils "1.7.4" + bn.js "^4.11.9" + web3-utils "1.5.3" web3-eth-personal@1.2.11: version "1.2.11" @@ -12547,17 +12330,17 @@ web3-eth-personal@1.2.11: web3-net "1.2.11" web3-utils "1.2.11" -web3-eth-personal@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.7.4.tgz#22c399794cb828a75703df8bb4b3c1331b471546" - integrity sha512-O10C1Hln5wvLQsDhlhmV58RhXo+GPZ5+W76frSsyIrkJWLtYQTCr5WxHtRC9sMD1idXLqODKKgI2DL+7xeZ0/g== +web3-eth-personal@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.5.3.tgz#4ebe09e9a77dd49d23d93b36b36cfbf4a6dae713" + integrity sha512-JzibJafR7ak/Icas8uvos3BmUNrZw1vShuNR5Cxjo+vteOC8XMqz1Vr7RH65B4bmlfb3bm9xLxetUHO894+Sew== dependencies: "@types/node" "^12.12.6" - web3-core "1.7.4" - web3-core-helpers "1.7.4" - web3-core-method "1.7.4" - web3-net "1.7.4" - web3-utils "1.7.4" + web3-core "1.5.3" + web3-core-helpers "1.5.3" + web3-core-method "1.5.3" + web3-net "1.5.3" + web3-utils "1.5.3" web3-eth@1.2.11: version "1.2.11" @@ -12578,23 +12361,23 @@ web3-eth@1.2.11: web3-net "1.2.11" web3-utils "1.2.11" -web3-eth@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.7.4.tgz#a7c1d3ccdbba4de4a82df7e3c4db716e4a944bf2" - integrity sha512-JG0tTMv0Ijj039emXNHi07jLb0OiWSA9O24MRSk5vToTQyDNXihdF2oyq85LfHuF690lXZaAXrjhtLNlYqb7Ug== - dependencies: - web3-core "1.7.4" - web3-core-helpers "1.7.4" - web3-core-method "1.7.4" - web3-core-subscriptions "1.7.4" - web3-eth-abi "1.7.4" - web3-eth-accounts "1.7.4" - web3-eth-contract "1.7.4" - web3-eth-ens "1.7.4" - web3-eth-iban "1.7.4" - web3-eth-personal "1.7.4" - web3-net "1.7.4" - web3-utils "1.7.4" +web3-eth@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.5.3.tgz#d7d1ac7198f816ab8a2088c01e0bf1eda45862fe" + integrity sha512-saFurA1L23Bd7MEf7cBli6/jRdMhD4X/NaMiO2mdMMCXlPujoudlIJf+VWpRWJpsbDFdu7XJ2WHkmBYT5R3p1Q== + dependencies: + web3-core "1.5.3" + web3-core-helpers "1.5.3" + web3-core-method "1.5.3" + web3-core-subscriptions "1.5.3" + web3-eth-abi "1.5.3" + web3-eth-accounts "1.5.3" + web3-eth-contract "1.5.3" + web3-eth-ens "1.5.3" + web3-eth-iban "1.5.3" + web3-eth-personal "1.5.3" + web3-net "1.5.3" + web3-utils "1.5.3" web3-net@1.2.11: version "1.2.11" @@ -12605,14 +12388,14 @@ web3-net@1.2.11: web3-core-method "1.2.11" web3-utils "1.2.11" -web3-net@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.7.4.tgz#3153dfd3423262dd6fbec7aae5467202c4cad431" - integrity sha512-d2Gj+DIARHvwIdmxFQ4PwAAXZVxYCR2lET0cxz4KXbE5Og3DNjJi+MoPkX+WqoUXqimu/EOd4Cd+7gefqVAFDg== +web3-net@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.5.3.tgz#545fee49b8e213b0c55cbe74ffd0295766057463" + integrity sha512-0W/xHIPvgVXPSdLu0iZYnpcrgNnhzHMC888uMlGP5+qMCt8VuflUZHy7tYXae9Mzsg1kxaJAS5lHVNyeNw4CoQ== dependencies: - web3-core "1.7.4" - web3-core-method "1.7.4" - web3-utils "1.7.4" + web3-core "1.5.3" + web3-core-method "1.5.3" + web3-utils "1.5.3" web3-provider-engine@14.2.1: version "14.2.1" @@ -12648,12 +12431,12 @@ web3-providers-http@1.2.11: web3-core-helpers "1.2.11" xhr2-cookies "1.1.0" -web3-providers-http@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.7.4.tgz#8209cdcb115db5ccae1f550d1c4e3005e7538d02" - integrity sha512-AU+/S+49rcogUER99TlhW+UBMk0N2DxvN54CJ2pK7alc2TQ7+cprNPLHJu4KREe8ndV0fT6JtWUfOMyTvl+FRA== +web3-providers-http@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.5.3.tgz#74f170fc3d79eb7941d9fbc34e2a067d61ced0b2" + integrity sha512-5DpUyWGHtDAr2RYmBu34Fu+4gJuBAuNx2POeiJIooUtJ+Mu6pIx4XkONWH6V+Ez87tZAVAsFOkJRTYuzMr3rPw== dependencies: - web3-core-helpers "1.7.4" + web3-core-helpers "1.5.3" xhr2-cookies "1.1.0" web3-providers-ipc@1.2.11: @@ -12665,13 +12448,13 @@ web3-providers-ipc@1.2.11: underscore "1.9.1" web3-core-helpers "1.2.11" -web3-providers-ipc@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.7.4.tgz#02e85e99e48f432c9d34cee7d786c3685ec9fcfa" - integrity sha512-jhArOZ235dZy8fS8090t60nTxbd1ap92ibQw5xIrAQ9m7LcZKNfmLAQUVsD+3dTFvadRMi6z1vCO7zRi84gWHw== +web3-providers-ipc@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.5.3.tgz#4bd7f5e445c2f3c2595fce0929c72bb879320a3f" + integrity sha512-JmeAptugVpmXI39LGxUSAymx0NOFdgpuI1hGQfIhbEAcd4sv7fhfd5D+ZU4oLHbRI8IFr4qfGU0uhR8BXhDzlg== dependencies: oboe "2.1.5" - web3-core-helpers "1.7.4" + web3-core-helpers "1.5.3" web3-providers-ws@1.2.11: version "1.2.11" @@ -12683,13 +12466,13 @@ web3-providers-ws@1.2.11: web3-core-helpers "1.2.11" websocket "^1.0.31" -web3-providers-ws@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.7.4.tgz#6e60bcefb456f569a3e766e386d7807a96f90595" - integrity sha512-g72X77nrcHMFU8hRzQJzfgi/072n8dHwRCoTw+WQrGp+XCQ71fsk2qIu3Tp+nlp5BPn8bRudQbPblVm2uT4myQ== +web3-providers-ws@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.5.3.tgz#eec6cfb32bb928a4106de506f13a49070a21eabf" + integrity sha512-6DhTw4Q7nm5CFYEUHOJM0gAb3xFx+9gWpVveg3YxJ/ybR1BUvEWo3bLgIJJtX56cYX0WyY6DS35a7f0LOI1kVg== dependencies: eventemitter3 "4.0.4" - web3-core-helpers "1.7.4" + web3-core-helpers "1.5.3" websocket "^1.0.32" web3-shh@1.2.11: @@ -12702,15 +12485,15 @@ web3-shh@1.2.11: web3-core-subscriptions "1.2.11" web3-net "1.2.11" -web3-shh@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.7.4.tgz#bee91cce2737c529fd347274010b548b6ea060f1" - integrity sha512-mlSZxSYcMkuMCxqhTYnZkUdahZ11h+bBv/8TlkXp/IHpEe4/Gg+KAbmfudakq3EzG/04z70XQmPgWcUPrsEJ+A== +web3-shh@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.5.3.tgz#3c04aa4cda9ba0b746d7225262401160f8e38b13" + integrity sha512-COfEXfsqoV/BkcsNLRxQqnWc1Teb8/9GxdGag5GtPC5gQC/vsN+7hYVJUwNxY9LtJPKYTij2DHHnx6UkITng+Q== dependencies: - web3-core "1.7.4" - web3-core-method "1.7.4" - web3-core-subscriptions "1.7.4" - web3-net "1.7.4" + web3-core "1.5.3" + web3-core-method "1.5.3" + web3-core-subscriptions "1.5.3" + web3-net "1.5.3" web3-utils@1.2.11: version "1.2.11" @@ -12726,12 +12509,25 @@ web3-utils@1.2.11: underscore "1.9.1" utf8 "3.0.0" -web3-utils@1.7.4, web3-utils@^1.0.0-beta.31, web3-utils@^1.3.0: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.7.4.tgz#eb6fa3706b058602747228234453811bbee017f5" - integrity sha512-acBdm6Evd0TEZRnChM/MCvGsMwYKmSh7OaUfNf5OKG0CIeGWD/6gqLOWIwmwSnre/2WrA1nKGId5uW2e5EfluA== +web3-utils@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.5.3.tgz#e914c9320cd663b2a09a5cb920ede574043eb437" + integrity sha512-56nRgA+Ad9SEyCv39g36rTcr5fpsd4L9LgV3FK0aB66nAMazLAA6Qz4lH5XrUKPDyBIPGJIR+kJsyRtwcu2q1Q== + dependencies: + bn.js "^4.11.9" + eth-lib "0.2.8" + ethereum-bloom-filters "^1.0.6" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + utf8 "3.0.0" + +web3-utils@^1.0.0-beta.31, web3-utils@^1.3.0: + version "1.7.1" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.7.1.tgz#77d8bacaf426c66027d8aa4864d77f0ed211aacd" + integrity sha512-fef0EsqMGJUgiHPdX+KN9okVWshbIumyJPmR+btnD1HgvoXijKEkuKBv0OmUqjbeqmLKP2/N9EiXKJel5+E1Dw== dependencies: - bn.js "^5.2.1" + bn.js "^4.11.9" ethereum-bloom-filters "^1.0.6" ethereumjs-util "^7.1.0" ethjs-unit "0.1.6" @@ -12752,18 +12548,18 @@ web3@1.2.11: web3-shh "1.2.11" web3-utils "1.2.11" -web3@1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/web3/-/web3-1.7.4.tgz#00c9aef8e13ade92fd773d845fff250535828e93" - integrity sha512-iFGK5jO32vnXM/ASaJBaI0+gVR6uHozvYdxkdhaeOCD6HIQ4iIXadbO2atVpE9oc/H8l2MovJ4LtPhG7lIBN8A== +web3@1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/web3/-/web3-1.5.3.tgz#11882679453c645bf33620fbc255a243343075aa" + integrity sha512-eyBg/1K44flfv0hPjXfKvNwcUfIVDI4NX48qHQe6wd7C8nPSdbWqo9vLy6ksZIt9NLa90HjI8HsGYgnMSUxn6w== dependencies: - web3-bzz "1.7.4" - web3-core "1.7.4" - web3-eth "1.7.4" - web3-eth-personal "1.7.4" - web3-net "1.7.4" - web3-shh "1.7.4" - web3-utils "1.7.4" + web3-bzz "1.5.3" + web3-core "1.5.3" + web3-eth "1.5.3" + web3-eth-personal "1.5.3" + web3-net "1.5.3" + web3-shh "1.5.3" + web3-utils "1.5.3" webidl-conversions@^3.0.0: version "3.0.1" @@ -12834,16 +12630,16 @@ which-module@^2.0.0: integrity sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q== which-typed-array@^1.1.2: - version "1.1.8" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.8.tgz#0cfd53401a6f334d90ed1125754a42ed663eb01f" - integrity sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw== + version "1.1.7" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.7.tgz#2761799b9a22d4b8660b3c1b40abaa7739691793" + integrity sha512-vjxaB4nfDqwKI0ws7wZpxIlde1XrLX5uB0ZjpfshgmapJMD7jJWhZI+yToJTqaFByF0eNBcYxbjmCzoRP7CfEw== dependencies: available-typed-arrays "^1.0.5" call-bind "^1.0.2" - es-abstract "^1.20.0" - for-each "^0.3.3" + es-abstract "^1.18.5" + foreach "^2.0.5" has-tostringtag "^1.0.0" - is-typed-array "^1.1.9" + is-typed-array "^1.1.7" which@1.3.1, which@^1.1.1, which@^1.2.9, which@^1.3.1: version "1.3.1" @@ -12852,7 +12648,7 @@ which@1.3.1, which@^1.1.1, which@^1.2.9, which@^1.3.1: dependencies: isexe "^2.0.0" -which@^2.0.1: +which@2.0.2, which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== @@ -12881,9 +12677,9 @@ winston-transport@^4.5.0: triple-beam "^1.3.0" winston@*, winston@^3.3.3: - version "3.8.1" - resolved "https://registry.yarnpkg.com/winston/-/winston-3.8.1.tgz#76f15b3478cde170b780234e0c4cf805c5a7fb57" - integrity sha512-r+6YAiCR4uI3N8eQNOg8k3P3PqwAm20cLKlzVD9E66Ch39+LZC+VH1UKf9JemQj2B3QoUHfKD7Poewn0Pr3Y1w== + version "3.7.2" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.7.2.tgz#95b4eeddbec902b3db1424932ac634f887c400b1" + integrity sha512-QziIqtojHBoyzUOdQvQiar1DH0Xp9nF1A1y7NVy2DGEsz82SBDtOalS0ulTRGVT14xPX3WRWkCsdcJKqNflKng== dependencies: "@dabh/diagnostics" "^2.0.2" async "^3.2.3" @@ -12918,6 +12714,11 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== +workerpool@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.0.tgz#827d93c9ba23ee2019c3ffaff5c27fccea289e8b" + integrity sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A== + workerpool@6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" @@ -12992,9 +12793,9 @@ ws@^5.1.1: async-limiter "~1.0.0" ws@^7.4.6: - version "7.5.9" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" - integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + version "7.5.7" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67" + integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A== xhr-request-promise@^0.1.2: version "0.1.3" @@ -13165,9 +12966,9 @@ yargs@16.2.0: yargs-parser "^20.2.2" yargs@^17.0.0: - version "17.5.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.5.1.tgz#e109900cab6fcb7fd44b1d8249166feb0b36e58e" - integrity sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA== + version "17.4.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.4.0.tgz#9fc9efc96bd3aa2c1240446af28499f0e7593d00" + integrity sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA== dependencies: cliui "^7.0.2" escalade "^3.1.1" @@ -13206,8 +13007,3 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== - -zksync-web3@^0.7.8: - version "0.7.9" - resolved "https://registry.yarnpkg.com/zksync-web3/-/zksync-web3-0.7.9.tgz#fbb9a17f4b297c0fb9361de2a0d85ff2aac5becc" - integrity sha512-B0pitKvEQGJuWkY2UWjXrL1YgHghXEoDaq6acVZnB62TRF099GV58Fzi7Fnqt+Nw14A7Wc9iJ2AHD4GBTLFgkg==