diff --git a/packages/common/contracts/quality/IServiceQualityOracle.sol b/packages/common/contracts/quality/IServiceQualityOracle.sol new file mode 100644 index 000000000..ec86b8c87 --- /dev/null +++ b/packages/common/contracts/quality/IServiceQualityOracle.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.7.6 || ^0.8.0; + +/** + * @title IServiceQualityOracle + * @author Edge & Node + * @notice Interface to check if an indexer is allowed to receive rewards based on its service quality + */ +interface IServiceQualityOracle { + /** + * @notice Check if an indexer is allowed to receive rewards + * @param indexer Address of the indexer + * @return True if the indexer is allowed to receive rewards, false otherwise + */ + function isAllowed(address indexer) external view returns (bool); +} diff --git a/packages/common/contracts/rewards/IRewardsManager.sol b/packages/common/contracts/rewards/IRewardsManager.sol index 5d750dd85..c2518d5c8 100644 --- a/packages/common/contracts/rewards/IRewardsManager.sol +++ b/packages/common/contracts/rewards/IRewardsManager.sol @@ -37,6 +37,12 @@ interface IRewardsManager { */ function setMinimumSubgraphSignal(uint256 _minimumSubgraphSignal) external; + /** + * @notice Set the service quality oracle address + * @param newServiceQualityOracle The address of the service quality oracle + */ + function setServiceQualityOracle(address newServiceQualityOracle) external; + // -- Denylist -- /** diff --git a/packages/contracts/contracts/rewards/IRewardsIssuer.sol b/packages/contracts/contracts/rewards/IRewardsIssuer.sol new file mode 100644 index 000000000..614dbf40e --- /dev/null +++ b/packages/contracts/contracts/rewards/IRewardsIssuer.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity ^0.7.6 || 0.8.27; + +/** + * @title Rewards Issuer Interface + * @author Edge & Node + * @notice Interface for contracts that issue rewards based on allocation data + */ +interface IRewardsIssuer { + /** + * @notice Get allocation data to calculate rewards issuance + * + * @param allocationId The allocation Id + * @return isActive Whether the allocation is active or not + * @return indexer The indexer address + * @return subgraphDeploymentId Subgraph deployment id for the allocation + * @return tokens Amount of allocated tokens + * @return accRewardsPerAllocatedToken Rewards snapshot + * @return accRewardsPending Snapshot of accumulated rewards from previous allocation resizing, pending to be claimed + */ + function getAllocationData( + address allocationId + ) + external + view + returns ( + bool isActive, + address indexer, + bytes32 subgraphDeploymentId, + uint256 tokens, + uint256 accRewardsPerAllocatedToken, + uint256 accRewardsPending + ); + + /** + * @notice Return the total amount of tokens allocated to subgraph. + * @param _subgraphDeploymentId Deployment Id for the subgraph + * @return Total tokens allocated to subgraph + */ + function getSubgraphAllocatedTokens(bytes32 _subgraphDeploymentId) external view returns (uint256); +} diff --git a/packages/contracts/contracts/rewards/RewardsManager.sol b/packages/contracts/contracts/rewards/RewardsManager.sol index d24f06243..a02c46625 100644 --- a/packages/contracts/contracts/rewards/RewardsManager.sol +++ b/packages/contracts/contracts/rewards/RewardsManager.sol @@ -7,6 +7,7 @@ pragma abicoder v2; // solhint-disable gas-increment-by-one, gas-indexed-events, gas-small-strings, gas-strict-inequalities import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol"; +import { IERC165 } from "@openzeppelin/contracts/introspection/IERC165.sol"; import { GraphUpgradeable } from "../upgrades/GraphUpgradeable.sol"; import { MathUtils } from "../staking/libs/MathUtils.sol"; @@ -14,8 +15,9 @@ import { Managed } from "../governance/Managed.sol"; import { IGraphToken } from "@graphprotocol/common/contracts/token/IGraphToken.sol"; import { IStaking, IStakingBase } from "../staking/IStaking.sol"; -import { RewardsManagerV4Storage } from "./RewardsManagerStorage.sol"; +import { RewardsManagerV5Storage } from "./RewardsManagerStorage.sol"; import { IRewardsManager } from "@graphprotocol/common/contracts/rewards/IRewardsManager.sol"; +import { IServiceQualityOracle } from "@graphprotocol/common/contracts/quality/IServiceQualityOracle.sol"; /** * @title Rewards Manager Contract @@ -37,7 +39,7 @@ import { IRewardsManager } from "@graphprotocol/common/contracts/rewards/IReward * until the actual takeRewards function is called. * custom:security-contact Please email security+contracts@ thegraph.com (remove space) if you find any bugs. We might have an active bug bounty program. */ -contract RewardsManager is RewardsManagerV4Storage, GraphUpgradeable, IRewardsManager { +contract RewardsManager is RewardsManagerV5Storage, GraphUpgradeable, IRewardsManager { using SafeMath for uint256; /// @dev Fixed point scaling factor used for decimals in reward calculations @@ -62,6 +64,14 @@ contract RewardsManager is RewardsManagerV4Storage, GraphUpgradeable, IRewardsMa */ event RewardsDenied(address indexed indexer, address indexed allocationID, uint256 epoch); + /** + * @notice Emitted when rewards are denied to an indexer due to service quality + * @param indexer Address of the indexer being denied rewards + * @param allocationID Address of the allocation being denied rewards + * @param amount Amount of rewards that would have been assigned + */ + event RewardsDeniedDueToServiceQuality(address indexed indexer, address indexed allocationID, uint256 amount); + /** * @notice Emitted when a subgraph is denied for claiming rewards * @param subgraphDeploymentID Subgraph deployment ID being denied @@ -69,6 +79,13 @@ contract RewardsManager is RewardsManagerV4Storage, GraphUpgradeable, IRewardsMa */ event RewardsDenylistUpdated(bytes32 indexed subgraphDeploymentID, uint256 sinceBlock); + /** + * @notice Emitted when the service quality oracle contract is set + * @param oldServiceQualityOracle Previous service quality oracle address + * @param newServiceQualityOracle New service quality oracle address + */ + event ServiceQualityOracleSet(address indexed oldServiceQualityOracle, address indexed newServiceQualityOracle); + // -- Modifiers -- /** @@ -135,6 +152,28 @@ contract RewardsManager is RewardsManagerV4Storage, GraphUpgradeable, IRewardsMa emit ParameterUpdated("minimumSubgraphSignal"); } + /** + * @inheritdoc IRewardsManager + * @dev Note that the service quality oracle can be set to the zero address to disable use of an oracle, in + * which case no indexers will be denied rewards due to service quality. + */ + function setServiceQualityOracle(address newServiceQualityOracle) external override onlyGovernor { + if (address(serviceQualityOracle) != newServiceQualityOracle) { + // Check that the contract supports the IServiceQualityOracle interface + // Allow zero address to disable the oracle + if (newServiceQualityOracle != address(0)) { + require( + IERC165(newServiceQualityOracle).supportsInterface(type(IServiceQualityOracle).interfaceId), + "Contract does not support IServiceQualityOracle interface" + ); + } + + address oldServiceQualityOracle = address(serviceQualityOracle); + serviceQualityOracle = IServiceQualityOracle(newServiceQualityOracle); + emit ServiceQualityOracleSet(oldServiceQualityOracle, newServiceQualityOracle); + } + } + // -- Denylist -- /** @@ -336,6 +375,13 @@ contract RewardsManager is RewardsManagerV4Storage, GraphUpgradeable, IRewardsMa // Calculate rewards accrued by this allocation uint256 rewards = _calcRewards(alloc.tokens, alloc.accRewardsPerAllocatedToken, accRewardsPerAllocatedToken); + + // Do not reward if indexer is not eligible based on service quality + if (address(serviceQualityOracle) != address(0) && !serviceQualityOracle.isAllowed(alloc.indexer)) { + emit RewardsDeniedDueToServiceQuality(alloc.indexer, _allocationID, rewards); + return 0; + } + if (rewards > 0) { // Mint directly to staking contract for the reward amount // The staking contract will do bookkeeping of the reward and diff --git a/packages/contracts/contracts/rewards/RewardsManagerStorage.sol b/packages/contracts/contracts/rewards/RewardsManagerStorage.sol index 1901b531e..7d7f1e5c6 100644 --- a/packages/contracts/contracts/rewards/RewardsManagerStorage.sol +++ b/packages/contracts/contracts/rewards/RewardsManagerStorage.sol @@ -5,6 +5,8 @@ pragma solidity 0.7.6; import { IRewardsManager } from "@graphprotocol/common/contracts/rewards/IRewardsManager.sol"; +import { IRewardsIssuer } from "./IRewardsIssuer.sol"; +import { IServiceQualityOracle } from "@graphprotocol/common/contracts/quality/IServiceQualityOracle.sol"; import { Managed } from "../governance/Managed.sol"; /** @@ -59,6 +61,17 @@ contract RewardsManagerV3Storage is RewardsManagerV2Storage { */ contract RewardsManagerV4Storage is RewardsManagerV3Storage { /// @notice GRT issued for indexer rewards per block - /// @dev Only used when issuanceAllocator is zero address. uint256 public issuancePerBlock; } + +/** + * @title RewardsManagerV5Storage + * @author Edge & Node + * @notice Storage layout for RewardsManager V5 + */ +contract RewardsManagerV5Storage is RewardsManagerV4Storage { + /// @notice Address of the subgraph service + IRewardsIssuer public subgraphService; + /// @notice Address of the service quality oracle contract + IServiceQualityOracle public serviceQualityOracle; +} diff --git a/packages/contracts/contracts/tests/MockERC165OnlyContract.sol b/packages/contracts/contracts/tests/MockERC165OnlyContract.sol new file mode 100644 index 000000000..478ecb795 --- /dev/null +++ b/packages/contracts/contracts/tests/MockERC165OnlyContract.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity 0.7.6; + +import { ERC165 } from "@openzeppelin/contracts/introspection/ERC165.sol"; + +/** + * @title MockERC165OnlyContract + * @author Edge & Node + * @notice A mock contract that supports ERC-165 but not IServiceQualityOracle + * @dev Used for testing ERC-165 interface checking in RewardsManager + */ +contract MockERC165OnlyContract is ERC165 { + /** + * @notice A dummy function to make this a non-trivial contract + * @return A dummy string + */ + function dummyFunction() external pure returns (string memory) { + return "This contract only supports ERC-165"; + } +} diff --git a/packages/contracts/contracts/tests/MockServiceQualityOracle.sol b/packages/contracts/contracts/tests/MockServiceQualityOracle.sol new file mode 100644 index 000000000..200037075 --- /dev/null +++ b/packages/contracts/contracts/tests/MockServiceQualityOracle.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity 0.7.6; + +import { IServiceQualityOracle } from "@graphprotocol/common/contracts/quality/IServiceQualityOracle.sol"; +import { ERC165 } from "@openzeppelin/contracts/introspection/ERC165.sol"; + +/** + * @title MockServiceQualityOracle + * @author Edge & Node + * @notice A simple mock contract for the ServiceQualityOracle interface + * @dev A simple mock contract for the ServiceQualityOracle interface + */ +contract MockServiceQualityOracle is IServiceQualityOracle, ERC165 { + /// @dev Mapping to store allowed status for each indexer + mapping(address => bool) private _allowed; + + /// @dev Mapping to track which indexers have been explicitly set + mapping(address => bool) private _isSet; + + /// @dev Default response for indexers not explicitly set + bool private _defaultResponse; + + /** + * @notice Constructor + * @param defaultResponse Default response for isAllowed + */ + constructor(bool defaultResponse) { + _defaultResponse = defaultResponse; + } + + /** + * @notice Set whether a specific indexer is allowed + * @param indexer The indexer address + * @param allowed Whether the indexer is allowed + */ + function setIndexerAllowed(address indexer, bool allowed) external { + _allowed[indexer] = allowed; + } + + /** + * @notice Set the default response for indexers not explicitly set + * @param defaultResponse The default response + */ + function setDefaultResponse(bool defaultResponse) external { + _defaultResponse = defaultResponse; + } + + /** + * @inheritdoc IServiceQualityOracle + */ + function isAllowed(address indexer) external view override returns (bool) { + // If the indexer has been explicitly set, return that value + if (_isSet[indexer]) { + return _allowed[indexer]; + } + + // Otherwise return the default response + return _defaultResponse; + } + + /** + * @inheritdoc ERC165 + */ + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return interfaceId == type(IServiceQualityOracle).interfaceId || super.supportsInterface(interfaceId); + } +} diff --git a/packages/contracts/task/tsconfig.json b/packages/contracts/task/tsconfig.json index f866f8173..f60d075ba 100644 --- a/packages/contracts/task/tsconfig.json +++ b/packages/contracts/task/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../../../tsconfig.json", "compilerOptions": { - "rootDir": ".", + "rootDir": "..", "outDir": "./build", "declarationDir": "./types" }, diff --git a/packages/contracts/test/tests/unit/rewards/rewards.test.ts b/packages/contracts/test/tests/unit/rewards/rewards.test.ts index 886d9a059..42cd12e4b 100644 --- a/packages/contracts/test/tests/unit/rewards/rewards.test.ts +++ b/packages/contracts/test/tests/unit/rewards/rewards.test.ts @@ -162,6 +162,14 @@ describe('Rewards', () => { }) describe('configuration', function () { + describe('initialize', function () { + it('should revert when called on implementation contract', async function () { + // Try to call initialize on the implementation contract (should revert with onlyImpl) + const tx = rewardsManager.connect(governor).initialize(contracts.Controller.address) + await expect(tx).revertedWith('Only implementation') + }) + }) + describe('issuance per block update', function () { it('reject set issuance per block if unauthorized', async function () { const tx = rewardsManager.connect(indexer1).setIssuancePerBlock(toGRT('1.025')) @@ -182,6 +190,82 @@ describe('Rewards', () => { }) }) + describe('service quality oracle', function () { + it('should reject setServiceQualityOracle if unauthorized', async function () { + const MockServiceQualityOracleFactory = await hre.ethers.getContractFactory( + 'contracts/tests/MockServiceQualityOracle.sol:MockServiceQualityOracle', + ) + const mockOracle = await MockServiceQualityOracleFactory.deploy(true) + await mockOracle.deployed() + const tx = rewardsManager.connect(indexer1).setServiceQualityOracle(mockOracle.address) + await expect(tx).revertedWith('Only Controller governor') + }) + + it('should set service quality oracle if governor', async function () { + const MockServiceQualityOracleFactory = await hre.ethers.getContractFactory( + 'contracts/tests/MockServiceQualityOracle.sol:MockServiceQualityOracle', + ) + const mockOracle = await MockServiceQualityOracleFactory.deploy(true) + await mockOracle.deployed() + + const tx = rewardsManager.connect(governor).setServiceQualityOracle(mockOracle.address) + await expect(tx) + .emit(rewardsManager, 'ServiceQualityOracleSet') + .withArgs(constants.AddressZero, mockOracle.address) + + expect(await rewardsManager.serviceQualityOracle()).eq(mockOracle.address) + }) + + it('should allow setting service quality oracle to zero address', async function () { + // First set an oracle + const MockServiceQualityOracleFactory = await hre.ethers.getContractFactory( + 'contracts/tests/MockServiceQualityOracle.sol:MockServiceQualityOracle', + ) + const mockOracle = await MockServiceQualityOracleFactory.deploy(true) + await mockOracle.deployed() + await rewardsManager.connect(governor).setServiceQualityOracle(mockOracle.address) + + // Then set to zero address to disable + const tx = rewardsManager.connect(governor).setServiceQualityOracle(constants.AddressZero) + await expect(tx) + .emit(rewardsManager, 'ServiceQualityOracleSet') + .withArgs(mockOracle.address, constants.AddressZero) + + expect(await rewardsManager.serviceQualityOracle()).eq(constants.AddressZero) + }) + + it('should reject setting oracle that does not support interface', async function () { + // Try to set an EOA (externally owned account) as the service quality oracle + const tx = rewardsManager.connect(governor).setServiceQualityOracle(indexer1.address) + await expect(tx).revertedWith('function call to a non-contract account') + }) + + it('should reject setting oracle that does not support IServiceQualityOracle interface', async function () { + // Deploy a contract that doesn't support the IServiceQualityOracle interface + const MockERC165OnlyContractFactory = await hre.ethers.getContractFactory( + 'contracts/tests/MockERC165OnlyContract.sol:MockERC165OnlyContract', + ) + const mockContract = await MockERC165OnlyContractFactory.deploy() + await mockContract.deployed() + + const tx = rewardsManager.connect(governor).setServiceQualityOracle(mockContract.address) + await expect(tx).revertedWith('Contract does not support IServiceQualityOracle interface') + }) + + it('should not emit event when setting same oracle address', async function () { + const MockServiceQualityOracleFactory = await hre.ethers.getContractFactory( + 'contracts/tests/MockServiceQualityOracle.sol:MockServiceQualityOracle', + ) + const mockOracle = await MockServiceQualityOracleFactory.deploy(true) + await mockOracle.deployed() + await rewardsManager.connect(governor).setServiceQualityOracle(mockOracle.address) + + // Setting the same oracle again should not emit an event + const tx = rewardsManager.connect(governor).setServiceQualityOracle(mockOracle.address) + await expect(tx).to.not.emit(rewardsManager, 'ServiceQualityOracleSet') + }) + }) + describe('subgraph availability service', function () { it('reject set subgraph oracle if unauthorized', async function () { const tx = rewardsManager.connect(indexer1).setSubgraphAvailabilityOracle(oracle.address) @@ -812,6 +896,98 @@ describe('Rewards', () => { .emit(rewardsManager, 'RewardsDenied') .withArgs(indexer1.address, allocationID1, await epochManager.currentEpoch()) }) + + it('should deny rewards due to service quality oracle', async function () { + // Setup service quality oracle that denies rewards for indexer1 + const MockServiceQualityOracleFactory = await hre.ethers.getContractFactory( + 'contracts/tests/MockServiceQualityOracle.sol:MockServiceQualityOracle', + ) + const mockOracle = await MockServiceQualityOracleFactory.deploy(false) // Default to deny + await mockOracle.deployed() + + // Set the service quality oracle + await rewardsManager.connect(governor).setServiceQualityOracle(mockOracle.address) + + // Align with the epoch boundary + await helpers.mineEpoch(epochManager) + + // Setup allocation + await setupIndexerAllocation() + + // Jump to next epoch + await helpers.mineEpoch(epochManager) + + // Calculate expected rewards (for verification in the event) + const expectedIndexingRewards = toGRT('1400') + + // Close allocation. At this point rewards should be denied due to service quality + const tx = staking.connect(indexer1).closeAllocation(allocationID1, randomHexBytes()) + await expect(tx) + .emit(rewardsManager, 'RewardsDeniedDueToServiceQuality') + .withArgs(indexer1.address, allocationID1, expectedIndexingRewards) + }) + + it('should allow rewards when service quality oracle approves', async function () { + // Setup service quality oracle that allows rewards for indexer1 + const MockServiceQualityOracleFactory = await hre.ethers.getContractFactory( + 'contracts/tests/MockServiceQualityOracle.sol:MockServiceQualityOracle', + ) + const mockOracle = await MockServiceQualityOracleFactory.deploy(true) // Default to allow + await mockOracle.deployed() + + // Set the service quality oracle + await rewardsManager.connect(governor).setServiceQualityOracle(mockOracle.address) + + // Align with the epoch boundary + await helpers.mineEpoch(epochManager) + + // Setup allocation + await setupIndexerAllocation() + + // Jump to next epoch + await helpers.mineEpoch(epochManager) + + // Calculate expected rewards + const expectedIndexingRewards = toGRT('1400') + + // Close allocation. At this point rewards should be assigned normally + const tx = staking.connect(indexer1).closeAllocation(allocationID1, randomHexBytes()) + await expect(tx) + .emit(rewardsManager, 'RewardsAssigned') + .withArgs(indexer1.address, allocationID1, await epochManager.currentEpoch(), expectedIndexingRewards) + }) + + it('should handle zero rewards scenario correctly', async function () { + // Setup allocation with zero issuance to create zero rewards scenario + await rewardsManager.connect(governor).setIssuancePerBlock(0) + + // Align with the epoch boundary + await helpers.mineEpoch(epochManager) + + // Setup allocation + await setupIndexerAllocation() + + // Jump to next epoch + await helpers.mineEpoch(epochManager) + + // Before state + const beforeTokenSupply = await grt.totalSupply() + const beforeStakingBalance = await grt.balanceOf(staking.address) + + // Close allocation. At this point rewards should be zero + const tx = staking.connect(indexer1).closeAllocation(allocationID1, randomHexBytes()) + await expect(tx) + .emit(rewardsManager, 'RewardsAssigned') + .withArgs(indexer1.address, allocationID1, await epochManager.currentEpoch(), 0) + + // After state - should be unchanged since no rewards were minted + const afterTokenSupply = await grt.totalSupply() + const afterStakingBalance = await grt.balanceOf(staking.address) + + // Check that no tokens were minted (rewards were 0) + expect(afterTokenSupply).eq(beforeTokenSupply) + expect(afterStakingBalance).eq(beforeStakingBalance) + }) }) }) diff --git a/packages/issuance/.markdownlint.json b/packages/issuance/.markdownlint.json new file mode 100644 index 000000000..18947b0be --- /dev/null +++ b/packages/issuance/.markdownlint.json @@ -0,0 +1,3 @@ +{ + "extends": "../../.markdownlint.json" +} diff --git a/packages/issuance/.solcover.js b/packages/issuance/.solcover.js new file mode 100644 index 000000000..e3dbe2e27 --- /dev/null +++ b/packages/issuance/.solcover.js @@ -0,0 +1,19 @@ +module.exports = { + skipFiles: ['test/'], + providerOptions: { + mnemonic: 'myth like bonus scare over problem client lizard pioneer submit female collect', + network_id: 1337, + }, + istanbulFolder: './test/reports/coverage', + configureYulOptimizer: true, + mocha: { + grep: '@skip-on-coverage', + invert: true, + }, + reporter: ['html', 'lcov', 'text'], + reporterOptions: { + html: { + directory: './test/reports/coverage/html', + }, + }, +} diff --git a/packages/issuance/.solhint.json b/packages/issuance/.solhint.json new file mode 100644 index 000000000..d30847305 --- /dev/null +++ b/packages/issuance/.solhint.json @@ -0,0 +1,3 @@ +{ + "extends": ["solhint:recommended", "./../../.solhint.json"] +} diff --git a/packages/issuance/contracts/common/BaseUpgradeable.sol b/packages/issuance/contracts/common/BaseUpgradeable.sol new file mode 100644 index 000000000..85610f0c9 --- /dev/null +++ b/packages/issuance/contracts/common/BaseUpgradeable.sol @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity 0.8.27; + +import { Initializable } from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol"; +import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; +import { IGraphToken } from "@graphprotocol/common/contracts/token/IGraphToken.sol"; + +/** + * @title BaseUpgradeable + * @author Edge & Node + * @notice A base contract that provides role-based access control and pausability. + * + * @dev This contract combines OpenZeppelin's AccessControl and Pausable + * to provide a standardized way to manage access control and pausing functionality. + * It uses ERC-7201 namespaced storage pattern for better storage isolation. + * This contract is abstract and meant to be inherited by other contracts. + * @custom:security-contact Please email security+contracts@thegraph.com if you find any bugs. We might have an active bug bounty program. + */ +abstract contract BaseUpgradeable is Initializable, AccessControlUpgradeable, PausableUpgradeable { + // -- Constants -- + + /// @notice One million - used as the denominator for values provided as Parts Per Million (PPM) + /// @dev This constant represents 1,000,000 and serves as the denominator when working with + /// PPM values. For example, 50% would be represented as 500,000 PPM, calculated as + /// (500,000 / MILLION) = 0.5 = 50% + uint256 public constant MILLION = 1_000_000; + + // -- Role Constants -- + + /** + * @notice Role identifier for governor accounts + * @dev Governors have the highest level of access and can: + * - Grant and revoke roles within the established hierarchy + * - Perform administrative functions and system configuration + * - Set critical parameters and upgrade contracts + * Admin of: GOVERNOR_ROLE, PAUSE_ROLE, OPERATOR_ROLE + */ + bytes32 public constant GOVERNOR_ROLE = keccak256("GOVERNOR_ROLE"); + + /** + * @notice Role identifier for pause accounts + * @dev Pause role holders can: + * - Pause and unpause contract operations for emergency situations + * Typically granted to automated monitoring systems or emergency responders. + * Pausing is intended for quick response to potential threats, and giving time for investigation and resolution (potentially with governance intervention). + * Admin: GOVERNOR_ROLE + */ + bytes32 public constant PAUSE_ROLE = keccak256("PAUSE_ROLE"); + + /** + * @notice Role identifier for operator accounts + * @dev Operators can: + * - Perform operational tasks as defined by inheriting contracts + * - Manage roles that are designated as operator-administered + * Admin: GOVERNOR_ROLE + */ + bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); + + // -- Immutable Variables -- + + /// @notice The Graph Token contract + /// @custom:oz-upgrades-unsafe-allow state-variable-immutable + IGraphToken internal immutable GRAPH_TOKEN; + + // -- Custom Errors -- + + /// @notice Thrown when attempting to set the Graph Token to the zero address + error GraphTokenCannotBeZeroAddress(); + + /// @notice Thrown when attempting to set the governor to the zero address + error GovernorCannotBeZeroAddress(); + + // -- Constructor -- + + /** + * @notice Constructor for the BaseUpgradeable contract + * @dev This contract is upgradeable, but we use the constructor to set immutable variables + * and disable initializers to prevent the implementation contract from being initialized. + * @param graphToken Address of the Graph Token contract + * @custom:oz-upgrades-unsafe-allow constructor + */ + constructor(address graphToken) { + require(graphToken != address(0), GraphTokenCannotBeZeroAddress()); + GRAPH_TOKEN = IGraphToken(graphToken); + _disableInitializers(); + } + + // -- Initialization -- + + /** + * @notice Internal function to initialize the BaseUpgradeable contract + * @dev This function is used by child contracts to initialize the BaseUpgradeable contract + * @param governor Address that will have the GOVERNOR_ROLE + */ + function __BaseUpgradeable_init(address governor) internal { + // solhint-disable-previous-line func-name-mixedcase + + __AccessControl_init(); + __Pausable_init(); + + __BaseUpgradeable_init_unchained(governor); + } + + /** + * @notice Internal unchained initialization function for BaseUpgradeable + * @dev This function sets up the governor role and role admin hierarchy + * @param governor Address that will have the GOVERNOR_ROLE + */ + function __BaseUpgradeable_init_unchained(address governor) internal { + // solhint-disable-previous-line func-name-mixedcase + + require(governor != address(0), GovernorCannotBeZeroAddress()); + + // Set up role admin hierarchy: + // GOVERNOR is admin of GOVERNOR, PAUSE, and OPERATOR roles + _setRoleAdmin(GOVERNOR_ROLE, GOVERNOR_ROLE); + _setRoleAdmin(PAUSE_ROLE, GOVERNOR_ROLE); + _setRoleAdmin(OPERATOR_ROLE, GOVERNOR_ROLE); + + // Grant initial governor role + _grantRole(GOVERNOR_ROLE, governor); + } + + // -- External Functions -- + + /** + * @notice Pause the contract + * @dev Only callable by accounts with the PAUSE_ROLE + */ + function pause() external onlyRole(PAUSE_ROLE) { + _pause(); + } + + /** + * @notice Unpause the contract + * @dev Only callable by accounts with the PAUSE_ROLE + */ + function unpause() external onlyRole(PAUSE_ROLE) { + _unpause(); + } +} diff --git a/packages/issuance/contracts/quality/ServiceQualityOracle.md b/packages/issuance/contracts/quality/ServiceQualityOracle.md new file mode 100644 index 000000000..c669dd765 --- /dev/null +++ b/packages/issuance/contracts/quality/ServiceQualityOracle.md @@ -0,0 +1,202 @@ +# ServiceQualityOracle + +The ServiceQualityOracle is a smart contract that manages indexer eligibility for receiving rewards based on service quality assessments. It implements a time-based allowlist system where indexers must be explicitly approved by authorized oracles to receive rewards. + +## Overview + +The contract operates on a "deny by default" principle - all indexers are initially ineligible for rewards until they are explicitly allowed by an authorized oracle. Once allowed, indexers remain eligible for a configurable period before their eligibility expires and they need to be re-approved. + +## Key Features + +- **Time-based Eligibility**: Indexers are allowed for a configurable period (default: 14 days) +- **Oracle-based Approval**: Only authorized oracles can approve indexers +- **Global Toggle**: Quality checking can be globally enabled/disabled +- **Timeout Mechanism**: If oracles don't update for too long, all indexers are automatically allowed +- **Role-based Access Control**: Uses hierarchical roles for governance and operations + +## Architecture + +### Roles + +The contract uses four main roles: + +- **GOVERNOR_ROLE**: Can grant/revoke operator roles and perform governance actions +- **OPERATOR_ROLE**: Can configure contract parameters and manage oracle roles +- **ORACLE_ROLE**: Can approve indexers for rewards +- **PAUSE_ROLE**: Can pause contract operations (inherited from BaseUpgradeable) + +### Storage + +The contract uses ERC-7201 namespaced storage to prevent storage collisions in upgradeable contracts: + +- `allowedIndexerTimestamps`: Maps indexer addresses to their last approval timestamp +- `allowedPeriod`: Duration (in seconds) for which approval lasts (default: 14 days) +- `checkingActive`: Global flag to enable/disable quality checking (default: false, to be enabled by operator when ready) +- `oracleUpdateTimeout`: Timeout after which all indexers are automatically allowed (default: 7 days) +- `lastOracleUpdateTime`: Timestamp of the last oracle update + +## Core Functions + +### Oracle Management + +Oracle roles are managed through the standard AccessControl functions inherited from BaseUpgradeable: + +- **`grantRole(bytes32 role, address account)`**: Grant oracle privileges to an account (OPERATOR_ROLE only) +- **`revokeRole(bytes32 role, address account)`**: Revoke oracle privileges from an account (OPERATOR_ROLE only) +- **`hasRole(bytes32 role, address account)`**: Check if an account has oracle privileges + +The `ORACLE_ROLE` constant can be used as the role parameter for these functions. + +### Configuration + +#### `setAllowedPeriod(uint256 allowedPeriod) → bool` + +- **Access**: OPERATOR_ROLE only +- **Purpose**: Set how long indexer approvals last +- **Parameters**: `allowedPeriod` - Duration in seconds +- **Returns**: Always true for current implementation +- **Events**: Emits `AllowedPeriodUpdated` if value changes + +#### `setOracleUpdateTimeout(uint256 oracleUpdateTimeout) → bool` + +- **Access**: OPERATOR_ROLE only +- **Purpose**: Set timeout after which all indexers are automatically allowed +- **Parameters**: `oracleUpdateTimeout` - Timeout duration in seconds +- **Returns**: Always true for current implementation +- **Events**: Emits `OracleUpdateTimeoutUpdated` if value changes + +#### `setQualityChecking(bool enabled) → bool` + +- **Access**: OPERATOR_ROLE only +- **Purpose**: Enable or disable quality checking globally +- **Parameters**: `enabled` - True to enable, false to disable +- **Returns**: Always true for current implementation +- **Events**: Emits `QualityCheckingUpdated` if state changes + +### Indexer Management + +#### `allowIndexers(address[] calldata indexers, bytes calldata data) → uint256` + +- **Access**: ORACLE_ROLE only +- **Purpose**: Approve indexers for rewards +- **Parameters**: + - `indexers` - Array of indexer addresses (zero addresses ignored) + - `data` - Arbitrary calldata for future extensions +- **Returns**: Number of indexers whose timestamp was updated +- **Events**: + - Emits `IndexerQualityData` with oracle and data + - Emits `IndexerAllowed` for each newly allowed indexer +- **Notes**: + - Updates `lastOracleUpdateTime` to current block timestamp + - Only updates timestamp if less than current block timestamp + - Ignores zero addresses and duplicate updates within same block + +### View Functions + +#### `isAllowed(address indexer) → bool` + +- **Purpose**: Check if an indexer is eligible for rewards +- **Logic**: + 1. If quality checking is disabled → return true + 2. If oracle timeout exceeded → return true + 3. Otherwise → check if indexer's approval is still valid +- **Returns**: True if indexer is allowed, false otherwise + +#### `isAuthorizedOracle(address oracle) → bool` + +- **Purpose**: Check if an address has oracle privileges +- **Returns**: True if address has ORACLE_ROLE + +#### `getLastAllowedTime(address indexer) → uint256` + +- **Purpose**: Get the timestamp when indexer was last approved +- **Returns**: Timestamp or 0 if never approved + +#### `getAllowedPeriod() → uint256` + +- **Purpose**: Get the current allowed period +- **Returns**: Duration in seconds + +#### `getOracleUpdateTimeout() → uint256` + +- **Purpose**: Get the current oracle update timeout +- **Returns**: Duration in seconds + +#### `getLastOracleUpdateTime() → uint256` + +- **Purpose**: Get when oracles last updated +- **Returns**: Timestamp of last oracle update + +#### `isQualityCheckingActive() → bool` + +- **Purpose**: Check if quality checking is enabled +- **Returns**: True if active, false if disabled + +## Eligibility Logic + +An indexer is considered allowed if ANY of the following conditions are met: + +1. **Valid approval** (`block.timestamp <= allowedIndexerTimestamps[indexer] + allowedPeriod`) +2. **Oracle timeout exceeded** (`lastOracleUpdateTime + oracleUpdateTimeout < block.timestamp`) +3. **Quality checking is disabled** (`checkingActive = false`) + +This design ensures that: + +- The system fails open if oracles stop updating +- Operators can disable quality checking entirely if needed +- Individual indexer approvals have time limits + +In normal operation, the first condition is expected to be the only one that applies. The other two conditions provide fail-safes for oracle failures, or in extreme cases an operator override. For normal operational failure of oracles, the system gracefully degrades into a "allow all" mode. This mechanism is not perfect in that oracles could still be updating but allowing far fewer indexers than they should. However this is regarded as simple mechanism that is good enough to start with and provide a foundation for future improvements and decentralization. + +While this simple model allows the criteria for providing good service to evolve over time (which is essential for the long-term health of the network), it captures sufficient information on-chain for indexers to be able to monitor their eligibility. This is important to ensure that even in the absence of other sources of information regarding observed indexer service, indexers have a good transparency about if they are being observed to be providing good service, and for how long their current approval is valid. + +It might initially seem safer to allow indexers by default unless an oracle explicitly denies an indexer. While that might seem safer from the perspective of the ServiceQualityOracle in isolation, in the absence of a more sophisticated voting system it would make the system vulnerable to a single bad oracle denying many indexers. The design of deny by default is better suited to allowing redundant oracles to be working in parallel, where only one needs to be successfully detecting indexers that are providing quality service, as well as eventually allowing different oracles to have different approval criteria and/or inputs. Therefore deny by default facilitates a more resilient and open oracle system that is less vulnerable to a single points of failure, and more open to increasing decentralization over time. + +In general to be rewarded for providing service on The Graph, there is expected to be proof provided of good operation (such as for proof of indexing). While proof should be required to receive rewards, the system is designed for participants to have confidence is being able to adequately prove good operation (and in the case of oracles, be seen by at least one observer) that is sufficient to allow the indexer to receive rewards. The oracle model is in general far more suited to collecting evidence of good operation, from multiple independent observers, rather than any observer being able to establish that an indexer is not providing good service. + +## Events + +```solidity +event IndexerQualityData(address indexed oracle, bytes data); +event IndexerAllowed(address indexed indexer, address indexed oracle); +event AllowedPeriodUpdated(uint256 oldPeriod, uint256 newPeriod); +event QualityCheckingUpdated(bool active); +event OracleUpdateTimeoutUpdated(uint256 oldTimeout, uint256 newTimeout); +``` + +## Default Configuration + +- **Allowed Period**: 14 days (1,209,600 seconds) +- **Oracle Update Timeout**: 7 days (604,800 seconds) +- **Quality Checking**: Disabled (false) +- **Last Oracle Update Time**: 0 (never updated) + +The system is deployed with reasonable defaults but can be adjusted as required. Quality checking is disabled by default as the expectation is to first see oracles successfully allowing indexers and having suitably established allowed indexers before enabling. + +## Usage Patterns + +### Initial Setup + +1. Deploy contract with Graph Token address +2. Initialize with governor address +3. Governor grants OPERATOR_ROLE to operational accounts +4. Operators grant ORACLE_ROLE to oracle services using `grantRole(ORACLE_ROLE, oracleAddress)` +5. Configure allowed period and timeout as needed +6. After demonstration of successful oracle operation and having established a set of allowed indexers, quality checking is enabled + +### Normal Operation + +1. Oracles periodically call `allowIndexers()` with quality-approved indexers +2. Reward systems call `isAllowed()` to check indexer eligibility +3. Operators adjust parameters as needed via configuration functions +4. The operation of the system is monitored and adjusted as needed + +### Emergency Scenarios + +- **Oracle failure**: System automatically allows all indexers after timeout +- **Quality issues**: Operators can disable quality checking globally +- **Parameter changes**: Operators can adjust periods and timeouts + +## Integration + +The contract implements the `IServiceQualityOracle` interface and can be integrated with any system that needs to verify indexer quality status. The primary integration point is the `isAllowed(address)` function which returns a simple boolean indicating eligibility. diff --git a/packages/issuance/contracts/quality/ServiceQualityOracle.sol b/packages/issuance/contracts/quality/ServiceQualityOracle.sol new file mode 100644 index 000000000..5c4c159e7 --- /dev/null +++ b/packages/issuance/contracts/quality/ServiceQualityOracle.sol @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +pragma solidity 0.8.27; + +import { IServiceQualityOracle } from "@graphprotocol/common/contracts/quality/IServiceQualityOracle.sol"; +import { BaseUpgradeable } from "../common/BaseUpgradeable.sol"; + +/** + * @title ServiceQualityOracle + * @author Edge & Node + * @notice This contract allows authorized oracles to allow indexers to receive rewards + * with an expiration mechanism. Indexers are denied by default until they are explicitly allowed, + * and their eligibility expires after a configurable allowed period. + * The contract also includes a global quality check toggle and an oracle update timeout mechanism. + * @custom:security-contact Please email security+contracts@thegraph.com if you find any bugs. We might have an active bug bounty program. + */ +contract ServiceQualityOracle is BaseUpgradeable, IServiceQualityOracle { + // -- Role Constants -- + + /** + * @notice Oracle role identifier + * @dev Oracle role holders can: + * - Allow indexers to receive rewards (based on off-chain quality assessment) + * This role is typically granted to automated quality assessment systems + * Admin: OPERATOR_ROLE (operators can manage oracle roles) + */ + bytes32 public constant ORACLE_ROLE = keccak256("ORACLE_ROLE"); + // -- Namespaced Storage -- + + /// @notice ERC-7201 storage location for ServiceQualityOracle + bytes32 private constant SERVICE_QUALITY_ORACLE_STORAGE_LOCATION = + // Not needed for compile time calculation + // solhint-disable-next-line gas-small-strings + keccak256(abi.encode(uint256(keccak256("graphprotocol.storage.ServiceQualityOracle")) - 1)) & + ~bytes32(uint256(0xff)); + + /// @notice Main storage structure for ServiceQualityOracle using ERC-7201 namespaced storage + /// @param allowedIndexerTimestamps Mapping of indexers to their last allowed timestamp + /// @param allowedPeriod Period in seconds for which indexer allowed status lasts + /// @param checkingActive Flag to enable/disable quality checking + /// @param oracleUpdateTimeout Timeout period in seconds after which isAllowed returns true if no oracle updates + /// @param lastOracleUpdateTime Timestamp of the last oracle update + /// @custom:storage-location erc7201:graphprotocol.storage.ServiceQualityOracle + struct ServiceQualityOracleData { + /// @dev Mapping of indexers to their last allowed timestamp + mapping(address => uint256) allowedIndexerTimestamps; + /// @dev Period in seconds for which indexer allowed status lasts + uint256 allowedPeriod; + /// @dev Flag to enable/disable quality checking + bool checkingActive; + /// @dev Timeout period in seconds after which isAllowed returns true if no oracle updates + uint256 oracleUpdateTimeout; + /// @dev Timestamp of the last oracle update + uint256 lastOracleUpdateTime; + } + + /** + * @notice Returns the storage struct for ServiceQualityOracle + * @return $ contract storage + */ + function _getServiceQualityOracleStorage() private pure returns (ServiceQualityOracleData storage $) { + // solhint-disable-previous-line use-natspec + // Solhint does not support $ return variable in natspec + bytes32 slot = SERVICE_QUALITY_ORACLE_STORAGE_LOCATION; + // solhint-disable-next-line no-inline-assembly + assembly { + $.slot := slot + } + } + + // -- Events -- + + /// @notice Emitted when an oracle submits quality data + /// @param oracle The address of the oracle that submitted the data + /// @param data The quality data submitted by the oracle + event IndexerQualityData(address indexed oracle, bytes data); + + /// @notice Emitted when an indexer is allowed by an oracle + /// @param indexer The address of the indexer that was allowed + /// @param oracle The address of the oracle that allowed the indexer + event IndexerAllowed(address indexed indexer, address indexed oracle); + + /// @notice Emitted when the allowed period is updated + /// @param oldPeriod The previous allowed period in seconds + /// @param newPeriod The new allowed period in seconds + event AllowedPeriodUpdated(uint256 indexed oldPeriod, uint256 indexed newPeriod); + + /// @notice Emitted when quality checking is enabled or disabled + /// @param active True if quality checking is enabled, false if disabled + event QualityCheckingUpdated(bool indexed active); // solhint-disable-line gas-indexed-events + + /// @notice Emitted when the oracle update timeout is updated + /// @param oldTimeout The previous timeout period in seconds + /// @param newTimeout The new timeout period in seconds + event OracleUpdateTimeoutUpdated(uint256 indexed oldTimeout, uint256 indexed newTimeout); + + // -- Constructor -- + + /** + * @notice Constructor for the ServiceQualityOracle contract + * @dev This contract is upgradeable, but we use the constructor to pass the Graph Token address + * to the base contract. + * @param graphToken Address of the Graph Token contract + * @custom:oz-upgrades-unsafe-allow constructor + */ + constructor(address graphToken) BaseUpgradeable(graphToken) {} + + // -- Initialization -- + + /** + * @notice Initialize the ServiceQualityOracle contract + * @param governor Address that will have the GOVERNOR_ROLE + * @dev Also sets OPERATOR as admin of ORACLE role + */ + function initialize(address governor) external virtual initializer { + __BaseUpgradeable_init(governor); + + // OPERATOR is admin of ORACLE role + _setRoleAdmin(ORACLE_ROLE, OPERATOR_ROLE); + + // Set default values + ServiceQualityOracleData storage $ = _getServiceQualityOracleStorage(); + $.allowedPeriod = 14 days; + $.oracleUpdateTimeout = 7 days; + $.checkingActive = false; // Start with quality checking disabled, to be enabled later when the oracle is ready + } + + /** + * @notice Check if this contract supports a given interface + * @dev Overrides the supportsInterface function from ERC165Upgradeable + * @param interfaceId The interface identifier to check + * @return True if the contract supports the interface, false otherwise + */ + function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { + return interfaceId == type(IServiceQualityOracle).interfaceId || super.supportsInterface(interfaceId); + } + + // -- Governance Functions -- + + /** + * @notice Set the allowed period for indexers + * @dev Only callable by accounts with the OPERATOR_ROLE + * @param allowedPeriod New allowed period in seconds + * @return True if the state is as requested (allowed period is set to the specified value) + */ + function setAllowedPeriod(uint256 allowedPeriod) external onlyRole(OPERATOR_ROLE) returns (bool) { + ServiceQualityOracleData storage $ = _getServiceQualityOracleStorage(); + uint256 oldAllowedPeriod = $.allowedPeriod; + + if (allowedPeriod != oldAllowedPeriod) { + $.allowedPeriod = allowedPeriod; + emit AllowedPeriodUpdated(oldAllowedPeriod, allowedPeriod); + } + + return true; + } + + /** + * @notice Set the oracle update timeout + * @dev Only callable by accounts with the OPERATOR_ROLE + * @param oracleUpdateTimeout New timeout period in seconds + * @return True if the state is as requested (timeout is set to the specified value) + */ + function setOracleUpdateTimeout(uint256 oracleUpdateTimeout) external onlyRole(OPERATOR_ROLE) returns (bool) { + ServiceQualityOracleData storage $ = _getServiceQualityOracleStorage(); + uint256 oldTimeout = $.oracleUpdateTimeout; + + if (oracleUpdateTimeout != oldTimeout) { + $.oracleUpdateTimeout = oracleUpdateTimeout; + emit OracleUpdateTimeoutUpdated(oldTimeout, oracleUpdateTimeout); + } + + return true; + } + + /** + * @notice Set quality checking state + * @dev Only callable by accounts with the OPERATOR_ROLE + * @param enabled True to enable quality checking, false to disable + * @return True if successfully set (always the case for current code) + */ + function setQualityChecking(bool enabled) external onlyRole(OPERATOR_ROLE) returns (bool) { + ServiceQualityOracleData storage $ = _getServiceQualityOracleStorage(); + + if ($.checkingActive != enabled) { + $.checkingActive = enabled; + emit QualityCheckingUpdated(enabled); + } + + return true; + } + + /** + * @notice Mark provided indexers as meeting service quality requirements to receive rewards + * @param indexers Array of indexer addresses. Zero addresses are ignored. + * @param data Arbitrary calldata for future extensions + * @return Number of indexers whose allowed timestamp was updated + */ + function allowIndexers( + address[] calldata indexers, + bytes calldata data + ) external onlyRole(ORACLE_ROLE) returns (uint256) { + emit IndexerQualityData(msg.sender, data); + + uint256 updatedCount = 0; + uint256 blockTimestamp = block.timestamp; + + ServiceQualityOracleData storage $ = _getServiceQualityOracleStorage(); + $.lastOracleUpdateTime = blockTimestamp; + + // Update each indexer's allowed timestamp + for (uint256 i = 0; i < indexers.length; ++i) { + address indexer = indexers[i]; + + if (indexer != address(0) && $.allowedIndexerTimestamps[indexer] < blockTimestamp) { + $.allowedIndexerTimestamps[indexer] = blockTimestamp; + emit IndexerAllowed(indexer, msg.sender); + ++updatedCount; + } + } + + return updatedCount; + } + + // -- View Functions -- + + /** + * @inheritdoc IServiceQualityOracle + */ + function isAllowed(address indexer) external view returns (bool) { + ServiceQualityOracleData storage $ = _getServiceQualityOracleStorage(); + + // If quality checking is disabled, treat all indexers as allowed + if (!$.checkingActive) return true; + + // If no oracle updates have been made for oracleUpdateTimeout, treat all indexers as allowed + if ($.lastOracleUpdateTime + $.oracleUpdateTimeout < block.timestamp) return true; + + return block.timestamp < $.allowedIndexerTimestamps[indexer] + $.allowedPeriod; + } + + /** + * @notice Check if an oracle is authorized + * @param oracle Address of the oracle + * @return True if the oracle is authorized, false otherwise + */ + function isAuthorizedOracle(address oracle) external view returns (bool) { + return hasRole(ORACLE_ROLE, oracle); + } + + /** + * @notice Get the last allowed timestamp for an indexer + * @param indexer Address of the indexer + * @return The last allowed timestamp, or 0 if the indexer has not been allowed + */ + function getLastAllowedTime(address indexer) external view returns (uint256) { + return _getServiceQualityOracleStorage().allowedIndexerTimestamps[indexer]; + } + + /** + * @notice Get the allowed period + * @return The current allowed period in seconds + */ + function getAllowedPeriod() external view returns (uint256) { + return _getServiceQualityOracleStorage().allowedPeriod; + } + + /** + * @notice Get the oracle update timeout + * @return The current oracle update timeout in seconds + */ + function getOracleUpdateTimeout() external view returns (uint256) { + return _getServiceQualityOracleStorage().oracleUpdateTimeout; + } + + /** + * @notice Get the last oracle update time + * @return The timestamp of the last oracle update + */ + function getLastOracleUpdateTime() external view returns (uint256) { + return _getServiceQualityOracleStorage().lastOracleUpdateTime; + } + + /** + * @notice Check if quality checking is active + * @return True if quality checking is active, false otherwise + */ + function isQualityCheckingActive() external view returns (bool) { + return _getServiceQualityOracleStorage().checkingActive; + } +} diff --git a/packages/issuance/contracts/test/InterfaceIdExtractor.sol b/packages/issuance/contracts/test/InterfaceIdExtractor.sol new file mode 100644 index 000000000..8fd0b7d81 --- /dev/null +++ b/packages/issuance/contracts/test/InterfaceIdExtractor.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.0; + +import { IServiceQualityOracle } from "@graphprotocol/common/contracts/quality/IServiceQualityOracle.sol"; + +/** + * @title InterfaceIdExtractor + * @author Edge & Node + * @notice Utility contract for extracting ERC-165 interface IDs from Solidity interfaces + * @dev This contract is used during the build process to generate interface ID constants + * that match Solidity's own calculations, ensuring consistency between tests and actual + * interface implementations. + */ +contract InterfaceIdExtractor { + /** + * @notice Returns the ERC-165 interface ID for IServiceQualityOracle + * @return The interface ID as calculated by Solidity + */ + function getIServiceQualityOracleId() external pure returns (bytes4) { + return type(IServiceQualityOracle).interfaceId; + } +} diff --git a/packages/issuance/hardhat.config.cjs b/packages/issuance/hardhat.config.cjs new file mode 100644 index 000000000..27f2f6214 --- /dev/null +++ b/packages/issuance/hardhat.config.cjs @@ -0,0 +1,115 @@ +require('@nomicfoundation/hardhat-ethers') +require('@nomicfoundation/hardhat-chai-matchers') +require('@typechain/hardhat') +require('hardhat-abi-exporter') +require('hardhat-contract-sizer') +require('hardhat-gas-reporter') +require('solidity-coverage') +require('@openzeppelin/hardhat-upgrades') +require('@nomicfoundation/hardhat-verify') + +const dotenv = require('dotenv') + +dotenv.config() + +const config = { + // paths: { + // sources: './contracts', + // tests: './test', + // artifacts: './build/contracts', + // cache: './cache', + // }, + solidity: { + compilers: [ + { + version: '0.8.27', + }, + ], + }, + defaultNetwork: 'hardhat', + networks: { + hardhat: { + chainId: 1337, + loggingEnabled: false, + gas: 12000000, + gasPrice: 'auto', + initialBaseFeePerGas: 0, + blockGasLimit: 12000000, + // Support for forking + forking: + process.env.FORK === 'true' + ? { + url: + process.env.FORK_NETWORK === 'arbitrumSepolia' + ? process.env.ARBITRUM_SEPOLIA_RPC_URL || 'https://sepolia-rollup.arbitrum.io/rpc' + : process.env.ARBITRUM_ONE_RPC_URL || 'https://arb1.arbitrum.io/rpc', + blockNumber: process.env.FORK_BLOCK_NUMBER ? parseInt(process.env.FORK_BLOCK_NUMBER) : undefined, + } + : undefined, + }, + localhost: { + chainId: 1337, + url: 'http://127.0.0.1:8545', + accounts: { + mnemonic: 'myth like bonus scare over problem client lizard pioneer submit female collect', + }, + }, + // For connecting to Anvil fork + anvilFork: { + chainId: 31337, // Anvil's default chainId + url: process.env.ANVIL_FORK_URL || 'http://127.0.0.1:8545', + accounts: process.env.PRIVATE_KEY + ? [process.env.PRIVATE_KEY] + : { + mnemonic: 'test test test test test test test test test test test junk', + }, + // Pass through environment variables to the deployment script + params_file: process.env.PARAMS_FILE, + }, + arbitrumSepolia: { + chainId: 421614, + url: process.env.ARBITRUM_SEPOLIA_RPC_URL || 'https://sepolia-rollup.arbitrum.io/rpc', + accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [], + gasPrice: 'auto', + }, + arbitrumOne: { + chainId: 42161, + url: process.env.ARBITRUM_ONE_RPC_URL || 'https://arb1.arbitrum.io/rpc', + accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [], + gasPrice: 'auto', + }, + }, + gasReporter: { + enabled: process.env.REPORT_GAS ? true : false, + showTimeSpent: true, + currency: 'USD', + outputFile: 'reports/gas-report.log', + }, + typechain: { + outDir: 'types', + target: 'ethers-v6', + }, + contractSizer: { + alphaSort: true, + runOnCompile: false, + disambiguatePaths: false, + }, + etherscan: { + apiKey: { + arbitrumOne: process.env.ARBISCAN_API_KEY, + arbitrumSepolia: process.env.ARBISCAN_API_KEY, + }, + customChains: [ + { + network: 'arbitrumSepolia', + chainId: 421614, + urls: { + apiURL: 'https://api-sepolia.arbiscan.io/api', + browserURL: 'https://sepolia.arbiscan.io', + }, + }, + ], + }, +} + +module.exports = config diff --git a/packages/issuance/hardhat.coverage.config.ts b/packages/issuance/hardhat.coverage.config.ts new file mode 100644 index 000000000..19d508b48 --- /dev/null +++ b/packages/issuance/hardhat.coverage.config.ts @@ -0,0 +1,82 @@ +import '@nomicfoundation/hardhat-ethers' +import '@nomicfoundation/hardhat-chai-matchers' +import '@nomicfoundation/hardhat-network-helpers' +import '@openzeppelin/hardhat-upgrades' +import 'hardhat-gas-reporter' +import 'solidity-coverage' +import 'dotenv/config' + +import { HardhatUserConfig } from 'hardhat/config' + +const config: HardhatUserConfig = { + paths: { + sources: './contracts', + tests: './test/tests', + artifacts: './artifacts', + cache: './cache', + }, + solidity: { + compilers: [ + { + version: '0.8.27', + }, + ], + }, + defaultNetwork: 'hardhat', + networks: { + hardhat: { + chainId: 1337, + loggingEnabled: false, + gas: 12000000, + gasPrice: 'auto', + initialBaseFeePerGas: 0, + blockGasLimit: 12000000, + forking: + process.env.FORK === 'true' + ? { + url: + process.env.FORK_NETWORK === 'arbitrumSepolia' + ? process.env.ARBITRUM_SEPOLIA_RPC_URL || 'https://sepolia-rollup.arbitrum.io/rpc' + : process.env.ARBITRUM_ONE_RPC_URL || 'https://arb1.arbitrum.io/rpc', + blockNumber: process.env.FORK_BLOCK_NUMBER ? parseInt(process.env.FORK_BLOCK_NUMBER) : undefined, + } + : undefined, + }, + localhost: { + chainId: 1337, + url: 'http://127.0.0.1:8545', + accounts: { + mnemonic: 'myth like bonus scare over problem client lizard pioneer submit female collect', + }, + }, + anvilFork: { + chainId: 31337, + url: process.env.ANVIL_FORK_URL || 'http://127.0.0.1:8545', + accounts: process.env.PRIVATE_KEY + ? [process.env.PRIVATE_KEY] + : { + mnemonic: 'test test test test test test test test test test test junk', + }, + }, + arbitrumSepolia: { + chainId: 421614, + url: process.env.ARBITRUM_SEPOLIA_RPC_URL || 'https://sepolia-rollup.arbitrum.io/rpc', + accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [], + gasPrice: 'auto', + }, + arbitrumOne: { + chainId: 42161, + url: process.env.ARBITRUM_ONE_RPC_URL || 'https://arb1.arbitrum.io/rpc', + accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [], + gasPrice: 'auto', + }, + }, + gasReporter: { + enabled: process.env.REPORT_GAS ? true : false, + showTimeSpent: true, + currency: 'USD', + outputFile: 'reports/gas-report.log', + }, +} + +export default config diff --git a/packages/issuance/index.js b/packages/issuance/index.js new file mode 100644 index 000000000..4b0935649 --- /dev/null +++ b/packages/issuance/index.js @@ -0,0 +1,11 @@ +// Main entry point for @graphprotocol/issuance +// This package provides issuance contracts and artifacts + +const path = require('path') + +module.exports = { + contractsDir: path.join(__dirname, 'contracts'), + artifactsDir: path.join(__dirname, 'artifacts'), + typesDir: path.join(__dirname, 'types'), + cacheDir: path.join(__dirname, 'cache'), +} diff --git a/packages/issuance/package.json b/packages/issuance/package.json new file mode 100644 index 000000000..790234619 --- /dev/null +++ b/packages/issuance/package.json @@ -0,0 +1,70 @@ +{ + "name": "@graphprotocol/issuance", + "version": "1.0.0", + "description": "The Graph Issuance Contracts", + "main": "index.js", + "exports": { + ".": "./index.js", + "./artifacts/*": "./artifacts/*", + "./contracts/*": "./contracts/*", + "./types/*": "./types/*" + }, + "scripts": { + "build": "npm run build:self", + "build:self": "pnpm compile; pnpm typechain", + "clean": "rm -rf build/ cache/ dist/ forge-artifacts/ cache_forge/", + "compile": "hardhat compile", + "test": "pnpm --filter @graphprotocol/issuance-test --filter @graphprotocol/issuance-deploy test", + "lint": "pnpm lint:ts; pnpm lint:sol; pnpm lint:md; 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:sol": "solhint --fix --noPrompt --noPoster 'contracts/**/*.sol'; prettier -w --cache --log-level warn 'contracts/**/*.sol'", + "lint:md": "markdownlint --fix --ignore-path ../../.gitignore '**/*.md'; prettier -w --cache --log-level warn '**/*.md'", + "lint:json": "prettier -w --cache --log-level warn '**/*.json'", + "typechain": "hardhat typechain", + "verify": "hardhat verify", + "size": "hardhat size-contracts", + "forge:build": "forge build" + }, + "files": [ + "artifacts/**/*", + "types/**/*", + "contracts/**/*", + "README.md", + "LICENSE" + ], + "author": "The Graph Team", + "license": "GPL-2.0-or-later", + "devDependencies": { + "@graphprotocol/common": "workspace:^", + "@nomicfoundation/hardhat-ethers": "catalog:", + "@nomicfoundation/hardhat-verify": "catalog:", + "@openzeppelin/contracts": "^5.3.0", + "@openzeppelin/contracts-upgradeable": "^5.3.0", + "@openzeppelin/hardhat-upgrades": "^3.9.0", + "@typechain/ethers-v6": "^0.5.0", + "@typechain/hardhat": "catalog:", + "@types/node": "^20.17.50", + "dotenv": "catalog:", + "eslint": "catalog:", + "ethers": "catalog:", + "glob": "^11.0.2", + "globals": "^16.1.0", + "hardhat": "catalog:", + "hardhat-contract-sizer": "catalog:", + "hardhat-secure-accounts": "catalog:", + "hardhat-storage-layout": "catalog:", + "lint-staged": "16.0.0", + "markdownlint-cli": "catalog:", + "prettier": "catalog:", + "prettier-plugin-solidity": "catalog:", + "solhint": "catalog:", + "ts-node": "^10.9.2", + "typechain": "^8.3.0", + "typescript": "catalog:", + "typescript-eslint": "catalog:", + "yaml-lint": "^1.7.0" + }, + "dependencies": { + "@noble/hashes": "^1.8.0" + } +} diff --git a/packages/issuance/prettier.config.cjs b/packages/issuance/prettier.config.cjs new file mode 100644 index 000000000..4e8dcf4f3 --- /dev/null +++ b/packages/issuance/prettier.config.cjs @@ -0,0 +1,5 @@ +const baseConfig = require('../../prettier.config.cjs') + +module.exports = { + ...baseConfig, +} diff --git a/packages/issuance/test/hardhat.config.ts b/packages/issuance/test/hardhat.config.ts new file mode 100644 index 000000000..c485d1936 --- /dev/null +++ b/packages/issuance/test/hardhat.config.ts @@ -0,0 +1,82 @@ +import '@nomicfoundation/hardhat-ethers' +import '@nomicfoundation/hardhat-chai-matchers' +import '@nomicfoundation/hardhat-network-helpers' +import '@openzeppelin/hardhat-upgrades' +import 'hardhat-gas-reporter' +import 'solidity-coverage' +import 'dotenv/config' + +import { artifactsDir, cacheDir } from '@graphprotocol/issuance' +import { HardhatUserConfig } from 'hardhat/config' + +const config: HardhatUserConfig = { + paths: { + tests: './tests', + artifacts: artifactsDir, + cache: cacheDir, + }, + solidity: { + compilers: [ + { + version: '0.8.27', + }, + ], + }, + defaultNetwork: 'hardhat', + networks: { + hardhat: { + chainId: 1337, + loggingEnabled: false, + gas: 12000000, + gasPrice: 'auto', + initialBaseFeePerGas: 0, + blockGasLimit: 12000000, + forking: + process.env.FORK === 'true' + ? { + url: + process.env.FORK_NETWORK === 'arbitrumSepolia' + ? process.env.ARBITRUM_SEPOLIA_RPC_URL || 'https://sepolia-rollup.arbitrum.io/rpc' + : process.env.ARBITRUM_ONE_RPC_URL || 'https://arb1.arbitrum.io/rpc', + blockNumber: process.env.FORK_BLOCK_NUMBER ? parseInt(process.env.FORK_BLOCK_NUMBER) : undefined, + } + : undefined, + }, + localhost: { + chainId: 1337, + url: 'http://127.0.0.1:8545', + accounts: { + mnemonic: 'myth like bonus scare over problem client lizard pioneer submit female collect', + }, + }, + anvilFork: { + chainId: 31337, + url: process.env.ANVIL_FORK_URL || 'http://127.0.0.1:8545', + accounts: process.env.PRIVATE_KEY + ? [process.env.PRIVATE_KEY] + : { + mnemonic: 'test test test test test test test test test test test junk', + }, + }, + arbitrumSepolia: { + chainId: 421614, + url: process.env.ARBITRUM_SEPOLIA_RPC_URL || 'https://sepolia-rollup.arbitrum.io/rpc', + accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [], + gasPrice: 'auto', + }, + arbitrumOne: { + chainId: 42161, + url: process.env.ARBITRUM_ONE_RPC_URL || 'https://arb1.arbitrum.io/rpc', + accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [], + gasPrice: 'auto', + }, + }, + gasReporter: { + enabled: process.env.REPORT_GAS ? true : false, + showTimeSpent: true, + currency: 'USD', + outputFile: 'reports/gas-report.log', + }, +} + +export default config diff --git a/packages/issuance/test/package.json b/packages/issuance/test/package.json new file mode 100644 index 000000000..51cb1a397 --- /dev/null +++ b/packages/issuance/test/package.json @@ -0,0 +1,58 @@ +{ + "name": "@graphprotocol/issuance-test", + "version": "1.0.0", + "private": true, + "description": "Test utilities for @graphprotocol/issuance", + "main": "src/index.ts", + "types": "src/index.ts", + "exports": { + ".": { + "default": "./src/index.ts", + "types": "./src/index.ts" + } + }, + "dependencies": { + "@graphprotocol/issuance": "workspace:^", + "@graphprotocol/common": "workspace:^", + "@graphprotocol/contracts": "workspace:^", + "@graphprotocol/sdk": "workspace:^" + }, + "devDependencies": { + "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", + "@nomicfoundation/hardhat-ethers": "^3.0.8", + "@nomicfoundation/hardhat-foundry": "^1.1.1", + "@nomicfoundation/hardhat-network-helpers": "^1.0.0", + "@nomicfoundation/hardhat-toolbox": "5.0.0", + "@openzeppelin/contracts": "^5.3.0", + "@openzeppelin/contracts-upgradeable": "^5.3.0", + "@openzeppelin/foundry-upgrades": "0.4.0", + "@types/chai": "^4.3.20", + "@types/mocha": "^10.0.10", + "@types/node": "^20.17.50", + "chai": "^4.3.7", + "dotenv": "^16.5.0", + "eslint": "catalog:", + "eslint-plugin-no-only-tests": "catalog:", + "ethers": "^6.13.4", + "forge-std": "https://github.com/foundry-rs/forge-std/tarball/v1.9.7", + "glob": "^11.0.2", + "hardhat": "catalog:", + "hardhat-gas-reporter": "catalog:", + "prettier": "catalog:", + "solidity-coverage": "^0.8.0", + "ts-node": "^10.9.2", + "typescript": "catalog:" + }, + "scripts": { + "build": "tsc --build && pnpm --filter @graphprotocol/issuance compile && pnpm generate:interfaces", + "generate:interfaces": "node scripts/generateInterfaceIds.js --silent", + "clean": "rm -rf build", + "test": "pnpm test:self", + "test:self": "pnpm test:hardhat", + "test:hardhat": "pnpm --filter @graphprotocol/contracts --filter @graphprotocol/issuance --filter @graphprotocol/sdk build && hardhat test tests/*.test.ts tests/**/*.test.ts", + "test:coverage": "scripts/coverage", + "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'" + } +} diff --git a/packages/issuance/test/prettier.config.cjs b/packages/issuance/test/prettier.config.cjs new file mode 100644 index 000000000..8eb0a0bee --- /dev/null +++ b/packages/issuance/test/prettier.config.cjs @@ -0,0 +1,5 @@ +const baseConfig = require('../prettier.config.cjs') + +module.exports = { + ...baseConfig, +} diff --git a/packages/issuance/test/scripts/coverage b/packages/issuance/test/scripts/coverage new file mode 100755 index 000000000..1801adad6 --- /dev/null +++ b/packages/issuance/test/scripts/coverage @@ -0,0 +1,10 @@ +#!/bin/bash + +set -eo pipefail + +# Build dependencies first +pnpm --filter @graphprotocol/contracts --filter @graphprotocol/issuance --filter @graphprotocol/sdk build + +# Run coverage from the parent issuance directory where contracts are local +cd .. +npx hardhat coverage --config hardhat.coverage.config.ts --testfiles "test/tests/**/*.test.ts" diff --git a/packages/issuance/test/scripts/generateInterfaceIds.js b/packages/issuance/test/scripts/generateInterfaceIds.js new file mode 100644 index 000000000..42b4af3ec --- /dev/null +++ b/packages/issuance/test/scripts/generateInterfaceIds.js @@ -0,0 +1,144 @@ +#!/usr/bin/env node + +/** + * Generate interface ID constants by deploying and calling InterfaceIdExtractor contract + */ + +const fs = require('fs') +const path = require('path') +const { spawn } = require('child_process') + +const OUTPUT_FILE = path.join(__dirname, '../tests/helpers/interfaceIds.js') +const SILENT = process.argv.includes('--silent') + +function log(...args) { + if (!SILENT) { + console.log(...args) + } +} + +async function runHardhatTask() { + return new Promise((resolve, reject) => { + const hardhatScript = ` +const hre = require('hardhat') + +async function main() { + const InterfaceIdExtractor = await hre.ethers.getContractFactory('InterfaceIdExtractor') + const extractor = await InterfaceIdExtractor.deploy() + await extractor.waitForDeployment() + + const results = { + IServiceQualityOracle: await extractor.getIServiceQualityOracleId(), + } + + console.log(JSON.stringify(results)) +} + +main().catch((error) => { + console.error(error) + process.exit(1) +}) +` + + // Write temporary script + const tempScript = path.join(__dirname, 'temp-extract.js') + fs.writeFileSync(tempScript, hardhatScript) + + // Run the script with hardhat + const child = spawn('npx', ['hardhat', 'run', tempScript, '--network', 'hardhat'], { + cwd: path.join(__dirname, '../..'), + stdio: 'pipe', + }) + + let output = '' + let errorOutput = '' + + child.stdout.on('data', (data) => { + output += data.toString() + }) + + child.stderr.on('data', (data) => { + errorOutput += data.toString() + }) + + child.on('close', (code) => { + // Clean up temp script + try { + fs.unlinkSync(tempScript) + } catch { + // Ignore cleanup errors + } + + if (code === 0) { + // Extract JSON from output + const lines = output.split('\n') + for (const line of lines) { + try { + const result = JSON.parse(line.trim()) + if (result && typeof result === 'object') { + resolve(result) + return + } + } catch { + // Not JSON, continue + } + } + reject(new Error('Could not parse interface IDs from output')) + } else { + reject(new Error(`Hardhat script failed with code ${code}: ${errorOutput}`)) + } + }) + }) +} + +async function extractInterfaceIds() { + const extractorPath = path.join( + __dirname, + '../../artifacts/contracts/test/InterfaceIdExtractor.sol/InterfaceIdExtractor.json', + ) + + if (!fs.existsSync(extractorPath)) { + console.error('❌ InterfaceIdExtractor artifact not found') + console.error('Run: pnpm compile to build the extractor contract') + throw new Error('InterfaceIdExtractor not compiled') + } + + log('Deploying InterfaceIdExtractor contract to extract interface IDs...') + + try { + const results = await runHardhatTask() + + // Convert from ethers BigNumber format to hex strings + const processed = {} + for (const [name, value] of Object.entries(results)) { + processed[name] = typeof value === 'string' ? value : `0x${value.toString(16).padStart(8, '0')}` + log(`✅ Extracted ${name}: ${processed[name]}`) + } + + return processed + } catch (error) { + console.error('Error extracting interface IDs:', error.message) + throw error + } +} + +async function main() { + log('Extracting interface IDs from Solidity compilation...') + + const results = await extractInterfaceIds() + + const content = `// Auto-generated interface IDs from Solidity compilation +module.exports = { +${Object.entries(results) + .map(([name, id]) => ` ${name}: '${id}',`) + .join('\n')} +} +` + + fs.writeFileSync(OUTPUT_FILE, content) + log(`✅ Generated ${OUTPUT_FILE}`) +} + +if (require.main === module) { + main().catch(console.error) +} diff --git a/packages/issuance/test/src/index.ts b/packages/issuance/test/src/index.ts new file mode 100644 index 000000000..614cfd50d --- /dev/null +++ b/packages/issuance/test/src/index.ts @@ -0,0 +1,5 @@ +// Test utilities for @graphprotocol/issuance +// This package contains test files, test helpers, and testing utilities + +// This package provides test utilities for issuance contracts +export const PACKAGE_NAME = '@graphprotocol/issuance-test' diff --git a/packages/issuance/test/tests/ServiceQualityOracle.test.ts b/packages/issuance/test/tests/ServiceQualityOracle.test.ts new file mode 100644 index 000000000..8ea096b5d --- /dev/null +++ b/packages/issuance/test/tests/ServiceQualityOracle.test.ts @@ -0,0 +1,630 @@ +const { time } = require('@nomicfoundation/hardhat-network-helpers') +const { expect } = require('chai') +const { ethers, upgrades } = require('hardhat') + +const { getTestAccounts, deployTestGraphToken, deployServiceQualityOracle } = require('./helpers/fixtures') +const { SHARED_CONSTANTS } = require('./helpers/sharedFixtures') + +// Role constants +const GOVERNOR_ROLE = SHARED_CONSTANTS.GOVERNOR_ROLE +const ORACLE_ROLE = SHARED_CONSTANTS.ORACLE_ROLE +const OPERATOR_ROLE = SHARED_CONSTANTS.OPERATOR_ROLE + +describe('ServiceQualityOracle', () => { + // Common variables + let accounts + let sharedContracts + + before(async () => { + accounts = await getTestAccounts() + + // Deploy shared contracts once + const graphToken = await deployTestGraphToken() + const graphTokenAddress = await graphToken.getAddress() + const serviceQualityOracle = await deployServiceQualityOracle(graphTokenAddress, accounts.governor) + const serviceQualityOracleAddress = await serviceQualityOracle.getAddress() + + sharedContracts = { + graphToken, + serviceQualityOracle, + addresses: { + graphToken: graphTokenAddress, + serviceQualityOracle: serviceQualityOracleAddress, + }, + } + }) + + // Fast state reset function + async function resetOracleState() { + if (!sharedContracts) return + + const { serviceQualityOracle } = sharedContracts + + // Remove oracle roles from all accounts + try { + for (const account of [accounts.operator, accounts.user, accounts.nonGovernor]) { + if (await serviceQualityOracle.hasRole(ORACLE_ROLE, account.address)) { + await serviceQualityOracle.connect(accounts.governor).revokeRole(ORACLE_ROLE, account.address) + } + if (await serviceQualityOracle.hasRole(OPERATOR_ROLE, account.address)) { + await serviceQualityOracle.connect(accounts.governor).revokeRole(OPERATOR_ROLE, account.address) + } + } + + // Remove operator role from governor if present + if (await serviceQualityOracle.hasRole(OPERATOR_ROLE, accounts.governor.address)) { + await serviceQualityOracle.connect(accounts.governor).revokeRole(OPERATOR_ROLE, accounts.governor.address) + } + } catch { + // Ignore role management errors during reset + } + + // Reset to default values + try { + // Reset allowed period to default (14 days) + const defaultAllowedPeriod = 14 * 24 * 60 * 60 + const currentAllowedPeriod = await serviceQualityOracle.getAllowedPeriod() + if (currentAllowedPeriod !== BigInt(defaultAllowedPeriod)) { + await serviceQualityOracle.connect(accounts.governor).grantRole(OPERATOR_ROLE, accounts.governor.address) + await serviceQualityOracle.connect(accounts.governor).setAllowedPeriod(defaultAllowedPeriod) + await serviceQualityOracle.connect(accounts.governor).revokeRole(OPERATOR_ROLE, accounts.governor.address) + } + + // Reset quality checking to inactive + if (await serviceQualityOracle.isQualityCheckingActive()) { + await serviceQualityOracle.connect(accounts.governor).grantRole(OPERATOR_ROLE, accounts.governor.address) + await serviceQualityOracle.connect(accounts.governor).setQualityChecking(false) + await serviceQualityOracle.connect(accounts.governor).revokeRole(OPERATOR_ROLE, accounts.governor.address) + } + + // Reset oracle update timeout to default (7 days) + const defaultTimeout = 7 * 24 * 60 * 60 + const currentTimeout = await serviceQualityOracle.getOracleUpdateTimeout() + if (currentTimeout !== BigInt(defaultTimeout)) { + await serviceQualityOracle.connect(accounts.governor).grantRole(OPERATOR_ROLE, accounts.governor.address) + await serviceQualityOracle.connect(accounts.governor).setOracleUpdateTimeout(defaultTimeout) + await serviceQualityOracle.connect(accounts.governor).revokeRole(OPERATOR_ROLE, accounts.governor.address) + } + } catch { + // Ignore reset errors + } + } + + beforeEach(async () => { + if (!accounts) { + accounts = await getTestAccounts() + } + await resetOracleState() + }) + + describe('Construction', () => { + it('should revert when constructed with zero GraphToken address', async () => { + const ServiceQualityOracleFactory = await ethers.getContractFactory('ServiceQualityOracle') + await expect(ServiceQualityOracleFactory.deploy(ethers.ZeroAddress)).to.be.revertedWithCustomError( + ServiceQualityOracleFactory, + 'GraphTokenCannotBeZeroAddress', + ) + }) + + it('should revert when initialized with zero governor address', async () => { + const graphToken = await deployTestGraphToken() + const graphTokenAddress = await graphToken.getAddress() + + // Try to deploy proxy with zero governor address - this should hit the BaseUpgradeable check + const ServiceQualityOracleFactory = await ethers.getContractFactory('ServiceQualityOracle') + await expect( + upgrades.deployProxy(ServiceQualityOracleFactory, [ethers.ZeroAddress], { + constructorArgs: [graphTokenAddress], + initializer: 'initialize', + }), + ).to.be.revertedWithCustomError(ServiceQualityOracleFactory, 'GovernorCannotBeZeroAddress') + }) + }) + + describe('Initialization', () => { + it('should set the governor role correctly', async () => { + const { serviceQualityOracle } = sharedContracts + expect(await serviceQualityOracle.hasRole(GOVERNOR_ROLE, accounts.governor.address)).to.be.true + }) + + it('should not set oracle role to anyone initially', async () => { + const { serviceQualityOracle } = sharedContracts + expect(await serviceQualityOracle.hasRole(ORACLE_ROLE, accounts.operator.address)).to.be.false + }) + + it('should set default allowed period to 14 days', async () => { + const { serviceQualityOracle } = sharedContracts + expect(await serviceQualityOracle.getAllowedPeriod()).to.equal(14 * 24 * 60 * 60) // 14 days in seconds + }) + + it('should set quality checking to inactive by default', async () => { + const { serviceQualityOracle } = sharedContracts + expect(await serviceQualityOracle.isQualityCheckingActive()).to.be.false + }) + + it('should set default oracle update timeout to 7 days', async () => { + const { serviceQualityOracle } = sharedContracts + expect(await serviceQualityOracle.getOracleUpdateTimeout()).to.equal(7 * 24 * 60 * 60) // 7 days in seconds + }) + + it('should initialize lastOracleUpdateTime to 0', async () => { + const { serviceQualityOracle } = sharedContracts + expect(await serviceQualityOracle.getLastOracleUpdateTime()).to.equal(0) + }) + + it('should revert when initialize is called more than once', async () => { + const { serviceQualityOracle } = sharedContracts + + // Try to call initialize again + await expect(serviceQualityOracle.initialize(accounts.governor.address)).to.be.revertedWithCustomError( + serviceQualityOracle, + 'InvalidInitialization', + ) + }) + }) + + describe('Oracle Management', () => { + it('should allow operator to grant oracle role', async () => { + const { serviceQualityOracle } = sharedContracts + + // Grant operator role to the operator account + await serviceQualityOracle.connect(accounts.governor).grantRole(OPERATOR_ROLE, accounts.operator.address) + + // Operator grants oracle role + await serviceQualityOracle.connect(accounts.operator).grantRole(ORACLE_ROLE, accounts.user.address) + expect(await serviceQualityOracle.hasRole(ORACLE_ROLE, accounts.user.address)).to.be.true + }) + + it('should allow operator to revoke oracle role', async () => { + const { serviceQualityOracle } = sharedContracts + + // Grant operator role to the operator account + await serviceQualityOracle.connect(accounts.governor).grantRole(OPERATOR_ROLE, accounts.operator.address) + + // Grant oracle role first + await serviceQualityOracle.connect(accounts.operator).grantRole(ORACLE_ROLE, accounts.user.address) + expect(await serviceQualityOracle.hasRole(ORACLE_ROLE, accounts.user.address)).to.be.true + + // Revoke role + await serviceQualityOracle.connect(accounts.operator).revokeRole(ORACLE_ROLE, accounts.user.address) + expect(await serviceQualityOracle.hasRole(ORACLE_ROLE, accounts.user.address)).to.be.false + }) + + // Access control tests moved to consolidated/AccessControl.test.ts + }) + + describe('Operator Functions', () => { + beforeEach(async () => { + const { serviceQualityOracle } = sharedContracts + + // Grant operator role to the operator account + await serviceQualityOracle.connect(accounts.governor).grantRole(OPERATOR_ROLE, accounts.operator.address) + }) + + it('should allow operator to set allowed period', async () => { + const { serviceQualityOracle } = sharedContracts + const newAllowedPeriod = 14 * 24 * 60 * 60 // 14 days + + // Set allowed period + await serviceQualityOracle.connect(accounts.operator).setAllowedPeriod(newAllowedPeriod) + + // Check if allowed period was updated + expect(await serviceQualityOracle.getAllowedPeriod()).to.equal(newAllowedPeriod) + }) + + it('should handle idempotent operations correctly', async () => { + const { serviceQualityOracle } = sharedContracts + + // Test setting same allowed period + const currentAllowedPeriod = await serviceQualityOracle.getAllowedPeriod() + let result = await serviceQualityOracle + .connect(accounts.operator) + .setAllowedPeriod.staticCall(currentAllowedPeriod) + expect(result).to.be.true + + // Verify no event emitted for same value + let tx = await serviceQualityOracle.connect(accounts.operator).setAllowedPeriod(currentAllowedPeriod) + let receipt = await tx.wait() + expect(receipt.logs.length).to.equal(0) + + // Test setting new oracle update timeout + const newTimeout = 60 * 24 * 60 * 60 // 60 days + await serviceQualityOracle.connect(accounts.operator).setOracleUpdateTimeout(newTimeout) + expect(await serviceQualityOracle.getOracleUpdateTimeout()).to.equal(newTimeout) + + // Test setting same oracle update timeout + result = await serviceQualityOracle.connect(accounts.operator).setOracleUpdateTimeout.staticCall(newTimeout) + expect(result).to.be.true + + // Verify no event emitted for same value + tx = await serviceQualityOracle.connect(accounts.operator).setOracleUpdateTimeout(newTimeout) + receipt = await tx.wait() + expect(receipt.logs.length).to.equal(0) + }) + + it('should allow operator to disable quality checking', async () => { + const { serviceQualityOracle } = sharedContracts + // Disable quality checking + await serviceQualityOracle.connect(accounts.operator).setQualityChecking(false) + + // Check if quality checking is disabled + expect(await serviceQualityOracle.isQualityCheckingActive()).to.be.false + }) + + it('should allow operator to enable quality checking', async () => { + const { serviceQualityOracle } = sharedContracts + // Disable quality checking first + await serviceQualityOracle.connect(accounts.operator).setQualityChecking(false) + expect(await serviceQualityOracle.isQualityCheckingActive()).to.be.false + + // Enable quality checking + await serviceQualityOracle.connect(accounts.operator).setQualityChecking(true) + + // Check if quality checking is enabled + expect(await serviceQualityOracle.isQualityCheckingActive()).to.be.true + }) + + it('should handle setQualityChecking return values and events correctly', async () => { + const { serviceQualityOracle } = sharedContracts + + // Test 1: Return true when enabling quality checking that is already enabled + await serviceQualityOracle.connect(accounts.operator).setQualityChecking(true) + expect(await serviceQualityOracle.isQualityCheckingActive()).to.be.true + + const enableResult = await serviceQualityOracle.connect(accounts.operator).setQualityChecking.staticCall(true) + expect(enableResult).to.be.true + + // Test 2: No event emitted when setting to same state (enabled) + const enableTx = await serviceQualityOracle.connect(accounts.operator).setQualityChecking(true) + const enableReceipt = await enableTx.wait() + expect(enableReceipt.logs.length).to.equal(0) + + // Test 3: Return true when disabling quality checking that is already disabled + await serviceQualityOracle.connect(accounts.operator).setQualityChecking(false) + expect(await serviceQualityOracle.isQualityCheckingActive()).to.be.false + + const disableResult = await serviceQualityOracle.connect(accounts.operator).setQualityChecking.staticCall(false) + expect(disableResult).to.be.true + + // Test 4: No event emitted when setting to same state (disabled) + const disableTx = await serviceQualityOracle.connect(accounts.operator).setQualityChecking(false) + const disableReceipt = await disableTx.wait() + expect(disableReceipt.logs.length).to.equal(0) + + // Test 5: Events are emitted when state actually changes + await expect(serviceQualityOracle.connect(accounts.operator).setQualityChecking(true)) + .to.emit(serviceQualityOracle, 'QualityCheckingUpdated') + .withArgs(true) + + await expect(serviceQualityOracle.connect(accounts.operator).setQualityChecking(false)) + .to.emit(serviceQualityOracle, 'QualityCheckingUpdated') + .withArgs(false) + }) + + // Access control tests moved to consolidated/AccessControl.test.ts + // Event and return value tests consolidated into 'should handle setQualityChecking return values and events correctly' + }) + + describe('Indexer Management', () => { + beforeEach(async () => { + const { serviceQualityOracle } = sharedContracts + + // Grant operator role to the operator account + await serviceQualityOracle.connect(accounts.governor).grantRole(OPERATOR_ROLE, accounts.operator.address) + + // Grant oracle role + await serviceQualityOracle.connect(accounts.operator).grantRole(ORACLE_ROLE, accounts.operator.address) + }) + + it('should allow oracle to allow a single indexer', async () => { + const { serviceQualityOracle } = sharedContracts + // Allow indexer using allowIndexers with a single-element array + await serviceQualityOracle.connect(accounts.operator).allowIndexers([accounts.indexer1.address], '0x') + + // Check if indexer is allowed + expect(await serviceQualityOracle.isAllowed(accounts.indexer1.address)).to.be.true + + // Check if allowed timestamp was updated + const allowedTime = await serviceQualityOracle.getLastAllowedTime(accounts.indexer1.address) + expect(allowedTime).to.be.gt(0) + }) + + it('should allow oracle to allow multiple indexers', async () => { + const { serviceQualityOracle } = sharedContracts + // Allow multiple indexers + const indexers = [accounts.indexer1.address, accounts.indexer2.address] + await serviceQualityOracle.connect(accounts.operator).allowIndexers(indexers, '0x') + + // Check if indexers are allowed + expect(await serviceQualityOracle.isAllowed(accounts.indexer1.address)).to.be.true + expect(await serviceQualityOracle.isAllowed(accounts.indexer2.address)).to.be.true + + // Check if allowed timestamps were updated + const allowedTime1 = await serviceQualityOracle.getLastAllowedTime(accounts.indexer1.address) + const allowedTime2 = await serviceQualityOracle.getLastAllowedTime(accounts.indexer2.address) + expect(allowedTime1).to.be.gt(0) + expect(allowedTime2).to.be.gt(0) + }) + + it('should not update timestamp for indexer already allowed in the same block', async () => { + const { serviceQualityOracle } = sharedContracts + // Allow indexer first time + await serviceQualityOracle.connect(accounts.operator).allowIndexers([accounts.indexer1.address], '0x') + + // Get the timestamp + const initialAllowedTime = await serviceQualityOracle.getLastAllowedTime(accounts.indexer1.address) + + // Call allowIndexers again with the same indexer + const result = await serviceQualityOracle + .connect(accounts.operator) + .allowIndexers.staticCall([accounts.indexer1.address], '0x') + + // The function should return 0 since the indexer was already allowed in this block + expect(result).to.equal(0) + + // Verify the timestamp hasn't changed + const finalAllowedTime = await serviceQualityOracle.getLastAllowedTime(accounts.indexer1.address) + expect(finalAllowedTime).to.equal(initialAllowedTime) + + // Mine a new block + await ethers.provider.send('evm_mine', []) + + // Now try again in a new block - it should return 1 + const newBlockResult = await serviceQualityOracle + .connect(accounts.operator) + .allowIndexers.staticCall([accounts.indexer1.address], '0x') + + // The function should return 1 since we're in a new block + expect(newBlockResult).to.equal(1) + }) + + it('should revert when non-oracle tries to allow a single indexer', async () => { + const { serviceQualityOracle } = sharedContracts + await expect( + serviceQualityOracle.connect(accounts.nonGovernor).allowIndexers([accounts.indexer1.address], '0x'), + ).to.be.revertedWithCustomError(serviceQualityOracle, 'AccessControlUnauthorizedAccount') + }) + + it('should revert when non-oracle tries to allow multiple indexers', async () => { + const { serviceQualityOracle } = sharedContracts + const indexers = [accounts.indexer1.address, accounts.indexer2.address] + await expect( + serviceQualityOracle.connect(accounts.nonGovernor).allowIndexers(indexers, '0x'), + ).to.be.revertedWithCustomError(serviceQualityOracle, 'AccessControlUnauthorizedAccount') + }) + + it('should return correct count for various allowIndexers scenarios', async () => { + const { serviceQualityOracle } = sharedContracts + + // Test 1: Single indexer should return 1 + const singleResult = await serviceQualityOracle + .connect(accounts.operator) + .allowIndexers.staticCall([accounts.indexer1.address], '0x') + expect(singleResult).to.equal(1) + + // Test 2: Multiple indexers should return correct count + const multipleIndexers = [accounts.indexer1.address, accounts.indexer2.address] + const multipleResult = await serviceQualityOracle + .connect(accounts.operator) + .allowIndexers.staticCall(multipleIndexers, '0x') + expect(multipleResult).to.equal(2) + + // Test 3: Empty array should return 0 + const emptyResult = await serviceQualityOracle.connect(accounts.operator).allowIndexers.staticCall([], '0x') + expect(emptyResult).to.equal(0) + + // Test 4: Array with zero addresses should only count non-zero addresses + const withZeroAddresses = [accounts.indexer1.address, ethers.ZeroAddress, accounts.indexer2.address] + const zeroResult = await serviceQualityOracle + .connect(accounts.operator) + .allowIndexers.staticCall(withZeroAddresses, '0x') + expect(zeroResult).to.equal(2) + + // Test 5: Array with duplicates should only count unique indexers + const withDuplicates = [accounts.indexer1.address, accounts.indexer1.address, accounts.indexer2.address] + const duplicateResult = await serviceQualityOracle + .connect(accounts.operator) + .allowIndexers.staticCall(withDuplicates, '0x') + expect(duplicateResult).to.equal(2) + }) + }) + + describe('View Functions', () => { + // Use shared contracts instead of deploying fresh ones for each test + + it('should return 0 when getting last allowed time for non-allowed indexer', async () => { + // Use a fresh deployment to avoid contamination from previous tests + const graphToken = await deployTestGraphToken() + const graphTokenAddress = await graphToken.getAddress() + const freshServiceQualityOracle = await deployServiceQualityOracle(graphTokenAddress, accounts.governor) + + // This should return 0 for a fresh contract + const lastAllowedTime = await freshServiceQualityOracle.getLastAllowedTime(accounts.indexer1.address) + expect(lastAllowedTime).to.equal(0) + }) + + it('should return correct timestamp for allowed indexer', async function () { + const { serviceQualityOracle } = sharedContracts + + // Grant operator role first (governor can grant operator role) + await serviceQualityOracle.connect(accounts.governor).grantRole(OPERATOR_ROLE, accounts.operator.address) + // Then operator can grant oracle role (operator is admin of oracle role) + await serviceQualityOracle.connect(accounts.operator).grantRole(ORACLE_ROLE, accounts.operator.address) + + // Allow indexer + await serviceQualityOracle.connect(accounts.operator).allowIndexers([accounts.indexer1.address], '0x') + + // Get the last allowed time + const lastAllowedTime = await serviceQualityOracle.getLastAllowedTime(accounts.indexer1.address) + + // Get the current block timestamp + const block = await ethers.provider.getBlock('latest') + const blockTimestamp = block ? block.timestamp : 0 + + // The last allowed time should be close to the current block timestamp + expect(lastAllowedTime).to.be.closeTo(blockTimestamp, 5) // Allow 5 seconds of difference + }) + + it('should correctly report if an indexer is allowed', async function () { + // Use a fresh deployment to avoid shared state contamination + const graphToken = await deployTestGraphToken() + const graphTokenAddress = await graphToken.getAddress() + const freshServiceQualityOracle = await deployServiceQualityOracle(graphTokenAddress, accounts.governor) + + // Grant necessary roles (follow role hierarchy) + await freshServiceQualityOracle.connect(accounts.governor).grantRole(OPERATOR_ROLE, accounts.operator.address) + await freshServiceQualityOracle.connect(accounts.operator).grantRole(ORACLE_ROLE, accounts.operator.address) + + // Enable quality checking first (since it's disabled by default) + await freshServiceQualityOracle.connect(accounts.operator).setQualityChecking(true) + + // First, set a non-zero lastOracleUpdateTime to prevent the timeout condition from triggering + await freshServiceQualityOracle.connect(accounts.operator).allowIndexers([accounts.nonGovernor.address], '0x') + + // Now check if our test indexer is allowed (it shouldn't be) + expect(await freshServiceQualityOracle.isAllowed(accounts.indexer1.address)).to.be.false + + // Allow indexer + await freshServiceQualityOracle.connect(accounts.operator).allowIndexers([accounts.indexer1.address], '0x') + expect(await freshServiceQualityOracle.isAllowed(accounts.indexer1.address)).to.be.true + }) + + it('should correctly report if an oracle is authorized', async function () { + const { serviceQualityOracle } = sharedContracts + + // Grant operator role to perform role management + await serviceQualityOracle.connect(accounts.governor).grantRole(OPERATOR_ROLE, accounts.operator.address) + + // Initially, user should not be an oracle + expect(await serviceQualityOracle.isAuthorizedOracle(accounts.user.address)).to.be.false + + // Grant oracle role + await serviceQualityOracle.connect(accounts.operator).grantRole(ORACLE_ROLE, accounts.user.address) + expect(await serviceQualityOracle.isAuthorizedOracle(accounts.user.address)).to.be.true + + // Revoke oracle role + await serviceQualityOracle.connect(accounts.operator).revokeRole(ORACLE_ROLE, accounts.user.address) + expect(await serviceQualityOracle.isAuthorizedOracle(accounts.user.address)).to.be.false + }) + + it('should return true for all indexers when quality checking is disabled', async function () { + // Use a fresh deployment to avoid shared state contamination + const graphToken = await deployTestGraphToken() + const graphTokenAddress = await graphToken.getAddress() + const freshServiceQualityOracle = await deployServiceQualityOracle(graphTokenAddress, accounts.governor) + + // Grant necessary roles (follow role hierarchy) + await freshServiceQualityOracle.connect(accounts.governor).grantRole(OPERATOR_ROLE, accounts.operator.address) + await freshServiceQualityOracle.connect(accounts.operator).grantRole(ORACLE_ROLE, accounts.operator.address) + + // Enable quality checking first (since it's disabled by default) + await freshServiceQualityOracle.connect(accounts.operator).setQualityChecking(true) + + // First, set a non-zero lastOracleUpdateTime to prevent the timeout condition from triggering + await freshServiceQualityOracle.connect(accounts.operator).allowIndexers([accounts.nonGovernor.address], '0x') + + // Set a very long oracle update timeout to prevent that condition from triggering + await freshServiceQualityOracle.connect(accounts.operator).setOracleUpdateTimeout(365 * 24 * 60 * 60) // 1 year + + // Now check if our test indexer is allowed (it shouldn't be) + expect(await freshServiceQualityOracle.isAllowed(accounts.indexer1.address)).to.be.false + + // Disable quality checking + await freshServiceQualityOracle.connect(accounts.operator).setQualityChecking(false) + + // Now indexer should be allowed even without being explicitly allowed + expect(await freshServiceQualityOracle.isAllowed(accounts.indexer1.address)).to.be.true + }) + + it('should return true for all indexers when oracle update timeout is exceeded', async function () { + // Use a fresh deployment to avoid shared state contamination + const graphToken = await deployTestGraphToken() + const graphTokenAddress = await graphToken.getAddress() + const freshServiceQualityOracle = await deployServiceQualityOracle(graphTokenAddress, accounts.governor) + + // Grant necessary roles (follow role hierarchy) + await freshServiceQualityOracle.connect(accounts.governor).grantRole(OPERATOR_ROLE, accounts.operator.address) + await freshServiceQualityOracle.connect(accounts.operator).grantRole(ORACLE_ROLE, accounts.operator.address) + + // Enable quality checking first (since it's disabled by default) + await freshServiceQualityOracle.connect(accounts.operator).setQualityChecking(true) + + // First, set a non-zero lastOracleUpdateTime to prevent the initial timeout condition from triggering + await freshServiceQualityOracle.connect(accounts.operator).allowIndexers([accounts.nonGovernor.address], '0x') + + // Set a very long oracle update timeout initially + await freshServiceQualityOracle.connect(accounts.operator).setOracleUpdateTimeout(365 * 24 * 60 * 60) // 1 year + + // Now check if our test indexer is allowed (it shouldn't be) + expect(await freshServiceQualityOracle.isAllowed(accounts.indexer1.address)).to.be.false + + // Set a short oracle update timeout + await freshServiceQualityOracle.connect(accounts.operator).setOracleUpdateTimeout(60) // 1 minute + + // Advance time beyond the timeout + await time.increase(120) // 2 minutes + + // Now indexer should be allowed even without being explicitly allowed + expect(await freshServiceQualityOracle.isAllowed(accounts.indexer1.address)).to.be.true + }) + + it('should return false for indexer after allowed period expires', async function () { + const { serviceQualityOracle } = sharedContracts + + // Grant necessary roles (follow role hierarchy) + await serviceQualityOracle.connect(accounts.governor).grantRole(OPERATOR_ROLE, accounts.operator.address) + await serviceQualityOracle.connect(accounts.operator).grantRole(ORACLE_ROLE, accounts.operator.address) + + // Enable quality checking first (since it's disabled by default) + await serviceQualityOracle.connect(accounts.operator).setQualityChecking(true) + + // Set a very long oracle update timeout to prevent that condition from triggering + await serviceQualityOracle.connect(accounts.operator).setOracleUpdateTimeout(365 * 24 * 60 * 60) // 1 year + + // Allow indexer + await serviceQualityOracle.connect(accounts.operator).allowIndexers([accounts.indexer1.address], '0x') + expect(await serviceQualityOracle.isAllowed(accounts.indexer1.address)).to.be.true + + // Set a short allowed period + await serviceQualityOracle.connect(accounts.operator).setAllowedPeriod(60) // 1 minute + + // Advance time beyond allowed period + await time.increase(120) // 2 minutes + + // Now indexer should not be allowed + expect(await serviceQualityOracle.isAllowed(accounts.indexer1.address)).to.be.false + }) + + it('should return true for indexer after re-allowing', async function () { + const { serviceQualityOracle } = sharedContracts + + // Grant necessary roles + await serviceQualityOracle.connect(accounts.governor).grantRole(OPERATOR_ROLE, accounts.operator.address) + await serviceQualityOracle.connect(accounts.operator).grantRole(ORACLE_ROLE, accounts.operator.address) + + // Enable quality checking first (since it's disabled by default) + await serviceQualityOracle.connect(accounts.operator).setQualityChecking(true) + + // Set a very long oracle update timeout to prevent that condition from triggering + await serviceQualityOracle.connect(accounts.operator).setOracleUpdateTimeout(365 * 24 * 60 * 60) // 1 year + + // Allow indexer + await serviceQualityOracle.connect(accounts.operator).allowIndexers([accounts.indexer1.address], '0x') + + // Set a short allowed period + await serviceQualityOracle.connect(accounts.operator).setAllowedPeriod(60) // 1 minute + + // Advance time beyond allowed period + await time.increase(120) // 2 minutes + + // Indexer should not be allowed + expect(await serviceQualityOracle.isAllowed(accounts.indexer1.address)).to.be.false + + // Re-allow indexer + await serviceQualityOracle.connect(accounts.operator).allowIndexers([accounts.indexer1.address], '0x') + + // Now indexer should be allowed again + expect(await serviceQualityOracle.isAllowed(accounts.indexer1.address)).to.be.true + }) + }) +}) diff --git a/packages/issuance/test/tests/consolidated/AccessControl.test.ts b/packages/issuance/test/tests/consolidated/AccessControl.test.ts new file mode 100644 index 000000000..385e9715e --- /dev/null +++ b/packages/issuance/test/tests/consolidated/AccessControl.test.ts @@ -0,0 +1,151 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/** + * Consolidated Access Control Tests + * Tests access control patterns across all contracts to reduce duplication + */ + +const { expect } = require('chai') +const { deploySharedContracts, resetContractState, SHARED_CONSTANTS } = require('../helpers/sharedFixtures') + +describe('Consolidated Access Control Tests', () => { + let accounts: any + let contracts: any + + before(async () => { + const sharedSetup = await deploySharedContracts() + accounts = sharedSetup.accounts + contracts = sharedSetup.contracts + }) + + beforeEach(async () => { + await resetContractState(contracts, accounts) + }) + + describe('ServiceQualityOracle Access Control', () => { + describe('Role Management Methods', () => { + it('should enforce access control on role management methods', async () => { + // First grant governor the OPERATOR_ROLE so they can manage oracle roles + await contracts.serviceQualityOracle + .connect(accounts.governor) + .grantRole(SHARED_CONSTANTS.OPERATOR_ROLE, accounts.governor.address) + + const methods = [ + { + method: 'grantRole', + args: [SHARED_CONSTANTS.ORACLE_ROLE, accounts.operator.address], + description: 'grantRole for ORACLE_ROLE', + }, + { + method: 'revokeRole', + args: [SHARED_CONSTANTS.ORACLE_ROLE, accounts.operator.address], + description: 'revokeRole for ORACLE_ROLE', + }, + ] + + for (const { method, args, description } of methods) { + // Test unauthorized access + await expect( + contracts.serviceQualityOracle.connect(accounts.nonGovernor)[method](...args), + `${description} should revert for unauthorized account`, + ).to.be.revertedWithCustomError(contracts.serviceQualityOracle, 'AccessControlUnauthorizedAccount') + + // Test authorized access + await expect( + contracts.serviceQualityOracle.connect(accounts.governor)[method](...args), + `${description} should succeed for authorized account`, + ).to.not.be.reverted + } + }) + }) + + it('should require ORACLE_ROLE for allowIndexers', async () => { + // Setup: Grant governor OPERATOR_ROLE first, then grant oracle role + await contracts.serviceQualityOracle + .connect(accounts.governor) + .grantRole(SHARED_CONSTANTS.OPERATOR_ROLE, accounts.governor.address) + await contracts.serviceQualityOracle + .connect(accounts.governor) + .grantRole(SHARED_CONSTANTS.ORACLE_ROLE, accounts.operator.address) + + // Non-oracle should be rejected + await expect( + contracts.serviceQualityOracle + .connect(accounts.nonGovernor) + .allowIndexers([accounts.nonGovernor.address], '0x'), + ).to.be.revertedWithCustomError(contracts.serviceQualityOracle, 'AccessControlUnauthorizedAccount') + + // Oracle should be allowed + const hasRole = await contracts.serviceQualityOracle.hasRole( + SHARED_CONSTANTS.ORACLE_ROLE, + accounts.operator.address, + ) + expect(hasRole).to.be.true + }) + + it('should require OPERATOR_ROLE for pause operations', async () => { + // Setup: Grant pause role to governor + await contracts.serviceQualityOracle + .connect(accounts.governor) + .grantRole(SHARED_CONSTANTS.PAUSE_ROLE, accounts.governor.address) + + // Non-pause-role account should be rejected + await expect(contracts.serviceQualityOracle.connect(accounts.nonGovernor).pause()).to.be.revertedWithCustomError( + contracts.serviceQualityOracle, + 'AccessControlUnauthorizedAccount', + ) + await expect( + contracts.serviceQualityOracle.connect(accounts.nonGovernor).unpause(), + ).to.be.revertedWithCustomError(contracts.serviceQualityOracle, 'AccessControlUnauthorizedAccount') + + // PAUSE_ROLE account should be allowed + await expect(contracts.serviceQualityOracle.connect(accounts.governor).pause()).to.not.be.reverted + }) + + it('should require OPERATOR_ROLE for configuration methods', async () => { + // Test all operator-only configuration methods + const operatorOnlyMethods = [ + { + call: () => contracts.serviceQualityOracle.connect(accounts.nonGovernor).setAllowedPeriod(14 * 24 * 60 * 60), + name: 'setAllowedPeriod', + }, + { + call: () => + contracts.serviceQualityOracle.connect(accounts.nonGovernor).setOracleUpdateTimeout(60 * 24 * 60 * 60), + name: 'setOracleUpdateTimeout', + }, + { + call: () => contracts.serviceQualityOracle.connect(accounts.nonGovernor).setQualityChecking(false), + name: 'setQualityChecking(false)', + }, + { + call: () => contracts.serviceQualityOracle.connect(accounts.nonGovernor).setQualityChecking(true), + name: 'setQualityChecking(true)', + }, + ] + + // Test all methods in sequence + for (const method of operatorOnlyMethods) { + await expect(method.call()).to.be.revertedWithCustomError( + contracts.serviceQualityOracle, + 'AccessControlUnauthorizedAccount', + ) + } + }) + }) + + describe('Role Management Consistency', () => { + it('should have consistent GOVERNOR_ROLE across all contracts', async () => { + const governorRole = SHARED_CONSTANTS.GOVERNOR_ROLE + + // All contracts should recognize the governor + expect(await contracts.serviceQualityOracle.hasRole(governorRole, accounts.governor.address)).to.be.true + }) + + it('should have correct role admin hierarchy', async () => { + const governorRole = SHARED_CONSTANTS.GOVERNOR_ROLE + + // GOVERNOR_ROLE should be admin of itself (allowing governors to manage other governors) + expect(await contracts.serviceQualityOracle.getRoleAdmin(governorRole)).to.equal(governorRole) + }) + }) +}) diff --git a/packages/issuance/test/tests/consolidated/InterfaceCompliance.test.ts b/packages/issuance/test/tests/consolidated/InterfaceCompliance.test.ts new file mode 100644 index 000000000..e64303dbd --- /dev/null +++ b/packages/issuance/test/tests/consolidated/InterfaceCompliance.test.ts @@ -0,0 +1,54 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { expect } from 'chai' +import { ethers } from 'hardhat' + +import { shouldSupportERC165Interface } from '../../utils/testPatterns' +import { deployServiceQualityOracle, deployTestGraphToken, getTestAccounts } from '../helpers/fixtures' + +// Import generated interface IDs +const interfaceIds = require('../helpers/interfaceIds') + +/** + * Consolidated ERC-165 Interface Compliance Tests + * Tests interface support across all contracts to reduce duplication + */ +describe('ERC-165 Interface Compliance', () => { + let accounts: any + let contracts: any + + before(async () => { + accounts = await getTestAccounts() + + // Deploy all contracts for interface testing + const graphToken = await deployTestGraphToken() + const graphTokenAddress = await graphToken.getAddress() + + const serviceQualityOracle = await deployServiceQualityOracle(graphTokenAddress, accounts.governor) + + contracts = { + serviceQualityOracle, + } + }) + + describe( + 'ServiceQualityOracle Interface Compliance', + shouldSupportERC165Interface( + () => contracts.serviceQualityOracle, + interfaceIds.IServiceQualityOracle, + 'IServiceQualityOracle', + ), + ) + + describe('Interface ID Consistency', () => { + it('should have consistent interface IDs with Solidity calculations', async () => { + const InterfaceIdExtractorFactory = await ethers.getContractFactory('InterfaceIdExtractor') + const extractor = await InterfaceIdExtractorFactory.deploy() + + expect(await extractor.getIServiceQualityOracleId()).to.equal(interfaceIds.IServiceQualityOracle) + }) + + it('should have valid interface IDs (not zero)', () => { + expect(interfaceIds.IServiceQualityOracle).to.not.equal('0x00000000') + }) + }) +}) diff --git a/packages/issuance/test/tests/helpers/fixtures.ts b/packages/issuance/test/tests/helpers/fixtures.ts new file mode 100644 index 000000000..c68c3f6b6 --- /dev/null +++ b/packages/issuance/test/tests/helpers/fixtures.ts @@ -0,0 +1,101 @@ +import * as fs from 'fs' +import { ethers, upgrades } from 'hardhat' + +/** + * Standard test accounts + */ +// TestAccounts interface converted to JSDoc for CommonJS +/** + * @typedef {Object} TestAccounts + * @property {SignerWithAddress} governor + * @property {SignerWithAddress} nonGovernor + * @property {SignerWithAddress} operator + * @property {SignerWithAddress} user + * @property {SignerWithAddress} indexer1 + * @property {SignerWithAddress} indexer2 + */ + +/** + * Get standard test accounts + * @returns {Promise} + */ +async function getTestAccounts() { + const [governor, nonGovernor, operator, user, indexer1, indexer2] = await ethers.getSigners() + + return { + governor, + nonGovernor, + operator, + user, + indexer1, + indexer2, + } +} + +/** + * Deploy a test GraphToken for testing + * This uses the real GraphToken contract + * @returns {Promise} + */ +async function deployTestGraphToken() { + // Get the governor account + const [governor] = await ethers.getSigners() + + // Load the GraphToken artifact directly from the contracts package + const graphTokenArtifactPath = require.resolve( + '@graphprotocol/contracts/artifacts/contracts/token/GraphToken.sol/GraphToken.json', + ) + const GraphTokenArtifact = JSON.parse(fs.readFileSync(graphTokenArtifactPath, 'utf8')) + + // Create a contract factory using the artifact + const GraphTokenFactory = new ethers.ContractFactory(GraphTokenArtifact.abi, GraphTokenArtifact.bytecode, governor) + + // Deploy the contract + const graphToken = await GraphTokenFactory.deploy(ethers.parseEther('1000000000')) + await graphToken.waitForDeployment() + + return graphToken +} + +/** + * Deploy the ServiceQualityOracle contract with proxy using OpenZeppelin's upgrades library + * @param {string} graphToken + * @param {HardhatEthersSigner} governor + * @param {number} [validityPeriod=7 * 24 * 60 * 60] The validity period in seconds (default: 7 days) + * @returns {Promise} + */ +async function deployServiceQualityOracle( + graphToken, + governor, + validityPeriod = 7 * 24 * 60 * 60, // 7 days in seconds +) { + // Deploy implementation and proxy using OpenZeppelin's upgrades library + const ServiceQualityOracleFactory = await ethers.getContractFactory('ServiceQualityOracle') + + // Deploy proxy with implementation + const serviceQualityOracleContract = await upgrades.deployProxy(ServiceQualityOracleFactory, [governor.address], { + constructorArgs: [graphToken], + initializer: 'initialize', + }) + + // Get the contract instance + const serviceQualityOracle = serviceQualityOracleContract + + // Set the validity period if it's different from the default + if (validityPeriod !== 7 * 24 * 60 * 60) { + // First grant operator role to governor so they can set the validity period + await serviceQualityOracle.connect(governor).grantOperatorRole(governor.address) + await serviceQualityOracle.connect(governor).setValidityPeriod(validityPeriod) + // Now revoke the operator role from governor to ensure tests start with clean state + await serviceQualityOracle.connect(governor).revokeRole(OPERATOR_ROLE, governor.address) + } + + return serviceQualityOracle +} + +// Export all functions and constants +module.exports = { + getTestAccounts, + deployTestGraphToken, + deployServiceQualityOracle, +} diff --git a/packages/issuance/test/tests/helpers/interfaceIds.js b/packages/issuance/test/tests/helpers/interfaceIds.js new file mode 100644 index 000000000..add1c098b --- /dev/null +++ b/packages/issuance/test/tests/helpers/interfaceIds.js @@ -0,0 +1,4 @@ +// Auto-generated interface IDs from Solidity compilation +module.exports = { + IServiceQualityOracle: '0xbabcc539', +} diff --git a/packages/issuance/test/tests/helpers/sharedFixtures.js b/packages/issuance/test/tests/helpers/sharedFixtures.js new file mode 100644 index 000000000..4bd31ccd5 --- /dev/null +++ b/packages/issuance/test/tests/helpers/sharedFixtures.js @@ -0,0 +1,80 @@ +/** + * Shared fixtures and setup utilities for all test files + * Reduces duplication of deployment and state management logic + */ + +const { ethers } = require('hardhat') +const { getTestAccounts, deployTestGraphToken, deployServiceQualityOracle } = require('./fixtures') +// Shared test constants +const SHARED_CONSTANTS = { + PPM: 1_000_000, + + // Pre-calculated role constants to avoid repeated async calls + GOVERNOR_ROLE: ethers.keccak256(ethers.toUtf8Bytes('GOVERNOR_ROLE')), + OPERATOR_ROLE: ethers.keccak256(ethers.toUtf8Bytes('OPERATOR_ROLE')), + PAUSE_ROLE: ethers.keccak256(ethers.toUtf8Bytes('PAUSE_ROLE')), + ORACLE_ROLE: ethers.keccak256(ethers.toUtf8Bytes('ORACLE_ROLE')), +} + +// Interface IDs +const INTERFACE_IDS = { + IERC165: '0x01ffc9a7', +} + +/** + * Shared contract deployment and setup + */ +async function deploySharedContracts() { + const accounts = await getTestAccounts() + + // Deploy base contracts + const graphToken = await deployTestGraphToken() + const graphTokenAddress = await graphToken.getAddress() + + const serviceQualityOracle = await deployServiceQualityOracle(graphTokenAddress, accounts.governor) + + // Cache addresses + const addresses = { + graphToken: graphTokenAddress, + serviceQualityOracle: await serviceQualityOracle.getAddress(), + } + + // Create helper + return { + accounts, + contracts: { + graphToken, + serviceQualityOracle, + }, + addresses, + } +} + +/** + * Reset contract state to initial conditions + * Optimized to avoid redeployment while ensuring clean state + */ +async function resetContractState(contracts, accounts) { + const { serviceQualityOracle } = contracts + + // Reset ServiceQualityOracle state + try { + if (await serviceQualityOracle.paused()) { + await serviceQualityOracle.connect(accounts.governor).unpause() + } + + // Reset quality checking to default (disabled) + if (await serviceQualityOracle.isQualityCheckingActive()) { + await serviceQualityOracle.connect(accounts.governor).disableQualityChecking() + } + } catch (error) { + console.warn('ServiceQualityOracle state reset failed:', error instanceof Error ? error.message : String(error)) + } +} + +module.exports = { + deploySharedContracts, + resetContractState, + SHARED_CONSTANTS, + INTERFACE_IDS, +} diff --git a/packages/issuance/test/tsconfig.json b/packages/issuance/test/tsconfig.json new file mode 100644 index 000000000..543ff61b9 --- /dev/null +++ b/packages/issuance/test/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "outDir": "./build", + "rootDir": "." + }, + "include": [ + "tests/**/*", + "utils/**/*" + ], + "exclude": [ + "node_modules", + "build", + "scripts/**/*" + ] +} diff --git a/packages/issuance/test/utils/testPatterns.ts b/packages/issuance/test/utils/testPatterns.ts new file mode 100644 index 000000000..52c3a7a56 --- /dev/null +++ b/packages/issuance/test/utils/testPatterns.ts @@ -0,0 +1,35 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/** + * Shared test patterns and utilities to reduce duplication across test files + */ + +const { expect } = require('chai') + +// Test constants - centralized to avoid magic numbers +export const TestConstants = { + // Interface IDs + IERC165_INTERFACE_ID: '0x01ffc9a7', +} as const + +/** + * Shared test pattern for ERC-165 interface compliance + */ +export function shouldSupportERC165Interface(contractGetter: () => T, interfaceId: string, interfaceName: string) { + return function () { + it(`should support ERC-165 interface`, async function () { + const contract = contractGetter() + expect(await (contract as any).supportsInterface(TestConstants.IERC165_INTERFACE_ID)).to.be.true + }) + + it(`should support ${interfaceName} interface`, async function () { + const contract = contractGetter() + expect(await (contract as any).supportsInterface(interfaceId)).to.be.true + }) + + it('should not support random interface', async function () { + const contract = contractGetter() + const randomInterfaceId = '0x12345678' + expect(await (contract as any).supportsInterface(randomInterfaceId)).to.be.false + }) + } +} diff --git a/packages/issuance/tsconfig.json b/packages/issuance/tsconfig.json new file mode 100644 index 000000000..00aa1b8ef --- /dev/null +++ b/packages/issuance/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "es2023", + "lib": ["es2023"], + "module": "Node16", + "moduleResolution": "node16", + "strict": true, + "esModuleInterop": true, + "declaration": true, + "resolveJsonModule": true, + "allowJs": true, + "checkJs": false, + "incremental": true + }, + + "include": ["./scripts", "./test", "./typechain"], + "files": ["./hardhat.config.cjs"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 190c9f403..1d5a15ae9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,12 +9,30 @@ catalogs: '@eslint/js': specifier: ^9.28.0 version: 9.28.0 + '@nomicfoundation/hardhat-ethers': + specifier: ^3.0.8 + version: 3.0.9 + '@nomicfoundation/hardhat-verify': + specifier: ^2.0.10 + version: 2.1.1 + '@typechain/hardhat': + specifier: ^9.0.0 + version: 9.1.0 + dotenv: + specifier: ^16.5.0 + version: 16.5.0 eslint: specifier: ^9.28.0 version: 9.28.0 eslint-config-prettier: specifier: ^10.1.5 version: 10.1.5 + eslint-plugin-no-only-tests: + specifier: ^3.3.0 + version: 3.3.0 + ethers: + specifier: ^6.15.0 + version: 6.15.0 hardhat: specifier: ^2.24.0 version: 2.24.2 @@ -734,6 +752,185 @@ importers: specifier: ^5.8.3 version: 5.8.3 + packages/issuance: + dependencies: + '@noble/hashes': + specifier: ^1.8.0 + version: 1.8.0 + devDependencies: + '@graphprotocol/common': + specifier: workspace:^ + version: link:../common + '@nomicfoundation/hardhat-ethers': + specifier: 'catalog:' + version: 3.0.9(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@nomicfoundation/hardhat-verify': + specifier: 'catalog:' + version: 2.1.1(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@openzeppelin/contracts': + specifier: ^5.3.0 + version: 5.3.0 + '@openzeppelin/contracts-upgradeable': + specifier: ^5.3.0 + version: 5.3.0(@openzeppelin/contracts@5.3.0) + '@openzeppelin/hardhat-upgrades': + specifier: ^3.9.0 + version: 3.9.1(@nomicfoundation/hardhat-ethers@3.0.9(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)))(@nomicfoundation/hardhat-verify@2.1.1(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)))(encoding@0.1.13)(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@typechain/ethers-v6': + specifier: ^0.5.0 + version: 0.5.1(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typechain@8.3.2(patch_hash=b34ed6afcf99760666fdc85ecb2094fdd20ce509f947eb09cef21665a2a6a1d6)(typescript@5.8.3))(typescript@5.8.3) + '@typechain/hardhat': + specifier: 'catalog:' + version: 9.1.0(@typechain/ethers-v6@0.5.1(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typechain@8.3.2(patch_hash=b34ed6afcf99760666fdc85ecb2094fdd20ce509f947eb09cef21665a2a6a1d6)(typescript@5.8.3))(typescript@5.8.3))(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))(typechain@8.3.2(patch_hash=b34ed6afcf99760666fdc85ecb2094fdd20ce509f947eb09cef21665a2a6a1d6)(typescript@5.8.3)) + '@types/node': + specifier: ^20.17.50 + version: 20.19.9 + dotenv: + specifier: 'catalog:' + version: 16.5.0 + eslint: + specifier: 'catalog:' + version: 9.28.0(jiti@2.4.2) + ethers: + specifier: 'catalog:' + version: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + glob: + specifier: ^11.0.2 + version: 11.0.2 + globals: + specifier: ^16.1.0 + version: 16.1.0 + hardhat: + specifier: 'catalog:' + version: 2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) + hardhat-contract-sizer: + specifier: 'catalog:' + version: 2.10.0(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) + hardhat-secure-accounts: + specifier: 'catalog:' + version: 1.0.5(@nomicfoundation/hardhat-ethers@3.0.9(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)))(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) + hardhat-storage-layout: + specifier: 'catalog:' + version: 0.1.7(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) + lint-staged: + specifier: 16.0.0 + version: 16.0.0 + markdownlint-cli: + specifier: 'catalog:' + version: 0.45.0 + prettier: + specifier: ^3.5.3 + version: 3.5.3 + prettier-plugin-solidity: + specifier: ^2.0.0 + version: 2.0.0(prettier@3.5.3) + solhint: + specifier: 'catalog:' + version: 6.0.0(typescript@5.8.3) + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@20.19.9)(typescript@5.8.3) + typechain: + specifier: ^8.3.0 + version: 8.3.2(patch_hash=b34ed6afcf99760666fdc85ecb2094fdd20ce509f947eb09cef21665a2a6a1d6)(typescript@5.8.3) + typescript: + specifier: ^5.8.3 + version: 5.8.3 + typescript-eslint: + specifier: 'catalog:' + version: 8.33.1(eslint@9.28.0(jiti@2.4.2))(typescript@5.8.3) + yaml-lint: + specifier: ^1.7.0 + version: 1.7.0 + + packages/issuance/test: + dependencies: + '@graphprotocol/common': + specifier: workspace:^ + version: link:../../common + '@graphprotocol/contracts': + specifier: workspace:^ + version: link:../../contracts + '@graphprotocol/issuance': + specifier: workspace:^ + version: link:.. + '@graphprotocol/sdk': + specifier: workspace:^ + version: link:../../sdk + devDependencies: + '@nomicfoundation/hardhat-chai-matchers': + specifier: ^2.0.0 + version: 2.1.0(@nomicfoundation/hardhat-ethers@3.0.9(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)))(chai@4.5.0)(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@nomicfoundation/hardhat-ethers': + specifier: ^3.0.8 + version: 3.0.9(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@nomicfoundation/hardhat-foundry': + specifier: ^1.1.1 + version: 1.2.0(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@nomicfoundation/hardhat-network-helpers': + specifier: ^1.0.0 + version: 1.0.12(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@nomicfoundation/hardhat-toolbox': + specifier: 5.0.0 + version: 5.0.0(d805b92a490ff7e353d8006b7d043405) + '@openzeppelin/contracts': + specifier: ^5.3.0 + version: 5.3.0 + '@openzeppelin/contracts-upgradeable': + specifier: ^5.3.0 + version: 5.3.0(@openzeppelin/contracts@5.3.0) + '@openzeppelin/foundry-upgrades': + specifier: 0.4.0 + version: 0.4.0(@openzeppelin/defender-deploy-client-cli@0.0.1-alpha.10(encoding@0.1.13))(@openzeppelin/upgrades-core@1.44.1) + '@types/chai': + specifier: ^4.3.20 + version: 4.3.20 + '@types/mocha': + specifier: ^10.0.10 + version: 10.0.10 + '@types/node': + specifier: ^20.17.50 + version: 20.19.9 + chai: + specifier: ^4.3.7 + version: 4.5.0 + dotenv: + specifier: ^16.5.0 + version: 16.5.0 + eslint: + specifier: 'catalog:' + version: 9.28.0(jiti@2.4.2) + eslint-plugin-no-only-tests: + specifier: 'catalog:' + version: 3.3.0 + ethers: + specifier: ^6.13.4 + version: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + forge-std: + specifier: https://github.com/foundry-rs/forge-std/tarball/v1.9.7 + version: https://github.com/foundry-rs/forge-std/tarball/v1.9.7 + glob: + specifier: ^11.0.2 + version: 11.0.2 + hardhat: + specifier: 'catalog:' + version: 2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) + hardhat-gas-reporter: + specifier: 'catalog:' + version: 1.0.10(bufferutil@4.0.9)(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) + prettier: + specifier: ^3.5.3 + version: 3.5.3 + solidity-coverage: + specifier: ^0.8.0 + version: 0.8.16(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) + ts-node: + specifier: ^10.9.2 + version: 10.9.2(@types/node@20.19.9)(typescript@5.8.3) + typescript: + specifier: ^5.8.3 + version: 5.8.3 + packages/sdk: dependencies: '@arbitrum/sdk': @@ -978,6 +1175,9 @@ importers: packages: + '@adraffy/ens-normalize@1.10.1': + resolution: {integrity: sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==} + '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} @@ -1002,19 +1202,132 @@ packages: resolution: {integrity: sha512-xhlTqH0m31mnsG0tIP4ETgfSB6gXDaYYsUWTrlUV93fFQPI9dd8hE0Ot6MHLCtqgB32hwJAC3YZMWlXZw7AleA==} engines: {node: '>=14'} + '@aws-crypto/crc32@5.2.0': + resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/sha256-browser@5.2.0': + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} + '@aws-crypto/sha256-js@1.2.2': resolution: {integrity: sha512-Nr1QJIbW/afYYGzYvrF70LtaHrIRtd4TNAglX8BvlfxJLZ45SAmueIKYl5tWoNBPzp65ymXGFK0Bb1vZUpuc9g==} + '@aws-crypto/sha256-js@5.2.0': + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} + + '@aws-crypto/supports-web-crypto@5.2.0': + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} + '@aws-crypto/util@1.2.2': resolution: {integrity: sha512-H8PjG5WJ4wz0UXAFXeJjWCW1vkvIJ3qUUD+rGRwJ2/hj+xT58Qle2MTql/2MGzkU+1JLAFuR6aJpLAjHwhmwwg==} + '@aws-crypto/util@5.2.0': + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + + '@aws-sdk/client-lambda@3.865.0': + resolution: {integrity: sha512-ncCEW/kNRV8yJA/45z5HO6WEeihADzFY7RISfezDbvP3/X4dZb2gycRVPmJIE6CBqf01jwTkbG36qO+/iHIELg==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/client-sso@3.864.0': + resolution: {integrity: sha512-THiOp0OpQROEKZ6IdDCDNNh3qnNn/kFFaTSOiugDpgcE5QdsOxh1/RXq7LmHpTJum3cmnFf8jG59PHcz9Tjnlw==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/core@3.864.0': + resolution: {integrity: sha512-LFUREbobleHEln+Zf7IG83lAZwvHZG0stI7UU0CtwyuhQy5Yx0rKksHNOCmlM7MpTEbSCfntEhYi3jUaY5e5lg==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-env@3.864.0': + resolution: {integrity: sha512-StJPOI2Rt8UE6lYjXUpg6tqSZaM72xg46ljPg8kIevtBAAfdtq9K20qT/kSliWGIBocMFAv0g2mC0hAa+ECyvg==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-http@3.864.0': + resolution: {integrity: sha512-E/RFVxGTuGnuD+9pFPH2j4l6HvrXzPhmpL8H8nOoJUosjx7d4v93GJMbbl1v/fkDLqW9qN4Jx2cI6PAjohA6OA==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-ini@3.864.0': + resolution: {integrity: sha512-PlxrijguR1gxyPd5EYam6OfWLarj2MJGf07DvCx9MAuQkw77HBnsu6+XbV8fQriFuoJVTBLn9ROhMr/ROAYfUg==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-node@3.864.0': + resolution: {integrity: sha512-2BEymFeXURS+4jE9tP3vahPwbYRl0/1MVaFZcijj6pq+nf5EPGvkFillbdBRdc98ZI2NedZgSKu3gfZXgYdUhQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-process@3.864.0': + resolution: {integrity: sha512-Zxnn1hxhq7EOqXhVYgkF4rI9MnaO3+6bSg/tErnBQ3F8kDpA7CFU24G1YxwaJXp2X4aX3LwthefmSJHwcVP/2g==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-sso@3.864.0': + resolution: {integrity: sha512-UPyPNQbxDwHVGmgWdGg9/9yvzuedRQVF5jtMkmP565YX9pKZ8wYAcXhcYdNPWFvH0GYdB0crKOmvib+bmCuwkw==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/credential-provider-web-identity@3.864.0': + resolution: {integrity: sha512-nNcjPN4SYg8drLwqK0vgVeSvxeGQiD0FxOaT38mV2H8cu0C5NzpvA+14Xy+W6vT84dxgmJYKk71Cr5QL2Oz+rA==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-host-header@3.862.0': + resolution: {integrity: sha512-jDje8dCFeFHfuCAxMDXBs8hy8q9NCTlyK4ThyyfAj3U4Pixly2mmzY2u7b7AyGhWsjJNx8uhTjlYq5zkQPQCYw==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-logger@3.862.0': + resolution: {integrity: sha512-N/bXSJznNBR/i7Ofmf9+gM6dx/SPBK09ZWLKsW5iQjqKxAKn/2DozlnE54uiEs1saHZWoNDRg69Ww4XYYSlG1Q==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-recursion-detection@3.862.0': + resolution: {integrity: sha512-KVoo3IOzEkTq97YKM4uxZcYFSNnMkhW/qj22csofLegZi5fk90ztUnnaeKfaEJHfHp/tm1Y3uSoOXH45s++kKQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/middleware-user-agent@3.864.0': + resolution: {integrity: sha512-wrddonw4EyLNSNBrApzEhpSrDwJiNfjxDm5E+bn8n32BbAojXASH8W8jNpxz/jMgNkkJNxCfyqybGKzBX0OhbQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/nested-clients@3.864.0': + resolution: {integrity: sha512-H1C+NjSmz2y8Tbgh7Yy89J20yD/hVyk15hNoZDbCYkXg0M358KS7KVIEYs8E2aPOCr1sK3HBE819D/yvdMgokA==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/region-config-resolver@3.862.0': + resolution: {integrity: sha512-VisR+/HuVFICrBPY+q9novEiE4b3mvDofWqyvmxHcWM7HumTz9ZQSuEtnlB/92GVM3KDUrR9EmBHNRrfXYZkcQ==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/token-providers@3.864.0': + resolution: {integrity: sha512-gTc2QHOBo05SCwVA65dUtnJC6QERvFaPiuppGDSxoF7O5AQNK0UR/kMSenwLqN8b5E1oLYvQTv3C1idJLRX0cg==} + engines: {node: '>=18.0.0'} + '@aws-sdk/types@3.821.0': resolution: {integrity: sha512-Znroqdai1a90TlxGaJ+FK1lwC0fHpo97Xjsp5UKGR5JODYm7f9+/fF17ebO1KdoBr/Rm0UIFiF5VmI8ts9F1eA==} engines: {node: '>=18.0.0'} + '@aws-sdk/types@3.862.0': + resolution: {integrity: sha512-Bei+RL0cDxxV+lW2UezLbCYYNeJm6Nzee0TpW0FfyTRBhH9C1XQh4+x+IClriXvgBnRquTMMYsmJfvx8iyLKrg==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/util-endpoints@3.862.0': + resolution: {integrity: sha512-eCZuScdE9MWWkHGM2BJxm726MCmWk/dlHjOKvkM0sN1zxBellBMw5JohNss1Z8/TUmnW2gb9XHTOiHuGjOdksA==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/util-locate-window@3.804.0': + resolution: {integrity: sha512-zVoRfpmBVPodYlnMjgVjfGoEZagyRF5IPn3Uo6ZvOZp24chnW/FRstH7ESDHDDRga4z3V+ElUQHKpFDXWyBW5A==} + engines: {node: '>=18.0.0'} + + '@aws-sdk/util-user-agent-browser@3.862.0': + resolution: {integrity: sha512-BmPTlm0r9/10MMr5ND9E92r8KMZbq5ltYXYpVcUbAsnB1RJ8ASJuRoLne5F7mB3YMx0FJoOTuSq7LdQM3LgW3Q==} + + '@aws-sdk/util-user-agent-node@3.864.0': + resolution: {integrity: sha512-d+FjUm2eJEpP+FRpVR3z6KzMdx1qwxEYDz8jzNKwxYLBBquaBaP/wfoMtMQKAcbrR7aT9FZVZF7zDgzNxUvQlQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + '@aws-sdk/util-utf8-browser@3.259.0': resolution: {integrity: sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==} + '@aws-sdk/xml-builder@3.862.0': + resolution: {integrity: sha512-6Ed0kmC1NMbuFTEgNmamAUU1h5gShgxL1hBVLbEzUa3trX5aJBz1vU4bXaBTvOYUAnOHtiy1Ml4AMStd6hJnFA==} + engines: {node: '>=18.0.0'} + '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} @@ -1804,6 +2117,9 @@ packages: '@ethersproject/address@5.6.0': resolution: {integrity: sha512-6nvhYXjbXsHPS+30sHZ+U4VMagFC/9zAk6Gd/h3S21YW4+yfb0WfRtaAIZ4kfM4rrVwqiy284LP0GtL5HXGLxQ==} + '@ethersproject/address@5.6.1': + resolution: {integrity: sha512-uOgF0kS5MJv9ZvCz7x6T2EXJSzotiybApn4XlOgoTX0xdtyVIJ7pF+6cGPxiEq/dpBiTfMiw7Yc81JcwhSYA0Q==} + '@ethersproject/address@5.7.0': resolution: {integrity: sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==} @@ -2641,6 +2957,9 @@ packages: '@manypkg/get-packages@1.1.3': resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + '@noble/curves@1.2.0': + resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==} + '@noble/curves@1.4.2': resolution: {integrity: sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==} @@ -2651,6 +2970,10 @@ packages: '@noble/hashes@1.2.0': resolution: {integrity: sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==} + '@noble/hashes@1.3.2': + resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} + engines: {node: '>= 16'} + '@noble/hashes@1.4.0': resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} engines: {node: '>= 16'} @@ -2724,17 +3047,78 @@ packages: c-kzg: optional: true + '@nomicfoundation/hardhat-chai-matchers@2.1.0': + resolution: {integrity: sha512-GPhBNafh1fCnVD9Y7BYvoLnblnvfcq3j8YDbO1gGe/1nOFWzGmV7gFu5DkwFXF+IpYsS+t96o9qc/mPu3V3Vfw==} + peerDependencies: + '@nomicfoundation/hardhat-ethers': ^3.1.0 + chai: ^4.2.0 + ethers: ^6.14.0 + hardhat: ^2.26.0 + '@nomicfoundation/hardhat-ethers@3.0.9': resolution: {integrity: sha512-xBJdRUiCwKpr0OYrOzPwAyNGtsVzoBx32HFPJVv6S+sFA9TmBIBDaqNlFPmBH58ZjgNnGhEr/4oBZvGr4q4TjQ==} peerDependencies: ethers: ^6.14.0 hardhat: ^2.0.0 + '@nomicfoundation/hardhat-foundry@1.2.0': + resolution: {integrity: sha512-2AJQLcWnUk/iQqHDVnyOadASKFQKF1PhNtt1cONEQqzUPK+fqME1IbP+EKu+RkZTRcyc4xqUMaB0sutglKRITg==} + peerDependencies: + hardhat: ^2.26.0 + + '@nomicfoundation/hardhat-ignition-ethers@0.15.14': + resolution: {integrity: sha512-eq+5n+c1DW18/Xp8/QrHBBvG5QaKUxYF/byol4f1jrnZ1zAy0OrqEa/oaNFWchhpLalX7d7suk/2EL0PbT0CDQ==} + peerDependencies: + '@nomicfoundation/hardhat-ethers': ^3.1.0 + '@nomicfoundation/hardhat-ignition': ^0.15.13 + '@nomicfoundation/ignition-core': ^0.15.13 + ethers: ^6.14.0 + hardhat: ^2.26.0 + + '@nomicfoundation/hardhat-ignition@0.15.13': + resolution: {integrity: sha512-G4XGPWvxs9DJhZ6PE1wdvKjHkjErWbsETf4c7YxO6GUz+MJGlw+PtgbnCwhL3tQzSq3oD4MB0LGi+sK0polpUA==} + peerDependencies: + '@nomicfoundation/hardhat-verify': ^2.1.0 + hardhat: ^2.26.0 + '@nomicfoundation/hardhat-network-helpers@1.0.12': resolution: {integrity: sha512-xTNQNI/9xkHvjmCJnJOTyqDSl8uq1rKb2WOVmixQxFtRd7Oa3ecO8zM0cyC2YmOK+jHB9WPZ+F/ijkHg1CoORA==} peerDependencies: hardhat: ^2.9.5 + '@nomicfoundation/hardhat-toolbox@5.0.0': + resolution: {integrity: sha512-FnUtUC5PsakCbwiVNsqlXVIWG5JIb5CEZoSXbJUsEBun22Bivx2jhF1/q9iQbzuaGpJKFQyOhemPB2+XlEE6pQ==} + peerDependencies: + '@nomicfoundation/hardhat-chai-matchers': ^2.0.0 + '@nomicfoundation/hardhat-ethers': ^3.0.0 + '@nomicfoundation/hardhat-ignition-ethers': ^0.15.0 + '@nomicfoundation/hardhat-network-helpers': ^1.0.0 + '@nomicfoundation/hardhat-verify': ^2.0.0 + '@typechain/ethers-v6': ^0.5.0 + '@typechain/hardhat': ^9.0.0 + '@types/chai': ^4.2.0 + '@types/mocha': '>=9.1.0' + '@types/node': ^20.17.50 + chai: ^4.2.0 + ethers: ^6.4.0 + hardhat: ^2.11.0 + hardhat-gas-reporter: ^1.0.8 + solidity-coverage: ^0.8.1 + ts-node: '>=8.0.0' + typechain: ^8.3.0 + typescript: ^5.8.3 + + '@nomicfoundation/hardhat-verify@2.1.1': + resolution: {integrity: sha512-K1plXIS42xSHDJZRkrE2TZikqxp9T4y6jUMUNI/imLgN5uCcEQokmfU0DlyP9zzHncYK92HlT5IWP35UVCLrPw==} + peerDependencies: + hardhat: ^2.26.0 + + '@nomicfoundation/ignition-core@0.15.13': + resolution: {integrity: sha512-Z4T1WIbw0EqdsN9RxtnHeQXBi7P/piAmCu8bZmReIdDo/2h06qgKWxjDoNfc9VBFZJ0+Dx79tkgQR3ewxMDcpA==} + + '@nomicfoundation/ignition-ui@0.15.12': + resolution: {integrity: sha512-nQl8tusvmt1ANoyIj5RQl9tVSEmG0FnNbtwnWbTim+F8JLm4YLHWS0yEgYUZC+BEO3oS0D8r6V8a02JGZJgqiQ==} + '@nomicfoundation/slang@0.18.3': resolution: {integrity: sha512-YqAWgckqbHM0/CZxi9Nlf4hjk9wUNLC9ngWCWBiqMxPIZmzsVKYuChdlrfeBPQyvQQBoOhbx+7C1005kLVQDZQ==} @@ -2827,6 +3211,25 @@ packages: resolution: {integrity: sha512-PTef+rMxkM5VQ7sLwLKSjp2DBakYQd661ZJiSRywx+q/nIpm3B/HYGcz5wPZCA5O/QcEP6TatXXDoeMwimbcnw==} deprecated: This package has been deprecated and will no longer be maintained, please use @openzeppelin/defender-sdk package instead. + '@openzeppelin/defender-deploy-client-cli@0.0.1-alpha.10': + resolution: {integrity: sha512-piZnEbGZle6I4L0XsnD4Is73pps16Zx1wakXrZeDH7vPyVzIr/1Gb0hKflj+ffVHlNR8MrAs1BSw4Z99wGkzfg==} + hasBin: true + + '@openzeppelin/defender-sdk-base-client@2.7.0': + resolution: {integrity: sha512-J5IpvbFfdIJM4IadBcXfhCXVdX2yEpaZtRR1ecq87d8CdkmmEpniYfef/yVlG98yekvu125LaIRg0yXQOt9Bdg==} + + '@openzeppelin/defender-sdk-deploy-client@2.7.0': + resolution: {integrity: sha512-YOHZmnHmM1y6uSqXWGfk2/5/ae4zZJE6xG92yFEAIOy8vqh1dxznWMsoCcAXRXTCWc8RdCDpFdMfEy4SBTyYtg==} + + '@openzeppelin/defender-sdk-network-client@2.7.0': + resolution: {integrity: sha512-4CYWPa9+kSjojE5KS7kRmP161qsBATdp97TCrzyDdGoVahj0GyqgafRL9AAjm0eHZOM1c7EIYEpbvYRtFi8vyA==} + + '@openzeppelin/foundry-upgrades@0.4.0': + resolution: {integrity: sha512-x9wxogNteR5rdDzh5d/XnINR+ncUdPsCLWud99ULCa1pNNqRVPYMadr6iEVmjdTEy/4keIgbHwLB0rw2zXiRyQ==} + peerDependencies: + '@openzeppelin/defender-deploy-client-cli': 0.0.1-alpha.10 + '@openzeppelin/upgrades-core': ^1.37.0 + '@openzeppelin/hardhat-upgrades@1.28.0': resolution: {integrity: sha512-7sb/Jf+X+uIufOBnmHR0FJVWuxEs2lpxjJnLNN6eCJCP8nD0v+Ot5lTOW2Qb/GFnh+fLvJtEkhkowz4ZQ57+zQ==} hasBin: true @@ -2840,6 +3243,18 @@ packages: '@nomiclabs/harhdat-etherscan': optional: true + '@openzeppelin/hardhat-upgrades@3.9.1': + resolution: {integrity: sha512-pSDjlOnIpP+PqaJVe144dK6VVKZw2v6YQusyt0OOLiCsl+WUzfo4D0kylax7zjrOxqy41EK2ipQeIF4T+cCn2A==} + hasBin: true + peerDependencies: + '@nomicfoundation/hardhat-ethers': ^3.0.6 + '@nomicfoundation/hardhat-verify': ^2.0.14 + ethers: ^6.6.0 + hardhat: ^2.24.1 + peerDependenciesMeta: + '@nomicfoundation/hardhat-verify': + optional: true + '@openzeppelin/platform-deploy-client@0.8.0': resolution: {integrity: sha512-POx3AsnKwKSV/ZLOU/gheksj0Lq7Is1q2F3pKmcFjGZiibf+4kjGxr4eSMrT+2qgKYZQH1ZLQZ+SkbguD8fTvA==} deprecated: '@openzeppelin/platform-deploy-client is deprecated. Please use @openzeppelin/defender-sdk-deploy-client' @@ -3028,50 +3443,242 @@ packages: '@sinonjs/fake-timers@10.3.0': resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} - '@smithy/types@4.3.1': - resolution: {integrity: sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==} + '@smithy/abort-controller@4.0.5': + resolution: {integrity: sha512-jcrqdTQurIrBbUm4W2YdLVMQDoL0sA9DTxYd2s+R/y+2U9NLOP7Xf/YqfSg1FZhlZIYEnvk2mwbyvIfdLEPo8g==} engines: {node: '>=18.0.0'} - '@solidity-parser/parser@0.14.5': - resolution: {integrity: sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg==} + '@smithy/config-resolver@4.1.5': + resolution: {integrity: sha512-viuHMxBAqydkB0AfWwHIdwf/PRH2z5KHGUzqyRtS/Wv+n3IHI993Sk76VCA7dD/+GzgGOmlJDITfPcJC1nIVIw==} + engines: {node: '>=18.0.0'} - '@solidity-parser/parser@0.20.1': - resolution: {integrity: sha512-58I2sRpzaQUN+jJmWbHfbWf9AKfzqCI8JAdFB0vbyY+u8tBRcuTt9LxzasvR0LGQpcRv97eyV7l61FQ3Ib7zVw==} + '@smithy/core@3.8.0': + resolution: {integrity: sha512-EYqsIYJmkR1VhVE9pccnk353xhs+lB6btdutJEtsp7R055haMJp2yE16eSxw8fv+G0WUY6vqxyYOP8kOqawxYQ==} + engines: {node: '>=18.0.0'} - '@szmarczak/http-timer@1.1.2': - resolution: {integrity: sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==} - engines: {node: '>=6'} + '@smithy/credential-provider-imds@4.0.7': + resolution: {integrity: sha512-dDzrMXA8d8riFNiPvytxn0mNwR4B3h8lgrQ5UjAGu6T9z/kRg/Xncf4tEQHE/+t25sY8IH3CowcmWi+1U5B1Gw==} + engines: {node: '>=18.0.0'} - '@szmarczak/http-timer@4.0.6': - resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} - engines: {node: '>=10'} + '@smithy/eventstream-codec@4.0.5': + resolution: {integrity: sha512-miEUN+nz2UTNoRYRhRqVTJCx7jMeILdAurStT2XoS+mhokkmz1xAPp95DFW9Gxt4iF2VBqpeF9HbTQ3kY1viOA==} + engines: {node: '>=18.0.0'} - '@szmarczak/http-timer@5.0.1': - resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} - engines: {node: '>=14.16'} + '@smithy/eventstream-serde-browser@4.0.5': + resolution: {integrity: sha512-LCUQUVTbM6HFKzImYlSB9w4xafZmpdmZsOh9rIl7riPC3osCgGFVP+wwvYVw6pXda9PPT9TcEZxaq3XE81EdJQ==} + engines: {node: '>=18.0.0'} - '@tenderly/api-client@1.1.0': - resolution: {integrity: sha512-kyye7TQ+RbDbJ7bSUjNf/O9fTtRYNUDIEUZQSrmNonowMw5/EpNi664eWaOoC00NEzxgttVrtme/GHvIOu7rNg==} - peerDependencies: - ts-node: '*' - typescript: ^5.8.3 - peerDependenciesMeta: - ts-node: - optional: true - typescript: - optional: true + '@smithy/eventstream-serde-config-resolver@4.1.3': + resolution: {integrity: sha512-yTTzw2jZjn/MbHu1pURbHdpjGbCuMHWncNBpJnQAPxOVnFUAbSIUSwafiphVDjNV93TdBJWmeVAds7yl5QCkcA==} + engines: {node: '>=18.0.0'} - '@tenderly/hardhat-integration@1.1.1': - resolution: {integrity: sha512-VHa380DrKv+KA1N4vbJGLDoghbVqMZ4wEozbxRfCzlkSs5V1keNgudRSUFK6lgfKhkoAWRO+dA8MZYnJOvUOkA==} - peerDependencies: - hardhat: ^2.22.6 + '@smithy/eventstream-serde-node@4.0.5': + resolution: {integrity: sha512-lGS10urI4CNzz6YlTe5EYG0YOpsSp3ra8MXyco4aqSkQDuyZPIw2hcaxDU82OUVtK7UY9hrSvgWtpsW5D4rb4g==} + engines: {node: '>=18.0.0'} - '@tenderly/hardhat-tenderly@1.11.0': - resolution: {integrity: sha512-7UU9i3wn+YiN5xXGvE015/SDR6QH5ULIc6Gu4PmGNIcBpePElY2+cFxGGF9M5gRbzvAxDDa+KCenCN5cg0cQ/w==} + '@smithy/eventstream-serde-universal@4.0.5': + resolution: {integrity: sha512-JFnmu4SU36YYw3DIBVao3FsJh4Uw65vVDIqlWT4LzR6gXA0F3KP0IXFKKJrhaVzCBhAuMsrUUaT5I+/4ZhF7aw==} + engines: {node: '>=18.0.0'} - '@trufflesuite/bigint-buffer@1.1.9': - resolution: {integrity: sha512-bdM5cEGCOhDSwminryHJbRmXc1x7dPKg6Pqns3qyTwFlxsqUgxE29lsERS3PlIW1HTjoIGMUqsk1zQQwST1Yxw==} - engines: {node: '>= 10.0.0'} + '@smithy/fetch-http-handler@5.1.1': + resolution: {integrity: sha512-61WjM0PWmZJR+SnmzaKI7t7G0UkkNFboDpzIdzSoy7TByUzlxo18Qlh9s71qug4AY4hlH/CwXdubMtkcNEb/sQ==} + engines: {node: '>=18.0.0'} + + '@smithy/hash-node@4.0.5': + resolution: {integrity: sha512-cv1HHkKhpyRb6ahD8Vcfb2Hgz67vNIXEp2vnhzfxLFGRukLCNEA5QdsorbUEzXma1Rco0u3rx5VTqbM06GcZqQ==} + engines: {node: '>=18.0.0'} + + '@smithy/invalid-dependency@4.0.5': + resolution: {integrity: sha512-IVnb78Qtf7EJpoEVo7qJ8BEXQwgC4n3igeJNNKEj/MLYtapnx8A67Zt/J3RXAj2xSO1910zk0LdFiygSemuLow==} + engines: {node: '>=18.0.0'} + + '@smithy/is-array-buffer@2.2.0': + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + + '@smithy/is-array-buffer@4.0.0': + resolution: {integrity: sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-content-length@4.0.5': + resolution: {integrity: sha512-l1jlNZoYzoCC7p0zCtBDE5OBXZ95yMKlRlftooE5jPWQn4YBPLgsp+oeHp7iMHaTGoUdFqmHOPa8c9G3gBsRpQ==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-endpoint@4.1.18': + resolution: {integrity: sha512-ZhvqcVRPZxnZlokcPaTwb+r+h4yOIOCJmx0v2d1bpVlmP465g3qpVSf7wxcq5zZdu4jb0H4yIMxuPwDJSQc3MQ==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-retry@4.1.19': + resolution: {integrity: sha512-X58zx/NVECjeuUB6A8HBu4bhx72EoUz+T5jTMIyeNKx2lf+Gs9TmWPNNkH+5QF0COjpInP/xSpJGJ7xEnAklQQ==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-serde@4.0.9': + resolution: {integrity: sha512-uAFFR4dpeoJPGz8x9mhxp+RPjo5wW0QEEIPPPbLXiRRWeCATf/Km3gKIVR5vaP8bN1kgsPhcEeh+IZvUlBv6Xg==} + engines: {node: '>=18.0.0'} + + '@smithy/middleware-stack@4.0.5': + resolution: {integrity: sha512-/yoHDXZPh3ocRVyeWQFvC44u8seu3eYzZRveCMfgMOBcNKnAmOvjbL9+Cp5XKSIi9iYA9PECUuW2teDAk8T+OQ==} + engines: {node: '>=18.0.0'} + + '@smithy/node-config-provider@4.1.4': + resolution: {integrity: sha512-+UDQV/k42jLEPPHSn39l0Bmc4sB1xtdI9Gd47fzo/0PbXzJ7ylgaOByVjF5EeQIumkepnrJyfx86dPa9p47Y+w==} + engines: {node: '>=18.0.0'} + + '@smithy/node-http-handler@4.1.1': + resolution: {integrity: sha512-RHnlHqFpoVdjSPPiYy/t40Zovf3BBHc2oemgD7VsVTFFZrU5erFFe0n52OANZZ/5sbshgD93sOh5r6I35Xmpaw==} + engines: {node: '>=18.0.0'} + + '@smithy/property-provider@4.0.5': + resolution: {integrity: sha512-R/bswf59T/n9ZgfgUICAZoWYKBHcsVDurAGX88zsiUtOTA/xUAPyiT+qkNCPwFn43pZqN84M4MiUsbSGQmgFIQ==} + engines: {node: '>=18.0.0'} + + '@smithy/protocol-http@5.1.3': + resolution: {integrity: sha512-fCJd2ZR7D22XhDY0l+92pUag/7je2BztPRQ01gU5bMChcyI0rlly7QFibnYHzcxDvccMjlpM/Q1ev8ceRIb48w==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-builder@4.0.5': + resolution: {integrity: sha512-NJeSCU57piZ56c+/wY+AbAw6rxCCAOZLCIniRE7wqvndqxcKKDOXzwWjrY7wGKEISfhL9gBbAaWWgHsUGedk+A==} + engines: {node: '>=18.0.0'} + + '@smithy/querystring-parser@4.0.5': + resolution: {integrity: sha512-6SV7md2CzNG/WUeTjVe6Dj8noH32r4MnUeFKZrnVYsQxpGSIcphAanQMayi8jJLZAWm6pdM9ZXvKCpWOsIGg0w==} + engines: {node: '>=18.0.0'} + + '@smithy/service-error-classification@4.0.7': + resolution: {integrity: sha512-XvRHOipqpwNhEjDf2L5gJowZEm5nsxC16pAZOeEcsygdjv9A2jdOh3YoDQvOXBGTsaJk6mNWtzWalOB9976Wlg==} + engines: {node: '>=18.0.0'} + + '@smithy/shared-ini-file-loader@4.0.5': + resolution: {integrity: sha512-YVVwehRDuehgoXdEL4r1tAAzdaDgaC9EQvhK0lEbfnbrd0bd5+CTQumbdPryX3J2shT7ZqQE+jPW4lmNBAB8JQ==} + engines: {node: '>=18.0.0'} + + '@smithy/signature-v4@5.1.3': + resolution: {integrity: sha512-mARDSXSEgllNzMw6N+mC+r1AQlEBO3meEAkR/UlfAgnMzJUB3goRBWgip1EAMG99wh36MDqzo86SfIX5Y+VEaw==} + engines: {node: '>=18.0.0'} + + '@smithy/smithy-client@4.4.10': + resolution: {integrity: sha512-iW6HjXqN0oPtRS0NK/zzZ4zZeGESIFcxj2FkWed3mcK8jdSdHzvnCKXSjvewESKAgGKAbJRA+OsaqKhkdYRbQQ==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.3.1': + resolution: {integrity: sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA==} + engines: {node: '>=18.0.0'} + + '@smithy/types@4.3.2': + resolution: {integrity: sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw==} + engines: {node: '>=18.0.0'} + + '@smithy/url-parser@4.0.5': + resolution: {integrity: sha512-j+733Um7f1/DXjYhCbvNXABV53NyCRRA54C7bNEIxNPs0YjfRxeMKjjgm2jvTYrciZyCjsicHwQ6Q0ylo+NAUw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-base64@4.0.0': + resolution: {integrity: sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-browser@4.0.0': + resolution: {integrity: sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==} + engines: {node: '>=18.0.0'} + + '@smithy/util-body-length-node@4.0.0': + resolution: {integrity: sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-buffer-from@2.2.0': + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + + '@smithy/util-buffer-from@4.0.0': + resolution: {integrity: sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==} + engines: {node: '>=18.0.0'} + + '@smithy/util-config-provider@4.0.0': + resolution: {integrity: sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-browser@4.0.26': + resolution: {integrity: sha512-xgl75aHIS/3rrGp7iTxQAOELYeyiwBu+eEgAk4xfKwJJ0L8VUjhO2shsDpeil54BOFsqmk5xfdesiewbUY5tKQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-defaults-mode-node@4.0.26': + resolution: {integrity: sha512-z81yyIkGiLLYVDetKTUeCZQ8x20EEzvQjrqJtb/mXnevLq2+w3XCEWTJ2pMp401b6BkEkHVfXb/cROBpVauLMQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-endpoints@3.0.7': + resolution: {integrity: sha512-klGBP+RpBp6V5JbrY2C/VKnHXn3d5V2YrifZbmMY8os7M6m8wdYFoO6w/fe5VkP+YVwrEktW3IWYaSQVNZJ8oQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-hex-encoding@4.0.0': + resolution: {integrity: sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==} + engines: {node: '>=18.0.0'} + + '@smithy/util-middleware@4.0.5': + resolution: {integrity: sha512-N40PfqsZHRSsByGB81HhSo+uvMxEHT+9e255S53pfBw/wI6WKDI7Jw9oyu5tJTLwZzV5DsMha3ji8jk9dsHmQQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-retry@4.0.7': + resolution: {integrity: sha512-TTO6rt0ppK70alZpkjwy+3nQlTiqNfoXja+qwuAchIEAIoSZW8Qyd76dvBv3I5bCpE38APafG23Y/u270NspiQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-stream@4.2.4': + resolution: {integrity: sha512-vSKnvNZX2BXzl0U2RgCLOwWaAP9x/ddd/XobPK02pCbzRm5s55M53uwb1rl/Ts7RXZvdJZerPkA+en2FDghLuQ==} + engines: {node: '>=18.0.0'} + + '@smithy/util-uri-escape@4.0.0': + resolution: {integrity: sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==} + engines: {node: '>=18.0.0'} + + '@smithy/util-utf8@2.3.0': + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + + '@smithy/util-utf8@4.0.0': + resolution: {integrity: sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==} + engines: {node: '>=18.0.0'} + + '@smithy/util-waiter@4.0.7': + resolution: {integrity: sha512-mYqtQXPmrwvUljaHyGxYUIIRI3qjBTEb/f5QFi3A6VlxhpmZd5mWXn9W+qUkf2pVE1Hv3SqxefiZOPGdxmO64A==} + engines: {node: '>=18.0.0'} + + '@solidity-parser/parser@0.14.5': + resolution: {integrity: sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg==} + + '@solidity-parser/parser@0.20.1': + resolution: {integrity: sha512-58I2sRpzaQUN+jJmWbHfbWf9AKfzqCI8JAdFB0vbyY+u8tBRcuTt9LxzasvR0LGQpcRv97eyV7l61FQ3Ib7zVw==} + + '@szmarczak/http-timer@1.1.2': + resolution: {integrity: sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==} + engines: {node: '>=6'} + + '@szmarczak/http-timer@4.0.6': + resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} + engines: {node: '>=10'} + + '@szmarczak/http-timer@5.0.1': + resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} + engines: {node: '>=14.16'} + + '@tenderly/api-client@1.1.0': + resolution: {integrity: sha512-kyye7TQ+RbDbJ7bSUjNf/O9fTtRYNUDIEUZQSrmNonowMw5/EpNi664eWaOoC00NEzxgttVrtme/GHvIOu7rNg==} + peerDependencies: + ts-node: '*' + typescript: ^5.8.3 + peerDependenciesMeta: + ts-node: + optional: true + typescript: + optional: true + + '@tenderly/hardhat-integration@1.1.1': + resolution: {integrity: sha512-VHa380DrKv+KA1N4vbJGLDoghbVqMZ4wEozbxRfCzlkSs5V1keNgudRSUFK6lgfKhkoAWRO+dA8MZYnJOvUOkA==} + peerDependencies: + hardhat: ^2.22.6 + + '@tenderly/hardhat-tenderly@1.11.0': + resolution: {integrity: sha512-7UU9i3wn+YiN5xXGvE015/SDR6QH5ULIc6Gu4PmGNIcBpePElY2+cFxGGF9M5gRbzvAxDDa+KCenCN5cg0cQ/w==} + + '@trufflesuite/bigint-buffer@1.1.9': + resolution: {integrity: sha512-bdM5cEGCOhDSwminryHJbRmXc1x7dPKg6Pqns3qyTwFlxsqUgxE29lsERS3PlIW1HTjoIGMUqsk1zQQwST1Yxw==} + engines: {node: '>= 10.0.0'} '@tsconfig/node10@1.0.11': resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} @@ -3100,6 +3707,13 @@ packages: ethers: ^5.0.0 typechain: ^3.0.0 + '@typechain/ethers-v6@0.5.1': + resolution: {integrity: sha512-F+GklO8jBWlsaVV+9oHaPh5NJdd6rAKN4tklGfInX1Q7h0xPgVLP39Jl3eCulPB5qexI71ZFHwbljx4ZXNfouA==} + peerDependencies: + ethers: 6.x + typechain: ^8.3.2 + typescript: ^5.8.3 + '@typechain/hardhat@6.1.6': resolution: {integrity: sha512-BiVnegSs+ZHVymyidtK472syodx1sXYlYJJixZfRstHVGYTi8V1O7QG4nsjyb0PC/LORcq7sfBUcHto1y6UgJA==} peerDependencies: @@ -3110,6 +3724,14 @@ packages: hardhat: ^2.9.9 typechain: ^8.1.1 + '@typechain/hardhat@9.1.0': + resolution: {integrity: sha512-mtaUlzLlkqTlfPwB3FORdejqBskSnh+Jl8AIJGjXNAQfRQ4ofHADPl1+oU7Z3pAJzmZbUXII8MhOLQltcHgKnA==} + peerDependencies: + '@typechain/ethers-v6': ^0.5.1 + ethers: ^6.1.0 + hardhat: ^2.9.9 + typechain: ^8.3.2 + '@types/abstract-leveldown@7.2.5': resolution: {integrity: sha512-/2B0nQF4UdupuxeKTJA2+Rj1D+uDemo6P4kMwKCpbfpnzeVaWSELTsAw4Lxn3VJD6APtRrZOCuYo+4nHUQfTfg==} @@ -3278,6 +3900,9 @@ packages: '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + '@types/uuid@9.0.8': + resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} + '@types/validator@13.15.1': resolution: {integrity: sha512-9gG6ogYcoI2mCMLdcO0NYI0AYrbxIjv0MDmy/5Ywo6CpWWrqYayc+mmgxRsCgtcGJm9BSbXkMsmxGah1iGHAAQ==} @@ -3486,6 +4111,9 @@ packages: aes-js@3.1.2: resolution: {integrity: sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==} + aes-js@4.0.0-beta.5: + resolution: {integrity: sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==} + agent-base@6.0.2: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} @@ -4113,6 +4741,9 @@ packages: resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + bowser@2.12.0: + resolution: {integrity: sha512-HcOcTudTeEWgbHh0Y1Tyb6fdeR71m4b/QACf0D4KswGTsNeIJQmg38mRENZPAYPZvGFN3fk3604XbQEPdxXdKg==} + boxen@5.1.2: resolution: {integrity: sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==} engines: {node: '>=10'} @@ -4329,6 +4960,10 @@ packages: resolution: {integrity: sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg==} engines: {node: '>=12.19'} + cbor@9.0.2: + resolution: {integrity: sha512-JPypkxsB10s9QOWwa6zwPzqE1Md3vqpPc+cai4sAecuCsRyAtAl/pMyhPlMbT/xtPnm2dznJZYRLui57qiRhaQ==} + engines: {node: '>=16'} + chai-as-promised@7.1.2: resolution: {integrity: sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw==} peerDependencies: @@ -5562,6 +6197,10 @@ packages: ethers@5.8.0: resolution: {integrity: sha512-DUq+7fHrCg1aPDFCHx6UIPb3nmt2XMpM7Y/g2gLhsl3lIBqeAfOJIl1qEvRf2uq3BiKxmh6Fh5pfp2ieyek7Kg==} + ethers@6.15.0: + resolution: {integrity: sha512-Kf/3ZW54L4UT0pZtsY/rf+EkBU7Qi5nnhonjUb8yTXcxH3cdcWrV2cRyk0Xk/4jK6OoHhxxZHriyhje20If2hQ==} + engines: {node: '>=14.0.0'} + ethjs-unit@0.1.6: resolution: {integrity: sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==} engines: {node: '>=6.5.0', npm: '>=3'} @@ -5711,6 +6350,10 @@ packages: fast-url-parser@1.1.3: resolution: {integrity: sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==} + fast-xml-parser@5.2.5: + resolution: {integrity: sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==} + hasBin: true + fastify-warning@0.2.0: resolution: {integrity: sha512-s1EQguBw/9qtc1p/WTY4eq9WMRIACkj+HTcOIK1in4MV5aFaQC9ZCIt0dJ7pr5bIf4lPpHvAtP2ywpTNgs7hqw==} deprecated: This module renamed to process-warning @@ -5880,6 +6523,10 @@ packages: forever-agent@0.6.1: resolution: {integrity: sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==} + forge-std@https://github.com/foundry-rs/forge-std/tarball/v1.9.7: + resolution: {tarball: https://github.com/foundry-rs/forge-std/tarball/v1.9.7} + version: 1.9.7 + form-data-encoder@2.1.4: resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} engines: {node: '>= 14.17'} @@ -6566,6 +7213,9 @@ packages: immediate@3.3.0: resolution: {integrity: sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==} + immer@10.0.2: + resolution: {integrity: sha512-Rx3CqeqQ19sxUtYV9CU911Vhy8/721wRFnJv3REVGWUmoAcIwzifTsdmJte/MV+0/XpM35LZdQMBGkRIoLPwQA==} + immutable@3.7.6: resolution: {integrity: sha512-AizQPcaofEtO11RZhPPHBOJRdo/20MKQF9mBLnVkBoyHi1/zXK8fzVdnEpSV9gxqtnh6Qomfp3F0xT5qP/vThw==} engines: {node: '>=0.8.0'} @@ -8148,6 +8798,11 @@ packages: resolution: {integrity: sha512-p2cfF+B3XXacQdswUYWZ0w6Vld0832A/tuqjLBu3H1sfUcby4N2oVbGhyuCkZv+t3iY3aiFEj7gZGqax9Q2c1w==} engines: {node: '>= 0.4.0'} + ndjson@2.0.0: + resolution: {integrity: sha512-nGl7LRGrzugTtaFcJMhLbpzJM6XdivmbkdlaGcrk/LXg2KL/YBC6z1g70xh0/al+oFuVFP8N8kiWRucmeEH/qQ==} + engines: {node: '>=10'} + hasBin: true + negotiator@0.6.3: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} @@ -8426,6 +9081,9 @@ packages: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} + ordinal@1.0.3: + resolution: {integrity: sha512-cMddMgb2QElm8G7vdaa02jhUNbTSrhsgAGUz1OokD83uJTwSUn+nKoNoKVVaRa08yF6sgfO7Maou1+bgLd9rdQ==} + os-homedir@1.0.2: resolution: {integrity: sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==} engines: {node: '>=0.10.0'} @@ -10038,6 +10696,9 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + strnum@2.1.1: + resolution: {integrity: sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==} + supports-color@2.0.0: resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==} engines: {node: '>=0.8.0'} @@ -10316,6 +10977,9 @@ packages: tslib@2.5.3: resolution: {integrity: sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==} + tslib@2.7.0: + resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} + tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} @@ -10491,6 +11155,10 @@ packages: resolution: {integrity: sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==} engines: {node: '>=14.0'} + undici@6.21.3: + resolution: {integrity: sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==} + engines: {node: '>=18.17'} + unfetch@4.2.0: resolution: {integrity: sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==} @@ -10617,6 +11285,10 @@ packages: resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true + uuid@9.0.1: + resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} + hasBin: true + v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} @@ -10944,6 +11616,18 @@ packages: utf-8-validate: optional: true + ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + ws@8.18.0: resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} engines: {node: '>=10.0.0'} @@ -11093,6 +11777,8 @@ packages: snapshots: + '@adraffy/ens-normalize@1.10.1': {} + '@ampproject/remapping@2.3.0': dependencies: '@jridgewell/gen-mapping': 0.3.8 @@ -11147,27 +11833,389 @@ snapshots: transitivePeerDependencies: - encoding + '@aws-crypto/crc32@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.862.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-browser@5.2.0': + dependencies: + '@aws-crypto/sha256-js': 5.2.0 + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.862.0 + '@aws-sdk/util-locate-window': 3.804.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + '@aws-crypto/sha256-js@1.2.2': dependencies: '@aws-crypto/util': 1.2.2 '@aws-sdk/types': 3.821.0 tslib: 1.14.1 + '@aws-crypto/sha256-js@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.862.0 + tslib: 2.8.1 + + '@aws-crypto/supports-web-crypto@5.2.0': + dependencies: + tslib: 2.8.1 + '@aws-crypto/util@1.2.2': dependencies: '@aws-sdk/types': 3.821.0 '@aws-sdk/util-utf8-browser': 3.259.0 tslib: 1.14.1 + '@aws-crypto/util@5.2.0': + dependencies: + '@aws-sdk/types': 3.862.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-sdk/client-lambda@3.865.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.864.0 + '@aws-sdk/credential-provider-node': 3.864.0 + '@aws-sdk/middleware-host-header': 3.862.0 + '@aws-sdk/middleware-logger': 3.862.0 + '@aws-sdk/middleware-recursion-detection': 3.862.0 + '@aws-sdk/middleware-user-agent': 3.864.0 + '@aws-sdk/region-config-resolver': 3.862.0 + '@aws-sdk/types': 3.862.0 + '@aws-sdk/util-endpoints': 3.862.0 + '@aws-sdk/util-user-agent-browser': 3.862.0 + '@aws-sdk/util-user-agent-node': 3.864.0 + '@smithy/config-resolver': 4.1.5 + '@smithy/core': 3.8.0 + '@smithy/eventstream-serde-browser': 4.0.5 + '@smithy/eventstream-serde-config-resolver': 4.1.3 + '@smithy/eventstream-serde-node': 4.0.5 + '@smithy/fetch-http-handler': 5.1.1 + '@smithy/hash-node': 4.0.5 + '@smithy/invalid-dependency': 4.0.5 + '@smithy/middleware-content-length': 4.0.5 + '@smithy/middleware-endpoint': 4.1.18 + '@smithy/middleware-retry': 4.1.19 + '@smithy/middleware-serde': 4.0.9 + '@smithy/middleware-stack': 4.0.5 + '@smithy/node-config-provider': 4.1.4 + '@smithy/node-http-handler': 4.1.1 + '@smithy/protocol-http': 5.1.3 + '@smithy/smithy-client': 4.4.10 + '@smithy/types': 4.3.2 + '@smithy/url-parser': 4.0.5 + '@smithy/util-base64': 4.0.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-body-length-node': 4.0.0 + '@smithy/util-defaults-mode-browser': 4.0.26 + '@smithy/util-defaults-mode-node': 4.0.26 + '@smithy/util-endpoints': 3.0.7 + '@smithy/util-middleware': 4.0.5 + '@smithy/util-retry': 4.0.7 + '@smithy/util-stream': 4.2.4 + '@smithy/util-utf8': 4.0.0 + '@smithy/util-waiter': 4.0.7 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/client-sso@3.864.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.864.0 + '@aws-sdk/middleware-host-header': 3.862.0 + '@aws-sdk/middleware-logger': 3.862.0 + '@aws-sdk/middleware-recursion-detection': 3.862.0 + '@aws-sdk/middleware-user-agent': 3.864.0 + '@aws-sdk/region-config-resolver': 3.862.0 + '@aws-sdk/types': 3.862.0 + '@aws-sdk/util-endpoints': 3.862.0 + '@aws-sdk/util-user-agent-browser': 3.862.0 + '@aws-sdk/util-user-agent-node': 3.864.0 + '@smithy/config-resolver': 4.1.5 + '@smithy/core': 3.8.0 + '@smithy/fetch-http-handler': 5.1.1 + '@smithy/hash-node': 4.0.5 + '@smithy/invalid-dependency': 4.0.5 + '@smithy/middleware-content-length': 4.0.5 + '@smithy/middleware-endpoint': 4.1.18 + '@smithy/middleware-retry': 4.1.19 + '@smithy/middleware-serde': 4.0.9 + '@smithy/middleware-stack': 4.0.5 + '@smithy/node-config-provider': 4.1.4 + '@smithy/node-http-handler': 4.1.1 + '@smithy/protocol-http': 5.1.3 + '@smithy/smithy-client': 4.4.10 + '@smithy/types': 4.3.2 + '@smithy/url-parser': 4.0.5 + '@smithy/util-base64': 4.0.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-body-length-node': 4.0.0 + '@smithy/util-defaults-mode-browser': 4.0.26 + '@smithy/util-defaults-mode-node': 4.0.26 + '@smithy/util-endpoints': 3.0.7 + '@smithy/util-middleware': 4.0.5 + '@smithy/util-retry': 4.0.7 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/core@3.864.0': + dependencies: + '@aws-sdk/types': 3.862.0 + '@aws-sdk/xml-builder': 3.862.0 + '@smithy/core': 3.8.0 + '@smithy/node-config-provider': 4.1.4 + '@smithy/property-provider': 4.0.5 + '@smithy/protocol-http': 5.1.3 + '@smithy/signature-v4': 5.1.3 + '@smithy/smithy-client': 4.4.10 + '@smithy/types': 4.3.2 + '@smithy/util-base64': 4.0.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-middleware': 4.0.5 + '@smithy/util-utf8': 4.0.0 + fast-xml-parser: 5.2.5 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-env@3.864.0': + dependencies: + '@aws-sdk/core': 3.864.0 + '@aws-sdk/types': 3.862.0 + '@smithy/property-provider': 4.0.5 + '@smithy/types': 4.3.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-http@3.864.0': + dependencies: + '@aws-sdk/core': 3.864.0 + '@aws-sdk/types': 3.862.0 + '@smithy/fetch-http-handler': 5.1.1 + '@smithy/node-http-handler': 4.1.1 + '@smithy/property-provider': 4.0.5 + '@smithy/protocol-http': 5.1.3 + '@smithy/smithy-client': 4.4.10 + '@smithy/types': 4.3.2 + '@smithy/util-stream': 4.2.4 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-ini@3.864.0': + dependencies: + '@aws-sdk/core': 3.864.0 + '@aws-sdk/credential-provider-env': 3.864.0 + '@aws-sdk/credential-provider-http': 3.864.0 + '@aws-sdk/credential-provider-process': 3.864.0 + '@aws-sdk/credential-provider-sso': 3.864.0 + '@aws-sdk/credential-provider-web-identity': 3.864.0 + '@aws-sdk/nested-clients': 3.864.0 + '@aws-sdk/types': 3.862.0 + '@smithy/credential-provider-imds': 4.0.7 + '@smithy/property-provider': 4.0.5 + '@smithy/shared-ini-file-loader': 4.0.5 + '@smithy/types': 4.3.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-node@3.864.0': + dependencies: + '@aws-sdk/credential-provider-env': 3.864.0 + '@aws-sdk/credential-provider-http': 3.864.0 + '@aws-sdk/credential-provider-ini': 3.864.0 + '@aws-sdk/credential-provider-process': 3.864.0 + '@aws-sdk/credential-provider-sso': 3.864.0 + '@aws-sdk/credential-provider-web-identity': 3.864.0 + '@aws-sdk/types': 3.862.0 + '@smithy/credential-provider-imds': 4.0.7 + '@smithy/property-provider': 4.0.5 + '@smithy/shared-ini-file-loader': 4.0.5 + '@smithy/types': 4.3.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-process@3.864.0': + dependencies: + '@aws-sdk/core': 3.864.0 + '@aws-sdk/types': 3.862.0 + '@smithy/property-provider': 4.0.5 + '@smithy/shared-ini-file-loader': 4.0.5 + '@smithy/types': 4.3.2 + tslib: 2.8.1 + + '@aws-sdk/credential-provider-sso@3.864.0': + dependencies: + '@aws-sdk/client-sso': 3.864.0 + '@aws-sdk/core': 3.864.0 + '@aws-sdk/token-providers': 3.864.0 + '@aws-sdk/types': 3.862.0 + '@smithy/property-provider': 4.0.5 + '@smithy/shared-ini-file-loader': 4.0.5 + '@smithy/types': 4.3.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/credential-provider-web-identity@3.864.0': + dependencies: + '@aws-sdk/core': 3.864.0 + '@aws-sdk/nested-clients': 3.864.0 + '@aws-sdk/types': 3.862.0 + '@smithy/property-provider': 4.0.5 + '@smithy/types': 4.3.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/middleware-host-header@3.862.0': + dependencies: + '@aws-sdk/types': 3.862.0 + '@smithy/protocol-http': 5.1.3 + '@smithy/types': 4.3.2 + tslib: 2.8.1 + + '@aws-sdk/middleware-logger@3.862.0': + dependencies: + '@aws-sdk/types': 3.862.0 + '@smithy/types': 4.3.2 + tslib: 2.8.1 + + '@aws-sdk/middleware-recursion-detection@3.862.0': + dependencies: + '@aws-sdk/types': 3.862.0 + '@smithy/protocol-http': 5.1.3 + '@smithy/types': 4.3.2 + tslib: 2.8.1 + + '@aws-sdk/middleware-user-agent@3.864.0': + dependencies: + '@aws-sdk/core': 3.864.0 + '@aws-sdk/types': 3.862.0 + '@aws-sdk/util-endpoints': 3.862.0 + '@smithy/core': 3.8.0 + '@smithy/protocol-http': 5.1.3 + '@smithy/types': 4.3.2 + tslib: 2.8.1 + + '@aws-sdk/nested-clients@3.864.0': + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.864.0 + '@aws-sdk/middleware-host-header': 3.862.0 + '@aws-sdk/middleware-logger': 3.862.0 + '@aws-sdk/middleware-recursion-detection': 3.862.0 + '@aws-sdk/middleware-user-agent': 3.864.0 + '@aws-sdk/region-config-resolver': 3.862.0 + '@aws-sdk/types': 3.862.0 + '@aws-sdk/util-endpoints': 3.862.0 + '@aws-sdk/util-user-agent-browser': 3.862.0 + '@aws-sdk/util-user-agent-node': 3.864.0 + '@smithy/config-resolver': 4.1.5 + '@smithy/core': 3.8.0 + '@smithy/fetch-http-handler': 5.1.1 + '@smithy/hash-node': 4.0.5 + '@smithy/invalid-dependency': 4.0.5 + '@smithy/middleware-content-length': 4.0.5 + '@smithy/middleware-endpoint': 4.1.18 + '@smithy/middleware-retry': 4.1.19 + '@smithy/middleware-serde': 4.0.9 + '@smithy/middleware-stack': 4.0.5 + '@smithy/node-config-provider': 4.1.4 + '@smithy/node-http-handler': 4.1.1 + '@smithy/protocol-http': 5.1.3 + '@smithy/smithy-client': 4.4.10 + '@smithy/types': 4.3.2 + '@smithy/url-parser': 4.0.5 + '@smithy/util-base64': 4.0.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-body-length-node': 4.0.0 + '@smithy/util-defaults-mode-browser': 4.0.26 + '@smithy/util-defaults-mode-node': 4.0.26 + '@smithy/util-endpoints': 3.0.7 + '@smithy/util-middleware': 4.0.5 + '@smithy/util-retry': 4.0.7 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + + '@aws-sdk/region-config-resolver@3.862.0': + dependencies: + '@aws-sdk/types': 3.862.0 + '@smithy/node-config-provider': 4.1.4 + '@smithy/types': 4.3.2 + '@smithy/util-config-provider': 4.0.0 + '@smithy/util-middleware': 4.0.5 + tslib: 2.8.1 + + '@aws-sdk/token-providers@3.864.0': + dependencies: + '@aws-sdk/core': 3.864.0 + '@aws-sdk/nested-clients': 3.864.0 + '@aws-sdk/types': 3.862.0 + '@smithy/property-provider': 4.0.5 + '@smithy/shared-ini-file-loader': 4.0.5 + '@smithy/types': 4.3.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + '@aws-sdk/types@3.821.0': dependencies: '@smithy/types': 4.3.1 tslib: 2.8.1 + '@aws-sdk/types@3.862.0': + dependencies: + '@smithy/types': 4.3.2 + tslib: 2.8.1 + + '@aws-sdk/util-endpoints@3.862.0': + dependencies: + '@aws-sdk/types': 3.862.0 + '@smithy/types': 4.3.2 + '@smithy/url-parser': 4.0.5 + '@smithy/util-endpoints': 3.0.7 + tslib: 2.8.1 + + '@aws-sdk/util-locate-window@3.804.0': + dependencies: + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-browser@3.862.0': + dependencies: + '@aws-sdk/types': 3.862.0 + '@smithy/types': 4.3.2 + bowser: 2.12.0 + tslib: 2.8.1 + + '@aws-sdk/util-user-agent-node@3.864.0': + dependencies: + '@aws-sdk/middleware-user-agent': 3.864.0 + '@aws-sdk/types': 3.862.0 + '@smithy/node-config-provider': 4.1.4 + '@smithy/types': 4.3.2 + tslib: 2.8.1 + '@aws-sdk/util-utf8-browser@3.259.0': dependencies: tslib: 2.8.1 + '@aws-sdk/xml-builder@3.862.0': + dependencies: + '@smithy/types': 4.3.2 + tslib: 2.8.1 + '@babel/code-frame@7.27.1': dependencies: '@babel/helper-validator-identifier': 7.27.1 @@ -11841,10 +12889,10 @@ snapshots: '@commitlint/execute-rule': 16.2.1 '@commitlint/resolve-extends': 16.2.1 '@commitlint/types': 16.2.1 - '@types/node': 20.17.58 + '@types/node': 20.19.9 chalk: 4.1.2 cosmiconfig: 7.1.0 - cosmiconfig-typescript-loader: 2.0.2(@types/node@20.17.58)(cosmiconfig@7.1.0)(typescript@5.8.3) + cosmiconfig-typescript-loader: 2.0.2(@types/node@20.19.9)(cosmiconfig@7.1.0)(typescript@5.8.3) lodash: 4.17.21 resolve-from: 5.0.0 typescript: 5.8.3 @@ -12386,6 +13434,14 @@ snapshots: '@ethersproject/logger': 5.8.0 '@ethersproject/rlp': 5.8.0 + '@ethersproject/address@5.6.1': + dependencies: + '@ethersproject/bignumber': 5.8.0 + '@ethersproject/bytes': 5.8.0 + '@ethersproject/keccak256': 5.8.0 + '@ethersproject/logger': 5.8.0 + '@ethersproject/rlp': 5.8.0 + '@ethersproject/address@5.7.0': dependencies: '@ethersproject/bignumber': 5.8.0 @@ -14174,7 +15230,7 @@ snapshots: '@manypkg/find-root@1.1.0': dependencies: '@babel/runtime': 7.27.6 - '@types/node': 20.17.58 + '@types/node': 20.19.9 find-up: 4.1.0 fs-extra: 8.1.0 @@ -14187,6 +15243,10 @@ snapshots: globby: 11.1.0 read-yaml-file: 1.1.0 + '@noble/curves@1.2.0': + dependencies: + '@noble/hashes': 1.3.2 + '@noble/curves@1.4.2': dependencies: '@noble/hashes': 1.4.0 @@ -14197,6 +15257,8 @@ snapshots: '@noble/hashes@1.2.0': {} + '@noble/hashes@1.3.2': {} + '@noble/hashes@1.4.0': {} '@noble/hashes@1.7.2': {} @@ -14248,6 +15310,17 @@ snapshots: '@nomicfoundation/ethereumjs-rlp': 5.0.4 ethereum-cryptography: 0.1.3 + '@nomicfoundation/hardhat-chai-matchers@2.1.0(@nomicfoundation/hardhat-ethers@3.0.9(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)))(chai@4.5.0)(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))': + dependencies: + '@nomicfoundation/hardhat-ethers': 3.0.9(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@types/chai-as-promised': 7.1.8 + chai: 4.5.0 + chai-as-promised: 7.1.2(chai@4.5.0) + deep-eql: 4.1.4 + ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + hardhat: 2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) + ordinal: 1.0.3 + '@nomicfoundation/hardhat-ethers@3.0.9(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.17.58)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))': dependencies: debug: 4.4.1(supports-color@9.4.0) @@ -14257,11 +15330,108 @@ snapshots: transitivePeerDependencies: - supports-color + '@nomicfoundation/hardhat-ethers@3.0.9(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))': + dependencies: + debug: 4.4.1(supports-color@9.4.0) + ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + hardhat: 2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) + lodash.isequal: 4.5.0 + transitivePeerDependencies: + - supports-color + + '@nomicfoundation/hardhat-foundry@1.2.0(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))': + dependencies: + hardhat: 2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) + picocolors: 1.1.1 + + '@nomicfoundation/hardhat-ignition-ethers@0.15.14(@nomicfoundation/hardhat-ethers@3.0.9(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)))(@nomicfoundation/hardhat-ignition@0.15.13(@nomicfoundation/hardhat-verify@2.1.1(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@nomicfoundation/ignition-core@0.15.13(bufferutil@4.0.9)(utf-8-validate@5.0.10))(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))': + dependencies: + '@nomicfoundation/hardhat-ethers': 3.0.9(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@nomicfoundation/hardhat-ignition': 0.15.13(@nomicfoundation/hardhat-verify@2.1.1(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) + '@nomicfoundation/ignition-core': 0.15.13(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + hardhat: 2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) + + '@nomicfoundation/hardhat-ignition@0.15.13(@nomicfoundation/hardhat-verify@2.1.1(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)': + dependencies: + '@nomicfoundation/hardhat-verify': 2.1.1(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@nomicfoundation/ignition-core': 0.15.13(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@nomicfoundation/ignition-ui': 0.15.12 + chalk: 4.1.2 + debug: 4.4.1(supports-color@9.4.0) + fs-extra: 10.1.0 + hardhat: 2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) + json5: 2.2.3 + prompts: 2.4.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + '@nomicfoundation/hardhat-network-helpers@1.0.12(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.17.58)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))': dependencies: ethereumjs-util: 7.1.5 hardhat: 2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.17.58)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) + '@nomicfoundation/hardhat-network-helpers@1.0.12(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))': + dependencies: + ethereumjs-util: 7.1.5 + hardhat: 2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) + + '@nomicfoundation/hardhat-toolbox@5.0.0(d805b92a490ff7e353d8006b7d043405)': + dependencies: + '@nomicfoundation/hardhat-chai-matchers': 2.1.0(@nomicfoundation/hardhat-ethers@3.0.9(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)))(chai@4.5.0)(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@nomicfoundation/hardhat-ethers': 3.0.9(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@nomicfoundation/hardhat-ignition-ethers': 0.15.14(@nomicfoundation/hardhat-ethers@3.0.9(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)))(@nomicfoundation/hardhat-ignition@0.15.13(@nomicfoundation/hardhat-verify@2.1.1(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@nomicfoundation/ignition-core@0.15.13(bufferutil@4.0.9)(utf-8-validate@5.0.10))(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@nomicfoundation/hardhat-network-helpers': 1.0.12(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@nomicfoundation/hardhat-verify': 2.1.1(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@typechain/ethers-v6': 0.5.1(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typechain@8.3.2(patch_hash=b34ed6afcf99760666fdc85ecb2094fdd20ce509f947eb09cef21665a2a6a1d6)(typescript@5.8.3))(typescript@5.8.3) + '@typechain/hardhat': 9.1.0(@typechain/ethers-v6@0.5.1(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typechain@8.3.2(patch_hash=b34ed6afcf99760666fdc85ecb2094fdd20ce509f947eb09cef21665a2a6a1d6)(typescript@5.8.3))(typescript@5.8.3))(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))(typechain@8.3.2(patch_hash=b34ed6afcf99760666fdc85ecb2094fdd20ce509f947eb09cef21665a2a6a1d6)(typescript@5.8.3)) + '@types/chai': 4.3.20 + '@types/mocha': 10.0.10 + '@types/node': 20.19.9 + chai: 4.5.0 + ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + hardhat: 2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) + hardhat-gas-reporter: 1.0.10(bufferutil@4.0.9)(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) + solidity-coverage: 0.8.16(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) + ts-node: 10.9.2(@types/node@20.19.9)(typescript@5.8.3) + typechain: 8.3.2(patch_hash=b34ed6afcf99760666fdc85ecb2094fdd20ce509f947eb09cef21665a2a6a1d6)(typescript@5.8.3) + typescript: 5.8.3 + + '@nomicfoundation/hardhat-verify@2.1.1(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))': + dependencies: + '@ethersproject/abi': 5.8.0 + '@ethersproject/address': 5.8.0 + cbor: 8.1.0 + debug: 4.4.1(supports-color@9.4.0) + hardhat: 2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) + lodash.clonedeep: 4.5.0 + picocolors: 1.1.1 + semver: 6.3.1 + table: 6.9.0 + undici: 5.29.0 + transitivePeerDependencies: + - supports-color + + '@nomicfoundation/ignition-core@0.15.13(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + dependencies: + '@ethersproject/address': 5.6.1 + '@nomicfoundation/solidity-analyzer': 0.1.2 + cbor: 9.0.2 + debug: 4.4.1(supports-color@9.4.0) + ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + fs-extra: 10.1.0 + immer: 10.0.2 + lodash: 4.17.21 + ndjson: 2.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + '@nomicfoundation/ignition-ui@0.15.12': {} + '@nomicfoundation/slang@0.18.3': dependencies: '@bytecodealliance/preview2-shim': 0.17.0 @@ -14377,6 +15547,52 @@ snapshots: - debug - encoding + '@openzeppelin/defender-deploy-client-cli@0.0.1-alpha.10(encoding@0.1.13)': + dependencies: + '@openzeppelin/defender-sdk-base-client': 2.7.0(encoding@0.1.13) + '@openzeppelin/defender-sdk-deploy-client': 2.7.0(debug@4.4.1)(encoding@0.1.13) + '@openzeppelin/defender-sdk-network-client': 2.7.0(debug@4.4.1)(encoding@0.1.13) + dotenv: 16.5.0 + minimist: 1.2.8 + transitivePeerDependencies: + - aws-crt + - debug + - encoding + + '@openzeppelin/defender-sdk-base-client@2.7.0(encoding@0.1.13)': + dependencies: + '@aws-sdk/client-lambda': 3.865.0 + amazon-cognito-identity-js: 6.3.15(encoding@0.1.13) + async-retry: 1.3.3 + transitivePeerDependencies: + - aws-crt + - encoding + + '@openzeppelin/defender-sdk-deploy-client@2.7.0(debug@4.4.1)(encoding@0.1.13)': + dependencies: + '@openzeppelin/defender-sdk-base-client': 2.7.0(encoding@0.1.13) + axios: 1.9.0(debug@4.4.1) + lodash: 4.17.21 + transitivePeerDependencies: + - aws-crt + - debug + - encoding + + '@openzeppelin/defender-sdk-network-client@2.7.0(debug@4.4.1)(encoding@0.1.13)': + dependencies: + '@openzeppelin/defender-sdk-base-client': 2.7.0(encoding@0.1.13) + axios: 1.9.0(debug@4.4.1) + lodash: 4.17.21 + transitivePeerDependencies: + - aws-crt + - debug + - encoding + + '@openzeppelin/foundry-upgrades@0.4.0(@openzeppelin/defender-deploy-client-cli@0.0.1-alpha.10(encoding@0.1.13))(@openzeppelin/upgrades-core@1.44.1)': + dependencies: + '@openzeppelin/defender-deploy-client-cli': 0.0.1-alpha.10(encoding@0.1.13) + '@openzeppelin/upgrades-core': 1.44.1 + '@openzeppelin/hardhat-upgrades@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.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.17.58)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)))(@nomiclabs/hardhat-etherscan@3.1.8(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.17.58)(typescript@5.8.3))(typescript@5.8.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.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.17.58)(typescript@5.8.3))(typescript@5.8.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.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.17.58)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) @@ -14393,6 +15609,27 @@ snapshots: - encoding - supports-color + '@openzeppelin/hardhat-upgrades@3.9.1(@nomicfoundation/hardhat-ethers@3.0.9(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)))(@nomicfoundation/hardhat-verify@2.1.1(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)))(encoding@0.1.13)(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))': + dependencies: + '@nomicfoundation/hardhat-ethers': 3.0.9(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) + '@openzeppelin/defender-sdk-base-client': 2.7.0(encoding@0.1.13) + '@openzeppelin/defender-sdk-deploy-client': 2.7.0(debug@4.4.1)(encoding@0.1.13) + '@openzeppelin/defender-sdk-network-client': 2.7.0(debug@4.4.1)(encoding@0.1.13) + '@openzeppelin/upgrades-core': 1.44.1 + chalk: 4.1.2 + debug: 4.4.1(supports-color@9.4.0) + ethereumjs-util: 7.1.5 + ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + hardhat: 2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) + proper-lockfile: 4.1.2 + undici: 6.21.3 + optionalDependencies: + '@nomicfoundation/hardhat-verify': 2.1.1(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) + transitivePeerDependencies: + - aws-crt + - encoding + - supports-color + '@openzeppelin/platform-deploy-client@0.8.0(debug@4.4.1)(encoding@0.1.13)': dependencies: '@ethersproject/abi': 5.8.0 @@ -14629,55 +15866,363 @@ snapshots: '@sentry/types': 5.30.0 tslib: 1.14.1 - '@sentry/node@5.30.0': + '@sentry/node@5.30.0': + dependencies: + '@sentry/core': 5.30.0 + '@sentry/hub': 5.30.0 + '@sentry/tracing': 5.30.0 + '@sentry/types': 5.30.0 + '@sentry/utils': 5.30.0 + cookie: 0.4.2 + https-proxy-agent: 5.0.1 + lru_map: 0.3.3 + tslib: 1.14.1 + transitivePeerDependencies: + - supports-color + + '@sentry/tracing@5.30.0': + dependencies: + '@sentry/hub': 5.30.0 + '@sentry/minimal': 5.30.0 + '@sentry/types': 5.30.0 + '@sentry/utils': 5.30.0 + tslib: 1.14.1 + + '@sentry/types@5.30.0': {} + + '@sentry/utils@5.30.0': + dependencies: + '@sentry/types': 5.30.0 + tslib: 1.14.1 + + '@sinclair/typebox@0.27.8': {} + + '@sindresorhus/is@0.14.0': + optional: true + + '@sindresorhus/is@4.6.0': + optional: true + + '@sindresorhus/is@5.6.0': {} + + '@sinonjs/commons@3.0.1': + dependencies: + type-detect: 4.0.8 + + '@sinonjs/fake-timers@10.3.0': + dependencies: + '@sinonjs/commons': 3.0.1 + + '@smithy/abort-controller@4.0.5': + dependencies: + '@smithy/types': 4.3.2 + tslib: 2.8.1 + + '@smithy/config-resolver@4.1.5': + dependencies: + '@smithy/node-config-provider': 4.1.4 + '@smithy/types': 4.3.2 + '@smithy/util-config-provider': 4.0.0 + '@smithy/util-middleware': 4.0.5 + tslib: 2.8.1 + + '@smithy/core@3.8.0': + dependencies: + '@smithy/middleware-serde': 4.0.9 + '@smithy/protocol-http': 5.1.3 + '@smithy/types': 4.3.2 + '@smithy/util-base64': 4.0.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-middleware': 4.0.5 + '@smithy/util-stream': 4.2.4 + '@smithy/util-utf8': 4.0.0 + '@types/uuid': 9.0.8 + tslib: 2.8.1 + uuid: 9.0.1 + + '@smithy/credential-provider-imds@4.0.7': + dependencies: + '@smithy/node-config-provider': 4.1.4 + '@smithy/property-provider': 4.0.5 + '@smithy/types': 4.3.2 + '@smithy/url-parser': 4.0.5 + tslib: 2.8.1 + + '@smithy/eventstream-codec@4.0.5': + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@smithy/types': 4.3.2 + '@smithy/util-hex-encoding': 4.0.0 + tslib: 2.8.1 + + '@smithy/eventstream-serde-browser@4.0.5': + dependencies: + '@smithy/eventstream-serde-universal': 4.0.5 + '@smithy/types': 4.3.2 + tslib: 2.8.1 + + '@smithy/eventstream-serde-config-resolver@4.1.3': + dependencies: + '@smithy/types': 4.3.2 + tslib: 2.8.1 + + '@smithy/eventstream-serde-node@4.0.5': + dependencies: + '@smithy/eventstream-serde-universal': 4.0.5 + '@smithy/types': 4.3.2 + tslib: 2.8.1 + + '@smithy/eventstream-serde-universal@4.0.5': + dependencies: + '@smithy/eventstream-codec': 4.0.5 + '@smithy/types': 4.3.2 + tslib: 2.8.1 + + '@smithy/fetch-http-handler@5.1.1': + dependencies: + '@smithy/protocol-http': 5.1.3 + '@smithy/querystring-builder': 4.0.5 + '@smithy/types': 4.3.2 + '@smithy/util-base64': 4.0.0 + tslib: 2.8.1 + + '@smithy/hash-node@4.0.5': + dependencies: + '@smithy/types': 4.3.2 + '@smithy/util-buffer-from': 4.0.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + + '@smithy/invalid-dependency@4.0.5': + dependencies: + '@smithy/types': 4.3.2 + tslib: 2.8.1 + + '@smithy/is-array-buffer@2.2.0': + dependencies: + tslib: 2.8.1 + + '@smithy/is-array-buffer@4.0.0': + dependencies: + tslib: 2.8.1 + + '@smithy/middleware-content-length@4.0.5': + dependencies: + '@smithy/protocol-http': 5.1.3 + '@smithy/types': 4.3.2 + tslib: 2.8.1 + + '@smithy/middleware-endpoint@4.1.18': + dependencies: + '@smithy/core': 3.8.0 + '@smithy/middleware-serde': 4.0.9 + '@smithy/node-config-provider': 4.1.4 + '@smithy/shared-ini-file-loader': 4.0.5 + '@smithy/types': 4.3.2 + '@smithy/url-parser': 4.0.5 + '@smithy/util-middleware': 4.0.5 + tslib: 2.8.1 + + '@smithy/middleware-retry@4.1.19': + dependencies: + '@smithy/node-config-provider': 4.1.4 + '@smithy/protocol-http': 5.1.3 + '@smithy/service-error-classification': 4.0.7 + '@smithy/smithy-client': 4.4.10 + '@smithy/types': 4.3.2 + '@smithy/util-middleware': 4.0.5 + '@smithy/util-retry': 4.0.7 + '@types/uuid': 9.0.8 + tslib: 2.8.1 + uuid: 9.0.1 + + '@smithy/middleware-serde@4.0.9': + dependencies: + '@smithy/protocol-http': 5.1.3 + '@smithy/types': 4.3.2 + tslib: 2.8.1 + + '@smithy/middleware-stack@4.0.5': + dependencies: + '@smithy/types': 4.3.2 + tslib: 2.8.1 + + '@smithy/node-config-provider@4.1.4': + dependencies: + '@smithy/property-provider': 4.0.5 + '@smithy/shared-ini-file-loader': 4.0.5 + '@smithy/types': 4.3.2 + tslib: 2.8.1 + + '@smithy/node-http-handler@4.1.1': + dependencies: + '@smithy/abort-controller': 4.0.5 + '@smithy/protocol-http': 5.1.3 + '@smithy/querystring-builder': 4.0.5 + '@smithy/types': 4.3.2 + tslib: 2.8.1 + + '@smithy/property-provider@4.0.5': + dependencies: + '@smithy/types': 4.3.2 + tslib: 2.8.1 + + '@smithy/protocol-http@5.1.3': + dependencies: + '@smithy/types': 4.3.2 + tslib: 2.8.1 + + '@smithy/querystring-builder@4.0.5': + dependencies: + '@smithy/types': 4.3.2 + '@smithy/util-uri-escape': 4.0.0 + tslib: 2.8.1 + + '@smithy/querystring-parser@4.0.5': + dependencies: + '@smithy/types': 4.3.2 + tslib: 2.8.1 + + '@smithy/service-error-classification@4.0.7': + dependencies: + '@smithy/types': 4.3.2 + + '@smithy/shared-ini-file-loader@4.0.5': + dependencies: + '@smithy/types': 4.3.2 + tslib: 2.8.1 + + '@smithy/signature-v4@5.1.3': + dependencies: + '@smithy/is-array-buffer': 4.0.0 + '@smithy/protocol-http': 5.1.3 + '@smithy/types': 4.3.2 + '@smithy/util-hex-encoding': 4.0.0 + '@smithy/util-middleware': 4.0.5 + '@smithy/util-uri-escape': 4.0.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + + '@smithy/smithy-client@4.4.10': + dependencies: + '@smithy/core': 3.8.0 + '@smithy/middleware-endpoint': 4.1.18 + '@smithy/middleware-stack': 4.0.5 + '@smithy/protocol-http': 5.1.3 + '@smithy/types': 4.3.2 + '@smithy/util-stream': 4.2.4 + tslib: 2.8.1 + + '@smithy/types@4.3.1': + dependencies: + tslib: 2.8.1 + + '@smithy/types@4.3.2': + dependencies: + tslib: 2.8.1 + + '@smithy/url-parser@4.0.5': + dependencies: + '@smithy/querystring-parser': 4.0.5 + '@smithy/types': 4.3.2 + tslib: 2.8.1 + + '@smithy/util-base64@4.0.0': + dependencies: + '@smithy/util-buffer-from': 4.0.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + + '@smithy/util-body-length-browser@4.0.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-body-length-node@4.0.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-buffer-from@2.2.0': + dependencies: + '@smithy/is-array-buffer': 2.2.0 + tslib: 2.8.1 + + '@smithy/util-buffer-from@4.0.0': + dependencies: + '@smithy/is-array-buffer': 4.0.0 + tslib: 2.8.1 + + '@smithy/util-config-provider@4.0.0': + dependencies: + tslib: 2.8.1 + + '@smithy/util-defaults-mode-browser@4.0.26': dependencies: - '@sentry/core': 5.30.0 - '@sentry/hub': 5.30.0 - '@sentry/tracing': 5.30.0 - '@sentry/types': 5.30.0 - '@sentry/utils': 5.30.0 - cookie: 0.4.2 - https-proxy-agent: 5.0.1 - lru_map: 0.3.3 - tslib: 1.14.1 - transitivePeerDependencies: - - supports-color + '@smithy/property-provider': 4.0.5 + '@smithy/smithy-client': 4.4.10 + '@smithy/types': 4.3.2 + bowser: 2.12.0 + tslib: 2.8.1 - '@sentry/tracing@5.30.0': + '@smithy/util-defaults-mode-node@4.0.26': dependencies: - '@sentry/hub': 5.30.0 - '@sentry/minimal': 5.30.0 - '@sentry/types': 5.30.0 - '@sentry/utils': 5.30.0 - tslib: 1.14.1 + '@smithy/config-resolver': 4.1.5 + '@smithy/credential-provider-imds': 4.0.7 + '@smithy/node-config-provider': 4.1.4 + '@smithy/property-provider': 4.0.5 + '@smithy/smithy-client': 4.4.10 + '@smithy/types': 4.3.2 + tslib: 2.8.1 - '@sentry/types@5.30.0': {} + '@smithy/util-endpoints@3.0.7': + dependencies: + '@smithy/node-config-provider': 4.1.4 + '@smithy/types': 4.3.2 + tslib: 2.8.1 - '@sentry/utils@5.30.0': + '@smithy/util-hex-encoding@4.0.0': dependencies: - '@sentry/types': 5.30.0 - tslib: 1.14.1 + tslib: 2.8.1 - '@sinclair/typebox@0.27.8': {} + '@smithy/util-middleware@4.0.5': + dependencies: + '@smithy/types': 4.3.2 + tslib: 2.8.1 - '@sindresorhus/is@0.14.0': - optional: true + '@smithy/util-retry@4.0.7': + dependencies: + '@smithy/service-error-classification': 4.0.7 + '@smithy/types': 4.3.2 + tslib: 2.8.1 - '@sindresorhus/is@4.6.0': - optional: true + '@smithy/util-stream@4.2.4': + dependencies: + '@smithy/fetch-http-handler': 5.1.1 + '@smithy/node-http-handler': 4.1.1 + '@smithy/types': 4.3.2 + '@smithy/util-base64': 4.0.0 + '@smithy/util-buffer-from': 4.0.0 + '@smithy/util-hex-encoding': 4.0.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 - '@sindresorhus/is@5.6.0': {} + '@smithy/util-uri-escape@4.0.0': + dependencies: + tslib: 2.8.1 - '@sinonjs/commons@3.0.1': + '@smithy/util-utf8@2.3.0': dependencies: - type-detect: 4.0.8 + '@smithy/util-buffer-from': 2.2.0 + tslib: 2.8.1 - '@sinonjs/fake-timers@10.3.0': + '@smithy/util-utf8@4.0.0': dependencies: - '@sinonjs/commons': 3.0.1 + '@smithy/util-buffer-from': 4.0.0 + tslib: 2.8.1 - '@smithy/types@4.3.1': + '@smithy/util-waiter@4.0.7': dependencies: + '@smithy/abort-controller': 4.0.5 + '@smithy/types': 4.3.2 tslib: 2.8.1 '@solidity-parser/parser@0.14.5': @@ -14788,6 +16333,14 @@ snapshots: ethers: 5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) typechain: 3.0.0(typescript@5.8.3) + '@typechain/ethers-v6@0.5.1(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typechain@8.3.2(patch_hash=b34ed6afcf99760666fdc85ecb2094fdd20ce509f947eb09cef21665a2a6a1d6)(typescript@5.8.3))(typescript@5.8.3)': + dependencies: + ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + lodash: 4.17.21 + ts-essentials: 7.0.3(typescript@5.8.3) + typechain: 8.3.2(patch_hash=b34ed6afcf99760666fdc85ecb2094fdd20ce509f947eb09cef21665a2a6a1d6)(typescript@5.8.3) + typescript: 5.8.3 + '@typechain/hardhat@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.8.3))(typescript@5.8.3))(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.17.58)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))(typechain@8.3.2(patch_hash=b34ed6afcf99760666fdc85ecb2094fdd20ce509f947eb09cef21665a2a6a1d6)(typescript@5.8.3))': dependencies: '@ethersproject/abi': 5.8.0 @@ -14798,6 +16351,14 @@ snapshots: hardhat: 2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.17.58)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) typechain: 8.3.2(patch_hash=b34ed6afcf99760666fdc85ecb2094fdd20ce509f947eb09cef21665a2a6a1d6)(typescript@5.8.3) + '@typechain/hardhat@9.1.0(@typechain/ethers-v6@0.5.1(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typechain@8.3.2(patch_hash=b34ed6afcf99760666fdc85ecb2094fdd20ce509f947eb09cef21665a2a6a1d6)(typescript@5.8.3))(typescript@5.8.3))(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))(typechain@8.3.2(patch_hash=b34ed6afcf99760666fdc85ecb2094fdd20ce509f947eb09cef21665a2a6a1d6)(typescript@5.8.3))': + dependencies: + '@typechain/ethers-v6': 0.5.1(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(typechain@8.3.2(patch_hash=b34ed6afcf99760666fdc85ecb2094fdd20ce509f947eb09cef21665a2a6a1d6)(typescript@5.8.3))(typescript@5.8.3) + ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + fs-extra: 9.1.0 + hardhat: 2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) + typechain: 8.3.2(patch_hash=b34ed6afcf99760666fdc85ecb2094fdd20ce509f947eb09cef21665a2a6a1d6)(typescript@5.8.3) + '@types/abstract-leveldown@7.2.5': {} '@types/babel__core@7.20.5': @@ -14823,17 +16384,17 @@ snapshots: '@types/bn.js@4.11.6': dependencies: - '@types/node': 20.17.58 + '@types/node': 20.19.9 '@types/bn.js@5.2.0': dependencies: - '@types/node': 20.17.58 + '@types/node': 20.19.9 '@types/cacheable-request@6.0.3': dependencies: '@types/http-cache-semantics': 4.0.4 '@types/keyv': 3.1.4 - '@types/node': 20.17.58 + '@types/node': 20.19.9 '@types/responselike': 1.0.3 optional: true @@ -14845,11 +16406,11 @@ snapshots: '@types/concat-stream@1.6.1': dependencies: - '@types/node': 20.17.58 + '@types/node': 20.19.9 '@types/conventional-commits-parser@5.0.1': dependencies: - '@types/node': 20.17.58 + '@types/node': 20.19.9 '@types/debug@4.1.12': dependencies: @@ -14859,12 +16420,12 @@ snapshots: '@types/form-data@0.0.33': dependencies: - '@types/node': 20.17.58 + '@types/node': 20.19.9 '@types/glob@7.2.0': dependencies: '@types/minimatch': 5.1.2 - '@types/node': 20.17.58 + '@types/node': 20.19.9 '@types/glob@8.1.0': dependencies: @@ -14900,7 +16461,7 @@ snapshots: '@types/keyv@3.1.4': dependencies: - '@types/node': 20.17.58 + '@types/node': 20.19.9 optional: true '@types/level-errors@3.0.2': {} @@ -14909,7 +16470,7 @@ snapshots: dependencies: '@types/abstract-leveldown': 7.2.5 '@types/level-errors': 3.0.2 - '@types/node': 20.17.58 + '@types/node': 20.19.9 '@types/lodash@4.17.17': {} @@ -14925,7 +16486,7 @@ snapshots: '@types/mkdirp@0.5.2': dependencies: - '@types/node': 20.17.58 + '@types/node': 20.19.9 '@types/mocha@10.0.10': {} @@ -14935,7 +16496,7 @@ snapshots: '@types/node-fetch@2.6.12': dependencies: - '@types/node': 20.17.58 + '@types/node': 20.19.9 form-data: 4.0.3 '@types/node@20.17.58': @@ -14952,7 +16513,7 @@ snapshots: '@types/pbkdf2@3.1.2': dependencies: - '@types/node': 20.17.58 + '@types/node': 20.19.9 '@types/prettier@2.7.3': {} @@ -14960,16 +16521,16 @@ snapshots: '@types/resolve@0.0.8': dependencies: - '@types/node': 20.17.58 + '@types/node': 20.19.9 '@types/responselike@1.0.3': dependencies: - '@types/node': 20.17.58 + '@types/node': 20.19.9 optional: true '@types/secp256k1@4.0.6': dependencies: - '@types/node': 20.17.58 + '@types/node': 20.19.9 '@types/sinon-chai@3.2.12': dependencies: @@ -14986,17 +16547,19 @@ snapshots: '@types/through@0.0.33': dependencies: - '@types/node': 20.17.58 + '@types/node': 20.19.9 '@types/triple-beam@1.3.5': {} '@types/unist@2.0.11': {} + '@types/uuid@9.0.8': {} + '@types/validator@13.15.1': {} '@types/ws@8.18.1': dependencies: - '@types/node': 20.17.58 + '@types/node': 20.19.9 '@types/yargs-parser@21.0.3': {} @@ -15251,6 +16814,8 @@ snapshots: aes-js@3.1.2: optional: true + aes-js@4.0.0-beta.5: {} + agent-base@6.0.2: dependencies: debug: 4.4.1(supports-color@9.4.0) @@ -16159,7 +17724,7 @@ snapshots: bip39@3.0.4: dependencies: - '@types/node': 20.17.58 + '@types/node': 20.19.9 create-hash: 1.2.0 pbkdf2: 3.1.2 randombytes: 2.1.0 @@ -16263,6 +17828,8 @@ snapshots: - supports-color optional: true + bowser@2.12.0: {} + boxen@5.1.2: dependencies: ansi-align: 3.0.1 @@ -16574,6 +18141,10 @@ snapshots: dependencies: nofilter: 3.1.0 + cbor@9.0.2: + dependencies: + nofilter: 3.1.0 + chai-as-promised@7.1.2(chai@4.5.0): dependencies: chai: 4.5.0 @@ -17052,11 +18623,11 @@ snapshots: object-assign: 4.1.1 vary: 1.1.2 - cosmiconfig-typescript-loader@2.0.2(@types/node@20.17.58)(cosmiconfig@7.1.0)(typescript@5.8.3): + cosmiconfig-typescript-loader@2.0.2(@types/node@20.19.9)(cosmiconfig@7.1.0)(typescript@5.8.3): dependencies: - '@types/node': 20.17.58 + '@types/node': 20.19.9 cosmiconfig: 7.1.0 - ts-node: 10.9.2(@types/node@20.17.58)(typescript@5.8.3) + ts-node: 10.9.2(@types/node@20.19.9)(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: - '@swc/core' @@ -18317,6 +19888,19 @@ snapshots: - bufferutil - utf-8-validate + ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): + dependencies: + '@adraffy/ens-normalize': 1.10.1 + '@noble/curves': 1.2.0 + '@noble/hashes': 1.3.2 + '@types/node': 20.19.9 + aes-js: 4.0.0-beta.5 + tslib: 2.7.0 + ws: 8.17.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - utf-8-validate + ethjs-unit@0.1.6: dependencies: bn.js: 4.11.6 @@ -18601,6 +20185,10 @@ snapshots: dependencies: punycode: 1.4.1 + fast-xml-parser@5.2.5: + dependencies: + strnum: 2.1.1 + fastify-warning@0.2.0: {} fastq@1.19.1: @@ -18810,6 +20398,8 @@ snapshots: forever-agent@0.6.1: {} + forge-std@https://github.com/foundry-rs/forge-std/tarball/v1.9.7: {} + form-data-encoder@2.1.4: {} form-data@2.3.3: @@ -19362,6 +20952,13 @@ snapshots: hardhat: 2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.17.58)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) strip-ansi: 6.0.1 + hardhat-contract-sizer@2.10.0(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)): + dependencies: + chalk: 4.1.2 + cli-table3: 0.6.5 + hardhat: 2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) + strip-ansi: 6.0.1 + hardhat-deploy@0.11.45(bufferutil@4.0.9)(utf-8-validate@5.0.10): dependencies: '@ethersproject/abi': 5.8.0 @@ -19434,6 +21031,18 @@ snapshots: - debug - utf-8-validate + hardhat-gas-reporter@1.0.10(bufferutil@4.0.9)(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10): + dependencies: + array-uniq: 1.0.3 + eth-gas-reporter: 0.2.27(bufferutil@4.0.9)(utf-8-validate@5.0.10) + hardhat: 2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) + sha1: 1.1.1 + transitivePeerDependencies: + - '@codechecks/client' + - bufferutil + - debug + - utf-8-validate + 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.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.17.58)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)))(ethers@5.8.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.17.58)(typescript@5.8.3))(typescript@5.8.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.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.17.58)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) @@ -19458,11 +21067,28 @@ snapshots: transitivePeerDependencies: - supports-color + hardhat-secure-accounts@1.0.5(@nomicfoundation/hardhat-ethers@3.0.9(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)))(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)): + dependencies: + '@nomicfoundation/hardhat-ethers': 3.0.9(ethers@6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)) + debug: 4.4.1(supports-color@9.4.0) + enquirer: 2.4.1 + ethers: 6.15.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + hardhat: 2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) + lodash.clonedeep: 4.5.0 + prompt-sync: 4.2.0 + transitivePeerDependencies: + - supports-color + hardhat-storage-layout@0.1.7(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.17.58)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)): dependencies: console-table-printer: 2.14.1 hardhat: 2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.17.58)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) + hardhat-storage-layout@0.1.7(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)): + dependencies: + console-table-printer: 2.14.1 + hardhat: 2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) + hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.17.58)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10): dependencies: '@ethereumjs/util': 9.1.0 @@ -19514,6 +21140,57 @@ snapshots: - supports-color - utf-8-validate + hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10): + dependencies: + '@ethereumjs/util': 9.1.0 + '@ethersproject/abi': 5.8.0 + '@nomicfoundation/edr': 0.11.0 + '@nomicfoundation/solidity-analyzer': 0.1.2 + '@sentry/node': 5.30.0 + '@types/bn.js': 5.2.0 + '@types/lru-cache': 5.1.1 + adm-zip: 0.4.16 + aggregate-error: 3.1.0 + ansi-escapes: 4.3.2 + boxen: 5.1.2 + chokidar: 4.0.3 + ci-info: 2.0.0 + debug: 4.4.1(supports-color@9.4.0) + enquirer: 2.4.1 + env-paths: 2.2.1 + ethereum-cryptography: 1.2.0 + find-up: 5.0.0 + fp-ts: 1.19.3 + fs-extra: 7.0.1 + immutable: 4.3.7 + io-ts: 1.10.4 + json-stream-stringify: 3.1.6 + keccak: 3.0.4 + lodash: 4.17.21 + micro-eth-signer: 0.14.0 + mnemonist: 0.38.5 + mocha: 10.8.2 + p-map: 4.0.0 + picocolors: 1.1.1 + raw-body: 2.5.2 + resolve: 1.17.0 + semver: 6.3.1 + solc: 0.8.26(debug@4.4.1) + source-map-support: 0.5.21 + stacktrace-parser: 0.1.11 + tinyglobby: 0.2.14 + tsort: 0.0.1 + undici: 5.29.0 + uuid: 8.3.2 + ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) + optionalDependencies: + ts-node: 10.9.2(@types/node@20.19.9)(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + has-ansi@2.0.0: dependencies: ansi-regex: 2.1.1 @@ -19678,7 +21355,7 @@ snapshots: http-response-object@3.0.2: dependencies: - '@types/node': 20.17.58 + '@types/node': 20.19.9 http-signature@1.2.0: dependencies: @@ -19747,6 +21424,8 @@ snapshots: immediate@3.3.0: {} + immer@10.0.2: {} + immutable@3.7.6: {} immutable@4.3.7: {} @@ -21727,6 +23406,14 @@ snapshots: secure-keys: 1.0.0 yargs: 16.2.0 + ndjson@2.0.0: + dependencies: + json-stringify-safe: 5.0.1 + minimist: 1.2.8 + readable-stream: 3.6.2 + split2: 3.2.2 + through2: 4.0.2 + negotiator@0.6.3: {} negotiator@0.6.4: {} @@ -22033,6 +23720,8 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 + ordinal@1.0.3: {} + os-homedir@1.0.2: {} os-locale@1.4.0: @@ -23674,6 +25363,29 @@ snapshots: shelljs: 0.8.5 web3-utils: 1.10.4 + solidity-coverage@0.8.16(hardhat@2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10)): + dependencies: + '@ethersproject/abi': 5.8.0 + '@solidity-parser/parser': 0.20.1 + chalk: 2.4.2 + death: 1.1.0 + difflib: 0.2.4 + fs-extra: 8.1.0 + ghost-testrpc: 0.0.2 + global-modules: 2.0.0 + globby: 10.0.2 + hardhat: 2.24.2(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3))(typescript@5.8.3)(utf-8-validate@5.0.10) + jsonschema: 1.5.0 + lodash: 4.17.21 + mocha: 10.8.2 + node-emoji: 1.11.0 + pify: 4.0.1 + recursive-readdir: 2.2.3 + sc-istanbul: 0.4.6 + semver: 7.7.2 + shelljs: 0.8.5 + web3-utils: 1.10.4 + solium-plugin-security@0.1.1(solium@1.2.5): dependencies: solium: 1.2.5 @@ -23943,6 +25655,8 @@ snapshots: strip-json-comments@3.1.1: {} + strnum@2.1.1: {} + supports-color@2.0.0: {} supports-color@3.2.3: @@ -24111,7 +25825,7 @@ snapshots: dependencies: '@types/concat-stream': 1.6.1 '@types/form-data': 0.0.33 - '@types/node': 20.17.58 + '@types/node': 20.19.9 '@types/qs': 6.14.0 caseless: 0.12.0 concat-stream: 1.6.2 @@ -24276,6 +25990,24 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + ts-node@10.9.2(@types/node@20.19.9)(typescript@5.8.3): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 20.19.9 + acorn: 8.14.1 + acorn-walk: 8.3.4 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.8.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + tsconfig-paths@3.15.0: dependencies: '@types/json5': 0.0.29 @@ -24295,6 +26027,8 @@ snapshots: tslib@2.5.3: {} + tslib@2.7.0: {} + tslib@2.8.1: {} tslog@4.9.3: {} @@ -24470,6 +26204,8 @@ snapshots: dependencies: '@fastify/busboy': 2.1.1 + undici@6.21.3: {} + unfetch@4.2.0: {} unicorn-magic@0.1.0: {} @@ -24592,6 +26328,8 @@ snapshots: uuid@8.3.2: {} + uuid@9.0.1: {} + v8-compile-cache-lib@3.0.1: {} validate-npm-package-license@3.0.4: @@ -24626,7 +26364,7 @@ snapshots: web3-bzz@1.2.11(bufferutil@4.0.9)(utf-8-validate@5.0.10): dependencies: - '@types/node': 20.17.58 + '@types/node': 20.19.9 got: 9.6.0 swarm-js: 0.1.42(bufferutil@4.0.9)(utf-8-validate@5.0.10) underscore: 1.9.1 @@ -24679,7 +26417,7 @@ snapshots: web3-core@1.2.11: dependencies: '@types/bn.js': 4.11.6 - '@types/node': 20.17.58 + '@types/node': 20.19.9 bignumber.js: 9.3.0 web3-core-helpers: 1.2.11 web3-core-method: 1.2.11 @@ -24751,7 +26489,7 @@ snapshots: web3-eth-personal@1.2.11: dependencies: - '@types/node': 20.17.58 + '@types/node': 20.19.9 web3-core: 1.2.11 web3-core-helpers: 1.2.11 web3-core-method: 1.2.11 @@ -25007,7 +26745,7 @@ snapshots: wkx@0.5.0: dependencies: - '@types/node': 20.17.58 + '@types/node': 20.19.9 wonka@4.0.15: {} @@ -25101,6 +26839,11 @@ snapshots: bufferutil: 4.0.9 utf-8-validate: 5.0.10 + ws@8.17.1(bufferutil@4.0.9)(utf-8-validate@5.0.10): + optionalDependencies: + bufferutil: 4.0.9 + utf-8-validate: 5.0.10 + ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): optionalDependencies: bufferutil: 4.0.9