diff --git a/packages/contracts/contracts/SmartInvoiceUpdatable.sol b/packages/contracts/contracts/SmartInvoiceUpdatable.sol index f3b67b15..b1c17b00 100644 --- a/packages/contracts/contracts/SmartInvoiceUpdatable.sol +++ b/packages/contracts/contracts/SmartInvoiceUpdatable.sol @@ -102,7 +102,7 @@ contract SmartInvoiceUpdatable is SmartInvoiceEscrow { * @notice Handles the provided data, decodes it, and initializes necessary contract state variables. * @param _data The data to be handled and decoded. */ - function _handleData(bytes calldata _data) internal override { + function _handleData(bytes calldata _data) internal virtual override { ( address _client, uint8 _resolverType, diff --git a/packages/contracts/contracts/SmartInvoiceUpdatableV2.sol b/packages/contracts/contracts/SmartInvoiceUpdatableV2.sol new file mode 100644 index 00000000..b225fe7d --- /dev/null +++ b/packages/contracts/contracts/SmartInvoiceUpdatableV2.sol @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import {ISmartInvoiceFactory} from "./interfaces/ISmartInvoiceFactory.sol"; +import {SmartInvoiceUpdatable} from "./SmartInvoiceUpdatable.sol"; + +/// @title SmartInvoiceUpdatableV2 +/// @notice An updatable smart invoice escrow contract with embedded arbitration tailored for guild work. +contract SmartInvoiceUpdatableV2 is SmartInvoiceUpdatable { + using SafeERC20 for IERC20; + + /// @notice The receiving address for the provider. + address public clientReceiver; + + /// @dev Custom errors for more efficient gas usage. + error InvalidClientReceiver(); + + /// @notice Emitted when the client's receiver address is updated. + /// @param clientReceiver The updated client receiver address. + event UpdatedClientReceiver(address indexed clientReceiver); + + /** + * @notice Internal function for updating the client's receiver address. + * @param _clientReceiver The updated client receiver address. + */ + function _updateClientReceiver(address _clientReceiver) internal { + clientReceiver = _clientReceiver; + emit UpdatedClientReceiver(_clientReceiver); + } + + /** + * @notice Updates the client's receiver address. + * @param _clientReceiver The updated client receiver address. + */ + function updateClientReceiver(address _clientReceiver) external onlyClient { + if (_clientReceiver == address(0)) revert InvalidClientReceiver(); + _updateClientReceiver(_clientReceiver); + } + + /** + * @notice Handles the provided data, decodes it, and initializes necessary contract state variables. + * @param _data The data to be handled and decoded. + */ + function _handleData(bytes calldata _data) internal override { + ( + address _client, + uint8 _resolverType, + address _resolver, + address _token, + uint256 _terminationTime, // exact termination date in seconds since epoch + bytes32 _details, + address _wrappedNativeToken, + bool _requireVerification, + address _factory, + address _providerReceiver, + address _clientReceiver + ) = abi.decode( + _data, + ( + address, + uint8, + address, + address, + uint256, + bytes32, + address, + bool, + address, + address, + address + ) + ); + + if (_clientReceiver == address(0)) revert InvalidClientReceiver(); + if (_providerReceiver == address(0)) revert InvalidProviderReceiver(); + if (_client == address(0)) revert InvalidClient(); + if (_resolverType > uint8(ADR.ARBITRATOR)) revert InvalidResolverType(); + if (_resolver == address(0)) revert InvalidResolver(); + if (_token == address(0)) revert InvalidToken(); + if (_terminationTime <= block.timestamp) revert DurationEnded(); + if (_terminationTime > block.timestamp + MAX_TERMINATION_TIME) + revert DurationTooLong(); + if (_wrappedNativeToken == address(0)) + revert InvalidWrappedNativeToken(); + + uint256 _resolutionRate = ISmartInvoiceFactory(_factory) + .resolutionRateOf(_resolver); + if (_resolutionRate == 0) { + _resolutionRate = 20; + } + + client = _client; + resolverType = ADR(_resolverType); + resolver = _resolver; + token = _token; + terminationTime = _terminationTime; + resolutionRate = _resolutionRate; + details = _details; + wrappedNativeToken = _wrappedNativeToken; + providerReceiver = _providerReceiver; + clientReceiver = _clientReceiver; + + if (!_requireVerification) emit Verified(client, address(this)); + } + + /** + * @dev Internal function to withdraw payment to the client's receiver. + * @param _token The address of the token to transfer. + * @param _amount The amount of tokens to transfer. + */ + function _withdrawDeposit( + address _token, + uint256 _amount + ) internal virtual override { + IERC20(_token).safeTransfer(clientReceiver, _amount); + } +} diff --git a/packages/contracts/package.json b/packages/contracts/package.json index ce2a0e3c..7b56e2ae 100644 --- a/packages/contracts/package.json +++ b/packages/contracts/package.json @@ -4,7 +4,7 @@ "version": "0.1.18", "dependencies": { "@nomicfoundation/hardhat-toolbox-viem": "^3.0.0", - "@nomicfoundation/hardhat-viem": "^2.0.3", + "@nomicfoundation/hardhat-viem": "^2.0.6", "@openzeppelin/contracts": "^5.1.0", "@openzeppelin/contracts-upgradeable": "^5.1.0", "@types/chai": "4.3.16", @@ -13,7 +13,7 @@ "chai": "^4.5.0", "chai-as-promised": "^7.1.2", "dotenv": "^16.3.1", - "hardhat": "^2.22.15", + "hardhat": "^2.22.18", "hardhat-chai-matchers-viem": "^2.0.8", "hardhat-gas-reporter": "^2.2.1", "prettier-plugin-solidity": "^1.4.1", @@ -57,6 +57,7 @@ "flatten-instant": "hardhat flatten contracts/SmartInvoiceInstant.sol > flat/SmartInvoiceInstant.sol", "flatten-spoils": "hardhat flatten contracts/SpoilsManager.sol > flat/SpoilsManager.sol", "flatten-updatable": "hardhat flatten contracts/SmartInvoiceUpdatable.sol > flat/SmartInvoiceUpdatable.sol", + "flatten-updatable-v2": "hardhat flatten contracts/SmartInvoiceUpdatableV2.sol > flat/SmartInvoiceUpdatableV2.sol", "flatten-zap": "hardhat flatten contracts/SafeSplitsEscrowZap.sol > flat/SafeSplitsEscrowZap.sol", "format": "prettier --ignore-path .gitignore --write --plugin=prettier-plugin-solidity \"{*,**/*}.{ts,json,md,sol}\"", "help": "hardhat help", @@ -73,6 +74,7 @@ "test-spoils": "hardhat test ./test/SpoilsManager.ts", "test-spoils-factory": "hardhat test ./test/SpoilsManagerFactory.ts", "test-updatable": "hardhat test ./test/SmartInvoiceUpdatable.ts", + "test-updatable-v2": "hardhat test ./test/SmartInvoiceUpdatableV2.ts", "test-zap": "hardhat test ./test/SafeSplitsEscrowZap.ts", "typecheck": "tsc --noEmit", "verify-contract": "hardhat run scripts/verify-contract.ts --network" diff --git a/packages/contracts/scripts/add-implementation.ts b/packages/contracts/scripts/add-implementation.ts index fc0d2442..b4678f9d 100644 --- a/packages/contracts/scripts/add-implementation.ts +++ b/packages/contracts/scripts/add-implementation.ts @@ -35,10 +35,14 @@ const ESCROW_TYPES: EscrowTypes = { invoiceType: 'updatable', contractName: 'SmartInvoiceUpdatable', }, + 'updatable-v2': { + invoiceType: 'updatable-v2', + contractName: 'SmartInvoiceUpdatableV2', + }, }; // Select the desired escrow type -const escrowTypeData = ESCROW_TYPES.escrow; +const escrowTypeData = ESCROW_TYPES['updatable-v2']; const escrowType = toHex(toBytes(escrowTypeData.invoiceType, { size: 32 })); async function main(): Promise { diff --git a/packages/contracts/scripts/utils/types.ts b/packages/contracts/scripts/utils/types.ts index c1e6e653..826f3d4f 100644 --- a/packages/contracts/scripts/utils/types.ts +++ b/packages/contracts/scripts/utils/types.ts @@ -1,6 +1,11 @@ import { Hex } from 'viem'; -export type InvoiceType = 'escrow' | 'instant' | 'split-escrow' | 'updatable'; +export type InvoiceType = + | 'escrow' + | 'instant' + | 'split-escrow' + | 'updatable' + | 'updatable-v2'; export type SpoilsManager = { factory?: Hex; diff --git a/packages/contracts/test/SmartInvoiceUpdatableV2.ts b/packages/contracts/test/SmartInvoiceUpdatableV2.ts new file mode 100644 index 00000000..ea32e07d --- /dev/null +++ b/packages/contracts/test/SmartInvoiceUpdatableV2.ts @@ -0,0 +1,2093 @@ +import { + PublicClient, + TestClient, + WalletClient, +} from '@nomicfoundation/hardhat-viem/types'; +import { expect } from 'chai'; +import { viem } from 'hardhat'; +import { ContractTypesMap } from 'hardhat/types'; +import { + encodeAbiParameters, + getAddress, + Hex, + parseEventLogs, + toBytes, + toHex, + zeroAddress, + zeroHash, +} from 'viem'; + +import { + awaitInvoiceAddress, + createUpdatableV2Escrow, + currentTimestamp, + getBalanceOf, + getLockedUpdatableV2Escrow, + setBalanceOf, +} from './utils'; + +const individualResolverType = 0; +const arbitratorResolverType = 1; +const amounts = [BigInt(10), BigInt(10)]; +const total = amounts.reduce((t, v) => t + v, BigInt(0)); +const terminationTime = + Math.floor(new Date().getTime() / 1000) + 30 * 24 * 60 * 60; +const resolutionRate = 20n; +const requireVerification = true; +const invoiceType = toHex(toBytes('updatable-v2', { size: 32 })); + +describe('SmartInvoiceUpdatableV2', function () { + let factory: ContractTypesMap['SmartInvoiceFactory']; + let invoice: ContractTypesMap['SmartInvoiceUpdatableV2']; + let mockToken: Hex; + let otherMockToken: Hex; + let mockWrappedNativeTokenContract: ContractTypesMap['MockWETH']; + let mockWrappedNativeToken: Hex; + let mockArbitrator: Hex; + let mockArbitratorContract: ContractTypesMap['MockArbitrator']; + let client: WalletClient; + let clientReceiver: WalletClient; + let provider: WalletClient; + let providerReceiver: WalletClient; + let resolver: WalletClient; + let randomSigner: WalletClient; + let client2: WalletClient; + let clientReceiver2: WalletClient; + let provider2: WalletClient; + let providerReceiver2: WalletClient; + let publicClient: PublicClient; + let testClient: TestClient; + let invoiceAddress: Hex | null; + + beforeEach(async function () { + const walletClients = await viem.getWalletClients(); + [ + client, + clientReceiver, + provider, + resolver, + randomSigner, + providerReceiver, + client2, + clientReceiver2, + provider2, + providerReceiver2, + ] = walletClients; + publicClient = await viem.getPublicClient(); + testClient = await viem.getTestClient(); + + const mockTokenContract = await viem.deployContract('MockToken'); + mockToken = getAddress(mockTokenContract.address); + + const otherMockTokenContract = await viem.deployContract('MockToken'); + otherMockToken = getAddress(otherMockTokenContract.address); + + mockWrappedNativeTokenContract = await viem.deployContract('MockWETH'); + mockWrappedNativeToken = getAddress(mockWrappedNativeTokenContract.address); + + mockArbitratorContract = await viem.deployContract('MockArbitrator', [10n]); + mockArbitrator = getAddress(mockArbitratorContract.address); + + factory = await viem.deployContract('SmartInvoiceFactory', [ + mockWrappedNativeToken, + ]); + const invoiceImpl = await viem.deployContract('SmartInvoiceUpdatableV2'); + + await factory.write.addImplementation([invoiceType, invoiceImpl.address]); + + const data = encodeAbiParameters( + [ + 'address', + 'uint8', + 'address', + 'address', + 'uint256', + 'bytes32', + 'address', + 'bool', + 'address', + 'address', + 'address', + ].map(v => ({ type: v })), + [ + getAddress(client.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + BigInt(terminationTime), + zeroHash, + mockWrappedNativeToken, + requireVerification, + factory.address, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ], + ); + + const hash = await factory.write.create([ + getAddress(provider.account.address), + amounts, + data, + invoiceType, + ]); + const receipt = await publicClient.waitForTransactionReceipt({ hash }); + const address = await awaitInvoiceAddress(receipt); + invoice = await viem.getContractAt('SmartInvoiceUpdatableV2', address!); + invoiceAddress = getAddress(address!); + }); + + it('Should deploy a SmartInvoice', async function () { + expect(await invoice.read.client()).to.equal( + getAddress(client.account.address), + ); + expect(await invoice.read.provider()).to.equal( + getAddress(provider.account.address), + ); + expect(await invoice.read.resolverType()).to.equal(individualResolverType); + expect(await invoice.read.resolver()).to.equal( + getAddress(resolver.account.address), + ); + expect(await invoice.read.token()).to.equal(mockToken); + + for (let i = 0; i < amounts.length; i += 1) { + // eslint-disable-next-line no-await-in-loop + expect(await invoice.read.amounts([BigInt(i)])).to.equal(amounts[i]); + } + expect(await invoice.read.terminationTime()).to.equal(terminationTime); + expect(await invoice.read.details()).to.equal(zeroHash); + expect(await invoice.read.resolutionRate()).to.equal(resolutionRate); + expect(await invoice.read.milestone()).to.equal(0n); + expect(await invoice.read.total()).to.equal(total); + expect(await invoice.read.locked()).to.equal(false); + expect(await invoice.read.disputeId()).to.equal(0n); + expect(await invoice.read.wrappedNativeToken()).to.equal( + mockWrappedNativeToken, + ); + expect(await invoice.read.providerReceiver()).to.equal( + getAddress(providerReceiver.account.address), + ); + expect(await invoice.read.clientReceiver()).to.equal( + getAddress(clientReceiver.account.address), + ); + }); + + it('Should revert init if initLocked', async function () { + const currentTime = await currentTimestamp(); + const newInvoice = await viem.deployContract('SmartInvoiceUpdatableV2'); + + const data = encodeAbiParameters( + [ + 'address', + 'uint8', + 'address', + 'address', + 'uint256', + 'bytes32', + 'address', + 'bool', + ].map(v => ({ type: v })), + [ + getAddress(client.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + BigInt(currentTime - 3600), + zeroHash, + mockWrappedNativeToken, + requireVerification, + ], + ); + + const receipt = newInvoice.write.init([ + getAddress(provider.account.address), + amounts, + data, + ]); + await expect(receipt).to.be.revertedWithCustomError( + newInvoice, + 'InvalidInitialization', + ); + }); + + it('Should revert init if invalid client', async function () { + const currentTime = await currentTimestamp(); + const receipt = createUpdatableV2Escrow( + factory, + invoice, + invoiceType, + zeroAddress, + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + currentTime - 3600, + zeroHash, + mockWrappedNativeToken, + requireVerification, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + await expect(receipt).to.be.revertedWithCustomError( + invoice, + 'InvalidClient', + ); + }); + + it('Should revert init if invalid provider', async function () { + const currentTime = await currentTimestamp(); + const receipt = createUpdatableV2Escrow( + factory, + invoice, + invoiceType, + getAddress(client.account.address), + zeroAddress, + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + currentTime, + zeroHash, + mockWrappedNativeToken, + requireVerification, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + await expect(receipt).to.be.revertedWithCustomError( + invoice, + 'InvalidProvider', + ); + }); + + it('Should revert init if invalid provider receiver', async function () { + const currentTime = await currentTimestamp(); + const receipt = createUpdatableV2Escrow( + factory, + invoice, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + currentTime, + zeroHash, + mockWrappedNativeToken, + requireVerification, + zeroAddress, + getAddress(clientReceiver.account.address), + ); + await expect(receipt).to.be.revertedWithCustomError( + invoice, + 'InvalidProviderReceiver', + ); + }); + + it('Should revert init if invalid client receiver', async function () { + const currentTime = await currentTimestamp(); + const receipt = createUpdatableV2Escrow( + factory, + invoice, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + currentTime, + zeroHash, + mockWrappedNativeToken, + requireVerification, + getAddress(providerReceiver.account.address), + zeroAddress, + ); + await expect(receipt).to.be.revertedWithCustomError( + invoice, + 'InvalidClientReceiver', + ); + }); + + it('Should revert init if invalid resolver', async function () { + const currentTime = await currentTimestamp(); + const receipt = createUpdatableV2Escrow( + factory, + invoice, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + zeroAddress, + mockToken, + amounts, + currentTime - 3600, + zeroHash, + mockWrappedNativeToken, + requireVerification, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + await expect(receipt).to.be.revertedWithCustomError( + invoice, + 'InvalidResolver', + ); + }); + + it('Should revert init if invalid token', async function () { + const currentTime = await currentTimestamp(); + const receipt = createUpdatableV2Escrow( + factory, + invoice, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + zeroAddress, + amounts, + currentTime - 3600, + zeroHash, + mockWrappedNativeToken, + requireVerification, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + await expect(receipt).to.be.revertedWithCustomError( + invoice, + 'InvalidToken', + ); + }); + + it('Should revert init if invalid wrappedNativeToken', async function () { + const receipt = createUpdatableV2Escrow( + factory, + invoice, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + terminationTime, + zeroHash, + zeroAddress, + requireVerification, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + await expect(receipt).to.be.revertedWithCustomError( + invoice, + 'InvalidWrappedNativeToken', + ); + }); + + it('Should revert init if terminationTime has ended', async function () { + const currentTime = await currentTimestamp(); + const receipt = createUpdatableV2Escrow( + factory, + invoice, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + currentTime - 3600, + zeroHash, + mockWrappedNativeToken, + requireVerification, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + await expect(receipt).to.be.revertedWithCustomError( + invoice, + 'DurationEnded', + ); + }); + + it('Should revert init if terminationTime too long', async function () { + const currentTime = await currentTimestamp(); + const receipt = createUpdatableV2Escrow( + factory, + invoice, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + currentTime + 5 * 365 * 24 * 3600, + zeroHash, + mockWrappedNativeToken, + requireVerification, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + await expect(receipt).to.be.revertedWithCustomError( + invoice, + 'DurationTooLong', + ); + }); + + it('Default resolution rate should equal 20', async function () { + const currentTime = await currentTimestamp(); + const tx = await createUpdatableV2Escrow( + factory, + invoice, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + currentTime + 365 * 24 * 3600, + zeroHash, + mockWrappedNativeToken, + requireVerification, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + const invoiceAddr = await awaitInvoiceAddress(tx); + expect(invoiceAddr).to.not.equal(undefined); + const deployedInvoice = await viem.getContractAt( + 'SmartInvoiceUpdatableV2', + invoiceAddr!, + ); + expect(await deployedInvoice.read.resolutionRate()).to.equal( + resolutionRate, + ); + }); + + it('Should revert init if resolverType > 1', async function () { + const currentTime = await currentTimestamp(); + const receipt = createUpdatableV2Escrow( + factory, + invoice, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + 2, + getAddress(resolver.account.address), + mockToken, + amounts, + currentTime + 365 * 24 * 3600, + zeroHash, + mockWrappedNativeToken, + requireVerification, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + await expect(receipt).to.revertedWithCustomError( + invoice, + 'InvalidResolverType', + ); + }); + + it('Should revert release by non client', async function () { + await expect( + invoice.write.release({ account: provider.account }), + ).to.be.revertedWithCustomError(invoice, 'NotClient'); + }); + + it('Should revert release with low balance', async function () { + // await setBalanceOf(mockToken, invoice.address, 5); + await setBalanceOf(mockToken, invoice.address, 5); + await expect(invoice.write.release()).to.be.revertedWithCustomError( + invoice, + 'InsufficientBalance', + ); + }); + + it('Should release', async function () { + await setBalanceOf(mockToken, invoice.address, 10); + const beforeBalance = await getBalanceOf( + mockToken, + providerReceiver.account.address, + ); + const receipt = await invoice.write.release(); + expect(await invoice.read.released()).to.equal(10); + expect(await invoice.read.milestone()).to.equal(1); + await expect(receipt).to.emit(invoice, 'Release').withArgs(0, 10); + const afterBalance = await getBalanceOf( + mockToken, + providerReceiver.account.address, + ); + expect(afterBalance).to.equal(beforeBalance + 10n); + }); + + it('Should release full balance at last milestone', async function () { + await setBalanceOf(mockToken, invoice.address, 10); + const beforeBalance = await getBalanceOf( + mockToken, + providerReceiver.account.address, + ); + let receipt = await invoice.write.release(); + expect(await invoice.read.released()).to.equal(10); + expect(await invoice.read.milestone()).to.equal(1); + await expect(receipt).to.emit(invoice, 'Release').withArgs(0, 10); + await setBalanceOf(mockToken, invoice.address, 15); + receipt = await invoice.write.release(); + expect(await invoice.read.released()).to.equal(25); + expect(await invoice.read.milestone()).to.equal(2); + await expect(receipt).to.emit(invoice, 'Release').withArgs(1, 15); + const afterBalance = await getBalanceOf( + mockToken, + providerReceiver.account.address, + ); + expect(afterBalance).to.equal(beforeBalance + 25n); + }); + + it('Should release full balance after all milestones are completed', async function () { + await setBalanceOf(mockToken, invoice.address, 10); + const beforeBalance = await getBalanceOf( + mockToken, + providerReceiver.account.address, + ); + let receipt = await invoice.write.release(); + await setBalanceOf(mockToken, invoice.address, 15); + receipt = await invoice.write.release(); + expect(await invoice.read.released()).to.equal(25); + expect(await invoice.read.milestone()).to.equal(2); + await expect(receipt).to.emit(invoice, 'Release').withArgs(1, 15); + + await setBalanceOf(mockToken, invoice.address, 20); + receipt = await invoice.write.release(); + expect(await invoice.read.released()).to.equal(45); + expect(await invoice.read.milestone()).to.equal(2); + await expect(receipt).to.emit(invoice, 'Release').withArgs(2, 20); + const afterBalance = await getBalanceOf( + mockToken, + providerReceiver.account.address, + ); + expect(afterBalance).to.equal(beforeBalance + 45n); + }); + + it('Should revert release if 0 balance after all milestones are completed', async function () { + await setBalanceOf(mockToken, invoice.address, 10); + let receipt = await invoice.write.release(); + await setBalanceOf(mockToken, invoice.address, 15); + receipt = await invoice.write.release(); + expect(await invoice.read.released()).to.equal(25); + expect(await invoice.read.milestone()).to.equal(2); + await expect(receipt).to.emit(invoice, 'Release').withArgs(1, 15); + + await setBalanceOf(mockToken, invoice.address, 0); + await expect(invoice.write.release()).to.be.revertedWithCustomError( + invoice, + 'BalanceIsZero', + ); + }); + + it('Should revert release if locked', async function () { + const lockedInvoice = await getLockedUpdatableV2Escrow( + factory, + invoiceType, + client.account.address, + provider.account.address, + individualResolverType, + resolver.account.address, + mockToken, + amounts, + zeroHash, + mockWrappedNativeToken, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + expect(lockedInvoice.write.release()).to.be.revertedWithCustomError( + invoice, + 'Locked', + ); + }); + + it('Should release with milestone number', async function () { + await setBalanceOf(mockToken, invoice.address, 10); + const receipt = await invoice.write.release([0n]); + expect(await invoice.read.released()).to.equal(10); + expect(await invoice.read.milestone()).to.equal(1); + await expect(receipt).to.emit(invoice, 'Release').withArgs(0, 10); + }); + + it('Should release with higher milestone number', async function () { + await setBalanceOf(mockToken, invoice.address, 20); + const receipt = await invoice.write.release([1n]); + expect(await invoice.read.released()).to.equal(20); + expect(await invoice.read.milestone()).to.equal(2); + await expect(receipt).to.emit(invoice, 'Release').withArgs(0, 10); + await expect(receipt).to.emit(invoice, 'Release').withArgs(1, 10); + }); + + it('Should release all with higher milestone number', async function () { + await setBalanceOf(mockToken, invoice.address, 25); + const receipt = await invoice.write.release([1n]); + expect(await invoice.read.released()).to.equal(25); + expect(await invoice.read.milestone()).to.equal(2); + await expect(receipt).to.emit(invoice, 'Release').withArgs(0, 10); + await expect(receipt).to.emit(invoice, 'Release').withArgs(1, 15); + }); + + it('Should revert release with higher milestone number', async function () { + await setBalanceOf(mockToken, invoice.address, 10); + const receipt = invoice.write.release([1n]); + await expect(receipt).to.revertedWithCustomError( + invoice, + 'InsufficientBalance', + ); + }); + + it('Should revert release with invalid milestone number', async function () { + await setBalanceOf(mockToken, invoice.address, 10); + const receipt = invoice.write.release([5n]); + await expect(receipt).to.revertedWithCustomError( + invoice, + 'InvalidMilestone', + ); + }); + + it('Should revert release with passed milestone number', async function () { + await setBalanceOf(mockToken, invoice.address, 10); + await invoice.write.release(); + const receipt = invoice.write.release([0n]); + await expect(receipt).to.revertedWithCustomError( + invoice, + 'InvalidMilestone', + ); + }); + + it('Should revert release milestone if not client', async function () { + await setBalanceOf(mockToken, invoice.address, 10); + const receipt = invoice.write.release([0n], { account: provider.account }); + await expect(receipt).to.revertedWithCustomError(invoice, 'NotClient'); + }); + + it('Should revert release milestone if locked', async function () { + const lockedInvoice = await getLockedUpdatableV2Escrow( + factory, + invoiceType, + client.account.address, + provider.account.address, + individualResolverType, + resolver.account.address, + mockToken, + amounts, + zeroHash, + mockWrappedNativeToken, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + await expect( + lockedInvoice.write.release([0n]), + ).to.be.revertedWithCustomError(invoice, 'Locked'); + }); + + it('Should releaseTokens with passed token', async function () { + await setBalanceOf(otherMockToken, invoice.address, 10); + + const providerBeforeBalance = await getBalanceOf( + otherMockToken, + providerReceiver.account.address, + ); + + await invoice.write.releaseTokens([otherMockToken]); + + const providerAfterBalance = await getBalanceOf( + otherMockToken, + providerReceiver.account.address, + ); + + expect(providerAfterBalance).to.be.equal(providerBeforeBalance + 10n); + }); + + it('Should call release if releaseTokens with invoice token', async function () { + await setBalanceOf(mockToken, invoice.address, 10); + const receipt = await invoice.write.releaseTokens([mockToken]); + expect(await invoice.read.released()).to.equal(10); + expect(await invoice.read.milestone()).to.equal(1); + await expect(receipt).to.emit(invoice, 'Release').withArgs(0, 10); + }); + + it('Should revert releaseTokens if not client', async function () { + const receipt = invoice.write.releaseTokens([otherMockToken], { + account: provider.account, + }); + await expect(receipt).to.revertedWithCustomError(invoice, 'NotClient'); + }); + + it('Should revert withdraw before terminationTime', async function () { + const currentTime = await currentTimestamp(); + const tx = await createUpdatableV2Escrow( + factory, + invoice, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + currentTime + 3600, + zeroHash, + mockWrappedNativeToken, + requireVerification, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + invoiceAddress = await awaitInvoiceAddress(tx); + invoice = await viem.getContractAt( + 'SmartInvoiceUpdatableV2', + invoiceAddress!, + ); + + const receipt = invoice.write.withdraw(); + await expect(receipt).to.revertedWithCustomError(invoice, 'Terminated'); + }); + + it('Should revert withdraw if locked', async function () { + const lockedInvoice = await getLockedUpdatableV2Escrow( + factory, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + zeroHash, + mockWrappedNativeToken, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + await expect(lockedInvoice.write.withdraw()).to.be.revertedWithCustomError( + invoice, + 'Locked', + ); + }); + + it('Should withdraw after terminationTime', async function () { + const currentTime = await currentTimestamp(); + const tx = await createUpdatableV2Escrow( + factory, + invoice, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + currentTime + 1000, + zeroHash, + mockWrappedNativeToken, + requireVerification, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + invoiceAddress = await awaitInvoiceAddress(tx); + invoice = await viem.getContractAt( + 'SmartInvoiceUpdatableV2', + invoiceAddress!, + ); + + await testClient.increaseTime({ seconds: 1000 }); + await setBalanceOf(mockToken, invoice.address, 10); + + const clientBeforeBalance = await getBalanceOf( + mockToken, + clientReceiver.account.address, + ); + + const receipt = await invoice.write.withdraw(); + + const clientAfterBalance = await getBalanceOf( + mockToken, + clientReceiver.account.address, + ); + + expect(clientAfterBalance).to.be.equal(clientBeforeBalance + 10n); + + expect(await invoice.read.milestone()).to.equal(2); + await expect(receipt).to.emit(invoice, 'Withdraw').withArgs(10); + }); + + it('Should revert withdraw after terminationTime if balance is 0', async function () { + const currentTime = await currentTimestamp(); + const tx = await createUpdatableV2Escrow( + factory, + invoice, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + currentTime + 1000, + zeroHash, + mockWrappedNativeToken, + requireVerification, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + invoiceAddress = await awaitInvoiceAddress(tx); + invoice = await viem.getContractAt( + 'SmartInvoiceUpdatableV2', + invoiceAddress!, + ); + + await testClient.increaseTime({ seconds: 1000 }); + await setBalanceOf(mockToken, invoice.address, 0); + + const receipt = invoice.write.withdraw(); + await expect(receipt).to.be.revertedWithCustomError( + invoice, + 'BalanceIsZero', + ); + }); + + it('Should call withdraw from withdrawTokens', async function () { + const currentTime = await currentTimestamp(); + const tx = await createUpdatableV2Escrow( + factory, + invoice, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + currentTime + 1000, + zeroHash, + mockWrappedNativeToken, + requireVerification, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + invoiceAddress = await awaitInvoiceAddress(tx); + invoice = await viem.getContractAt( + 'SmartInvoiceUpdatableV2', + invoiceAddress!, + ); + await testClient.increaseTime({ seconds: 1000 }); + await setBalanceOf(mockToken, invoice.address, 10); + + const clientBeforeBalance = await getBalanceOf( + mockToken, + clientReceiver.account.address, + ); + + const receipt = await invoice.write.withdrawTokens([mockToken]); + + const clientAfterBalance = await getBalanceOf( + mockToken, + clientReceiver.account.address, + ); + + expect(clientAfterBalance).to.be.equal(clientBeforeBalance + 10n); + + expect(await invoice.read.milestone()).to.equal(2); + await expect(receipt).to.emit(invoice, 'Withdraw').withArgs(10); + }); + + it('Should withdrawTokens for otherToken', async function () { + const currentTime = await currentTimestamp(); + const tx = await createUpdatableV2Escrow( + factory, + invoice, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + currentTime + 1000, + zeroHash, + mockWrappedNativeToken, + requireVerification, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + invoiceAddress = await awaitInvoiceAddress(tx); + invoice = await viem.getContractAt( + 'SmartInvoiceUpdatableV2', + invoiceAddress!, + ); + await testClient.increaseTime({ seconds: 1000 }); + await setBalanceOf(otherMockToken, invoice.address, 10); + + const clientBeforeBalance = await getBalanceOf( + otherMockToken, + clientReceiver.account.address, + ); + + await invoice.write.withdrawTokens([otherMockToken]); + + const clientAfterBalance = await getBalanceOf( + otherMockToken, + clientReceiver.account.address, + ); + + expect(clientAfterBalance).to.be.equal(clientBeforeBalance + 10n); + expect(await invoice.read.milestone()).to.equal(0); + }); + + it('Should revert withdrawTokens for otherToken if not terminated', async function () { + const currentTime = await currentTimestamp(); + const tx = await createUpdatableV2Escrow( + factory, + invoice, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + currentTime + 1000, + zeroHash, + mockWrappedNativeToken, + requireVerification, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + invoiceAddress = await awaitInvoiceAddress(tx); + invoice = await viem.getContractAt( + 'SmartInvoiceUpdatableV2', + invoiceAddress!, + ); + + const receipt = invoice.write.withdrawTokens([otherMockToken]); + await expect(receipt).to.be.revertedWithCustomError(invoice, 'Terminated'); + }); + + it('Should revert withdrawTokens for otherToken if balance is 0', async function () { + const currentTime = await currentTimestamp(); + const tx = await createUpdatableV2Escrow( + factory, + invoice, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + currentTime + 1000, + zeroHash, + mockWrappedNativeToken, + requireVerification, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + invoiceAddress = await awaitInvoiceAddress(tx); + invoice = await viem.getContractAt( + 'SmartInvoiceUpdatableV2', + invoiceAddress!, + ); + + await testClient.increaseTime({ seconds: 1000 }); + await setBalanceOf(otherMockToken, invoice.address, 0); + const receipt = invoice.write.withdrawTokens([otherMockToken]); + await expect(receipt).to.be.revertedWithCustomError( + invoice, + 'BalanceIsZero', + ); + }); + + it('Should revert lock if terminated', async function () { + const currentTime = await currentTimestamp(); + const tx = await createUpdatableV2Escrow( + factory, + invoice, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + currentTime + 1000, + zeroHash, + mockWrappedNativeToken, + requireVerification, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + invoiceAddress = await awaitInvoiceAddress(tx); + invoice = await viem.getContractAt( + 'SmartInvoiceUpdatableV2', + invoiceAddress!, + ); + + await testClient.increaseTime({ seconds: 1000 }); + await setBalanceOf(mockToken, invoice.address, 10); + const receipt = invoice.write.lock([zeroHash]); + await expect(receipt).to.be.revertedWithCustomError(invoice, 'Terminated'); + }); + + it('Should revert lock if balance is 0', async function () { + const currentTime = await currentTimestamp(); + const tx = await createUpdatableV2Escrow( + factory, + invoice, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + currentTime + 1000, + zeroHash, + mockWrappedNativeToken, + requireVerification, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + invoiceAddress = await awaitInvoiceAddress(tx); + invoice = await viem.getContractAt( + 'SmartInvoiceUpdatableV2', + invoiceAddress!, + ); + await setBalanceOf(mockToken, invoice.address, 0); + const receipt = invoice.write.lock([zeroHash]); + await expect(receipt).to.be.revertedWithCustomError( + invoice, + 'BalanceIsZero', + ); + }); + + it('Should revert lock if not client or provider', async function () { + const currentTime = await currentTimestamp(); + const tx = await createUpdatableV2Escrow( + factory, + invoice, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + currentTime + 1000, + zeroHash, + mockWrappedNativeToken, + requireVerification, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + invoiceAddress = await awaitInvoiceAddress(tx); + invoice = await viem.getContractAt( + 'SmartInvoiceUpdatableV2', + invoiceAddress!, + ); + await setBalanceOf(mockToken, invoice.address, 10); + const receipt = invoice.write.lock([zeroHash], { + account: resolver.account, + }); + await expect(receipt).to.be.revertedWithCustomError(invoice, 'NotParty'); + }); + + it('Should revert lock if locked', async function () { + const lockedInvoice = await getLockedUpdatableV2Escrow( + factory, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + zeroHash, + mockWrappedNativeToken, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + const receipt = lockedInvoice.write.lock([zeroHash]); + await expect(receipt).to.be.revertedWithCustomError(invoice, 'Locked'); + }); + + it('Should lock if balance is greater than 0', async function () { + const lockedInvoice = await getLockedUpdatableV2Escrow( + factory, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + zeroHash, + mockWrappedNativeToken, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + expect(await lockedInvoice.read.locked()).to.equal(true); + }); + + it('Should revert resolve if not locked', async function () { + await expect( + invoice.write.resolve([0n, 10n, zeroHash]), + ).to.be.revertedWithCustomError(invoice, 'Locked'); + }); + + it('Should revert resolve if balance is 0', async function () { + const lockedInvoice = await getLockedUpdatableV2Escrow( + factory, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + zeroHash, + mockWrappedNativeToken, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + await setBalanceOf(mockToken, lockedInvoice.address, 0); + await expect( + lockedInvoice.write.resolve([0n, 10n, zeroHash]), + ).to.be.revertedWithCustomError(invoice, 'BalanceIsZero'); + }); + + it('Should revert resolve if not resolver', async function () { + const lockedInvoice = await getLockedUpdatableV2Escrow( + factory, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + zeroHash, + mockWrappedNativeToken, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + await setBalanceOf(mockToken, lockedInvoice.address, 10); + await expect( + lockedInvoice.write.resolve([0n, 10n, zeroHash]), + ).to.be.revertedWithCustomError(invoice, 'NotResolver'); + }); + + it('Should revert resolve if awards do not add up', async function () { + const lockedInvoice = await getLockedUpdatableV2Escrow( + factory, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + zeroHash, + mockWrappedNativeToken, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + await setBalanceOf(mockToken, lockedInvoice.address, 10); + await expect( + lockedInvoice.write.resolve([0n, 0n, zeroHash], { + account: resolver.account, + }), + ).to.be.revertedWithCustomError(invoice, 'ResolutionMismatch'); + }); + + it('Should revert resolver if not individual', async function () { + const tx = await createUpdatableV2Escrow( + factory, + invoice, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + arbitratorResolverType, + getAddress(resolver.account.address), + mockWrappedNativeToken, + amounts, + terminationTime, + zeroHash, + mockWrappedNativeToken, + requireVerification, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + invoiceAddress = await awaitInvoiceAddress(tx); + invoice = await viem.getContractAt( + 'SmartInvoiceUpdatableV2', + invoiceAddress!, + ); + expect(await invoice.read.resolverType()).to.be.equal( + arbitratorResolverType, + ); + await expect( + invoice.write.resolve([0n, 0n, zeroHash]), + ).to.be.revertedWithCustomError(invoice, 'InvalidIndividualResolver'); + }); + + it('Should resolve with correct rewards', async function () { + const lockedInvoice = await getLockedUpdatableV2Escrow( + factory, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + zeroHash, + mockWrappedNativeToken, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + await setBalanceOf(mockToken, lockedInvoice.address, 100); + const clientBeforeBalance = await getBalanceOf( + mockToken, + clientReceiver.account.address, + ); + const providerBeforeBalance = await getBalanceOf( + mockToken, + providerReceiver.account.address, + ); + const resolverBeforeBalance = await getBalanceOf( + mockToken, + resolver.account.address, + ); + const receipt = lockedInvoice.write.resolve([5n, 90n, zeroHash], { + account: resolver.account, + }); + await expect(receipt) + .to.emit(lockedInvoice, 'Resolve') + .withArgs(getAddress(resolver.account.address), 5, 90, 5, zeroHash); + expect(await lockedInvoice.read.released()).to.be.equal(0); + expect(await lockedInvoice.read.milestone()).to.be.equal(2); + expect(await lockedInvoice.read.locked()).to.be.equal(false); + const clientAfterBalance = await getBalanceOf( + mockToken, + clientReceiver.account.address, + ); + const providerAfterBalance = await getBalanceOf( + mockToken, + providerReceiver.account.address, + ); + const resolverAfterBalance = await getBalanceOf( + mockToken, + resolver.account.address, + ); + expect(clientAfterBalance).to.be.equal(clientBeforeBalance + 5n); + expect(providerAfterBalance).to.be.equal(providerBeforeBalance + 90n); + expect(resolverAfterBalance).to.be.equal(resolverBeforeBalance + 5n); + }); + + it('Should resolve and not transfer if 0 clientAward', async function () { + const lockedInvoice = await getLockedUpdatableV2Escrow( + factory, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + zeroHash, + mockWrappedNativeToken, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + await setBalanceOf(mockToken, lockedInvoice.address, 100); + const receipt = lockedInvoice.write.resolve([0n, 95n, zeroHash], { + account: resolver.account, + }); + await expect(receipt) + .to.emit(lockedInvoice, 'Resolve') + .withArgs(getAddress(resolver.account.address), 0, 95, 5, zeroHash); + expect(await lockedInvoice.read.released()).to.be.equal(0); + expect(await lockedInvoice.read.milestone()).to.be.equal(2); + expect(await lockedInvoice.read.locked()).to.be.equal(false); + }); + + it('Should resolve and not transfer if 0 providerAward', async function () { + const lockedInvoice = await getLockedUpdatableV2Escrow( + factory, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + zeroHash, + mockWrappedNativeToken, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + await setBalanceOf(mockToken, lockedInvoice.address, 100); + const receipt = lockedInvoice.write.resolve([95n, 0n, zeroHash], { + account: resolver.account, + }); + await expect(receipt) + .to.emit(lockedInvoice, 'Resolve') + .withArgs(getAddress(resolver.account.address), 95, 0, 5, zeroHash); + expect(await lockedInvoice.read.released()).to.be.equal(0); + expect(await lockedInvoice.read.milestone()).to.be.equal(2); + expect(await lockedInvoice.read.locked()).to.be.equal(false); + }); + + it('Should resolve and not transfer if 0 resolutionFee', async function () { + const lockedInvoice = await getLockedUpdatableV2Escrow( + factory, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + zeroHash, + mockWrappedNativeToken, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + await setBalanceOf(mockToken, lockedInvoice.address, 10); + const receipt = lockedInvoice.write.resolve([5n, 5n, zeroHash], { + account: resolver.account, + }); + await expect(receipt) + .to.emit(lockedInvoice, 'Resolve') + .withArgs(getAddress(resolver.account.address), 5, 5, 0, zeroHash); + expect(await lockedInvoice.read.released()).to.be.equal(0); + expect(await lockedInvoice.read.milestone()).to.be.equal(2); + expect(await lockedInvoice.read.locked()).to.be.equal(false); + }); + + it('Should revert rule if not arbitrable', async function () { + expect(await invoice.read.resolverType()).to.be.equal( + individualResolverType, + ); + await expect(invoice.write.rule([0n, 0n])).to.be.revertedWithCustomError( + invoice, + 'InvalidArbitratorResolver', + ); + }); + + it('Should revert rule if not locked', async function () { + const tx = await createUpdatableV2Escrow( + factory, + invoice, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + arbitratorResolverType, + mockArbitrator, + mockWrappedNativeToken, + amounts, + terminationTime, + zeroHash, + mockWrappedNativeToken, + requireVerification, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + invoiceAddress = await awaitInvoiceAddress(tx); + invoice = await viem.getContractAt( + 'SmartInvoiceUpdatableV2', + invoiceAddress!, + ); + await expect(invoice.write.rule([0n, 0n])).to.be.revertedWithCustomError( + invoice, + 'Locked', + ); + }); + + it('Should revert rule if not resolver', async function () { + const lockedInvoice = await getLockedUpdatableV2Escrow( + factory, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + arbitratorResolverType, + mockArbitrator, + mockToken, + amounts, + zeroHash, + mockWrappedNativeToken, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + 10n, + ); + expect(await lockedInvoice.read.resolverType()).to.be.equal( + arbitratorResolverType, + ); + expect(await lockedInvoice.read.disputeId()).to.be.equal(1); + + await expect( + lockedInvoice.write.rule([0n, 0n], { account: provider.account }), + ).to.be.revertedWithCustomError(invoice, 'NotResolver'); + }); + + it('Should revert rule if invalid disputeId', async function () { + const lockedInvoice = await getLockedUpdatableV2Escrow( + factory, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + arbitratorResolverType, + mockArbitrator, + mockToken, + amounts, + zeroHash, + mockWrappedNativeToken, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + 10n, + ); + expect(await lockedInvoice.read.resolverType()).to.be.equal( + arbitratorResolverType, + ); + expect(await lockedInvoice.read.disputeId()).to.be.equal(1); + + const receipt = mockArbitratorContract.write.executeRulingWithDisputeId([ + lockedInvoice.address, + 6n, + 10n, + ]); + await expect(receipt).to.be.revertedWithCustomError( + invoice, + 'IncorrectDisputeId', + ); + }); + + it('Should revert rule if invalid ruling', async function () { + const lockedInvoice = await getLockedUpdatableV2Escrow( + factory, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + arbitratorResolverType, + mockArbitrator, + mockToken, + amounts, + zeroHash, + mockWrappedNativeToken, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + 10n, + ); + expect(await lockedInvoice.read.resolverType()).to.be.equal( + arbitratorResolverType, + ); + expect(await lockedInvoice.read.disputeId()).to.be.equal(1); + + const receipt = mockArbitratorContract.write.executeRuling([ + lockedInvoice.address, + 6n, + ]); + await expect(receipt).to.be.revertedWithCustomError( + invoice, + 'InvalidRuling', + ); + }); + + it('Should revert rule if balance is 0', async function () { + const lockedInvoice = await getLockedUpdatableV2Escrow( + factory, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + arbitratorResolverType, + mockArbitrator, + mockToken, + amounts, + zeroHash, + mockWrappedNativeToken, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + 10n, + ); + expect(await lockedInvoice.read.resolverType()).to.be.equal( + arbitratorResolverType, + ); + expect(await lockedInvoice.read.disputeId()).to.be.equal(1); + + await setBalanceOf(mockToken, lockedInvoice.address, 0); + const receipt = mockArbitratorContract.write.executeRuling([ + lockedInvoice.address, + 1n, + ]); + await expect(receipt).to.be.revertedWithCustomError( + invoice, + 'BalanceIsZero', + ); + }); + + it('Should rule 1:1 for ruling 0', async function () { + const lockedInvoice = await getLockedUpdatableV2Escrow( + factory, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + arbitratorResolverType, + mockArbitrator, + mockToken, + amounts, + zeroHash, + mockWrappedNativeToken, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + 10n, + ); + + expect(await lockedInvoice.read.resolverType()).to.be.equal( + arbitratorResolverType, + ); + expect(await lockedInvoice.read.disputeId()).to.be.equal(1); + + await setBalanceOf(mockToken, lockedInvoice.address, 100); + const receipt = await mockArbitratorContract.write.executeRuling([ + lockedInvoice.address, + 0n, + ]); + await expect(receipt) + .to.emit(lockedInvoice, 'Rule') + .withArgs(mockArbitrator, 50, 50, 0); + await expect(receipt) + .to.emit(lockedInvoice, 'Ruling') + .withArgs(mockArbitrator, 1, 0); + expect(await lockedInvoice.read.released()).to.be.equal(0); + expect(await lockedInvoice.read.milestone()).to.be.equal(2); + expect(await lockedInvoice.read.locked()).to.be.equal(false); + }); + + it('Should rule 1:0 for ruling 1', async function () { + const lockedInvoice = await getLockedUpdatableV2Escrow( + factory, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + arbitratorResolverType, + mockArbitrator, + mockToken, + amounts, + zeroHash, + mockWrappedNativeToken, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + 10n, + ); + expect(await lockedInvoice.read.resolverType()).to.be.equal( + arbitratorResolverType, + ); + expect(await lockedInvoice.read.disputeId()).to.be.equal(1); + + await setBalanceOf(mockToken, lockedInvoice.address, 100); + const receipt = await mockArbitratorContract.write.executeRuling([ + lockedInvoice.address, + 1n, + ]); + await expect(receipt) + .to.emit(lockedInvoice, 'Rule') + .withArgs(mockArbitrator, 100, 0, 1); + await expect(receipt) + .to.emit(lockedInvoice, 'Ruling') + .withArgs(mockArbitrator, 1, 1); + expect(await lockedInvoice.read.released()).to.be.equal(0); + expect(await lockedInvoice.read.milestone()).to.be.equal(2); + expect(await lockedInvoice.read.locked()).to.be.equal(false); + }); + + it('Should rule 3:1 for ruling 2', async function () { + const lockedInvoice = await getLockedUpdatableV2Escrow( + factory, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + arbitratorResolverType, + mockArbitrator, + mockToken, + amounts, + zeroHash, + mockWrappedNativeToken, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + 10n, + ); + expect(await lockedInvoice.read.resolverType()).to.be.equal( + arbitratorResolverType, + ); + expect(await lockedInvoice.read.disputeId()).to.be.equal(1); + + await setBalanceOf(mockToken, lockedInvoice.address, 100); + const receipt = await mockArbitratorContract.write.executeRuling([ + lockedInvoice.address, + 2n, + ]); + await expect(receipt) + .to.emit(lockedInvoice, 'Rule') + .withArgs(mockArbitrator, 75, 25, 2); + await expect(receipt) + .to.emit(lockedInvoice, 'Ruling') + .withArgs(mockArbitrator, 1, 2); + expect(await lockedInvoice.read.released()).to.be.equal(0); + expect(await lockedInvoice.read.milestone()).to.be.equal(2); + expect(await lockedInvoice.read.locked()).to.be.equal(false); + }); + + it('Should rule 1:1 for ruling 3', async function () { + const lockedInvoice = await getLockedUpdatableV2Escrow( + factory, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + arbitratorResolverType, + mockArbitrator, + mockToken, + amounts, + zeroHash, + mockWrappedNativeToken, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + 10n, + ); + expect(await lockedInvoice.read.resolverType()).to.be.equal( + arbitratorResolverType, + ); + expect(await lockedInvoice.read.disputeId()).to.be.equal(1); + + await setBalanceOf(mockToken, lockedInvoice.address, 100); + const receipt = await mockArbitratorContract.write.executeRuling([ + lockedInvoice.address, + 3n, + ]); + await expect(receipt) + .to.emit(lockedInvoice, 'Rule') + .withArgs(mockArbitrator, 50, 50, 3); + await expect(receipt) + .to.emit(lockedInvoice, 'Ruling') + .withArgs(mockArbitrator, 1, 3); + expect(await lockedInvoice.read.released()).to.be.equal(0); + expect(await lockedInvoice.read.milestone()).to.be.equal(2); + expect(await lockedInvoice.read.locked()).to.be.equal(false); + }); + + it('Should rule 1:3 for ruling 4', async function () { + const lockedInvoice = await getLockedUpdatableV2Escrow( + factory, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + arbitratorResolverType, + mockArbitrator, + mockToken, + amounts, + zeroHash, + mockWrappedNativeToken, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + 10n, + ); + expect(await lockedInvoice.read.resolverType()).to.be.equal( + arbitratorResolverType, + ); + expect(await lockedInvoice.read.disputeId()).to.be.equal(1); + + await setBalanceOf(mockToken, lockedInvoice.address, 100); + const receipt = await mockArbitratorContract.write.executeRuling([ + lockedInvoice.address, + 4n, + ]); + await expect(receipt) + .to.emit(lockedInvoice, 'Rule') + .withArgs(mockArbitrator, 25, 75, 4); + await expect(receipt) + .to.emit(lockedInvoice, 'Ruling') + .withArgs(mockArbitrator, 1, 4); + expect(await lockedInvoice.read.released()).to.be.equal(0); + expect(await lockedInvoice.read.milestone()).to.be.equal(2); + expect(await lockedInvoice.read.locked()).to.be.equal(false); + }); + + it('Should rule 0:1 for ruling 5', async function () { + const lockedInvoice = await getLockedUpdatableV2Escrow( + factory, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + arbitratorResolverType, + mockArbitrator, + mockToken, + amounts, + zeroHash, + mockWrappedNativeToken, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + 10n, + ); + expect(await lockedInvoice.read.resolverType()).to.be.equal( + arbitratorResolverType, + ); + expect(await lockedInvoice.read.disputeId()).to.be.equal(1); + + await setBalanceOf(mockToken, lockedInvoice.address, 100); + const receipt = await mockArbitratorContract.write.executeRuling([ + lockedInvoice.address, + 5n, + ]); + await expect(receipt) + .to.emit(lockedInvoice, 'Rule') + .withArgs(mockArbitrator, 0, 100, 5); + await expect(receipt) + .to.emit(lockedInvoice, 'Ruling') + .withArgs(mockArbitrator, 1, 5); + expect(await lockedInvoice.read.released()).to.be.equal(0); + expect(await lockedInvoice.read.milestone()).to.be.equal(2); + expect(await lockedInvoice.read.locked()).to.be.equal(false); + }); + + it('Should revert receive if not wrappedNativeToken', async function () { + const receipt = client.sendTransaction({ + to: invoice.address, + value: 10n, + }); + await expect(receipt).to.be.revertedWithCustomError( + invoice, + 'InvalidWrappedNativeToken', + ); + }); + + it('Should revert receive if locked', async function () { + const lockedInvoice = await getLockedUpdatableV2Escrow( + factory, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + zeroHash, + mockWrappedNativeToken, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + const receipt = client.sendTransaction({ + to: lockedInvoice.address, + value: 10n, + }); + await expect(receipt).to.be.revertedWithCustomError(invoice, 'Locked'); + }); + + it('Should accept receive and convert to wrapped token', async function () { + const tx = await createUpdatableV2Escrow( + factory, + invoice, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockWrappedNativeToken, + amounts, + terminationTime, + zeroHash, + mockWrappedNativeToken, + requireVerification, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + invoiceAddress = await awaitInvoiceAddress(tx); + invoice = await viem.getContractAt( + 'SmartInvoiceUpdatableV2', + invoiceAddress!, + ); + const receipt = await client.sendTransaction({ + to: invoice.address, + value: 10n, + }); + await expect(receipt) + .to.emit(invoice, 'Deposit') + .withArgs(getAddress(client.account.address), 10); + expect( + await mockWrappedNativeTokenContract.read.balanceOf([invoice.address]), + ).to.equal(10); + }); + + it('Should emit Verified when client calls verify()', async function () { + await expect(invoice.write.verify({ account: client.account })) + .to.emit(invoice, 'Verified') + .withArgs(getAddress(client.account.address), invoice.address); + }); + + it('Should not emit Verified if caller !client', async function () { + await expect(invoice.write.verify({ account: randomSigner.account })).to.be + .reverted; + }); + + it('Should emit Verified if client verification requirement waived on invoice creation', async function () { + const noVerification = false; + const currentTime = await currentTimestamp(); + const tx = await createUpdatableV2Escrow( + factory, + invoice, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + currentTime + 1000, + zeroHash, + mockWrappedNativeToken, + noVerification, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + invoiceAddress = await awaitInvoiceAddress(tx); + invoice = await viem.getContractAt( + 'SmartInvoiceUpdatableV2', + invoiceAddress!, + ); + + const events = parseEventLogs({ + abi: invoice.abi, + logs: tx.logs, + }); + + const event = events.find(e => e.eventName === 'Verified'); + expect(event).to.not.equal(undefined); + expect(event!.args.client).to.equal(getAddress(client.account.address)); + expect(event!.args.invoice).to.equal(getAddress(invoice.address)); + }); + + it('Should addMilestones if client', async function () { + await invoice.write.addMilestones([[13n, 14n]]); + expect((await invoice.read.getAmounts()).length).to.equal(4); + expect(await invoice.read.amounts([0n])).to.equal(10n); + expect(await invoice.read.amounts([1n])).to.equal(10n); + expect(await invoice.read.amounts([2n])).to.equal(13n); + expect(await invoice.read.amounts([3n])).to.equal(14n); + }); + + it('Should addMilestones if provider', async function () { + await invoice.write.addMilestones([[13n, 14n]], { + account: provider.account, + }); + expect((await invoice.read.getAmounts()).length).to.equal(4); + expect(await invoice.read.amounts([0n])).to.equal(10n); + expect(await invoice.read.amounts([1n])).to.equal(10n); + expect(await invoice.read.amounts([2n])).to.equal(13n); + expect(await invoice.read.amounts([3n])).to.equal(14n); + }); + + it('Should addMilestones and update total with added milestones', async function () { + await invoice.write.addMilestones([[13n, 14n]], { + account: provider.account, + }); + expect(await invoice.read.total()).to.equal(47); + }); + + it('Should addMilestones and emit MilestonesAdded event', async function () { + await expect( + invoice.write.addMilestones([[13n, 14n]], { account: client.account }), + ) + .to.emit(invoice, 'MilestonesAdded') + .withArgs(getAddress(client.account.address), invoice.address, [ + 13n, + 14n, + ]); + + await expect( + invoice.write.addMilestones([[13n, 14n], zeroHash], { + account: provider.account, + }), + ) + .to.emit(invoice, 'MilestonesAdded') + .withArgs(getAddress(provider.account.address), invoice.address, [ + 13n, + 14n, + ]); + }); + + it('Should revert addMilestones if executed by non-client/non-getAddress(provider.account.address)', async function () { + await expect( + invoice.write.addMilestones([[13n, 14n]], { + account: randomSigner.account, + }), + ).to.be.revertedWithCustomError(invoice, 'NotParty'); + }); + + it('Should revert addMilestones if locked', async function () { + const lockedInvoice = await getLockedUpdatableV2Escrow( + factory, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + zeroHash, + mockWrappedNativeToken, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + + await expect( + lockedInvoice.write.addMilestones([[13n, 14n]]), + ).to.be.revertedWithCustomError(invoice, 'Locked'); + }); + + it('Should revert addMilestones if terminationTime passed', async function () { + const currentTime = await currentTimestamp(); + const tx = await createUpdatableV2Escrow( + factory, + invoice, + invoiceType, + getAddress(client.account.address), + getAddress(provider.account.address), + individualResolverType, + getAddress(resolver.account.address), + mockToken, + amounts, + currentTime + 1000, + zeroHash, + mockWrappedNativeToken, + requireVerification, + getAddress(providerReceiver.account.address), + getAddress(clientReceiver.account.address), + ); + await testClient.increaseTime({ seconds: 1000 }); + invoiceAddress = await awaitInvoiceAddress(tx); + invoice = await viem.getContractAt( + 'SmartInvoiceUpdatableV2', + invoiceAddress!, + ); + + await expect( + invoice.write.addMilestones([[13n, 14n]]), + ).to.be.revertedWithCustomError(invoice, 'Terminated'); + }); + + it('Should revert addMilestones if milestones array length is not between 1-10', async function () { + await expect( + invoice.write.addMilestones([[]], { account: client.account }), + ).to.be.revertedWithCustomError(invoice, 'NoMilestones'); + await expect( + invoice.write.addMilestones( + [[1n, 2n, 3n, 4n, 5n, 6n, 7n, 8n, 9n, 10n, 11n, 12n]], + { account: client.account }, + ), + ).to.be.revertedWithCustomError(invoice, 'ExceedsMilestoneLimit'); + }); + + it('Should addMilestones(uint256[],bytes32) and update details', async function () { + const NEW_BYTES32 = + '0x1010101000000000000000000000000000000000000000000000000000000000'; + + const oldDetails = await invoice.read.details(); + await invoice.write.addMilestones([[13n, 14n], NEW_BYTES32]); + const newDetails = await invoice.read.details(); + + expect(oldDetails).to.equal(zeroHash); + expect(oldDetails).to.not.equal(newDetails); + expect(newDetails).to.equal(NEW_BYTES32); + }); + + it('Should addMilestones(uint256[],bytes32) and emit DetailsUpdated event', async function () { + const NEW_BYTES32 = + '0x1010101000000000000000000000000000000000000000000000000000000000'; + + await expect(invoice.write.addMilestones([[13n, 14n], NEW_BYTES32])) + .to.emit(invoice, 'DetailsUpdated') + .withArgs(getAddress(client.account.address), NEW_BYTES32); + + await expect( + invoice.write.addMilestones([[13n, 14n], NEW_BYTES32], { + account: provider.account, + }), + ) + .to.emit(invoice, 'DetailsUpdated') + .withArgs(getAddress(provider.account.address), NEW_BYTES32); + }); + + // UPDATABLE TESTS + it('Should allow the client to update their address', async function () { + await invoice.write.updateClient([client2.account.address], { + account: client.account, + }); + expect(await invoice.read.client()).to.equal( + getAddress(client2.account.address), + ); + }); + + it('Should revert the client update if not the client', async function () { + await expect( + invoice.write.updateClient([client2.account.address], { + account: provider.account, + }), + ).to.be.reverted; + }); + + it('Should allow the client to update their receiving address', async function () { + await invoice.write.updateClientReceiver( + [clientReceiver2.account.address], + { + account: client.account, + }, + ); + expect(await invoice.read.clientReceiver()).to.equal( + getAddress(clientReceiver2.account.address), + ); + }); + + it('Should revert the clientReceiver update if not the client', async function () { + await expect( + invoice.write.updateClientReceiver([clientReceiver2.account.address], { + account: provider.account, + }), + ).to.be.reverted; + }); + + it('Should allow the provider to update their receiving address', async function () { + await invoice.write.updateProviderReceiver( + [providerReceiver2.account.address], + { account: provider.account }, + ); + expect(await invoice.read.providerReceiver()).to.equal( + getAddress(providerReceiver2.account.address), + ); + }); + + it('Should revert the providerReceiver update if not provider', async function () { + await expect( + invoice.write.updateProviderReceiver( + [providerReceiver2.account.address], + { account: client.account }, + ), + ).to.be.reverted; + }); + + it('Should allow the provider to update their address', async function () { + await invoice.write.updateProvider([provider2.account.address], { + account: provider.account, + }); + expect(await invoice.read.provider()).to.equal( + getAddress(provider2.account.address), + ); + }); + + it('Should revert the provider update if not the provider', async function () { + await expect( + invoice.write.updateProvider([provider2.account.address], { + account: client.account, + }), + ).to.be.reverted; + }); +}); diff --git a/packages/contracts/test/utils.ts b/packages/contracts/test/utils.ts index e8b94126..fa4e40f3 100644 --- a/packages/contracts/test/utils.ts +++ b/packages/contracts/test/utils.ts @@ -234,6 +234,58 @@ export const createSplitEscrow = async ( return (await viem.getPublicClient()).waitForTransactionReceipt({ hash }); }; +export const createUpdatableV2Escrow = async ( + factory: ContractTypesMap['SmartInvoiceFactory'], + invoice: GetContractReturnType, + type: Hex, + client: Hex, + provider: Hex, + resolverType: number, + resolver: Hex, + token: Hex, + amounts: bigint[], + terminationTime: bigint | number, + details: Hex, + wrappedNativeToken: Hex, + requireVerification: boolean, + providerReceiver: Hex, + clientReceiver: Hex, +): Promise => { + await factory.write.addImplementation([type, invoice.address]); + + const data = encodeAbiParameters( + [ + 'address', + 'uint8', + 'address', + 'address', + 'uint256', + 'bytes32', + 'address', + 'bool', + 'address', + 'address', + 'address', + ].map(x => ({ type: x })), + [ + client, + resolverType, + resolver, + token, + BigInt(terminationTime), // exact termination date in seconds since epoch + details, + wrappedNativeToken, + requireVerification, + factory.address, + providerReceiver, + clientReceiver, + ], + ); + + const hash = await factory.write.create([provider, amounts, data, type]); + return (await viem.getPublicClient()).waitForTransactionReceipt({ hash }); +}; + export const createUpdatableEscrow = async ( factory: ContractTypesMap['SmartInvoiceFactory'], invoice: GetContractReturnType, @@ -283,6 +335,75 @@ export const createUpdatableEscrow = async ( return (await viem.getPublicClient()).waitForTransactionReceipt({ hash }); }; +export const getLockedUpdatableV2Escrow = async ( + factory: ContractTypesMap['SmartInvoiceFactory'], + invoiceType: Hex, + client: Hex, + provider: Hex, + resolverType: number, + resolver: Hex, + token: Hex, + amounts: bigint[], + details: Hex, + mockWrappedNativeToken: Hex, + providerReceiver: Hex, + clientReceiver: Hex, + value = 0n, + requireVerification: boolean = false, +): Promise => { + const currentTime = await currentTimestamp(); + const newInvoice = await viem.deployContract('SmartInvoiceUpdatableV2'); + + const initReceipt = await createUpdatableV2Escrow( + factory, + newInvoice, + invoiceType, + client, + provider, + resolverType, + resolver, + token, + amounts, + BigInt(currentTime + 1000), + details, + mockWrappedNativeToken, + requireVerification, + providerReceiver, + clientReceiver, + ); + + const newInvoiceAddress = await awaitInvoiceAddress(initReceipt); + if (!newInvoiceAddress) { + throw new Error('Failed to get invoice address'); + } + + const lockedInvoice = await viem.getContractAt( + 'SmartInvoiceUpdatableV2', + newInvoiceAddress, + ); + + expect(await lockedInvoice.read.locked()).to.equal(false); + + await setBalanceOf(token, newInvoiceAddress, 10n); + + const hash = await lockedInvoice.write.lock([zeroHash], { value }); + + const receipt = await ( + await viem.getPublicClient() + ).waitForTransactionReceipt({ hash }); + + const events = parseEventLogs({ + abi: parseAbi(['event Lock(address indexed client, bytes32 id)']), + logs: receipt.logs, + }); + + expect(events[0].eventName).to.equal('Lock'); + expect(events[0].args.client).to.equal(getAddress(client)); + expect(events[0].args.id).to.equal(zeroHash); + + return lockedInvoice; +}; + export const getLockedUpdatableEscrow = async ( factory: ContractTypesMap['SmartInvoiceFactory'], invoiceType: Hex, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index deb61684..a159220b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -123,10 +123,10 @@ importers: dependencies: '@nomicfoundation/hardhat-toolbox-viem': specifier: ^3.0.0 - version: 3.0.0(k45xe6q2gf6gmym2ylceu5kr6y) + version: 3.0.0(gmornzcqjt75ymzx6xpsbxhru4) '@nomicfoundation/hardhat-viem': - specifier: ^2.0.3 - version: 2.0.5(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(typescript@5.6.3)(viem@2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) + specifier: ^2.0.6 + version: 2.0.6(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(typescript@5.6.3)(viem@2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) '@openzeppelin/contracts': specifier: ^5.1.0 version: 5.1.0 @@ -152,14 +152,14 @@ importers: specifier: ^16.3.1 version: 16.4.5 hardhat: - specifier: ^2.22.15 - version: 2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10) + specifier: ^2.22.18 + version: 2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10) hardhat-chai-matchers-viem: specifier: ^2.0.8 - version: 2.0.8(@nomicfoundation/hardhat-viem@2.0.5(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(typescript@5.6.3)(viem@2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8))(chai@4.5.0)(ethers@6.13.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(viem@2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8)) + version: 2.0.8(@nomicfoundation/hardhat-viem@2.0.6(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(typescript@5.6.3)(viem@2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8))(chai@4.5.0)(ethers@6.13.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(viem@2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8)) hardhat-gas-reporter: specifier: ^2.2.1 - version: 2.2.1(bufferutil@4.0.8)(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8) + version: 2.2.1(bufferutil@4.0.8)(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8) prettier-plugin-solidity: specifier: ^1.4.1 version: 1.4.1(prettier@3.3.3) @@ -171,7 +171,7 @@ importers: version: 0.1.0(prettier-plugin-solidity@1.4.1(prettier@3.3.3))(prettier@3.3.3) solidity-coverage: specifier: ^0.8.13 - version: 0.8.13(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10)) + version: 0.8.13(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10)) typescript: specifier: ^5.5.4 version: 5.6.3 @@ -2693,36 +2693,36 @@ packages: resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} engines: {node: '>=12.4.0'} - '@nomicfoundation/edr-darwin-arm64@0.6.4': - resolution: {integrity: sha512-QNQErISLgssV9+qia8sIjRANqtbW8snSDvjspixT/kSQ5ZSGxxctTg7x72wPSrcu8+EBEveIe5uqENIp5GH8HQ==} + '@nomicfoundation/edr-darwin-arm64@0.7.0': + resolution: {integrity: sha512-vAH20oh4GaSB/iQFTRcoO8jLc0CLd9XuLY9I7vtcqZWAiM4U1J4Y8cu67PWmtxbvUQOqXR7S6FtAr8/AlWm14g==} engines: {node: '>= 18'} - '@nomicfoundation/edr-darwin-x64@0.6.4': - resolution: {integrity: sha512-cjVmREiwByyc9+oGfvAh49IAw+oVJHF9WWYRD+Tm/ZlSpnEVWxrGNBak2bd/JSYjn+mZE7gmWS4SMRi4nKaLUg==} + '@nomicfoundation/edr-darwin-x64@0.7.0': + resolution: {integrity: sha512-WHDdIrPvLlgXQr2eKypBM5xOZAwdxhDAEQIvEMQL8tEEm2qYW2bliUlssBPrs8E3bdivFbe1HizImslMAfU3+g==} engines: {node: '>= 18'} - '@nomicfoundation/edr-linux-arm64-gnu@0.6.4': - resolution: {integrity: sha512-96o9kRIVD6W5VkgKvUOGpWyUGInVQ5BRlME2Fa36YoNsRQMaKtmYJEU0ACosYES6ZTpYC8U5sjMulvPtVoEfOA==} + '@nomicfoundation/edr-linux-arm64-gnu@0.7.0': + resolution: {integrity: sha512-WXpJB54ukz1no7gxCPXVEw9pgl/9UZ/WO3l1ctyv/T7vOygjqA4SUd6kppTs6MNXAuTiisPtvJ/fmvHiMBLrsw==} engines: {node: '>= 18'} - '@nomicfoundation/edr-linux-arm64-musl@0.6.4': - resolution: {integrity: sha512-+JVEW9e5plHrUfQlSgkEj/UONrIU6rADTEk+Yp9pbe+mzNkJdfJYhs5JYiLQRP4OjxH4QOrXI97bKU6FcEbt5Q==} + '@nomicfoundation/edr-linux-arm64-musl@0.7.0': + resolution: {integrity: sha512-1iZYOcEgc+zJI7JQrlAFziuy9sBz1WgnIx3HIIu0J7lBRZ/AXeHHgATb+4InqxtEx9O3W8A0s7f11SyFqJL4Aw==} engines: {node: '>= 18'} - '@nomicfoundation/edr-linux-x64-gnu@0.6.4': - resolution: {integrity: sha512-nzYWW+fO3EZItOeP4CrdMgDXfaGBIBkKg0Y/7ySpUxLqzut40O4Mb0/+quqLAFkacUSWMlFp8nsmypJfOH5zoA==} + '@nomicfoundation/edr-linux-x64-gnu@0.7.0': + resolution: {integrity: sha512-wSjC94WcR5MM8sg9w3OsAmT6+bbmChJw6uJKoXR3qscps/jdhjzJWzfgT0XGRq3XMUfimyafW2RWOyfX3ouhrQ==} engines: {node: '>= 18'} - '@nomicfoundation/edr-linux-x64-musl@0.6.4': - resolution: {integrity: sha512-QFRoE9qSQ2boRrVeQ1HdzU+XN7NUgwZ1SIy5DQt4d7jCP+5qTNsq8LBNcqhRBOATgO63nsweNUhxX/Suj5r1Sw==} + '@nomicfoundation/edr-linux-x64-musl@0.7.0': + resolution: {integrity: sha512-Us22+AZ7wkG1mZwxqE4S4ZcuwkEA5VrUiBOJSvKHGOgy6vFvB/Euh5Lkp4GovwjrtiXuvyGO2UmtkzymZKDxZw==} engines: {node: '>= 18'} - '@nomicfoundation/edr-win32-x64-msvc@0.6.4': - resolution: {integrity: sha512-2yopjelNkkCvIjUgBGhrn153IBPLwnsDeNiq6oA0WkeM8tGmQi4td+PGi9jAriUDAkc59Yoi2q9hYA6efiY7Zw==} + '@nomicfoundation/edr-win32-x64-msvc@0.7.0': + resolution: {integrity: sha512-HAry0heTsWkzReVtjHwoIq3BgFCvXpVhJ5qPmTnegZGsr/KxqvMmHyDMifzKao4bycU8yrpTSyOiAJt27RWjzQ==} engines: {node: '>= 18'} - '@nomicfoundation/edr@0.6.4': - resolution: {integrity: sha512-YgrSuT3yo5ZQkbvBGqQ7hG+RDvz3YygSkddg4tb1Z0Y6pLXFzwrcEwWaJCFAVeeZxdxGfCgGMUYgRVneK+WXkw==} + '@nomicfoundation/edr@0.7.0': + resolution: {integrity: sha512-+Zyu7TE47TGNcPhOfWLPA/zISs32WDMXrhSWdWYyPHDVn/Uux5TVuOeScKb0BR/R8EJ+leR8COUF/EGxvDOVKg==} engines: {node: '>= 18'} '@nomicfoundation/ethereumjs-common@4.0.4': @@ -2795,11 +2795,10 @@ packages: peerDependencies: hardhat: ^2.0.4 - '@nomicfoundation/hardhat-viem@2.0.5': - resolution: {integrity: sha512-T3xqRzPwhKawqjKvqUT1fZUD85JxFhHjb/nAiJiuYQzaR6NyDPEJVwcc3CU5uwB79BwgQlSHDGOKjSNYmQP+VA==} + '@nomicfoundation/hardhat-viem@2.0.6': + resolution: {integrity: sha512-Pl5pvYK5VYKflfoUk4fVBESqKMNBtAIGPIT4j+Q8KNFueAe1vB2PsbRESeNJyW5YLL9pqKaD1RVqLmgIa1yvDg==} peerDependencies: hardhat: ^2.17.0 - typescript: ~5.0.0 viem: ^2.7.6 '@nomicfoundation/ignition-core@0.15.5': @@ -4066,7 +4065,7 @@ packages: engines: {node: '>= 0.4'} array-flatten@1.1.1: - resolution: {integrity: sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=} + resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} array-ify@1.0.0: resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} @@ -4741,7 +4740,7 @@ packages: resolution: {integrity: sha512-nadqwNxghAGTamwIqQSG433W6OADZx2vCo3UXHNrzTRHK/htu+7+L0zhjEoaeaQVNAi3YgqWDv8+tzf0hRfR+A==} concat-map@0.0.1: - resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} concat-stream@1.6.2: resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} @@ -4853,7 +4852,7 @@ packages: resolution: {integrity: sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==} cookie-signature@1.0.6: - resolution: {integrity: sha1-4wOogrNCzD7oylE6eZmXNNqzriw=} + resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} cookie@0.4.2: resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==} @@ -5258,7 +5257,7 @@ packages: deprecated: Please upgrade to v0.4+ ee-first@1.1.1: - resolution: {integrity: sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=} + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} effect@3.6.5: resolution: {integrity: sha512-NhopZTAKljaAlR0CEroOAJJngdqg7bzlnWcDrCwh4d2WNVohVbBtUS4SGqLt8tUy7IFsTWATYiUtmhDG+YELjA==} @@ -5678,7 +5677,7 @@ packages: engines: {node: '>=14.0.0'} ethjs-unit@0.1.6: - resolution: {integrity: sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk=} + resolution: {integrity: sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==} engines: {node: '>=6.5.0', npm: '>=3'} ethjs-util@0.1.6: @@ -5814,6 +5813,14 @@ packages: fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + fdir@6.4.3: + resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + fetch-blob@3.2.0: resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} engines: {node: ^12.20 || >= 14.13} @@ -5856,10 +5863,6 @@ packages: resolution: {integrity: sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==} engines: {node: '>=18'} - find-up@2.1.0: - resolution: {integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==} - engines: {node: '>=4'} - find-up@3.0.0: resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} engines: {node: '>=6'} @@ -6099,10 +6102,6 @@ packages: resolution: {integrity: sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==} deprecated: Glob versions prior to v9 are no longer supported - glob@7.2.0: - resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==} - deprecated: Glob versions prior to v9 are no longer supported - glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported @@ -6249,8 +6248,8 @@ packages: peerDependencies: hardhat: ^2.16.0 - hardhat@2.22.15: - resolution: {integrity: sha512-BpTGa9PE/sKAaHi4s/S1e9WGv63DR1m7Lzfd60C8gSEchDPfAJssVRSq0MZ2v2k76ig9m0kHAwVLf5teYwu/Mw==} + hardhat@2.22.18: + resolution: {integrity: sha512-2+kUz39gvMo56s75cfLBhiFedkQf+gXdrwCcz4R/5wW0oBdwiyfj2q9BIkMoaA0WIGYYMU2I1Cc4ucTunhfjzw==} hasBin: true peerDependencies: ts-node: '*' @@ -6540,6 +6539,7 @@ packages: ipfs-http-client@55.0.0: resolution: {integrity: sha512-GpvEs7C7WL9M6fN/kZbjeh4Y8YN7rY8b18tVWZnKxRsVwM25cIFrRI8CwNt3Ugin9yShieI3i9sPyzYGMrLNnQ==} engines: {node: '>=14.0.0', npm: '>=3.0.0'} + deprecated: js-IPFS has been deprecated in favour of Helia - please see https://github.com/ipfs/js-ipfs/issues/4336 for details ipfs-unixfs@6.0.9: resolution: {integrity: sha512-0DQ7p0/9dRB6XCb0mVCTli33GzIzSVx5udpJuVM47tGcD+W+Bl4LsnoLswd3ggNnNEakMv1FdoFITiEnchXDqQ==} @@ -6661,7 +6661,7 @@ packages: engines: {node: '>=0.10.0'} is-hex-prefixed@1.0.0: - resolution: {integrity: sha1-fY035q135dEnFIkTxXPggtd39VQ=} + resolution: {integrity: sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==} engines: {node: '>=6.5.0', npm: '>=3'} is-inside-container@1.0.0: @@ -7246,10 +7246,6 @@ packages: lit@2.8.0: resolution: {integrity: sha512-4Sc3OFX9QHOJaHbmTMk28SYgVxLN3ePDjg7hofEft2zWlehFL3LiAuapWc4U/kYwMYJSh2hTCPZ6/LIC7ii0MA==} - locate-path@2.0.0: - resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==} - engines: {node: '>=4'} - locate-path@3.0.0: resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} engines: {node: '>=6'} @@ -7462,7 +7458,7 @@ packages: resolution: {integrity: sha512-1N4qp+jE0pL5Xv4uEcwVUhIkwdUO3S/9gML90nqKA7v7FcOS5vUtatfzok9S9U1EJU8dHWlcv95WLnKmmxZI9w==} media-typer@0.3.0: - resolution: {integrity: sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=} + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} memoize-one@5.2.1: @@ -7943,7 +7939,7 @@ packages: resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} number-to-bn@1.7.0: - resolution: {integrity: sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA=} + resolution: {integrity: sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==} engines: {node: '>=6.5.0', npm: '>=3'} nwsapi@2.2.12: @@ -8103,10 +8099,6 @@ packages: resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} engines: {node: '>=4'} - p-limit@1.3.0: - resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==} - engines: {node: '>=4'} - p-limit@2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} @@ -8115,10 +8107,6 @@ packages: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} - p-locate@2.0.0: - resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==} - engines: {node: '>=4'} - p-locate@3.0.0: resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} engines: {node: '>=6'} @@ -8139,10 +8127,6 @@ packages: resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} engines: {node: '>=10'} - p-try@1.0.0: - resolution: {integrity: sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==} - engines: {node: '>=4'} - p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} @@ -8249,9 +8233,6 @@ packages: resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} engines: {node: '>=0.12'} - picocolors@1.1.0: - resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -8259,6 +8240,10 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + pidtree@0.6.0: resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} engines: {node: '>=0.10'} @@ -9326,7 +9311,7 @@ packages: engines: {node: '>=12'} strip-hex-prefix@1.0.0: - resolution: {integrity: sha1-DF8VX+8RUTczd96du1iNoFUA428=} + resolution: {integrity: sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==} engines: {node: '>=6.5.0', npm: '>=3'} strip-indent@3.0.0: @@ -9517,6 +9502,10 @@ packages: tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tinyglobby@0.2.10: + resolution: {integrity: sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==} + engines: {node: '>=12.0.0'} + tmp-promise@3.0.3: resolution: {integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==} @@ -9643,7 +9632,7 @@ packages: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} tsort@0.0.1: - resolution: {integrity: sha1-4igPXoF/i/QnVlf9D5rr1E9aJ4Y=} + resolution: {integrity: sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==} tweetnacl-util@0.15.1: resolution: {integrity: sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==} @@ -9931,7 +9920,7 @@ packages: resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} utils-merge@1.0.1: - resolution: {integrity: sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=} + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} uuid@8.3.2: @@ -10371,7 +10360,7 @@ snapshots: '@babel/code-frame@7.24.7': dependencies: '@babel/highlight': 7.24.7 - picocolors: 1.1.0 + picocolors: 1.1.1 '@babel/code-frame@7.26.2': dependencies: @@ -10619,7 +10608,7 @@ snapshots: '@babel/helper-validator-identifier': 7.24.7 chalk: 2.4.2 js-tokens: 4.0.0 - picocolors: 1.1.0 + picocolors: 1.1.1 '@babel/parser@7.25.6': dependencies: @@ -13196,7 +13185,7 @@ snapshots: binary-install-raw: 0.0.13(debug@4.3.4) chalk: 3.0.0 chokidar: 3.5.3 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 docker-compose: 0.23.19 dockerode: 2.5.8 fs-extra: 9.1.0 @@ -13912,29 +13901,29 @@ snapshots: '@nolyfill/is-core-module@1.0.39': {} - '@nomicfoundation/edr-darwin-arm64@0.6.4': {} + '@nomicfoundation/edr-darwin-arm64@0.7.0': {} - '@nomicfoundation/edr-darwin-x64@0.6.4': {} + '@nomicfoundation/edr-darwin-x64@0.7.0': {} - '@nomicfoundation/edr-linux-arm64-gnu@0.6.4': {} + '@nomicfoundation/edr-linux-arm64-gnu@0.7.0': {} - '@nomicfoundation/edr-linux-arm64-musl@0.6.4': {} + '@nomicfoundation/edr-linux-arm64-musl@0.7.0': {} - '@nomicfoundation/edr-linux-x64-gnu@0.6.4': {} + '@nomicfoundation/edr-linux-x64-gnu@0.7.0': {} - '@nomicfoundation/edr-linux-x64-musl@0.6.4': {} + '@nomicfoundation/edr-linux-x64-musl@0.7.0': {} - '@nomicfoundation/edr-win32-x64-msvc@0.6.4': {} + '@nomicfoundation/edr-win32-x64-msvc@0.7.0': {} - '@nomicfoundation/edr@0.6.4': + '@nomicfoundation/edr@0.7.0': dependencies: - '@nomicfoundation/edr-darwin-arm64': 0.6.4 - '@nomicfoundation/edr-darwin-x64': 0.6.4 - '@nomicfoundation/edr-linux-arm64-gnu': 0.6.4 - '@nomicfoundation/edr-linux-arm64-musl': 0.6.4 - '@nomicfoundation/edr-linux-x64-gnu': 0.6.4 - '@nomicfoundation/edr-linux-x64-musl': 0.6.4 - '@nomicfoundation/edr-win32-x64-msvc': 0.6.4 + '@nomicfoundation/edr-darwin-arm64': 0.7.0 + '@nomicfoundation/edr-darwin-x64': 0.7.0 + '@nomicfoundation/edr-linux-arm64-gnu': 0.7.0 + '@nomicfoundation/edr-linux-arm64-musl': 0.7.0 + '@nomicfoundation/edr-linux-x64-gnu': 0.7.0 + '@nomicfoundation/edr-linux-x64-musl': 0.7.0 + '@nomicfoundation/edr-win32-x64-msvc': 0.7.0 '@nomicfoundation/ethereumjs-common@4.0.4': dependencies: @@ -13956,61 +13945,61 @@ snapshots: '@nomicfoundation/ethereumjs-rlp': 5.0.4 ethereum-cryptography: 0.1.3 - '@nomicfoundation/hardhat-ignition-viem@0.15.5(@nomicfoundation/hardhat-ignition@0.15.5(@nomicfoundation/hardhat-verify@2.0.11(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@nomicfoundation/hardhat-viem@2.0.5(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(typescript@5.6.3)(viem@2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8))(@nomicfoundation/ignition-core@0.15.5(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(viem@2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8))': + '@nomicfoundation/hardhat-ignition-viem@0.15.5(@nomicfoundation/hardhat-ignition@0.15.5(@nomicfoundation/hardhat-verify@2.0.11(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@nomicfoundation/hardhat-viem@2.0.6(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(typescript@5.6.3)(viem@2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8))(@nomicfoundation/ignition-core@0.15.5(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(viem@2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8))': dependencies: - '@nomicfoundation/hardhat-ignition': 0.15.5(@nomicfoundation/hardhat-verify@2.0.11(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) - '@nomicfoundation/hardhat-viem': 2.0.5(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(typescript@5.6.3)(viem@2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) + '@nomicfoundation/hardhat-ignition': 0.15.5(@nomicfoundation/hardhat-verify@2.0.11(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) + '@nomicfoundation/hardhat-viem': 2.0.6(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(typescript@5.6.3)(viem@2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) '@nomicfoundation/ignition-core': 0.15.5(bufferutil@4.0.8)(utf-8-validate@5.0.10) - hardhat: 2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10) + hardhat: 2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10) viem: 2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8) - '@nomicfoundation/hardhat-ignition@0.15.5(@nomicfoundation/hardhat-verify@2.0.11(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)': + '@nomicfoundation/hardhat-ignition@0.15.5(@nomicfoundation/hardhat-verify@2.0.11(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)': dependencies: - '@nomicfoundation/hardhat-verify': 2.0.11(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10)) + '@nomicfoundation/hardhat-verify': 2.0.11(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10)) '@nomicfoundation/ignition-core': 0.15.5(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@nomicfoundation/ignition-ui': 0.15.8 chalk: 4.1.2 debug: 4.3.7(supports-color@8.1.1) fs-extra: 10.1.0 - hardhat: 2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10) + hardhat: 2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10) prompts: 2.4.2 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - '@nomicfoundation/hardhat-network-helpers@1.0.12(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))': + '@nomicfoundation/hardhat-network-helpers@1.0.12(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))': dependencies: ethereumjs-util: 7.1.5 - hardhat: 2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10) + hardhat: 2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10) - '@nomicfoundation/hardhat-toolbox-viem@3.0.0(k45xe6q2gf6gmym2ylceu5kr6y)': + '@nomicfoundation/hardhat-toolbox-viem@3.0.0(gmornzcqjt75ymzx6xpsbxhru4)': dependencies: - '@nomicfoundation/hardhat-ignition-viem': 0.15.5(@nomicfoundation/hardhat-ignition@0.15.5(@nomicfoundation/hardhat-verify@2.0.11(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@nomicfoundation/hardhat-viem@2.0.5(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(typescript@5.6.3)(viem@2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8))(@nomicfoundation/ignition-core@0.15.5(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(viem@2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8)) - '@nomicfoundation/hardhat-network-helpers': 1.0.12(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10)) - '@nomicfoundation/hardhat-verify': 2.0.11(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10)) - '@nomicfoundation/hardhat-viem': 2.0.5(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(typescript@5.6.3)(viem@2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) + '@nomicfoundation/hardhat-ignition-viem': 0.15.5(@nomicfoundation/hardhat-ignition@0.15.5(@nomicfoundation/hardhat-verify@2.0.11(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@nomicfoundation/hardhat-viem@2.0.6(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(typescript@5.6.3)(viem@2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8))(@nomicfoundation/ignition-core@0.15.5(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(viem@2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8)) + '@nomicfoundation/hardhat-network-helpers': 1.0.12(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10)) + '@nomicfoundation/hardhat-verify': 2.0.11(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10)) + '@nomicfoundation/hardhat-viem': 2.0.6(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(typescript@5.6.3)(viem@2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) '@types/chai': 4.3.16 '@types/chai-as-promised': 7.1.8 '@types/mocha': 10.0.9 '@types/node': 22.8.6 chai: 4.5.0 chai-as-promised: 7.1.2(chai@4.5.0) - hardhat: 2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10) - hardhat-gas-reporter: 2.2.1(bufferutil@4.0.8)(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8) - solidity-coverage: 0.8.13(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10)) + hardhat: 2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10) + hardhat-gas-reporter: 2.2.1(bufferutil@4.0.8)(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8) + solidity-coverage: 0.8.13(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10)) ts-node: 10.9.2(@types/node@22.8.6)(typescript@5.6.3) typescript: 5.6.3 viem: 2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8) - '@nomicfoundation/hardhat-verify@2.0.11(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))': + '@nomicfoundation/hardhat-verify@2.0.11(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))': dependencies: '@ethersproject/abi': 5.7.0 '@ethersproject/address': 5.7.0 cbor: 8.1.0 chalk: 2.4.2 debug: 4.3.7(supports-color@8.1.1) - hardhat: 2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10) + hardhat: 2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10) lodash.clonedeep: 4.5.0 semver: 6.3.1 table: 6.8.2 @@ -14018,14 +14007,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@nomicfoundation/hardhat-viem@2.0.5(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(typescript@5.6.3)(viem@2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)': + '@nomicfoundation/hardhat-viem@2.0.6(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(typescript@5.6.3)(viem@2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8)': dependencies: abitype: 0.9.10(typescript@5.6.3)(zod@3.23.8) - hardhat: 2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10) + hardhat: 2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10) lodash.memoize: 4.1.2 - typescript: 5.6.3 viem: 2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8) transitivePeerDependencies: + - typescript - zod '@nomicfoundation/ignition-core@0.15.5(bufferutil@4.0.8)(utf-8-validate@5.0.10)': @@ -14103,7 +14092,7 @@ snapshots: supports-color: 8.1.1 supports-hyperlinks: 2.3.0 ts-node: 10.9.2(@types/node@22.8.6)(typescript@5.6.3) - tslib: 2.7.0 + tslib: 2.8.1 widest-line: 3.1.0 wordwrap: 1.0.0 wrap-ansi: 7.0.0 @@ -14122,7 +14111,7 @@ snapshots: chalk: 4.1.2 clean-stack: 3.0.1 cli-progress: 3.12.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.7(supports-color@8.1.1) ejs: 3.1.10 fs-extra: 9.1.0 get-package-type: 0.1.0 @@ -14134,7 +14123,7 @@ snapshots: natural-orderby: 2.0.3 object-treeify: 1.1.33 password-prompt: 1.1.3 - semver: 7.4.0 + semver: 7.6.3 string-width: 4.2.3 strip-ansi: 6.0.1 supports-color: 8.1.1 @@ -14154,7 +14143,7 @@ snapshots: dependencies: '@oclif/core': 2.16.0(@types/node@22.8.6)(typescript@5.6.3) chalk: 4.1.2 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.7(supports-color@8.1.1) transitivePeerDependencies: - '@swc/core' - '@swc/wasm' @@ -14244,18 +14233,18 @@ snapshots: dependencies: asn1js: 3.0.5 pvtsutils: 1.3.5 - tslib: 2.7.0 + tslib: 2.8.1 '@peculiar/json-schema@1.1.12': dependencies: - tslib: 2.7.0 + tslib: 2.8.1 '@peculiar/webcrypto@1.5.0': dependencies: '@peculiar/asn1-schema': 2.3.13 '@peculiar/json-schema': 1.1.12 pvtsutils: 1.3.5 - tslib: 2.7.0 + tslib: 2.8.1 webcrypto-core: 1.8.0 '@pkgjs/parseargs@0.11.0': @@ -15058,7 +15047,7 @@ snapshots: '@types/concat-stream@1.6.1': dependencies: - '@types/node': 8.10.66 + '@types/node': 22.8.6 '@types/connect@3.4.38': dependencies: @@ -15081,11 +15070,11 @@ snapshots: '@types/execa@0.9.0': dependencies: - '@types/node': 22.6.1 + '@types/node': 22.8.6 '@types/form-data@0.0.33': dependencies: - '@types/node': 8.10.66 + '@types/node': 22.8.6 '@types/glob@7.2.0': dependencies: @@ -15926,7 +15915,7 @@ snapshots: busboy: 1.6.0 fast-querystring: 1.1.2 fast-url-parser: 1.1.3 - tslib: 2.7.0 + tslib: 2.8.1 '@wry/caches@1.0.1': dependencies: @@ -15934,7 +15923,7 @@ snapshots: '@wry/context@0.7.4': dependencies: - tslib: 2.7.0 + tslib: 2.8.1 '@wry/equality@0.5.7': dependencies: @@ -15942,7 +15931,7 @@ snapshots: '@wry/trie@0.4.3': dependencies: - tslib: 2.7.0 + tslib: 2.8.1 '@wry/trie@0.5.0': dependencies: @@ -16186,7 +16175,7 @@ snapshots: aria-hidden@1.2.4: dependencies: - tslib: 2.7.0 + tslib: 2.8.1 aria-query@5.1.3: dependencies: @@ -17069,7 +17058,7 @@ snapshots: node-fetch: 3.3.2 open: 8.4.2 ora: 6.3.1 - picocolors: 1.1.0 + picocolors: 1.1.1 pkg-install: 1.0.0 qs: 6.13.0 run-async: 2.4.1 @@ -17429,11 +17418,9 @@ snapshots: dependencies: ms: 2.1.3 - debug@4.3.4(supports-color@8.1.1): + debug@4.3.4: dependencies: ms: 2.1.2 - optionalDependencies: - supports-color: 8.1.1 debug@4.3.7(supports-color@8.1.1): dependencies: @@ -18478,6 +18465,10 @@ snapshots: dependencies: bser: 2.1.1 + fdir@6.4.3(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + fetch-blob@3.2.0: dependencies: node-domexception: 1.0.0 @@ -18536,10 +18527,6 @@ snapshots: find-up-simple@1.0.0: {} - find-up@2.1.0: - dependencies: - locate-path: 2.0.0 - find-up@3.0.0: dependencies: locate-path: 3.0.0 @@ -18570,13 +18557,13 @@ snapshots: focus-lock@1.3.5: dependencies: - tslib: 2.7.0 + tslib: 2.8.1 focus-visible@5.2.1: {} follow-redirects@1.15.9(debug@4.3.4): optionalDependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 follow-redirects@1.15.9(debug@4.3.7): optionalDependencies: @@ -18795,15 +18782,6 @@ snapshots: once: 1.4.0 path-is-absolute: 1.0.1 - glob@7.2.0: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - glob@7.2.3: dependencies: fs.realpath: 1.0.0 @@ -19007,19 +18985,19 @@ snapshots: optionalDependencies: uglify-js: 3.19.3 - hardhat-chai-matchers-viem@2.0.8(@nomicfoundation/hardhat-viem@2.0.5(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(typescript@5.6.3)(viem@2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8))(chai@4.5.0)(ethers@6.13.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(viem@2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8)): + hardhat-chai-matchers-viem@2.0.8(@nomicfoundation/hardhat-viem@2.0.6(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(typescript@5.6.3)(viem@2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8))(chai@4.5.0)(ethers@6.13.4(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(viem@2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8)): dependencies: - '@nomicfoundation/hardhat-viem': 2.0.5(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(typescript@5.6.3)(viem@2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) + '@nomicfoundation/hardhat-viem': 2.0.6(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(typescript@5.6.3)(viem@2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8))(zod@3.23.8) '@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.13.4(bufferutil@4.0.8)(utf-8-validate@5.0.10) - hardhat: 2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10) + hardhat: 2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10) ordinal: 1.0.3 viem: 2.17.0(bufferutil@4.0.8)(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8) - hardhat-gas-reporter@2.2.1(bufferutil@4.0.8)(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8): + hardhat-gas-reporter@2.2.1(bufferutil@4.0.8)(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10))(typescript@5.6.3)(utf-8-validate@5.0.10)(zod@3.23.8): dependencies: '@ethersproject/abi': 5.7.0 '@ethersproject/bytes': 5.7.0 @@ -19031,7 +19009,7 @@ snapshots: cli-table3: 0.6.5 ethereum-cryptography: 2.2.1 glob: 10.4.5 - hardhat: 2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10) + hardhat: 2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10) jsonschema: 1.4.1 lodash: 4.17.21 markdown-table: 2.0.0 @@ -19044,11 +19022,11 @@ snapshots: - utf-8-validate - zod - hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10): + hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10): dependencies: '@ethersproject/abi': 5.7.0 '@metamask/eth-sig-util': 4.0.1 - '@nomicfoundation/edr': 0.6.4 + '@nomicfoundation/edr': 0.7.0 '@nomicfoundation/ethereumjs-common': 4.0.4 '@nomicfoundation/ethereumjs-tx': 5.0.4 '@nomicfoundation/ethereumjs-util': 9.0.4 @@ -19060,7 +19038,6 @@ snapshots: aggregate-error: 3.1.0 ansi-escapes: 4.3.2 boxen: 5.1.2 - chalk: 2.4.2 chokidar: 4.0.1 ci-info: 2.0.0 debug: 4.3.7(supports-color@8.1.1) @@ -19068,10 +19045,9 @@ snapshots: env-paths: 2.2.1 ethereum-cryptography: 1.2.0 ethereumjs-abi: 0.6.8 - find-up: 2.1.0 + find-up: 5.0.0 fp-ts: 1.19.3 fs-extra: 7.0.1 - glob: 7.2.0 immutable: 4.3.7 io-ts: 1.10.4 json-stream-stringify: 3.1.6 @@ -19080,12 +19056,14 @@ snapshots: 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.3.7) source-map-support: 0.5.21 stacktrace-parser: 0.1.10 + tinyglobby: 0.2.10 tsort: 0.0.1 undici: 5.28.4 uuid: 8.3.2 @@ -19443,7 +19421,7 @@ snapshots: '@ipld/dag-pb': 2.1.18 abort-controller: 3.0.0 any-signal: 2.1.2 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.7(supports-color@8.1.1) err-code: 3.0.1 ipfs-core-types: 0.9.0(node-fetch@2.7.0(encoding@0.1.13)) ipfs-core-utils: 0.13.0(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13)) @@ -19833,7 +19811,7 @@ snapshots: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.6.1 + '@types/node': 22.8.6 chalk: 4.1.2 co: 4.6.0 dedent: 1.5.3(babel-plugin-macros@3.1.0) @@ -20023,7 +20001,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.6.1 + '@types/node': 22.8.6 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -20209,7 +20187,7 @@ snapshots: jest-worker@29.7.0: dependencies: - '@types/node': 22.6.1 + '@types/node': 22.8.6 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -20499,11 +20477,6 @@ snapshots: lit-element: 3.3.3 lit-html: 2.8.0 - locate-path@2.0.0: - dependencies: - p-locate: 2.0.0 - path-exists: 3.0.0 - locate-path@3.0.0: dependencies: p-locate: 3.0.0 @@ -21590,10 +21563,6 @@ snapshots: p-finally@1.0.0: {} - p-limit@1.3.0: - dependencies: - p-try: 1.0.0 - p-limit@2.3.0: dependencies: p-try: 2.2.0 @@ -21602,10 +21571,6 @@ snapshots: dependencies: yocto-queue: 0.1.0 - p-locate@2.0.0: - dependencies: - p-limit: 1.3.0 - p-locate@3.0.0: dependencies: p-limit: 2.3.0 @@ -21624,8 +21589,6 @@ snapshots: dependencies: aggregate-error: 3.1.0 - p-try@1.0.0: {} - p-try@2.2.0: {} package-json-from-dist@1.0.1: {} @@ -21714,12 +21677,12 @@ snapshots: safe-buffer: 5.2.1 sha.js: 2.4.11 - picocolors@1.1.0: {} - picocolors@1.1.1: {} picomatch@2.3.1: {} + picomatch@4.0.2: {} + pidtree@0.6.0: {} pify@3.0.0: {} @@ -21788,13 +21751,13 @@ snapshots: postcss@8.4.31: dependencies: nanoid: 3.3.7 - picocolors: 1.1.0 + picocolors: 1.1.1 source-map-js: 1.2.1 postcss@8.4.38: dependencies: nanoid: 3.3.7 - picocolors: 1.1.0 + picocolors: 1.1.1 source-map-js: 1.2.1 preact@10.24.3: {} @@ -21910,7 +21873,7 @@ snapshots: pvtsutils@1.3.5: dependencies: - tslib: 2.7.0 + tslib: 2.8.1 pvutils@1.1.3: {} @@ -22832,7 +22795,7 @@ snapshots: prettier-linter-helpers: 1.0.0 prettier-plugin-solidity: 1.4.1(prettier@3.3.3) - solidity-coverage@0.8.13(hardhat@2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10)): + solidity-coverage@0.8.13(hardhat@2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10)): dependencies: '@ethersproject/abi': 5.7.0 '@solidity-parser/parser': 0.18.0 @@ -22843,7 +22806,7 @@ snapshots: ghost-testrpc: 0.0.2 global-modules: 2.0.0 globby: 10.0.2 - hardhat: 2.22.15(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10) + hardhat: 2.22.18(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.8.6)(typescript@5.6.3))(typescript@5.6.3)(utf-8-validate@5.0.10) jsonschema: 1.4.1 lodash: 4.17.21 mocha: 10.8.2 @@ -23300,6 +23263,11 @@ snapshots: tiny-invariant@1.3.3: {} + tinyglobby@0.2.10: + dependencies: + fdir: 6.4.3(picomatch@4.0.2) + picomatch: 4.0.2 + tmp-promise@3.0.3: dependencies: tmp: 0.2.3 @@ -23683,14 +23651,14 @@ snapshots: use-callback-ref@1.3.2(@types/react@18.3.12)(react@18.3.1): dependencies: react: 18.3.1 - tslib: 2.7.0 + tslib: 2.8.1 optionalDependencies: '@types/react': 18.3.12 use-callback-ref@1.3.2(@types/react@18.3.8)(react@18.3.1): dependencies: react: 18.3.1 - tslib: 2.7.0 + tslib: 2.8.1 optionalDependencies: '@types/react': 18.3.8 @@ -23704,7 +23672,7 @@ snapshots: dependencies: detect-node-es: 1.1.0 react: 18.3.1 - tslib: 2.7.0 + tslib: 2.8.1 optionalDependencies: '@types/react': 18.3.12 @@ -23712,7 +23680,7 @@ snapshots: dependencies: detect-node-es: 1.1.0 react: 18.3.1 - tslib: 2.7.0 + tslib: 2.8.1 optionalDependencies: '@types/react': 18.3.8 @@ -23982,7 +23950,7 @@ snapshots: '@peculiar/json-schema': 1.1.12 asn1js: 3.0.5 pvtsutils: 1.3.5 - tslib: 2.7.0 + tslib: 2.8.1 webextension-polyfill@0.10.0: {}