From db2cc4f0e00097dd8e2ae2116ec6fae795ded273 Mon Sep 17 00:00:00 2001 From: Miguel de Elias Date: Fri, 30 Oct 2026 12:22:21 -0300 Subject: [PATCH] chore: remove contracts-test and the sdk --- .gitignore | 1 - packages/contracts-test/.solcover.js | 17 - packages/contracts-test/CHANGELOG.md | 24 - .../config/graph.arbitrum-goerli.yml | 152 --- .../config/graph.arbitrum-hardhat.yml | 163 --- .../config/graph.arbitrum-localhost.yml | 164 --- .../config/graph.arbitrum-one.yml | 152 --- .../config/graph.arbitrum-sepolia.yml | 163 --- .../contracts-test/config/graph.goerli.yml | 161 --- .../contracts-test/config/graph.hardhat.yml | 160 --- .../contracts-test/config/graph.localhost.yml | 161 --- .../contracts-test/config/graph.mainnet.yml | 161 --- .../contracts-test/config/graph.sepolia.yml | 161 --- packages/contracts-test/contracts | 1 - packages/contracts-test/hardhat.config.ts | 94 -- packages/contracts-test/package.json | 76 -- packages/contracts-test/prettier.config.cjs | 5 - packages/contracts-test/scripts/coverage | 11 - packages/contracts-test/scripts/e2e | 193 ---- packages/contracts-test/scripts/evm | 65 -- .../contracts-test/scripts/setup-symlinks | 32 - packages/contracts-test/scripts/test | 42 - .../contracts-test/scripts/test-coverage-file | 21 - .../contracts-test/tasks/migrate/nitro.ts | 64 -- packages/contracts-test/tasks/test-upgrade.ts | 52 - .../tests/unit/curation/configuration.test.ts | 130 --- .../tests/unit/curation/curation.test.ts | 526 --------- .../tests/unit/disputes/common.ts | 42 - .../tests/unit/disputes/configuration.test.ts | 136 --- .../tests/unit/disputes/poi.test.ts | 230 ---- .../tests/unit/disputes/query.test.ts | 501 -------- .../tests/unit/l2/l2Staking.test.ts | 375 ------ .../contracts-test/tests/unit/lib/fixtures.ts | 173 --- .../unit/payments/allocationExchange.test.ts | 269 ----- .../unit/rewards/subgraphAvailability.test.ts | 414 ------- .../tests/unit/serviceRegisty.test.ts | 137 --- .../tests/unit/staking/allocation.test.ts | 1007 ----------------- .../tests/unit/staking/configuration.test.ts | 221 ---- .../tests/unit/staking/delegation.test.ts | 577 ---------- .../tests/unit/staking/l2Transfer.test.ts | 736 ------------ .../tests/unit/staking/rebate.test.ts | 238 ---- .../tests/unit/staking/staking.test.ts | 510 --------- packages/contracts-test/tsconfig.json | 12 - packages/contracts-test/utils/coverage.ts | 13 - packages/contracts/.mocharc.json | 5 - packages/contracts/hardhat.config.ts | 3 - packages/contracts/package.json | 4 +- .../data.json | 342 ------ .../migrate.ts | 67 -- .../ops/testDisputeConflict/acceptDispute.ts | 20 - .../ops/testDisputeConflict/createDispute.ts | 46 - .../ops/testDisputeConflict/setupIndexer.ts | 40 - packages/contracts/tsconfig.json | 2 +- pnpm-lock.yaml | 670 +---------- pnpm-workspace.yaml | 3 - 55 files changed, 3 insertions(+), 9742 deletions(-) delete mode 100644 packages/contracts-test/.solcover.js delete mode 100644 packages/contracts-test/CHANGELOG.md delete mode 100644 packages/contracts-test/config/graph.arbitrum-goerli.yml delete mode 100644 packages/contracts-test/config/graph.arbitrum-hardhat.yml delete mode 100644 packages/contracts-test/config/graph.arbitrum-localhost.yml delete mode 100644 packages/contracts-test/config/graph.arbitrum-one.yml delete mode 100644 packages/contracts-test/config/graph.arbitrum-sepolia.yml delete mode 100644 packages/contracts-test/config/graph.goerli.yml delete mode 100644 packages/contracts-test/config/graph.hardhat.yml delete mode 100644 packages/contracts-test/config/graph.localhost.yml delete mode 100644 packages/contracts-test/config/graph.mainnet.yml delete mode 100644 packages/contracts-test/config/graph.sepolia.yml delete mode 120000 packages/contracts-test/contracts delete mode 100644 packages/contracts-test/hardhat.config.ts delete mode 100644 packages/contracts-test/package.json delete mode 100644 packages/contracts-test/prettier.config.cjs delete mode 100755 packages/contracts-test/scripts/coverage delete mode 100755 packages/contracts-test/scripts/e2e delete mode 100755 packages/contracts-test/scripts/evm delete mode 100755 packages/contracts-test/scripts/setup-symlinks delete mode 100755 packages/contracts-test/scripts/test delete mode 100755 packages/contracts-test/scripts/test-coverage-file delete mode 100644 packages/contracts-test/tasks/migrate/nitro.ts delete mode 100644 packages/contracts-test/tasks/test-upgrade.ts delete mode 100644 packages/contracts-test/tests/unit/curation/configuration.test.ts delete mode 100644 packages/contracts-test/tests/unit/curation/curation.test.ts delete mode 100644 packages/contracts-test/tests/unit/disputes/common.ts delete mode 100644 packages/contracts-test/tests/unit/disputes/configuration.test.ts delete mode 100644 packages/contracts-test/tests/unit/disputes/poi.test.ts delete mode 100644 packages/contracts-test/tests/unit/disputes/query.test.ts delete mode 100644 packages/contracts-test/tests/unit/l2/l2Staking.test.ts delete mode 100644 packages/contracts-test/tests/unit/lib/fixtures.ts delete mode 100644 packages/contracts-test/tests/unit/payments/allocationExchange.test.ts delete mode 100644 packages/contracts-test/tests/unit/rewards/subgraphAvailability.test.ts delete mode 100644 packages/contracts-test/tests/unit/serviceRegisty.test.ts delete mode 100644 packages/contracts-test/tests/unit/staking/allocation.test.ts delete mode 100644 packages/contracts-test/tests/unit/staking/configuration.test.ts delete mode 100644 packages/contracts-test/tests/unit/staking/delegation.test.ts delete mode 100644 packages/contracts-test/tests/unit/staking/l2Transfer.test.ts delete mode 100644 packages/contracts-test/tests/unit/staking/rebate.test.ts delete mode 100644 packages/contracts-test/tests/unit/staking/staking.test.ts delete mode 100644 packages/contracts-test/tsconfig.json delete mode 100644 packages/contracts-test/utils/coverage.ts delete mode 100644 packages/contracts/.mocharc.json delete mode 100644 packages/contracts/scripts/ops/20240208-migrate-legacy-subgraphs/data.json delete mode 100644 packages/contracts/scripts/ops/20240208-migrate-legacy-subgraphs/migrate.ts delete mode 100644 packages/contracts/scripts/ops/testDisputeConflict/acceptDispute.ts delete mode 100644 packages/contracts/scripts/ops/testDisputeConflict/createDispute.ts delete mode 100644 packages/contracts/scripts/ops/testDisputeConflict/setupIndexer.ts diff --git a/.gitignore b/.gitignore index ba06116ad..231c9c7a8 100644 --- a/.gitignore +++ b/.gitignore @@ -41,7 +41,6 @@ typechain-types/ types-v5/ wagmi/ packages/contracts/types/ -packages/contracts-test/types/ packages/interfaces/types/ packages/token-distribution/types/ packages/issuance/types/ diff --git a/packages/contracts-test/.solcover.js b/packages/contracts-test/.solcover.js deleted file mode 100644 index 125581cd1..000000000 --- a/packages/contracts-test/.solcover.js +++ /dev/null @@ -1,17 +0,0 @@ -const skipFiles = ['bancor', 'ens', 'erc1056', 'arbitrum', 'tests', '*Mock.sol'] - -module.exports = { - providerOptions: { - mnemonic: 'myth like bonus scare over problem client lizard pioneer submit female collect', - network_id: 1337, - }, - skipFiles, - istanbulFolder: '../coverage', - // Remove 'html' reporter to avoid duplicates, keep lcov for lcov.info - istanbulReporter: ['lcov', 'text', 'json'], - configureYulOptimizer: true, - mocha: { - grep: '@skip-on-coverage', - invert: true, - }, -} diff --git a/packages/contracts-test/CHANGELOG.md b/packages/contracts-test/CHANGELOG.md deleted file mode 100644 index 80966da99..000000000 --- a/packages/contracts-test/CHANGELOG.md +++ /dev/null @@ -1,24 +0,0 @@ -# @graphprotocol/contracts-tests - -## 1.0.3 - -### Patch Changes - -- Updated dependencies - - @graphprotocol/contracts@7.3.0 - -## 1.0.2 - -### Patch Changes - -- Updated dependencies - - @graphprotocol/contracts@7.2.1 - - @graphprotocol/sdk@0.6.2 - -## 1.0.1 - -### Patch Changes - -- Updated dependencies - - @graphprotocol/contracts@7.2.0 - - @graphprotocol/sdk@0.6.1 diff --git a/packages/contracts-test/config/graph.arbitrum-goerli.yml b/packages/contracts-test/config/graph.arbitrum-goerli.yml deleted file mode 100644 index 10c57d7ec..000000000 --- a/packages/contracts-test/config/graph.arbitrum-goerli.yml +++ /dev/null @@ -1,152 +0,0 @@ -general: - arbitrator: &arbitrator '0xF89688d5d44d73cc4dE880857A3940487076e5A4' # Arbitration Council (TODO: update) - governor: &governor '0x5CeeeE16F30357d49c50bcd7F520ca6527cf388a' # Graph Council (TODO: update) - authority: &authority '0xac01B0b3B2Dc5D8E0D484c02c4d077C15C96a7b4' # Authority that signs payment vouchers - 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: '${{L2Curation.address}}' - - fn: 'setContractProxy' - id: '0x39605a6c26a173774ca666c67ef70cf491880e5d3d6d0ca66ec0a31034f15ea3' # keccak256('GNS') - contractAddress: '${{L2GNS.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: '${{L2Staking.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: 'addMinter' - minter: '${{RewardsManager.address}}' - - fn: 'renounceMinter' - - fn: 'transferOwnership' - owner: *governor - L2Curation: - proxy: true - init: - controller: '${{Controller.address}}' - curationTokenMaster: '${{GraphCurationToken.address}}' - curationTaxPercentage: 10000 # in parts per million - minimumCurationDeposit: '1' # 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' - L2GNS: - proxy: true - init: - controller: '${{Controller.address}}' - subgraphNFT: '${{SubgraphNFT.address}}' - calls: - - fn: 'approveAll' - - fn: 'syncAllContracts' - SubgraphNFT: - init: - governor: '${{Env.deployer}}' - calls: - - fn: 'setTokenDescriptor' - tokenDescriptor: '${{SubgraphNFTDescriptor.address}}' - - fn: 'setMinter' - minter: '${{L2GNS.address}}' - - fn: 'transferOwnership' - owner: *governor - L2Staking: - 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 - maxAllocationEpochs: 8 # in epochs - delegationUnbondingPeriod: 12 # in epochs - delegationRatio: 16 # delegated stake to indexer stake multiplier - rebateParameters: - alphaNumerator: 100 # alphaNumerator / alphaDenominator - alphaDenominator: 100 # alphaNumerator / alphaDenominator - lambdaNumerator: 60 # lambdaNumerator / lambdaDenominator - lambdaDenominator: 100 # lambdaNumerator / lambdaDenominator - extensionImpl: '${{StakingExtension.address}}' - calls: - - fn: 'setDelegationTaxPercentage' - delegationTaxPercentage: 5000 # parts per million - - fn: 'setSlasher' - slasher: '${{DisputeManager.address}}' - allowed: true - - fn: 'syncAllContracts' - RewardsManager: - proxy: true - init: - controller: '${{Controller.address}}' - calls: - - fn: 'setIssuancePerBlock' - issuancePerBlock: '6036500000000000000' # per block increase of total supply, blocks in a year = 365*60*60*24/12 - - fn: 'setSubgraphAvailabilityOracle' - subgraphAvailabilityOracle: *availabilityOracle - - fn: 'syncAllContracts' - AllocationExchange: - init: - graphToken: '${{L2GraphToken.address}}' - staking: '${{L2Staking.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/packages/contracts-test/config/graph.arbitrum-hardhat.yml b/packages/contracts-test/config/graph.arbitrum-hardhat.yml deleted file mode 100644 index e8f35847f..000000000 --- a/packages/contracts-test/config/graph.arbitrum-hardhat.yml +++ /dev/null @@ -1,163 +0,0 @@ -general: - arbitrator: &arbitrator '0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0' # Arbitration Council - governor: &governor '0x22d491Bde2303f2f43325b2108D26f1eAbA1e32b' # Graph Council - authority: &authority '0xE11BA2b4D45Eaed5996Cd0823791E0C93114882d' # Authority that signs payment vouchers - availabilityOracles: &availabilityOracles # Subgraph Availability Oracles - - '0xd03ea8624C8C5987235048901fB614fDcA89b117' - - '0xd03ea8624C8C5987235048901fB614fDcA89b117' - - '0xd03ea8624C8C5987235048901fB614fDcA89b117' - - '0xd03ea8624C8C5987235048901fB614fDcA89b117' - - '0xd03ea8624C8C5987235048901fB614fDcA89b117' - pauseGuardian: &pauseGuardian '0x95cED938F7991cd0dFcb48F0a06a40FA1aF46EBC' # Protocol pause guardian - allocationExchangeOwner: &allocationExchangeOwner '0x3E5e9111Ae8eB78Fe1CC3bb8915d5D461F3Ef9A9' # Allocation Exchange owner - -contracts: - Controller: - calls: - - fn: 'setContractProxy' - id: '0xe6876326c1291dfcbbd3864a6816d698cd591defc7aa2153d7f9c4c04016c89f' # keccak256('Curation') - contractAddress: '${{L2Curation.address}}' - - fn: 'setContractProxy' - id: '0x39605a6c26a173774ca666c67ef70cf491880e5d3d6d0ca66ec0a31034f15ea3' # keccak256('GNS') - contractAddress: '${{L2GNS.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: '${{L2Staking.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: 60 # length in hours = lengthInBlocks*13/60/60 (~13 second blocks) - L2GraphToken: - proxy: true - init: - owner: '${{Env.deployer}}' - calls: - - fn: 'addMinter' - minter: '${{RewardsManager.address}}' - - fn: 'transferOwnership' - owner: *governor - L2Curation: - proxy: true - init: - controller: '${{Controller.address}}' - curationTokenMaster: '${{GraphCurationToken.address}}' - curationTaxPercentage: 0 # in parts per million - minimumCurationDeposit: '1' # in wei - calls: - - fn: 'syncAllContracts' - DisputeManager: - proxy: true - init: - controller: '${{Controller.address}}' - arbitrator: *arbitrator - minimumDeposit: '100000000000000000000' # in wei - fishermanRewardPercentage: 1000 # in parts per million - qrySlashingPercentage: 1000 # in parts per million - idxSlashingPercentage: 100000 # in parts per million - calls: - - fn: 'syncAllContracts' - L2GNS: - proxy: true - init: - controller: '${{Controller.address}}' - subgraphNFT: '${{SubgraphNFT.address}}' - calls: - - fn: 'approveAll' - - fn: 'syncAllContracts' - SubgraphNFT: - init: - governor: '${{Env.deployer}}' - calls: - - fn: 'setTokenDescriptor' - tokenDescriptor: '${{SubgraphNFTDescriptor.address}}' - - fn: 'setMinter' - minter: '${{L2GNS.address}}' - - fn: 'transferOwnership' - owner: *governor - L2Staking: - proxy: true - init: - controller: '${{Controller.address}}' - minimumIndexerStake: '10000000000000000000' # in wei - thawingPeriod: 20 # in blocks - protocolPercentage: 0 # in parts per million - curationPercentage: 0 # in parts per million - maxAllocationEpochs: 5 # in epochs - delegationUnbondingPeriod: 1 # in epochs - delegationRatio: 16 # delegated stake to indexer stake multiplier - rebateParameters: - alphaNumerator: 100 # alphaNumerator / alphaDenominator - alphaDenominator: 100 # alphaNumerator / alphaDenominator - lambdaNumerator: 60 # lambdaNumerator / lambdaDenominator - lambdaDenominator: 100 # lambdaNumerator / lambdaDenominator - extensionImpl: '${{StakingExtension.address}}' - calls: - - fn: 'setDelegationTaxPercentage' - delegationTaxPercentage: 0 # parts per million - - fn: 'setSlasher' - slasher: '${{DisputeManager.address}}' - allowed: true - - fn: 'syncAllContracts' - RewardsManager: - proxy: true - init: - controller: '${{Controller.address}}' - calls: - - fn: 'setIssuancePerBlock' - issuancePerBlock: '114155251141552511415' # per block increase of total supply, blocks in a year = 365*60*60*24/12 - - fn: 'setSubgraphAvailabilityOracle' - subgraphAvailabilityOracle: '${{SubgraphAvailabilityManager.address}}' - - fn: 'syncAllContracts' - AllocationExchange: - init: - graphToken: '${{L2GraphToken.address}}' - staking: '${{L2Staking.address}}' - governor: *allocationExchangeOwner - authority: *authority - calls: - - fn: 'approveAll' - L2GraphTokenGateway: - proxy: true - init: - controller: '${{Controller.address}}' - calls: - - fn: 'syncAllContracts' - - fn: 'setPauseGuardian' - pauseGuardian: *pauseGuardian - SubgraphAvailabilityManager: - init: - governor: *governor - rewardsManager: '${{RewardsManager.address}}' - executionThreshold: 5 - voteTimeLimit: 300 - oracles: *availabilityOracles diff --git a/packages/contracts-test/config/graph.arbitrum-localhost.yml b/packages/contracts-test/config/graph.arbitrum-localhost.yml deleted file mode 100644 index b09508fef..000000000 --- a/packages/contracts-test/config/graph.arbitrum-localhost.yml +++ /dev/null @@ -1,164 +0,0 @@ -general: - arbitrator: &arbitrator '0x4237154FE0510FdE3575656B60c68a01B9dCDdF8' # Arbitration Council - governor: &governor '0x1257227a2ECA34834940110f7B5e341A5143A2c4' # Graph Council - authority: &authority '0x12B8D08b116E1E3cc29eE9Cf42bB0AA8129C3215' # Authority that signs payment vouchers - availabilityOracles: &availabilityOracles # Subgraph Availability Oracles - - '0x7694a48065f063a767a962610C6717c59F36b445' - - '0x7694a48065f063a767a962610C6717c59F36b445' - - '0x7694a48065f063a767a962610C6717c59F36b445' - - '0x7694a48065f063a767a962610C6717c59F36b445' - - '0x7694a48065f063a767a962610C6717c59F36b445' - pauseGuardian: &pauseGuardian '0x601060e0DC5349AA55EC73df5A58cB0FC1cD2e3C' # Protocol pause guardian - allocationExchangeOwner: &allocationExchangeOwner '0xbD38F7b67a591A5cc7D642e1026E5095B819d952' # Allocation Exchange owner - -contracts: - Controller: - calls: - - fn: 'setContractProxy' - id: '0xe6876326c1291dfcbbd3864a6816d698cd591defc7aa2153d7f9c4c04016c89f' # keccak256('Curation') - contractAddress: '${{L2Curation.address}}' - - fn: 'setContractProxy' - id: '0x39605a6c26a173774ca666c67ef70cf491880e5d3d6d0ca66ec0a31034f15ea3' # keccak256('GNS') - contractAddress: '${{L2GNS.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: '${{L2Staking.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: 'addMinter' - minter: '${{RewardsManager.address}}' - - fn: 'renounceMinter' - - fn: 'transferOwnership' - owner: *governor - L2Curation: - proxy: true - init: - controller: '${{Controller.address}}' - curationTokenMaster: '${{GraphCurationToken.address}}' - curationTaxPercentage: 10000 # in parts per million - minimumCurationDeposit: '1' # 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' - L2GNS: - proxy: true - init: - controller: '${{Controller.address}}' - subgraphNFT: '${{SubgraphNFT.address}}' - calls: - - fn: 'approveAll' - - fn: 'syncAllContracts' - SubgraphNFT: - init: - governor: '${{Env.deployer}}' - calls: - - fn: 'setTokenDescriptor' - tokenDescriptor: '${{SubgraphNFTDescriptor.address}}' - - fn: 'setMinter' - minter: '${{L2GNS.address}}' - - fn: 'transferOwnership' - owner: *governor - L2Staking: - 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 - maxAllocationEpochs: 4 # in epochs - delegationUnbondingPeriod: 12 # in epochs - delegationRatio: 16 # delegated stake to indexer stake multiplier - rebateParameters: - alphaNumerator: 100 # alphaNumerator / alphaDenominator - alphaDenominator: 100 # alphaNumerator / alphaDenominator - lambdaNumerator: 60 # lambdaNumerator / lambdaDenominator - lambdaDenominator: 100 # lambdaNumerator / lambdaDenominator - extensionImpl: '${{StakingExtension.address}}' - calls: - - fn: 'setDelegationTaxPercentage' - delegationTaxPercentage: 5000 # parts per million - - fn: 'setSlasher' - slasher: '${{DisputeManager.address}}' - allowed: true - - fn: 'syncAllContracts' - RewardsManager: - proxy: true - init: - controller: '${{Controller.address}}' - calls: - - fn: 'setIssuancePerBlock' - issuancePerBlock: '6036500000000000000' # per block increase of total supply, blocks in a year = 365*60*60*24/12 - - fn: 'setSubgraphAvailabilityOracle' - subgraphAvailabilityOracle: '${{SubgraphAvailabilityManager.address}}' - - fn: 'syncAllContracts' - AllocationExchange: - init: - graphToken: '${{L2GraphToken.address}}' - staking: '${{L2Staking.address}}' - governor: *allocationExchangeOwner - authority: *authority - calls: - - fn: 'approveAll' - L2GraphTokenGateway: - proxy: true - init: - controller: '${{Controller.address}}' - calls: - - fn: 'syncAllContracts' - - fn: 'setPauseGuardian' - pauseGuardian: *pauseGuardian - SubgraphAvailabilityManager: - init: - governor: *governor - rewardsManager: '${{RewardsManager.address}}' - executionThreshold: 5 - voteTimeLimit: 300 - oracles: *availabilityOracles diff --git a/packages/contracts-test/config/graph.arbitrum-one.yml b/packages/contracts-test/config/graph.arbitrum-one.yml deleted file mode 100644 index c3796b548..000000000 --- a/packages/contracts-test/config/graph.arbitrum-one.yml +++ /dev/null @@ -1,152 +0,0 @@ -general: - arbitrator: &arbitrator '0x113DC95e796836b8F0Fa71eE7fB42f221740c3B0' # Arbitration Council - governor: &governor '0x8C6de8F8D562f3382417340A6994601eE08D3809' # Graph Council - authority: &authority '0x4a06858f104B2aB1e1185AB7E09F7B5d3b700479' # Authority that signs payment vouchers - availabilityOracle: &availabilityOracle '0x1BEB2266f264Cebd9C6FBE1ceB394a8d944401c1' # 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: '${{L2Curation.address}}' - - fn: 'setContractProxy' - id: '0x39605a6c26a173774ca666c67ef70cf491880e5d3d6d0ca66ec0a31034f15ea3' # keccak256('GNS') - contractAddress: '${{L2GNS.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: '${{L2Staking.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: 'addMinter' - minter: '${{RewardsManager.address}}' - - fn: 'renounceMinter' - - fn: 'transferOwnership' - owner: *governor - L2Curation: - proxy: true - init: - controller: '${{Controller.address}}' - curationTokenMaster: '${{GraphCurationToken.address}}' - curationTaxPercentage: 10000 # in parts per million - minimumCurationDeposit: '1' # 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' - L2GNS: - proxy: true - init: - controller: '${{Controller.address}}' - subgraphNFT: '${{SubgraphNFT.address}}' - calls: - - fn: 'approveAll' - - fn: 'syncAllContracts' - SubgraphNFT: - init: - governor: '${{Env.deployer}}' - calls: - - fn: 'setTokenDescriptor' - tokenDescriptor: '${{SubgraphNFTDescriptor.address}}' - - fn: 'setMinter' - minter: '${{L2GNS.address}}' - - fn: 'transferOwnership' - owner: *governor - L2Staking: - 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 - maxAllocationEpochs: 28 # in epochs - delegationUnbondingPeriod: 28 # in epochs - delegationRatio: 16 # delegated stake to indexer stake multiplier - rebateParameters: - alphaNumerator: 100 # alphaNumerator / alphaDenominator - alphaDenominator: 100 # alphaNumerator / alphaDenominator - lambdaNumerator: 60 # lambdaNumerator / lambdaDenominator - lambdaDenominator: 100 # lambdaNumerator / lambdaDenominator - extensionImpl: '${{StakingExtension.address}}' - calls: - - fn: 'setDelegationTaxPercentage' - delegationTaxPercentage: 5000 # parts per million - - fn: 'setSlasher' - slasher: '${{DisputeManager.address}}' - allowed: true - - fn: 'syncAllContracts' - RewardsManager: - proxy: true - init: - controller: '${{Controller.address}}' - calls: - - fn: 'setIssuancePerBlock' - issuancePerBlock: '6036500000000000000' # per block increase of total supply, blocks in a year = 365*60*60*24/12 - - fn: 'setSubgraphAvailabilityOracle' - subgraphAvailabilityOracle: *availabilityOracle - - fn: 'syncAllContracts' - AllocationExchange: - init: - graphToken: '${{L2GraphToken.address}}' - staking: '${{L2Staking.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/packages/contracts-test/config/graph.arbitrum-sepolia.yml b/packages/contracts-test/config/graph.arbitrum-sepolia.yml deleted file mode 100644 index 4d4c0785e..000000000 --- a/packages/contracts-test/config/graph.arbitrum-sepolia.yml +++ /dev/null @@ -1,163 +0,0 @@ -general: - arbitrator: &arbitrator '0x1726A5d52e279d02ff4732eCeB2D67BFE5Add328' # EOA (TODO: update to a multisig) - governor: &governor '0x72ee30d43Fb5A90B3FE983156C5d2fBE6F6d07B3' # EOA (TODO: update to a multisig) - authority: &authority '0x49D4CFC037430cA9355B422bAeA7E9391e1d3215' # Authority that signs payment vouchers - availabilityOracles: &availabilityOracles # Array of Subgraph Availability Oracles - - '0x5e4e823Ed094c035133eEC5Ec0d08ae1Af04e9Fa' - - '0x5aeE4c46cF9260E85E630ca7d9D757D5D5DbaFD6' - - '0x7369Cf2a917296c36f506144f3dE552403d1e1f1' - - '0x1e1f84c07e0471fc979f6f08228b0bd34cda9e54' - - '0x711aEA1f358DFAf74D34B4B525F9190e631F406C' - pauseGuardian: &pauseGuardian '0xa0444508232dA3FA6C2f96a5f105f3f0cc0d20D7' # Protocol pause guardian - allocationExchangeOwner: &allocationExchangeOwner '0x72ee30d43Fb5A90B3FE983156C5d2fBE6F6d07B3' # Allocation Exchange owner - -contracts: - Controller: - calls: - - fn: 'setContractProxy' - id: '0xe6876326c1291dfcbbd3864a6816d698cd591defc7aa2153d7f9c4c04016c89f' # keccak256('Curation') - contractAddress: '${{L2Curation.address}}' - - fn: 'setContractProxy' - id: '0x39605a6c26a173774ca666c67ef70cf491880e5d3d6d0ca66ec0a31034f15ea3' # keccak256('GNS') - contractAddress: '${{L2GNS.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: '${{L2Staking.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: 'addMinter' - minter: '${{RewardsManager.address}}' - - fn: 'transferOwnership' - owner: *governor - L2Curation: - proxy: true - init: - controller: '${{Controller.address}}' - curationTokenMaster: '${{GraphCurationToken.address}}' - curationTaxPercentage: 10000 # in parts per million - minimumCurationDeposit: '1' # 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' - L2GNS: - proxy: true - init: - controller: '${{Controller.address}}' - subgraphNFT: '${{SubgraphNFT.address}}' - calls: - - fn: 'approveAll' - - fn: 'syncAllContracts' - SubgraphNFT: - init: - governor: '${{Env.deployer}}' - calls: - - fn: 'setTokenDescriptor' - tokenDescriptor: '${{SubgraphNFTDescriptor.address}}' - - fn: 'setMinter' - minter: '${{L2GNS.address}}' - - fn: 'transferOwnership' - owner: *governor - L2Staking: - 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 - maxAllocationEpochs: 8 # in epochs - delegationUnbondingPeriod: 12 # in epochs - delegationRatio: 16 # delegated stake to indexer stake multiplier - rebateParameters: - alphaNumerator: 100 # alphaNumerator / alphaDenominator - alphaDenominator: 100 # alphaNumerator / alphaDenominator - lambdaNumerator: 60 # lambdaNumerator / lambdaDenominator - lambdaDenominator: 100 # lambdaNumerator / lambdaDenominator - extensionImpl: '${{StakingExtension.address}}' - calls: - - fn: 'setDelegationTaxPercentage' - delegationTaxPercentage: 5000 # parts per million - - fn: 'setSlasher' - slasher: '${{DisputeManager.address}}' - allowed: true - - fn: 'syncAllContracts' - RewardsManager: - proxy: true - init: - controller: '${{Controller.address}}' - calls: - - fn: 'setIssuancePerBlock' - issuancePerBlock: '6036500000000000000' # per block increase of total supply, blocks in a year = 365*60*60*24/12 - - fn: 'setSubgraphAvailabilityOracle' - subgraphAvailabilityOracle: '${{SubgraphAvailabilityManager.address}}' - - fn: 'syncAllContracts' - AllocationExchange: - init: - graphToken: '${{L2GraphToken.address}}' - staking: '${{L2Staking.address}}' - governor: *allocationExchangeOwner - authority: *authority - calls: - - fn: 'approveAll' - L2GraphTokenGateway: - proxy: true - init: - controller: '${{Controller.address}}' - calls: - - fn: 'syncAllContracts' - - fn: 'setPauseGuardian' - pauseGuardian: *pauseGuardian - SubgraphAvailabilityManager: - init: - governor: *governor - rewardsManager: '${{RewardsManager.address}}' - executionThreshold: 5 - voteTimeLimit: 300 - oracles: *availabilityOracles diff --git a/packages/contracts-test/config/graph.goerli.yml b/packages/contracts-test/config/graph.goerli.yml deleted file mode 100644 index f4128f936..000000000 --- a/packages/contracts-test/config/graph.goerli.yml +++ /dev/null @@ -1,161 +0,0 @@ -general: - arbitrator: &arbitrator '0xFD01aa87BeB04D0ac764FC298aCFd05FfC5439cD' # Arbitration Council - governor: &governor '0xf1135bFF22512FF2A585b8d4489426CE660f204c' # Graph Council - authority: &authority '0x52e498aE9B8A5eE2A5Cd26805F06A9f29A7F489F' # Authority that signs payment vouchers - availabilityOracle: &availabilityOracle '0xDC6785e39Cba34A6d258148e3E83E24285118796' # Subgraph Availability Oracle - pauseGuardian: &pauseGuardian '0x6855D551CaDe60754D145fb5eDCD90912D860262' # Protocol pause guardian - allocationExchangeOwner: &allocationExchangeOwner '0xf1135bFF22512FF2A585b8d4489426CE660f204c' # Allocation Exchange owner - -contracts: - Controller: - calls: - - fn: 'setContractProxy' - id: '0xe6876326c1291dfcbbd3864a6816d698cd591defc7aa2153d7f9c4c04016c89f' # keccak256('Curation') - contractAddress: '${{Curation.address}}' - - fn: 'setContractProxy' - id: '0x39605a6c26a173774ca666c67ef70cf491880e5d3d6d0ca66ec0a31034f15ea3' # keccak256('GNS') - contractAddress: '${{L1GNS.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: '${{L1Staking.address}}' - - fn: 'setContractProxy' - id: '0x45fc200c7e4544e457d3c5709bfe0d520442c30bbcbdaede89e8d4a4bbc19247' # keccak256('GraphToken') - contractAddress: '${{GraphToken.address}}' - - fn: 'setContractProxy' - id: '0xd362cac9cb75c10d67bcc0b7eeb0b1ef48bb5420b556c092d4fd7f758816fcf0' # keccak256('GraphTokenGateway') - contractAddress: '${{L1GraphTokenGateway.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) - GraphToken: - init: - initialSupply: '10000000000000000000000000000' # in wei - calls: - - fn: 'addMinter' - minter: '${{RewardsManager.address}}' - - fn: 'addMinter' - minter: '${{L1GraphTokenGateway.address}}' - - fn: 'renounceMinter' - - fn: 'transferOwnership' - owner: *governor - Curation: - proxy: true - init: - controller: '${{Controller.address}}' - bondingCurve: '${{BancorFormula.address}}' - curationTokenMaster: '${{GraphCurationToken.address}}' - reserveRatio: 500000 # 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' - L1GNS: - proxy: true - init: - controller: '${{Controller.address}}' - subgraphNFT: '${{SubgraphNFT.address}}' - calls: - - fn: 'approveAll' - - fn: 'syncAllContracts' - SubgraphNFT: - init: - governor: '${{Env.deployer}}' - calls: - - fn: 'setTokenDescriptor' - tokenDescriptor: '${{SubgraphNFTDescriptor.address}}' - - fn: 'setMinter' - minter: '${{L1GNS.address}}' - - fn: 'transferOwnership' - owner: *governor - L1Staking: - 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 - maxAllocationEpochs: 4 # in epochs - delegationUnbondingPeriod: 12 # in epochs - delegationRatio: 16 # delegated stake to indexer stake multiplier - rebateParameters: - alphaNumerator: 100 # alphaNumerator / alphaDenominator - alphaDenominator: 100 # alphaNumerator / alphaDenominator - lambdaNumerator: 60 # lambdaNumerator / lambdaDenominator - lambdaDenominator: 100 # lambdaNumerator / lambdaDenominator - extensionImpl: '${{StakingExtension.address}}' - calls: - - fn: 'setDelegationTaxPercentage' - delegationTaxPercentage: 5000 # parts per million - - fn: 'setSlasher' - slasher: '${{DisputeManager.address}}' - allowed: true - - fn: 'syncAllContracts' - RewardsManager: - proxy: true - init: - controller: '${{Controller.address}}' - calls: - - fn: 'setIssuancePerBlock' - issuancePerBlock: '114693500000000000000' # per block increase of total supply, blocks in a year = 365*60*60*24/12 - - fn: 'setSubgraphAvailabilityOracle' - subgraphAvailabilityOracle: *availabilityOracle - - fn: 'syncAllContracts' - AllocationExchange: - init: - graphToken: '${{GraphToken.address}}' - staking: '${{L1Staking.address}}' - governor: *allocationExchangeOwner - 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/packages/contracts-test/config/graph.hardhat.yml b/packages/contracts-test/config/graph.hardhat.yml deleted file mode 100644 index c5e15f2dd..000000000 --- a/packages/contracts-test/config/graph.hardhat.yml +++ /dev/null @@ -1,160 +0,0 @@ -general: - arbitrator: &arbitrator '0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0' # Arbitration Council - governor: &governor '0x22d491Bde2303f2f43325b2108D26f1eAbA1e32b' # Governor Council - authority: &authority '0xE11BA2b4D45Eaed5996Cd0823791E0C93114882d' # Authority that signs payment vouchers - availabilityOracle: &availabilityOracle '0xd03ea8624C8C5987235048901fB614fDcA89b117' # Subgraph Availability Oracle - pauseGuardian: &pauseGuardian '0x95cED938F7991cd0dFcb48F0a06a40FA1aF46EBC' # Protocol pause guardian - allocationExchangeOwner: &allocationExchangeOwner '0x3E5e9111Ae8eB78Fe1CC3bb8915d5D461F3Ef9A9' # Allocation Exchange owner - -contracts: - Controller: - calls: - - fn: 'setContractProxy' - id: '0xe6876326c1291dfcbbd3864a6816d698cd591defc7aa2153d7f9c4c04016c89f' # keccak256('Curation') - contractAddress: '${{Curation.address}}' - - fn: 'setContractProxy' - id: '0x39605a6c26a173774ca666c67ef70cf491880e5d3d6d0ca66ec0a31034f15ea3' # keccak256('GNS') - contractAddress: '${{L1GNS.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: '${{L1Staking.address}}' - - fn: 'setContractProxy' - id: '0x45fc200c7e4544e457d3c5709bfe0d520442c30bbcbdaede89e8d4a4bbc19247' # keccak256('GraphToken') - contractAddress: '${{GraphToken.address}}' - - fn: 'setContractProxy' - id: '0xd362cac9cb75c10d67bcc0b7eeb0b1ef48bb5420b556c092d4fd7f758816fcf0' # keccak256('GraphTokenGateway') - contractAddress: '${{L1GraphTokenGateway.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: 60 # length in hours = lengthInBlocks*13/60/60 (~13 second blocks) - GraphToken: - init: - initialSupply: '10000000000000000000000000000' # in wei - calls: - - fn: 'addMinter' - minter: '${{RewardsManager.address}}' - - fn: 'addMinter' - minter: '${{L1GraphTokenGateway.address}}' - - fn: 'transferOwnership' - owner: *governor - Curation: - proxy: true - init: - controller: '${{Controller.address}}' - bondingCurve: '${{BancorFormula.address}}' - curationTokenMaster: '${{GraphCurationToken.address}}' - reserveRatio: 500000 # in parts per million - curationTaxPercentage: 0 # in parts per million - minimumCurationDeposit: '100000000000000000000' # in wei - calls: - - fn: 'syncAllContracts' - DisputeManager: - proxy: true - init: - controller: '${{Controller.address}}' - arbitrator: *arbitrator - minimumDeposit: '100000000000000000000' # in wei - fishermanRewardPercentage: 1000 # in parts per million - qrySlashingPercentage: 1000 # in parts per million - idxSlashingPercentage: 100000 # in parts per million - calls: - - fn: 'syncAllContracts' - L1GNS: - proxy: true - init: - controller: '${{Controller.address}}' - subgraphNFT: '${{SubgraphNFT.address}}' - calls: - - fn: 'approveAll' - - fn: 'syncAllContracts' - SubgraphNFT: - init: - governor: '${{Env.deployer}}' - calls: - - fn: 'setTokenDescriptor' - tokenDescriptor: '${{SubgraphNFTDescriptor.address}}' - - fn: 'setMinter' - minter: '${{L1GNS.address}}' - - fn: 'transferOwnership' - owner: *governor - L1Staking: - proxy: true - init: - controller: '${{Controller.address}}' - minimumIndexerStake: '10000000000000000000' # in wei - thawingPeriod: 20 # in blocks - protocolPercentage: 0 # in parts per million - curationPercentage: 0 # in parts per million - maxAllocationEpochs: 5 # in epochs - delegationUnbondingPeriod: 1 # in epochs - delegationRatio: 16 # delegated stake to indexer stake multiplier - rebateParameters: - alphaNumerator: 100 # alphaNumerator / alphaDenominator - alphaDenominator: 100 # alphaNumerator / alphaDenominator - lambdaNumerator: 60 # lambdaNumerator / lambdaDenominator - lambdaDenominator: 100 # lambdaNumerator / lambdaDenominator - extensionImpl: '${{StakingExtension.address}}' - calls: - - fn: 'setDelegationTaxPercentage' - delegationTaxPercentage: 0 # parts per million - - fn: 'setSlasher' - slasher: '${{DisputeManager.address}}' - allowed: true - - fn: 'syncAllContracts' - RewardsManager: - proxy: true - init: - controller: '${{Controller.address}}' - calls: - - fn: 'setIssuancePerBlock' - issuancePerBlock: '114155251141552511415' # per block increase of total supply, blocks in a year = 365*60*60*24/12 - - fn: 'setSubgraphAvailabilityOracle' - subgraphAvailabilityOracle: *availabilityOracle - - fn: 'syncAllContracts' - AllocationExchange: - init: - graphToken: '${{GraphToken.address}}' - staking: '${{L1Staking.address}}' - governor: *allocationExchangeOwner - 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/packages/contracts-test/config/graph.localhost.yml b/packages/contracts-test/config/graph.localhost.yml deleted file mode 100644 index 0a2b2f75d..000000000 --- a/packages/contracts-test/config/graph.localhost.yml +++ /dev/null @@ -1,161 +0,0 @@ -general: - arbitrator: &arbitrator '0xFFcf8FDEE72ac11b5c542428B35EEF5769C409f0' # Arbitration Council - governor: &governor '0x22d491Bde2303f2f43325b2108D26f1eAbA1e32b' # Governor Council - authority: &authority '0xE11BA2b4D45Eaed5996Cd0823791E0C93114882d' # Authority that signs payment vouchers - availabilityOracle: &availabilityOracle '0xd03ea8624C8C5987235048901fB614fDcA89b117' # Subgraph Availability Oracle - pauseGuardian: &pauseGuardian '0x95cED938F7991cd0dFcb48F0a06a40FA1aF46EBC' # Protocol pause guardian - allocationExchangeOwner: &allocationExchangeOwner '0x3E5e9111Ae8eB78Fe1CC3bb8915d5D461F3Ef9A9' # Allocation Exchange owner - -contracts: - Controller: - calls: - - fn: 'setContractProxy' - id: '0xe6876326c1291dfcbbd3864a6816d698cd591defc7aa2153d7f9c4c04016c89f' # keccak256('Curation') - contractAddress: '${{Curation.address}}' - - fn: 'setContractProxy' - id: '0x39605a6c26a173774ca666c67ef70cf491880e5d3d6d0ca66ec0a31034f15ea3' # keccak256('GNS') - contractAddress: '${{L1GNS.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: '${{L1Staking.address}}' - - fn: 'setContractProxy' - id: '0x45fc200c7e4544e457d3c5709bfe0d520442c30bbcbdaede89e8d4a4bbc19247' # keccak256('GraphToken') - contractAddress: '${{GraphToken.address}}' - - fn: 'setContractProxy' - id: '0xd362cac9cb75c10d67bcc0b7eeb0b1ef48bb5420b556c092d4fd7f758816fcf0' # keccak256('GraphTokenGateway') - contractAddress: '${{L1GraphTokenGateway.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) - GraphToken: - init: - initialSupply: '10000000000000000000000000000' # in wei - calls: - - fn: 'addMinter' - minter: '${{RewardsManager.address}}' - - fn: 'addMinter' - minter: '${{L1GraphTokenGateway.address}}' - - fn: 'renounceMinter' - - fn: 'transferOwnership' - owner: *governor - Curation: - proxy: true - init: - controller: '${{Controller.address}}' - bondingCurve: '${{BancorFormula.address}}' - curationTokenMaster: '${{GraphCurationToken.address}}' - reserveRatio: 500000 # 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' - L1GNS: - proxy: true - init: - controller: '${{Controller.address}}' - subgraphNFT: '${{SubgraphNFT.address}}' - calls: - - fn: 'approveAll' - - fn: 'syncAllContracts' - SubgraphNFT: - init: - governor: '${{Env.deployer}}' - calls: - - fn: 'setTokenDescriptor' - tokenDescriptor: '${{SubgraphNFTDescriptor.address}}' - - fn: 'setMinter' - minter: '${{L1GNS.address}}' - - fn: 'transferOwnership' - owner: *governor - L1Staking: - 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 - maxAllocationEpochs: 4 # in epochs - delegationUnbondingPeriod: 12 # in epochs - delegationRatio: 16 # delegated stake to indexer stake multiplier - rebateParameters: - alphaNumerator: 100 # alphaNumerator / alphaDenominator - alphaDenominator: 100 # alphaNumerator / alphaDenominator - lambdaNumerator: 60 # lambdaNumerator / lambdaDenominator - lambdaDenominator: 100 # lambdaNumerator / lambdaDenominator - extensionImpl: '${{StakingExtension.address}}' - calls: - - fn: 'setDelegationTaxPercentage' - delegationTaxPercentage: 5000 # parts per million - - fn: 'setSlasher' - slasher: '${{DisputeManager.address}}' - allowed: true - - fn: 'syncAllContracts' - RewardsManager: - proxy: true - init: - controller: '${{Controller.address}}' - calls: - - fn: 'setIssuancePerBlock' - issuancePerBlock: '114693500000000000000' # per block increase of total supply, blocks in a year = 365*60*60*24/12 - - fn: 'setSubgraphAvailabilityOracle' - subgraphAvailabilityOracle: *availabilityOracle - - fn: 'syncAllContracts' - AllocationExchange: - init: - graphToken: '${{GraphToken.address}}' - staking: '${{L1Staking.address}}' - governor: *allocationExchangeOwner - 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/packages/contracts-test/config/graph.mainnet.yml b/packages/contracts-test/config/graph.mainnet.yml deleted file mode 100644 index 93aefea25..000000000 --- a/packages/contracts-test/config/graph.mainnet.yml +++ /dev/null @@ -1,161 +0,0 @@ -general: - arbitrator: &arbitrator '0xE1FDD398329C6b74C14cf19100316f0826a492d3' # Arbitration Council - governor: &governor '0x48301Fe520f72994d32eAd72E2B6A8447873CF50' # Graph Council - authority: &authority '0xF994fB0f5c06B31E364B868886140cC30A7fcF15' # Authority that signs payment vouchers - availabilityOracle: &availabilityOracle '0xE24f399999D47D276Da88FAa7646e2C597822333' # Subgraph Availability Oracle - pauseGuardian: &pauseGuardian '0x8290362Aba20D17c51995085369E001Bad99B21c' # Protocol pause guardian - allocationExchangeOwner: &allocationExchangeOwner '0x74Db79268e63302d3FC69FB5a7627F7454a41732' # Allocation Exchange owner - -contracts: - Controller: - calls: - - fn: 'setContractProxy' - id: '0xe6876326c1291dfcbbd3864a6816d698cd591defc7aa2153d7f9c4c04016c89f' # keccak256('Curation') - contractAddress: '${{Curation.address}}' - - fn: 'setContractProxy' - id: '0x39605a6c26a173774ca666c67ef70cf491880e5d3d6d0ca66ec0a31034f15ea3' # keccak256('GNS') - contractAddress: '${{L1GNS.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: '${{L1Staking.address}}' - - fn: 'setContractProxy' - id: '0x45fc200c7e4544e457d3c5709bfe0d520442c30bbcbdaede89e8d4a4bbc19247' # keccak256('GraphToken') - contractAddress: '${{GraphToken.address}}' - - fn: 'setContractProxy' - id: '0xd362cac9cb75c10d67bcc0b7eeb0b1ef48bb5420b556c092d4fd7f758816fcf0' # keccak256('GraphTokenGateway') - contractAddress: '${{L1GraphTokenGateway.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) - GraphToken: - init: - initialSupply: '10000000000000000000000000000' # in wei - calls: - - fn: 'addMinter' - minter: '${{RewardsManager.address}}' - - fn: 'addMinter' - minter: '${{L1GraphTokenGateway.address}}' - - fn: 'renounceMinter' - - fn: 'transferOwnership' - owner: *governor - Curation: - proxy: true - init: - controller: '${{Controller.address}}' - bondingCurve: '${{BancorFormula.address}}' - curationTokenMaster: '${{GraphCurationToken.address}}' - reserveRatio: 500000 # 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' - L1GNS: - proxy: true - init: - controller: '${{Controller.address}}' - subgraphNFT: '${{SubgraphNFT.address}}' - calls: - - fn: 'approveAll' - - fn: 'syncAllContracts' - SubgraphNFT: - init: - governor: '${{Env.deployer}}' - calls: - - fn: 'setTokenDescriptor' - tokenDescriptor: '${{SubgraphNFTDescriptor.address}}' - - fn: 'setMinter' - minter: '${{L1GNS.address}}' - - fn: 'transferOwnership' - owner: *governor - L1Staking: - 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 - maxAllocationEpochs: 28 # in epochs - delegationUnbondingPeriod: 28 # in epochs - delegationRatio: 16 # delegated stake to indexer stake multiplier - rebateParameters: - alphaNumerator: 100 # alphaNumerator / alphaDenominator - alphaDenominator: 100 # alphaNumerator / alphaDenominator - lambdaNumerator: 60 # lambdaNumerator / lambdaDenominator - lambdaDenominator: 100 # lambdaNumerator / lambdaDenominator - extensionImpl: '${{StakingExtension.address}}' - calls: - - fn: 'setDelegationTaxPercentage' - delegationTaxPercentage: 5000 # parts per million - - fn: 'setSlasher' - slasher: '${{DisputeManager.address}}' - allowed: true - - fn: 'syncAllContracts' - RewardsManager: - proxy: true - init: - controller: '${{Controller.address}}' - calls: - - fn: 'setIssuancePerBlock' - issuancePerBlock: '114693500000000000000' # per block increase of total supply, blocks in a year = 365*60*60*24/12 - - fn: 'setSubgraphAvailabilityOracle' - subgraphAvailabilityOracle: *availabilityOracle - - fn: 'syncAllContracts' - AllocationExchange: - init: - graphToken: '${{GraphToken.address}}' - staking: '${{L1Staking.address}}' - governor: *allocationExchangeOwner - 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/packages/contracts-test/config/graph.sepolia.yml b/packages/contracts-test/config/graph.sepolia.yml deleted file mode 100644 index f73bca3d7..000000000 --- a/packages/contracts-test/config/graph.sepolia.yml +++ /dev/null @@ -1,161 +0,0 @@ -general: - arbitrator: &arbitrator '0xd6ff9e98F0Fd99ccB658832F586e23F4D8Cb8Bad' # Arbitration Council - governor: &governor '0x4EBf30832eC2db76aE228D5d239083B59f530d1f' # Graph Council - authority: &authority '0x840daec5dF962D49cf2EFd789c4E40A7b7e0117D' # Authority that signs payment vouchers - availabilityOracle: &availabilityOracle '0x840daec5dF962D49cf2EFd789c4E40A7b7e0117D' # Subgraph Availability Oracle - pauseGuardian: &pauseGuardian '0x382688E15Cc894D04cf3313b26a4F2c93C8fDe06' # Protocol pause guardian - allocationExchangeOwner: &allocationExchangeOwner '0x4EBf30832eC2db76aE228D5d239083B59f530d1f' # Allocation Exchange owner - -contracts: - Controller: - calls: - - fn: 'setContractProxy' - id: '0xe6876326c1291dfcbbd3864a6816d698cd591defc7aa2153d7f9c4c04016c89f' # keccak256('Curation') - contractAddress: '${{Curation.address}}' - - fn: 'setContractProxy' - id: '0x39605a6c26a173774ca666c67ef70cf491880e5d3d6d0ca66ec0a31034f15ea3' # keccak256('GNS') - contractAddress: '${{L1GNS.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: '${{L1Staking.address}}' - - fn: 'setContractProxy' - id: '0x45fc200c7e4544e457d3c5709bfe0d520442c30bbcbdaede89e8d4a4bbc19247' # keccak256('GraphToken') - contractAddress: '${{GraphToken.address}}' - - fn: 'setContractProxy' - id: '0xd362cac9cb75c10d67bcc0b7eeb0b1ef48bb5420b556c092d4fd7f758816fcf0' # keccak256('GraphTokenGateway') - contractAddress: '${{L1GraphTokenGateway.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) - GraphToken: - init: - initialSupply: '10000000000000000000000000000' # in wei - calls: - - fn: 'addMinter' - minter: '${{RewardsManager.address}}' - - fn: 'addMinter' - minter: '${{L1GraphTokenGateway.address}}' - - fn: 'renounceMinter' - - fn: 'transferOwnership' - owner: *governor - Curation: - proxy: true - init: - controller: '${{Controller.address}}' - bondingCurve: '${{BancorFormula.address}}' - curationTokenMaster: '${{GraphCurationToken.address}}' - reserveRatio: 500000 # 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' - L1GNS: - proxy: true - init: - controller: '${{Controller.address}}' - subgraphNFT: '${{SubgraphNFT.address}}' - calls: - - fn: 'approveAll' - - fn: 'syncAllContracts' - SubgraphNFT: - init: - governor: '${{Env.deployer}}' - calls: - - fn: 'setTokenDescriptor' - tokenDescriptor: '${{SubgraphNFTDescriptor.address}}' - - fn: 'setMinter' - minter: '${{L1GNS.address}}' - - fn: 'transferOwnership' - owner: *governor - L1Staking: - 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 - maxAllocationEpochs: 4 # in epochs - delegationUnbondingPeriod: 12 # in epochs - delegationRatio: 16 # delegated stake to indexer stake multiplier - rebateParameters: - alphaNumerator: 100 # alphaNumerator / alphaDenominator - alphaDenominator: 100 # alphaNumerator / alphaDenominator - lambdaNumerator: 60 # lambdaNumerator / lambdaDenominator - lambdaDenominator: 100 # lambdaNumerator / lambdaDenominator - extensionImpl: '${{StakingExtension.address}}' - calls: - - fn: 'setDelegationTaxPercentage' - delegationTaxPercentage: 5000 # parts per million - - fn: 'setSlasher' - slasher: '${{DisputeManager.address}}' - allowed: true - - fn: 'syncAllContracts' - RewardsManager: - proxy: true - init: - controller: '${{Controller.address}}' - calls: - - fn: 'setIssuancePerBlock' - issuancePerBlock: '114693500000000000000' # per block increase of total supply, blocks in a year = 365*60*60*24/12 - - fn: 'setSubgraphAvailabilityOracle' - subgraphAvailabilityOracle: *availabilityOracle - - fn: 'syncAllContracts' - AllocationExchange: - init: - graphToken: '${{GraphToken.address}}' - staking: '${{L1Staking.address}}' - governor: *allocationExchangeOwner - 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/packages/contracts-test/contracts b/packages/contracts-test/contracts deleted file mode 120000 index e741e39c3..000000000 --- a/packages/contracts-test/contracts +++ /dev/null @@ -1 +0,0 @@ -../contracts/contracts \ No newline at end of file diff --git a/packages/contracts-test/hardhat.config.ts b/packages/contracts-test/hardhat.config.ts deleted file mode 100644 index 718359730..000000000 --- a/packages/contracts-test/hardhat.config.ts +++ /dev/null @@ -1,94 +0,0 @@ -// Test-focused Hardhat configuration -import '@graphprotocol/sdk/gre' -import '@nomiclabs/hardhat-ethers' -import '@nomiclabs/hardhat-waffle' -import '@typechain/hardhat' -import 'dotenv/config' -import 'hardhat-gas-reporter' -import 'hardhat-ignore-warnings' -import 'solidity-coverage' -// Test-specific tasks -import './tasks/migrate/nitro' -import './tasks/test-upgrade' - -import { configDir } from '@graphprotocol/contracts' -import { HardhatUserConfig } from 'hardhat/config' -import path from 'path' - -// Default mnemonic for testing -const DEFAULT_TEST_MNEMONIC = 'myth like bonus scare over problem client lizard pioneer submit female collect' - -const config: HardhatUserConfig = { - graph: { - addressBook: process.env.ADDRESS_BOOK || 'addresses.json', - disableSecureAccounts: true, - }, - solidity: { - compilers: [ - { - version: '0.7.6', - settings: { - optimizer: { - enabled: true, - runs: 200, - }, - }, - }, - ], - }, - paths: { - tests: './tests/unit', - cache: './cache', - graph: '../contracts', - artifacts: './artifacts', - }, - typechain: { - outDir: 'types', - }, - warnings: { - // Suppress warnings from legacy OpenZeppelin contracts and external dependencies - 'arbos-precompiles/**/*': { - default: 'off', - }, - '@openzeppelin/contracts/**/*': { - default: 'off', - }, - 'contracts/staking/StakingExtension.sol': { - 5667: 'off', // Unused function parameter - }, - }, - defaultNetwork: 'hardhat', - networks: { - hardhat: { - chainId: 1337, - loggingEnabled: false, - gas: 12000000, - gasPrice: 'auto', - initialBaseFeePerGas: 0, - blockGasLimit: 12000000, - accounts: { - mnemonic: DEFAULT_TEST_MNEMONIC, - }, - hardfork: 'london', - // Graph Protocol extensions (not in standard Hardhat types) - graphConfig: path.join(configDir, 'graph.hardhat.yml'), - addressBook: process.env.ADDRESS_BOOK || 'addresses.json', - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } as any, - localhost: { - chainId: 1337, - url: 'http://127.0.0.1:8545', - accounts: { mnemonic: DEFAULT_TEST_MNEMONIC }, - }, - }, - - gasReporter: { - enabled: process.env.REPORT_GAS ? true : false, - showTimeSpent: true, - currency: 'USD', - outputFile: 'reports/gas-report.log', - }, - // eslint-disable-next-line @typescript-eslint/no-explicit-any -} as any - -export default config diff --git a/packages/contracts-test/package.json b/packages/contracts-test/package.json deleted file mode 100644 index 1ced3bca4..000000000 --- a/packages/contracts-test/package.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "name": "@graphprotocol/contracts-tests", - "version": "1.0.3", - "private": true, - "description": "Tests for @graphprotocol/contracts", - "author": "Edge & Node", - "license": "GPL-2.0-or-later", - "scripts": { - "postinstall": "scripts/setup-symlinks", - "clean": "rm -rf artifacts/ cache/ types/", - "build": "pnpm build:dep", - "build:dep": "pnpm --filter '@graphprotocol/contracts-tests^...' run build:self", - "test": "pnpm build && pnpm test:self", - "test:self": "scripts/test", - "test:e2e": "scripts/e2e", - "test:gas": "RUN_EVM=true REPORT_GAS=true scripts/test", - "test:coverage": "pnpm build && pnpm test:coverage:self", - "test:coverage:self": "scripts/coverage", - "test:upgrade": "scripts/upgrade", - "lint": "pnpm lint:ts; pnpm lint:json", - "lint:ts": "eslint '**/*.{js,ts,cjs,mjs,jsx,tsx}' --fix --cache; prettier -w --cache --log-level warn '**/*.{js,ts,cjs,mjs,jsx,tsx}'", - "lint:json": "prettier -w --cache --log-level warn '**/*.json'" - }, - "dependencies": { - "@graphprotocol/contracts": "workspace:^", - "@graphprotocol/interfaces": "workspace:^", - "@graphprotocol/sdk": "0.6.0" - }, - "devDependencies": { - "@arbitrum/sdk": "~3.1.13", - "@defi-wonderland/smock": "^2.4.1", - "@ethersproject/abi": "^5.8.0", - "@ethersproject/abstract-provider": "^5.8.0", - "@ethersproject/abstract-signer": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/providers": "^5.8.0", - "@graphprotocol/common-ts": "^1.8.3", - "@nomicfoundation/hardhat-network-helpers": "^1.0.0", - "@nomiclabs/hardhat-ethers": "^2.2.3", - "@nomiclabs/hardhat-etherscan": "^3.1.0", - "@nomiclabs/hardhat-waffle": "^2.0.6", - "@openzeppelin/contracts": "3.4.2", - "@openzeppelin/contracts-upgradeable": "3.4.2", - "@openzeppelin/hardhat-upgrades": "^1.22.1", - "@typechain/ethers-v5": "^10.2.1", - "@typechain/hardhat": "^6.1.2", - "@types/chai": "^4.2.0", - "@types/mocha": ">=9.1.0", - "@types/node": "^20.17.50", - "@types/sinon-chai": "^3.2.12", - "arbos-precompiles": "^1.0.2", - "chai": "^4.2.0", - "dotenv": "^16.5.0", - "eslint": "catalog:", - "ethereum-waffle": "^4.0.10", - "ethers": "^5.7.0", - "form-data": "^4.0.0", - "glob": "catalog:", - "graphql": "^16.11.0", - "graphql-tag": "^2.12.4", - "hardhat": "catalog:", - "hardhat-abi-exporter": "^2.11.0", - "hardhat-contract-sizer": "^2.10.0", - "hardhat-gas-reporter": "^1.0.8", - "hardhat-storage-layout": "^0.1.7", - "prettier": "catalog:", - "prettier-plugin-solidity": "catalog:", - "solidity-coverage": "^0.8.16", - "ts-node": "^10.9.2", - "typechain": "^8.3.2", - "typescript": "catalog:", - "winston": "^3.3.3", - "yaml": "^1.10.2", - "yargs": "^17.0.0" - } -} diff --git a/packages/contracts-test/prettier.config.cjs b/packages/contracts-test/prettier.config.cjs deleted file mode 100644 index 18006454f..000000000 --- a/packages/contracts-test/prettier.config.cjs +++ /dev/null @@ -1,5 +0,0 @@ -const baseConfig = require('../contracts/prettier.config.cjs') - -module.exports = { - ...baseConfig, -} diff --git a/packages/contracts-test/scripts/coverage b/packages/contracts-test/scripts/coverage deleted file mode 100755 index 76f36856b..000000000 --- a/packages/contracts-test/scripts/coverage +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash - -set -eo pipefail - -echo {} > addresses-local.json - -DISABLE_SECURE_ACCOUNTS=true \ -L1_GRAPH_CONFIG=config/graph.hardhat.yml \ -L2_GRAPH_CONFIG=config/graph.arbitrum-hardhat.yml \ -ADDRESS_BOOK=addresses-local.json \ - npx hardhat coverage $@ diff --git a/packages/contracts-test/scripts/e2e b/packages/contracts-test/scripts/e2e deleted file mode 100755 index 98978ccc3..000000000 --- a/packages/contracts-test/scripts/e2e +++ /dev/null @@ -1,193 +0,0 @@ -#!/bin/bash - -set -eo pipefail -source $(pwd)/scripts/evm - -### > SCRIPT CONFIG < -# Allow overriding config -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"} - -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 deploy() { - local NETWORK=$1 - local GRAPH_CONFIG=$2 - local ADDRESS_BOOK=$3 - - echo "Deploying protocol on $NETWORK" - - # Fund accounts if using nitro test nodes - if [[ "$NETWORK" == *"localnitro"* ]]; then - npx hardhat migrate:nitro:fund-accounts --network "$NETWORK" --graph-config "$GRAPH_CONFIG" --disable-secure-accounts - fi - - # Deploy protocol - npx hardhat migrate \ - --network "$NETWORK" \ - --disable-secure-accounts \ - --skip-confirmation \ - --force \ - --graph-config "$GRAPH_CONFIG" \ - --address-book "$ADDRESS_BOOK" -} - -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 - - # Copy required arbitrum contract addresses to the local arbitrum address book - if [[ "$L1_NETWORK" == *"localnitro"* ]]; then - npx hardhat migrate:nitro:address-book --deployment-file "$ARBITRUM_DEPLOYMENT_FILE" --arbitrum-address-book "$ARBITRUM_ADDRESS_BOOK" - fi - - npx hardhat migrate:bridge --network "$L1_NETWORK" --l1-graph-config "$L1_GRAPH_CONFIG" --l2-graph-config "$L2_GRAPH_CONFIG" --address-book "$ADDRESS_BOOK" --arbitrum-address-book "$ARBITRUM_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 -pnpm build - -# Start evm -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 - deploy "$L1_NETWORK" "$L1_GRAPH_CONFIG" "$ADDRESS_BOOK" -fi - -# Deploy L2 -if [[ -n "$L2_NETWORK" ]]; then - 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 -if [[ "$L1_NETWORK" == "localhost" || "$L2_NETWORK" == "localhost" ]]; then - set +e - result=0 - - evm_kill - exit $result -fi diff --git a/packages/contracts-test/scripts/evm b/packages/contracts-test/scripts/evm deleted file mode 100755 index acbc07b80..000000000 --- a/packages/contracts-test/scripts/evm +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/bash - -TESTRPC_PORT=${TESTRPC_PORT:-8545} -MAX_RETRIES=120 - -evm_running() { - 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_automine() { - curl --location --request POST "localhost:$TESTRPC_PORT/" \ - --header 'Content-Type: application/json' \ - --data-raw '{ - "jsonrpc":"2.0", - "method":"evm_setAutomine", - "params":[true], - "id":1 - }' - echo -} - -evm_start() { - local FORK_URL=$1 - local FORK_BLOCK_NUMBER="$2" - echo "Starting our own evm instance at port $TESTRPC_PORT" - if [[ -n "$FORK_URL" ]]; then - if [[ "$FORK_BLOCK_NUMBER" == "latest" ]]; then - npx hardhat node --port "$TESTRPC_PORT" --fork "$FORK_URL" > /dev/null & - else - npx hardhat node --port "$TESTRPC_PORT" --fork "$FORK_URL" --fork-block-number "$FORK_BLOCK_NUMBER" > /dev/null & - fi - else - npx hardhat node --port "$TESTRPC_PORT" > /dev/null & - fi - 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 - - echo -e "\nEVM instance is ready" -} - -evm_kill() { - if evm_running; then - echo "Killing evm instance at port $TESTRPC_PORT" - kill -9 $(lsof -i:$TESTRPC_PORT -t) - fi -} diff --git a/packages/contracts-test/scripts/setup-symlinks b/packages/contracts-test/scripts/setup-symlinks deleted file mode 100755 index 9c7f72949..000000000 --- a/packages/contracts-test/scripts/setup-symlinks +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash - -# Setup symbolic links for contracts test package -# This script ensures that the necessary symbolic links are created -# for the test package to access contracts from the parent package - -set -e - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -TEST_DIR="$(dirname "$SCRIPT_DIR")" - -# Create symbolic link from contracts to ../contracts/contracts -CONTRACTS_LINK="$TEST_DIR/contracts" -CONTRACTS_TARGET="../contracts/contracts" - -if [ -L "$CONTRACTS_LINK" ]; then - # Check if the link points to the correct target - if [ "$(readlink "$CONTRACTS_LINK")" = "$CONTRACTS_TARGET" ]; then - echo "Contracts symlink: OK" - else - rm "$CONTRACTS_LINK" - ln -s "$CONTRACTS_TARGET" "$CONTRACTS_LINK" - echo "Contracts symlink: Created" - fi -elif [ -e "$CONTRACTS_LINK" ]; then - echo "Error: $CONTRACTS_LINK exists but is not a symbolic link" - echo "Please remove it manually and run this script again" - exit 1 -else - ln -s "$CONTRACTS_TARGET" "$CONTRACTS_LINK" - echo "Contracts symlink: Created" -fi diff --git a/packages/contracts-test/scripts/test b/packages/contracts-test/scripts/test deleted file mode 100755 index 7b5c01372..000000000 --- a/packages/contracts-test/scripts/test +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash - -set -eo pipefail -source $(pwd)/scripts/evm - -### Cleanup -function cleanup() { - if [ "$RUN_EVM" = true ]; then - evm_kill - fi -} -trap cleanup EXIT - -# Gas reporter needs to run in its own evm instance -if [ "$RUN_EVM" = true ]; then - evm_kill - evm_start - sleep 5 -fi - -### Main - -# Init address book -echo {} > ../contracts/addresses-local.json - -# TODO: fix this! For some reason the resolved package does not have a few required files -echo {} > ../../node_modules/.pnpm/@graphprotocol+contracts@7.2.1/node_modules/@graphprotocol/contracts/addresses-local.json -cp -r ../contracts/config ../../node_modules/.pnpm/@graphprotocol+contracts@7.2.1/node_modules/@graphprotocol/contracts/config - -mkdir -p reports - -# Run using the standalone evm instance -DISABLE_SECURE_ACCOUNTS=true \ -L1_GRAPH_CONFIG=config/graph.hardhat.yml \ -L2_GRAPH_CONFIG=config/graph.arbitrum-hardhat.yml \ -ADDRESS_BOOK=addresses-local.json \ - npx hardhat test --network hardhat $@ - -if [ "$REPORT_GAS" = true ]; then - cat reports/gas-report.log - echo "" # Gas report doesn't have a newline at the end -fi diff --git a/packages/contracts-test/scripts/test-coverage-file b/packages/contracts-test/scripts/test-coverage-file deleted file mode 100755 index 64800d217..000000000 --- a/packages/contracts-test/scripts/test-coverage-file +++ /dev/null @@ -1,21 +0,0 @@ -#!/bin/bash - -set -eo pipefail - -# Check if a test file was provided -if [ $# -eq 0 ]; then - echo "Error: You must provide a test file path" - echo "Usage: ./scripts/test-coverage-file test/unit/rewards/rewards.test.ts" - exit 1 -fi - -# Build contracts first to ensure tests run against latest code -echo "Building contracts before running coverage..." -yarn build - -echo "Running coverage for test file: $1" -DISABLE_SECURE_ACCOUNTS=true \ -L1_GRAPH_CONFIG=config/graph.hardhat.yml \ -L2_GRAPH_CONFIG=config/graph.arbitrum-hardhat.yml \ -ADDRESS_BOOK=addresses-local.json \ - npx hardhat coverage --testfiles "$1" diff --git a/packages/contracts-test/tasks/migrate/nitro.ts b/packages/contracts-test/tasks/migrate/nitro.ts deleted file mode 100644 index 480830cb4..000000000 --- a/packages/contracts-test/tasks/migrate/nitro.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { helpers } from '@graphprotocol/sdk' -import { greTask } from '@graphprotocol/sdk/gre' -import { execSync } from 'child_process' -import fs from 'fs' -import { subtask, task } from 'hardhat/config' - -greTask('migrate:nitro:fund-accounts', 'Funds protocol accounts on Arbitrum Nitro testnodes').setAction( - async (taskArgs, hre) => { - const graph = hre.graph(taskArgs) - await helpers.fundLocalAccounts([await graph.getDeployer(), ...(await graph.getAllAccounts())], graph.provider) - }, -) - -// Arbitrum SDK does not support Nitro testnodes out of the box -// This adds the testnodes to the SDK configuration -subtask('migrate:nitro:register', 'Adds nitro testnodes to SDK config') - .addParam('deploymentFile', 'The testnode deployment file to use', 'localNetwork.json') - .setAction(async (taskArgs): Promise => { - helpers.addLocalNetwork(taskArgs.deploymentFile) - }) - -subtask('migrate:nitro: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 container cp $(docker ps -alqf "name=tokenbridge" --format "{{.ID}}"):/workspace/localNetwork.json .` - 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('migrate:nitro: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('migrate:nitro: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, null, 2) + '\n') - }) diff --git a/packages/contracts-test/tasks/test-upgrade.ts b/packages/contracts-test/tasks/test-upgrade.ts deleted file mode 100644 index d5c76e7ab..000000000 --- a/packages/contracts-test/tasks/test-upgrade.ts +++ /dev/null @@ -1,52 +0,0 @@ -import fs from 'fs' -import { task } from 'hardhat/config' - -function saveProxyAddresses(data) { - try { - fs.writeFileSync('.proxies.json', JSON.stringify(data, null, 2)) - } catch (e) { - console.log(`Error saving artifacts: ${e.message}`) - } -} - -interface UpgradeableContract { - name: string - libraries?: string[] -} - -const UPGRADEABLE_CONTRACTS: UpgradeableContract[] = [ - { name: 'EpochManager' }, - { name: 'Curation' }, - { name: 'Staking', libraries: ['LibExponential'] }, - { name: 'DisputeManager' }, - { name: 'RewardsManager' }, - { name: 'ServiceRegistry' }, -] - -task('test:upgrade-setup', 'Deploy contracts using an OZ proxy').setAction(async (_, hre) => { - const contractAddresses = {} - for (const upgradeableContract of UPGRADEABLE_CONTRACTS) { - // Deploy libraries - const deployedLibraries = {} - if (upgradeableContract.libraries) { - for (const libraryName of upgradeableContract.libraries) { - const libraryFactory = await hre.ethers.getContractFactory(libraryName) - const libraryInstance = await libraryFactory.deploy() - deployedLibraries[libraryName] = libraryInstance.address - } - } - - // Deploy contract with Proxy - const contractFactory = await hre.ethers.getContractFactory(upgradeableContract.name, { - libraries: deployedLibraries, - }) - const deployedContract = await hre.upgrades.deployProxy(contractFactory, { - initializer: false, - unsafeAllowLinkedLibraries: true, - }) - contractAddresses[upgradeableContract.name] = deployedContract.address - } - - // Save proxies to a file - saveProxyAddresses(contractAddresses) -}) diff --git a/packages/contracts-test/tests/unit/curation/configuration.test.ts b/packages/contracts-test/tests/unit/curation/configuration.test.ts deleted file mode 100644 index d313cc393..000000000 --- a/packages/contracts-test/tests/unit/curation/configuration.test.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { GraphNetworkContracts, randomAddress, toBN } from '@graphprotocol/sdk' -import type { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { expect } from 'chai' -import { constants } from 'ethers' -import hre from 'hardhat' - -import { NetworkFixture } from '../lib/fixtures' - -const { AddressZero } = constants - -const MAX_PPM = 1000000 - -describe('Curation:Config', () => { - let me: SignerWithAddress - let governor: SignerWithAddress - - let fixture: NetworkFixture - - let contracts: GraphNetworkContracts - - const graph = hre.graph() - const defaults = graph.graphConfig.defaults - - before(async function () { - ;[me] = await graph.getTestAccounts() - ;({ governor } = await graph.getNamedAccounts()) - fixture = new NetworkFixture(graph.provider) - contracts = await fixture.load(governor) - }) - - beforeEach(async function () { - await fixture.setUp() - }) - - afterEach(async function () { - await fixture.tearDown() - }) - - describe('defaultReserveRatio', function () { - it('should set `defaultReserveRatio`', async function () { - // Set right in the constructor - expect(await contracts.Curation.defaultReserveRatio()).eq(defaults.curation.reserveRatio) - - // Can set if allowed - const newValue = toBN('100') - await contracts.Curation.connect(governor).setDefaultReserveRatio(newValue) - expect(await contracts.Curation.defaultReserveRatio()).eq(newValue) - }) - - it('reject set `defaultReserveRatio` if out of bounds', async function () { - const tx1 = contracts.Curation.connect(governor).setDefaultReserveRatio(0) - await expect(tx1).revertedWith('Default reserve ratio must be > 0') - - const tx2 = contracts.Curation.connect(governor).setDefaultReserveRatio(MAX_PPM + 1) - await expect(tx2).revertedWith('Default reserve ratio cannot be higher than MAX_PPM') - }) - - it('reject set `defaultReserveRatio` if not allowed', async function () { - const tx = contracts.Curation.connect(me).setDefaultReserveRatio(defaults.curation.reserveRatio) - await expect(tx).revertedWith('Only Controller governor') - }) - }) - - describe('minimumCurationDeposit', function () { - it('should set `minimumCurationDeposit`', async function () { - // Set right in the constructor - expect(await contracts.Curation.minimumCurationDeposit()).eq(defaults.curation.minimumCurationDeposit) - - // Can set if allowed - const newValue = toBN('100') - await contracts.Curation.connect(governor).setMinimumCurationDeposit(newValue) - expect(await contracts.Curation.minimumCurationDeposit()).eq(newValue) - }) - - it('reject set `minimumCurationDeposit` if out of bounds', async function () { - const tx = contracts.Curation.connect(governor).setMinimumCurationDeposit(0) - await expect(tx).revertedWith('Minimum curation deposit cannot be 0') - }) - - it('reject set `minimumCurationDeposit` if not allowed', async function () { - const tx = contracts.Curation.connect(me).setMinimumCurationDeposit(defaults.curation.minimumCurationDeposit) - await expect(tx).revertedWith('Only Controller governor') - }) - }) - - describe('curationTaxPercentage', function () { - it('should set `curationTaxPercentage`', async function () { - const curationTaxPercentage = defaults.curation.curationTaxPercentage - - // Set new value - await contracts.Curation.connect(governor).setCurationTaxPercentage(0) - await contracts.Curation.connect(governor).setCurationTaxPercentage(curationTaxPercentage) - }) - - it('reject set `curationTaxPercentage` if out of bounds', async function () { - const tx = contracts.Curation.connect(governor).setCurationTaxPercentage(MAX_PPM + 1) - await expect(tx).revertedWith('Curation tax percentage must be below or equal to MAX_PPM') - }) - - it('reject set `curationTaxPercentage` if not allowed', async function () { - const tx = contracts.Curation.connect(me).setCurationTaxPercentage(0) - await expect(tx).revertedWith('Only Controller governor') - }) - }) - - describe('curationTokenMaster', function () { - it('should set `curationTokenMaster`', async function () { - const newCurationTokenMaster = contracts.Curation.address - await contracts.Curation.connect(governor).setCurationTokenMaster(newCurationTokenMaster) - }) - - it('reject set `curationTokenMaster` to empty value', async function () { - const newCurationTokenMaster = AddressZero - const tx = contracts.Curation.connect(governor).setCurationTokenMaster(newCurationTokenMaster) - await expect(tx).revertedWith('Token master must be non-empty') - }) - - it('reject set `curationTokenMaster` to non-contract', async function () { - const newCurationTokenMaster = randomAddress() - const tx = contracts.Curation.connect(governor).setCurationTokenMaster(newCurationTokenMaster) - await expect(tx).revertedWith('Token master must be a contract') - }) - - it('reject set `curationTokenMaster` if not allowed', async function () { - const newCurationTokenMaster = contracts.Curation.address - const tx = contracts.Curation.connect(me).setCurationTokenMaster(newCurationTokenMaster) - await expect(tx).revertedWith('Only Controller governor') - }) - }) -}) diff --git a/packages/contracts-test/tests/unit/curation/curation.test.ts b/packages/contracts-test/tests/unit/curation/curation.test.ts deleted file mode 100644 index 78b952042..000000000 --- a/packages/contracts-test/tests/unit/curation/curation.test.ts +++ /dev/null @@ -1,526 +0,0 @@ -import { formatGRT, GraphNetworkContracts, helpers, randomHexBytes, toBN, toGRT } from '@graphprotocol/sdk' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { expect } from 'chai' -import { BigNumber, Event, utils } from 'ethers' -import { parseEther } from 'ethers/lib/utils' -import hre from 'hardhat' - -import { NetworkFixture } from '../lib/fixtures' - -const MAX_PPM = 1000000 - -const chunkify = (total: BigNumber, maxChunks = 10): Array => { - const chunks = [] - while (total.gt(0) && maxChunks > 0) { - const m = 1000000 - const p = Math.floor(Math.random() * m) - const n = total.mul(p).div(m) - chunks.push(n) - total = total.sub(n) - maxChunks-- - } - if (total.gt(0)) { - chunks.push(total) - } - return chunks as BigNumber[] -} - -const toFloat = (n: BigNumber) => parseFloat(formatGRT(n)) -const toRound = (n: number) => n.toFixed(12) - -describe('Curation', () => { - let me: SignerWithAddress - let governor: SignerWithAddress - let curator: SignerWithAddress - let stakingMock: SignerWithAddress - let gnsImpersonator: SignerWithAddress - - let fixture: NetworkFixture - - let contracts: GraphNetworkContracts - - const graph = hre.graph() - - // Test values - const signalAmountFor1000Tokens = toGRT('3.162277660168379331') - const subgraphDeploymentID = randomHexBytes() - const curatorTokens = toGRT('1000000000') - const tokensToDeposit = toGRT('1000') - const tokensToCollect = toGRT('2000') - - async function calcBondingCurve( - supply: BigNumber, - reserveBalance: BigNumber, - reserveRatio: number, - depositAmount: BigNumber, - ): Promise { - // Handle the initialization of the bonding curve - if (supply.eq(0)) { - const minDeposit = await contracts.Curation.minimumCurationDeposit() - if (depositAmount.lt(minDeposit)) { - throw new Error('deposit must be above minimum') - } - const defaultReserveRatio = await contracts.Curation.defaultReserveRatio() - const minSupply = toGRT('1') - return ( - (await calcBondingCurve(minSupply, minDeposit, defaultReserveRatio, depositAmount.sub(minDeposit))) + - toFloat(minSupply) - ) - } - // Calculate bonding curve in the test - return toFloat(supply) * ((1 + toFloat(depositAmount) / toFloat(reserveBalance)) ** (reserveRatio / 1000000) - 1) - } - - const shouldMint = async (tokensToDeposit: BigNumber, expectedSignal: BigNumber) => { - // Before state - const beforeTokenTotalSupply = await contracts.GraphToken.totalSupply() - const beforeCuratorTokens = await contracts.GraphToken.balanceOf(curator.address) - const beforeCuratorSignal = await contracts.Curation.getCuratorSignal(curator.address, subgraphDeploymentID) - const beforePool = await contracts.Curation.pools(subgraphDeploymentID) - const beforePoolSignal = await contracts.Curation.getCurationPoolSignal(subgraphDeploymentID) - const beforeTotalTokens = await contracts.GraphToken.balanceOf(contracts.Curation.address) - - // Calculations - const curationTaxPercentage = await contracts.Curation.curationTaxPercentage() - const curationTax = tokensToDeposit.mul(toBN(curationTaxPercentage)).div(toBN(MAX_PPM)) - - // Curate - const tx = contracts.Curation.connect(curator).mint(subgraphDeploymentID, tokensToDeposit, 0) - await expect(tx) - .emit(contracts.Curation, 'Signalled') - .withArgs(curator.address, subgraphDeploymentID, tokensToDeposit, expectedSignal, curationTax) - - // After state - const afterTokenTotalSupply = await contracts.GraphToken.totalSupply() - const afterCuratorTokens = await contracts.GraphToken.balanceOf(curator.address) - const afterCuratorSignal = await contracts.Curation.getCuratorSignal(curator.address, subgraphDeploymentID) - const afterPool = await contracts.Curation.pools(subgraphDeploymentID) - const afterPoolSignal = await contracts.Curation.getCurationPoolSignal(subgraphDeploymentID) - const afterTotalTokens = await contracts.GraphToken.balanceOf(contracts.Curation.address) - - // Curator balance updated - expect(afterCuratorTokens).eq(beforeCuratorTokens.sub(tokensToDeposit)) - expect(afterCuratorSignal).eq(beforeCuratorSignal.add(expectedSignal)) - // Allocated and balance updated - expect(afterPool.tokens).eq(beforePool.tokens.add(tokensToDeposit.sub(curationTax))) - expect(afterPoolSignal).eq(beforePoolSignal.add(expectedSignal)) - expect(afterPool.reserveRatio).eq(await contracts.Curation.defaultReserveRatio()) - // Contract balance updated - expect(afterTotalTokens).eq(beforeTotalTokens.add(tokensToDeposit.sub(curationTax))) - // Total supply is reduced to curation tax burning - expect(afterTokenTotalSupply).eq(beforeTokenTotalSupply.sub(curationTax)) - } - - const shouldBurn = async (signalToRedeem: BigNumber, expectedTokens: BigNumber) => { - // Before balances - const beforeTokenTotalSupply = await contracts.GraphToken.totalSupply() - const beforeCuratorTokens = await contracts.GraphToken.balanceOf(curator.address) - const beforeCuratorSignal = await contracts.Curation.getCuratorSignal(curator.address, subgraphDeploymentID) - const beforePool = await contracts.Curation.pools(subgraphDeploymentID) - const beforePoolSignal = await contracts.Curation.getCurationPoolSignal(subgraphDeploymentID) - const beforeTotalTokens = await contracts.GraphToken.balanceOf(contracts.Curation.address) - - // Redeem - const tx = contracts.Curation.connect(curator).burn(subgraphDeploymentID, signalToRedeem, 0) - await expect(tx) - .emit(contracts.Curation, 'Burned') - .withArgs(curator.address, subgraphDeploymentID, expectedTokens, signalToRedeem) - - // After balances - const afterTokenTotalSupply = await contracts.GraphToken.totalSupply() - const afterCuratorTokens = await contracts.GraphToken.balanceOf(curator.address) - const afterCuratorSignal = await contracts.Curation.getCuratorSignal(curator.address, subgraphDeploymentID) - const afterPool = await contracts.Curation.pools(subgraphDeploymentID) - const afterPoolSignal = await contracts.Curation.getCurationPoolSignal(subgraphDeploymentID) - const afterTotalTokens = await contracts.GraphToken.balanceOf(contracts.Curation.address) - - // Curator balance updated - expect(afterCuratorTokens).eq(beforeCuratorTokens.add(expectedTokens)) - expect(afterCuratorSignal).eq(beforeCuratorSignal.sub(signalToRedeem)) - // Curation balance updated - expect(afterPool.tokens).eq(beforePool.tokens.sub(expectedTokens)) - expect(afterPoolSignal).eq(beforePoolSignal.sub(signalToRedeem)) - // Contract balance updated - expect(afterTotalTokens).eq(beforeTotalTokens.sub(expectedTokens)) - // Total supply is conserved - expect(afterTokenTotalSupply).eq(beforeTokenTotalSupply) - } - - const shouldCollect = async (tokensToCollect: BigNumber) => { - // Before state - const beforePool = await contracts.Curation.pools(subgraphDeploymentID) - const beforeTotalBalance = await contracts.GraphToken.balanceOf(contracts.Curation.address) - - // Source of tokens must be the staking for this to work - await contracts.GraphToken.connect(stakingMock).transfer(contracts.Curation.address, tokensToCollect) - const tx = contracts.Curation.connect(stakingMock).collect(subgraphDeploymentID, tokensToCollect) - await expect(tx).emit(contracts.Curation, 'Collected').withArgs(subgraphDeploymentID, tokensToCollect) - - // After state - const afterPool = await contracts.Curation.pools(subgraphDeploymentID) - const afterTotalBalance = await contracts.GraphToken.balanceOf(contracts.Curation.address) - - // State updated - expect(afterPool.tokens).eq(beforePool.tokens.add(tokensToCollect)) - expect(afterTotalBalance).eq(beforeTotalBalance.add(tokensToCollect)) - } - - before(async function () { - // Use stakingMock so we can call collect - ;[me, curator, stakingMock] = await graph.getTestAccounts() - ;({ governor } = await graph.getNamedAccounts()) - - fixture = new NetworkFixture(graph.provider) - contracts = await fixture.load(governor) - - gnsImpersonator = await helpers.impersonateAccount(contracts.GNS.address) - await helpers.setBalance(contracts.GNS.address, parseEther('1')) - // Give some funds to the curator and GNS impersonator and approve the curation contract - await contracts.GraphToken.connect(governor).mint(curator.address, curatorTokens) - await contracts.GraphToken.connect(curator).approve(contracts.Curation.address, curatorTokens) - await contracts.GraphToken.connect(governor).mint(contracts.GNS.address, curatorTokens) - await contracts.GraphToken.connect(gnsImpersonator).approve(contracts.Curation.address, curatorTokens) - - // Give some funds to the staking contract and approve the curation contract - await contracts.GraphToken.connect(governor).mint(stakingMock.address, tokensToCollect) - await contracts.GraphToken.connect(stakingMock).approve(contracts.Curation.address, tokensToCollect) - }) - - beforeEach(async function () { - await fixture.setUp() - }) - - afterEach(async function () { - await fixture.tearDown() - }) - - describe('bonding curve', function () { - const tokensToDeposit = curatorTokens - - it('reject convert signal to tokens if subgraph deployment not initted', async function () { - const tx = contracts.Curation.signalToTokens(subgraphDeploymentID, toGRT('100')) - await expect(tx).revertedWith('Subgraph deployment must be curated to perform calculations') - }) - - it('convert signal to tokens', async function () { - // Curate - await contracts.Curation.connect(curator).mint(subgraphDeploymentID, tokensToDeposit, 0) - - // Conversion - const signal = await contracts.Curation.getCurationPoolSignal(subgraphDeploymentID) - const expectedTokens = await contracts.Curation.signalToTokens(subgraphDeploymentID, signal) - expect(expectedTokens).eq(tokensToDeposit) - }) - - it('convert signal to tokens (with curation tax)', async function () { - // Set curation tax - const curationTaxPercentage = 50000 // 5% - await contracts.Curation.connect(governor).setCurationTaxPercentage(curationTaxPercentage) - - // Curate - const expectedCurationTax = tokensToDeposit.mul(curationTaxPercentage).div(MAX_PPM) - const { 1: curationTax } = await contracts.Curation.tokensToSignal(subgraphDeploymentID, tokensToDeposit) - await contracts.Curation.connect(curator).mint(subgraphDeploymentID, tokensToDeposit, 0) - - // Conversion - const signal = await contracts.Curation.getCurationPoolSignal(subgraphDeploymentID) - const tokens = await contracts.Curation.signalToTokens(subgraphDeploymentID, signal) - expect(tokens).eq(tokensToDeposit.sub(expectedCurationTax)) - expect(expectedCurationTax).eq(curationTax) - }) - - it('convert tokens to signal', async function () { - // Conversion - const tokens = toGRT('1000') - const { 0: signal } = await contracts.Curation.tokensToSignal(subgraphDeploymentID, tokens) - expect(signal).eq(signalAmountFor1000Tokens) - }) - - it('convert tokens to signal if non-curated subgraph', async function () { - // Conversion - const nonCuratedSubgraphDeploymentID = randomHexBytes() - const tokens = toGRT('1') - const tx = contracts.Curation.tokensToSignal(nonCuratedSubgraphDeploymentID, tokens) - await expect(tx).revertedWith('Curation deposit is below minimum required') - }) - }) - - describe('curate', function () { - it('reject deposit below minimum tokens required', async function () { - const tokensToDeposit = (await contracts.Curation.minimumCurationDeposit()).sub(toBN(1)) - const tx = contracts.Curation.connect(curator).mint(subgraphDeploymentID, tokensToDeposit, 0) - await expect(tx).revertedWith('Curation deposit is below minimum required') - }) - - it('should deposit on a subgraph deployment', async function () { - const tokensToDeposit = await contracts.Curation.minimumCurationDeposit() - const expectedSignal = toGRT('1') - await shouldMint(tokensToDeposit, expectedSignal) - }) - - it('should get signal according to bonding curve', async function () { - const tokensToDeposit = toGRT('1000') - const expectedSignal = signalAmountFor1000Tokens - await shouldMint(tokensToDeposit, expectedSignal) - }) - - it('should get signal according to bonding curve (and account for curation tax)', async function () { - // Set curation tax - await contracts.Curation.connect(governor).setCurationTaxPercentage(50000) // 5% - - // Mint - const tokensToDeposit = toGRT('1000') - const { 0: expectedSignal } = await contracts.Curation.tokensToSignal(subgraphDeploymentID, tokensToDeposit) - await shouldMint(tokensToDeposit, expectedSignal) - }) - - it('should revert curate if over slippage', async function () { - const tokensToDeposit = toGRT('1000') - const expectedSignal = signalAmountFor1000Tokens - const tx = contracts.Curation.connect(curator).mint(subgraphDeploymentID, tokensToDeposit, expectedSignal.add(1)) - await expect(tx).revertedWith('Slippage protection') - }) - }) - - describe('collect', function () { - context('> not curated', function () { - it('reject collect tokens distributed to the curation pool', async function () { - // Source of tokens must be the staking for this to work - await contracts.Controller.connect(governor).setContractProxy(utils.id('Staking'), stakingMock.address) - await contracts.Curation.connect(governor).syncAllContracts() // call sync because we change the proxy for staking - - const tx = contracts.Curation.connect(stakingMock).collect(subgraphDeploymentID, tokensToCollect) - await expect(tx).revertedWith('Subgraph deployment must be curated to collect fees') - }) - }) - - context('> curated', function () { - beforeEach(async function () { - await contracts.Curation.connect(curator).mint(subgraphDeploymentID, toGRT('1000'), 0) - }) - - it('reject collect tokens distributed from invalid address', async function () { - const tx = contracts.Curation.connect(me).collect(subgraphDeploymentID, tokensToCollect) - await expect(tx).revertedWith('Caller must be the staking contract') - }) - - it('should collect tokens distributed to the curation pool', async function () { - await contracts.Controller.connect(governor).setContractProxy(utils.id('Staking'), stakingMock.address) - await contracts.Curation.connect(governor).syncAllContracts() // call sync because we change the proxy for staking - - await shouldCollect(toGRT('1')) - await shouldCollect(toGRT('10')) - await shouldCollect(toGRT('100')) - await shouldCollect(toGRT('200')) - await shouldCollect(toGRT('500.25')) - }) - - it('should collect tokens and then unsignal all', async function () { - await contracts.Controller.connect(governor).setContractProxy(utils.id('Staking'), stakingMock.address) - await contracts.Curation.connect(governor).syncAllContracts() // call sync because we change the proxy for staking - - // Collect increase the pool reserves - await shouldCollect(toGRT('100')) - - // When we burn signal we should get more tokens than initially curated - const signalToRedeem = await contracts.Curation.getCuratorSignal(curator.address, subgraphDeploymentID) - await shouldBurn(signalToRedeem, toGRT('1100')) - }) - - it('should collect tokens and then unsignal multiple times', async function () { - await contracts.Controller.connect(governor).setContractProxy(utils.id('Staking'), stakingMock.address) - await contracts.Curation.connect(governor).syncAllContracts() // call sync because we change the proxy for staking - - // Collect increase the pool reserves - const tokensToCollect = toGRT('100') - await shouldCollect(tokensToCollect) - - // Unsignal partially - const signalOutRemainder = toGRT(1) - const signalOutPartial = (await contracts.Curation.getCuratorSignal(curator.address, subgraphDeploymentID)).sub( - signalOutRemainder, - ) - const tx1 = await contracts.Curation.connect(curator).burn(subgraphDeploymentID, signalOutPartial, 0) - const r1 = await tx1.wait() - const event1 = contracts.Curation.interface.parseLog(r1.events[2]).args - const tokensOut1 = event1.tokens - - // Collect increase the pool reserves - await shouldCollect(tokensToCollect) - - // Unsignal the rest - const tx2 = await contracts.Curation.connect(curator).burn(subgraphDeploymentID, signalOutRemainder, 0) - const r2 = await tx2.wait() - const event2 = contracts.Curation.interface.parseLog(r2.events[2]).args - const tokensOut2 = event2.tokens - - expect(tokensOut1.add(tokensOut2)).eq(toGRT('1000').add(tokensToCollect.mul(2))) - }) - }) - }) - - describe('burn', function () { - beforeEach(async function () { - await contracts.Curation.connect(curator).mint(subgraphDeploymentID, tokensToDeposit, 0) - }) - - it('reject redeem more than a curator owns', async function () { - const tx = contracts.Curation.connect(me).burn(subgraphDeploymentID, toGRT('1'), 0) - await expect(tx).revertedWith('Cannot burn more signal than you own') - }) - - it('reject redeem zero signal', async function () { - const tx = contracts.Curation.connect(me).burn(subgraphDeploymentID, toGRT('0'), 0) - await expect(tx).revertedWith('Cannot burn zero signal') - }) - - it('should allow to redeem *partially*', async function () { - // Redeem just one signal - const signalToRedeem = toGRT('1') - const expectedTokens = toGRT('532.455532033675866536') - await shouldBurn(signalToRedeem, expectedTokens) - }) - - it('should allow to redeem *fully*', async function () { - // Get all signal of the curator - const signalToRedeem = await contracts.Curation.getCuratorSignal(curator.address, subgraphDeploymentID) - const expectedTokens = tokensToDeposit - await shouldBurn(signalToRedeem, expectedTokens) - }) - - it('should allow to redeem back below minimum deposit', async function () { - // Redeem "almost" all signal - const signal = await contracts.Curation.getCuratorSignal(curator.address, subgraphDeploymentID) - const signalToRedeem = signal.sub(toGRT('0.000001')) - const expectedTokens = await contracts.Curation.signalToTokens(subgraphDeploymentID, signalToRedeem) - await shouldBurn(signalToRedeem, expectedTokens) - - // The pool should have less tokens that required by minimumCurationDeposit - const afterPool = await contracts.Curation.pools(subgraphDeploymentID) - expect(afterPool.tokens).lt(await contracts.Curation.minimumCurationDeposit()) - - // Should be able to deposit more after being under minimumCurationDeposit - const tokensToDeposit = toGRT('1') - const { 0: expectedSignal } = await contracts.Curation.tokensToSignal(subgraphDeploymentID, tokensToDeposit) - await shouldMint(tokensToDeposit, expectedSignal) - }) - - it('should revert redeem if over slippage', async function () { - const signalToRedeem = await contracts.Curation.getCuratorSignal(curator.address, subgraphDeploymentID) - const expectedTokens = tokensToDeposit - - const tx = contracts.Curation.connect(curator).burn(subgraphDeploymentID, signalToRedeem, expectedTokens.add(1)) - await expect(tx).revertedWith('Slippage protection') - }) - - it('should not re-deploy the curation token when signal is reset', async function () { - const beforeSubgraphPool = await contracts.Curation.pools(subgraphDeploymentID) - - // Burn all the signal - const signalToRedeem = await contracts.Curation.getCuratorSignal(curator.address, subgraphDeploymentID) - const expectedTokens = tokensToDeposit - await shouldBurn(signalToRedeem, expectedTokens) - - // Mint again on the same subgraph - await contracts.Curation.connect(curator).mint(subgraphDeploymentID, tokensToDeposit, 0) - - // Check state - const afterSubgraphPool = await contracts.Curation.pools(subgraphDeploymentID) - expect(afterSubgraphPool.gcs).eq(beforeSubgraphPool.gcs) - }) - }) - - describe('conservation', function () { - it('should match multiple deposits and redeems back to initial state', async function () { - this.timeout(60000) // increase timeout for test runner - - const totalDeposits = toGRT('1000000000') - - // Signal multiple times - let totalSignal = toGRT('0') - for (const tokensToDeposit of chunkify(totalDeposits, 10)) { - const tx = await contracts.Curation.connect(curator).mint(subgraphDeploymentID, tokensToDeposit, 0) - const receipt = await tx.wait() - const event: Event = receipt.events.pop() - const signal = event.args['signal'] - totalSignal = totalSignal.add(signal) - } - - // Redeem signal multiple times - let totalTokens = toGRT('0') - for (const signalToRedeem of chunkify(totalSignal, 10)) { - const tx = await contracts.Curation.connect(curator).burn(subgraphDeploymentID, signalToRedeem, 0) - const receipt = await tx.wait() - const event: Event = receipt.events.pop() - const tokens = event.args['tokens'] - totalTokens = totalTokens.add(tokens) - // console.log('<', formatEther(signalToRedeem), '=', formatEther(tokens)) - } - - // Conservation of work - const afterPool = await contracts.Curation.pools(subgraphDeploymentID) - const afterPoolSignal = await contracts.Curation.getCurationPoolSignal(subgraphDeploymentID) - expect(afterPool.tokens).eq(toGRT('0')) - expect(afterPoolSignal).eq(toGRT('0')) - expect(await contracts.Curation.isCurated(subgraphDeploymentID)).eq(false) - expect(totalDeposits).eq(totalTokens) - }) - }) - - describe('multiple minting', function () { - it('should mint less signal every time due to the bonding curve', async function () { - const tokensToDepositMany = [ - toGRT('1000'), // should mint if we start with number above minimum deposit - toGRT('1000'), // every time it should mint less GCS due to bonding curve... - toGRT('1000'), - toGRT('1000'), - toGRT('2000'), - toGRT('2000'), - toGRT('123'), - toGRT('1'), // should mint below minimum deposit - ] - for (const tokensToDeposit of tokensToDepositMany) { - const expectedSignal = await calcBondingCurve( - await contracts.Curation.getCurationPoolSignal(subgraphDeploymentID), - await contracts.Curation.getCurationPoolTokens(subgraphDeploymentID), - await contracts.Curation.defaultReserveRatio(), - tokensToDeposit, - ) - - const tx = await contracts.Curation.connect(curator).mint(subgraphDeploymentID, tokensToDeposit, 0) - const receipt = await tx.wait() - const event: Event = receipt.events.pop() - const signal = event.args['signal'] - expect(toRound(expectedSignal)).eq(toRound(toFloat(signal))) - } - }) - - it('should mint when using the edge case of linear function', async function () { - this.timeout(60000) // increase timeout for test runner - - // Setup edge case like linear function: 1 GRT = 1 GCS - await contracts.Curation.connect(governor).setMinimumCurationDeposit(toGRT('1')) - await contracts.Curation.connect(governor).setDefaultReserveRatio(1000000) - - const tokensToDepositMany = [ - toGRT('1000'), // should mint if we start with number above minimum deposit - toGRT('1000'), // every time it should mint less GCS due to bonding curve... - toGRT('1000'), - toGRT('1000'), - toGRT('2000'), - toGRT('2000'), - toGRT('123'), - toGRT('1'), // should mint below minimum deposit - ] - - // Mint multiple times - for (const tokensToDeposit of tokensToDepositMany) { - const tx = await contracts.Curation.connect(curator).mint(subgraphDeploymentID, tokensToDeposit, 0) - const receipt = await tx.wait() - const event: Event = receipt.events.pop() - const signal = event.args['signal'] - expect(tokensToDeposit).eq(signal) // we compare 1:1 ratio - } - }) - }) -}) diff --git a/packages/contracts-test/tests/unit/disputes/common.ts b/packages/contracts-test/tests/unit/disputes/common.ts deleted file mode 100644 index 2e1816bf7..000000000 --- a/packages/contracts-test/tests/unit/disputes/common.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Attestation, Receipt } from '@graphprotocol/common-ts' -import { utils } from 'ethers' - -export const MAX_PPM = 1000000 - -const { defaultAbiCoder: abi, arrayify, concat, hexlify, solidityKeccak256, joinSignature } = utils - -export interface Dispute { - id: string - attestation: Attestation - encodedAttestation: string - indexerAddress: string - receipt: Receipt -} - -export function createQueryDisputeID( - attestation: Attestation, - indexerAddress: string, - submitterAddress: string, -): string { - return solidityKeccak256( - ['bytes32', 'bytes32', 'bytes32', 'address', 'address'], - [ - attestation.requestCID, - attestation.responseCID, - attestation.subgraphDeploymentID, - indexerAddress, - submitterAddress, - ], - ) -} - -export function encodeAttestation(attestation: Attestation): string { - const data = arrayify( - abi.encode( - ['bytes32', 'bytes32', 'bytes32'], - [attestation.requestCID, attestation.responseCID, attestation.subgraphDeploymentID], - ), - ) - const sig = joinSignature(attestation) - return hexlify(concat([data, sig])) -} diff --git a/packages/contracts-test/tests/unit/disputes/configuration.test.ts b/packages/contracts-test/tests/unit/disputes/configuration.test.ts deleted file mode 100644 index e03c0d1db..000000000 --- a/packages/contracts-test/tests/unit/disputes/configuration.test.ts +++ /dev/null @@ -1,136 +0,0 @@ -import { DisputeManager } from '@graphprotocol/contracts' -import { GraphNetworkContracts, toBN } from '@graphprotocol/sdk' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { expect } from 'chai' -import { constants } from 'ethers' -import hre from 'hardhat' - -import { NetworkFixture } from '../lib/fixtures' - -const { AddressZero } = constants - -const MAX_PPM = 1000000 - -describe('DisputeManager:Config', () => { - let me: SignerWithAddress - let governor: SignerWithAddress - let arbitrator: SignerWithAddress - - const graph = hre.graph() - const defaults = graph.graphConfig.defaults - let fixture: NetworkFixture - - let contracts: GraphNetworkContracts - let disputeManager: DisputeManager - - before(async function () { - ;[me] = await graph.getTestAccounts() - ;({ governor, arbitrator } = await graph.getNamedAccounts()) - - fixture = new NetworkFixture(graph.provider) - contracts = await fixture.load(governor) - disputeManager = contracts.DisputeManager as DisputeManager - }) - - beforeEach(async function () { - await fixture.setUp() - }) - - afterEach(async function () { - await fixture.tearDown() - }) - - describe('configuration', () => { - describe('arbitrator', function () { - it('should set `arbitrator`', async function () { - // Set right in the constructor - expect(await disputeManager.arbitrator()).eq(arbitrator.address) - - // Can set if allowed - await disputeManager.connect(governor).setArbitrator(me.address) - expect(await disputeManager.arbitrator()).eq(me.address) - }) - - it('reject set `arbitrator` if not allowed', async function () { - const tx = disputeManager.connect(me).setArbitrator(arbitrator.address) - await expect(tx).revertedWith('Only Controller governor') - }) - - it('reject set `arbitrator` to address zero', async function () { - const tx = disputeManager.connect(governor).setArbitrator(AddressZero) - await expect(tx).revertedWith('Arbitrator must be set') - }) - }) - - describe('minimumDeposit', function () { - it('should set `minimumDeposit`', async function () { - const oldValue = defaults.dispute.minimumDeposit - const newValue = toBN('1') - - // Set right in the constructor - expect(await disputeManager.minimumDeposit()).eq(oldValue) - - // Set new value - await disputeManager.connect(governor).setMinimumDeposit(newValue) - expect(await disputeManager.minimumDeposit()).eq(newValue) - }) - - it('reject set `minimumDeposit` if not allowed', async function () { - const newValue = toBN('1') - const tx = disputeManager.connect(me).setMinimumDeposit(newValue) - await expect(tx).revertedWith('Only Controller governor') - }) - }) - - describe('fishermanRewardPercentage', function () { - it('should set `fishermanRewardPercentage`', async function () { - const newValue = defaults.dispute.fishermanRewardPercentage - - // Set right in the constructor - expect(await disputeManager.fishermanRewardPercentage()).eq(newValue) - - // Set new value - await disputeManager.connect(governor).setFishermanRewardPercentage(0) - await disputeManager.connect(governor).setFishermanRewardPercentage(newValue) - }) - - it('reject set `fishermanRewardPercentage` if out of bounds', async function () { - const tx = disputeManager.connect(governor).setFishermanRewardPercentage(MAX_PPM + 1) - await expect(tx).revertedWith('Reward percentage must be below or equal to MAX_PPM') - }) - - it('reject set `fishermanRewardPercentage` if not allowed', async function () { - const tx = disputeManager.connect(me).setFishermanRewardPercentage(50) - await expect(tx).revertedWith('Only Controller governor') - }) - }) - - describe('slashingPercentage', function () { - it('should set `slashingPercentage`', async function () { - const qryNewValue = defaults.dispute.qrySlashingPercentage - const idxNewValue = defaults.dispute.idxSlashingPercentage - - // Set right in the constructor - expect(await disputeManager.qrySlashingPercentage()).eq(qryNewValue) - expect(await disputeManager.idxSlashingPercentage()).eq(idxNewValue) - - // Set new value - await disputeManager.connect(governor).setSlashingPercentage(0, 0) - await disputeManager.connect(governor).setSlashingPercentage(qryNewValue, idxNewValue) - }) - - it('reject set `slashingPercentage` if out of bounds', async function () { - const tx1 = disputeManager.connect(governor).setSlashingPercentage(0, MAX_PPM + 1) - await expect(tx1).revertedWith('Slashing percentage must be below or equal to MAX_PPM') - - const tx2 = disputeManager.connect(governor).setSlashingPercentage(MAX_PPM + 1, 0) - await expect(tx2).revertedWith('Slashing percentage must be below or equal to MAX_PPM') - }) - - it('reject set `slashingPercentage` if not allowed', async function () { - const tx = disputeManager.connect(me).setSlashingPercentage(50, 50) - await expect(tx).revertedWith('Only Controller governor') - }) - }) - }) -}) diff --git a/packages/contracts-test/tests/unit/disputes/poi.test.ts b/packages/contracts-test/tests/unit/disputes/poi.test.ts deleted file mode 100644 index b391dd0d4..000000000 --- a/packages/contracts-test/tests/unit/disputes/poi.test.ts +++ /dev/null @@ -1,230 +0,0 @@ -import { DisputeManager, IRewardsManager } from '@graphprotocol/contracts' -import { EpochManager } from '@graphprotocol/contracts' -import { GraphToken } from '@graphprotocol/contracts' -import { IStaking } from '@graphprotocol/contracts' -import { deriveChannelKey, GraphNetworkContracts, helpers, randomHexBytes, toBN, toGRT } from '@graphprotocol/sdk' -import type { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { expect } from 'chai' -import { utils } from 'ethers' -import hre from 'hardhat' - -import { NetworkFixture } from '../lib/fixtures' -import { MAX_PPM } from './common' - -const { keccak256 } = utils - -describe('DisputeManager:POI', () => { - let governor: SignerWithAddress - let arbitrator: SignerWithAddress - let indexer: SignerWithAddress - let rewardsDestination: SignerWithAddress - let fisherman: SignerWithAddress - let assetHolder: SignerWithAddress - - const graph = hre.graph() - - let fixture: NetworkFixture - - let contracts: GraphNetworkContracts - let disputeManager: DisputeManager - let epochManager: EpochManager - let grt: GraphToken - let staking: IStaking - let rewardsManager: IRewardsManager - - // Derive some channel keys for each indexer used to sign attestations - const indexerChannelKey = deriveChannelKey() - - // Test values - const fishermanTokens = toGRT('100000') - const fishermanDeposit = toGRT('1000') - const indexerTokens = toGRT('100000') - const indexerAllocatedTokens = toGRT('10000') - const allocationID = indexerChannelKey.address - const subgraphDeploymentID = randomHexBytes(32) - const metadata = randomHexBytes(32) - const poi = randomHexBytes(32) // proof of indexing - - async function calculateSlashConditions(indexerAddress: string) { - const idxSlashingPercentage = await disputeManager.idxSlashingPercentage() - const fishermanRewardPercentage = await disputeManager.fishermanRewardPercentage() - const stakeAmount = await staking.getIndexerStakedTokens(indexerAddress) - const slashAmount = stakeAmount.mul(idxSlashingPercentage).div(toBN(MAX_PPM)) - const rewardsAmount = slashAmount.mul(fishermanRewardPercentage).div(toBN(MAX_PPM)) - - return { slashAmount, rewardsAmount } - } - - async function setupIndexers() { - // Dispute manager is allowed to slash - await staking.connect(governor).setSlasher(disputeManager.address, true) - - // Stake & allocate - const indexerList = [{ account: indexer, allocationID: indexerChannelKey.address, channelKey: indexerChannelKey }] - for (const activeIndexer of indexerList) { - const { channelKey, allocationID, account: indexerAccount } = activeIndexer - - // Give some funds to the indexer - await grt.connect(governor).mint(indexerAccount.address, indexerTokens) - await grt.connect(indexerAccount).approve(staking.address, indexerTokens) - - // Indexer stake funds - await staking.connect(indexerAccount).stake(indexerTokens) - await staking - .connect(indexerAccount) - .allocateFrom( - indexerAccount.address, - subgraphDeploymentID, - indexerAllocatedTokens, - allocationID, - metadata, - await channelKey.generateProof(indexerAccount.address), - ) - } - } - - before(async function () { - ;[indexer, fisherman, assetHolder, rewardsDestination] = await graph.getTestAccounts() - ;({ governor, arbitrator } = await graph.getNamedAccounts()) - - fixture = new NetworkFixture(graph.provider) - contracts = await fixture.load(governor) - disputeManager = contracts.DisputeManager as DisputeManager - epochManager = contracts.EpochManager as EpochManager - grt = contracts.GraphToken as GraphToken - staking = contracts.Staking as IStaking - rewardsManager = contracts.RewardsManager as IRewardsManager - - // Give some funds to the fisherman - await grt.connect(governor).mint(fisherman.address, fishermanTokens) - await grt.connect(fisherman).approve(disputeManager.address, fishermanTokens) - - // HACK: we set the staking contract as the subgraph service to make tests pass. - // This is due to the test suite being outdated. - await rewardsManager.connect(governor).setSubgraphService(staking.address) - }) - - beforeEach(async function () { - await fixture.setUp() - }) - - afterEach(async function () { - await fixture.tearDown() - }) - - describe('disputes', function () { - it('reject create a dispute if allocation does not exist', async function () { - const invalidAllocationID = randomHexBytes(20) - - // Create dispute - const tx = disputeManager.connect(fisherman).createIndexingDispute(invalidAllocationID, fishermanDeposit) - await expect(tx).revertedWith('Dispute allocation must exist') - }) - - it('reject create a dispute if indexer below stake', async function () { - // This tests reproduce the case when someones present a dispute for - // an indexer that is under the minimum required staked amount - - const indexerCollectedTokens = toGRT('10') - - // Give some funds to the indexer - await grt.connect(governor).mint(indexer.address, indexerTokens) - await grt.connect(indexer).approve(staking.address, indexerTokens) - - // Give some funds to the channel - await grt.connect(governor).mint(assetHolder.address, indexerCollectedTokens) - await grt.connect(assetHolder).approve(staking.address, indexerCollectedTokens) - - // Set the thawing period to one to make the test easier - await staking.connect(governor).setThawingPeriod(toBN('1')) - - // Indexer stake funds, allocate, close, unstake and withdraw the stake fully - await staking.connect(indexer).stake(indexerTokens) - const tx1 = await staking - .connect(indexer) - .allocateFrom( - indexer.address, - subgraphDeploymentID, - indexerAllocatedTokens, - allocationID, - metadata, - await indexerChannelKey.generateProof(indexer.address), - ) - const receipt1 = await tx1.wait() - const event1 = staking.interface.parseLog(receipt1.logs[0]).args - await helpers.mineEpoch(epochManager) // wait the required one epoch to close allocation - // set rewards destination so collected query fees are not added to indexer balance - await staking.connect(indexer).setRewardsDestination(rewardsDestination.address) - await staking.connect(assetHolder).collect(indexerCollectedTokens, event1.allocationID) - await staking.connect(indexer).closeAllocation(event1.allocationID, poi) - await staking.connect(indexer).unstake(indexerTokens) - await helpers.mine() // pass thawing period - await staking.connect(indexer).withdraw() - - // Create dispute - const tx = disputeManager.connect(fisherman).createIndexingDispute(event1.allocationID, fishermanDeposit) - await expect(tx).revertedWith('Dispute indexer has no stake') - }) - - context('> when indexer is staked', function () { - beforeEach(async function () { - await setupIndexers() - }) - - it('should create a dispute', async function () { - // Create dispute - const tx = disputeManager.connect(fisherman).createIndexingDispute(allocationID, fishermanDeposit) - await expect(tx) - .emit(disputeManager, 'IndexingDisputeCreated') - .withArgs(keccak256(allocationID), indexer.address, fisherman.address, fishermanDeposit, allocationID) - }) - - context('> when dispute is created', function () { - // NOTE: other dispute resolution paths are tested in query.test.ts - - beforeEach(async function () { - // Create dispute - await disputeManager.connect(fisherman).createIndexingDispute(allocationID, fishermanDeposit) - }) - - it('reject create duplicated dispute', async function () { - const tx = disputeManager.connect(fisherman).createIndexingDispute(allocationID, fishermanDeposit) - await expect(tx).revertedWith('Dispute already created') - }) - - describe('accept a dispute', function () { - it('should resolve dispute, slash indexer and reward the fisherman', async function () { - const disputeID = keccak256(allocationID) - - // Before state - const beforeIndexerStake = await staking.getIndexerStakedTokens(indexer.address) - const beforeFishermanBalance = await grt.balanceOf(fisherman.address) - const beforeTotalSupply = await grt.totalSupply() - - // Calculations - const { slashAmount, rewardsAmount } = await calculateSlashConditions(indexer.address) - - // Perform transaction (accept) - const tx = disputeManager.connect(arbitrator).acceptDispute(disputeID) - await expect(tx) - .emit(disputeManager, 'DisputeAccepted') - .withArgs(disputeID, indexer.address, fisherman.address, fishermanDeposit.add(rewardsAmount)) - - // After state - const afterFishermanBalance = await grt.balanceOf(fisherman.address) - const afterIndexerStake = await staking.getIndexerStakedTokens(indexer.address) - const afterTotalSupply = await grt.totalSupply() - - // Fisherman reward properly assigned + deposit returned - expect(afterFishermanBalance).eq(beforeFishermanBalance.add(fishermanDeposit).add(rewardsAmount)) - // Indexer slashed - expect(afterIndexerStake).eq(beforeIndexerStake.sub(slashAmount)) - // Slashed funds burned - const tokensToBurn = slashAmount.sub(rewardsAmount) - expect(afterTotalSupply).eq(beforeTotalSupply.sub(tokensToBurn)) - }) - }) - }) - }) - }) -}) diff --git a/packages/contracts-test/tests/unit/disputes/query.test.ts b/packages/contracts-test/tests/unit/disputes/query.test.ts deleted file mode 100644 index e411bd028..000000000 --- a/packages/contracts-test/tests/unit/disputes/query.test.ts +++ /dev/null @@ -1,501 +0,0 @@ -import { createAttestation, Receipt } from '@graphprotocol/common-ts' -import { DisputeManager, IRewardsManager } from '@graphprotocol/contracts' -import { EpochManager } from '@graphprotocol/contracts' -import { GraphToken } from '@graphprotocol/contracts' -import { IStaking } from '@graphprotocol/contracts' -import { deriveChannelKey, GraphNetworkContracts, helpers, randomHexBytes, toBN, toGRT } from '@graphprotocol/sdk' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { expect } from 'chai' -import { constants } from 'ethers' -import hre from 'hardhat' - -import { NetworkFixture } from '../lib/fixtures' -import { createQueryDisputeID, Dispute, encodeAttestation, MAX_PPM } from './common' - -const { HashZero } = constants - -const NON_EXISTING_DISPUTE_ID = randomHexBytes() - -describe('DisputeManager:Query', () => { - let me: SignerWithAddress - let governor: SignerWithAddress - let arbitrator: SignerWithAddress - let indexer: SignerWithAddress - let indexer2: SignerWithAddress - let rewardsDestination: SignerWithAddress - let fisherman: SignerWithAddress - let fisherman2: SignerWithAddress - let assetHolder: SignerWithAddress - - const graph = hre.graph() - let fixture: NetworkFixture - - let contracts: GraphNetworkContracts - let disputeManager: DisputeManager - let epochManager: EpochManager - let grt: GraphToken - let staking: IStaking - let rewardsManager: IRewardsManager - - // Derive some channel keys for each indexer used to sign attestations - const indexer1ChannelKey = deriveChannelKey() - const indexer2ChannelKey = deriveChannelKey() - - // Test values - const fishermanTokens = toGRT('100000') - const fishermanDeposit = toGRT('1000') - const indexerTokens = toGRT('100000') - const indexerAllocatedTokens = toGRT('10000') - const metadata = HashZero - - const poi = randomHexBytes() - - // Create an attesation receipt for the dispute - const receipt: Receipt = { - requestCID: randomHexBytes(), - responseCID: randomHexBytes(), - subgraphDeploymentID: randomHexBytes(), - } - let dispute: Dispute - - async function buildAttestation(receipt: Receipt, signer: string) { - const attestation = await createAttestation(signer, graph.chainId, disputeManager.address, receipt, '0') - return attestation - } - - async function calculateSlashConditions(indexerAddress: string) { - const qrySlashingPercentage = await disputeManager.qrySlashingPercentage() - const fishermanRewardPercentage = await disputeManager.fishermanRewardPercentage() - const stakeAmount = await staking.getIndexerStakedTokens(indexerAddress) - const slashAmount = stakeAmount.mul(qrySlashingPercentage).div(toBN(MAX_PPM)) - const rewardsAmount = slashAmount.mul(fishermanRewardPercentage).div(toBN(MAX_PPM)) - - return { slashAmount, rewardsAmount } - } - - async function setupIndexers() { - // Dispute manager is allowed to slash - await staking.connect(governor).setSlasher(disputeManager.address, true) - - // Stake - const indexerList = [ - { - account: indexer, - allocationID: indexer1ChannelKey.address, - channelKey: indexer1ChannelKey, - }, - { - account: indexer2, - allocationID: indexer2ChannelKey.address, - channelKey: indexer2ChannelKey, - }, - ] - for (const activeIndexer of indexerList) { - const { channelKey, allocationID, account: indexerAccount } = activeIndexer - - // Give some funds to the indexer - await grt.connect(governor).mint(indexerAccount.address, indexerTokens) - await grt.connect(indexerAccount).approve(staking.address, indexerTokens) - - // Indexer stake funds - await staking.connect(indexerAccount).stake(indexerTokens) - await staking - .connect(indexerAccount) - .allocateFrom( - indexerAccount.address, - dispute.receipt.subgraphDeploymentID, - indexerAllocatedTokens, - allocationID, - metadata, - await channelKey.generateProof(indexerAccount.address), - ) - } - } - - before(async function () { - ;[me, indexer, indexer2, fisherman, fisherman2, assetHolder, rewardsDestination] = await graph.getTestAccounts() - ;({ governor, arbitrator } = await graph.getNamedAccounts()) - - fixture = new NetworkFixture(graph.provider) - contracts = await fixture.load(governor) - disputeManager = contracts.DisputeManager as DisputeManager - epochManager = contracts.EpochManager as EpochManager - grt = contracts.GraphToken as GraphToken - staking = contracts.Staking as IStaking - rewardsManager = contracts.RewardsManager as IRewardsManager - - // Give some funds to the fisherman - for (const dst of [fisherman, fisherman2]) { - await grt.connect(governor).mint(dst.address, fishermanTokens) - await grt.connect(dst).approve(disputeManager.address, fishermanTokens) - } - - // Create an attestation - const attestation = await buildAttestation(receipt, indexer1ChannelKey.privKey) - - // Create dispute data - dispute = { - id: createQueryDisputeID(attestation, indexer.address, fisherman.address), - attestation, - encodedAttestation: encodeAttestation(attestation), - indexerAddress: indexer.address, - receipt, - } - - // HACK: we set the staking contract as the subgraph service to make tests pass. - // This is due to the test suite being outdated. - await rewardsManager.connect(governor).setSubgraphService(staking.address) - }) - - beforeEach(async function () { - await fixture.setUp() - }) - - afterEach(async function () { - await fixture.tearDown() - }) - - describe('disputes', function () { - it('reject create a dispute if attestation does not refer to valid indexer', async function () { - // Create dispute - const tx = disputeManager.connect(fisherman).createQueryDispute(dispute.encodedAttestation, fishermanDeposit) - await expect(tx).revertedWith('Indexer cannot be found for the attestation') - }) - - it('reject create a dispute if indexer below stake', async function () { - // This tests reproduce the case when someones present a dispute for - // an indexer that is under the minimum required staked amount - - const indexerCollectedTokens = toGRT('10') - - // Give some funds to the indexer - await grt.connect(governor).mint(indexer.address, indexerTokens) - await grt.connect(indexer).approve(staking.address, indexerTokens) - - // Give some funds to the channel - await grt.connect(governor).mint(assetHolder.address, indexerCollectedTokens) - await grt.connect(assetHolder).approve(staking.address, indexerCollectedTokens) - - // Set the thawing period to zero to make the test easier - await staking.connect(governor).setThawingPeriod(toBN('1')) - - // Indexer stake funds, allocate, close allocation, unstake and withdraw the stake fully - await staking.connect(indexer).stake(indexerTokens) - const tx1 = await staking - .connect(indexer) - .allocateFrom( - indexer.address, - dispute.receipt.subgraphDeploymentID, - indexerAllocatedTokens, - indexer1ChannelKey.address, - metadata, - await indexer1ChannelKey.generateProof(indexer.address), - ) - const receipt1 = await tx1.wait() - const event1 = staking.interface.parseLog(receipt1.logs[0]).args - await helpers.mineEpoch(epochManager) // wait the required one epoch to close allocation - // set rewards destination so collected query fees are not added to indexer balance - await staking.connect(indexer).setRewardsDestination(rewardsDestination.address) - await staking.connect(assetHolder).collect(indexerCollectedTokens, event1.allocationID) - await staking.connect(indexer).closeAllocation(event1.allocationID, poi) - await staking.connect(indexer).unstake(indexerTokens) - await helpers.mine() // pass thawing period - await staking.connect(indexer).withdraw() - - // Create dispute - const tx = disputeManager.connect(fisherman).createQueryDispute(dispute.encodedAttestation, fishermanDeposit) - await expect(tx).revertedWith('Dispute indexer has no stake') - }) - - context('> when indexer is staked', function () { - beforeEach(async function () { - await setupIndexers() - }) - - describe('create dispute', function () { - it('reject fisherman deposit below minimum required', async function () { - // Minimum deposit a fisherman is required to do should be >= reward - const minimumDeposit = await disputeManager.minimumDeposit() - const belowMinimumDeposit = minimumDeposit.sub(toBN('1')) - - // Create invalid dispute as deposit is below minimum - const tx = disputeManager - .connect(fisherman) - .createQueryDispute(dispute.encodedAttestation, belowMinimumDeposit) - await expect(tx).revertedWith('Dispute deposit is under minimum required') - }) - - it('should create a dispute', async function () { - // Create dispute - const tx = disputeManager.connect(fisherman).createQueryDispute(dispute.encodedAttestation, fishermanDeposit) - await expect(tx) - .emit(disputeManager, 'QueryDisputeCreated') - .withArgs( - dispute.id, - dispute.indexerAddress, - fisherman.address, - fishermanDeposit, - dispute.receipt.subgraphDeploymentID, - dispute.encodedAttestation, - ) - }) - }) - - describe('accept a dispute', function () { - it('reject to accept a non-existing dispute', async function () { - const tx = disputeManager.connect(arbitrator).acceptDispute(NON_EXISTING_DISPUTE_ID) - await expect(tx).revertedWith('Dispute does not exist') - }) - }) - - describe('reject a dispute', function () { - it('reject to reject a non-existing dispute', async function () { - const tx = disputeManager.connect(arbitrator).rejectDispute(NON_EXISTING_DISPUTE_ID) - await expect(tx).revertedWith('Dispute does not exist') - }) - }) - - describe('draw a dispute', function () { - it('reject to draw a non-existing dispute', async function () { - const tx = disputeManager.connect(arbitrator).drawDispute(NON_EXISTING_DISPUTE_ID) - await expect(tx).revertedWith('Dispute does not exist') - }) - }) - - context('> when dispute is created', function () { - beforeEach(async function () { - // Create dispute - await disputeManager.connect(fisherman).createQueryDispute(dispute.encodedAttestation, fishermanDeposit) - }) - - describe('create a dispute', function () { - it('should create dispute if receipt is equal but for other indexer', async function () { - // Create dispute (same receipt but different indexer) - const attestation = await buildAttestation(receipt, indexer2ChannelKey.privKey) - const newDispute: Dispute = { - id: createQueryDisputeID(attestation, indexer2.address, fisherman.address), - attestation, - encodedAttestation: encodeAttestation(attestation), - indexerAddress: indexer2.address, - receipt, - } - - // Create dispute - const tx = disputeManager - .connect(fisherman) - .createQueryDispute(newDispute.encodedAttestation, fishermanDeposit) - await expect(tx) - .emit(disputeManager, 'QueryDisputeCreated') - .withArgs( - newDispute.id, - newDispute.indexerAddress, - fisherman.address, - fishermanDeposit, - newDispute.receipt.subgraphDeploymentID, - newDispute.encodedAttestation, - ) - }) - - it('should create dispute as long as it is from different fisherman', async function () { - await disputeManager.connect(fisherman2).createQueryDispute(dispute.encodedAttestation, fishermanDeposit) - }) - - it('reject create duplicated dispute', async function () { - const tx = disputeManager - .connect(fisherman) - .createQueryDispute(dispute.encodedAttestation, fishermanDeposit) - await expect(tx).revertedWith('Dispute already created') - }) - }) - - describe('accept a dispute', function () { - it('reject to accept a dispute if not the arbitrator', async function () { - const tx = disputeManager.connect(me).acceptDispute(dispute.id) - await expect(tx).revertedWith('Caller is not the Arbitrator') - }) - - it('reject to accept a dispute if not slasher', async function () { - // Dispute manager is not allowed to slash - await staking.connect(governor).setSlasher(disputeManager.address, false) - - // Perform transaction (accept) - const tx = disputeManager.connect(arbitrator).acceptDispute(dispute.id) - await expect(tx).revertedWith('!slasher') - }) - - it('reject to accept a dispute if zero tokens to slash', async function () { - await disputeManager.connect(governor).setSlashingPercentage(toBN('0'), toBN('0')) - const tx = disputeManager.connect(arbitrator).acceptDispute(dispute.id) - await expect(tx).revertedWith('Dispute has zero tokens to slash') - }) - - it('should resolve dispute, slash indexer and reward the fisherman', async function () { - // Before state - const beforeIndexerStake = await staking.getIndexerStakedTokens(indexer.address) - const beforeFishermanBalance = await grt.balanceOf(fisherman.address) - const beforeTotalSupply = await grt.totalSupply() - - // Calculations - const { slashAmount, rewardsAmount } = await calculateSlashConditions(indexer.address) - - // Perform transaction (accept) - const tx = disputeManager.connect(arbitrator).acceptDispute(dispute.id) - await expect(tx) - .emit(disputeManager, 'DisputeAccepted') - .withArgs(dispute.id, dispute.indexerAddress, fisherman.address, fishermanDeposit.add(rewardsAmount)) - - // After state - const afterFishermanBalance = await grt.balanceOf(fisherman.address) - const afterIndexerStake = await staking.getIndexerStakedTokens(indexer.address) - const afterTotalSupply = await grt.totalSupply() - - // Fisherman reward properly assigned + deposit returned - expect(afterFishermanBalance).eq(beforeFishermanBalance.add(fishermanDeposit).add(rewardsAmount)) - // Indexer slashed - expect(afterIndexerStake).eq(beforeIndexerStake.sub(slashAmount)) - // Slashed funds burned - const tokensToBurn = slashAmount.sub(rewardsAmount) - expect(afterTotalSupply).eq(beforeTotalSupply.sub(tokensToBurn)) - }) - }) - - describe('reject a dispute', function () { - it('reject to reject a dispute if not the arbitrator', async function () { - const tx = disputeManager.connect(me).rejectDispute(dispute.id) - await expect(tx).revertedWith('Caller is not the Arbitrator') - }) - - it('should reject a dispute and burn deposit', async function () { - // Before state - const beforeFishermanBalance = await grt.balanceOf(fisherman.address) - const beforeTotalSupply = await grt.totalSupply() - - // Perform transaction (reject) - const tx = disputeManager.connect(arbitrator).rejectDispute(dispute.id) - await expect(tx) - .emit(disputeManager, 'DisputeRejected') - .withArgs(dispute.id, dispute.indexerAddress, fisherman.address, fishermanDeposit) - - // After state - const afterFishermanBalance = await grt.balanceOf(fisherman.address) - const afterTotalSupply = await grt.totalSupply() - - // No change in fisherman balance - expect(afterFishermanBalance).eq(beforeFishermanBalance) - // Burn fisherman deposit - const burnedTokens = fishermanDeposit - expect(afterTotalSupply).eq(beforeTotalSupply.sub(burnedTokens)) - }) - }) - - describe('draw a dispute', function () { - it('reject to draw a dispute if not the arbitrator', async function () { - const tx = disputeManager.connect(me).drawDispute(dispute.id) - await expect(tx).revertedWith('Caller is not the Arbitrator') - }) - - it('should draw a dispute and return deposit', async function () { - // Before state - const beforeFishermanBalance = await grt.balanceOf(fisherman.address) - - // Perform transaction (draw) - const tx = disputeManager.connect(arbitrator).drawDispute(dispute.id) - await expect(tx) - .emit(disputeManager, 'DisputeDrawn') - .withArgs(dispute.id, dispute.indexerAddress, fisherman.address, fishermanDeposit) - - // Fisherman should see the deposit returned - const afterFishermanBalance = await grt.balanceOf(fisherman.address) - expect(afterFishermanBalance).eq(beforeFishermanBalance.add(fishermanDeposit)) - }) - }) - }) - }) - }) - - describe('disputes for conflicting attestations', function () { - async function getIndependentAttestations() { - const attestation1 = await buildAttestation(receipt, indexer1ChannelKey.privKey) - const attestation2 = await buildAttestation(receipt, indexer2ChannelKey.privKey) - return [attestation1, attestation2] - } - - async function getConflictingAttestations() { - const receipt1 = receipt - const receipt2 = { ...receipt1, responseCID: randomHexBytes() } - - const attestation1 = await buildAttestation(receipt1, indexer1ChannelKey.privKey) - const attestation2 = await buildAttestation(receipt2, indexer2ChannelKey.privKey) - return [attestation1, attestation2] - } - - beforeEach(async function () { - await setupIndexers() - }) - - it('reject if attestations are not in conflict', async function () { - const [attestation1, attestation2] = await getIndependentAttestations() - const tx = disputeManager - .connect(fisherman) - .createQueryDisputeConflict(encodeAttestation(attestation1), encodeAttestation(attestation2)) - await expect(tx).revertedWith('Attestations must be in conflict') - }) - - it('should create dispute', async function () { - const [attestation1, attestation2] = await getConflictingAttestations() - const dID1 = createQueryDisputeID(attestation1, indexer.address, fisherman.address) - const dID2 = createQueryDisputeID(attestation2, indexer2.address, fisherman.address) - const tx = disputeManager - .connect(fisherman) - .createQueryDisputeConflict(encodeAttestation(attestation1), encodeAttestation(attestation2)) - await expect(tx).emit(disputeManager, 'DisputeLinked').withArgs(dID1, dID2) - - // Test state - const dispute1 = await disputeManager.disputes(dID1) - const dispute2 = await disputeManager.disputes(dID2) - expect(dispute1.relatedDisputeID).eq(dID2) - expect(dispute2.relatedDisputeID).eq(dID1) - }) - - async function setupConflictingDisputes() { - const [attestation1, attestation2] = await getConflictingAttestations() - const dID1 = createQueryDisputeID(attestation1, indexer.address, fisherman.address) - const dID2 = createQueryDisputeID(attestation2, indexer2.address, fisherman.address) - const tx = disputeManager - .connect(fisherman) - .createQueryDisputeConflict(encodeAttestation(attestation1), encodeAttestation(attestation2)) - await tx - return [dID1, dID2] - } - - it('should accept one dispute and resolve the related dispute', async function () { - // Setup - const [dID1, dID2] = await setupConflictingDisputes() - // Do - await disputeManager.connect(arbitrator).acceptDispute(dID1) - // Check - const mainDispute = await disputeManager.disputes(dID1) - expect(mainDispute.status).to.eq(1) // 1 = DisputeStatus.Accepted - const relatedDispute = await disputeManager.disputes(dID2) - expect(relatedDispute.status).to.eq(2) // 2 = DisputeStatus.Rejected - }) - - it('should not allow to reject, user need to accept the related dispute ID to reject it', async function () { - // Setup - const [dID1] = await setupConflictingDisputes() - // Do - const tx = disputeManager.connect(arbitrator).rejectDispute(dID1) - await expect(tx).revertedWith('Dispute for conflicting attestation, must accept the related ID to reject') - }) - - it('should draw one dispute and resolve the related dispute', async function () { - // Setup - const [dID1, dID2] = await setupConflictingDisputes() - // Do - await disputeManager.connect(arbitrator).drawDispute(dID1) - // Check - const relatedDispute = await disputeManager.disputes(dID2) - expect(relatedDispute.status).not.eq(4) // 4 = DisputeStatus.Pending - }) - }) -}) diff --git a/packages/contracts-test/tests/unit/l2/l2Staking.test.ts b/packages/contracts-test/tests/unit/l2/l2Staking.test.ts deleted file mode 100644 index cf22eaba0..000000000 --- a/packages/contracts-test/tests/unit/l2/l2Staking.test.ts +++ /dev/null @@ -1,375 +0,0 @@ -import { IL2Staking, IRewardsManager } from '@graphprotocol/contracts' -import { L2GraphTokenGateway } from '@graphprotocol/contracts' -import { GraphToken } from '@graphprotocol/contracts' -import { EpochManager, L1GNS, L1GraphTokenGateway, L1Staking } from '@graphprotocol/contracts' -import { deriveChannelKey, GraphNetworkContracts, helpers, randomHexBytes, toBN, toGRT } from '@graphprotocol/sdk' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { expect } from 'chai' -import { BigNumber, ContractTransaction, ethers } from 'ethers' -import { defaultAbiCoder, parseEther } from 'ethers/lib/utils' -import hre from 'hardhat' - -import { NetworkFixture } from '../lib/fixtures' - -const { AddressZero } = ethers.constants - -const subgraphDeploymentID = randomHexBytes() -const channelKey = deriveChannelKey() -const allocationID = channelKey.address -const metadata = randomHexBytes(32) - -describe('L2Staking', () => { - const graph = hre.graph() - let me: SignerWithAddress - let other: SignerWithAddress - let governor: SignerWithAddress - - let fixture: NetworkFixture - - let fixtureContracts: GraphNetworkContracts - let l1MockContracts: GraphNetworkContracts - let l1GRTMock: GraphToken - let l1StakingMock: L1Staking - let l1GNSMock: L1GNS - let l1GRTGatewayMock: L1GraphTokenGateway - let l2GraphTokenGateway: L2GraphTokenGateway - let staking: IL2Staking - let grt: GraphToken - let rewardsManager: IRewardsManager - - const tokens10k = toGRT('10000') - const tokens100k = toGRT('100000') - const tokens1m = toGRT('1000000') - - // Allocate with test values - const allocate = async (tokens: BigNumber) => { - return staking - .connect(me) - .allocateFrom( - me.address, - subgraphDeploymentID, - tokens, - allocationID, - metadata, - await channelKey.generateProof(me.address), - ) - } - - const gatewayFinalizeTransfer = async function ( - from: string, - to: string, - amount: BigNumber, - callhookData: string, - ): Promise { - const mockL1GatewayL2Alias = await helpers.getL2SignerFromL1(l1GRTGatewayMock.address) - // Eth for gas: - await helpers.setBalance(await mockL1GatewayL2Alias.getAddress(), parseEther('1')) - - const tx = l2GraphTokenGateway - .connect(mockL1GatewayL2Alias) - .finalizeInboundTransfer(l1GRTMock.address, from, to, amount, callhookData) - return tx - } - - before(async function () { - ;[me, other] = await graph.getTestAccounts() - ;({ governor } = await graph.getNamedAccounts()) - - fixture = new NetworkFixture(graph.provider) - - // Deploy L2 - fixtureContracts = await fixture.load(governor, true) - grt = fixtureContracts.GraphToken as GraphToken - staking = fixtureContracts.Staking as IL2Staking - l2GraphTokenGateway = fixtureContracts.L2GraphTokenGateway as L2GraphTokenGateway - - // Deploy L1 mock - l1MockContracts = await fixture.loadMock(false) - l1GRTMock = l1MockContracts.GraphToken as GraphToken - l1StakingMock = l1MockContracts.L1Staking as L1Staking - l1GNSMock = l1MockContracts.L1GNS as L1GNS - l1GRTGatewayMock = l1MockContracts.L1GraphTokenGateway as L1GraphTokenGateway - rewardsManager = fixtureContracts.RewardsManager as IRewardsManager - - // Deploy L2 arbitrum bridge - await fixture.loadL2ArbitrumBridge(governor) - - // Configure L2 bridge - await fixture.configureL2Bridge(governor, fixtureContracts, l1MockContracts) - - await grt.connect(governor).mint(me.address, tokens1m) - await grt.connect(me).approve(staking.address, tokens1m) - await grt.connect(governor).mint(other.address, tokens1m) - await grt.connect(other).approve(staking.address, tokens1m) - - // HACK: we set the staking contract as the subgraph service to make tests pass. - // This is due to the test suite being outdated. - await rewardsManager.connect(governor).setSubgraphService(staking.address) - }) - - beforeEach(async function () { - await fixture.setUp() - }) - - afterEach(async function () { - await fixture.tearDown() - }) - - describe('receive()', function () { - it('should not allow receiving ETH', async function () { - const tx = me.sendTransaction({ - to: staking.address, - value: parseEther('1'), - }) - await expect(tx).revertedWith('RECEIVE_ETH_NOT_ALLOWED') - }) - }) - describe('receiving indexer stake from L1 (onTokenTransfer)', function () { - it('cannot be called by someone other than the L2GraphTokenGateway', async function () { - const functionData = defaultAbiCoder.encode(['tuple(address)'], [[me.address]]) - - const callhookData = defaultAbiCoder.encode( - ['uint8', 'bytes'], - [toBN(0), functionData], // code = 1 means RECEIVE_INDEXER_CODE - ) - const tx = staking.connect(me).onTokenTransfer(l1GNSMock.address, tokens100k, callhookData) - await expect(tx).revertedWith('ONLY_GATEWAY') - }) - it('rejects calls if the L1 sender is not the L1Staking', async function () { - const functionData = defaultAbiCoder.encode(['tuple(address)'], [[me.address]]) - - const callhookData = defaultAbiCoder.encode( - ['uint8', 'bytes'], - [toBN(0), functionData], // code = 1 means RECEIVE_INDEXER_CODE - ) - const tx = gatewayFinalizeTransfer(me.address, staking.address, tokens100k, callhookData) - - await expect(tx).revertedWith('ONLY_L1_STAKING_THROUGH_BRIDGE') - }) - it('adds stake to a new indexer', async function () { - const functionData = defaultAbiCoder.encode(['tuple(address)'], [[me.address]]) - - const callhookData = defaultAbiCoder.encode( - ['uint8', 'bytes'], - [toBN(0), functionData], // code = 1 means RECEIVE_INDEXER_CODE - ) - const tx = gatewayFinalizeTransfer(l1StakingMock.address, staking.address, tokens100k, callhookData) - - await expect(tx) - .emit(l2GraphTokenGateway, 'DepositFinalized') - .withArgs(l1GRTMock.address, l1StakingMock.address, staking.address, tokens100k) - await expect(tx).emit(staking, 'StakeDeposited').withArgs(me.address, tokens100k) - expect(await staking.getIndexerStakedTokens(me.address)).to.equal(tokens100k) - const delegationPool = await staking.delegationPools(me.address) - expect(delegationPool.indexingRewardCut).eq(toBN(1000000)) // 1 in PPM - expect(delegationPool.queryFeeCut).eq(toBN(1000000)) // 1 in PPM - }) - it('adds stake to an existing indexer that was already transferred', async function () { - const functionData = defaultAbiCoder.encode(['tuple(address)'], [[me.address]]) - - const callhookData = defaultAbiCoder.encode( - ['uint8', 'bytes'], - [toBN(0), functionData], // code = 1 means RECEIVE_INDEXER_CODE - ) - await gatewayFinalizeTransfer(l1StakingMock.address, staking.address, tokens100k, callhookData) - const tx = gatewayFinalizeTransfer(l1StakingMock.address, staking.address, tokens100k, callhookData) - - await expect(tx) - .emit(l2GraphTokenGateway, 'DepositFinalized') - .withArgs(l1GRTMock.address, l1StakingMock.address, staking.address, tokens100k) - await expect(tx).emit(staking, 'StakeDeposited').withArgs(me.address, tokens100k) - expect(await staking.getIndexerStakedTokens(me.address)).to.equal(tokens100k.add(tokens100k)) - }) - it('adds stake to an existing indexer that was staked in L2 (without changing delegation params)', async function () { - const functionData = defaultAbiCoder.encode(['tuple(address)'], [[me.address]]) - - const callhookData = defaultAbiCoder.encode( - ['uint8', 'bytes'], - [toBN(0), functionData], // code = 1 means RECEIVE_INDEXER_CODE - ) - await staking.connect(me).stake(tokens100k) - await staking.connect(me).setDelegationParameters(1000, 1000, 1000) - const tx = gatewayFinalizeTransfer(l1StakingMock.address, staking.address, tokens100k, callhookData) - - await expect(tx) - .emit(l2GraphTokenGateway, 'DepositFinalized') - .withArgs(l1GRTMock.address, l1StakingMock.address, staking.address, tokens100k) - await expect(tx).emit(staking, 'StakeDeposited').withArgs(me.address, tokens100k) - expect(await staking.getIndexerStakedTokens(me.address)).to.equal(tokens100k.add(tokens100k)) - const delegationPool = await staking.delegationPools(me.address) - expect(delegationPool.indexingRewardCut).eq(toBN(1000)) - expect(delegationPool.queryFeeCut).eq(toBN(1000)) - }) - }) - - describe('receiving delegation from L1 (onTokenTransfer)', function () { - it('adds delegation for a new delegator', async function () { - await staking.connect(me).stake(tokens100k) - - const functionData = defaultAbiCoder.encode(['tuple(address,address)'], [[me.address, other.address]]) - - const callhookData = defaultAbiCoder.encode( - ['uint8', 'bytes'], - [toBN(1), functionData], // code = 1 means RECEIVE_DELEGATION_CODE - ) - const tx = gatewayFinalizeTransfer(l1StakingMock.address, staking.address, tokens10k, callhookData) - - await expect(tx) - .emit(l2GraphTokenGateway, 'DepositFinalized') - .withArgs(l1GRTMock.address, l1StakingMock.address, staking.address, tokens10k) - const expectedShares = tokens10k - await expect(tx).emit(staking, 'StakeDelegated').withArgs(me.address, other.address, tokens10k, expectedShares) - const delegation = await staking.getDelegation(me.address, other.address) - expect(delegation.shares).to.equal(expectedShares) - }) - it('adds delegation for an existing delegator', async function () { - await staking.connect(me).stake(tokens100k) - await staking.connect(other).delegate(me.address, tokens10k) - - const functionData = defaultAbiCoder.encode(['tuple(address,address)'], [[me.address, other.address]]) - - const callhookData = defaultAbiCoder.encode( - ['uint8', 'bytes'], - [toBN(1), functionData], // code = 1 means RECEIVE_DELEGATION_CODE - ) - const tx = gatewayFinalizeTransfer(l1StakingMock.address, staking.address, tokens10k, callhookData) - - await expect(tx) - .emit(l2GraphTokenGateway, 'DepositFinalized') - .withArgs(l1GRTMock.address, l1StakingMock.address, staking.address, tokens10k) - const expectedNewShares = tokens10k - const expectedTotalShares = tokens10k.mul(2) - await expect(tx).emit(staking, 'StakeDelegated').withArgs(me.address, other.address, tokens10k, expectedNewShares) - const delegation = await staking.getDelegation(me.address, other.address) - expect(delegation.shares).to.equal(expectedTotalShares) - }) - it('returns delegation to the delegator if it would produce no shares', async function () { - await fixtureContracts.RewardsManager.connect(governor).setIssuancePerBlock(toGRT('114')) - - await staking.connect(me).stake(tokens100k) - // Initialize the delegation pool to allow delegating less than 1 GRT - await staking.connect(me).delegate(me.address, tokens10k) - - await staking.connect(me).setDelegationParameters(1000, 1000, 1000) - await grt.connect(me).approve(fixtureContracts.Curation.address, tokens10k) - await fixtureContracts.Curation.connect(me).mint(subgraphDeploymentID, tokens10k, 0) - - await allocate(tokens100k) - await helpers.mineEpoch(fixtureContracts.EpochManager as EpochManager) - await helpers.mineEpoch(fixtureContracts.EpochManager as EpochManager) - await staking.connect(me).closeAllocation(allocationID, randomHexBytes(32)) - // Now there are some rewards sent to delegation pool, so 1 weiGRT is less than 1 share - - const functionData = defaultAbiCoder.encode(['tuple(address,address)'], [[me.address, other.address]]) - - const callhookData = defaultAbiCoder.encode( - ['uint8', 'bytes'], - [toBN(1), functionData], // code = 1 means RECEIVE_DELEGATION_CODE - ) - const delegatorGRTBalanceBefore = await grt.balanceOf(other.address) - const tx = gatewayFinalizeTransfer( - l1StakingMock.address, - staking.address, - toBN(1), // Less than 1 share! - callhookData, - ) - - await expect(tx) - .emit(l2GraphTokenGateway, 'DepositFinalized') - .withArgs(l1GRTMock.address, l1StakingMock.address, staking.address, toBN(1)) - const delegation = await staking.getDelegation(me.address, other.address) - await expect(tx) - .emit(staking, 'TransferredDelegationReturnedToDelegator') - .withArgs(me.address, other.address, toBN(1)) - - expect(delegation.shares).to.equal(0) - const delegatorGRTBalanceAfter = await grt.balanceOf(other.address) - expect(delegatorGRTBalanceAfter.sub(delegatorGRTBalanceBefore)).to.equal(toBN(1)) - }) - it('returns delegation to the delegator if it initializes the pool with less than the minimum delegation', async function () { - await fixtureContracts.RewardsManager.connect(governor).setIssuancePerBlock(toGRT('114')) - - await staking.connect(me).stake(tokens100k) - - await staking.connect(me).setDelegationParameters(1000, 1000, 1000) - await grt.connect(me).approve(fixtureContracts.Curation.address, tokens10k) - await fixtureContracts.Curation.connect(me).mint(subgraphDeploymentID, tokens10k, 0) - - await allocate(tokens100k) - await helpers.mineEpoch(fixtureContracts.EpochManager as EpochManager, 2) - await staking.connect(me).closeAllocation(allocationID, randomHexBytes(32)) - // Now there are some rewards sent to delegation pool, so 1 weiGRT is less than 1 share - - const functionData = defaultAbiCoder.encode(['tuple(address,address)'], [[me.address, other.address]]) - - const callhookData = defaultAbiCoder.encode( - ['uint8', 'bytes'], - [toBN(1), functionData], // code = 1 means RECEIVE_DELEGATION_CODE - ) - const delegatorGRTBalanceBefore = await grt.balanceOf(other.address) - const tx = gatewayFinalizeTransfer( - l1StakingMock.address, - staking.address, - toGRT('0.1'), // Less than 1 GRT! - callhookData, - ) - - await expect(tx) - .emit(l2GraphTokenGateway, 'DepositFinalized') - .withArgs(l1GRTMock.address, l1StakingMock.address, staking.address, toGRT('0.1')) - const delegation = await staking.getDelegation(me.address, other.address) - await expect(tx) - .emit(staking, 'TransferredDelegationReturnedToDelegator') - .withArgs(me.address, other.address, toGRT('0.1')) - - expect(delegation.shares).to.equal(0) - const delegatorGRTBalanceAfter = await grt.balanceOf(other.address) - expect(delegatorGRTBalanceAfter.sub(delegatorGRTBalanceBefore)).to.equal(toGRT('0.1')) - }) - it('returns delegation under the minimum if the pool is initialized', async function () { - await staking.connect(me).stake(tokens100k) - - // Initialize the delegation pool to allow delegating less than 1 GRT - await staking.connect(me).delegate(me.address, tokens10k) - - const functionData = defaultAbiCoder.encode(['tuple(address,address)'], [[me.address, other.address]]) - - const callhookData = defaultAbiCoder.encode( - ['uint8', 'bytes'], - [toBN(1), functionData], // code = 1 means RECEIVE_DELEGATION_CODE - ) - const delegatorGRTBalanceBefore = await grt.balanceOf(other.address) - const tx = gatewayFinalizeTransfer(l1StakingMock.address, staking.address, toGRT('0.1'), callhookData) - - await expect(tx) - .emit(l2GraphTokenGateway, 'DepositFinalized') - .withArgs(l1GRTMock.address, l1StakingMock.address, staking.address, toGRT('0.1')) - - const delegation = await staking.getDelegation(me.address, other.address) - await expect(tx) - .emit(staking, 'TransferredDelegationReturnedToDelegator') - .withArgs(me.address, other.address, toGRT('0.1')) - - expect(delegation.shares).to.equal(0) - const delegatorGRTBalanceAfter = await grt.balanceOf(other.address) - expect(delegatorGRTBalanceAfter.sub(delegatorGRTBalanceBefore)).to.equal(toGRT('0.1')) - }) - }) - describe('onTokenTransfer with invalid messages', function () { - it('reverts if the code is invalid', async function () { - // This should never really happen unless the Arbitrum bridge is compromised, - // so we test it anyway to ensure it's a well-defined behavior. - // code 2 does not exist: - const callhookData = defaultAbiCoder.encode(['uint8', 'bytes'], [toBN(2), '0x12345678']) - const tx = gatewayFinalizeTransfer(l1StakingMock.address, staking.address, toGRT('1'), callhookData) - await expect(tx).revertedWith('INVALID_CODE') - }) - it('reverts if the message encoding is invalid', async function () { - // This should never really happen unless the Arbitrum bridge is compromised, - // so we test it anyway to ensure it's a well-defined behavior. - const callhookData = defaultAbiCoder.encode(['address', 'uint128'], [AddressZero, toBN(2)]) - const tx = gatewayFinalizeTransfer(l1StakingMock.address, staking.address, toGRT('1'), callhookData) - await expect(tx).reverted // abi.decode will fail with no reason - }) - }) -}) diff --git a/packages/contracts-test/tests/unit/lib/fixtures.ts b/packages/contracts-test/tests/unit/lib/fixtures.ts deleted file mode 100644 index 6e3b6b501..000000000 --- a/packages/contracts-test/tests/unit/lib/fixtures.ts +++ /dev/null @@ -1,173 +0,0 @@ -import { Controller } from '@graphprotocol/contracts' -import { DisputeManager } from '@graphprotocol/contracts' -import { EpochManager } from '@graphprotocol/contracts' -import { GraphToken } from '@graphprotocol/contracts' -import { Curation } from '@graphprotocol/contracts' -import { L2Curation } from '@graphprotocol/contracts' -import { L1GNS } from '@graphprotocol/contracts' -import { L2GNS } from '@graphprotocol/contracts' -import { IL1Staking } from '@graphprotocol/contracts' -import { IL2Staking } from '@graphprotocol/contracts' -import { RewardsManager } from '@graphprotocol/contracts' -import { ServiceRegistry } from '@graphprotocol/contracts' -import { GraphProxyAdmin } from '@graphprotocol/contracts' -import { L1GraphTokenGateway } from '@graphprotocol/contracts' -import { BridgeEscrow } from '@graphprotocol/contracts' -import { L2GraphTokenGateway } from '@graphprotocol/contracts' -import { L2GraphToken } from '@graphprotocol/contracts' -import { LibExponential } from '@graphprotocol/contracts' -import { - configureL1Bridge, - configureL2Bridge, - deployGraphNetwork, - deployMockGraphNetwork, - GraphNetworkContracts, - helpers, -} from '@graphprotocol/sdk' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { providers, Wallet } from 'ethers' - -import { isRunningUnderCoverage } from '../../../utils/coverage' - -export interface L1FixtureContracts { - controller: Controller - disputeManager: DisputeManager - epochManager: EpochManager - grt: GraphToken - curation: Curation - gns: L1GNS - staking: IL1Staking - libExponential: LibExponential - rewardsManager: RewardsManager - serviceRegistry: ServiceRegistry - proxyAdmin: GraphProxyAdmin - l1GraphTokenGateway: L1GraphTokenGateway - bridgeEscrow: BridgeEscrow -} - -export interface L2FixtureContracts { - controller: Controller - disputeManager: DisputeManager - epochManager: EpochManager - grt: L2GraphToken - curation: L2Curation - gns: L2GNS - staking: IL2Staking - libExponential: LibExponential - rewardsManager: RewardsManager - serviceRegistry: ServiceRegistry - proxyAdmin: GraphProxyAdmin - l2GraphTokenGateway: L2GraphTokenGateway -} - -export interface L2BridgeMocks { - l2GRTMock: Wallet - l2GRTGatewayMock: Wallet - l2GNSMock: Wallet - l2StakingMock: Wallet -} - -export class NetworkFixture { - lastSnapshot: any - constructor(public provider: providers.Provider) {} - - async load(deployer: SignerWithAddress, l2Deploy?: boolean): Promise { - // Use instrumented artifacts when running coverage tests, otherwise use local artifacts - const artifactsDir = isRunningUnderCoverage() ? './artifacts' : '../contracts/artifacts' - - const contracts = await deployGraphNetwork( - 'addresses-local.json', - l2Deploy ? 'graph.arbitrum-hardhat.yml' : 'graph.hardhat.yml', - 1337, - deployer, - this.provider, - { - governor: deployer, - skipConfirmation: true, - forceDeploy: true, - l2Deploy: l2Deploy, - enableTxLogging: false, - artifactsDir: artifactsDir, - } as any, // Type assertion to bypass TypeScript issue - ) - if (!contracts) { - throw new Error('Failed to deploy contracts') - } - - // WORKAROUND: The SDK version 0.6.0 uses old contract artifacts that were bundled - // before recent changes to L2GNS. - // We manually upgrade the proxy to use the locally compiled L2GNS implementation - // with the latest changes, and re-attach with the correct ABI. - if (l2Deploy && contracts.L2GNS) { - const hre = await import('hardhat') - const L2GNSFactory = await hre.default.ethers.getContractFactory('contracts/l2/discovery/L2GNS.sol:L2GNS') - - // Deploy new L2GNS implementation - const l2gnsImpl = await L2GNSFactory.deploy() - await l2gnsImpl.deployed() - - // Upgrade L2GNS proxy to new implementation - const proxyAdmin = contracts.GraphProxyAdmin as GraphProxyAdmin - const proxy = contracts.L2GNS - await proxyAdmin.connect(deployer).upgrade(proxy.address, l2gnsImpl.address) - await proxyAdmin.connect(deployer).acceptProxy(l2gnsImpl.address, proxy.address) - - // Re-attach with correct ABI - contracts.L2GNS = L2GNSFactory.attach(proxy.address) as any - } - - return contracts - } - - async loadMock(l2Deploy: boolean): Promise { - return await deployMockGraphNetwork(l2Deploy) - } - - async loadL1ArbitrumBridge(deployer: SignerWithAddress): Promise { - return await helpers.deployL1MockBridge(deployer, 'arbitrum-addresses-local.json', this.provider) - } - - async loadL2ArbitrumBridge(deployer: SignerWithAddress): Promise { - return await helpers.deployL2MockBridge(deployer, 'arbitrum-addresses-local.json', this.provider) - } - - async configureL1Bridge( - deployer: SignerWithAddress, - l1FixtureContracts: GraphNetworkContracts, - l2MockContracts: GraphNetworkContracts, - ): Promise { - await configureL1Bridge(l1FixtureContracts, deployer, { - l2GRTAddress: l2MockContracts.L2GraphToken.address, - l2GRTGatewayAddress: l2MockContracts.L2GraphTokenGateway.address, - l2GNSAddress: l2MockContracts.L2GNS.address, - l2StakingAddress: l2MockContracts.L2Staking.address, - arbAddressBookPath: './arbitrum-addresses-local.json', - chainId: 1337, - }) - await l1FixtureContracts.L1GraphTokenGateway.connect(deployer).setPaused(false) - } - - async configureL2Bridge( - deployer: SignerWithAddress, - l2FixtureContracts: GraphNetworkContracts, - l1MockContracts: GraphNetworkContracts, - ): Promise { - await configureL2Bridge(l2FixtureContracts, deployer, { - l1GRTAddress: l1MockContracts.GraphToken.address, - l1GRTGatewayAddress: l1MockContracts.L1GraphTokenGateway.address, - l1GNSAddress: l1MockContracts.L1GNS.address, - l1StakingAddress: l1MockContracts.L1Staking.address, - arbAddressBookPath: './arbitrum-addresses-local.json', - chainId: 412346, - }) - await l2FixtureContracts.L2GraphTokenGateway.connect(deployer).setPaused(false) - } - - async setUp(): Promise { - this.lastSnapshot = await helpers.takeSnapshot() - } - - async tearDown(): Promise { - await helpers.restoreSnapshot(this.lastSnapshot) - } -} diff --git a/packages/contracts-test/tests/unit/payments/allocationExchange.test.ts b/packages/contracts-test/tests/unit/payments/allocationExchange.test.ts deleted file mode 100644 index 1f40b448d..000000000 --- a/packages/contracts-test/tests/unit/payments/allocationExchange.test.ts +++ /dev/null @@ -1,269 +0,0 @@ -import { AllocationExchange } from '@graphprotocol/contracts' -import { GraphToken } from '@graphprotocol/contracts' -import { IStaking } from '@graphprotocol/contracts' -import { EpochManager } from '@graphprotocol/contracts' -import { - deriveChannelKey, - GraphNetworkContracts, - helpers, - randomAddress, - randomHexBytes, - toGRT, -} from '@graphprotocol/sdk' -import type { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { expect } from 'chai' -import { BigNumber, constants, Wallet } from 'ethers' -import { arrayify, joinSignature, SigningKey, solidityKeccak256 } from 'ethers/lib/utils' -import hre from 'hardhat' - -import { NetworkFixture } from '../lib/fixtures' - -const { AddressZero, MaxUint256 } = constants - -interface Voucher { - allocationID: string - amount: BigNumber - signature: string -} - -describe('AllocationExchange', () => { - let governor: SignerWithAddress - let allocationExchangeOwner: SignerWithAddress - let indexer: SignerWithAddress - let authority: Wallet - - let fixture: NetworkFixture - - let contracts: GraphNetworkContracts - let grt: GraphToken - let staking: IStaking - let allocationExchange: AllocationExchange - let epochManager: EpochManager - - const graph = hre.graph() - - function createVoucher(allocationID: string, amount: BigNumber, signerKey: string): Voucher { - const messageHash = solidityKeccak256(['address', 'uint256'], [allocationID, amount]) - const messageHashBytes = arrayify(messageHash) - const key = new SigningKey(signerKey) - const signature = key.signDigest(messageHashBytes) - return { - allocationID, - amount, - signature: joinSignature(signature), - } - } - - before(async function () { - ;[indexer] = await graph.getTestAccounts() - ;({ governor, allocationExchangeOwner } = await graph.getNamedAccounts()) - authority = Wallet.createRandom() - - fixture = new NetworkFixture(graph.provider) - contracts = await fixture.load(governor) - allocationExchange = contracts.AllocationExchange as AllocationExchange - grt = contracts.GraphToken as GraphToken - staking = contracts.Staking as IStaking - epochManager = contracts.EpochManager as EpochManager - - // Give some funds to the indexer and approve staking contract to use funds on indexer behalf - const indexerTokens = toGRT('100000') - await grt.connect(governor).mint(indexer.address, indexerTokens) - await grt.connect(indexer).approve(staking.address, indexerTokens) - - // Give some funds to the AllocationExchange - const exchangeTokens = toGRT('2000') - await grt.connect(governor).mint(allocationExchange.address, exchangeTokens) - - // Ensure the exchange is correctly setup - await allocationExchange.connect(allocationExchangeOwner).setAuthority(authority.address, true) - await allocationExchange.connect(allocationExchangeOwner).approveAll() - }) - - beforeEach(async function () { - await fixture.setUp() - }) - - afterEach(async function () { - await fixture.tearDown() - }) - - async function setupAllocation(): Promise { - // Generate test data - const channelKey = deriveChannelKey() - const allocationID = channelKey.address - const subgraphDeploymentID = randomHexBytes(32) - const metadata = randomHexBytes(32) - - // Setup staking - const stakeTokens = toGRT('100000') - await staking.connect(indexer).stake(stakeTokens) - await staking - .connect(indexer) - .allocateFrom( - indexer.address, - subgraphDeploymentID, - stakeTokens, - allocationID, - metadata, - await channelKey.generateProof(indexer.address), - ) - return allocationID - } - - describe('config', function () { - it('should set an authority', async function () { - // Set authority - const newAuthority = randomAddress() - const tx1 = allocationExchange.connect(allocationExchangeOwner).setAuthority(newAuthority, true) - await expect(tx1).emit(allocationExchange, 'AuthoritySet').withArgs(newAuthority, true) - expect(await allocationExchange.authority(newAuthority)).eq(true) - - // Unset authority - const tx2 = allocationExchange.connect(allocationExchangeOwner).setAuthority(newAuthority, false) - await expect(tx2).emit(allocationExchange, 'AuthoritySet').withArgs(newAuthority, false) - expect(await allocationExchange.authority(newAuthority)).eq(false) - }) - - it('reject set an authority if not allowed', async function () { - const newAuthority = randomAddress() - const tx = allocationExchange.connect(indexer).setAuthority(newAuthority, true) - await expect(tx).revertedWith('Only Governor can call') - }) - - it('reject set an empty authority', async function () { - const newAuthority = AddressZero - const tx = allocationExchange.connect(allocationExchangeOwner).setAuthority(newAuthority, true) - await expect(tx).revertedWith('Exchange: empty authority') - }) - - it('should allow to approve all tokens to staking contract', async function () { - await allocationExchange.connect(allocationExchangeOwner).approveAll() - const allowance = await grt.allowance(allocationExchange.address, staking.address) - expect(allowance).eq(MaxUint256) - }) - }) - - describe('withdraw funds', function () { - it('should withdraw to destination', async function () { - const beforeExchangeBalance = await grt.balanceOf(allocationExchange.address) - - const destinationAddress = randomAddress() - const amount = toGRT('1000') - const tx = allocationExchange.connect(allocationExchangeOwner).withdraw(destinationAddress, amount) - await expect(tx).emit(allocationExchange, 'TokensWithdrawn').withArgs(destinationAddress, amount) - - const afterExchangeBalance = await grt.balanceOf(allocationExchange.address) - const afterDestinationBalance = await grt.balanceOf(destinationAddress) - - expect(afterExchangeBalance).eq(beforeExchangeBalance.sub(amount)) - expect(afterDestinationBalance).eq(amount) - }) - - it('reject withdraw zero amount', async function () { - const destinationAddress = randomAddress() - const amount = toGRT('0') - const tx = allocationExchange.connect(allocationExchangeOwner).withdraw(destinationAddress, amount) - await expect(tx).revertedWith('Exchange: empty amount') - }) - - it('reject withdraw to zero destination', async function () { - const destinationAddress = AddressZero - const amount = toGRT('1000') - const tx = allocationExchange.connect(allocationExchangeOwner).withdraw(destinationAddress, amount) - await expect(tx).revertedWith('Exchange: empty destination') - }) - - it('reject withdraw if not allowed', async function () { - const destinationAddress = randomAddress() - const amount = toGRT('1000') - const tx = allocationExchange.connect(indexer.address).withdraw(destinationAddress, amount) - await expect(tx).revertedWith('Only Governor can call') - }) - }) - - describe('redeem vouchers', function () { - it('redeem a voucher', async function () { - const beforeExchangeBalance = await grt.balanceOf(allocationExchange.address) - - // Setup an active allocation - const allocationID = await setupAllocation() - await helpers.mineEpoch(epochManager) - - // Initiate a withdrawal - const actualAmount = toGRT('2000') // <- withdraw amount - const voucher = createVoucher(allocationID, actualAmount, authority.privateKey) - const tx = allocationExchange.connect(allocationExchangeOwner).redeem(voucher) - await expect(tx).emit(allocationExchange, 'AllocationRedeemed').withArgs(allocationID, actualAmount) - - // Allocation must have collected the withdrawn tokens - const allocation = await staking.allocations(allocationID) - expect(allocation.collectedFees).eq(actualAmount) - - // AllocationExchange should have less funds - const afterExchangeBalance = await grt.balanceOf(allocationExchange.address) - expect(afterExchangeBalance).eq(beforeExchangeBalance.sub(actualAmount)) - }) - - it('reject double spending of a voucher', async function () { - // Setup an active allocation - const allocationID = await setupAllocation() - await helpers.mineEpoch(epochManager) - - // Initiate a withdrawal - const actualAmount = toGRT('2000') // <- withdraw amount - const voucher = createVoucher(allocationID, actualAmount, authority.privateKey) - - // First redeem - await allocationExchange.connect(allocationExchangeOwner).redeem(voucher) - - // Double spend the same voucher! - await expect(allocationExchange.connect(allocationExchangeOwner).redeem(voucher)).revertedWith( - 'Exchange: allocation already redeemed', - ) - }) - - it('reject redeem voucher for invalid allocation', async function () { - // Use an invalid allocation - const allocationID = '0xfefefefefefefefefefefefefefefefefefefefe' - - // Ensure the exchange is correctly setup - await allocationExchange.connect(allocationExchangeOwner).setAuthority(authority.address, true) - await allocationExchange.connect(allocationExchangeOwner).approveAll() - - // Initiate a withdrawal - const actualAmount = toGRT('2000') // <- withdraw amount - const voucher = createVoucher(allocationID, actualAmount, authority.privateKey) - const tx = allocationExchange.connect(allocationExchangeOwner).redeem(voucher) - await expect(tx).revertedWith('!collect') - }) - - it('reject redeem voucher not signed by the authority', async function () { - // Initiate a withdrawal - const actualAmount = toGRT('2000') // <- withdraw amount - const voucher = createVoucher( - randomAddress(), - actualAmount, - Wallet.createRandom().privateKey, // <-- signed by anon - ) - const tx = allocationExchange.connect(allocationExchangeOwner).redeem(voucher) - await expect(tx).revertedWith('Exchange: invalid signer') - }) - - it('reject redeem voucher with empty amount', async function () { - // Initiate a withdrawal - const actualAmount = toGRT('0') // <- withdraw amount - const voucher = createVoucher(randomAddress(), actualAmount, authority.privateKey) - const tx = allocationExchange.connect(allocationExchangeOwner).redeem(voucher) - await expect(tx).revertedWith('Exchange: zero tokens voucher') - }) - - it('reject redeem voucher with invalid signature', async function () { - const actualAmount = toGRT('2000') // <- withdraw amount - const voucher = createVoucher(randomAddress(), actualAmount, authority.privateKey) - voucher.signature = '0x1234' - const tx = allocationExchange.connect(allocationExchangeOwner).redeem(voucher) - await expect(tx).revertedWith('Exchange: invalid signature') - }) - }) -}) diff --git a/packages/contracts-test/tests/unit/rewards/subgraphAvailability.test.ts b/packages/contracts-test/tests/unit/rewards/subgraphAvailability.test.ts deleted file mode 100644 index a4afbffa5..000000000 --- a/packages/contracts-test/tests/unit/rewards/subgraphAvailability.test.ts +++ /dev/null @@ -1,414 +0,0 @@ -import { SubgraphAvailabilityManager } from '@graphprotocol/contracts' -import { IRewardsManager } from '@graphprotocol/contracts' -import { deploy, DeployType, GraphNetworkContracts, randomAddress, randomHexBytes } from '@graphprotocol/sdk' -import type { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { expect } from 'chai' -import { constants } from 'ethers' -import hre from 'hardhat' -import { ethers } from 'hardhat' - -import { NetworkFixture } from '../lib/fixtures' - -const { AddressZero } = constants - -describe('SubgraphAvailabilityManager', () => { - const graph = hre.graph() - let me: SignerWithAddress - let governor: SignerWithAddress - - let oracles: string[] - let oracleOne: SignerWithAddress - let oracleTwo: SignerWithAddress - let oracleThree: SignerWithAddress - let oracleFour: SignerWithAddress - let oracleFive: SignerWithAddress - - let newOracle: SignerWithAddress - - let fixture: NetworkFixture - - const executionThreshold = '3' - const voteTimeLimit = '5' // 5 seconds - - let contracts: GraphNetworkContracts - let rewardsManager: IRewardsManager - let subgraphAvailabilityManager: SubgraphAvailabilityManager - - const subgraphDeploymentID1 = randomHexBytes() - const subgraphDeploymentID2 = randomHexBytes() - const subgraphDeploymentID3 = randomHexBytes() - - before(async () => { - ;[me, oracleOne, oracleTwo, oracleThree, oracleFour, oracleFive, newOracle] = await graph.getTestAccounts() - ;({ governor } = await graph.getNamedAccounts()) - - oracles = [oracleOne.address, oracleTwo.address, oracleThree.address, oracleFour.address, oracleFive.address] - - fixture = new NetworkFixture(graph.provider) - contracts = await fixture.load(governor) - rewardsManager = contracts.RewardsManager as IRewardsManager - const deployResult = await deploy(DeployType.Deploy, governor, { - name: 'SubgraphAvailabilityManager', - args: [governor.address, rewardsManager.address, executionThreshold, voteTimeLimit, oracles], - }) - subgraphAvailabilityManager = deployResult.contract as SubgraphAvailabilityManager - - await rewardsManager.connect(governor).setSubgraphAvailabilityOracle(subgraphAvailabilityManager.address) - }) - - beforeEach(async function () { - await fixture.setUp() - }) - - afterEach(async function () { - await fixture.tearDown() - }) - - describe('deployment', () => { - it('should deploy', function () { - expect(subgraphAvailabilityManager.address).to.be.properAddress - }) - - it('should revert if oracles array is less than 5', async () => { - await expect( - deploy(DeployType.Deploy, governor, { - name: 'SubgraphAvailabilityManager', - args: [ - governor.address, - rewardsManager.address, - executionThreshold, - voteTimeLimit, - [oracleOne.address, oracleTwo.address, oracleThree.address, oracleFour.address], - ], - }), - ).to.be.reverted - }) - - it('should revert if an oracle is address zero', async () => { - await expect( - deploy(DeployType.Deploy, governor, { - name: 'SubgraphAvailabilityManager', - args: [ - governor.address, - rewardsManager.address, - executionThreshold, - voteTimeLimit, - [AddressZero, oracleTwo.address, oracleThree.address, oracleFour.address, oracleFive.address], - ], - }), - ).to.be.revertedWith('SAM: oracle cannot be address zero') - }) - - it('should revert if governor is address zero', async () => { - await expect( - deploy(DeployType.Deploy, governor, { - name: 'SubgraphAvailabilityManager', - args: [AddressZero, rewardsManager.address, executionThreshold, voteTimeLimit, oracles], - }), - ).to.be.revertedWith('SAM: governor must be set') - }) - - it('should revert if rewardsManager is address zero', async () => { - await expect( - deploy(DeployType.Deploy, governor, { - name: 'SubgraphAvailabilityManager', - args: [governor.address, AddressZero, executionThreshold, voteTimeLimit, oracles], - }), - ).to.be.revertedWith('SAM: rewardsManager must be set') - }) - - it('should revert if executionThreshold is too low', async () => { - await expect( - deploy(DeployType.Deploy, governor, { - name: 'SubgraphAvailabilityManager', - args: [governor.address, rewardsManager.address, '2', voteTimeLimit, oracles], - }), - ).to.be.revertedWith('SAM: executionThreshold too low') - }) - - it('should revert if executionThreshold is too high', async () => { - await expect( - deploy(DeployType.Deploy, governor, { - name: 'SubgraphAvailabilityManager', - args: [governor.address, rewardsManager.address, '6', voteTimeLimit, oracles], - }), - ).to.be.revertedWith('SAM: executionThreshold too high') - }) - }) - - describe('initializer', () => { - it('should init governor', async () => { - expect(await subgraphAvailabilityManager.governor()).to.be.equal(governor.address) - }) - - it('should init executionThreshold', async () => { - expect(await subgraphAvailabilityManager.executionThreshold()).to.be.equal(executionThreshold) - }) - - it('should init voteTimeLimit', async () => { - expect(await subgraphAvailabilityManager.voteTimeLimit()).to.be.equal(voteTimeLimit) - }) - - it('should init oracles', async () => { - for (let i = 0; i < oracles.length; i++) { - expect(await subgraphAvailabilityManager.oracles(i)).to.be.equal(oracles[i]) - } - }) - }) - - describe('set vote limit', function () { - it('sets voteTimeLimit successfully', async () => { - const newVoteTimeLimit = 10 - await expect(subgraphAvailabilityManager.connect(governor).setVoteTimeLimit(newVoteTimeLimit)) - .emit(subgraphAvailabilityManager, 'VoteTimeLimitSet') - .withArgs(newVoteTimeLimit) - expect(await subgraphAvailabilityManager.voteTimeLimit()).to.be.equal(newVoteTimeLimit) - }) - - it('should fail if not called by governor', async () => { - const newVoteTimeLimit = 10 - await expect(subgraphAvailabilityManager.connect(me).setVoteTimeLimit(newVoteTimeLimit)).to.be.revertedWith( - 'Only Governor can call', - ) - }) - }) - - describe('set oracles', () => { - it('sets an oracle successfully', async () => { - const oracle = randomAddress() - await expect(subgraphAvailabilityManager.connect(governor).setOracle(0, oracle)) - .emit(subgraphAvailabilityManager, 'OracleSet') - .withArgs(0, oracle) - expect(await subgraphAvailabilityManager.oracles(0)).to.be.equal(oracle) - }) - - it('should fail if not called by governor', async () => { - const oracle = randomAddress() - await expect(subgraphAvailabilityManager.connect(me).setOracle(0, oracle)).to.be.revertedWith( - 'Only Governor can call', - ) - }) - - it('should fail if setting oracle to address zero', async () => { - await expect(subgraphAvailabilityManager.connect(governor).setOracle(0, AddressZero)).to.revertedWith( - 'SAM: oracle cannot be address zero', - ) - }) - - it('should fail if index is out of bounds', async () => { - const oracle = randomAddress() - await expect(subgraphAvailabilityManager.connect(governor).setOracle(5, oracle)).to.be.revertedWith( - 'SAM: index out of bounds', - ) - }) - }) - - describe('voting', function () { - it('votes denied successfully', async () => { - const denied = true - const tx = await subgraphAvailabilityManager.connect(oracleOne).vote(subgraphDeploymentID1, denied, 0) - const timestamp = (await ethers.provider.getBlock('latest')).timestamp - await expect(tx) - .to.emit(subgraphAvailabilityManager, 'OracleVote') - .withArgs(subgraphDeploymentID1, denied, 0, timestamp) - }) - - it('should fail if not called by oracle', async () => { - const denied = true - await expect(subgraphAvailabilityManager.connect(me).vote(subgraphDeploymentID1, denied, 0)).to.be.revertedWith( - 'SAM: caller must be oracle', - ) - }) - - it('should fail if index is out of bounds', async () => { - const denied = true - await expect( - subgraphAvailabilityManager.connect(oracleOne).vote(subgraphDeploymentID1, denied, 5), - ).to.be.revertedWith('SAM: index out of bounds') - }) - - it('should fail if oracle used an incorrect index', async () => { - const denied = true - await expect( - subgraphAvailabilityManager.connect(oracleOne).vote(subgraphDeploymentID1, denied, 1), - ).to.be.revertedWith('SAM: caller must be oracle') - }) - - it('should still be allowed if only one oracle has voted', async () => { - const denied = true - const tx = await subgraphAvailabilityManager.connect(oracleOne).vote(subgraphDeploymentID1, denied, 0) - await expect(tx).to.emit(subgraphAvailabilityManager, 'OracleVote') - expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.false - }) - - it('should be denied or allowed if majority of oracles have voted', async () => { - // 3/5 oracles vote denied = true - let denied = true - await subgraphAvailabilityManager.connect(oracleOne).vote(subgraphDeploymentID1, denied, 0) - await subgraphAvailabilityManager.connect(oracleTwo).vote(subgraphDeploymentID1, denied, 1) - const tx = await subgraphAvailabilityManager.connect(oracleThree).vote(subgraphDeploymentID1, denied, 2) - await expect(tx).to.emit(rewardsManager, 'RewardsDenylistUpdated').withArgs(subgraphDeploymentID1, tx.blockNumber) - - // check events order - const receipt = await tx.wait() - expect(receipt.events[0].event).to.be.equal('OracleVote') - const rewardsManangerEvent = rewardsManager.interface.parseLog(receipt.logs[1]).name - expect(rewardsManangerEvent).to.be.equal('RewardsDenylistUpdated') - - // check that subgraph is denied - expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.true - - // 3/5 oracles vote denied = false - denied = false - await subgraphAvailabilityManager.connect(oracleOne).vote(subgraphDeploymentID1, denied, 0) - await subgraphAvailabilityManager.connect(oracleTwo).vote(subgraphDeploymentID1, denied, 1) - await subgraphAvailabilityManager.connect(oracleThree).vote(subgraphDeploymentID1, denied, 2) - - // check that subgraph is not denied - expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.false - }) - - it('should not be denied if the same oracle votes three times', async () => { - const denied = true - await subgraphAvailabilityManager.connect(oracleOne).vote(subgraphDeploymentID1, denied, 0) - await subgraphAvailabilityManager.connect(oracleOne).vote(subgraphDeploymentID1, denied, 0) - await subgraphAvailabilityManager.connect(oracleOne).vote(subgraphDeploymentID1, denied, 0) - - expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.false - }) - - it('should not be denied if voteTimeLimit has passed and not enough oracles have voted', async () => { - // 2/3 oracles vote denied = true - const denied = true - await subgraphAvailabilityManager.connect(oracleOne).vote(subgraphDeploymentID1, denied, 0) - await subgraphAvailabilityManager.connect(oracleTwo).vote(subgraphDeploymentID1, denied, 1) - - // increase time by 6 seconds - await ethers.provider.send('evm_increaseTime', [6]) - // last oracle votes denied = true - const tx = await subgraphAvailabilityManager.connect(oracleThree).vote(subgraphDeploymentID1, denied, 2) - await expect(tx).to.not.emit(rewardsManager, 'RewardsDenylistUpdated') - - // subgraph state didn't change because enough time has passed so that - // previous votes are no longer valid - expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.false - }) - - it('clears opposite vote when voting', async () => { - const denied = true - await subgraphAvailabilityManager.connect(oracleOne).vote(subgraphDeploymentID1, denied, 0) - await subgraphAvailabilityManager.connect(oracleTwo).vote(subgraphDeploymentID1, denied, 1) - await subgraphAvailabilityManager.connect(oracleThree).vote(subgraphDeploymentID1, denied, 2) - - // 3/5 oracles vote denied = true so subgraph is denied - expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.true - - // oracleOne changes its vote to denied = false - await subgraphAvailabilityManager.connect(oracleOne).vote(subgraphDeploymentID1, false, 0) - - // last deny vote should be 0 for oracleOne - expect(await subgraphAvailabilityManager.lastDenyVote(0, subgraphDeploymentID1, 0)).to.be.equal(0) - - // executionThreshold isn't met now since oracleOne changed its vote - expect(await subgraphAvailabilityManager.checkVotes(subgraphDeploymentID1, denied)).to.be.false - - // subgraph is still denied in rewards manager because only one oracle changed its vote - expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.true - }) - }) - - describe('vote many', function () { - it('votes many successfully', async () => { - const subgraphs = [subgraphDeploymentID1, subgraphDeploymentID2, subgraphDeploymentID3] - const denied = [true, false, true] - const tx = await subgraphAvailabilityManager.connect(oracleOne).voteMany(subgraphs, denied, 0) - const timestamp = (await ethers.provider.getBlock('latest')).timestamp - await expect(tx) - .to.emit(subgraphAvailabilityManager, 'OracleVote') - .withArgs(subgraphDeploymentID1, true, 0, timestamp) - await expect(tx) - .to.emit(subgraphAvailabilityManager, 'OracleVote') - .withArgs(subgraphDeploymentID2, false, 0, timestamp) - await expect(tx) - .to.emit(subgraphAvailabilityManager, 'OracleVote') - .withArgs(subgraphDeploymentID3, true, 0, timestamp) - }) - - it('should change subgraph state if majority of oracles have voted', async () => { - const subgraphs = [subgraphDeploymentID1, subgraphDeploymentID2, subgraphDeploymentID3] - const denied = [true, false, true] - // 3/5 oracles vote denied = true - await subgraphAvailabilityManager.connect(oracleOne).voteMany(subgraphs, denied, 0) - await subgraphAvailabilityManager.connect(oracleTwo).voteMany(subgraphs, denied, 1) - - const tx = await subgraphAvailabilityManager.connect(oracleThree).voteMany(subgraphs, denied, 2) - - await expect(tx).to.emit(rewardsManager, 'RewardsDenylistUpdated').withArgs(subgraphDeploymentID1, tx.blockNumber) - // subgraphDeploymentID2 voted false but was never denied, so setDenied is idempotent (no event) - await expect(tx).to.emit(rewardsManager, 'RewardsDenylistUpdated').withArgs(subgraphDeploymentID3, tx.blockNumber) - - // check that subgraphs are denied - expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.true - expect(await rewardsManager.isDenied(subgraphDeploymentID2)).to.be.false - expect(await rewardsManager.isDenied(subgraphDeploymentID3)).to.be.true - }) - - it('should fail if not called by oracle', async () => { - const subgraphs = [subgraphDeploymentID1, subgraphDeploymentID2, subgraphDeploymentID3] - const denied = [true, false, true] - await expect(subgraphAvailabilityManager.connect(me).voteMany(subgraphs, denied, 0)).to.be.revertedWith( - 'SAM: caller must be oracle', - ) - }) - - it('should fail if index is out of bounds', async () => { - const subgraphs = [subgraphDeploymentID1, subgraphDeploymentID2, subgraphDeploymentID3] - const denied = [true, false, true] - await expect(subgraphAvailabilityManager.connect(oracleOne).voteMany(subgraphs, denied, 5)).to.be.revertedWith( - 'SAM: index out of bounds', - ) - }) - - it('should fail if oracle used an incorrect index', async () => { - const subgraphs = [subgraphDeploymentID1, subgraphDeploymentID2, subgraphDeploymentID3] - const denied = [true, false, true] - await expect(subgraphAvailabilityManager.connect(oracleOne).voteMany(subgraphs, denied, 1)).to.be.revertedWith( - 'SAM: caller must be oracle', - ) - }) - }) - - describe('refreshing votes', () => { - it('should refresh votes if an oracle is replaced', async () => { - const denied = true - // 2/3 oracles vote denied = true - await subgraphAvailabilityManager.connect(oracleOne).vote(subgraphDeploymentID1, denied, 0) - await subgraphAvailabilityManager.connect(oracleTwo).vote(subgraphDeploymentID1, denied, 1) - - // replace oracleOne with a new oracle - await subgraphAvailabilityManager.connect(governor).setOracle(2, newOracle.address) - - // new oracle votes denied = true - await subgraphAvailabilityManager.connect(newOracle).vote(subgraphDeploymentID1, denied, 2) - - // subgraph shouldn't be denied because setting a new oracle should refresh the votes - expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.false - }) - - it('should refresh votes if voteTimeLimit changes', async () => { - const denied = true - // 2/3 oracles vote denied = true - await subgraphAvailabilityManager.connect(oracleOne).vote(subgraphDeploymentID1, denied, 0) - await subgraphAvailabilityManager.connect(oracleTwo).vote(subgraphDeploymentID1, denied, 1) - - // change voteTimeLimit to 10 seconds - await subgraphAvailabilityManager.connect(governor).setVoteTimeLimit(10) - - // last oracle votes denied = true - await subgraphAvailabilityManager.connect(oracleThree).vote(subgraphDeploymentID1, denied, 2) - - // subgraph shouldn't be denied because voteTimeLimit should refresh the votes - expect(await rewardsManager.isDenied(subgraphDeploymentID1)).to.be.false - }) - }) -}) diff --git a/packages/contracts-test/tests/unit/serviceRegisty.test.ts b/packages/contracts-test/tests/unit/serviceRegisty.test.ts deleted file mode 100644 index 52aa74557..000000000 --- a/packages/contracts-test/tests/unit/serviceRegisty.test.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { ServiceRegistry } from '@graphprotocol/contracts' -import { IStaking } from '@graphprotocol/contracts' -import { GraphNetworkContracts } from '@graphprotocol/sdk' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { expect } from 'chai' -import hre from 'hardhat' - -import { NetworkFixture } from './lib/fixtures' - -describe('ServiceRegistry', () => { - const graph = hre.graph() - let governor: SignerWithAddress - let indexer: SignerWithAddress - let operator: SignerWithAddress - - let fixture: NetworkFixture - - let contracts: GraphNetworkContracts - let serviceRegistry: ServiceRegistry - let staking: IStaking - - const shouldRegister = async (url: string, geohash: string) => { - // Register the indexer service - const tx = serviceRegistry.connect(indexer).register(url, geohash) - await expect(tx).emit(serviceRegistry, 'ServiceRegistered').withArgs(indexer.address, url, geohash) - - // Updated state - const indexerService = await serviceRegistry.services(indexer.address) - expect(indexerService.url).eq(url) - expect(indexerService.geohash).eq(geohash) - } - - before(async function () { - ;[governor, indexer, operator] = await graph.getTestAccounts() - ;({ governor } = await graph.getNamedAccounts()) - fixture = new NetworkFixture(graph.provider) - contracts = await fixture.load(governor) - serviceRegistry = contracts.ServiceRegistry as ServiceRegistry - staking = contracts.Staking as IStaking - }) - - beforeEach(async function () { - await fixture.setUp() - }) - - afterEach(async function () { - await fixture.tearDown() - }) - - describe('register', function () { - const url = 'https://192.168.2.1/' - const geo = '69y7hdrhm6mp' - - it('should allow registering', async function () { - await shouldRegister(url, geo) - }) - - it('should allow registering with a very long string', async function () { - const url = 'https://' + 'a'.repeat(125) + '.com' - await shouldRegister(url, geo) - }) - - it('should allow updating a registration', async function () { - const [url1, geohash1] = ['https://thegraph.com', '69y7hdrhm6mp'] - const [url2, geohash2] = ['https://192.168.0.1', 'dr5regw2z6y'] - await shouldRegister(url1, geohash1) - await shouldRegister(url2, geohash2) - }) - - it('reject registering empty URL', async function () { - const tx = serviceRegistry.connect(indexer).register('', '') - await expect(tx).revertedWith('Service must specify a URL') - }) - - describe('operator', function () { - it('reject register from unauthorized operator', async function () { - const tx = serviceRegistry.connect(operator).registerFor(indexer.address, 'http://thegraph.com', '') - await expect(tx).revertedWith('!auth') - }) - - it('should register from operator', async function () { - // Auth and register - await staking.connect(indexer).setOperator(operator.address, true) - await serviceRegistry.connect(operator).registerFor(indexer.address, url, geo) - - // Updated state - const indexerService = await serviceRegistry.services(indexer.address) - expect(indexerService.url).eq(url) - expect(indexerService.geohash).eq(geo) - }) - }) - }) - - describe('unregister', function () { - const url = 'https://192.168.2.1/' - const geo = '69y7hdrhm6mp' - - it('should unregister existing registration', async function () { - // Register the indexer service - await serviceRegistry.connect(indexer).register(url, geo) - - // Unregister the indexer service - const tx = serviceRegistry.connect(indexer).unregister() - await expect(tx).emit(serviceRegistry, 'ServiceUnregistered').withArgs(indexer.address) - }) - - it('reject unregister non-existing registration', async function () { - const tx = serviceRegistry.connect(indexer).unregister() - await expect(tx).revertedWith('Service already unregistered') - }) - - describe('operator', function () { - it('reject unregister from unauthorized operator', async function () { - // Register the indexer service - await serviceRegistry.connect(indexer).register(url, geo) - - // Unregister - const tx = serviceRegistry.connect(operator).unregisterFor(indexer.address) - await expect(tx).revertedWith('!auth') - }) - - it('should unregister from operator', async function () { - // Register the indexer service - await serviceRegistry.connect(indexer).register(url, geo) - - // Auth and unregister - await staking.connect(indexer).setOperator(operator.address, true) - await serviceRegistry.connect(operator).unregisterFor(indexer.address) - - // Updated state - const indexerService = await serviceRegistry.services(indexer.address) - expect(indexerService.url).eq('') - expect(indexerService.geohash).eq('') - }) - }) - }) -}) diff --git a/packages/contracts-test/tests/unit/staking/allocation.test.ts b/packages/contracts-test/tests/unit/staking/allocation.test.ts deleted file mode 100644 index 76de77a35..000000000 --- a/packages/contracts-test/tests/unit/staking/allocation.test.ts +++ /dev/null @@ -1,1007 +0,0 @@ -import { Curation } from '@graphprotocol/contracts' -import { EpochManager } from '@graphprotocol/contracts' -import { GraphToken } from '@graphprotocol/contracts' -import { IStaking } from '@graphprotocol/contracts' -import { LibExponential } from '@graphprotocol/contracts' -import { IRewardsManager } from '@graphprotocol/contracts' -import { - deriveChannelKey, - GraphNetworkContracts, - helpers, - isGraphL1ChainId, - randomHexBytes, - toBN, - toGRT, -} from '@graphprotocol/sdk' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { expect } from 'chai' -import { BigNumber, constants, Contract, PopulatedTransaction } from 'ethers' -import hre from 'hardhat' - -import { NetworkFixture } from '../lib/fixtures' - -const { AddressZero } = constants - -const MAX_PPM = toBN('1000000') -const toPercentage = (ppm: BigNumber) => ppm.mul(100).div(MAX_PPM).toNumber() - -enum AllocationState { - Null, - Active, - Closed, -} - -const ABI_LIB_EXPONENTIAL = [ - { - inputs: [ - { - internalType: 'uint256', - name: 'fees', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'stake', - type: 'uint256', - }, - { - internalType: 'uint32', - name: 'alphaNumerator', - type: 'uint32', - }, - { - internalType: 'uint32', - name: 'alphaDenominator', - type: 'uint32', - }, - { - internalType: 'uint32', - name: 'lambdaNumerator', - type: 'uint32', - }, - { - internalType: 'uint32', - name: 'lambdaDenominator', - type: 'uint32', - }, - ], - name: 'exponentialRebates', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'pure', - type: 'function', - }, -] - -describe('Staking:Allocation', () => { - const graph = hre.graph({ addressBook: 'addresses-local.json' }) - let me: SignerWithAddress - let governor: SignerWithAddress - let indexer: SignerWithAddress - let delegator: SignerWithAddress - let assetHolder: SignerWithAddress - - let fixture: NetworkFixture - - let contracts: GraphNetworkContracts - let curation: Curation - let epochManager: EpochManager - let grt: GraphToken - let staking: IStaking - let rewardsManager: IRewardsManager - let libExponential: LibExponential - - // Test values - - const indexerTokens = toGRT('1000') - const tokensToStake = toGRT('100') - const tokensToDelegate = toGRT('10') - const tokensToAllocate = toGRT('100') - const tokensToCollect = toGRT('100') - const subgraphDeploymentID = randomHexBytes() - const channelKey = deriveChannelKey() - const allocationID = channelKey.address - const anotherChannelKey = deriveChannelKey() - const anotherAllocationID = anotherChannelKey.address - const metadata = randomHexBytes(32) - const poi = randomHexBytes() - - // Helpers - - const allocate = async (tokens: BigNumber, _allocationID?: string, _proof?: string) => { - return staking - .connect(indexer) - .allocateFrom( - indexer.address, - subgraphDeploymentID, - tokens, - _allocationID ?? allocationID, - metadata, - _proof ?? (await channelKey.generateProof(indexer.address)), - ) - } - - const shouldAllocate = async (tokensToAllocate: BigNumber) => { - // Advance epoch to prevent epoch jumping mid test - await helpers.mineEpoch(epochManager) - - // Before state - const beforeStake = await staking.stakes(indexer.address) - - // Allocate - const currentEpoch = await epochManager.currentEpoch() - const tx = allocate(tokensToAllocate) - await expect(tx) - .emit(staking, 'AllocationCreated') - .withArgs(indexer.address, subgraphDeploymentID, currentEpoch, tokensToAllocate, allocationID, metadata) - - // After state - const afterStake = await staking.stakes(indexer.address) - const afterAlloc = await staking.getAllocation(allocationID) - const afterState = await staking.getAllocationState(allocationID) - - // Stake updated - expect(afterStake.tokensAllocated).eq(beforeStake.tokensAllocated.add(tokensToAllocate)) - // Allocation updated - expect(afterAlloc.indexer).eq(indexer.address) - expect(afterAlloc.subgraphDeploymentID).eq(subgraphDeploymentID) - expect(afterAlloc.tokens).eq(tokensToAllocate) - expect(afterAlloc.createdAtEpoch).eq(currentEpoch) - expect(afterAlloc.collectedFees).eq(toGRT('0')) - expect(afterAlloc.closedAtEpoch).eq(toBN('0')) - expect(afterState).to.eq(AllocationState.Active) - } - - // This function tests collect with state updates - const shouldCollect = async ( - tokensToCollect: BigNumber, - options: { - allocationID?: string - expectEvent?: boolean - } = {}, - ): Promise<{ queryRebates: BigNumber; queryFeesBurnt: BigNumber }> => { - const expectEvent = options.expectEvent ?? true - const alloID = options.allocationID ?? allocationID - const alloStateBefore = await staking.getAllocationState(alloID) - // Should have a particular state before collecting - expect(alloStateBefore).to.be.oneOf([AllocationState.Active, AllocationState.Closed]) - - // Before state - const beforeTokenSupply = await grt.totalSupply() - const beforePool = await curation.pools(subgraphDeploymentID) - const beforeAlloc = await staking.getAllocation(alloID) - const beforeIndexerBalance = await grt.balanceOf(indexer.address) - const beforeStake = await staking.stakes(indexer.address) - const beforeDelegationPool = await staking.delegationPools(indexer.address) - - // Advance blocks to get the allocation in epoch where it can be closed - await helpers.mineEpoch(epochManager) - - // Collect fees and calculate expected results - let rebateFees = tokensToCollect - const protocolPercentage = await staking.protocolPercentage() - const protocolFees = rebateFees.mul(protocolPercentage).div(MAX_PPM) - rebateFees = rebateFees.sub(protocolFees) - - const curationPercentage = await staking.curationPercentage() - const curationFees = rebateFees.mul(curationPercentage).div(MAX_PPM) - rebateFees = rebateFees.sub(curationFees) - - const queryFees = tokensToCollect.sub(protocolFees).sub(curationFees) - - const [alphaNumerator, alphaDenominator, lambdaNumerator, lambdaDenominator] = await Promise.all([ - staking.alphaNumerator(), - staking.alphaDenominator(), - staking.lambdaNumerator(), - staking.lambdaDenominator(), - ]) - const accumulatedRebates = await libExponential.exponentialRebates( - queryFees.add(beforeAlloc.collectedFees), - beforeAlloc.tokens, - alphaNumerator, - alphaDenominator, - lambdaNumerator, - lambdaDenominator, - ) - let queryRebates = beforeAlloc.distributedRebates.gt(accumulatedRebates) - ? BigNumber.from(0) - : accumulatedRebates.sub(beforeAlloc.distributedRebates) - queryRebates = queryRebates.gt(queryFees) ? queryFees : queryRebates - const queryFeesBurnt = queryFees.sub(queryRebates) - - const indexerCut = queryRebates.mul(beforeDelegationPool.queryFeeCut).div(MAX_PPM) - const delegationRewards = queryRebates.sub(indexerCut) - queryRebates = queryRebates.sub(delegationRewards) - - // Collect tokens from allocation - const tx = staking.connect(assetHolder).collect(tokensToCollect, alloID) - if (expectEvent) { - await expect(tx) - .emit(staking, 'RebateCollected') - .withArgs( - assetHolder.address, - indexer.address, - subgraphDeploymentID, - alloID, - await epochManager.currentEpoch(), - tokensToCollect, - protocolFees, - curationFees, - queryFees, - queryRebates, - delegationRewards, - ) - } else { - await expect(tx).to.not.be.reverted - await expect(tx).to.not.emit(staking, 'RebateCollected') - } - - // After state - const afterTokenSupply = await grt.totalSupply() - const afterPool = await curation.pools(subgraphDeploymentID) - const afterAlloc = await staking.getAllocation(alloID) - const afterIndexerBalance = await grt.balanceOf(indexer.address) - const afterStake = await staking.stakes(indexer.address) - const alloStateAfter = await staking.getAllocationState(alloID) - - // Check that protocol fees are burnt - expect(afterTokenSupply).eq(beforeTokenSupply.sub(protocolFees).sub(queryFeesBurnt)) - - // Check that collected tokens are correctly distributed for rebating + tax + curators - // tokensToCollect = queryFees + protocolFees + curationFees - expect(tokensToCollect).eq(queryFees.add(protocolFees).add(curationFees)) - - // Check that queryFees are distributed - // queryFees = queryRebates + queryFeesBurnt + delegationRewards - expect(queryFees).eq(queryRebates.add(queryFeesBurnt).add(delegationRewards)) - - // Check that curation reserves increased for the SubgraphDeployment - expect(afterPool.tokens).eq(beforePool.tokens.add(curationFees)) - - // Verify allocation struct - expect(afterAlloc.tokens).eq(beforeAlloc.tokens) - expect(afterAlloc.createdAtEpoch).eq(beforeAlloc.createdAtEpoch) - expect(afterAlloc.closedAtEpoch).eq(beforeAlloc.closedAtEpoch) - expect(afterAlloc.accRewardsPerAllocatedToken).eq(beforeAlloc.accRewardsPerAllocatedToken) - expect(afterAlloc.collectedFees).eq(beforeAlloc.collectedFees.add(queryFees)) - expect(afterAlloc.distributedRebates).eq(beforeAlloc.distributedRebates.add(queryRebates).add(delegationRewards)) - expect(alloStateAfter).eq(alloStateBefore) - - // // Funds distributed to indexer or restaked - const restake = (await staking.rewardsDestination(indexer.address)) === AddressZero - if (restake) { - expect(afterIndexerBalance).eq(beforeIndexerBalance) - // Next invariant is only true if there are no delegation rewards (which is true in this case) - expect(afterStake.tokensStaked).eq(beforeStake.tokensStaked.add(queryRebates)) - } else { - expect(afterIndexerBalance).eq(beforeIndexerBalance.add(queryRebates)) - expect(afterStake.tokensStaked).eq(beforeStake.tokensStaked) - } - - return { queryRebates, queryFeesBurnt } - } - - const shouldCollectMultiple = async (collections: BigNumber[]) => { - // Perform the multiple collections on currently open allocation - const totalTokensToCollect = collections.reduce((a, b) => a.add(b), BigNumber.from(0)) - let rebatedAmountMultiple = BigNumber.from(0) - for (const collect of collections) { - rebatedAmountMultiple = rebatedAmountMultiple.add((await shouldCollect(collect)).queryRebates) - } - - // Reset rebates state by closing allocation, advancing epoch and opening a new allocation - await staking.connect(indexer).closeAllocation(allocationID, poi) - await helpers.mineEpoch(epochManager) - await allocate(tokensToAllocate, anotherAllocationID, await anotherChannelKey.generateProof(indexer.address)) - - // Collect `tokensToCollect` with a single voucher - const rebatedAmountFull = (await shouldCollect(totalTokensToCollect, { allocationID: anotherAllocationID })) - .queryRebates - - // Check rebated amounts match, allow a small error margin of 5 wei - // Due to rounding it's not possible to guarantee an exact match in case of multiple collections - expect(rebatedAmountMultiple.sub(rebatedAmountFull)).lt(5) - } - - const shouldCloseAllocation = async () => { - // Before state - const beforeStake = await staking.stakes(indexer.address) - const beforeAlloc = await staking.getAllocation(allocationID) - const beforeAlloState = await staking.getAllocationState(allocationID) - expect(beforeAlloState).eq(AllocationState.Active) - - // Move at least one epoch to be able to close - await helpers.mineEpoch(epochManager) - await helpers.mineEpoch(epochManager) - - // Calculations - const currentEpoch = await epochManager.currentEpoch() - - // Close allocation - const tx = staking.connect(indexer).closeAllocation(allocationID, poi) - await expect(tx) - .emit(staking, 'AllocationClosed') - .withArgs( - indexer.address, - subgraphDeploymentID, - currentEpoch, - beforeAlloc.tokens, - allocationID, - indexer.address, - poi, - false, - ) - - // After state - const afterStake = await staking.stakes(indexer.address) - const afterAlloc = await staking.getAllocation(allocationID) - const afterAlloState = await staking.getAllocationState(allocationID) - - // Stake updated - expect(afterStake.tokensAllocated).eq(beforeStake.tokensAllocated.sub(beforeAlloc.tokens)) - // Allocation updated - expect(afterAlloc.closedAtEpoch).eq(currentEpoch) - // State progressed - expect(afterAlloState).eq(AllocationState.Closed) - } - // -- Tests -- - - before(async function () { - ;[me, indexer, delegator, assetHolder] = await graph.getTestAccounts() - ;({ governor } = await graph.getNamedAccounts()) - - fixture = new NetworkFixture(graph.provider) - contracts = await fixture.load(governor) - curation = contracts.Curation as Curation - epochManager = contracts.EpochManager as EpochManager - grt = contracts.GraphToken as GraphToken - staking = contracts.Staking as IStaking - rewardsManager = contracts.RewardsManager as IRewardsManager - - const stakingName = isGraphL1ChainId(graph.chainId) ? 'L1Staking' : 'L2Staking' - const entry = graph.addressBook.getEntry(stakingName) - - libExponential = new Contract( - entry.implementation.libraries.LibExponential, - ABI_LIB_EXPONENTIAL, - graph.provider, - ) as LibExponential - - // Give some funds to the indexer and approve staking contract to use funds on indexer behalf - await grt.connect(governor).mint(indexer.address, indexerTokens) - await grt.connect(indexer).approve(staking.address, indexerTokens) - - // Give some funds to the delegator and approve staking contract to use funds on delegator behalf - await grt.connect(governor).mint(delegator.address, tokensToDelegate) - await grt.connect(delegator).approve(staking.address, tokensToDelegate) - - // HACK: we set the staking contract as the subgraph service to make tests pass. - // This is due to the test suite being outdated. - await rewardsManager.connect(governor).setSubgraphService(staking.address) - }) - - beforeEach(async function () { - await fixture.setUp() - }) - - afterEach(async function () { - await fixture.tearDown() - }) - - describe('operators', function () { - it('should set operator', async function () { - // Before state - const beforeOperator = await staking.operatorAuth(indexer.address, me.address) - - // Set operator - const tx = staking.connect(indexer).setOperator(me.address, true) - await expect(tx).emit(staking, 'SetOperator').withArgs(indexer.address, me.address, true) - - // After state - const afterOperator = await staking.operatorAuth(indexer.address, me.address) - - // State updated - expect(beforeOperator).eq(false) - expect(afterOperator).eq(true) - }) - - it('should unset operator', async function () { - await staking.connect(indexer).setOperator(me.address, true) - - // Before state - const beforeOperator = await staking.operatorAuth(indexer.address, me.address) - - // Set operator - const tx = staking.connect(indexer).setOperator(me.address, false) - await expect(tx).emit(staking, 'SetOperator').withArgs(indexer.address, me.address, false) - - // After state - const afterOperator = await staking.operatorAuth(indexer.address, me.address) - - // State updated - expect(beforeOperator).eq(true) - expect(afterOperator).eq(false) - }) - it('should reject setting the operator to the msg.sender', async function () { - await expect(staking.connect(indexer).setOperator(indexer.address, true)).to.be.revertedWith('operator == sender') - }) - }) - - describe('rewardsDestination', function () { - it('should set rewards destination', async function () { - // Before state - const beforeDestination = await staking.rewardsDestination(indexer.address) - - // Set - const tx = staking.connect(indexer).setRewardsDestination(me.address) - await expect(tx).emit(staking, 'SetRewardsDestination').withArgs(indexer.address, me.address) - - // After state - const afterDestination = await staking.rewardsDestination(indexer.address) - - // State updated - expect(beforeDestination).eq(AddressZero) - expect(afterDestination).eq(me.address) - - // Must be able to set back to zero - await staking.connect(indexer).setRewardsDestination(AddressZero) - expect(await staking.rewardsDestination(indexer.address)).eq(AddressZero) - }) - }) - - /** - * Allocate - */ - describe('allocate', function () { - it('reject allocate with invalid allocationID', async function () { - const tx = staking - .connect(indexer) - .allocateFrom( - indexer.address, - subgraphDeploymentID, - tokensToAllocate, - AddressZero, - metadata, - randomHexBytes(20), - ) - await expect(tx).revertedWith('!alloc') - }) - - it('reject allocate if no tokens staked', async function () { - const tx = allocate(toBN('1')) - await expect(tx).revertedWith('!minimumIndexerStake') - }) - - it('reject allocate zero tokens if no minimum stake', async function () { - const tx = allocate(toBN('0')) - await expect(tx).revertedWith('!minimumIndexerStake') - }) - - context('> when staked', function () { - beforeEach(async function () { - await staking.connect(indexer).stake(tokensToStake) - }) - - it('reject allocate more than available tokens', async function () { - const tokensOverCapacity = tokensToStake.add(toBN('1')) - const tx = allocate(tokensOverCapacity) - await expect(tx).revertedWith('!capacity') - }) - - it('should allocate', async function () { - await helpers.mineEpoch(epochManager) - await shouldAllocate(tokensToAllocate) - }) - - it('should allow allocation of zero tokens', async function () { - const zeroTokens = toGRT('0') - const tx = allocate(zeroTokens) - await tx - }) - - it('should allocate on behalf of indexer', async function () { - const proof = await channelKey.generateProof(indexer.address) - - // Reject to allocate if the address is not operator - const tx1 = staking - .connect(me) - .allocateFrom(indexer.address, subgraphDeploymentID, tokensToAllocate, allocationID, metadata, proof) - await expect(tx1).revertedWith('!auth') - - // Should allocate if given operator auth - await staking.connect(indexer).setOperator(me.address, true) - await staking - .connect(me) - .allocateFrom(indexer.address, subgraphDeploymentID, tokensToAllocate, allocationID, metadata, proof) - }) - - it('reject allocate reusing an allocation ID', async function () { - await helpers.mineEpoch(epochManager) - const someTokensToAllocate = toGRT('10') - await shouldAllocate(someTokensToAllocate) - const tx = allocate(someTokensToAllocate) - await expect(tx).revertedWith('!null') - }) - - describe('reject allocate on invalid proof', function () { - it('invalid message', async function () { - const invalidProof = await channelKey.generateProof(randomHexBytes(20)) - const tx = staking - .connect(indexer) - .allocateFrom( - indexer.address, - subgraphDeploymentID, - tokensToAllocate, - indexer.address, - metadata, - invalidProof, - ) - await expect(tx).revertedWith('!proof') - }) - - it('invalid proof signature format', async function () { - const tx = staking - .connect(indexer) - .allocateFrom( - indexer.address, - subgraphDeploymentID, - tokensToAllocate, - indexer.address, - metadata, - randomHexBytes(32), - ) - await expect(tx).revertedWith('ECDSA: invalid signature length') - }) - }) - }) - }) - - /** - * Collect - */ - describe('collect', function () { - beforeEach(async function () { - // Create the allocation - await staking.connect(indexer).stake(tokensToStake) - await helpers.mineEpoch(epochManager) - await allocate(tokensToAllocate) - - // Add some signal to the subgraph to enable curation fees - const tokensToSignal = toGRT('100') - await grt.connect(governor).mint(me.address, tokensToSignal) - await grt.connect(me).approve(curation.address, tokensToSignal) - await curation.connect(me).mint(subgraphDeploymentID, tokensToSignal, 0) - - // Fund asset holder wallet - const tokensToFund = toGRT('100000') - await grt.connect(governor).mint(assetHolder.address, tokensToFund) - await grt.connect(assetHolder).approve(staking.address, tokensToFund) - }) - - // * Test with different curation fees and protocol tax - for (const params of [ - { curationPercentage: toBN('0'), protocolPercentage: toBN('0'), queryFeeCut: toBN('0') }, - { curationPercentage: toBN('0'), protocolPercentage: toBN('100000'), queryFeeCut: toBN('0') }, - { curationPercentage: toBN('200000'), protocolPercentage: toBN('0'), queryFeeCut: toBN('0') }, - { - curationPercentage: toBN('200000'), - protocolPercentage: toBN('100000'), - queryFeeCut: toBN('950000'), - }, - ]) { - context( - `> with ${toPercentage(params.curationPercentage)}% curationFees, ${toPercentage( - params.protocolPercentage, - )}% protocolTax and ${toPercentage(params.queryFeeCut)}% queryFeeCut`, - function () { - beforeEach(async function () { - // Set a protocol fee percentage - await staking.connect(governor).setProtocolPercentage(params.protocolPercentage) - - // Set a curation fee percentage - await staking.connect(governor).setCurationPercentage(params.curationPercentage) - - // Setup delegation - await staking.connect(governor).setDelegationRatio(10) // 1:10 delegation capacity - await staking.connect(indexer).setDelegationParameters(toBN('800000'), params.queryFeeCut, 5) - await staking.connect(delegator).delegate(indexer.address, tokensToDelegate) - }) - - it('should collect funds from asset holder (restake=true)', async function () { - await shouldCollect(tokensToCollect) - }) - - it('should collect funds from asset holder (restake=false)', async function () { - // Set a random rewards destination address - await staking.connect(governor).setRewardsDestination(me.address) - await shouldCollect(tokensToCollect) - }) - - it('should collect funds on both active and closed allocations', async function () { - // Collect from active allocation - await shouldCollect(tokensToCollect) - - // Close allocation - await staking.connect(indexer).closeAllocation(allocationID, poi) - - // Collect from closed allocation - await shouldCollect(tokensToCollect) - }) - - it('should collect zero tokens', async function () { - await shouldCollect(toGRT('0'), { expectEvent: false }) - }) - - it('should allow multiple collections on the same allocation', async function () { - // Collect `tokensToCollect` with 4 different vouchers - // This can represent vouchers not necessarily from the same gateway - const splitCollect = tokensToCollect.div(4) - await shouldCollectMultiple(Array(4).fill(splitCollect)) - }) - - it('should allow multiple collections on the same allocation (edge case 1: small then big)', async function () { - // Collect `tokensToCollect` with 2 vouchers, one small and then one big - const smallCollect = tokensToCollect.div(100) - const bigCollect = tokensToCollect.sub(smallCollect) - await shouldCollectMultiple([smallCollect, bigCollect]) - }) - - it('should allow multiple collections on the same allocation (edge case 2: big then small)', async function () { - // Collect `tokensToCollect` with 2 vouchers, one big and then one small - const smallCollect = tokensToCollect.div(100) - const bigCollect = tokensToCollect.sub(smallCollect) - await shouldCollectMultiple([bigCollect, smallCollect]) - }) - }, - ) - } - - it('reject collect if invalid collection', async function () { - const tx = staking.connect(indexer).collect(tokensToCollect, AddressZero) - await expect(tx).revertedWith('!alloc') - }) - - it('reject collect if allocation does not exist', async function () { - const invalidAllocationID = randomHexBytes(20) - const tx = staking.connect(assetHolder).collect(tokensToCollect, invalidAllocationID) - await expect(tx).revertedWith('!collect') - }) - - it('should get no rebates if allocated stake is zero', async function () { - // Create an allocation with no stake - await staking.connect(indexer).stake(tokensToStake) - await allocate(BigNumber.from(0), anotherAllocationID, await anotherChannelKey.generateProof(indexer.address)) - - // Collect from closed allocation, should get no rebates - const rebates = await shouldCollect(tokensToCollect, { allocationID: anotherAllocationID }) - expect(rebates.queryRebates).eq(BigNumber.from(0)) - expect(rebates.queryFeesBurnt).eq(tokensToCollect) - }) - - it('should resolve over-rebated scenarios correctly', async function () { - // Set up a new allocation with `tokensToAllocate` staked - await staking.connect(indexer).stake(tokensToStake) - await allocate(tokensToAllocate, anotherAllocationID, await anotherChannelKey.generateProof(indexer.address)) - - // Set initial rebate parameters, α = 0, λ = 1 - await staking.connect(governor).setRebateParameters(0, 1, 1, 1) - - // Collection amounts - const firstTokensToCollect = tokensToAllocate.mul(8).div(10) // q1 < sij - const secondTokensToCollect = tokensToAllocate.div(10) // q2 small amount, second collect should get "negative rebates" - const thirdTokensToCollect = tokensToAllocate.mul(3) // q3 big amount so we get rebates again - - // First collection - // Indexer gets 100% of the query fees due to α = 0 - const firstRebates = await shouldCollect(firstTokensToCollect, { - allocationID: anotherAllocationID, - }) - expect(firstRebates.queryRebates).eq(firstTokensToCollect) - expect(firstRebates.queryFeesBurnt).eq(BigNumber.from(0)) - - // Update rebate parameters, α = 1, λ = 1 - await staking.connect(governor).setRebateParameters(1, 1, 1, 1) - - // Second collection - // Indexer gets 0% of the query fees - // Parameters changed so now they are over-rebated and should get "negative rebates", instead they get 0 - const secondRebates = await shouldCollect(secondTokensToCollect, { - allocationID: anotherAllocationID, - }) - expect(secondRebates.queryRebates).eq(BigNumber.from(0)) - expect(secondRebates.queryFeesBurnt).eq(secondTokensToCollect) - - // Third collection - // Previous collection plus this new one tip the balance and indexer is no longer over-rebated - // They get rebates and burn again - const thirdRebates = await shouldCollect(thirdTokensToCollect, { - allocationID: anotherAllocationID, - }) - expect(thirdRebates.queryRebates).gt(BigNumber.from(0)) - expect(thirdRebates.queryFeesBurnt).gt(BigNumber.from(0)) - }) - - it('should resolve under-rebated scenarios correctly', async function () { - // Set up a new allocation with `tokensToAllocate` staked - await staking.connect(indexer).stake(tokensToStake) - await allocate(tokensToAllocate, anotherAllocationID, await anotherChannelKey.generateProof(indexer.address)) - - // Set initial rebate parameters, α = 1, λ = 1 - await staking.connect(governor).setRebateParameters(1, 1, 1, 1) - - // Collection amounts - const firstTokensToCollect = tokensToAllocate - const secondTokensToCollect = tokensToAllocate - const thirdTokensToCollect = tokensToAllocate.mul(50) - - // First collection - // Indexer gets rebates and burn - const firstRebates = await shouldCollect(firstTokensToCollect, { - allocationID: anotherAllocationID, - }) - expect(firstRebates.queryRebates).gt(BigNumber.from(0)) - expect(firstRebates.queryFeesBurnt).gt(BigNumber.from(0)) - - // Update rebate parameters, α = 0.1, λ = 1 - await staking.connect(governor).setRebateParameters(1, 10, 1, 1) - - // Second collection - // Indexer gets 100% of the query fees - // Parameters changed so now they are under-rebated and should get more than the available amount but we cap it - const secondRebates = await shouldCollect(secondTokensToCollect, { - allocationID: anotherAllocationID, - }) - expect(secondRebates.queryRebates).eq(secondTokensToCollect) - expect(secondRebates.queryFeesBurnt).eq(BigNumber.from(0)) - - // Third collection - // Previous collection plus this new one tip the balance and indexer is no longer under-rebated - // They get rebates and burn again - const thirdRebates = await shouldCollect(thirdTokensToCollect, { - allocationID: anotherAllocationID, - }) - expect(thirdRebates.queryRebates).gt(BigNumber.from(0)) - expect(thirdRebates.queryFeesBurnt).gt(BigNumber.from(0)) - }) - - it('should collect zero tokens', async function () { - await shouldCollect(toGRT('0'), { expectEvent: false }) - }) - - it('should get stuck under-rebated if alpha is changed to zero', async function () { - // Set up a new allocation with `tokensToAllocate` staked - await staking.connect(indexer).stake(tokensToStake) - await allocate(tokensToAllocate, anotherAllocationID, await anotherChannelKey.generateProof(indexer.address)) - - // Set initial rebate parameters, α = 1, λ = 1 - await staking.connect(governor).setRebateParameters(1, 1, 1, 1) - - // First collection - // Indexer gets rebates and burn - const firstRebates = await shouldCollect(tokensToCollect, { - allocationID: anotherAllocationID, - }) - expect(firstRebates.queryRebates).gt(BigNumber.from(0)) - expect(firstRebates.queryFeesBurnt).gt(BigNumber.from(0)) - - // Update rebate parameters, α = 0, λ = 1 - await staking.connect(governor).setRebateParameters(0, 1, 1, 1) - - // Successive collections - // Indexer gets 100% of the query fees - // Parameters changed so now they are under-rebated and should get more than the available amount but we cap it - // Distributed amount will never catch up due to the initial collection which was less than 100% - for (const _i of [...Array(10).keys()]) { - const succesiveRebates = await shouldCollect(tokensToCollect, { - allocationID: anotherAllocationID, - }) - expect(succesiveRebates.queryRebates).eq(tokensToCollect) - expect(succesiveRebates.queryFeesBurnt).eq(BigNumber.from(0)) - } - }) - }) - - /** - * Close allocation - */ - describe('closeAllocation', function () { - beforeEach(async function () { - // Stake and allocate - await staking.connect(indexer).stake(tokensToStake) - }) - - for (const tokensToAllocate of [toBN(100), toBN(0)]) { - context(`> with ${tokensToAllocate.toString()} allocated tokens`, function () { - beforeEach(async function () { - // Advance to next epoch to avoid creating the allocation - // right at the epoch boundary, which would mess up the tests. - await helpers.mineEpoch(epochManager) - - // Allocate - await allocate(tokensToAllocate) - }) - - it('reject close a non-existing allocation', async function () { - const invalidAllocationID = randomHexBytes(20) - const tx = staking.connect(indexer).closeAllocation(invalidAllocationID, poi) - await expect(tx).revertedWith('!active') - }) - - it('allow close before one epoch has passed', async function () { - const currentEpoch = await epochManager.currentEpoch() - const beforeAlloc = await staking.getAllocation(allocationID) - - const tx = staking.connect(indexer).closeAllocation(allocationID, poi) - await expect(tx) - .emit(staking, 'AllocationClosed') - .withArgs( - indexer.address, - subgraphDeploymentID, - currentEpoch, - beforeAlloc.tokens, - allocationID, - indexer.address, - poi, - false, - ) - await expect(tx).not.to.emit(rewardsManager, 'HorizonRewardsAssigned') - }) - - it('reject close if not the owner of allocation', async function () { - // Move at least one epoch to be able to close - await helpers.mineEpoch(epochManager) - - // Close allocation - const tx = staking.connect(me).closeAllocation(allocationID, poi) - await expect(tx).revertedWith('!auth') - }) - - it('reject close if allocation is already closed', async function () { - // Move at least one epoch to be able to close - await helpers.mineEpoch(epochManager) - - // First closing - await staking.connect(indexer).closeAllocation(allocationID, poi) - - // Second closing - const tx = staking.connect(indexer).closeAllocation(allocationID, poi) - await expect(tx).revertedWith('!active') - }) - - it('should close an allocation', async function () { - await shouldCloseAllocation() - }) - - it('should close an allocation (by operator)', async function () { - // Move at least one epoch to be able to close - await helpers.mineEpoch(epochManager) - await helpers.mineEpoch(epochManager) - - // Reject to close if the address is not operator - const tx1 = staking.connect(me).closeAllocation(allocationID, poi) - await expect(tx1).revertedWith('!auth') - - // Should close if given operator auth - await staking.connect(indexer).setOperator(me.address, true) - await staking.connect(me).closeAllocation(allocationID, poi) - }) - - it('should close an allocation (by public) only if allocation is non-zero', async function () { - // Reject to close if public address and under max allocation epochs - const tx1 = staking.connect(me).closeAllocation(allocationID, poi) - await expect(tx1).revertedWith('!auth') - - // Move max allocation epochs to close by delegator - const maxAllocationEpochs = await staking.maxAllocationEpochs() - await helpers.mineEpoch(epochManager, maxAllocationEpochs + 1) - - // Closing should only be possible if allocated tokens > 0 - const alloc = await staking.getAllocation(allocationID) - if (alloc.tokens.gt(0)) { - // Calculations - const beforeAlloc = await staking.getAllocation(allocationID) - const currentEpoch = await epochManager.currentEpoch() - - // Setup - await grt.connect(governor).mint(me.address, toGRT('1')) - await grt.connect(me).approve(staking.address, toGRT('1')) - - // Should close by public - const tx = staking.connect(me).closeAllocation(allocationID, poi) - await expect(tx) - .emit(staking, 'AllocationClosed') - .withArgs( - indexer.address, - subgraphDeploymentID, - currentEpoch, - beforeAlloc.tokens, - allocationID, - me.address, - poi, - true, - ) - } else { - // closing by the public on a zero allocation is not authorized - const tx = staking.connect(me).closeAllocation(allocationID, poi) - await expect(tx).revertedWith('!auth') - } - }) - - it('should close many allocations in batch', async function () { - // Setup a second allocation - await staking.connect(indexer).stake(tokensToStake) - const channelKey2 = deriveChannelKey() - const allocationID2 = channelKey2.address - await staking - .connect(indexer) - .allocate( - subgraphDeploymentID, - tokensToAllocate, - allocationID2, - metadata, - await channelKey2.generateProof(indexer.address), - ) - - // Move at least one epoch to be able to close - await helpers.mineEpoch(epochManager) - await helpers.mineEpoch(epochManager) - - // Close multiple allocations in one tx - const requests = await Promise.all( - [ - { - allocationID: allocationID, - poi: poi, - }, - { - allocationID: allocationID2, - poi: poi, - }, - ].map(({ allocationID, poi }) => - staking.connect(indexer).populateTransaction.closeAllocation(allocationID, poi), - ), - ).then((e) => e.map((e: PopulatedTransaction) => e.data)) - await staking.connect(indexer).multicall(requests) - }) - }) - } - }) - - describe('closeAndAllocate', function () { - beforeEach(async function () { - // Stake and allocate - await staking.connect(indexer).stake(tokensToAllocate) - await allocate(tokensToAllocate) - }) - - it('should close and create a new allocation', async function () { - // Move at least one epoch to be able to close - await helpers.mineEpoch(epochManager) - - // Close and allocate - const newChannelKey = deriveChannelKey() - const newAllocationID = newChannelKey.address - - // Close multiple allocations in one tx - const requests = await Promise.all([ - staking.connect(indexer).populateTransaction.closeAllocation(allocationID, poi), - staking - .connect(indexer) - .populateTransaction.allocateFrom( - indexer.address, - subgraphDeploymentID, - tokensToAllocate, - newAllocationID, - metadata, - await newChannelKey.generateProof(indexer.address), - ), - ]).then((e) => e.map((e: PopulatedTransaction) => e.data)) - await staking.connect(indexer).multicall(requests) - }) - }) -}) diff --git a/packages/contracts-test/tests/unit/staking/configuration.test.ts b/packages/contracts-test/tests/unit/staking/configuration.test.ts deleted file mode 100644 index 693e8648a..000000000 --- a/packages/contracts-test/tests/unit/staking/configuration.test.ts +++ /dev/null @@ -1,221 +0,0 @@ -import { IStaking } from '@graphprotocol/contracts' -import { GraphProxyAdmin } from '@graphprotocol/contracts' -import { deploy, DeployType, GraphNetworkContracts, toBN, toGRT } from '@graphprotocol/sdk' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { expect } from 'chai' -import { constants } from 'ethers' -import hre from 'hardhat' -import { ethers } from 'hardhat' - -import { NetworkFixture } from '../lib/fixtures' - -const { AddressZero } = constants - -const MAX_PPM = toBN('1000000') - -describe('Staking:Config', () => { - const graph = hre.graph() - - let me: SignerWithAddress - let other: SignerWithAddress - let governor: SignerWithAddress - - let fixture: NetworkFixture - - let contracts: GraphNetworkContracts - let staking: IStaking - let proxyAdmin: GraphProxyAdmin - - before(async function () { - ;[me, other] = await graph.getTestAccounts() - ;({ governor } = await graph.getNamedAccounts()) - - fixture = new NetworkFixture(graph.provider) - contracts = await fixture.load(governor) - staking = contracts.Staking as IStaking - proxyAdmin = contracts.GraphProxyAdmin as GraphProxyAdmin - }) - - beforeEach(async function () { - await fixture.setUp() - }) - - afterEach(async function () { - await fixture.tearDown() - }) - - describe('minimumIndexerStake', function () { - it('should set `minimumIndexerStake`', async function () { - const oldValue = toGRT('10') - const newValue = toGRT('100') - - // Set right in the constructor - expect(await staking.minimumIndexerStake()).eq(oldValue) - - // Set new value - await staking.connect(governor).setMinimumIndexerStake(newValue) - expect(await staking.minimumIndexerStake()).eq(newValue) - }) - - it('reject set `minimumIndexerStake` if not allowed', async function () { - const newValue = toGRT('100') - const tx = staking.connect(me).setMinimumIndexerStake(newValue) - await expect(tx).revertedWith('Only Controller governor') - }) - - it('reject set `minimumIndexerStake` to zero', async function () { - const tx = staking.connect(governor).setMinimumIndexerStake(0) - await expect(tx).revertedWith('!minimumIndexerStake') - }) - }) - - describe('setSlasher', function () { - it('should set `slasher`', async function () { - expect(await staking.slashers(me.address)).eq(false) - - await staking.connect(governor).setSlasher(me.address, true) - expect(await staking.slashers(me.address)).eq(true) - - await staking.connect(governor).setSlasher(me.address, false) - expect(await staking.slashers(me.address)).eq(false) - }) - - it('reject set `slasher` if not allowed', async function () { - const tx = staking.connect(other).setSlasher(me.address, true) - await expect(tx).revertedWith('Only Controller governor') - }) - - it('reject set `slasher` for zero', async function () { - const tx = staking.connect(governor).setSlasher(AddressZero, true) - await expect(tx).revertedWith('!slasher') - }) - }) - - describe('curationPercentage', function () { - it('should set `curationPercentage`', async function () { - const newValue = toBN('5') - await staking.connect(governor).setCurationPercentage(newValue) - expect(await staking.curationPercentage()).eq(newValue) - }) - - it('reject set `curationPercentage` if out of bounds', async function () { - const newValue = MAX_PPM.add(toBN('1')) - const tx = staking.connect(governor).setCurationPercentage(newValue) - await expect(tx).revertedWith('>percentage') - }) - - it('reject set `curationPercentage` if not allowed', async function () { - const tx = staking.connect(other).setCurationPercentage(50) - await expect(tx).revertedWith('Only Controller governor') - }) - }) - - describe('protocolPercentage', function () { - it('should set `protocolPercentage`', async function () { - for (const newValue of [toBN('0'), toBN('5'), MAX_PPM]) { - await staking.connect(governor).setProtocolPercentage(newValue) - expect(await staking.protocolPercentage()).eq(newValue) - } - }) - - it('reject set `protocolPercentage` if out of bounds', async function () { - const newValue = MAX_PPM.add(toBN('1')) - const tx = staking.connect(governor).setProtocolPercentage(newValue) - await expect(tx).revertedWith('>percentage') - }) - - it('reject set `protocolPercentage` if not allowed', async function () { - const tx = staking.connect(other).setProtocolPercentage(50) - await expect(tx).revertedWith('Only Controller governor') - }) - }) - - describe('maxAllocationEpochs', function () { - it('should set `maxAllocationEpochs`', async function () { - const newValue = toBN('5') - await staking.connect(governor).setMaxAllocationEpochs(newValue) - expect(await staking.maxAllocationEpochs()).eq(newValue) - }) - - it('reject set `maxAllocationEpochs` if not allowed', async function () { - const newValue = toBN('5') - const tx = staking.connect(other).setMaxAllocationEpochs(newValue) - await expect(tx).revertedWith('Only Controller governor') - }) - }) - - describe('thawingPeriod', function () { - it('should set `thawingPeriod`', async function () { - const newValue = toBN('5') - await staking.connect(governor).setThawingPeriod(newValue) - expect(await staking.thawingPeriod()).eq(newValue) - }) - - it('reject set `thawingPeriod` if not allowed', async function () { - const newValue = toBN('5') - const tx = staking.connect(other).setThawingPeriod(newValue) - await expect(tx).revertedWith('Only Controller governor') - }) - - it('reject set `thawingPeriod` to zero', async function () { - const tx = staking.connect(governor).setThawingPeriod(0) - await expect(tx).revertedWith('!thawingPeriod') - }) - }) - - describe('rebateParameters', function () { - it('should be setup on init', async function () { - expect(await staking.alphaNumerator()).eq(toBN(100)) - expect(await staking.alphaDenominator()).eq(toBN(100)) - expect(await staking.lambdaNumerator()).eq(toBN(60)) - expect(await staking.lambdaDenominator()).eq(toBN(100)) - }) - - it('should set `rebateParameters`', async function () { - await staking.connect(governor).setRebateParameters(5, 6, 7, 8) - expect(await staking.alphaNumerator()).eq(toBN(5)) - expect(await staking.alphaDenominator()).eq(toBN(6)) - expect(await staking.lambdaNumerator()).eq(toBN(7)) - expect(await staking.lambdaDenominator()).eq(toBN(8)) - }) - - it('reject set `rebateParameters` if out of bounds', async function () { - const tx2 = staking.connect(governor).setRebateParameters(1, 0, 1, 1) - await expect(tx2).revertedWith('!alphaDenominator') - - const tx3 = staking.connect(governor).setRebateParameters(1, 1, 0, 1) - await expect(tx3).revertedWith('!lambdaNumerator') - - const tx4 = staking.connect(governor).setRebateParameters(1, 1, 1, 0) - await expect(tx4).revertedWith('!lambdaDenominator') - }) - - it('reject set `rebateParameters` if not allowed', async function () { - const tx = staking.connect(other).setRebateParameters(1, 1, 1, 1) - await expect(tx).revertedWith('Only Controller governor') - }) - }) - - describe('Staking and StakingExtension', function () { - it('does not allow calling the fallback from the Staking implementation', async function () { - const impl = await proxyAdmin.getProxyImplementation(staking.address) - - const factory = await ethers.getContractFactory('contracts/staking/StakingExtension.sol:StakingExtension') - const implAsStaking = factory.attach(impl) as IStaking - const tx = implAsStaking.connect(other).setDelegationRatio(50) - await expect(tx).revertedWith('only through proxy') - }) - it('can set the staking extension implementation with setExtensionImpl', async function () { - const newImpl = await deploy(DeployType.Deploy, governor, { - name: 'StakingExtension', - }) - const tx = await staking.connect(governor).setExtensionImpl(newImpl.contract.address) - await expect(tx).emit(staking, 'ExtensionImplementationSet').withArgs(newImpl.contract.address) - }) - it('rejects calls to setExtensionImpl from non-governor', async function () { - const newImpl = await deploy(DeployType.Deploy, governor, { name: 'StakingExtension' }) - const tx = staking.connect(other).setExtensionImpl(newImpl.contract.address) - await expect(tx).revertedWith('Only Controller governor') - }) - }) -}) diff --git a/packages/contracts-test/tests/unit/staking/delegation.test.ts b/packages/contracts-test/tests/unit/staking/delegation.test.ts deleted file mode 100644 index 3542e817e..000000000 --- a/packages/contracts-test/tests/unit/staking/delegation.test.ts +++ /dev/null @@ -1,577 +0,0 @@ -import { EpochManager, IRewardsManager } from '@graphprotocol/contracts' -import { GraphToken } from '@graphprotocol/contracts' -import { IStaking } from '@graphprotocol/contracts' -import { deriveChannelKey, GraphNetworkContracts, helpers, randomHexBytes, toBN, toGRT } from '@graphprotocol/sdk' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { expect } from 'chai' -import { BigNumber, constants } from 'ethers' -import hre from 'hardhat' - -import { NetworkFixture } from '../lib/fixtures' - -const { AddressZero, HashZero } = constants -const MAX_PPM = toBN('1000000') -const tokensToCollect = toGRT('50000000000000000000') - -describe('Staking::Delegation', () => { - const graph = hre.graph() - let me: SignerWithAddress - let delegator: SignerWithAddress - let delegator2: SignerWithAddress - let governor: SignerWithAddress - let indexer: SignerWithAddress - let indexer2: SignerWithAddress - let assetHolder: SignerWithAddress - - let fixture: NetworkFixture - - let contracts: GraphNetworkContracts - let epochManager: EpochManager - let grt: GraphToken - let staking: IStaking - let rewardsManager: IRewardsManager - - // Test values - const poi = randomHexBytes() - const metadata = HashZero - - async function shouldDelegate(sender: SignerWithAddress, tokens: BigNumber) { - // Before state - const beforeTotalSupply = await grt.totalSupply() - const beforePool = await staking.delegationPools(indexer.address) - const beforeDelegation = await staking.getDelegation(indexer.address, sender.address) - const beforeShares = beforeDelegation.shares - const beforeTokens = beforePool.shares.gt(0) ? beforeShares.mul(beforePool.tokens).div(beforePool.shares) : toBN(0) - - // Get current delegation tax percentage for deposits - const delegationTaxPercentage = BigNumber.from(await staking.delegationTaxPercentage()) - const delegationTax = delegationTaxPercentage.mul(tokens).div(MAX_PPM) - const delegatedTokens = tokens.sub(delegationTax) - - // Calculate shares to receive - const shares = beforePool.tokens.eq(toBN('0')) - ? delegatedTokens - : delegatedTokens.mul(beforePool.tokens).div(beforePool.shares) - - // Delegate - const tx = staking.connect(sender).delegate(indexer.address, tokens) - await expect(tx).emit(staking, 'StakeDelegated').withArgs(indexer.address, sender.address, delegatedTokens, shares) - - // After state - const afterTotalSupply = await grt.totalSupply() - const afterPool = await staking.delegationPools(indexer.address) - const afterDelegation = await staking.getDelegation(indexer.address, sender.address) - const afterShares = afterDelegation.shares - const afterTokens = afterPool.shares.gt(0) ? afterShares.mul(afterPool.tokens).div(afterPool.shares) : toBN(0) - - // State updated - expect(afterPool.tokens).eq(beforePool.tokens.add(delegatedTokens)) - expect(afterPool.shares).eq(beforePool.shares.add(shares)) - expect(afterShares).eq(beforeShares.add(shares)) - expect(afterTokens).eq(beforeTokens.add(delegatedTokens)) - expect(afterTotalSupply).eq(beforeTotalSupply.sub(delegationTax)) - } - - async function shouldUndelegate(sender: SignerWithAddress, shares: BigNumber) { - // Before state - const beforePool = await staking.delegationPools(indexer.address) - const beforeDelegation = await staking.getDelegation(indexer.address, sender.address) - const beforeShares = beforeDelegation.shares - const beforeTokens = beforePool.shares.gt(0) ? beforeShares.mul(beforePool.tokens).div(beforePool.shares) : toBN(0) - const beforeDelegatorBalance = await grt.balanceOf(sender.address) - const tokensToWithdraw = await staking.getWithdraweableDelegatedTokens(beforeDelegation) - - // Calculate tokens to receive - const tokens = shares.mul(beforePool.shares).div(beforePool.tokens) - - // Undelegate - const currentEpoch = await epochManager.currentEpoch() - const delegationUnbondingPeriod = await staking.delegationUnbondingPeriod() - const tokensLockedUntil = currentEpoch.add(delegationUnbondingPeriod) - - const tx = staking.connect(sender).undelegate(indexer.address, shares) - await expect(tx) - .emit(staking, 'StakeDelegatedLocked') - .withArgs(indexer.address, sender.address, tokens, shares, tokensLockedUntil) - - // After state - const afterPool = await staking.delegationPools(indexer.address) - const afterDelegation = await staking.getDelegation(indexer.address, sender.address) - const afterShares = afterDelegation.shares - const afterTokens = afterPool.shares.gt(0) ? afterShares.mul(afterPool.tokens).div(afterPool.shares) : toBN(0) - const afterDelegatorBalance = await grt.balanceOf(sender.address) - - // State updated - expect(afterPool.tokens).eq(beforePool.tokens.sub(tokens)) - expect(afterPool.shares).eq(beforePool.shares.sub(shares)) - expect(afterShares).eq(beforeShares.sub(shares)) - expect(afterTokens).eq(beforeTokens.sub(tokens)) - - // Undelegated funds must be put on lock - expect(afterDelegation.tokensLocked).eq(beforeDelegation.tokensLocked.add(tokens).sub(tokensToWithdraw)) - expect(afterDelegation.tokensLockedUntil).eq(tokensLockedUntil) - // Delegator see balance increased only if there were tokens to withdraw - expect(afterDelegatorBalance).eq(beforeDelegatorBalance.add(tokensToWithdraw)) - } - - async function shouldWithdrawDelegated(sender: SignerWithAddress, redelegateTo: string, tokens: BigNumber) { - // Before state - const beforePool = await staking.delegationPools(indexer2.address) - const beforeDelegation = await staking.getDelegation(indexer2.address, sender.address) - const beforeShares = beforeDelegation.shares - const beforeTokens = beforePool.shares.gt(0) ? beforeShares.mul(beforePool.tokens).div(beforePool.shares) : toBN(0) - const beforeBalance = await grt.balanceOf(delegator.address) - - // Calculate shares to receive - const shares = beforePool.tokens.eq(toBN('0')) ? tokens : tokens.mul(beforePool.tokens).div(beforePool.shares) - - // Withdraw - const tx = staking.connect(delegator).withdrawDelegated(indexer.address, redelegateTo) - await expect(tx).emit(staking, 'StakeDelegatedWithdrawn').withArgs(indexer.address, delegator.address, tokens) - - // After state - const afterPool = await staking.delegationPools(indexer2.address) - const afterDelegation = await staking.getDelegation(indexer2.address, sender.address) - const afterShares = afterDelegation.shares - const afterTokens = afterPool.shares.gt(0) ? afterShares.mul(afterPool.tokens).div(afterPool.shares) : toBN(0) - const afterBalance = await grt.balanceOf(delegator.address) - - // State updated - expect(afterDelegation.tokensLocked).eq(toGRT('0')) - expect(afterDelegation.tokensLockedUntil).eq(toBN('0')) - // Redelegation vs transfer - if (redelegateTo === AddressZero) { - expect(afterBalance).eq(beforeBalance.add(tokens)) - } else { - expect(afterBalance).eq(beforeBalance) - expect(afterPool.tokens).eq(beforePool.tokens.add(tokens)) - expect(afterPool.shares).eq(beforePool.shares.add(shares)) - expect(afterShares).eq(beforeShares.add(shares)) - expect(afterTokens).eq(beforeTokens.add(tokens)) - } - } - - before(async function () { - ;[me, delegator, delegator2, governor, indexer, indexer2, assetHolder] = await graph.getTestAccounts() - ;({ governor } = await graph.getNamedAccounts()) - - fixture = new NetworkFixture(graph.provider) - contracts = await fixture.load(governor) - epochManager = contracts.EpochManager as EpochManager - grt = contracts.GraphToken as GraphToken - staking = contracts.Staking as IStaking - rewardsManager = contracts.RewardsManager as IRewardsManager - - // Distribute test funds - for (const wallet of [delegator, delegator2]) { - await grt.connect(governor).mint(wallet.address, toGRT('10000000000000000000')) - await grt.connect(wallet).approve(staking.address, toGRT('10000000000000000000')) - } - - // Distribute test funds - for (const wallet of [me, indexer, indexer2]) { - await grt.connect(governor).mint(wallet.address, toGRT('1000000')) - await grt.connect(wallet).approve(staking.address, toGRT('1000000')) - } - await grt.connect(governor).mint(assetHolder.address, tokensToCollect) - await grt.connect(assetHolder).approve(staking.address, tokensToCollect) - - // HACK: we set the staking contract as the subgraph service to make tests pass. - // This is due to the test suite being outdated. - await rewardsManager.connect(governor).setSubgraphService(staking.address) - }) - - beforeEach(async function () { - await fixture.setUp() - }) - - afterEach(async function () { - await fixture.tearDown() - }) - - describe('configuration', function () { - describe('delegationRatio', function () { - const delegationRatio = 5 - - it('should set `delegationRatio`', async function () { - await staking.connect(governor).setDelegationRatio(delegationRatio) - expect(await staking.delegationRatio()).eq(delegationRatio) - }) - - it('reject set `delegationRatio` if not allowed', async function () { - const tx = staking.connect(me).setDelegationRatio(delegationRatio) - await expect(tx).revertedWith('Only Controller governor') - }) - }) - - describe('delegationTaxPercentage', function () { - it('should set `delegationTaxPercentage`', async function () { - for (const newValue of [toBN('0'), toBN('5'), MAX_PPM]) { - await staking.connect(governor).setDelegationTaxPercentage(newValue) - expect(await staking.delegationTaxPercentage()).eq(newValue) - } - }) - - it('reject set `delegationTaxPercentage` if out of bounds', async function () { - const newValue = MAX_PPM.add(toBN('1')) - const tx = staking.connect(governor).setDelegationTaxPercentage(newValue) - await expect(tx).revertedWith('>percentage') - }) - - it('reject set `delegationTaxPercentage` if not allowed', async function () { - const tx = staking.connect(me).setDelegationTaxPercentage(50) - await expect(tx).revertedWith('Only Controller governor') - }) - }) - - describe('delegationParameters', function () { - const indexingRewardCut = toBN('50000') - const queryFeeCut = toBN('80000') - - it('reject to set parameters out of bound', async function () { - // Indexing reward out of bounds - const tx1 = staking.connect(indexer).setDelegationParameters(MAX_PPM.add('1'), queryFeeCut, 0) - await expect(tx1).revertedWith('>indexingRewardCut') - - // Query fee out of bounds - const tx2 = staking.connect(indexer).setDelegationParameters(indexingRewardCut, MAX_PPM.add('1'), 0) - await expect(tx2).revertedWith('>queryFeeCut') - }) - - it('should set parameters', async function () { - // Set parameters - const tx = staking.connect(indexer).setDelegationParameters(indexingRewardCut, queryFeeCut, 0) - await expect(tx) - .emit(staking, 'DelegationParametersUpdated') - .withArgs(indexer.address, indexingRewardCut, queryFeeCut, 0) - - // State updated - const params = await staking.delegationPools(indexer.address) - expect(params.indexingRewardCut).eq(indexingRewardCut) - expect(params.queryFeeCut).eq(queryFeeCut) - expect(params.updatedAtBlock).eq(await helpers.latestBlock()) - }) - - it('should init delegation parameters on first stake', async function () { - // Before - const beforeParams = await staking.delegationPools(indexer.address) - expect(beforeParams.indexingRewardCut).eq(0) - expect(beforeParams.queryFeeCut).eq(0) - expect(beforeParams.updatedAtBlock).eq(0) - - // Indexer stake tokens - const tx = staking.connect(indexer).stake(toGRT('200')) - await expect(tx).emit(staking, 'DelegationParametersUpdated').withArgs(indexer.address, MAX_PPM, MAX_PPM, 0) - - // State updated - const afterParams = await staking.delegationPools(indexer.address) - expect(afterParams.indexingRewardCut).eq(MAX_PPM) - expect(afterParams.queryFeeCut).eq(MAX_PPM) - expect(afterParams.updatedAtBlock).eq(await helpers.latestBlock()) - }) - - it('should init delegation parameters on first stake using stakeTo()', async function () { - // Before - const beforeParams = await staking.delegationPools(indexer.address) - expect(beforeParams.indexingRewardCut).eq(0) - expect(beforeParams.queryFeeCut).eq(0) - expect(beforeParams.updatedAtBlock).eq(0) - - // Indexer stake tokens - const tx = staking.connect(me).stakeTo(indexer.address, toGRT('200')) - await expect(tx).emit(staking, 'DelegationParametersUpdated').withArgs(indexer.address, MAX_PPM, MAX_PPM, 0) - - // State updated - const afterParams = await staking.delegationPools(indexer.address) - expect(afterParams.indexingRewardCut).eq(MAX_PPM) - expect(afterParams.queryFeeCut).eq(MAX_PPM) - expect(afterParams.updatedAtBlock).eq(await helpers.latestBlock()) - }) - }) - }) - - describe('lifecycle', function () { - beforeEach(async function () { - // Stake some funds as indexer - await staking.connect(indexer).stake(toGRT('1000')) - }) - - describe('delegate', function () { - it('reject delegate with zero tokens', async function () { - const tokensToDelegate = toGRT('0') - const tx = staking.connect(delegator).delegate(indexer.address, tokensToDelegate) - await expect(tx).revertedWith('!minimum-delegation') - }) - - it('reject delegate with less than 1 GRT when the pool is not initialized', async function () { - const tokensToDelegate = toGRT('0.5') - const tx = staking.connect(delegator).delegate(indexer.address, tokensToDelegate) - await expect(tx).revertedWith('!minimum-delegation') - }) - - it('reject delegating under 1 GRT when the pool is initialized', async function () { - await shouldDelegate(delegator, toGRT('1')) - const tokensToDelegate = toGRT('0.5') - const tx = staking.connect(delegator).delegate(indexer.address, tokensToDelegate) - await expect(tx).revertedWith('!minimum-delegation') - }) - - it('reject delegate to empty address', async function () { - const tokensToDelegate = toGRT('100') - const tx = staking.connect(delegator).delegate(AddressZero, tokensToDelegate) - await expect(tx).revertedWith('!indexer') - }) - - it('reject delegate to non-staked indexer', async function () { - const tokensToDelegate = toGRT('100') - const tx = staking.connect(delegator).delegate(me.address, tokensToDelegate) - await expect(tx).revertedWith('!stake') - }) - - it('should delegate tokens and account shares proportionally', async function () { - // Multiple delegations should work - await shouldDelegate(delegator, toGRT('1234')) - await shouldDelegate(delegator, toGRT('100')) - await shouldDelegate(delegator, toGRT('50')) - await shouldDelegate(delegator, toGRT('25')) - await shouldDelegate(delegator, toGRT('10')) - await shouldDelegate(delegator, toGRT('1')) - - // Delegation by other delegator - await shouldDelegate(delegator2, toGRT('5000')) - }) - - it('should delegate a high amount of tokens', async function () { - await shouldDelegate(delegator, toGRT('100')) - await shouldDelegate(delegator, toGRT('1000000000000000000')) - }) - - describe('delegation tax', function () { - it('should delegate and burn delegation deposit tax (0.0001%)', async function () { - await staking.connect(governor).setDelegationTaxPercentage(1) - await shouldDelegate(delegator, toGRT('10000000')) - }) - - it('should delegate and burn delegation deposit tax (1%)', async function () { - await staking.connect(governor).setDelegationTaxPercentage(10000) - await shouldDelegate(delegator, toGRT('10000000')) - }) - - it('reject delegate with delegation deposit tax (100%)', async function () { - await staking.connect(governor).setDelegationTaxPercentage(1000000) - const tx = staking.connect(delegator).delegate(indexer.address, toGRT('10000000')) - await expect(tx).revertedWith('!shares') - }) - }) - }) - - describe('undelegate', function () { - it('reject to undelegate zero shares', async function () { - const tx = staking.connect(delegator).undelegate(indexer.address, toGRT('0')) - await expect(tx).revertedWith('!shares') - }) - - it('reject to undelegate more shares than owned', async function () { - const tx = staking.connect(delegator).undelegate(indexer.address, toGRT('100')) - await expect(tx).revertedWith('!shares-avail') - }) - - it('should exchange delegation pool shares for tokens', async function () { - const tokens = toGRT('100') - - // Have two parties that delegated tokens to the same indexer - await shouldDelegate(delegator, tokens) - await shouldDelegate(delegator2, tokens) - - // Delegate half of the delegated funds - await shouldUndelegate(delegator, tokens.div(toBN('2'))) - }) - - it('should undelegate properly when multiple delegations', async function () { - this.timeout(60000) // increase timeout for test runner - - // Use long enough epochs to avoid jumping to the next epoch involuntarily on our test - await epochManager.connect(governor).setEpochLength(toBN((60 * 60) / 15)) - - await shouldDelegate(delegator, toGRT('1234')) - await shouldDelegate(delegator, toGRT('100')) - await shouldDelegate(delegator, toGRT('50')) - await shouldDelegate(delegator2, toGRT('50')) - - await shouldUndelegate(delegator, toGRT('1')) - await shouldUndelegate(delegator2, toGRT('50')) - await helpers.mineEpoch(epochManager) - await shouldUndelegate(delegator, toGRT('25')) - }) - - it('should undelegate and withdraw freed tokens from unbonding period', async function () { - await staking.connect(governor).setDelegationUnbondingPeriod('2') - await shouldDelegate(delegator, toGRT('100')) - await shouldUndelegate(delegator, toGRT('50')) - await helpers.mineEpoch(epochManager) // epoch 1 - await helpers.mineEpoch(epochManager) // epoch 2 - await shouldUndelegate(delegator, toGRT('10')) - }) - - it('reject undelegate if remaining tokens are less than the minimum', async function () { - await shouldDelegate(delegator, toGRT('100')) - const tx = staking.connect(delegator).undelegate(indexer.address, toGRT('99.5')) - await expect(tx).revertedWith('!minimum-delegation') - }) - }) - - describe('withdraw', function () { - it('reject withdraw if no funds available', async function () { - const tx = staking.connect(delegator).withdrawDelegated(indexer.address, AddressZero) - await expect(tx).revertedWith('!tokens') - }) - - it('reject withdraw before unbonding period', async function () { - await staking.connect(governor).setDelegationUnbondingPeriod('2') - await shouldDelegate(delegator, toGRT('1000')) - await shouldUndelegate(delegator, toGRT('100')) - - // Withdraw - const tx = staking.connect(delegator).withdrawDelegated(indexer.address, AddressZero) - await expect(tx).revertedWith('!tokens') - }) - - it('should withdraw after waiting an unbonding period', async function () { - const tokensToWithdraw = toGRT('100') - - // Setup - await staking.connect(governor).setDelegationUnbondingPeriod('2') - await shouldDelegate(delegator, toGRT('1000')) - await shouldUndelegate(delegator, tokensToWithdraw) - await helpers.mineEpoch(epochManager) // epoch 1 - await helpers.mineEpoch(epochManager) // epoch 2 - - // Withdraw - await shouldWithdrawDelegated(delegator, AddressZero, tokensToWithdraw) - }) - - it('should withdraw after waiting an unbonding period (with redelegation)', async function () { - const tokensToWithdraw = toGRT('100') - - // Setup - await staking.connect(governor).setDelegationUnbondingPeriod('2') - await shouldDelegate(delegator, toGRT('1000')) - await shouldUndelegate(delegator, tokensToWithdraw) - await helpers.mineEpoch(epochManager) // epoch 1 - await helpers.mineEpoch(epochManager) // epoch 2 - - // We stake on indexer2 so the delegator is able to re-delegate to it - // if we didn't do it, it will revert because of indexer2 not havings stake - await staking.connect(indexer2).stake(toGRT('1000')) - // Withdraw - await shouldWithdrawDelegated(delegator, indexer2.address, tokensToWithdraw) - }) - }) - }) - - describe('use of delegated funds', function () { - // Test values - const tokensToStake = toGRT('200') - const tokensToAllocate = toGRT('2000') - const tokensToDelegate = toGRT('1800') - const subgraphDeploymentID = randomHexBytes() - const channelKey = deriveChannelKey() - const allocationID = channelKey.address - - const setupAllocation = async (tokens: BigNumber) => { - return staking - .connect(indexer) - .allocateFrom( - indexer.address, - subgraphDeploymentID, - tokens, - allocationID, - metadata, - await channelKey.generateProof(indexer.address), - ) - } - - beforeEach(async function () { - // Indexer stake tokens - await staking.connect(indexer).stake(tokensToStake) - }) - - it('revert allocate when capacity is not enough', async function () { - // 1:2 delegation capacity - await staking.connect(governor).setDelegationRatio(2) - - // Delegate - await staking.connect(delegator).delegate(indexer.address, tokensToDelegate) - - // Staked: 200 - // Delegated: 1800 - // Capacity: 200 + min(200*2, 1800) = 600 - const tx = setupAllocation(tokensToAllocate) - await expect(tx).revertedWith('!capacity') - }) - - it('should allocate using full delegation capacity', async function () { - // 1:10 delegation capacity - await staking.connect(governor).setDelegationRatio(10) - - // Delegate - await staking.connect(delegator).delegate(indexer.address, tokensToDelegate) - - // Staked: 200 - // Delegated: 1800 - // Capacity: 200 + min(200*10, 1800) = 2000 - await setupAllocation(tokensToAllocate) - - // State updated - const alloc = await staking.getAllocation(allocationID) - expect(alloc.tokens).eq(tokensToAllocate) - }) - - it('should account delegation for indexer capacity properly', async function () { - // 1:10 delegation capacity - await staking.connect(governor).setDelegationRatio(10) - - // Delegate - await staking.connect(delegator).delegate(indexer.address, tokensToDelegate) - - // If we unstake all, the indexer capacity should go to zero - // Should not be able to use delegated tokens - await staking.connect(indexer).unstake(tokensToStake) - expect(await staking.getIndexerCapacity(indexer.address)).eq(0) - }) - - it('revert if it cannot assign the smallest amount of shares', async function () { - // Init the delegation pool - await shouldDelegate(delegator, toGRT('1')) - const tokensToAllocate = toGRT('1') - // Collect funds thru full allocation cycle - // Set rebate alpha to 0 to ensure all fees are collected - await staking.connect(governor).setRebateParameters(0, 1, 1, 1) - await staking.connect(governor).setDelegationRatio(10) - await staking.connect(indexer).setDelegationParameters(0, 0, 0) - await setupAllocation(tokensToAllocate) - await helpers.mineEpoch(epochManager) - await staking.connect(assetHolder).collect(tokensToCollect, allocationID) - await helpers.mineEpoch(epochManager) - await staking.connect(indexer).closeAllocation(allocationID, poi) - // We've callected 5e18 GRT (a ridiculous amount), - // which means the price of a share is now 5 GRT - - // Delegate with such small amount of tokens (1 GRT) that we do not have enough precision - // to even assign 1 share - const tx = staking.connect(delegator).delegate(indexer.address, toGRT('1')) - await expect(tx).revertedWith('!shares') - }) - }) - describe('isDelegator', function () { - it('should return true if the address is a delegator', async function () { - await staking.connect(indexer).stake(toGRT('1000')) - await shouldDelegate(delegator, toGRT('1')) - expect(await staking.isDelegator(indexer.address, delegator.address)).eq(true) - }) - - it('should return false if the address is not a delegator', async function () { - expect(await staking.isDelegator(indexer.address, delegator.address)).eq(false) - }) - }) -}) diff --git a/packages/contracts-test/tests/unit/staking/l2Transfer.test.ts b/packages/contracts-test/tests/unit/staking/l2Transfer.test.ts deleted file mode 100644 index 39a5d878f..000000000 --- a/packages/contracts-test/tests/unit/staking/l2Transfer.test.ts +++ /dev/null @@ -1,736 +0,0 @@ -import { GraphToken } from '@graphprotocol/contracts' -import { IL1Staking } from '@graphprotocol/contracts' -import { IController } from '@graphprotocol/contracts' -import { L1GraphTokenGateway } from '@graphprotocol/contracts' -import { L1GraphTokenLockTransferToolMock } from '@graphprotocol/contracts' -import { L1GraphTokenLockTransferToolBadMock } from '@graphprotocol/contracts' -import { L2GraphTokenGateway, L2Staking } from '@graphprotocol/contracts' -import { - deploy, - DeployType, - deriveChannelKey, - GraphNetworkContracts, - helpers, - randomHexBytes, - toBN, - toGRT, -} from '@graphprotocol/sdk' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { expect } from 'chai' -import { BigNumber, constants } from 'ethers' -import { defaultAbiCoder, parseEther } from 'ethers/lib/utils' -import hre from 'hardhat' - -import { NetworkFixture } from '../lib/fixtures' - -const { AddressZero } = constants - -describe('L1Staking:L2Transfer', () => { - const graph = hre.graph() - let governor: SignerWithAddress - let indexer: SignerWithAddress - let l2Indexer: SignerWithAddress - let delegator: SignerWithAddress - let l2Delegator: SignerWithAddress - - let fixture: NetworkFixture - let fixtureContracts: GraphNetworkContracts - let l2MockContracts: GraphNetworkContracts - - let l2StakingMock: L2Staking - let l2GRTGatewayMock: L2GraphTokenGateway - - let grt: GraphToken - let staking: IL1Staking - let controller: IController - let l1GraphTokenGateway: L1GraphTokenGateway - let l1GraphTokenLockTransferTool: L1GraphTokenLockTransferToolMock - let l1GraphTokenLockTransferToolBad: L1GraphTokenLockTransferToolBadMock - - // Test values - const indexerTokens = toGRT('10000000') - const delegatorTokens = toGRT('1000000') - const tokensToStake = toGRT('200000') - const subgraphDeploymentID = randomHexBytes() - const channelKey = deriveChannelKey() - const allocationID = channelKey.address - const metadata = randomHexBytes(32) - const minimumIndexerStake = toGRT('100000') - const delegationTaxPPM = 10000 // 1% - // Dummy L2 gas values - const maxGas = toBN('1000000') - const gasPriceBid = toBN('1000000000') - const maxSubmissionCost = toBN('1000000000') - - // Allocate with test values - const allocate = async (tokens: BigNumber) => { - return staking - .connect(indexer) - .allocateFrom( - indexer.address, - subgraphDeploymentID, - tokens, - allocationID, - metadata, - await channelKey.generateProof(indexer.address), - ) - } - - before(async function () { - ;[indexer, delegator, l2Indexer, l2Delegator] = await graph.getTestAccounts() - ;({ governor } = await graph.getNamedAccounts()) - - fixture = new NetworkFixture(graph.provider) - - // Deploy L1 - fixtureContracts = await fixture.load(governor) - grt = fixtureContracts.GraphToken as GraphToken - staking = fixtureContracts.L1Staking as unknown as IL1Staking - l1GraphTokenGateway = fixtureContracts.L1GraphTokenGateway as L1GraphTokenGateway - controller = fixtureContracts.Controller as IController - - // Deploy L1 arbitrum bridge - await fixture.loadL1ArbitrumBridge(governor) - - // Deploy L2 mock - l2MockContracts = await fixture.loadMock(true) - l2StakingMock = l2MockContracts.L2Staking as L2Staking - l2GRTGatewayMock = l2MockContracts.L2GraphTokenGateway as L2GraphTokenGateway - - // Configure graph bridge - await fixture.configureL1Bridge(governor, fixtureContracts, l2MockContracts) - - l1GraphTokenLockTransferTool = ( - await deploy(DeployType.Deploy, governor, { - name: 'L1GraphTokenLockTransferToolMock', - }) - ).contract as L1GraphTokenLockTransferToolMock - - l1GraphTokenLockTransferToolBad = ( - await deploy(DeployType.Deploy, governor, { name: 'L1GraphTokenLockTransferToolBadMock' }) - ).contract as L1GraphTokenLockTransferToolBadMock - - await helpers.setBalances([ - { address: l1GraphTokenLockTransferTool.address, balance: parseEther('1') }, - { address: l1GraphTokenLockTransferToolBad.address, balance: parseEther('1') }, - ]) - - await staking.connect(governor).setL1GraphTokenLockTransferTool(l1GraphTokenLockTransferTool.address) - - // Give some funds to the indexer and approve staking contract to use funds on indexer behalf - await grt.connect(governor).mint(indexer.address, indexerTokens) - await grt.connect(indexer).approve(staking.address, indexerTokens) - - await grt.connect(governor).mint(delegator.address, delegatorTokens) - await grt.connect(delegator).approve(staking.address, delegatorTokens) - - await staking.connect(governor).setMinimumIndexerStake(minimumIndexerStake) - await staking.connect(governor).setDelegationTaxPercentage(delegationTaxPPM) // 1% - }) - - beforeEach(async function () { - await fixture.setUp() - }) - - afterEach(async function () { - await fixture.tearDown() - }) - - context('> when not staked', function () { - describe('transferStakeToL2', function () { - it('should not allow transferring for someone who has not staked', async function () { - const tx = staking - .connect(indexer) - .transferStakeToL2(l2Indexer.address, tokensToStake, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - await expect(tx).revertedWith('tokensStaked == 0') - }) - }) - }) - - context('> when staked', function () { - const shouldTransferIndexerStake = async ( - amountToSend: BigNumber, - options: { - expectedSeqNum?: number - l2Beneficiary?: string - } = {}, - ) => { - const l2Beneficiary = options.l2Beneficiary ?? l2Indexer.address - const expectedSeqNum = options.expectedSeqNum ?? 1 - const tx = staking - .connect(indexer) - .transferStakeToL2(l2Beneficiary, amountToSend, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - const expectedFunctionData = defaultAbiCoder.encode(['tuple(address)'], [[l2Indexer.address]]) - - const expectedCallhookData = defaultAbiCoder.encode( - ['uint8', 'bytes'], - [toBN(0), expectedFunctionData], // code = 1 means RECEIVE_INDEXER_CODE - ) - const expectedL2Data = await l1GraphTokenGateway.getOutboundCalldata( - grt.address, - staking.address, - l2StakingMock.address, - amountToSend, - expectedCallhookData, - ) - - await expect(tx) - .emit(l1GraphTokenGateway, 'TxToL2') - .withArgs(staking.address, l2GRTGatewayMock.address, toBN(expectedSeqNum), expectedL2Data) - } - - beforeEach(async function () { - await staking.connect(indexer).stake(tokensToStake) - }) - - describe('receive()', function () { - it('should not allow receiving funds from a random address', async function () { - const tx = indexer.sendTransaction({ - to: staking.address, - value: parseEther('1'), - }) - await expect(tx).revertedWith('Only transfer tool can send ETH') - }) - it('should allow receiving funds from the transfer tool', async function () { - const impersonatedTransferTool = await helpers.impersonateAccount(l1GraphTokenLockTransferTool.address) - - const tx = impersonatedTransferTool.sendTransaction({ - to: staking.address, - value: parseEther('1'), - }) - await expect(tx).to.not.be.reverted - }) - }) - describe('transferStakeToL2', function () { - it('should not allow transferring if the protocol is partially paused', async function () { - await controller.connect(governor).setPartialPaused(true) - - const tx = staking - .connect(indexer) - .transferStakeToL2( - l2Indexer.address, - tokensToStake.sub(minimumIndexerStake), - maxGas, - gasPriceBid, - maxSubmissionCost, - { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }, - ) - await expect(tx).revertedWith('Partial-paused') - }) - it('should not allow transferring but leaving less than the minimum indexer stake', async function () { - const tx = staking - .connect(indexer) - .transferStakeToL2( - l2Indexer.address, - tokensToStake.sub(minimumIndexerStake).add(1), - maxGas, - gasPriceBid, - maxSubmissionCost, - { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }, - ) - await expect(tx).revertedWith('!minimumIndexerStake remaining') - }) - it('should not allow transferring less than the minimum indexer stake the first time', async function () { - const tx = staking - .connect(indexer) - .transferStakeToL2(l2Indexer.address, minimumIndexerStake.sub(1), maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - await expect(tx).revertedWith('!minimumIndexerStake sent') - }) - it('should not allow transferring if there are tokens locked for withdrawal', async function () { - await staking.connect(indexer).unstake(tokensToStake) - const tx = staking - .connect(indexer) - .transferStakeToL2(l2Indexer.address, tokensToStake, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - await expect(tx).revertedWith('tokensLocked != 0') - }) - it('should not allow transferring to a beneficiary that is address zero', async function () { - const tx = staking - .connect(indexer) - .transferStakeToL2(AddressZero, tokensToStake, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - await expect(tx).revertedWith('l2Beneficiary == 0') - }) - it('should not allow transferring the whole stake if there are open allocations', async function () { - await allocate(toGRT('10')) - const tx = staking - .connect(indexer) - .transferStakeToL2(l2Indexer.address, tokensToStake, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - await expect(tx).revertedWith('allocated') - }) - it('should not allow transferring partial stake if the remaining indexer capacity is insufficient for open allocations', async function () { - // We set delegation ratio == 1 so an indexer can only use as much delegation as their own stake - await staking.connect(governor).setDelegationRatio(1) - const tokensToDelegate = toGRT('202100') - await staking.connect(delegator).delegate(indexer.address, tokensToDelegate) - - // Now the indexer has 200k tokens staked and 200k tokens delegated - await allocate(toGRT('400000')) - - // But if we try to transfer even 100k, we will not have enough indexer capacity to cover the open allocation - const tx = staking - .connect(indexer) - .transferStakeToL2(l2Indexer.address, toGRT('100000'), maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - await expect(tx).revertedWith('! allocation capacity') - }) - it('should not allow transferring if the ETH sent is more than required', async function () { - const tx = staking - .connect(indexer) - .transferStakeToL2(indexer.address, tokensToStake, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)).add(1), - }) - await expect(tx).revertedWith('INVALID_ETH_AMOUNT') - }) - it('sends the tokens and a message through the L1GraphTokenGateway', async function () { - const amountToSend = minimumIndexerStake - await shouldTransferIndexerStake(amountToSend) - // Check that the indexer stake was reduced by the sent amount - expect((await staking.stakes(indexer.address)).tokensStaked).to.equal(tokensToStake.sub(amountToSend)) - }) - it('should allow transferring the whole stake if there are no open allocations', async function () { - await shouldTransferIndexerStake(tokensToStake) - // Check that the indexer stake was reduced by the sent amount - expect((await staking.stakes(indexer.address)).tokensStaked).to.equal(0) - }) - it('should allow transferring partial stake if the remaining capacity can cover the allocations', async function () { - // We set delegation ratio == 1 so an indexer can only use as much delegation as their own stake - await staking.connect(governor).setDelegationRatio(1) - const tokensToDelegate = toGRT('200000') - await staking.connect(delegator).delegate(indexer.address, tokensToDelegate) - - // Now the indexer has 200k tokens staked and 200k tokens delegated, - // but they allocate 200k - await allocate(toGRT('200000')) - - // If we transfer 100k, we will still have enough indexer capacity to cover the open allocation - const amountToSend = toGRT('100000') - await shouldTransferIndexerStake(amountToSend) - // Check that the indexer stake was reduced by the sent amount - expect((await staking.stakes(indexer.address)).tokensStaked).to.equal(tokensToStake.sub(amountToSend)) - }) - it('allows transferring several times to the same beneficiary', async function () { - // Stake a bit more so we're still over the minimum stake after transferring twice - await staking.connect(indexer).stake(tokensToStake) - await shouldTransferIndexerStake(minimumIndexerStake) - await shouldTransferIndexerStake(toGRT('1000'), { expectedSeqNum: 2 }) - expect((await staking.stakes(indexer.address)).tokensStaked).to.equal( - tokensToStake.mul(2).sub(minimumIndexerStake).sub(toGRT('1000')), - ) - }) - it('should not allow transferring to a different beneficiary the second time', async function () { - await shouldTransferIndexerStake(minimumIndexerStake) - const tx = staking.connect(indexer).transferStakeToL2( - indexer.address, // Note this is different from l2Indexer used before - minimumIndexerStake, - maxGas, - gasPriceBid, - maxSubmissionCost, - { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }, - ) - await expect(tx).revertedWith('l2Beneficiary != previous') - }) - }) - - describe('transferLockedStakeToL2', function () { - it('should not allow transferring if the protocol is partially paused', async function () { - await controller.connect(governor).setPartialPaused(true) - - const tx = staking - .connect(indexer) - .transferLockedStakeToL2(minimumIndexerStake, maxGas, gasPriceBid, maxSubmissionCost) - await expect(tx).revertedWith('Partial-paused') - }) - it('sends a message through L1GraphTokenGateway like transferStakeToL2, but gets the beneficiary and ETH from a transfer tool contract', async function () { - const amountToSend = minimumIndexerStake - - await l1GraphTokenLockTransferTool.setL2WalletAddress(indexer.address, l2Indexer.address) - const oldTransferToolEthBalance = await graph.provider.getBalance(l1GraphTokenLockTransferTool.address) - const tx = staking - .connect(indexer) - .transferLockedStakeToL2(minimumIndexerStake, maxGas, gasPriceBid, maxSubmissionCost) - const expectedFunctionData = defaultAbiCoder.encode(['tuple(address)'], [[l2Indexer.address]]) - - const expectedCallhookData = defaultAbiCoder.encode( - ['uint8', 'bytes'], - [toBN(0), expectedFunctionData], // code = 0 means RECEIVE_INDEXER_CODE - ) - const expectedL2Data = await l1GraphTokenGateway.getOutboundCalldata( - grt.address, - staking.address, - l2StakingMock.address, - amountToSend, - expectedCallhookData, - ) - - await expect(tx) - .emit(l1GraphTokenGateway, 'TxToL2') - .withArgs(staking.address, l2GRTGatewayMock.address, toBN(1), expectedL2Data) - expect(await graph.provider.getBalance(l1GraphTokenLockTransferTool.address)).to.equal( - oldTransferToolEthBalance.sub(maxSubmissionCost).sub(gasPriceBid.mul(maxGas)), - ) - }) - it('should not allow transferring if the transfer tool contract returns a zero address beneficiary', async function () { - const tx = staking - .connect(indexer) - .transferLockedStakeToL2(minimumIndexerStake, maxGas, gasPriceBid, maxSubmissionCost) - await expect(tx).revertedWith('LOCK NOT TRANSFERRED') - }) - it('should not allow transferring if the transfer tool contract does not provide enough ETH', async function () { - await staking.connect(governor).setL1GraphTokenLockTransferTool(l1GraphTokenLockTransferToolBad.address) - await l1GraphTokenLockTransferToolBad.setL2WalletAddress(indexer.address, l2Indexer.address) - const tx = staking - .connect(indexer) - .transferLockedStakeToL2(minimumIndexerStake, maxGas, gasPriceBid, maxSubmissionCost) - await expect(tx).revertedWith('ETH TRANSFER FAILED') - }) - }) - describe('unlockDelegationToTransferredIndexer', function () { - beforeEach(async function () { - await staking.connect(governor).setDelegationUnbondingPeriod(28) // epochs - }) - it('allows a delegator to a transferred indexer to withdraw locked delegation before the unbonding period', async function () { - const tokensToDelegate = toGRT('10000') - await staking.connect(delegator).delegate(indexer.address, tokensToDelegate) - const actualDelegation = tokensToDelegate.sub(tokensToDelegate.mul(delegationTaxPPM).div(1000000)) - await staking - .connect(indexer) - .transferStakeToL2(l2Indexer.address, tokensToStake, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - await staking.connect(delegator).undelegate(indexer.address, actualDelegation) - const tx = await staking.connect(delegator).unlockDelegationToTransferredIndexer(indexer.address) - await expect(tx) - .emit(staking, 'StakeDelegatedUnlockedDueToL2Transfer') - .withArgs(indexer.address, delegator.address) - const tx2 = await staking.connect(delegator).withdrawDelegated(indexer.address, AddressZero) - await expect(tx2) - .emit(staking, 'StakeDelegatedWithdrawn') - .withArgs(indexer.address, delegator.address, actualDelegation) - }) - it('rejects calls if the protocol is partially paused', async function () { - await controller.connect(governor).setPartialPaused(true) - - const tx = staking.connect(delegator).unlockDelegationToTransferredIndexer(indexer.address) - await expect(tx).revertedWith('Partial-paused') - }) - it('rejects calls if the indexer has not transferred their stake to L2', async function () { - const tokensToDelegate = toGRT('10000') - await staking.connect(delegator).delegate(indexer.address, tokensToDelegate) - const tx = staking.connect(delegator).unlockDelegationToTransferredIndexer(indexer.address) - await expect(tx).revertedWith('indexer not transferred') - }) - it('rejects calls if the indexer has only transferred part of their stake but not all', async function () { - const tokensToDelegate = toGRT('10000') - await staking.connect(delegator).delegate(indexer.address, tokensToDelegate) - await staking - .connect(indexer) - .transferStakeToL2(l2Indexer.address, minimumIndexerStake, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - const tx = staking.connect(delegator).unlockDelegationToTransferredIndexer(indexer.address) - await expect(tx).revertedWith('indexer not transferred') - }) - it('rejects calls if the delegator has not undelegated first', async function () { - const tokensToDelegate = toGRT('10000') - await staking.connect(delegator).delegate(indexer.address, tokensToDelegate) - await staking - .connect(indexer) - .transferStakeToL2(l2Indexer.address, tokensToStake, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - const tx = staking.connect(delegator).unlockDelegationToTransferredIndexer(indexer.address) - await expect(tx).revertedWith('! locked') - }) - it('rejects calls if the caller is not a delegator', async function () { - await staking - .connect(indexer) - .transferStakeToL2(l2Indexer.address, tokensToStake, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - const tx = staking.connect(delegator).unlockDelegationToTransferredIndexer(indexer.address) - // The function checks for tokensLockedUntil so this is the error we should get: - await expect(tx).revertedWith('! locked') - }) - }) - describe('transferDelegationToL2', function () { - it('rejects calls if the protocol is partially paused', async function () { - await controller.connect(governor).setPartialPaused(true) - - const tx = staking - .connect(delegator) - .transferDelegationToL2(indexer.address, l2Delegator.address, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - await expect(tx).revertedWith('Partial-paused') - }) - it('rejects calls if the delegated indexer has not transferred stake to L2', async function () { - const tokensToDelegate = toGRT('10000') - await staking.connect(delegator).delegate(indexer.address, tokensToDelegate) - - const tx = staking - .connect(delegator) - .transferDelegationToL2(indexer.address, l2Delegator.address, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - await expect(tx).revertedWith('indexer not transferred') - }) - it('rejects calls if the beneficiary is zero', async function () { - const tokensToDelegate = toGRT('10000') - await staking.connect(delegator).delegate(indexer.address, tokensToDelegate) - await staking - .connect(indexer) - .transferStakeToL2(l2Indexer.address, minimumIndexerStake, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - - const tx = staking - .connect(delegator) - .transferDelegationToL2(indexer.address, AddressZero, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - await expect(tx).revertedWith('l2Beneficiary == 0') - }) - it('rejects calls if the delegator has tokens locked for undelegation', async function () { - const tokensToDelegate = toGRT('10000') - await staking.connect(delegator).delegate(indexer.address, tokensToDelegate) - await staking - .connect(indexer) - .transferStakeToL2(l2Indexer.address, minimumIndexerStake, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - await staking.connect(delegator).undelegate(indexer.address, toGRT('1')) - - const tx = staking - .connect(delegator) - .transferDelegationToL2(indexer.address, l2Delegator.address, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - await expect(tx).revertedWith('tokensLocked != 0') - }) - it('rejects calls if the delegator has no tokens delegated to the indexer', async function () { - await staking - .connect(indexer) - .transferStakeToL2(l2Indexer.address, minimumIndexerStake, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - - const tx = staking - .connect(delegator) - .transferDelegationToL2(indexer.address, l2Delegator.address, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - await expect(tx).revertedWith('delegation == 0') - }) - it('sends all the tokens delegated to the indexer to the beneficiary on L2, using the gateway', async function () { - const tokensToDelegate = toGRT('10000') - await staking.connect(delegator).delegate(indexer.address, tokensToDelegate) - const actualDelegation = tokensToDelegate.sub(tokensToDelegate.mul(delegationTaxPPM).div(1000000)) - await staking - .connect(indexer) - .transferStakeToL2(l2Indexer.address, minimumIndexerStake, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - - const expectedFunctionData = defaultAbiCoder.encode( - ['tuple(address,address)'], - [[l2Indexer.address, l2Delegator.address]], - ) - - const expectedCallhookData = defaultAbiCoder.encode( - ['uint8', 'bytes'], - [toBN(1), expectedFunctionData], // code = 1 means RECEIVE_DELEGATION_CODE - ) - const expectedL2Data = await l1GraphTokenGateway.getOutboundCalldata( - grt.address, - staking.address, - l2StakingMock.address, - actualDelegation, - expectedCallhookData, - ) - - const tx = staking - .connect(delegator) - .transferDelegationToL2(indexer.address, l2Delegator.address, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - // seqNum is 2 because the first bridge call was in transferStakeToL2 - await expect(tx) - .emit(l1GraphTokenGateway, 'TxToL2') - .withArgs(staking.address, l2GRTGatewayMock.address, toBN(2), expectedL2Data) - await expect(tx) - .emit(staking, 'DelegationTransferredToL2') - .withArgs(delegator.address, l2Delegator.address, indexer.address, l2Indexer.address, actualDelegation) - }) - it('sets the delegation shares to zero so cannot be called twice', async function () { - const tokensToDelegate = toGRT('10000') - await staking.connect(delegator).delegate(indexer.address, tokensToDelegate) - await staking - .connect(indexer) - .transferStakeToL2(l2Indexer.address, minimumIndexerStake, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - - await staking - .connect(delegator) - .transferDelegationToL2(indexer.address, l2Delegator.address, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - - const tx = staking - .connect(delegator) - .transferDelegationToL2(indexer.address, l2Delegator.address, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - await expect(tx).revertedWith('delegation == 0') - }) - it('can be called again if the delegator added more delegation (edge case)', async function () { - const tokensToDelegate = toGRT('10000') - await staking.connect(delegator).delegate(indexer.address, tokensToDelegate) - const actualDelegation = tokensToDelegate.sub(tokensToDelegate.mul(delegationTaxPPM).div(1000000)) - await staking - .connect(indexer) - .transferStakeToL2(l2Indexer.address, minimumIndexerStake, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - - await staking - .connect(delegator) - .transferDelegationToL2(indexer.address, l2Delegator.address, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - - await staking.connect(delegator).delegate(indexer.address, tokensToDelegate) - - const tx = staking - .connect(delegator) - .transferDelegationToL2(indexer.address, l2Delegator.address, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - await expect(tx) - .emit(staking, 'DelegationTransferredToL2') - .withArgs(delegator.address, l2Delegator.address, indexer.address, l2Indexer.address, actualDelegation) - }) - it('rejects calls if the ETH value is larger than expected', async function () { - const tokensToDelegate = toGRT('10000') - await staking.connect(delegator).delegate(indexer.address, tokensToDelegate) - await staking - .connect(indexer) - .transferStakeToL2(l2Indexer.address, minimumIndexerStake, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - - const tx = staking - .connect(delegator) - .transferDelegationToL2(indexer.address, l2Delegator.address, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)).add(1), - }) - await expect(tx).revertedWith('INVALID_ETH_AMOUNT') - }) - }) - describe('transferLockedDelegationToL2', function () { - it('rejects calls if the protocol is partially paused', async function () { - await controller.connect(governor).setPartialPaused(true) - - const tx = staking - .connect(delegator) - .transferLockedDelegationToL2(indexer.address, maxGas, gasPriceBid, maxSubmissionCost) - await expect(tx).revertedWith('Partial-paused') - }) - it('sends delegated tokens to L2 like transferDelegationToL2, but gets the beneficiary and ETH from the L1GraphTokenLockTransferTool', async function () { - const tokensToDelegate = toGRT('10000') - await staking.connect(delegator).delegate(indexer.address, tokensToDelegate) - const actualDelegation = tokensToDelegate.sub(tokensToDelegate.mul(delegationTaxPPM).div(1000000)) - - await staking - .connect(indexer) - .transferStakeToL2(l2Indexer.address, minimumIndexerStake, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - - const expectedFunctionData = defaultAbiCoder.encode( - ['tuple(address,address)'], - [[l2Indexer.address, l2Delegator.address]], - ) - - const expectedCallhookData = defaultAbiCoder.encode( - ['uint8', 'bytes'], - [toBN(1), expectedFunctionData], // code = 1 means RECEIVE_DELEGATION_CODE - ) - const expectedL2Data = await l1GraphTokenGateway.getOutboundCalldata( - grt.address, - staking.address, - l2StakingMock.address, - actualDelegation, - expectedCallhookData, - ) - - await l1GraphTokenLockTransferTool.setL2WalletAddress(delegator.address, l2Delegator.address) - - const oldTransferToolEthBalance = await graph.provider.getBalance(l1GraphTokenLockTransferTool.address) - const tx = staking - .connect(delegator) - .transferLockedDelegationToL2(indexer.address, maxGas, gasPriceBid, maxSubmissionCost) - // seqNum is 2 because the first bridge call was in transferStakeToL2 - await expect(tx) - .emit(l1GraphTokenGateway, 'TxToL2') - .withArgs(staking.address, l2GRTGatewayMock.address, toBN(2), expectedL2Data) - await expect(tx) - .emit(staking, 'DelegationTransferredToL2') - .withArgs(delegator.address, l2Delegator.address, indexer.address, l2Indexer.address, actualDelegation) - expect(await graph.provider.getBalance(l1GraphTokenLockTransferTool.address)).to.equal( - oldTransferToolEthBalance.sub(maxSubmissionCost).sub(gasPriceBid.mul(maxGas)), - ) - }) - it('rejects calls if the transfer tool contract returns a zero address beneficiary', async function () { - const tokensToDelegate = toGRT('10000') - await staking.connect(delegator).delegate(indexer.address, tokensToDelegate) - - await staking - .connect(indexer) - .transferStakeToL2(l2Indexer.address, minimumIndexerStake, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - - const tx = staking - .connect(delegator) - .transferLockedDelegationToL2(indexer.address, maxGas, gasPriceBid, maxSubmissionCost) - await expect(tx).revertedWith('LOCK NOT TRANSFERRED') - }) - it('rejects calls if the transfer tool contract does not provide enough ETH', async function () { - const tokensToDelegate = toGRT('10000') - await staking.connect(delegator).delegate(indexer.address, tokensToDelegate) - - await staking - .connect(indexer) - .transferStakeToL2(l2Indexer.address, minimumIndexerStake, maxGas, gasPriceBid, maxSubmissionCost, { - value: maxSubmissionCost.add(gasPriceBid.mul(maxGas)), - }) - await staking.connect(governor).setL1GraphTokenLockTransferTool(l1GraphTokenLockTransferToolBad.address) - - await l1GraphTokenLockTransferToolBad.setL2WalletAddress(delegator.address, l2Delegator.address) - const tx = staking - .connect(delegator) - .transferLockedDelegationToL2(indexer.address, maxGas, gasPriceBid, maxSubmissionCost) - await expect(tx).revertedWith('ETH TRANSFER FAILED') - }) - }) - }) -}) diff --git a/packages/contracts-test/tests/unit/staking/rebate.test.ts b/packages/contracts-test/tests/unit/staking/rebate.test.ts deleted file mode 100644 index 373789842..000000000 --- a/packages/contracts-test/tests/unit/staking/rebate.test.ts +++ /dev/null @@ -1,238 +0,0 @@ -import { LibExponential } from '@graphprotocol/contracts' -import { formatGRT, isGraphL1ChainId, toGRT } from '@graphprotocol/sdk' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { expect } from 'chai' -import { BigNumber, Contract } from 'ethers' -import hre from 'hardhat' - -import { NetworkFixture } from '../lib/fixtures' - -const toFloat = (n: BigNumber) => parseFloat(formatGRT(n)) -const toFixed = (n: number | BigNumber, precision = 12) => { - if (typeof n === 'number') { - return n.toFixed(precision) - } - return toFloat(n).toFixed(precision) -} - -const ABI_LIB_EXPONENTIAL = [ - { - inputs: [ - { - internalType: 'uint256', - name: 'fees', - type: 'uint256', - }, - { - internalType: 'uint256', - name: 'stake', - type: 'uint256', - }, - { - internalType: 'uint32', - name: 'alphaNumerator', - type: 'uint32', - }, - { - internalType: 'uint32', - name: 'alphaDenominator', - type: 'uint32', - }, - { - internalType: 'uint32', - name: 'lambdaNumerator', - type: 'uint32', - }, - { - internalType: 'uint32', - name: 'lambdaDenominator', - type: 'uint32', - }, - ], - name: 'exponentialRebates', - outputs: [ - { - internalType: 'uint256', - name: '', - type: 'uint256', - }, - ], - stateMutability: 'pure', - type: 'function', - }, -] - -type RebateParameters = number[] - -interface RebateTestCase { - totalRewards: number - fees: number - totalFees: number - stake: number - totalStake: number -} - -// This function calculates the exponential rebates formula in Typescript so we can compare against -// the Solidity implementation -export function exponentialRebates( - fees: number, - stake: number, - alphaNumerator: number, - alphaDenominator: number, - lambdaNumerator: number, - lambdaDenominator: number, -): number { - const alpha = alphaNumerator / alphaDenominator - if (alpha === 0) { - return fees - } - - const lambda = lambdaNumerator / lambdaDenominator - if (fees === 0) { - return 0 - } - - const exponent = (lambda * stake) / fees - - // LibExponential.MAX_EXPONENT = 15 - if (exponent > 15) { - return fees - } - - return fees * (1 - alpha * Math.exp(-exponent)) -} - -describe('Staking:rebates', () => { - const graph = hre.graph({ addressBook: 'addresses-local.json' }) - - let libExponential: LibExponential - let fixture: NetworkFixture - - let governor: SignerWithAddress - - const testCases: RebateTestCase[] = [ - { totalRewards: 1400, fees: 100, totalFees: 1400, stake: 5000, totalStake: 7300 }, - { totalRewards: 1400, fees: 300, totalFees: 1400, stake: 600, totalStake: 7300 }, - { totalRewards: 1400, fees: 1000, totalFees: 1400, stake: 500, totalStake: 7300 }, - { totalRewards: 1400, fees: 0, totalFees: 1400, stake: 1200, totalStake: 7300 }, - ] - - // Edge case #1 - No closed allocations any query fees - const edgeCases1: RebateTestCase[] = [ - { totalRewards: 0, fees: 0, totalFees: 0, stake: 5000, totalStake: 7300 }, - { totalRewards: 0, fees: 0, totalFees: 0, stake: 600, totalStake: 7300 }, - { totalRewards: 0, fees: 0, totalFees: 0, stake: 500, totalStake: 7300 }, - { totalRewards: 0, fees: 0, totalFees: 0, stake: 1200, totalStake: 7300 }, - ] - - // Edge case #2 - Closed allocations with queries but no stake - const edgeCases2: RebateTestCase[] = [ - { totalRewards: 1300, fees: 100, totalFees: 1300, stake: 0, totalStake: 0 }, - { totalRewards: 1300, fees: 0, totalFees: 1300, stake: 0, totalStake: 0 }, - { totalRewards: 1300, fees: 200, totalFees: 1300, stake: 0, totalStake: 0 }, - { totalRewards: 1300, fees: 1000, totalFees: 1300, stake: 0, totalStake: 0 }, - ] - - // Test if the Solidity implementation of the rebate formula match the local implementation - async function shouldMatchFormulas(testCases: RebateTestCase[], rebateParams: RebateParameters) { - const [alphaNumerator, alphaDenominator, lambdaNumerator, lambdaDenominator] = rebateParams - - for (const testCase of testCases) { - // Test Typescript exponential rebates formula implementation - const r1 = exponentialRebates( - testCase.fees, - testCase.stake, - alphaNumerator, - alphaDenominator, - lambdaNumerator, - lambdaDenominator, - ) - - // Convert non-alpha values to wei before sending for precision - const r2 = await libExponential.exponentialRebates( - toGRT(testCase.fees), - toGRT(testCase.stake), - alphaNumerator, - alphaDenominator, - lambdaNumerator, - lambdaDenominator, - ) - - // Must match : contracts to local implementation - expect(toFixed(r1)).eq(toFixed(r2)) - } - } - - function testRebateParameters(fn, testCases) { - // *** Exponential rebates *** - // Typical alpha and lambda - it('alpha 1 - lambda 0.6', async function () { - const params: RebateParameters = [10, 10, 6, 10] - await fn(testCases, params) - }) - - // Typical alpha and lambda - it('alpha 0.25 - lambda 0.1', async function () { - const params: RebateParameters = [1, 4, 1, 10] - await fn(testCases, params) - }) - - // Periodic alpha and lambda - it('alpha ~0.33 - lambda ~0.66', async function () { - const params: RebateParameters = [1, 3, 2, 3] - await fn(testCases, params) - }) - - // Small alpha typical lambda - it('alpha 0.005 - lambda 0.6', async function () { - const params: RebateParameters = [1, 200, 6, 10] - await fn(testCases, params) - }) - - // Typical alpha small lambda - it('alpha 1 - lambda 0.6', async function () { - const params: RebateParameters = [1, 1, 1, 200] - await fn(testCases, params) - }) - - // Edge alpha - typical lambda - it('alpha 0 - lambda 0.6', async function () { - const params: RebateParameters = [0, 1, 6, 10] - await fn(testCases, params) - }) - - // Typical alpha - edge lambda - it('alpha 1 - lambda 0', async function () { - const params: RebateParameters = [1, 1, 0, 10] - await fn(testCases, params) - }) - } - - before(async function () { - ;({ governor } = await graph.getNamedAccounts()) - fixture = new NetworkFixture(graph.provider) - await fixture.load(governor) - - const stakingName = isGraphL1ChainId(graph.chainId) ? 'L1Staking' : 'L2Staking' - const entry = graph.addressBook.getEntry(stakingName) - libExponential = new Contract( - entry.implementation.libraries.LibExponential, - ABI_LIB_EXPONENTIAL, - graph.provider, - ) as LibExponential - }) - - describe('should match rebates Solidity implementation', function () { - describe('normal test case', function () { - testRebateParameters(shouldMatchFormulas, testCases) - }) - - describe('edge #1 test case', function () { - testRebateParameters(shouldMatchFormulas, edgeCases1) - }) - - describe('edge #2 test case', function () { - testRebateParameters(shouldMatchFormulas, edgeCases2) - }) - }) -}) diff --git a/packages/contracts-test/tests/unit/staking/staking.test.ts b/packages/contracts-test/tests/unit/staking/staking.test.ts deleted file mode 100644 index 41605f4dd..000000000 --- a/packages/contracts-test/tests/unit/staking/staking.test.ts +++ /dev/null @@ -1,510 +0,0 @@ -import { GraphToken } from '@graphprotocol/contracts' -import { IStaking } from '@graphprotocol/contracts' -import { deriveChannelKey, GraphNetworkContracts, helpers, randomHexBytes, toBN, toGRT } from '@graphprotocol/sdk' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -import { expect } from 'chai' -import { BigNumber, constants, Event } from 'ethers' -import hre from 'hardhat' - -import { NetworkFixture } from '../lib/fixtures' - -const { AddressZero, MaxUint256 } = constants - -function weightedAverage(valueA: BigNumber, valueB: BigNumber, periodA: BigNumber, periodB: BigNumber) { - return periodA.mul(valueA).add(periodB.mul(valueB)).div(valueA.add(valueB)) -} - -describe('Staking:Stakes', () => { - const graph = hre.graph() - let me: SignerWithAddress - let governor: SignerWithAddress - let indexer: SignerWithAddress - let slasher: SignerWithAddress - let fisherman: SignerWithAddress - - let fixture: NetworkFixture - - let contracts: GraphNetworkContracts - let grt: GraphToken - let staking: IStaking - - // Test values - const indexerTokens = toGRT('1000') - const tokensToStake = toGRT('100') - const subgraphDeploymentID = randomHexBytes() - const channelKey = deriveChannelKey() - const allocationID = channelKey.address - const metadata = randomHexBytes(32) - - // Allocate with test values - const allocate = async (tokens: BigNumber) => { - return staking - .connect(indexer) - .allocateFrom( - indexer.address, - subgraphDeploymentID, - tokens, - allocationID, - metadata, - await channelKey.generateProof(indexer.address), - ) - } - - // Stake and verify state change - const shouldStake = async function (tokensToStake: BigNumber) { - // Before state - const beforeIndexerStake = await staking.getIndexerStakedTokens(indexer.address) - const beforeStakingBalance = await grt.balanceOf(staking.address) - - // Stake - const tx = staking.connect(indexer).stake(tokensToStake) - await expect(tx).emit(staking, 'StakeDeposited').withArgs(indexer.address, tokensToStake) - - // After state - const afterIndexerStake = await staking.getIndexerStakedTokens(indexer.address) - const afterStakingBalance = await grt.balanceOf(staking.address) - - // State updated - expect(afterIndexerStake).eq(beforeIndexerStake.add(tokensToStake)) - expect(afterStakingBalance).eq(beforeStakingBalance.add(tokensToStake)) - } - - before(async function () { - ;[me, indexer, slasher, fisherman] = await graph.getTestAccounts() - ;({ governor } = await graph.getNamedAccounts()) - fixture = new NetworkFixture(graph.provider) - contracts = await fixture.load(governor) - grt = contracts.GraphToken as GraphToken - staking = contracts.Staking as IStaking - - // Give some funds to the indexer and approve staking contract to use funds on indexer behalf - await grt.connect(governor).mint(indexer.address, indexerTokens) - await grt.connect(indexer).approve(staking.address, indexerTokens) - - await staking.connect(governor).setSlasher(slasher.address, true) - }) - - beforeEach(async function () { - await fixture.setUp() - }) - - afterEach(async function () { - await fixture.tearDown() - }) - - context('> when not staked', function () { - describe('hasStake', function () { - it('should not have stakes', async function () { - expect(await staking.hasStake(indexer.address)).eq(false) - }) - }) - - describe('stake', function () { - it('reject stake zero tokens', async function () { - const tx = staking.connect(indexer).stake(toGRT('0')) - await expect(tx).revertedWith('!tokens') - }) - - it('reject stake less than minimum indexer stake', async function () { - const amount = (await staking.minimumIndexerStake()).sub(toGRT('1')) - const tx = staking.connect(indexer).stake(amount) - await expect(tx).revertedWith('!minimumIndexerStake') - }) - - it('should stake tokens', async function () { - await shouldStake(tokensToStake) - }) - - it('should stake tokens = minimumIndexerStake', async function () { - await shouldStake(await staking.minimumIndexerStake()) - }) - }) - - describe('unstake', function () { - it('reject unstake tokens', async function () { - const tokensToUnstake = toGRT('2') - const tx = staking.connect(indexer).unstake(tokensToUnstake) - await expect(tx).revertedWith('!stake') - }) - }) - - describe('slash', function () { - it('reject slash indexer', async function () { - const tokensToSlash = toGRT('10') - const tokensToReward = toGRT('10') - const tx = staking.connect(slasher).slash(indexer.address, tokensToSlash, tokensToReward, fisherman.address) - await expect(tx).revertedWith('!stake') - }) - }) - }) - - context('> when staked', function () { - beforeEach(async function () { - await staking.connect(indexer).stake(tokensToStake) - }) - - describe('hasStake', function () { - it('should have stakes', async function () { - expect(await staking.hasStake(indexer.address)).eq(true) - }) - }) - - describe('stake', function () { - it('should allow re-staking', async function () { - await shouldStake(tokensToStake) - }) - - it('reject to stake under the minimum indexer stake after unstake + slash', async function () { - // Unstake (we get right on the minimum stake) - const minimumIndexerStake = await staking.minimumIndexerStake() - const tokensStaked = (await staking.stakes(indexer.address)).tokensStaked - const tokensToGetOnMinimumStake = tokensStaked.sub(minimumIndexerStake) - await staking.connect(indexer).unstake(tokensToGetOnMinimumStake) - - // Slash some indexer tokens to get under the water of the minimum indexer stake - await staking.connect(slasher).slash(indexer.address, toGRT('10'), toGRT(0), fisherman.address) - - // Stake should require to go over the minimum stake - const tx = staking.connect(indexer).stake(toGRT('1')) - await expect(tx).revertedWith('!minimumIndexerStake') - }) - }) - - describe('unstake', function () { - it('should unstake and lock tokens for thawing period', async function () { - const tokensToUnstake = toGRT('2') - const thawingPeriod = toBN(await staking.thawingPeriod()) - const currentBlock = await helpers.latestBlock() - const until = currentBlock + thawingPeriod.add(toBN('1')).toNumber() - - // Unstake - const tx = staking.connect(indexer).unstake(tokensToUnstake) - await expect(tx).emit(staking, 'StakeLocked').withArgs(indexer.address, tokensToUnstake, until) - }) - - it('should unstake and lock tokens for (weighted avg) thawing period if repeated', async function () { - const tokensToUnstake = toGRT('10') - const thawingPeriod = toBN(await staking.thawingPeriod()) - - // Unstake (1) - const tx1 = await staking.connect(indexer).unstake(tokensToUnstake) - const receipt1 = await tx1.wait() - const event1: Event = receipt1.events.pop() - const tokensLockedUntil1 = event1.args['until'] - - // Move forward before the tokens are unlocked for withdrawal - await helpers.mineUpTo(tokensLockedUntil1.sub(5)) - - // Calculate locking time for tokens taking into account the previous unstake request - const currentBlock = await helpers.latestBlock() - const lockingPeriod = weightedAverage( - tokensToUnstake, - tokensToUnstake, - tokensLockedUntil1.sub(currentBlock), - thawingPeriod, - ) - const expectedLockedUntil = currentBlock + lockingPeriod.add(toBN('1')).toNumber() - - // Unstake (2) - const tx2 = await staking.connect(indexer).unstake(tokensToUnstake) - const receipt2 = await tx2.wait() - - // Verify events - const event2: Event = receipt2.events.pop() - expect(event2.args['until']).eq(expectedLockedUntil) - - // Verify state - const afterIndexerStake = await staking.stakes(indexer.address) - expect(afterIndexerStake.tokensLocked).eq(tokensToUnstake.mul(2)) // we unstaked two times - expect(afterIndexerStake.tokensLockedUntil).eq(expectedLockedUntil) - }) - - it('should always increase the thawing period on subsequent unstakes', async function () { - const tokensToUnstake = toGRT('10') - const tokensToUnstakeSecondTime = toGRT('0.000001') - - // Unstake (1) - const tx1 = await staking.connect(indexer).unstake(tokensToUnstake) - const receipt1 = await tx1.wait() - const event1: Event = receipt1.events.pop() - const tokensLockedUntil1 = event1.args['until'] - - // Move forward before the tokens are unlocked for withdrawal - await helpers.mineUpTo(tokensLockedUntil1.sub(5)) - - // Ensure at least 1 block is added (i.e. the weighted average rounds up) - const expectedLockedUntil = tokensLockedUntil1.add(1) - - // Unstake (2) - const tx2 = await staking.connect(indexer).unstake(tokensToUnstakeSecondTime) - const receipt2 = await tx2.wait() - - // Verify events - const event2: Event = receipt2.events.pop() - expect(event2.args['until']).eq(expectedLockedUntil) - - // Verify state - const afterIndexerStake = await staking.stakes(indexer.address) - expect(afterIndexerStake.tokensLocked).eq(tokensToUnstake.add(tokensToUnstakeSecondTime)) // we unstaked two times - expect(afterIndexerStake.tokensLockedUntil).eq(expectedLockedUntil) - }) - - it('should unstake and withdraw if some tokens are unthawed', async function () { - const tokensToUnstake = toGRT('10') - const thawingPeriod = toBN(await staking.thawingPeriod()) - - const beforeIndexerBalance = await grt.balanceOf(indexer.address) - - // Unstake (1) - const tx1 = await staking.connect(indexer).unstake(tokensToUnstake) - const receipt1 = await tx1.wait() - const event1: Event = receipt1.events.pop() - const tokensLockedUntil1 = event1.args['until'] - - // Move forward after the tokens are unlocked for withdrawal - await helpers.mineUpTo(tokensLockedUntil1) - - // Calculate locking time for tokens taking into account some tokens are withdraweable - const currentBlock = await helpers.latestBlock() - const expectedLockedUntil = currentBlock + thawingPeriod.add(toBN('1')).toNumber() - - // Unstake (2) - const tx2 = await staking.connect(indexer).unstake(tokensToUnstake) - const receipt2 = await tx2.wait() - - // Verify events - const unstakeEvent: Event = receipt2.events.pop() - const withdrawEvent: Event = receipt2.events.pop() - expect(unstakeEvent.args['until']).eq(expectedLockedUntil) - expect(withdrawEvent.args['tokens']).eq(tokensToUnstake) - - // Verify state - const afterIndexerStake = await staking.stakes(indexer.address) - const afterIndexerBalance = await grt.balanceOf(indexer.address) - expect(afterIndexerStake.tokensLocked).eq(tokensToUnstake) - expect(afterIndexerStake.tokensLockedUntil).eq(expectedLockedUntil) - expect(afterIndexerBalance).eq(beforeIndexerBalance.add(tokensToUnstake)) - }) - - it('should unstake available tokens even if passed a higher amount', async function () { - // Try to unstake a bit more than currently staked - const tokensOverCapacity = tokensToStake.add(toGRT('1')) - await staking.connect(indexer).unstake(tokensOverCapacity) - - // Check state - const tokensLocked = (await staking.stakes(indexer.address)).tokensLocked - expect(tokensLocked).eq(tokensToStake) - }) - - it('reject unstake zero tokens', async function () { - const tx = staking.connect(indexer).unstake(toGRT('0')) - await expect(tx).revertedWith('!stake-avail') - }) - - it('reject unstake under the minimum indexer stake', async function () { - const minimumIndexerStake = await staking.minimumIndexerStake() - const tokensStaked = (await staking.stakes(indexer.address)).tokensStaked - const tokensToGetUnderMinimumStake = tokensStaked.sub(minimumIndexerStake).add(1) - const tx = staking.connect(indexer).unstake(tokensToGetUnderMinimumStake) - await expect(tx).revertedWith('!minimumIndexerStake') - }) - - it('reject unstake under the minimum indexer stake w/multiple unstake', async function () { - const minimumIndexerStake = await staking.minimumIndexerStake() - const tokensStaked = (await staking.stakes(indexer.address)).tokensStaked - - // First unstake (we get right on the minimum stake) - const tokensToGetOnMinimumStake = tokensStaked.sub(minimumIndexerStake) - await staking.connect(indexer).unstake(tokensToGetOnMinimumStake) - - // Second unstake, taking just one token out will make us under the minimum stake - const tx = staking.connect(indexer).unstake(toGRT('1')) - await expect(tx).revertedWith('!minimumIndexerStake') - }) - - it('should allow unstake of full amount', async function () { - await staking.connect(indexer).unstake(tokensToStake) - expect(await staking.getIndexerCapacity(indexer.address)).eq(0) - }) - - it('should allow unstake of full amount with no upper limits', async function () { - // Use manual mining - await helpers.setAutoMine(false) - - // Setup - const newTokens = toGRT('2') - const stakedTokens = await staking.getIndexerStakedTokens(indexer.address) - const tokensToUnstake = stakedTokens.add(newTokens) - - // StakeTo & Unstake - await staking.connect(indexer).stakeTo(indexer.address, newTokens) - await staking.connect(indexer).unstake(MaxUint256) - await helpers.mine() - - // Check state - const tokensLocked = (await staking.stakes(indexer.address)).tokensLocked - expect(tokensLocked).eq(tokensToUnstake) - - // Restore automine - await helpers.setAutoMine(true) - }) - }) - - describe('withdraw', function () { - it('reject withdraw if no tokens available', async function () { - const tx = staking.connect(indexer).withdraw() - await expect(tx).revertedWith('!tokens') - }) - - it('should withdraw if tokens available', async function () { - // Unstake - const tokensToUnstake = toGRT('10') - const tx1 = await staking.connect(indexer).unstake(tokensToUnstake) - const receipt1 = await tx1.wait() - const event1: Event = receipt1.events.pop() - const tokensLockedUntil = event1.args['until'] - - // Withdraw on locking period (should fail) - const tx2 = staking.connect(indexer).withdraw() - await expect(tx2).revertedWith('!tokens') - - // Move forward - await helpers.mineUpTo(tokensLockedUntil) - - // Withdraw after locking period (all good) - const beforeBalance = await grt.balanceOf(indexer.address) - const tx3 = staking.connect(indexer).withdraw() - await expect(tx3).emit(staking, 'StakeWithdrawn').withArgs(indexer.address, tokensToUnstake) - const afterBalance = await grt.balanceOf(indexer.address) - expect(afterBalance).eq(beforeBalance.add(tokensToUnstake)) - }) - }) - - describe('slash', function () { - // This function tests slashing behaviour under different conditions - const shouldSlash = async function ( - indexer: SignerWithAddress, - tokensToSlash: BigNumber, - tokensToReward: BigNumber, - fisherman: SignerWithAddress, - ) { - // Before - const beforeTotalSupply = await grt.totalSupply() - const beforeFishermanTokens = await grt.balanceOf(fisherman.address) - const beforeIndexerStake = await staking.getIndexerStakedTokens(indexer.address) - - // Slash indexer - const tokensToBurn = tokensToSlash.sub(tokensToReward) - const tx = staking.connect(slasher).slash(indexer.address, tokensToSlash, tokensToReward, fisherman.address) - await expect(tx) - .emit(staking, 'StakeSlashed') - .withArgs(indexer.address, tokensToSlash, tokensToReward, fisherman.address) - - // After - const afterTotalSupply = await grt.totalSupply() - const afterFishermanTokens = await grt.balanceOf(fisherman.address) - const afterIndexerStake = await staking.getIndexerStakedTokens(indexer.address) - - // Check slashed tokens has been burned - expect(afterTotalSupply).eq(beforeTotalSupply.sub(tokensToBurn)) - // Check reward was given to the fisherman - expect(afterFishermanTokens).eq(beforeFishermanTokens.add(tokensToReward)) - // Check indexer stake was updated - expect(afterIndexerStake).eq(beforeIndexerStake.sub(tokensToSlash)) - } - - it('should slash indexer and give reward to beneficiary slash>reward', async function () { - // Slash indexer - const tokensToSlash = toGRT('100') - const tokensToReward = toGRT('10') - await shouldSlash(indexer, tokensToSlash, tokensToReward, fisherman) - }) - - it('should slash indexer and give reward to beneficiary slash=reward', async function () { - // Slash indexer - const tokensToSlash = toGRT('10') - const tokensToReward = toGRT('10') - await shouldSlash(indexer, tokensToSlash, tokensToReward, fisherman) - }) - - it('should slash indexer even when overallocated', async function () { - // Initial stake - const beforeTokensStaked = await staking.getIndexerStakedTokens(indexer.address) - - // Unstake partially, these tokens will be locked - const tokensToUnstake = toGRT('10') - await staking.connect(indexer).unstake(tokensToUnstake) - - // Allocate indexer stake - const tokensToAllocate = toGRT('70') - await allocate(tokensToAllocate) - - // State pre-slashing - // helpers.logStake(await staking.stakes(indexer)) - // > Current state: - // = Staked: 100 - // = Locked: 10 - // = Allocated: 70 - // = Available: 20 (staked - allocated - locked) - - // Even if all stake is allocated it should slash the indexer - const tokensToSlash = toGRT('80') - const tokensToReward = toGRT('0') - await shouldSlash(indexer, tokensToSlash, tokensToReward, fisherman) - - // State post-slashing - // helpers.logStake(await staking.stakes(indexer)) - // > Current state: - // = Staked: 20 - // = Locked: 0 - // = Allocated: 70 - // = Available: -50 (staked - allocated - locked) => when tokens available becomes negative - // we are overallocated, the staking contract will prevent unstaking or allocating until - // the balance is restored by staking or unallocating - - const stakes = await staking.stakes(indexer.address) - // Stake should be reduced by the amount slashed - expect(stakes.tokensStaked).eq(beforeTokensStaked.sub(tokensToSlash)) - // All allocated tokens should be untouched - expect(stakes.tokensAllocated).eq(tokensToAllocate) - // All locked tokens need to be consumed from the stake - expect(stakes.tokensLocked).eq(toBN('0')) - expect(stakes.tokensLockedUntil).eq(toBN('0')) - // Tokens available when negative means over allocation - const tokensAvailable = stakes.tokensStaked.sub(stakes.tokensAllocated).sub(stakes.tokensLocked) - expect(tokensAvailable).eq(toGRT('-50')) - - const tx = staking.connect(indexer).unstake(tokensToUnstake) - await expect(tx).revertedWith('!stake-avail') - }) - - it('reject to slash zero tokens', async function () { - const tokensToSlash = toGRT('0') - const tokensToReward = toGRT('0') - const tx = staking.connect(slasher).slash(indexer.address, tokensToSlash, tokensToReward, me.address) - await expect(tx).revertedWith('!tokens') - }) - - it('reject to slash indexer if caller is not slasher', async function () { - const tokensToSlash = toGRT('100') - const tokensToReward = toGRT('10') - const tx = staking.connect(me).slash(indexer.address, tokensToSlash, tokensToReward, me.address) - await expect(tx).revertedWith('!slasher') - }) - - it('reject to slash indexer if beneficiary is zero address', async function () { - const tokensToSlash = toGRT('100') - const tokensToReward = toGRT('10') - const tx = staking.connect(slasher).slash(indexer.address, tokensToSlash, tokensToReward, AddressZero) - await expect(tx).revertedWith('!beneficiary') - }) - - it('reject to slash indexer if reward is greater than slash amount', async function () { - const tokensToSlash = toGRT('100') - const tokensToReward = toGRT('200') - const tx = staking.connect(slasher).slash(indexer.address, tokensToSlash, tokensToReward, fisherman.address) - await expect(tx).revertedWith('rewards>slash') - }) - }) - }) -}) diff --git a/packages/contracts-test/tsconfig.json b/packages/contracts-test/tsconfig.json deleted file mode 100644 index 17bf6437c..000000000 --- a/packages/contracts-test/tsconfig.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "compilerOptions": { - "target": "es2020", - "module": "commonjs", - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "strict": true, - "skipLibCheck": true, - "resolveJsonModule": true, - "incremental": true - } -} diff --git a/packages/contracts-test/utils/coverage.ts b/packages/contracts-test/utils/coverage.ts deleted file mode 100644 index 068ab8527..000000000 --- a/packages/contracts-test/utils/coverage.ts +++ /dev/null @@ -1,13 +0,0 @@ -import hre from 'hardhat' - -/** - * Utility functions for detecting and handling coverage test execution - */ - -/** - * Checks if tests are currently running under solidity-coverage instrumentation - * @returns true if running under coverage, false otherwise - */ -export function isRunningUnderCoverage(): boolean { - return hre.__SOLIDITY_COVERAGE_RUNNING === true -} diff --git a/packages/contracts/.mocharc.json b/packages/contracts/.mocharc.json deleted file mode 100644 index 79ba35ddf..000000000 --- a/packages/contracts/.mocharc.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "require": "ts-node/register/files", - "ignore": ["test/fixture-projects/**/*"], - "timeout": 6000 -} diff --git a/packages/contracts/hardhat.config.ts b/packages/contracts/hardhat.config.ts index ba90039ca..53541e927 100644 --- a/packages/contracts/hardhat.config.ts +++ b/packages/contracts/hardhat.config.ts @@ -31,9 +31,6 @@ const config: HardhatUserConfig = { }, ], }, - paths: { - tests: './test/tests/unit', - }, defaultNetwork: 'hardhat', networks: { hardhat: { diff --git a/packages/contracts/package.json b/packages/contracts/package.json index a7a4933c8..e4dc0b0dc 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -20,12 +20,10 @@ "types": "index.d.ts", "scripts": { "prepack": "pnpm build", - "clean": "rm -rf artifacts/ cache/ types/ abis/ build/ dist/ coverage/ test/node_modules/", + "clean": "rm -rf artifacts/ cache/ types/ abis/ build/ dist/ coverage/", "build": "pnpm build:self", "build:self": "pnpm compile", "compile": "hardhat compile --quiet", - "test": "pnpm --filter @graphprotocol/contracts-tests test", - "test:coverage": "pnpm --filter @graphprotocol/contracts-tests run test:coverage", "deploy": "pnpm predeploy && pnpm build", "deploy-localhost": "pnpm build", "predeploy": "scripts/predeploy", diff --git a/packages/contracts/scripts/ops/20240208-migrate-legacy-subgraphs/data.json b/packages/contracts/scripts/ops/20240208-migrate-legacy-subgraphs/data.json deleted file mode 100644 index 9ec23ad5b..000000000 --- a/packages/contracts/scripts/ops/20240208-migrate-legacy-subgraphs/data.json +++ /dev/null @@ -1,342 +0,0 @@ -{ - "data": { - "subgraphs": [ - { - "id": "HXFFyPyPtUrBC78p2bntnQu7rXPKo79GA5wQQ2YxLtTE", - "owner": { - "id": "0x4bb4c1b0745ef7b4642feeccd0740dec417ca0a0" - }, - "subgraphNumber": "3", - "metadataHash": "0x8cf2627713b50bce6551ad0451f73517cf731431daecd557114cc3cd6d0e1a4a" - }, - { - "id": "2Kr8drrtxmCN7vbpD6NYHSkNTNBv5pQRVtSNi4m4UvRF", - "owner": { - "id": "0xbeb1faa6e7e39c7d9bdab03a7a362fe9d73d7c61" - }, - "subgraphNumber": "0", - "metadataHash": "0x0c4b3bea53d0f01dd74d57ce5408aaadba15df4077cc3dc38085dd3796eeda03" - }, - { - "id": "2qdAX71bB2DfXbNKgpTv3b5gY4EnfYNQVpL71o7e7nEz", - "owner": { - "id": "0xefe232372e2e30951fa90d7a800d5938eedc935d" - }, - "subgraphNumber": "0", - "metadataHash": "0x5998fe5b6e689794aebf986beac1557367cba0f18d666bf8ec3aa037e6f08877" - }, - { - "id": "ba7J1xjrWGhVk5Hbonf42Fv6B9MZdzs6pN3nzeRfYzY", - "owner": { - "id": "0xadafc1e419d03c84661b00c022ecd0101f190172" - }, - "subgraphNumber": "0", - "metadataHash": "0xc0e9a91e403be87c55494a6e74f493a3da829cc9cd0bc603472212cb68641f3b" - }, - { - "id": "HJxHihBy3o1na3FDuMBPXL9ytwgP64wu3wDWZGWst3w2", - "owner": { - "id": "0x33d0d63a1f0da691b283aedd29875afeaf98e6ba" - }, - "subgraphNumber": "1", - "metadataHash": "0x2f44901f32e2d14363a5237687b8daa8e724e359df8378e997c84f1c387bc32d" - }, - { - "id": "Druh8CtrwDPwwWNdG2FPEZUeR3XP39BzvJ6QW2cZUX1N", - "owner": { - "id": "0x9f9b34c201efe888b02fdcab52422199957a0407" - }, - "subgraphNumber": "1", - "metadataHash": "0x2f44901f32e2d14363a5237687b8daa8e724e359df8378e997c84f1c387bc32d" - }, - { - "id": "DncWyiEVwDE7RDgDgbWupaqwLvnKC78xHVyRCc6Ys6M4", - "owner": { - "id": "0x33d0d63a1f0da691b283aedd29875afeaf98e6ba" - }, - "subgraphNumber": "0", - "metadataHash": "0x2f44901f32e2d14363a5237687b8daa8e724e359df8378e997c84f1c387bc32d" - }, - { - "id": "6N1FHPcJ1sQCBzmWrrNntLibGRvGHJiWWFRY7LJcmtHJ", - "owner": { - "id": "0x9f9b34c201efe888b02fdcab52422199957a0407" - }, - "subgraphNumber": "0", - "metadataHash": "0x2f44901f32e2d14363a5237687b8daa8e724e359df8378e997c84f1c387bc32d" - }, - { - "id": "okz4sT96je97dMiAJtPYmkBwDN9rov9mcMYTPskGtrq", - "owner": { - "id": "0x534a9ac3d3401f80800375680bded6a84137d296" - }, - "subgraphNumber": "4", - "metadataHash": "0xff8e9977885ae9aad139fcb0eefe78ab66ff374f86d491e878006c7d2a1c0070" - }, - { - "id": "HNWN5ymGqWGpvBbYFRyebhdGeSac3CDEssY3dpoMKgdK", - "owner": { - "id": "0xa508c16666c5b8981fa46eb32784fccc01942a71" - }, - "subgraphNumber": "0", - "metadataHash": "0xbf9b4090ff738cb433e532263065756284dc57c4092ef377f52a445e3322976d" - }, - { - "id": "Gv1rewdMuEeDQ9sWsWMjJfwKWrTxo5e19vgxr1br7Ut4", - "owner": { - "id": "0x673b6e9fe607f6ddf4a4f25b386b846c5c82995e" - }, - "subgraphNumber": "0", - "metadataHash": "0xddb85d0e63098b79b4340052b866c4d8eb638a81d74c6e135c979d23f4d1e93f" - }, - { - "id": "GNpbzUsp352dYJwP7jEL7yRJqA7uf4zfoLAHk5PiMMJu", - "owner": { - "id": "0xde0c83462a75036b74928865941b97d83a4bc994" - }, - "subgraphNumber": "0", - "metadataHash": "0x67e55fbda97b73cdb4a92f475568f27f054c6e97e8286c51256a79df9d3eb826" - }, - { - "id": "Fzw1DXaBaSeBAqg4R6qa8KZDTp7j1jpNhfAP9fpHkCGb", - "owner": { - "id": "0x9e795fc1c644ecad8031ed52856fd64b65a678ac" - }, - "subgraphNumber": "0", - "metadataHash": "0xa035f76b2dc73756a5209382a1fbf22fc714027d687439a478f7e1d38aba22ec" - }, - { - "id": "FTTVbaL4HBzjhzgstyLEhN3WrjEMDB8owqfLARNtJ2FX", - "owner": { - "id": "0xefe232372e2e30951fa90d7a800d5938eedc935d" - }, - "subgraphNumber": "4", - "metadataHash": "0x5998fe5b6e689794aebf986beac1557367cba0f18d666bf8ec3aa037e6f08877" - }, - { - "id": "EtGRQWQruHSy9sZUHwX4FKXQPT3p15xsV6Bx3Pt5LMHU", - "owner": { - "id": "0x03c65e533cc73cc65cd71a0cb65efa4b11e74c22" - }, - "subgraphNumber": "3", - "metadataHash": "0x3b9721ef654fc1de4fb3654d8b27eb152face455c64bab02011e417575cc1077" - }, - { - "id": "EsEnqMt8GrWXSWjdkTphcauCRvcyC1eC4U9sdBRZEB84", - "owner": { - "id": "0x5991a31ab4095cb5470a5bd532e02568393b06d5" - }, - "subgraphNumber": "1", - "metadataHash": "0x56899ebbe432ee9fe47db5fe709b0c47ef0e0fbe3cc5a7fde97861b0ff37cb41" - }, - { - "id": "E1vew3qN9voUJCmkPWUubQBBppiBuC2f3T2Exh2CsyDH", - "owner": { - "id": "0xefe232372e2e30951fa90d7a800d5938eedc935d" - }, - "subgraphNumber": "2", - "metadataHash": "0x5998fe5b6e689794aebf986beac1557367cba0f18d666bf8ec3aa037e6f08877" - }, - { - "id": "Dz55sRXXemD3QUos3orXQWm5GQs3ACECGDbE86ZVsyrG", - "owner": { - "id": "0x03c65e533cc73cc65cd71a0cb65efa4b11e74c22" - }, - "subgraphNumber": "4", - "metadataHash": "0x5998fe5b6e689794aebf986beac1557367cba0f18d666bf8ec3aa037e6f08877" - }, - { - "id": "DSpmdAKr3pzkcU5R6NgreBsYabdkY9C5ocBbP9Sk4FAA", - "owner": { - "id": "0x5ca1ea5549e4e7cb64ae35225e11865d2572b3f9" - }, - "subgraphNumber": "0", - "metadataHash": "0x7369f32b51f12127e0591f079c99f6b997d90aecbd938270c0d0ecf902edfb2a" - }, - { - "id": "CvkpgHJW4ApYwYX831M8GpN472FbnhGKjdL2ERT5wie9", - "owner": { - "id": "0x9d196973c0c5bd65ccfb797a2c23eb5c1faba7ab" - }, - "subgraphNumber": "1", - "metadataHash": "0x82a07620f76548e3d7b38b468b9b67b2e9355a3dcef48ac1a4ebf96eb588685e" - }, - { - "id": "BM3vXF5bRFy4NRYrYirEnkfNsi3veUetKrqvqxmwMPSP", - "owner": { - "id": "0x03c65e533cc73cc65cd71a0cb65efa4b11e74c22" - }, - "subgraphNumber": "0", - "metadataHash": "0xbd37407528628edbdc2703dd0297097e41bb95b101c382d6f8fddcd8f15df58a" - }, - { - "id": "B39LecV8UELFUxULRswPhcifsZWJ6qaLL29cGYMcCK4s", - "owner": { - "id": "0x534a9ac3d3401f80800375680bded6a84137d296" - }, - "subgraphNumber": "3", - "metadataHash": "0xeeee64a3f268f229331f0088d0ef92b65257eee2a50492ed763dd21254c00799" - }, - { - "id": "A4xtyhChUywTCww37fFBtjd1poY2kHtMNu2fwKuxSSB", - "owner": { - "id": "0xa508c16666c5b8981fa46eb32784fccc01942a71" - }, - "subgraphNumber": "2", - "metadataHash": "0x7a7ca98b60850593645a9348d47143c7f550e790e48e51c4820baddd92319d35" - }, - { - "id": "9xL1fnWqqQ4QjC9Z33hTXkgPBZAHU14Tzqzf5mEtDbxM", - "owner": { - "id": "0xefe232372e2e30951fa90d7a800d5938eedc935d" - }, - "subgraphNumber": "3", - "metadataHash": "0x5998fe5b6e689794aebf986beac1557367cba0f18d666bf8ec3aa037e6f08877" - }, - { - "id": "9js1z4GHLYQuGQ6yGs9SdcCRUDTCXBLKY7e37KFUKRre", - "owner": { - "id": "0x69aa76325b4890f0fa9b44f4d99d56f3834738b4" - }, - "subgraphNumber": "0", - "metadataHash": "0xcaf93a686e6b7f7d1b78543ffc512d4046b493b4d125dc3c6fc5469c4af57d70" - }, - { - "id": "9EjsLLp4Ti39xUfZE1Xzz6bjwuzJSc3tVb3FvYxugq65", - "owner": { - "id": "0x03c65e533cc73cc65cd71a0cb65efa4b11e74c22" - }, - "subgraphNumber": "2", - "metadataHash": "0x3b9721ef654fc1de4fb3654d8b27eb152face455c64bab02011e417575cc1077" - }, - { - "id": "8mteqsnKePqyqAXVb6e6KkcBqJpR3tjwPacgP1jSmxSE", - "owner": { - "id": "0x5991a31ab4095cb5470a5bd532e02568393b06d5" - }, - "subgraphNumber": "3", - "metadataHash": "0x7876a5e7860a6abe0acebdde502d39b180856c41163d536fa632725f1f58cd3b" - }, - { - "id": "7Y8ZaLST7xYVcVZsVRiRw2xhnDe9C62nLmLQpxffPVj", - "owner": { - "id": "0xefe232372e2e30951fa90d7a800d5938eedc935d" - }, - "subgraphNumber": "6", - "metadataHash": "0xbdfd81ed973596a181375b4e3c69d5a1fe52d2c41a07ec0ba5621a1e2bd35ddb" - }, - { - "id": "7fspzcqYDgVF1gV7VxyA2k833EmhYqnsgRvXk9YAEPT4", - "owner": { - "id": "0xa508c16666c5b8981fa46eb32784fccc01942a71" - }, - "subgraphNumber": "1", - "metadataHash": "0x7400f53d252f899ec54094484d9dc3102e38147755f7dbf9ddb3ad4e9487acb9" - }, - { - "id": "7Ew6SQmN1Xdjk5YEonqYAaT1v2HikNa92VZUQPpJ2QvX", - "owner": { - "id": "0x534a9ac3d3401f80800375680bded6a84137d296" - }, - "subgraphNumber": "5", - "metadataHash": "0x54bc59114d36eaec4ff6a196c6e40a33d4eca894c514f9b127238424a4188274" - }, - { - "id": "6SVLBaaAcujGWswitHpLixkgjQVZugrQHj8AihGJgFgD", - "owner": { - "id": "0xfcb576bc8ee187945fa00f0daa7bbe9c1b51b0c9" - }, - "subgraphNumber": "2", - "metadataHash": "0x7aba482a8b8b809d890f865548b5a2a194271ca9afcb0f5e47a604a286f4d9aa" - }, - { - "id": "6bsUMv2twT2SA9RCToCBDXfStADVvDh2nvEmSuX4F8kc", - "owner": { - "id": "0x0b1302c23d9eb4b42a74cbefc4f9b3081ff1bf18" - }, - "subgraphNumber": "0", - "metadataHash": "0xfa65e518e90b49586e173e29ae25202995c400ee021353d90c42b682c34494d6" - }, - { - "id": "6AmTt2V2PQiKJuHF6X1ySjJpNTuwzvUTmUQRYL74eFxT", - "owner": { - "id": "0xefe232372e2e30951fa90d7a800d5938eedc935d" - }, - "subgraphNumber": "5", - "metadataHash": "0x5998fe5b6e689794aebf986beac1557367cba0f18d666bf8ec3aa037e6f08877" - }, - { - "id": "5vhK8XAGXiK4zwGPcN2C63TjhAHtqyqoUcS6RGR5H86i", - "owner": { - "id": "0x673b6e9fe607f6ddf4a4f25b386b846c5c82995e" - }, - "subgraphNumber": "3", - "metadataHash": "0xddb85d0e63098b79b4340052b866c4d8eb638a81d74c6e135c979d23f4d1e93f" - }, - { - "id": "5qyjWvZ1FLwAsBeJvPuyvhBddJDavKYyyjdXQZ28REAJ", - "owner": { - "id": "0xf556322b497b475d535d0d4048f56c092a0ea3ad" - }, - "subgraphNumber": "1", - "metadataHash": "0x9bdee2ed59fcf9532df294d9b07fa1ef3c127a144658c35fa3c1d9fee4f7cc2a" - }, - { - "id": "57MzWCjGmKWrUYGHgEVne3ieFCXnwAUHKKDLXm4mJPJb", - "owner": { - "id": "0x03c65e533cc73cc65cd71a0cb65efa4b11e74c22" - }, - "subgraphNumber": "6", - "metadataHash": "0x5998fe5b6e689794aebf986beac1557367cba0f18d666bf8ec3aa037e6f08877" - }, - { - "id": "4MQwNPuso8ABKHhddwUmJCaPqg2w4Vst6B2pPr21Ygn9", - "owner": { - "id": "0xefe232372e2e30951fa90d7a800d5938eedc935d" - }, - "subgraphNumber": "1", - "metadataHash": "0x5998fe5b6e689794aebf986beac1557367cba0f18d666bf8ec3aa037e6f08877" - }, - { - "id": "47BEnfQFYjURYjD8r2JNoFjXkYHTqJ3xfcnrKUrbnTiq", - "owner": { - "id": "0x534a9ac3d3401f80800375680bded6a84137d296" - }, - "subgraphNumber": "2", - "metadataHash": "0xba7ebef4cc835813d6bdd70b2ee4db65594898e57e3412197964bd30b383e5fb" - }, - { - "id": "3zULJJdf5s9d87rn5aDgoT4pE5Uih3z6SAEumXy1dQqv", - "owner": { - "id": "0x03c65e533cc73cc65cd71a0cb65efa4b11e74c22" - }, - "subgraphNumber": "1", - "metadataHash": "0x45926246b7a13663ccacd05331419b7c2519bbc8b8bb6be89ab285022eb6e557" - }, - { - "id": "3sHuzLagq1T5yYrsctLtz7QdcNAGsdsodHk7j3g2mrVV", - "owner": { - "id": "0x03c65e533cc73cc65cd71a0cb65efa4b11e74c22" - }, - "subgraphNumber": "5", - "metadataHash": "0x5998fe5b6e689794aebf986beac1557367cba0f18d666bf8ec3aa037e6f08877" - }, - { - "id": "3caWu7riX9kGBe2zmXVc4V8vQQ5U1c3QAjzeSGNT7Jo", - "owner": { - "id": "0x072e829ff4e1c9bdb95acaf947f8ec262ecdf92f" - }, - "subgraphNumber": "0", - "metadataHash": "0x7f6fe58ac5f0c86007d61813897fcdb956f3f71e816f105b18f22cde4213ca20" - }, - { - "id": "2qbnVdk8RfatovF1sogHzH1eZ52DoziY6d7qR5KHzk8X", - "owner": { - "id": "0xc320812b4bbb7f2369ee1316fd703de403ad0d2e" - }, - "subgraphNumber": "0", - "metadataHash": "0xd9ff52d1ac82d8ebe295ee1bf3fd0f352eacb134039af2464342a7285cf0d921" - } - ] - } -} diff --git a/packages/contracts/scripts/ops/20240208-migrate-legacy-subgraphs/migrate.ts b/packages/contracts/scripts/ops/20240208-migrate-legacy-subgraphs/migrate.ts deleted file mode 100644 index 1441ecf82..000000000 --- a/packages/contracts/scripts/ops/20240208-migrate-legacy-subgraphs/migrate.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { confirm, subgraphIdToHex } from '@graphprotocol/sdk' -import { BigNumber, ethers } from 'ethers' -import hre from 'hardhat' - -import data from './data.json' - -async function main() { - const graph = hre.graph() - const deployer = await graph.getDeployer() - - // First estimate cost - const gasEstimate = await data.data.subgraphs.reduce( - async (acc, subgraph) => { - return (await acc).add( - await graph.contracts.L1GNS.connect(deployer).estimateGas.migrateLegacySubgraph( - subgraph.owner.id, - subgraph.subgraphNumber, - subgraph.metadataHash, - ), - ) - }, - Promise.resolve(BigNumber.from(0)), - ) - const gasPrice = await graph.provider.getGasPrice() - const cost = ethers.utils.formatEther(gasEstimate.mul(gasPrice)) - - // Ask for confirmation - if ( - !(await confirm( - `This script will migrate ${data.data.subgraphs.length} legacy subgraphs, with an approximate cost of ${cost} Ξ. Are you sure you want to continue?`, - false, - )) - ) - return - - // do it - for (const subgraph of data.data.subgraphs) { - console.log(`Migrating legacy subgraph ${subgraph.owner.id}/${subgraph.subgraphNumber}...`) - - const legacyKey = await graph.contracts.L1GNS.legacySubgraphKeys(subgraphIdToHex(subgraph.id)) - if (legacyKey.account !== ethers.constants.AddressZero) { - console.log(` - Already migrated, skipping`) - continue - } - try { - const tx = await graph.contracts.L1GNS.connect(deployer).migrateLegacySubgraph( - subgraph.owner.id, - subgraph.subgraphNumber, - subgraph.metadataHash, - ) - const receipt = await tx.wait() - if (receipt.status == 1) { - console.log(` ✔ Migration succeeded!`) - } else { - console.log(` ✖ Migration failed!`) - console.log(receipt) - } - } catch (error) { - console.log(error) - } - } -} - -main().catch((error) => { - console.error(error) - process.exitCode = 1 -}) diff --git a/packages/contracts/scripts/ops/testDisputeConflict/acceptDispute.ts b/packages/contracts/scripts/ops/testDisputeConflict/acceptDispute.ts deleted file mode 100644 index 013daec30..000000000 --- a/packages/contracts/scripts/ops/testDisputeConflict/acceptDispute.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Wallet } from 'ethers' -import hre from 'hardhat' - -async function main() { - const graph = hre.graph() - const arbitratorPrivateKey = process.env.ARBITRATOR_PRIVATE_KEY - const arbitrator = new Wallet(arbitratorPrivateKey, graph.provider) - console.log('Arbitrator:', arbitrator.address) - - const disputeId = '0x35e6e68aa71ee59cb710d8005563d63d644f11f2eee879eca9bc22f523c9fade' - console.log('Dispute ID:', disputeId) - - // Accept dispute - await graph.contracts.DisputeManager.connect(arbitrator).acceptDispute(disputeId) -} - -main().catch((error) => { - console.error(error) - process.exitCode = 1 -}) diff --git a/packages/contracts/scripts/ops/testDisputeConflict/createDispute.ts b/packages/contracts/scripts/ops/testDisputeConflict/createDispute.ts deleted file mode 100644 index 452f78726..000000000 --- a/packages/contracts/scripts/ops/testDisputeConflict/createDispute.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { buildAttestation, encodeAttestation, GraphChainId, randomHexBytes } from '@graphprotocol/sdk' -import hre from 'hardhat' - -async function main() { - const graph = hre.graph() - const deployer = await graph.getDeployer() - const [indexer] = await graph.getTestAccounts() - const indexerChannelPrivKey = '0x82226c70efbe0d9525a5f9dc85c29b11fed1f46798a416b7626e21fdd6518d08' - - console.log('Deployer:', deployer.address) - console.log('Indexer:', indexer.address) - - const receipt = { - requestCID: '0x8bec406793c8e1c5d4bd4e059833e95b7a9aeed6a118cbe335a79735836f9ff7', - responseCID: '0xbdfc41643b5ff8d55f6cdb50f05575e1fdf177fa54d98cae1b9c76d8b360ff57', - subgraphDeploymentID: '0xa3bfbfc6f53fd8a61b78e0b9a90c7fbe9ff290cba87b045bc476137fb2963cf9', - } - const receipt2 = { ...receipt, responseCID: randomHexBytes() } - - const attestation1 = await buildAttestation( - receipt, - indexerChannelPrivKey, - graph.contracts.DisputeManager.address, - graph.chainId as GraphChainId, - ) - const attestation2 = await buildAttestation( - receipt2, - indexerChannelPrivKey, - graph.contracts.DisputeManager.address, - graph.chainId as GraphChainId, - ) - - console.log('Attestation 1:', attestation1) - console.log('Attestation 2:', attestation2) - - // Create dispute - await graph.contracts.DisputeManager.connect(deployer).createQueryDisputeConflict( - encodeAttestation(attestation1), - encodeAttestation(attestation2), - ) -} - -main().catch((error) => { - console.error(error) - process.exitCode = 1 -}) diff --git a/packages/contracts/scripts/ops/testDisputeConflict/setupIndexer.ts b/packages/contracts/scripts/ops/testDisputeConflict/setupIndexer.ts deleted file mode 100644 index 393dc7e35..000000000 --- a/packages/contracts/scripts/ops/testDisputeConflict/setupIndexer.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { allocateFrom, deriveChannelKey, stake, toGRT } from '@graphprotocol/sdk' -import hre from 'hardhat' - -async function main() { - const graph = hre.graph() - const deployer = await graph.getDeployer() - const [indexer] = await graph.getTestAccounts() - - console.log('Deployer:', deployer.address) - console.log('Indexer:', indexer.address) - - const receipt = { - requestCID: '0x8bec406793c8e1c5d4bd4e059833e95b7a9aeed6a118cbe335a79735836f9ff7', - responseCID: '0xbdfc41643b5ff8d55f6cdb50f05575e1fdf177fa54d98cae1b9c76d8b360ff57', - subgraphDeploymentID: '0xa3bfbfc6f53fd8a61b78e0b9a90c7fbe9ff290cba87b045bc476137fb2963cf9', - } - - console.log('Receipt requestCID:', receipt.requestCID) - console.log('Receipt response CID:', receipt.responseCID) - console.log('Receipt subgraphDeploymentID:', receipt.subgraphDeploymentID) - - const indexerChannelKey = deriveChannelKey() - console.log('Indexer channel key:', indexerChannelKey.address) - console.log('Indexer channel key privKey:', indexerChannelKey.privKey) - - // Set up indexer - await deployer.sendTransaction({ value: toGRT('0.05'), to: indexer.address }) - await graph.contracts.GraphToken.connect(deployer).transfer(indexer.address, toGRT('100000')) - await stake(graph.contracts, indexer, { amount: toGRT('100000') }) - await allocateFrom(graph.contracts, indexer, { - channelKey: indexerChannelKey, - amount: toGRT('100000'), - subgraphDeploymentID: receipt.subgraphDeploymentID, - }) -} - -main().catch((error) => { - console.error(error) - process.exitCode = 1 -}) diff --git a/packages/contracts/tsconfig.json b/packages/contracts/tsconfig.json index fba9f48b3..a5e3c1eac 100644 --- a/packages/contracts/tsconfig.json +++ b/packages/contracts/tsconfig.json @@ -4,6 +4,6 @@ "outDir": "./build", "declarationDir": "./types" }, - "include": ["src/**/*.ts", "test/**/*.ts", "deploy/**/*.ts"], + "include": ["src/**/*.ts", "deploy/**/*.ts"], "exclude": ["node_modules", "build", "types", "artifacts", "cache"] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f9c30f314..af8a98d0e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -373,154 +373,6 @@ importers: specifier: ^17.0.0 version: 17.7.2 - packages/contracts-test: - dependencies: - '@graphprotocol/contracts': - specifier: workspace:^ - version: link:../contracts - '@graphprotocol/interfaces': - specifier: workspace:^ - version: link:../interfaces - '@graphprotocol/sdk': - specifier: 0.6.0 - version: 0.6.0(bufferutil@4.0.9)(encoding@0.1.13)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10) - devDependencies: - '@arbitrum/sdk': - specifier: ~3.1.13 - version: 3.1.13(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@defi-wonderland/smock': - specifier: ^2.4.1 - version: 2.4.1(@ethersproject/abi@5.8.0)(@ethersproject/abstract-provider@5.8.0)(@ethersproject/abstract-signer@5.8.0)(@nomiclabs/hardhat-ethers@2.2.3(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10)))(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10)) - '@ethersproject/abi': - specifier: ^5.8.0 - version: 5.8.0 - '@ethersproject/abstract-provider': - specifier: ^5.8.0 - version: 5.8.0 - '@ethersproject/abstract-signer': - specifier: ^5.8.0 - version: 5.8.0 - '@ethersproject/bytes': - specifier: ^5.8.0 - version: 5.8.0 - '@ethersproject/providers': - specifier: ^5.8.0 - version: 5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@graphprotocol/common-ts': - specifier: ^1.8.3 - version: 1.8.7(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) - '@nomicfoundation/hardhat-network-helpers': - specifier: ^1.0.0 - version: 1.1.0(hardhat@2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10)) - '@nomiclabs/hardhat-ethers': - specifier: ^2.2.3 - version: 2.2.3(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10)) - '@nomiclabs/hardhat-etherscan': - specifier: ^3.1.0 - version: 3.1.8(hardhat@2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10)) - '@nomiclabs/hardhat-waffle': - specifier: ^2.0.6 - version: 2.0.6(@ensdomains/ens@0.4.5)(@ensdomains/resolver@0.2.4)(@nomiclabs/hardhat-ethers@2.2.3(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10)))(@types/sinon-chai@3.2.12)(ethereum-waffle@4.0.10(@ensdomains/ens@0.4.5)(@ensdomains/resolver@0.2.4)(@ethersproject/abi@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(encoding@0.1.13)(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typescript@5.9.3))(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10)) - '@openzeppelin/contracts': - specifier: 3.4.2 - version: 3.4.2 - '@openzeppelin/contracts-upgradeable': - specifier: 3.4.2 - version: 3.4.2 - '@openzeppelin/hardhat-upgrades': - specifier: ^1.22.1 - version: 1.28.0(@nomiclabs/hardhat-ethers@2.2.3(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10)))(@nomiclabs/hardhat-etherscan@3.1.8(hardhat@2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10)))(encoding@0.1.13)(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10)) - '@typechain/ethers-v5': - specifier: ^10.2.1 - version: 10.2.1(@ethersproject/abi@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typechain@8.3.2(patch_hash=b34ed6afcf99760666fdc85ecb2094fdd20ce509f947eb09cef21665a2a6a1d6)(typescript@5.9.3))(typescript@5.9.3) - '@typechain/hardhat': - specifier: ^6.1.2 - version: 6.1.6(@ethersproject/abi@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(@typechain/ethers-v5@10.2.1(@ethersproject/abi@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typechain@8.3.2(patch_hash=b34ed6afcf99760666fdc85ecb2094fdd20ce509f947eb09cef21665a2a6a1d6)(typescript@5.9.3))(typescript@5.9.3))(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10))(typechain@8.3.2(patch_hash=b34ed6afcf99760666fdc85ecb2094fdd20ce509f947eb09cef21665a2a6a1d6)(typescript@5.9.3)) - '@types/chai': - specifier: ^4.2.0 - version: 4.3.20 - '@types/mocha': - specifier: '>=9.1.0' - version: 10.0.10 - '@types/node': - specifier: ^20.17.50 - version: 20.19.14 - '@types/sinon-chai': - specifier: ^3.2.12 - version: 3.2.12 - arbos-precompiles: - specifier: ^1.0.2 - version: 1.0.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10) - chai: - specifier: ^4.2.0 - version: 4.5.0 - dotenv: - specifier: ^16.5.0 - version: 16.6.1 - eslint: - specifier: 'catalog:' - version: 9.39.2(jiti@2.5.1) - ethereum-waffle: - specifier: ^4.0.10 - version: 4.0.10(@ensdomains/ens@0.4.5)(@ensdomains/resolver@0.2.4)(@ethersproject/abi@5.8.0)(@ethersproject/providers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(encoding@0.1.13)(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typescript@5.9.3) - ethers: - specifier: ^5.7.0 - version: 5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - form-data: - specifier: ^4.0.0 - version: 4.0.4 - glob: - specifier: 'catalog:' - version: 11.0.3 - graphql: - specifier: ^16.11.0 - version: 16.11.0 - graphql-tag: - specifier: ^2.12.4 - version: 2.12.6(graphql@16.11.0) - hardhat: - specifier: 'catalog:' - version: 2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10) - hardhat-abi-exporter: - specifier: ^2.11.0 - version: 2.11.0(hardhat@2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10)) - hardhat-contract-sizer: - specifier: ^2.10.0 - version: 2.10.1(hardhat@2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10)) - hardhat-gas-reporter: - specifier: ^1.0.8 - version: 1.0.10(bufferutil@4.0.9)(hardhat@2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) - hardhat-storage-layout: - specifier: ^0.1.7 - version: 0.1.7(hardhat@2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10)) - prettier: - specifier: 'catalog:' - version: 3.8.1 - prettier-plugin-solidity: - specifier: 'catalog:' - version: 2.1.0(prettier@3.8.1) - solidity-coverage: - specifier: ^0.8.16 - version: 0.8.16(hardhat@2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10)) - ts-node: - specifier: ^10.9.2 - version: 10.9.2(@types/node@20.19.14)(typescript@5.9.3) - typechain: - specifier: ^8.3.2 - version: 8.3.2(patch_hash=b34ed6afcf99760666fdc85ecb2094fdd20ce509f947eb09cef21665a2a6a1d6)(typescript@5.9.3) - typescript: - specifier: 'catalog:' - version: 5.9.3 - winston: - specifier: ^3.3.3 - version: 3.17.0 - yaml: - specifier: ^1.10.2 - version: 1.10.2 - yargs: - specifier: ^17.0.0 - version: 17.7.2 - packages/data-edge: devDependencies: '@nomicfoundation/hardhat-chai-matchers': @@ -2699,9 +2551,6 @@ packages: '@ethersproject/networks@5.6.1': resolution: {integrity: sha512-b2rrupf3kCTcc3jr9xOWBuHylSFtbpJf79Ga7QR98ienU2UqGimPGEsYMgbI29KHJfA5Us89XwGVmxrlxmSrMg==} - '@ethersproject/networks@5.7.0': - resolution: {integrity: sha512-MG6oHSQHd4ebvJrleEQQ4HhVu8Ichr0RDYEfHzsVAVjHNM+w36x9wp9r+hf1JstMXtseXDtkiVoARAG6M959AA==} - '@ethersproject/networks@5.7.1': resolution: {integrity: sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==} @@ -2729,9 +2578,6 @@ packages: '@ethersproject/providers@5.6.2': resolution: {integrity: sha512-6/EaFW/hNWz+224FXwl8+HdMRzVHt8DpPmu5MZaIQqx/K/ELnC9eY236SMV7mleCM3NnEArFwcAAxH5kUUgaRg==} - '@ethersproject/providers@5.7.0': - resolution: {integrity: sha512-+TTrrINMzZ0aXtlwO/95uhAggKm4USLm1PbeCBR/3XZ7+Oey+3pMyddzZEyRhizHpy1HXV0FRWRMI1O3EGYibA==} - '@ethersproject/providers@5.7.2': resolution: {integrity: sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==} @@ -2822,9 +2668,6 @@ packages: '@ethersproject/web@5.6.0': resolution: {integrity: sha512-G/XHj0hV1FxI2teHRfCGvfBUHFmU+YOSbCxlAMqJklxSa7QMiHFQfAxvwY2PFqgvdkxEKwRNr/eCjfAPEm2Ctg==} - '@ethersproject/web@5.7.0': - resolution: {integrity: sha512-ApHcbbj+muRASVDSCl/tgxaH2LBkRMEYfLOLVa0COipx0+nlu0QKet7U2lEg0vdkh8XRSLf2nd1f1Uk9SrVSGA==} - '@ethersproject/web@5.7.1': resolution: {integrity: sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==} @@ -2915,25 +2758,13 @@ packages: '@graphprotocol/common-ts@1.8.7': resolution: {integrity: sha512-B1LZHbWnP7HOjkycN/y0dB47yZ8PuhOrZHGLsRo+4qhpMJx55NzJBfPOPnivnrfbM7wL69l158NpSHhpr9Mgug==} - '@graphprotocol/common-ts@2.0.11': - resolution: {integrity: sha512-WtQGYMGVwaXDIli+OCAZUSqh8+ql9THzjztqvLGeSbAIPKxysvej9vua0voMguqEkI/RyEEMBajelodMzzZlEw==} - '@graphprotocol/contracts@2.1.0': resolution: {integrity: sha512-SeymJCUxBp488K/KNi77EKvrkPT0t/7rERGp8zFkmf5KByerjihhE9Ty9oXJAU9RzbUpxsU0JkPBSmiw9/2DYQ==} - '@graphprotocol/contracts@5.3.3': - resolution: {integrity: sha512-fmFSKr+VDinWWotj2q/Ztn92PppcRrYXeO/62gLgkLos/DcYa7bGWKbcOWyMUw0vsUvXxk6QAtr5o/LG3yQ1WQ==} - - '@graphprotocol/contracts@7.2.1': - resolution: {integrity: sha512-Ge2RD5QoEeSabCzdlMG3DT1mdsTGaD3aqxDsxRWwDx9nejudkrTlhjTTRAGVtBkfxlEy7P4Kxejr+fZmK29yRg==} - '@graphprotocol/pino-sentry-simple@0.7.1': resolution: {integrity: sha512-iccKFdFBjarSp8/liXuK1EtGq8Vwn118tqymbOJBxblecRsi4rOebk63qnL+dK/a0IvxH6h2+RjjWDbRt7UsUA==} engines: {node: '>=10'} - '@graphprotocol/sdk@0.6.0': - resolution: {integrity: sha512-oZGzUNFvZZ5MVsBvKXE0IzVBMzQ6Rxp9L3eCSeeOvHxQD7fUTFwbNr3SsGQagQlo3axLemAvK1hrl+aB5DlZIA==} - '@graphql-codegen/core@3.1.0': resolution: {integrity: sha512-DH1/yaR7oJE6/B+c6ZF2Tbdh7LixF1K8L+8BoSubjNyQ8pNwR4a70mvc1sv6H7qgp6y1bPQ9tKE+aazRRshysw==} peerDependencies: @@ -4376,21 +4207,11 @@ packages: peerDependencies: graphql: ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - '@urql/core@3.1.0': - resolution: {integrity: sha512-6pYB4/WGZmuCxCc+h8AX9h+g1o75cPgMrcan+G/pYEDGAd6+PXoEuDumhEXpwu4vnkqCvVnFELEYcqkaM8ddPg==} - peerDependencies: - graphql: ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - '@urql/exchange-execute@1.2.2': resolution: {integrity: sha512-KebdnKWMKI1NkRtIxp8YIouynOaFnhcdaMNCcJEtp+kmY4vGZUgdxT/SIzTPXXYJvk5G2aiQ/JMr97I+wM/EHA==} peerDependencies: graphql: ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - '@urql/exchange-execute@2.1.0': - resolution: {integrity: sha512-b84hq5EPgbERmg+98SqmMZqD5W3YrFkFii/6ZWVm5zsVdb+c0ZhRpUrpOCSemh5Jl5X291PG0WUUy2hdMtjraQ==} - peerDependencies: - graphql: ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - '@wagmi/cli@2.5.1': resolution: {integrity: sha512-X8cFOy9CtVZOiixqW+Y8/EbL91MLWfzV1Wd+M88LErxLBtP8K0Z4P6lS8I5nrH64zm5dC2ZN9SlrZMSbwQqbOQ==} hasBin: true @@ -4829,9 +4650,6 @@ packages: base-x@3.0.11: resolution: {integrity: sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==} - base-x@4.0.1: - resolution: {integrity: sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw==} - base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -4901,14 +4719,6 @@ packages: resolution: {integrity: sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==} engines: {node: '>= 0.8'} - body-parser@1.20.1: - resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - - body-parser@1.20.2: - resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - bowser@2.14.1: resolution: {integrity: sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==} @@ -4950,9 +4760,6 @@ packages: bs58@4.0.1: resolution: {integrity: sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==} - bs58@5.0.0: - resolution: {integrity: sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==} - bs58check@2.1.2: resolution: {integrity: sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==} @@ -5371,10 +5178,6 @@ packages: resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==} engines: {node: '>= 0.6'} - cookie@0.5.0: - resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} - engines: {node: '>= 0.6'} - core-js-pure@3.45.1: resolution: {integrity: sha512-OHnWFKgTUshEU8MK+lOs1H8kC8GkTi9Z1tvNkxrCcw9wl3MJIO7q2ld77wjWn4/xuGrVu2X+nME1iIIPBSdyEQ==} @@ -5437,9 +5240,6 @@ packages: cross-fetch@3.2.0: resolution: {integrity: sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==} - cross-fetch@4.0.0: - resolution: {integrity: sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==} - cross-inspect@1.0.1: resolution: {integrity: sha512-Pcw1JTvZLSJH83iiGWt6fRcT+BjZlCDRVwYLbUcHzv/CRpB7r0MlSrGbIyQvVSNyGnbt7G4AXuyCiDR3POvZ1A==} engines: {node: '>=16.0.0'} @@ -5984,9 +5784,6 @@ packages: ethers@5.6.2: resolution: {integrity: sha512-EzGCbns24/Yluu7+ToWnMca3SXJ1Jk1BvWB7CCmVNxyOeM4LLvw2OLuIHhlkhQk1dtOcj9UMsdkxUh8RiG1dxQ==} - ethers@5.7.0: - resolution: {integrity: sha512-5Xhzp2ZQRi0Em+0OkOcRHxPzCfoBfgtOQA+RUylSkuHbhTEaQklnYi2hsWbRgs3ztJsXVXd9VKBcO1ScWL8YfA==} - ethers@5.7.2: resolution: {integrity: sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==} @@ -6037,10 +5834,6 @@ packages: resolution: {integrity: sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==} engines: {node: '>= 0.10.0'} - express@4.18.2: - resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} - engines: {node: '>= 0.10.0'} - extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -6155,10 +5948,6 @@ packages: resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==} engines: {node: '>= 0.8'} - finalhandler@1.2.0: - resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} - engines: {node: '>= 0.8'} - find-replace@3.0.0: resolution: {integrity: sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==} engines: {node: '>=4.0.0'} @@ -6525,10 +6314,6 @@ packages: resolution: {integrity: sha512-xm+ANmA16BzCT5pLjuXySbQVFwH3oJctUVdy81w1sV0vBU0KgDdBGtxQOUd5zqOBk/JayAFeG8Dlmeq74rjm/A==} engines: {node: ^12.22.0 || ^14.16.0 || >=16.0.0} - graphql@16.8.0: - resolution: {integrity: sha512-0oKGaR+y3qcS5mCu1vb7KG+a89vjn06C7Ihq/dDl3jA+A8B3TKomvi3CiEcVLJQGalbu8F52LxkOym7U5sSfbg==} - engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} - handlebars@4.7.8: resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} engines: {node: '>=0.4.7'} @@ -6577,13 +6362,6 @@ packages: hardhat-ignore-warnings@0.2.12: resolution: {integrity: sha512-SaxCLKzYBMk3Rd1275TnanUmmxwgU+bu4Ekf2MKcqXxxt6xTGcPTtTaM+USrLgmejZHC4Itg/PaWITlOp4RL3g==} - hardhat-secure-accounts@0.0.6: - resolution: {integrity: sha512-KnSLrjdNdxg5YJ4/FZ0Ogf1S4nR0YdlIWG9DLMyUurF0S345yzKt0IMPDqcG5/MNDI/hMNfSv6/AQuBWZ4i21w==} - peerDependencies: - '@nomiclabs/hardhat-ethers': ^2.1.1 - ethers: ^5.0.0 - hardhat: ^2.0.0 - hardhat-secure-accounts@1.0.5: resolution: {integrity: sha512-gr0bV1eh1sOOSy2CgQ0BMFwQ8QPLBLBB7mJIT4VYNkEf6+fPjO8r70lNapoX53zMhfy9kQe7F0ko6yKpkcBqBg==} peerDependencies: @@ -6681,10 +6459,6 @@ packages: resolution: {integrity: sha512-QWlwUZZ8BtlvwYVTSDTBChGf8EOcQ2LkGMnQJxSzD1mUu8CCjXJZq/BXP8eWw4kikRnzlhtYo3lCk0ucmYA3Vg==} engines: {node: '>=12.0.0'} - helmet@7.0.0: - resolution: {integrity: sha512-MsIgYmdBh460ZZ8cJC81q4XJknjG567wzEmv46WOBblDb6TUd3z8/GhgmsM9pn8g2B80tAJ4m5/d3Bi1KrSUBQ==} - engines: {node: '>=16.0.0'} - hermes-estree@0.29.1: resolution: {integrity: sha512-jl+x31n4/w+wEqm0I2r4CMimukLbLQEYpisys5oCre611CI5fc9TxhqkBBCJ1edDG4Kza0f7CgNz8xVMLZQOmQ==} @@ -8462,9 +8236,6 @@ packages: performance-now@2.1.0: resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} - pg-cloudflare@1.2.7: - resolution: {integrity: sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==} - pg-connection-string@2.9.1: resolution: {integrity: sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==} @@ -8488,15 +8259,6 @@ packages: resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} engines: {node: '>=4'} - pg@8.11.3: - resolution: {integrity: sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==} - engines: {node: '>= 8.0.0'} - peerDependencies: - pg-native: '>=3.0.1' - peerDependenciesMeta: - pg-native: - optional: true - pg@8.7.3: resolution: {integrity: sha512-HPmH4GH4H3AOprDJOazoIcpI49XFsHCe8xlrjHkWiapdbHK+HLtbm/GQzXYAZwmPju/kzKhjaSfMACG+8cgJcw==} engines: {node: '>= 8.0.0'} @@ -8642,10 +8404,6 @@ packages: resolution: {integrity: sha512-HxTArb6fkOntQHoRGvv4qd/BkorjliiuO2uSWC2KC17MUTKYttWdDoXX/vxOhQdkoECEM9BBH0pj2l8G8kev6w==} engines: {node: '>=10'} - prom-client@14.2.0: - resolution: {integrity: sha512-sF308EhTenb/pDRPakm+WgiN+VdM/T1RaHj1x+MvAuT8UiQP8JmOEbxVqtkbfR4LrvOg5n7ic01kRBDGXjYikA==} - engines: {node: '>=10'} - promise-retry@2.0.1: resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} engines: {node: '>=10'} @@ -8714,10 +8472,6 @@ packages: resolution: {integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==} engines: {node: '>=6.0.0'} - qs@6.11.0: - resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} - engines: {node: '>=0.6'} - qs@6.14.0: resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} engines: {node: '>=0.6'} @@ -8765,10 +8519,6 @@ packages: resolution: {integrity: sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==} engines: {node: '>= 0.8'} - raw-body@2.5.1: - resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==} - engines: {node: '>= 0.8'} - raw-body@2.5.2: resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} engines: {node: '>= 0.8'} @@ -8966,9 +8716,6 @@ packages: retry-as-promised@5.0.0: resolution: {integrity: sha512-6S+5LvtTl2ggBumk04hBo/4Uf6fRJUwIgunGZ7CYEBCeufGFW1Pu6ucUf/UskHeWOIsUcLOGLFXPig5tR5V1nA==} - retry-as-promised@7.1.1: - resolution: {integrity: sha512-hMD7odLOt3LkTjcif8aRZqi/hybjpLNgSk5oF5FCowfCjok6LukpN2bDX7R5wDmbgBQFn7YoBxSagmtXHaJYJw==} - retry@0.12.0: resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} engines: {node: '>= 4'} @@ -9112,10 +8859,6 @@ packages: resolution: {integrity: sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==} engines: {node: '>= 0.8.0'} - send@0.18.0: - resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} - engines: {node: '>= 0.8.0'} - send@0.19.2: resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} engines: {node: '>= 0.8.0'} @@ -9157,39 +8900,6 @@ packages: tedious: optional: true - sequelize@6.33.0: - resolution: {integrity: sha512-GkeCbqgaIcpyZ1EyXrDNIwktbfMldHAGOVXHGM4x8bxGSRAOql5htDWofPvwpfL/FoZ59CaFmfO3Mosv1lDbQw==} - engines: {node: '>=10.0.0'} - peerDependencies: - ibm_db: '*' - mariadb: '*' - mysql2: '*' - oracledb: '*' - pg: '*' - pg-hstore: '*' - snowflake-sdk: '*' - sqlite3: '*' - tedious: '*' - peerDependenciesMeta: - ibm_db: - optional: true - mariadb: - optional: true - mysql2: - optional: true - oracledb: - optional: true - pg: - optional: true - pg-hstore: - optional: true - snowflake-sdk: - optional: true - sqlite3: - optional: true - tedious: - optional: true - serialize-error@2.1.0: resolution: {integrity: sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==} engines: {node: '>=0.10.0'} @@ -9201,10 +8911,6 @@ packages: resolution: {integrity: sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==} engines: {node: '>= 0.8.0'} - serve-static@1.15.0: - resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} - engines: {node: '>= 0.8.0'} - serve-static@1.16.3: resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} engines: {node: '>= 0.8.0'} @@ -10185,9 +9891,6 @@ packages: wonka@4.0.15: resolution: {integrity: sha512-U0IUQHKXXn6PFo9nqsHphVCE5m3IntqZNB9Jjn7EB1lrR7YTDY3YWgFvEvwniTzXSvOH/XMzAZaIfJF/LvHYXg==} - wonka@6.3.5: - resolution: {integrity: sha512-SSil+ecw6B4/Dm7Pf2sAshKQ5hWFvfyGlfPbEd6A14dOH6VDjrmbY86u6nZvy9omGwwIPFR8V41+of1EezgoUw==} - word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} @@ -12214,10 +11917,6 @@ snapshots: dependencies: '@ethersproject/logger': 5.8.0 - '@ethersproject/networks@5.7.0': - dependencies: - '@ethersproject/logger': 5.8.0 - '@ethersproject/networks@5.7.1': dependencies: '@ethersproject/logger': 5.7.0 @@ -12278,32 +11977,6 @@ snapshots: - bufferutil - utf-8-validate - '@ethersproject/providers@5.7.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)': - dependencies: - '@ethersproject/abstract-provider': 5.8.0 - '@ethersproject/abstract-signer': 5.8.0 - '@ethersproject/address': 5.8.0 - '@ethersproject/base64': 5.8.0 - '@ethersproject/basex': 5.8.0 - '@ethersproject/bignumber': 5.8.0 - '@ethersproject/bytes': 5.8.0 - '@ethersproject/constants': 5.8.0 - '@ethersproject/hash': 5.8.0 - '@ethersproject/logger': 5.8.0 - '@ethersproject/networks': 5.8.0 - '@ethersproject/properties': 5.8.0 - '@ethersproject/random': 5.8.0 - '@ethersproject/rlp': 5.8.0 - '@ethersproject/sha2': 5.8.0 - '@ethersproject/strings': 5.8.0 - '@ethersproject/transactions': 5.8.0 - '@ethersproject/web': 5.8.0 - bech32: 1.1.4 - ws: 7.4.6(bufferutil@4.0.9)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - utf-8-validate - '@ethersproject/providers@5.7.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)': dependencies: '@ethersproject/abstract-provider': 5.8.0 @@ -12592,15 +12265,7 @@ snapshots: '@ethersproject/properties': 5.6.0 '@ethersproject/strings': 5.6.0 - '@ethersproject/web@5.7.0': - dependencies: - '@ethersproject/base64': 5.8.0 - '@ethersproject/bytes': 5.8.0 - '@ethersproject/logger': 5.8.0 - '@ethersproject/properties': 5.8.0 - '@ethersproject/strings': 5.8.0 - - '@ethersproject/web@5.7.1': + '@ethersproject/web@5.7.1': dependencies: '@ethersproject/base64': 5.7.0 '@ethersproject/bytes': 5.8.0 @@ -12808,43 +12473,6 @@ snapshots: - tedious - utf-8-validate - '@graphprotocol/common-ts@2.0.11(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10)': - dependencies: - '@graphprotocol/contracts': 5.3.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@graphprotocol/pino-sentry-simple': 0.7.1 - '@urql/core': 3.1.0(graphql@16.8.0) - '@urql/exchange-execute': 2.1.0(graphql@16.8.0) - body-parser: 1.20.2 - bs58: 5.0.0 - cors: 2.8.5 - cross-fetch: 4.0.0(encoding@0.1.13) - ethers: 5.7.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - express: 4.18.2 - graphql: 16.8.0 - graphql-tag: 2.12.6(graphql@16.8.0) - helmet: 7.0.0 - morgan: 1.10.0 - ngeohash: 0.6.3 - pg: 8.11.3 - pg-hstore: 2.3.4 - pino: 7.6.0 - pino-multi-stream: 6.0.0 - prom-client: 14.2.0 - sequelize: 6.33.0(pg-hstore@2.3.4)(pg@8.11.3) - transitivePeerDependencies: - - bufferutil - - encoding - - ibm_db - - mariadb - - mysql2 - - oracledb - - pg-native - - snowflake-sdk - - sqlite3 - - supports-color - - tedious - - utf-8-validate - '@graphprotocol/contracts@2.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)': dependencies: console-table-printer: 2.14.6 @@ -12853,16 +12481,6 @@ snapshots: - bufferutil - utf-8-validate - '@graphprotocol/contracts@5.3.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)': - dependencies: - console-table-printer: 2.14.6 - ethers: 5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - '@graphprotocol/contracts@7.2.1': {} - '@graphprotocol/pino-sentry-simple@0.7.1': dependencies: '@sentry/node': 5.30.0 @@ -12872,39 +12490,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@graphprotocol/sdk@0.6.0(bufferutil@4.0.9)(encoding@0.1.13)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10)': - dependencies: - '@arbitrum/sdk': 3.1.13(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@ethersproject/abstract-provider': 5.8.0 - '@ethersproject/experimental': 5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@ethersproject/providers': 5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@graphprotocol/common-ts': 2.0.11(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) - '@graphprotocol/contracts': 7.2.1 - '@nomicfoundation/hardhat-network-helpers': 1.1.0(hardhat@2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10)) - '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10)) - debug: 4.4.3(supports-color@8.1.1) - ethers: 5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - hardhat: 2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10) - hardhat-secure-accounts: 0.0.6(@nomiclabs/hardhat-ethers@2.2.3(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10)))(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10)) - inquirer: 8.0.0 - lodash: 4.17.21 - yaml: 1.10.2 - transitivePeerDependencies: - - bufferutil - - encoding - - ibm_db - - mariadb - - mysql2 - - oracledb - - pg-native - - snowflake-sdk - - sqlite3 - - supports-color - - tedious - - ts-node - - typescript - - utf-8-validate - '@graphql-codegen/core@3.1.0(graphql@16.11.0)': dependencies: '@graphql-codegen/plugin-helpers': 4.2.0(graphql@16.11.0) @@ -15282,23 +14867,12 @@ snapshots: graphql: 16.3.0 wonka: 4.0.15 - '@urql/core@3.1.0(graphql@16.8.0)': - dependencies: - graphql: 16.8.0 - wonka: 6.3.5 - '@urql/exchange-execute@1.2.2(graphql@16.3.0)': dependencies: '@urql/core': 2.4.4(graphql@16.3.0) graphql: 16.3.0 wonka: 4.0.15 - '@urql/exchange-execute@2.1.0(graphql@16.8.0)': - dependencies: - '@urql/core': 3.1.0(graphql@16.8.0) - graphql: 16.8.0 - wonka: 6.3.5 - '@wagmi/cli@2.5.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)': dependencies: abitype: 1.1.0(typescript@5.9.3)(zod@3.25.76) @@ -15841,8 +15415,6 @@ snapshots: dependencies: safe-buffer: 5.2.1 - base-x@4.0.1: {} - base64-js@1.5.1: {} baseline-browser-mapping@2.8.4: {} @@ -15935,40 +15507,6 @@ snapshots: transitivePeerDependencies: - supports-color - body-parser@1.20.1: - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - on-finished: 2.4.1 - qs: 6.11.0 - raw-body: 2.5.1 - type-is: 1.6.18 - unpipe: 1.0.0 - transitivePeerDependencies: - - supports-color - - body-parser@1.20.2: - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - on-finished: 2.4.1 - qs: 6.11.0 - raw-body: 2.5.2 - type-is: 1.6.18 - unpipe: 1.0.0 - transitivePeerDependencies: - - supports-color - bowser@2.14.1: {} boxen@5.1.2: @@ -16029,10 +15567,6 @@ snapshots: dependencies: base-x: 3.0.11 - bs58@5.0.0: - dependencies: - base-x: 4.0.1 - bs58check@2.1.2: dependencies: bs58: 4.0.1 @@ -16530,8 +16064,6 @@ snapshots: cookie@0.4.2: {} - cookie@0.5.0: {} - core-js-pure@3.45.1: {} core-util-is@1.0.2: {} @@ -16608,12 +16140,6 @@ snapshots: transitivePeerDependencies: - encoding - cross-fetch@4.0.0(encoding@0.1.13): - dependencies: - node-fetch: 2.7.0(encoding@0.1.13) - transitivePeerDependencies: - - encoding - cross-inspect@1.0.1: dependencies: tslib: 2.8.1 @@ -17347,42 +16873,6 @@ snapshots: - bufferutil - utf-8-validate - ethers@5.7.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): - dependencies: - '@ethersproject/abi': 5.7.0 - '@ethersproject/abstract-provider': 5.7.0 - '@ethersproject/abstract-signer': 5.7.0 - '@ethersproject/address': 5.7.0 - '@ethersproject/base64': 5.7.0 - '@ethersproject/basex': 5.7.0 - '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/constants': 5.7.0 - '@ethersproject/contracts': 5.7.0 - '@ethersproject/hash': 5.7.0 - '@ethersproject/hdnode': 5.7.0 - '@ethersproject/json-wallets': 5.7.0 - '@ethersproject/keccak256': 5.7.0 - '@ethersproject/logger': 5.7.0 - '@ethersproject/networks': 5.7.0 - '@ethersproject/pbkdf2': 5.7.0 - '@ethersproject/properties': 5.7.0 - '@ethersproject/providers': 5.7.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@ethersproject/random': 5.7.0 - '@ethersproject/rlp': 5.7.0 - '@ethersproject/sha2': 5.7.0 - '@ethersproject/signing-key': 5.7.0 - '@ethersproject/solidity': 5.7.0 - '@ethersproject/strings': 5.7.0 - '@ethersproject/transactions': 5.7.0 - '@ethersproject/units': 5.7.0 - '@ethersproject/wallet': 5.7.0 - '@ethersproject/web': 5.7.0 - '@ethersproject/wordlists': 5.7.0 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - ethers@5.7.2(bufferutil@4.0.9)(utf-8-validate@5.0.10): dependencies: '@ethersproject/abi': 5.7.0 @@ -17544,42 +17034,6 @@ snapshots: transitivePeerDependencies: - supports-color - express@4.18.2: - dependencies: - accepts: 1.3.8 - array-flatten: 1.1.1 - body-parser: 1.20.1 - content-disposition: 0.5.4 - content-type: 1.0.5 - 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.11.0 - 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 - transitivePeerDependencies: - - supports-color - extend@3.0.2: {} extendable-error@0.1.7: {} @@ -17716,18 +17170,6 @@ snapshots: transitivePeerDependencies: - supports-color - finalhandler@1.2.0: - 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 - transitivePeerDependencies: - - supports-color - find-replace@3.0.0: dependencies: array-back: 3.1.0 @@ -18130,11 +17572,6 @@ snapshots: graphql: 16.3.0 tslib: 2.8.1 - graphql-tag@2.12.6(graphql@16.8.0): - dependencies: - graphql: 16.8.0 - tslib: 2.8.1 - graphql-ws@5.12.1(graphql@16.11.0): dependencies: graphql: 16.11.0 @@ -18180,8 +17617,6 @@ snapshots: graphql@16.3.0: {} - graphql@16.8.0: {} - handlebars@4.7.8: dependencies: minimist: 1.2.8 @@ -18316,18 +17751,6 @@ snapshots: node-interval-tree: 2.1.2 solidity-comments: 0.0.2 - hardhat-secure-accounts@0.0.6(@nomiclabs/hardhat-ethers@2.2.3(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10)))(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10)): - dependencies: - '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10)) - debug: 4.4.3(supports-color@8.1.1) - enquirer: 2.4.1 - ethers: 5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - hardhat: 2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10) - lodash.clonedeep: 4.5.0 - prompt-sync: 4.2.0 - transitivePeerDependencies: - - supports-color - hardhat-secure-accounts@1.0.5(@nomicfoundation/hardhat-ethers@3.1.0(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10)))(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10)): dependencies: '@nomicfoundation/hardhat-ethers': 3.1.0(ethers@6.16.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.28.6(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.14)(typescript@5.9.3))(typescript@5.9.3)(utf-8-validate@5.0.10)) @@ -18592,8 +18015,6 @@ snapshots: helmet@5.0.2: {} - helmet@7.0.0: {} - hermes-estree@0.29.1: {} hermes-estree@0.35.0: {} @@ -20673,9 +20094,6 @@ snapshots: performance-now@2.1.0: {} - pg-cloudflare@1.2.7: - optional: true - pg-connection-string@2.9.1: {} pg-hstore@2.3.4: @@ -20684,10 +20102,6 @@ snapshots: pg-int8@1.0.1: {} - pg-pool@3.10.1(pg@8.11.3): - dependencies: - pg: 8.11.3 - pg-pool@3.10.1(pg@8.7.3): dependencies: pg: 8.7.3 @@ -20702,18 +20116,6 @@ snapshots: postgres-date: 1.0.7 postgres-interval: 1.2.0 - pg@8.11.3: - dependencies: - buffer-writer: 2.0.0 - packet-reader: 1.0.0 - pg-connection-string: 2.9.1 - pg-pool: 3.10.1(pg@8.11.3) - pg-protocol: 1.10.3 - pg-types: 2.2.0 - pgpass: 1.0.5 - optionalDependencies: - pg-cloudflare: 1.2.7 - pg@8.7.3: dependencies: buffer-writer: 2.0.0 @@ -20855,10 +20257,6 @@ snapshots: dependencies: tdigest: 0.1.2 - prom-client@14.2.0: - dependencies: - tdigest: 0.1.2 - promise-retry@2.0.1: dependencies: err-code: 2.0.3 @@ -20929,10 +20327,6 @@ snapshots: pvutils@1.1.3: {} - qs@6.11.0: - dependencies: - side-channel: 1.1.0 - qs@6.14.0: dependencies: side-channel: 1.1.0 @@ -20975,13 +20369,6 @@ snapshots: iconv-lite: 0.4.24 unpipe: 1.0.0 - raw-body@2.5.1: - dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - unpipe: 1.0.0 - raw-body@2.5.2: dependencies: bytes: 3.1.2 @@ -21243,8 +20630,6 @@ snapshots: retry-as-promised@5.0.0: {} - retry-as-promised@7.1.1: {} - retry@0.12.0: {} retry@0.13.1: {} @@ -21410,24 +20795,6 @@ snapshots: transitivePeerDependencies: - supports-color - send@0.18.0: - 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 - transitivePeerDependencies: - - supports-color - send@0.19.2: dependencies: debug: 2.6.9 @@ -21478,30 +20845,6 @@ snapshots: transitivePeerDependencies: - supports-color - sequelize@6.33.0(pg-hstore@2.3.4)(pg@8.11.3): - dependencies: - '@types/debug': 4.1.12 - '@types/validator': 13.15.3 - debug: 4.4.3(supports-color@8.1.1) - dottie: 2.0.6 - inflection: 1.13.4 - lodash: 4.17.21 - moment: 2.30.1 - moment-timezone: 0.5.48 - pg-connection-string: 2.9.1 - retry-as-promised: 7.1.1 - semver: 7.7.3 - sequelize-pool: 7.1.0 - toposort-class: 1.0.1 - uuid: 8.3.2 - validator: 13.15.15 - wkx: 0.5.0 - optionalDependencies: - pg: 8.11.3 - pg-hstore: 2.3.4 - transitivePeerDependencies: - - supports-color - serialize-error@2.1.0: {} serialize-javascript@6.0.2: @@ -21517,15 +20860,6 @@ snapshots: transitivePeerDependencies: - supports-color - serve-static@1.15.0: - dependencies: - encodeurl: 1.0.2 - escape-html: 1.0.3 - parseurl: 1.3.3 - send: 0.18.0 - transitivePeerDependencies: - - supports-color - serve-static@1.16.3: dependencies: encodeurl: 2.0.0 @@ -22685,8 +22019,6 @@ snapshots: wonka@4.0.15: {} - wonka@6.3.5: {} - word-wrap@1.2.5: {} wordwrap@1.0.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index fa5c80080..6197176c1 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,6 +1,4 @@ minimumReleaseAge: 7200 -minumumReleaseAgeExclude: - - '@graphprotocol/sdk' packages: - packages/* @@ -12,7 +10,6 @@ catalog: '@commitlint/cli': ^20.1.0 '@commitlint/config-conventional': ^20.0.0 '@eslint/js': ^9.39.2 - '@graphprotocol/sdk': ^0.6.1 '@nomicfoundation/hardhat-chai-matchers': ^2.0.0 '@nomicfoundation/hardhat-ethers': ^3.1.0 '@nomicfoundation/hardhat-foundry': ^1.1.1