diff --git a/contracts/deployment/fix/CMTATStandaloneFix.sol b/contracts/deployment/fix/CMTATStandaloneFix.sol new file mode 100644 index 00000000..8175ed24 --- /dev/null +++ b/contracts/deployment/fix/CMTATStandaloneFix.sol @@ -0,0 +1,25 @@ +//SPDX-License-Identifier: MPL-2.0 + +pragma solidity ^0.8.20; + +import {CMTATBaseFix} from "../../modules/1_CMTATBaseFix.sol"; +import {IFixDescriptorEngine} from "../../interfaces/engine/IFixDescriptorEngine.sol"; +import {ICMTATConstructor} from "../../interfaces/technical/ICMTATConstructor.sol"; + +/** + * @title CMTATStandaloneFix + * @notice Standalone deployment that wires an external FIX descriptor engine. + */ +contract CMTATStandaloneFix is CMTATBaseFix { + /// @custom:oz-upgrades-unsafe-allow constructor + constructor( + address admin, + ICMTATConstructor.ERC20Attributes memory ERC20Attributes_, + ICMTATConstructor.ExtraInformationAttributes memory extraInformationAttributes_, + ICMTATConstructor.Engine memory engines_, + IFixDescriptorEngine fixDescriptorEngine_ + ) { + initializeFix(admin, ERC20Attributes_, extraInformationAttributes_, engines_, fixDescriptorEngine_); + } +} + diff --git a/contracts/deployment/fix/CMTATUpgradeableFix.sol b/contracts/deployment/fix/CMTATUpgradeableFix.sol new file mode 100644 index 00000000..93c1f7f6 --- /dev/null +++ b/contracts/deployment/fix/CMTATUpgradeableFix.sol @@ -0,0 +1,17 @@ +//SPDX-License-Identifier: MPL-2.0 + +pragma solidity ^0.8.20; + +import {CMTATBaseFix} from "../../modules/1_CMTATBaseFix.sol"; + +/** + * @title CMTATUpgradeableFix + * @notice Upgradeable (Transparent/Beacon) deployment variant with FIX descriptors. + */ +contract CMTATUpgradeableFix is CMTATBaseFix { + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } +} + diff --git a/contracts/deployment/fix/CMTATUpgradeableUUPSFix.sol b/contracts/deployment/fix/CMTATUpgradeableUUPSFix.sol new file mode 100644 index 00000000..3a76ada6 --- /dev/null +++ b/contracts/deployment/fix/CMTATUpgradeableUUPSFix.sol @@ -0,0 +1,41 @@ +//SPDX-License-Identifier: MPL-2.0 + +pragma solidity ^0.8.20; + +import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import {CMTATBaseFix} from "../../modules/1_CMTATBaseFix.sol"; +import {IFixDescriptorEngine} from "../../interfaces/engine/IFixDescriptorEngine.sol"; +import {ICMTATConstructor} from "../../interfaces/technical/ICMTATConstructor.sol"; + +/** + * @title CMTATUpgradeableUUPSFix + * @notice UUPS proxy-compatible deployment variant with FIX descriptor support. + */ +contract CMTATUpgradeableUUPSFix is CMTATBaseFix, UUPSUpgradeable { + bytes32 public constant PROXY_UPGRADE_ROLE = keccak256("PROXY_UPGRADE_ROLE"); + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + function initializeFix( + address admin, + ICMTATConstructor.ERC20Attributes memory ERC20Attributes_, + ICMTATConstructor.ExtraInformationAttributes memory extraInformationAttributes_, + ICMTATConstructor.Engine memory engines_, + IFixDescriptorEngine fixDescriptorEngine_ + ) public override(CMTATBaseFix) initializer { + CMTATBaseFix.initializeFix( + admin, + ERC20Attributes_, + extraInformationAttributes_, + engines_, + fixDescriptorEngine_ + ); + __UUPSUpgradeable_init_unchained(); + } + + function _authorizeUpgrade(address) internal override onlyRole(PROXY_UPGRADE_ROLE) {} +} + diff --git a/contracts/engines/FixDescriptorEngine.sol b/contracts/engines/FixDescriptorEngine.sol new file mode 100644 index 00000000..6a58f6be --- /dev/null +++ b/contracts/engines/FixDescriptorEngine.sol @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: MPL-2.0 +pragma solidity ^0.8.20; + +import {IFixDescriptorEngine} from "../interfaces/engine/IFixDescriptorEngine.sol"; +import {IFixDescriptor} from "@fixdescriptorkit/contracts/src/IFixDescriptor.sol"; +import {FixDescriptorLib} from "@fixdescriptorkit/contracts/src/FixDescriptorLib.sol"; +import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; + +/** + * @title FixDescriptorEngine + * @notice Production implementation of FIX descriptor engine using FixDescriptorLib + * @dev Uses the full FixDescriptorLib from @fixdescriptorkit/contracts for all functionality + * including SSTORE2 reading, Merkle verification, and human-readable output with CBOR parsing + */ +contract FixDescriptorEngine is IFixDescriptorEngine, Ownable { + using FixDescriptorLib for FixDescriptorLib.Storage; + + /// @notice Storage for FIX descriptor using FixDescriptorLib + FixDescriptorLib.Storage private _fixDescriptor; + + /* ============ Events ============ */ + // Note: FixDescriptorLib emits its own events (FixDescriptorSet, FixDescriptorUpdated) + // through IFixDescriptor interface + + /* ============ Errors ============ */ + error DescriptorNotInitialized(); + + /** + * @notice Constructor + * @param initialOwner Address to receive ownership + */ + constructor(address initialOwner) Ownable(initialOwner) {} + + /* ============ Admin Functions ============ */ + + /** + * @notice Sets the FIX descriptor + * @dev Can be called multiple times (library handles first-set vs update) + * Emits FixDescriptorSet (first time) or FixDescriptorUpdated (subsequent) + * Requires owner privileges + * @param descriptor_ The complete FixDescriptor struct (with fixCBORPtr already set via SSTORE2) + */ + function setFixDescriptor( + FixDescriptor calldata descriptor_ + ) external onlyOwner { + IFixDescriptor.FixDescriptor calldata libDescriptor = _asFixDescriptor(descriptor_); + _fixDescriptor.setDescriptor(libDescriptor); + } + + /* ============ View Functions ============ */ + + /// @inheritdoc IFixDescriptorEngine + function getFixDescriptor() + external + view + override + returns (FixDescriptor memory descriptor) + { + if (!_fixDescriptor.isInitialized()) { + revert DescriptorNotInitialized(); + } + IFixDescriptor.FixDescriptor memory libDescriptor = _fixDescriptor.getDescriptor(); + return _fromLibDescriptor(libDescriptor); + } + + /// @inheritdoc IFixDescriptorEngine + function getFixRoot() + external + view + override + returns (bytes32 fixRoot) + { + if (!_fixDescriptor.isInitialized()) { + revert DescriptorNotInitialized(); + } + return _fixDescriptor.getRoot(); + } + + /// @inheritdoc IFixDescriptorEngine + function verifyField( + bytes calldata pathCBOR, + bytes calldata value, + bytes32[] calldata proof, + bool[] calldata directions + ) + external + view + override + returns (bool valid) + { + if (!_fixDescriptor.isInitialized()) { + return false; + } + return _fixDescriptor.verifyFieldProof(pathCBOR, value, proof, directions); + } + + /// @inheritdoc IFixDescriptorEngine + function getHumanReadableDescriptor() + external + view + override + returns (string memory readable) + { + if (!_fixDescriptor.isInitialized()) { + return "FIX Descriptor not initialized"; + } + // This uses FixHumanReadable.toHumanReadable() internally + // Requires dictionaryContract to be set for full tag name lookups + return _fixDescriptor.getHumanReadable(); + } + + /// @inheritdoc IFixDescriptorEngine + function getFixCBORChunk(uint256 start, uint256 size) + external + view + override + returns (bytes memory chunk) + { + if (!_fixDescriptor.isInitialized()) { + return ""; + } + // Reads from SSTORE2 bytecode using extcodecopy + return _fixDescriptor.getFixCBORChunk(start, size); + } + + /// @inheritdoc IFixDescriptorEngine + function isInitialized() + external + view + override + returns (bool initialized) + { + return _fixDescriptor.isInitialized(); + } + + /** + * @notice Get complete CBOR data from SSTORE2 storage + * @dev Convenience function to read all CBOR data at once + * Uses FixDescriptorLib.getFullCBORData() internally + * @return cborData The complete CBOR-encoded descriptor + */ + function getFullCBORData() external view returns (bytes memory cborData) { + if (!_fixDescriptor.isInitialized()) { + return ""; + } + return _fixDescriptor.getFullCBORData(); + } + + function _asFixDescriptor( + FixDescriptor calldata descriptor_ + ) + private + pure + returns (IFixDescriptor.FixDescriptor calldata libDescriptor) + { + assembly { + libDescriptor := descriptor_ + } + } + + function _fromLibDescriptor( + IFixDescriptor.FixDescriptor memory libDescriptor + ) private pure returns (FixDescriptor memory descriptor) { + descriptor.fixMajor = libDescriptor.fixMajor; + descriptor.fixMinor = libDescriptor.fixMinor; + descriptor.dictHash = libDescriptor.dictHash; + descriptor.dictionaryContract = libDescriptor.dictionaryContract; + descriptor.fixRoot = libDescriptor.fixRoot; + descriptor.fixCBORPtr = libDescriptor.fixCBORPtr; + descriptor.fixCBORLen = libDescriptor.fixCBORLen; + descriptor.fixURI = libDescriptor.fixURI; + } +} diff --git a/contracts/interfaces/engine/IFixDescriptorEngine.sol b/contracts/interfaces/engine/IFixDescriptorEngine.sol new file mode 100644 index 00000000..b85f896c --- /dev/null +++ b/contracts/interfaces/engine/IFixDescriptorEngine.sol @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: MPL-2.0 + +pragma solidity ^0.8.20; + +/** + * @title IFixDescriptorEngine + * @notice Interface for external FIX descriptor engines + * @dev Defines the standard interface for managing FIX Protocol descriptors + * following the ERC-FIX specification with CBOR encoding and Merkle commitments + */ +interface IFixDescriptorEngine { + + /// @notice FIX descriptor structure per ERC-FIX spec + struct FixDescriptor { + uint16 fixMajor; // FIX version major (e.g., 5) + uint16 fixMinor; // FIX version minor (e.g., 0) + bytes32 dictHash; // FIX dictionary hash + address dictionaryContract; // FixDictionary contract address + bytes32 fixRoot; // Merkle root commitment + address fixCBORPtr; // SSTORE2 data address + uint32 fixCBORLen; // CBOR payload length + string fixURI; // Optional off-chain mirror URI + } + + /* ============ View Functions ============ */ + + /** + * @notice Returns the complete FIX descriptor + * @return descriptor The FIX descriptor with all metadata + */ + function getFixDescriptor() external view returns (FixDescriptor memory descriptor); + + /** + * @notice Returns only the Merkle root for gas-efficient verification + * @return fixRoot The Merkle root commitment + */ + function getFixRoot() external view returns (bytes32 fixRoot); + + /** + * @notice Verifies a specific field using Merkle proof + * @param pathCBOR CBOR-encoded path to the field (e.g., [55] for Symbol) + * @param value The field value bytes to verify + * @param proof Array of sibling hashes for Merkle proof + * @param directions Boolean array indicating left (false) or right (true) at each level + * @return valid True if the field is valid, false otherwise + */ + function verifyField( + bytes calldata pathCBOR, + bytes calldata value, + bytes32[] calldata proof, + bool[] calldata directions + ) external view returns (bool valid); + + /** + * @notice Returns a human-readable representation of the FIX descriptor + * @dev Parses CBOR and formats as readable string (JSON-like or FIX tag=value) + * @return readable Human-readable descriptor string + */ + function getHumanReadableDescriptor() external view returns (string memory readable); + + /** + * @notice Get CBOR data chunk from SSTORE2 storage + * @dev Reads from SSTORE2 contract bytecode, handling STOP byte offset + * @param start Start offset (in the data, not including STOP byte) + * @param size Number of bytes to read + * @return chunk The requested CBOR data + */ + function getFixCBORChunk(uint256 start, uint256 size) external view returns (bytes memory chunk); + + /** + * @notice Checks if a FIX descriptor has been initialized + * @return initialized True if descriptor is set, false otherwise + */ + function isInitialized() external view returns (bool initialized); +} diff --git a/contracts/interfaces/modules/IFixDescriptorEngineModule.sol b/contracts/interfaces/modules/IFixDescriptorEngineModule.sol new file mode 100644 index 00000000..ee90a6d2 --- /dev/null +++ b/contracts/interfaces/modules/IFixDescriptorEngineModule.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MPL-2.0 + +pragma solidity ^0.8.20; + +import {IFixDescriptorEngine} from "../engine/IFixDescriptorEngine.sol"; + +/** + * @title IFixDescriptorEngineModule + * @notice Interface for modules that delegate FIX descriptor management to an external engine + */ +interface IFixDescriptorEngineModule { + + /* ============ Events ============ */ + + /** + * @notice Emitted when a new FIX descriptor engine is set + * @dev Indicates that the module now delegates FIX descriptor logic to a new external contract + * @param newFixDescriptorEngine The address of the newly assigned FIX descriptor engine + */ + event FixDescriptorEngine(IFixDescriptorEngine indexed newFixDescriptorEngine); + + /* ============ Errors ============ */ + + /** + * @notice Thrown when attempting to set the same FIX descriptor engine as the current one + */ + error CMTAT_FixDescriptorEngineModule_SameValue(); + + /* ============ Functions ============ */ + + /** + * @notice Sets a new FIX descriptor engine contract + * @dev Only changes the engine if the new address is different from the current one + * Throws {CMTAT_FixDescriptorEngineModule_SameValue} if the same engine is provided + * Allows setting the engine to address(0) to disable FIX descriptors. + * @param fixDescriptorEngine_ The address of the new IFixDescriptorEngine-compliant engine (or address(0) to clear) + */ + function setFixDescriptorEngine( + IFixDescriptorEngine fixDescriptorEngine_ + ) external; + + function fixDescriptorEngine() external view returns (address fixDescriptorEngine_); +} diff --git a/contracts/mocks/DataContractFactory.sol b/contracts/mocks/DataContractFactory.sol new file mode 100644 index 00000000..b526026b --- /dev/null +++ b/contracts/mocks/DataContractFactory.sol @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: MPL-2.0 +pragma solidity ^0.8.20; + +/** + * @title DataContractFactory + * @notice Factory for deploying SSTORE2-style data contracts + * @dev Uses the same pattern as 0xSequence's SSTORE2 library + * Stores data as contract bytecode which is much more gas-efficient than storage + */ +contract DataContractFactory { + event DataDeployed(address indexed ptr, uint256 length); + + /** + * @notice Deploy data to a contract using SSTORE2 pattern + * @dev Prepends STOP opcode (0x00) to prevent calls to the contract + * The data can then be read using EXTCODECOPY, skipping the first byte + * @param data The data to store + * @return ptr Address of the deployed data contract + */ + function deploy(bytes calldata data) external returns (address ptr) { + // Prepend STOP opcode (0x00) to prevent calls to the contract + bytes memory runtimeCode = abi.encodePacked(hex"00", data); + + // Create initialization code that returns the runtime code + // Pattern from 0xSequence SSTORE2: + // 0x63 - PUSH4 (size) + // size - runtime code size (4 bytes) + // 0x80 - DUP1 + // 0x60 0x0E - PUSH1 14 (offset where runtime code starts) + // 0x60 0x00 - PUSH1 0 (memory destination) + // 0x39 - CODECOPY + // 0x60 0x00 - PUSH1 0 (offset in memory to return from) + // 0xF3 - RETURN + bytes memory creationCode = abi.encodePacked( + hex"63", + uint32(runtimeCode.length), + hex"80_60_0E_60_00_39_60_00_F3", + runtimeCode + ); + + assembly { + ptr := create(0, add(creationCode, 0x20), mload(creationCode)) + } + + require(ptr != address(0), "DEPLOY_FAIL"); + emit DataDeployed(ptr, data.length); + } + + /** + * @notice Read data from an SSTORE2 pointer + * @dev Helper function to read data deployed via this factory + * @param ptr Address of the data contract + * @return data The stored data (excluding STOP byte) + */ + function read(address ptr) external view returns (bytes memory data) { + // Get code size + uint256 codeSize; + assembly { + codeSize := extcodesize(ptr) + } + + require(codeSize > 0, "NO_CODE"); + + // Data starts at byte 1 (after STOP byte at position 0) + uint256 dataLength = codeSize - 1; + data = new bytes(dataLength); + + assembly { + // extcodecopy(address, memory destination, code offset, size) + // Skip first byte (STOP opcode) by starting at offset 1 + extcodecopy(ptr, add(data, 0x20), 1, dataLength) + } + } + + /** + * @notice Read a chunk of data from an SSTORE2 pointer + * @dev More gas-efficient when you don't need all the data + * @param ptr Address of the data contract + * @param start Start offset (in the data, not including STOP byte) + * @param size Number of bytes to read + * @return chunk The requested data chunk + */ + function readChunk(address ptr, uint256 start, uint256 size) + external + view + returns (bytes memory chunk) + { + // Get code size + uint256 codeSize; + assembly { + codeSize := extcodesize(ptr) + } + + require(codeSize > 0, "NO_CODE"); + + // Data starts at byte 1 (after STOP byte) + uint256 dataLength = codeSize - 1; + + // Validate and adjust range + if (start >= dataLength) { + return new bytes(0); + } + + uint256 end = start + size; + if (end > dataLength) { + end = dataLength; + } + + uint256 actualSize = end - start; + chunk = new bytes(actualSize); + + assembly { + // Add 1 to start to skip the STOP byte + extcodecopy(ptr, add(chunk, 0x20), add(start, 1), actualSize) + } + } +} diff --git a/contracts/mocks/FixDescriptorEngineMock.sol b/contracts/mocks/FixDescriptorEngineMock.sol new file mode 100644 index 00000000..0fbedaff --- /dev/null +++ b/contracts/mocks/FixDescriptorEngineMock.sol @@ -0,0 +1,301 @@ + +// SPDX-License-Identifier: MPL-2.0 +pragma solidity ^0.8.20; +import {IFixDescriptorEngine} from "../interfaces/engine/IFixDescriptorEngine.sol"; + +/** + * @title FixDescriptorEngine Mock + * @notice Mock implementation for testing - NOT for production use + * @dev Provides simple storage and retrieval without SSTORE2 or full verification + */ +contract FixDescriptorEngineMock is IFixDescriptorEngine { + + FixDescriptor private _descriptor; + bytes private _cborData; + bool private _isInitialized; + + /* ============ Errors ============ */ + error DescriptorNotInitialized(); + error DescriptorAlreadyInitialized(); + + /* ============ Events ============ */ + event DescriptorSet(bytes32 indexed fixRoot, uint16 fixMajor, uint16 fixMinor); + event DescriptorUpdated(bytes32 indexed fixRoot, uint16 fixMajor, uint16 fixMinor); + + /* ============ Admin Functions ============ */ + + /** + * @notice Sets the FIX descriptor (mock - no access control for simplicity) + * @param descriptor_ The FIX descriptor to store + * @param cborData_ The CBOR-encoded data + */ + function setFixDescriptor( + FixDescriptor calldata descriptor_, + bytes calldata cborData_ + ) external { + require(!_isInitialized, DescriptorAlreadyInitialized()); + _descriptor = descriptor_; + _cborData = cborData_; + _isInitialized = true; + emit DescriptorSet(descriptor_.fixRoot, descriptor_.fixMajor, descriptor_.fixMinor); + } + + /** + * @notice Updates the FIX descriptor (mock - allows updates for testing) + * @param descriptor_ The FIX descriptor to store + * @param cborData_ The CBOR-encoded data + */ + function updateFixDescriptor( + FixDescriptor calldata descriptor_, + bytes calldata cborData_ + ) external { + _descriptor = descriptor_; + _cborData = cborData_; + _isInitialized = true; + emit DescriptorUpdated(descriptor_.fixRoot, descriptor_.fixMajor, descriptor_.fixMinor); + } + + /* ============ View Functions ============ */ + + /// @inheritdoc IFixDescriptorEngine + function getFixDescriptor() + external + view + override + returns (FixDescriptor memory descriptor) + { + if (!_isInitialized) { + revert DescriptorNotInitialized(); + } + return _descriptor; + } + + /// @inheritdoc IFixDescriptorEngine + function getFixRoot() + external + view + override + returns (bytes32 fixRoot) + { + return _descriptor.fixRoot; + } + + /// @inheritdoc IFixDescriptorEngine + function verifyField( + bytes calldata pathCBOR, + bytes calldata value, + bytes32[] calldata proof, + bool[] calldata directions + ) + external + view + override + returns (bool valid) + { + // Mock implementation - simple hash check without full Merkle verification + bytes32 leaf = keccak256(abi.encodePacked(pathCBOR, value)); + + // For mock, just verify proof is not empty and root matches + if (proof.length == 0 || directions.length != proof.length) { + return false; + } + + bytes32 computedHash = leaf; + for (uint256 i = 0; i < proof.length; i++) { + if (directions[i]) { + computedHash = keccak256(abi.encodePacked(computedHash, proof[i])); + } else { + computedHash = keccak256(abi.encodePacked(proof[i], computedHash)); + } + } + + return computedHash == _descriptor.fixRoot; + } + + /// @inheritdoc IFixDescriptorEngine + function getHumanReadableDescriptor() + external + view + override + returns (string memory readable) + { + if (!_isInitialized) { + return "FIX Descriptor not initialized"; + } + + // Mock implementation: Returns simplified output + // Production should use FixHumanReadable.toHumanReadable() with full CBOR parsing + // and dictionary contract for tag name lookups + + string memory dictInfo = ""; + if (_descriptor.dictionaryContract != address(0)) { + dictInfo = string(abi.encodePacked( + " | Dict: ", + _addressToHexString(_descriptor.dictionaryContract) + )); + } + + return string(abi.encodePacked( + "FIX ", + _uint16ToString(_descriptor.fixMajor), + ".", + _uint16ToString(_descriptor.fixMinor), + " | Root: 0x", + _bytes32ToHexString(_descriptor.fixRoot), + dictInfo, + " | [MOCK - Use production engine for full CBOR parsing]" + )); + } + + /// @inheritdoc IFixDescriptorEngine + function getFixCBORChunk(uint256 start, uint256 size) + external + view + override + returns (bytes memory chunk) + { + if (!_isInitialized) { + revert DescriptorNotInitialized(); + } + + // If fixCBORPtr is set, read from SSTORE2 bytecode (like production) + // Otherwise read from storage (for testing without SSTORE2) + if (_descriptor.fixCBORPtr != address(0)) { + return _readFromSSTORE2(_descriptor.fixCBORPtr, start, size); + } else { + return _readFromStorage(start, size); + } + } + + /* ============ Internal Reading Functions ============ */ + + /** + * @dev Read chunk from SSTORE2 bytecode (simulates production behavior) + * @param ptr SSTORE2 contract address + * @param start Start offset + * @param size Chunk size + * @return chunk The data chunk + */ + function _readFromSSTORE2(address ptr, uint256 start, uint256 size) + internal + view + returns (bytes memory chunk) + { + // Get code size using extcodesize + uint256 codeSize; + assembly { + codeSize := extcodesize(ptr) + } + + require(codeSize > 0, "No CBOR data at pointer"); + + // Data starts at byte 1 (after STOP byte at position 0) + // Data length is codeSize - 1 + uint256 dataLength = codeSize - 1; + + // Validate and adjust range + if (start >= dataLength) { + return new bytes(0); + } + + uint256 end = start + size; + if (end > dataLength) { + end = dataLength; + } + + uint256 actualSize = end - start; + + // Use extcodecopy to read directly from contract bytecode + // Add 1 to start to skip the STOP byte (data begins at position 1) + chunk = new bytes(actualSize); + assembly { + extcodecopy(ptr, add(chunk, 0x20), add(start, 1), actualSize) + } + } + + /** + * @dev Read chunk from storage array (for testing without SSTORE2) + * @param start Start offset + * @param size Chunk size + * @return chunk The data chunk + */ + function _readFromStorage(uint256 start, uint256 size) + internal + view + returns (bytes memory chunk) + { + uint256 dataLength = _cborData.length; + + // Validate and adjust range + if (start >= dataLength) { + return new bytes(0); + } + + uint256 end = start + size; + if (end > dataLength) { + end = dataLength; + } + + uint256 actualSize = end - start; + chunk = new bytes(actualSize); + + // Copy data from storage + for (uint256 i = 0; i < actualSize; i++) { + chunk[i] = _cborData[start + i]; + } + } + + /// @inheritdoc IFixDescriptorEngine + function isInitialized() + external + view + override + returns (bool initialized) + { + return _isInitialized; + } + + /* ============ Helper Functions ============ */ + + function _uint16ToString(uint16 value) private pure returns (string memory) { + if (value == 0) { + return "0"; + } + uint16 temp = value; + uint256 digits; + while (temp != 0) { + digits++; + temp /= 10; + } + bytes memory buffer = new bytes(digits); + while (value != 0) { + digits -= 1; + buffer[digits] = bytes1(uint8(48 + uint16(value % 10))); + value /= 10; + } + return string(buffer); + } + + function _bytes32ToHexString(bytes32 value) private pure returns (string memory) { + bytes memory alphabet = "0123456789abcdef"; + bytes memory str = new bytes(64); + for (uint256 i = 0; i < 32; i++) { + str[i*2] = alphabet[uint8(value[i] >> 4)]; + str[1+i*2] = alphabet[uint8(value[i] & 0x0f)]; + } + return string(str); + } + + function _addressToHexString(address addr) private pure returns (string memory) { + bytes memory alphabet = "0123456789abcdef"; + bytes memory str = new bytes(42); // "0x" + 40 hex chars + str[0] = "0"; + str[1] = "x"; + for (uint256 i = 0; i < 20; i++) { + uint8 b = uint8(uint160(addr) >> (8 * (19 - i))); + str[2 + i * 2] = alphabet[b >> 4]; + str[3 + i * 2] = alphabet[b & 0x0f]; + } + return string(str); + } +} diff --git a/contracts/mocks/FixDescriptorEngineModuleHarness.sol b/contracts/mocks/FixDescriptorEngineModuleHarness.sol new file mode 100644 index 00000000..27f3a919 --- /dev/null +++ b/contracts/mocks/FixDescriptorEngineModuleHarness.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MPL-2.0 + +pragma solidity ^0.8.20; + +import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; +import {FixDescriptorEngineModule} from "../modules/wrapper/extensions/FixDescriptorEngineModule.sol"; +import {IFixDescriptorEngine} from "../interfaces/engine/IFixDescriptorEngine.sol"; + +/** + * @title FixDescriptorEngineModuleHarness + * @notice Test helper harness that exposes the FixDescriptorEngineModule initializer workflow. + */ +contract FixDescriptorEngineModuleHarness is FixDescriptorEngineModule, AccessControlUpgradeable { + /// @notice Initializes access control and optionally wires an initial FIX descriptor engine. + function initialize(address admin, IFixDescriptorEngine engine) external initializer { + __AccessControl_init(); + _grantRole(DEFAULT_ADMIN_ROLE, admin); + _grantRole(FIX_DESCRIPTOR_ROLE, admin); + __FixDescriptorEngineModule_init_unchained(engine); + } + + /// @notice Convenience helper used in tests to grant the FIX descriptor role. + function grantFixRole(address account) external onlyRole(DEFAULT_ADMIN_ROLE) { + _grantRole(FIX_DESCRIPTOR_ROLE, account); + } + + function _checkFixDescriptorRole() internal view override { + _checkRole(FIX_DESCRIPTOR_ROLE, _msgSender()); + } +} + diff --git a/contracts/modules/1_CMTATBaseFix.sol b/contracts/modules/1_CMTATBaseFix.sol new file mode 100644 index 00000000..84160551 --- /dev/null +++ b/contracts/modules/1_CMTATBaseFix.sol @@ -0,0 +1,54 @@ +//SPDX-License-Identifier: MPL-2.0 + +pragma solidity ^0.8.20; + +import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; + +/* ==== Base === */ +import {CMTATBaseRuleEngine} from "./1_CMTATBaseRuleEngine.sol"; + +/* ==== Extensions === */ +import {FixDescriptorEngineModule} from "./wrapper/extensions/FixDescriptorEngineModule.sol"; + +/* ==== Interfaces === */ +import {IFixDescriptorEngine} from "../interfaces/engine/IFixDescriptorEngine.sol"; +import {ICMTATConstructor} from "../interfaces/technical/ICMTATConstructor.sol"; + +/** + * @title CMTATBaseFix + * @notice CMTAT base contract with FIX descriptor support + * @dev Extends CMTATBaseCommon with FixDescriptorEngineModule for ERC-FIX compliance + * This base contract adds FIX Protocol descriptor functionality to CMTAT tokens, + * enabling seamless integration with traditional financial infrastructure + */ +abstract contract CMTATBaseFix is CMTATBaseRuleEngine, FixDescriptorEngineModule { + /*////////////////////////////////////////////////////////////// + CONTEXT / ACCESS OVERRIDES + //////////////////////////////////////////////////////////////*/ + + function _checkFixDescriptorRole() internal view override { + _checkRole(FIX_DESCRIPTOR_ROLE, _msgSender()); + } + + /*////////////////////////////////////////////////////////////// + INITIALIZER FUNCTIONS + //////////////////////////////////////////////////////////////*/ + + function initializeFix( + address admin, + ICMTATConstructor.ERC20Attributes memory ERC20Attributes_, + ICMTATConstructor.ExtraInformationAttributes memory extraInformationAttributes_, + ICMTATConstructor.Engine memory engines_, + IFixDescriptorEngine fixDescriptorEngine_ + ) public virtual initializer { + CMTATBaseRuleEngine.initialize( + admin, + ERC20Attributes_, + extraInformationAttributes_, + engines_ + ); + + _grantRole(FIX_DESCRIPTOR_ROLE, admin); + __FixDescriptorEngineModule_init_unchained(fixDescriptorEngine_); + } +} diff --git a/contracts/modules/5_CMTATBaseERC2771Fix.sol b/contracts/modules/5_CMTATBaseERC2771Fix.sol new file mode 100644 index 00000000..fb7e0cc6 --- /dev/null +++ b/contracts/modules/5_CMTATBaseERC2771Fix.sol @@ -0,0 +1,181 @@ +//SPDX-License-Identifier: MPL-2.0 + +pragma solidity ^0.8.20; + +/* ==== OpenZeppelin ==== */ +import {ContextUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"; +import {AccessControlUpgradeable} from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; + +/* ==== Base === */ +import {CMTATBaseERC2771} from "./4_CMTATBaseERC2771.sol"; +import {CMTATBaseERC20CrossChain} from "./3_CMTATBaseERC20CrossChain.sol"; +import {CMTATBaseERC1404} from "./2_CMTATBaseERC1404.sol"; + +/* ==== Extensions === */ +import {FixDescriptorEngineModule} from "./wrapper/extensions/FixDescriptorEngineModule.sol"; +import {ERC2771ContextUpgradeable} from "./wrapper/options/ERC2771Module.sol"; + +/* ==== Interfaces === */ +import {IFixDescriptorEngine} from "../interfaces/engine/IFixDescriptorEngine.sol"; +import {ICMTATConstructor} from "../interfaces/technical/ICMTATConstructor.sol"; + +/** + * @title CMTATBaseERC2771Fix + * @notice CMTAT base contract with FIX descriptor support + * @dev Extends CMTATBaseERC2771 with FixDescriptorEngineModule for ERC-FIX compliance + * This is the top-level base contract for CMTAT tokens with FIX Protocol descriptor support, + * enabling seamless integration with traditional financial infrastructure + */ +abstract contract CMTATBaseERC2771Fix is + CMTATBaseERC2771, + FixDescriptorEngineModule +{ + /*////////////////////////////////////////////////////////////// + CONTEXT OVERRIDES + //////////////////////////////////////////////////////////////*/ + + function _msgSender() + internal + view + virtual + override(CMTATBaseERC2771) + returns (address) + { + return CMTATBaseERC2771._msgSender(); + } + + function _msgData() + internal + view + virtual + override(CMTATBaseERC2771) + returns (bytes calldata) + { + return CMTATBaseERC2771._msgData(); + } + + function _contextSuffixLength() + internal + view + virtual + override(CMTATBaseERC2771) + returns (uint256) + { + return CMTATBaseERC2771._contextSuffixLength(); + } + + function _checkFixDescriptorRole() internal view override { + _checkRole(FIX_DESCRIPTOR_ROLE, _msgSender()); + } + + /*////////////////////////////////////////////////////////////// + ACCESS CONTROL OVERRIDES + //////////////////////////////////////////////////////////////*/ + + /*////////////////////////////////////////////////////////////// + INITIALIZER FUNCTION + //////////////////////////////////////////////////////////////*/ + + /** + * @notice Initialize the proxy contract with FIX descriptor engine support + * @dev The calls to this function will revert if the contract was deployed without a proxy + * @param admin address of the admin of contract (Access Control) + * @param ERC20Attributes_ ERC20 name, symbol and decimals + * @param extraInformationAttributes_ tokenId, terms, information + * @param engines_ external contract engines (rule, snapshot, document) + * @param fixDescriptorEngine_ external FIX descriptor engine (optional) + */ + function initializeFix( + address admin, + ICMTATConstructor.ERC20Attributes memory ERC20Attributes_, + ICMTATConstructor.ExtraInformationAttributes memory extraInformationAttributes_, + ICMTATConstructor.Engine memory engines_, + IFixDescriptorEngine fixDescriptorEngine_ + ) public virtual initializer { + __CMTAT_init_withFix( + admin, + ERC20Attributes_, + extraInformationAttributes_, + engines_, + fixDescriptorEngine_ + ); + } + + /** + * @dev Calls the different initialize functions from the different modules including FIX + * @param admin address of the admin of contract (Access Control) + * @param ERC20Attributes_ ERC20 name, symbol and decimals + * @param ExtraInformationAttributes_ tokenId, terms, information + * @param engines_ external contract engines (rule, snapshot, document) + * @param fixDescriptorEngine_ external FIX descriptor engine (optional) + */ + function __CMTAT_init_withFix( + address admin, + ICMTATConstructor.ERC20Attributes memory ERC20Attributes_, + ICMTATConstructor.ExtraInformationAttributes memory ExtraInformationAttributes_, + ICMTATConstructor.Engine memory engines_, + IFixDescriptorEngine fixDescriptorEngine_ + ) internal virtual onlyInitializing { + /* OpenZeppelin library */ + // OZ init_unchained functions are called firstly due to inheritance + __Context_init_unchained(); + + // AccessControlUpgradeable inherits from ERC165Upgradeable + __ERC165_init_unchained(); + + // OpenZeppelin + __CMTAT_openzeppelin_init_unchained(); + + /* Internal Modules */ + __CMTAT_internal_init_unchained_withFix(engines_); + + /* Wrapper modules */ + __CMTAT_modules_init_unchained_withFix( + admin, + ERC20Attributes_, + ExtraInformationAttributes_, + engines_, + fixDescriptorEngine_ + ); + } + + /** + * @dev CMTAT internal module initialization with FIX + * @param engines_ external contract engines + */ + function __CMTAT_internal_init_unchained_withFix( + ICMTATConstructor.Engine memory engines_ + ) internal virtual onlyInitializing { + __ValidationRuleEngine_init_unchained(engines_.ruleEngine); + } + + /** + * @dev CMTAT wrapper modules initialization with FIX + * @param admin address of the admin of contract (Access Control) + * @param ERC20Attributes_ ERC20 name, symbol and decimals + * @param extraInformationAttributes_ tokenId, terms, information + * @param engines_ external contract engines (rule, snapshot, document) + * @param fixDescriptorEngine_ external FIX descriptor engine (optional) + */ + function __CMTAT_modules_init_unchained_withFix( + address admin, + ICMTATConstructor.ERC20Attributes memory ERC20Attributes_, + ICMTATConstructor.ExtraInformationAttributes memory extraInformationAttributes_, + ICMTATConstructor.Engine memory engines_, + IFixDescriptorEngine fixDescriptorEngine_ + ) internal virtual onlyInitializing { + // Initialize common modules + __CMTAT_commonModules_init_unchained( + admin, + ERC20Attributes_, + extraInformationAttributes_, + engines_.snapshotEngine, + engines_.documentEngine + ); + + _grantRole(FIX_DESCRIPTOR_ROLE, admin); + + // Initialize FIX descriptor module + __FixDescriptorEngineModule_init_unchained(fixDescriptorEngine_); + } +} diff --git a/contracts/modules/wrapper/extensions/FixDescriptorEngineModule.sol b/contracts/modules/wrapper/extensions/FixDescriptorEngineModule.sol new file mode 100644 index 00000000..c877405e --- /dev/null +++ b/contracts/modules/wrapper/extensions/FixDescriptorEngineModule.sol @@ -0,0 +1,123 @@ +//SPDX-License-Identifier: MPL-2.0 + +pragma solidity ^0.8.20; + +/* ==== Engine === */ +import {IFixDescriptorEngine} from "../../../interfaces/engine/IFixDescriptorEngine.sol"; +import {IFixDescriptorEngineModule} from "../../../interfaces/modules/IFixDescriptorEngineModule.sol"; +import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + +/** + * @title FixDescriptorEngine Module + * @notice Delegates FIX descriptor management to an external engine following ERC-FIX spec + * @dev Stores reference to IFixDescriptorEngine and forwards all calls + * Uses ERC-7201 namespaced storage pattern to prevent collisions + */ + +abstract contract FixDescriptorEngineModule is IFixDescriptorEngineModule, Initializable { + /* ============ Roles ============ */ + bytes32 public constant FIX_DESCRIPTOR_ROLE = keccak256("FIX_DESCRIPTOR_ROLE"); + + /* ============ ERC-7201 ============ */ + // keccak256(abi.encode(uint256(keccak256("CMTAT.storage.FixDescriptorEngineModule")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 private constant FixDescriptorEngineModuleStorageLocation = + 0xa53cb59b6022663116b97fd8896a8d8c96544a6d32d4ec30cfa96e5d8df7e300; + + /* ==== ERC-7201 State Variables === */ + struct FixDescriptorEngineModuleStorage { + address _fixDescriptorEngine; + } + + modifier onlyFixDescriptorRole() { + _checkFixDescriptorRole(); + _; + } + + /* ============ Initializer Function ============ */ + + /** + * @dev Sets a FixDescriptorEngine if address is not zero + * @param fixDescriptorEngine_ The initial FIX descriptor engine + */ + function __FixDescriptorEngineModule_init_unchained( + IFixDescriptorEngine fixDescriptorEngine_ + ) internal virtual onlyInitializing { + if (address(fixDescriptorEngine_) != address(0)) { + _setFixDescriptorEngine( + _getFixDescriptorEngineModuleStorage(), + fixDescriptorEngine_ + ); + } + } + + /*////////////////////////////////////////////////////////////// + PUBLIC/EXTERNAL FUNCTIONS + //////////////////////////////////////////////////////////////*/ + + /** + * @inheritdoc IFixDescriptorEngineModule + */ + function fixDescriptorEngine() + public + view + virtual + override(IFixDescriptorEngineModule) + returns (address fixDescriptorEngine_) + { + return _getFixDescriptorEngineModuleStorage()._fixDescriptorEngine; + } + + /** + * @inheritdoc IFixDescriptorEngineModule + */ + /* ============ Restricted Functions ============ */ + + /** + * @inheritdoc IFixDescriptorEngineModule + */ + function setFixDescriptorEngine( + IFixDescriptorEngine fixDescriptorEngine_ + ) public virtual override(IFixDescriptorEngineModule) onlyFixDescriptorRole { + FixDescriptorEngineModuleStorage storage $ = _getFixDescriptorEngineModuleStorage(); + address current = $._fixDescriptorEngine; + address next = address(fixDescriptorEngine_); + + require(current != next, CMTAT_FixDescriptorEngineModule_SameValue()); + + _setFixDescriptorEngine($, fixDescriptorEngine_); + } + + /*////////////////////////////////////////////////////////////// + INTERNAL/PRIVATE FUNCTIONS + //////////////////////////////////////////////////////////////*/ + + /** + * @dev Internal function to set the FIX descriptor engine + * @param $ Storage pointer to module storage + * @param fixDescriptorEngine_ The new engine address + */ + function _setFixDescriptorEngine( + FixDescriptorEngineModuleStorage storage $, + IFixDescriptorEngine fixDescriptorEngine_ + ) internal virtual { + $._fixDescriptorEngine = address(fixDescriptorEngine_); + emit FixDescriptorEngine(fixDescriptorEngine_); + } + + /* ============ ERC-7201 Storage Access ============ */ + + /** + * @dev Returns the storage pointer for this module using ERC-7201 + */ + function _getFixDescriptorEngineModuleStorage() + private + pure + returns (FixDescriptorEngineModuleStorage storage $) + { + assembly { + $.slot := FixDescriptorEngineModuleStorageLocation + } + } + + function _checkFixDescriptorRole() internal view virtual; +} diff --git a/hardhat.config.js b/hardhat.config.js index 4190cfee..ab5cff6b 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -13,6 +13,7 @@ module.exports = { enabled: true, runs: 200 }, + viaIR: true, evmVersion: 'prague' } }, diff --git a/package-lock.json b/package-lock.json index be466352..01a32da8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,11 +9,13 @@ "version": "3.0.0", "license": "MPL", "dependencies": { + "@fixdescriptorkit/contracts": "^1.0.0", "@openzeppelin/contracts": "5.4.0", "@openzeppelin/contracts-upgradeable": "5.4.0", "hardhat": "^2.24.0" }, "devDependencies": { + "@fixdescriptorkit/ts-sdk": "^1.0.1", "@nomicfoundation/hardhat-chai-matchers": "^2.0.7", "@nomicfoundation/hardhat-network-helpers": "^1.0.11", "@openzeppelin/hardhat-upgrades": "^3.2.0", @@ -32,6 +34,7 @@ "hardhat-gas-reporter": "^1.0.9", "keccak256": "^1.0.6", "npm": "^10.2.5", + "patch-package": "^8.0.1", "prettier": "^2.8.7", "prettier-plugin-solidity": "^1.1.3", "sol2uml": "^2.5.20", @@ -2405,6 +2408,90 @@ "integrity": "sha512-JorcEwe4ud0x5BS/Ar2aQWOQoFzjq/7jcnxYXCvSMh0oRm0dQXzOA+hqLDBnOMks1LLBA7dmiLLsEBl09Yd6iQ==", "dev": true }, + "node_modules/@cbor-extract/cbor-extract-darwin-arm64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-darwin-arm64/-/cbor-extract-darwin-arm64-2.2.0.tgz", + "integrity": "sha512-P7swiOAdF7aSi0H+tHtHtr6zrpF3aAq/W9FXx5HektRvLTM2O89xCyXF3pk7pLc7QpaY7AoaE8UowVf9QBdh3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@cbor-extract/cbor-extract-darwin-x64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-darwin-x64/-/cbor-extract-darwin-x64-2.2.0.tgz", + "integrity": "sha512-1liF6fgowph0JxBbYnAS7ZlqNYLf000Qnj4KjqPNW4GViKrEql2MgZnAsExhY9LSy8dnvA4C0qHEBgPrll0z0w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@cbor-extract/cbor-extract-linux-arm": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-arm/-/cbor-extract-linux-arm-2.2.0.tgz", + "integrity": "sha512-QeBcBXk964zOytiedMPQNZr7sg0TNavZeuUCD6ON4vEOU/25+pLhNN6EDIKJ9VLTKaZ7K7EaAriyYQ1NQ05s/Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@cbor-extract/cbor-extract-linux-arm64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-arm64/-/cbor-extract-linux-arm64-2.2.0.tgz", + "integrity": "sha512-rQvhNmDuhjTVXSPFLolmQ47/ydGOFXtbR7+wgkSY0bdOxCFept1hvg59uiLPT2fVDuJFuEy16EImo5tE2x3RsQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@cbor-extract/cbor-extract-linux-x64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-linux-x64/-/cbor-extract-linux-x64-2.2.0.tgz", + "integrity": "sha512-cWLAWtT3kNLHSvP4RKDzSTX9o0wvQEEAj4SKvhWuOVZxiDAeQazr9A+PSiRILK1VYMLeDml89ohxCnUNQNQNCw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@cbor-extract/cbor-extract-win32-x64": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@cbor-extract/cbor-extract-win32-x64/-/cbor-extract-win32-x64-2.2.0.tgz", + "integrity": "sha512-l2M+Z8DO2vbvADOBNLbbh9y5ST1RY5sqkWOg/58GkUPBYou/cuNZ68SGQ644f1CvZ8kcOxyZtw06+dxWHIoN/w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -3288,6 +3375,28 @@ "node": ">=14" } }, + "node_modules/@fixdescriptorkit/contracts": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@fixdescriptorkit/contracts/-/contracts-1.0.0.tgz", + "integrity": "sha512-/IUSjbZ6RPrzcVWBqa2GRgL03MhpPWpo4KcQ8D7EVIcYNcBBHCb5bB+r+aYbxG1tfs3u6oPZJIrHaMnCe9HX5A==", + "license": "MIT", + "peerDependencies": { + "@openzeppelin/contracts": "^5.0.0" + } + }, + "node_modules/@fixdescriptorkit/ts-sdk": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@fixdescriptorkit/ts-sdk/-/ts-sdk-1.0.1.tgz", + "integrity": "sha512-ltTvHjF/WOfybOn5QSMX1cRGIuvC6Ij2QrcYUY+/xZKcyeUnASiXxP1EBvTnE3bR6u9rXie3nigFrt9PIUWP7g==", + "dev": true, + "license": "ISC", + "dependencies": { + "cbor-x": "^1.6.0", + "fixparser": "^9.3.13", + "viem": "^2.37.8", + "zod": "^4.1.11" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -3387,6 +3496,19 @@ "dev": true, "optional": true }, + "node_modules/@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@noble/curves": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", @@ -3666,6 +3788,24 @@ "node": ">= 12" } }, + "node_modules/@openpgp/web-stream-tools": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@openpgp/web-stream-tools/-/web-stream-tools-0.1.3.tgz", + "integrity": "sha512-mT/ds43cH6c+AO5RFpxs+LkACr7KjC3/dZWHrP6KPrWJu4uJ/XJ+p7telaoYiqUfdjiiIvdNSOfhezW9fkmboQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.0.0" + }, + "peerDependencies": { + "typescript": ">=4.2" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/@openzeppelin/contracts": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-5.4.0.tgz", @@ -4638,6 +4778,13 @@ "antlr4ts": "^0.5.0-alpha.4" } }, + "node_modules/@tootallnate/quickjs-emscripten": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", + "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/bn.js": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.2.0.tgz", @@ -4779,12 +4926,41 @@ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "dev": true }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/abbrev": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", "integrity": "sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==", "dev": true }, + "node_modules/abitype": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.1.0.tgz", + "integrity": "sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3.22.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", @@ -5214,6 +5390,19 @@ "node": ">=0.10.0" } }, + "node_modules/ast-types": { + "version": "0.13.4", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", + "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", @@ -5434,6 +5623,16 @@ } ] }, + "node_modules/basic-ftp": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.5.tgz", + "integrity": "sha512-4Bcg1P8xhUuqcii/S0Z9wiHIrQVPMermM1any+MX5GeGD7faD3/msQUDGLol9wOcz4/jbg/WJnGqoJF6LiBdtg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -5854,6 +6053,39 @@ "node": ">=20" } }, + "node_modules/cbor-extract": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cbor-extract/-/cbor-extract-2.2.0.tgz", + "integrity": "sha512-Ig1zM66BjLfTXpNgKpvBePq271BPOvu8MR0Jl080yG7Jsl+wAZunfrwiwA+9ruzm/WEdIV5QF/bjDZTqyAIVHA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.1.1" + }, + "bin": { + "download-cbor-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@cbor-extract/cbor-extract-darwin-arm64": "2.2.0", + "@cbor-extract/cbor-extract-darwin-x64": "2.2.0", + "@cbor-extract/cbor-extract-linux-arm": "2.2.0", + "@cbor-extract/cbor-extract-linux-arm64": "2.2.0", + "@cbor-extract/cbor-extract-linux-x64": "2.2.0", + "@cbor-extract/cbor-extract-win32-x64": "2.2.0" + } + }, + "node_modules/cbor-x": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/cbor-x/-/cbor-x-1.6.0.tgz", + "integrity": "sha512-0kareyRwHSkL6ws5VXHEf8uY1liitysCVJjlmhaLG+IXLqhSaOO+t63coaso7yjwEzWZzLy8fJo06gZDVQM9Qg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "cbor-extract": "^2.2.0" + } + }, "node_modules/chai": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", @@ -6610,6 +6842,16 @@ "node": ">=0.10" } }, + "node_modules/data-uri-to-buffer": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", + "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", @@ -6777,6 +7019,54 @@ "node": ">=0.10.0" } }, + "node_modules/degenerator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", + "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ast-types": "^0.13.4", + "escodegen": "^2.1.0", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/degenerator/node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/degenerator/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -6794,6 +7084,17 @@ "node": ">= 0.8" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/devtools-protocol": { "version": "0.0.981744", "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.981744.tgz", @@ -8336,6 +8637,13 @@ "es5-ext": "~0.10.14" } }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true, + "license": "MIT" + }, "node_modules/evp_bytestokey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", @@ -8861,66 +9169,172 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "bin": { - "flat": "cli.js" + "node_modules/find-yarn-workspace-root": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", + "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "micromatch": "^4.0.2" } }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "node_modules/find-yarn-workspace-root/node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "fill-range": "^7.1.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true - }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } + "node": ">=8" } }, - "node_modules/for-each": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "node_modules/find-yarn-workspace-root/node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { - "is-callable": "^1.2.7" + "to-regex-range": "^5.0.1" }, "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" + "node": ">=8" + } + }, + "node_modules/find-yarn-workspace-root/node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/find-yarn-workspace-root/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/fixparser": { + "version": "9.3.13", + "resolved": "https://registry.npmjs.org/fixparser/-/fixparser-9.3.13.tgz", + "integrity": "sha512-2cfPuKgp2TUr0k+fKKW9AkJkpATd8k2UBSg1NkM17DxxRJLWnZoeDMxZqXrGZH8tjJtO3BZjNDlkWdXmcttn6g==", + "dev": true, + "license": "LICENSE.md", + "dependencies": { + "@openpgp/web-stream-tools": "0.1.3", + "fixparser-common": "^9.3.7", + "openpgp": "6.2.2", + "proxy-agent": "6.5.0", + "ws": "8.18.3" + } + }, + "node_modules/fixparser-common": { + "version": "9.3.13", + "resolved": "https://registry.npmjs.org/fixparser-common/-/fixparser-common-9.3.13.tgz", + "integrity": "sha512-xOJEKE7h5SHpMO3kXTXg27lT+3RrCClDUqkRJCXoUTyu83QiQu3RpVUPFG1XzFNVCNCdGUdwok4UjPnnUhVc6g==", + "dev": true, + "license": "LICENSE.md" + }, + "node_modules/fixparser/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/for-in": { @@ -9211,6 +9625,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-uri": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", + "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "basic-ftp": "^5.0.2", + "data-uri-to-buffer": "^6.0.2", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -10227,6 +10656,30 @@ "node": ">= 0.8" } }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/http-response-object": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", @@ -10408,6 +10861,16 @@ "fp-ts": "^1.0.0" } }, + "node_modules/ip-address": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/is-accessor-descriptor": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.1.tgz", @@ -10590,6 +11053,22 @@ "node": ">= 0.4" } }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-dotfile": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", @@ -10978,6 +11457,19 @@ "node": ">=0.10.0" } }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -11012,6 +11504,22 @@ "unfetch": "^4.2.0" } }, + "node_modules/isows": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.7.tgz", + "integrity": "sha512-I1fSfDCZL5P0v33sVqeTDSpcstAg/N+wF5HS033mogOVIp4B+oHC7oOCsA3axAbBSGTJ8QubbNmnIRN/h8U7hg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "peerDependencies": { + "ws": "*" + } + }, "node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -11102,12 +11610,39 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "node_modules/json-stable-stringify": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.3.0.tgz", + "integrity": "sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "isarray": "^2.0.5", + "jsonify": "^0.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/json-stable-stringify/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, "node_modules/json-stream-stringify": { "version": "3.1.6", "resolved": "https://registry.npmjs.org/json-stream-stringify/-/json-stream-stringify-3.1.6.tgz", @@ -11142,6 +11677,16 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", + "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", + "dev": true, + "license": "Public Domain", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/jsonschema": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.5.0.tgz", @@ -11245,6 +11790,16 @@ "node": ">=14.14.0" } }, + "node_modules/klaw-sync": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.11" + } + }, "node_modules/lcid": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", @@ -12012,6 +12567,16 @@ "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, + "node_modules/netmask": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", + "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/next-tick": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", @@ -12062,6 +12627,22 @@ "node-gyp-build-test": "build-test.js" } }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.1.1.tgz", + "integrity": "sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, "node_modules/node-releases": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", @@ -15054,6 +15635,33 @@ "wrappy": "1" } }, + "node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/openpgp": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/openpgp/-/openpgp-6.2.2.tgz", + "integrity": "sha512-P/dyEqQ3gfwOCo+xsqffzXjmUhGn4AZTOJ1LCcN21S23vAk+EAvMJOQTsb/C8krL6GjOSBxqGYckhik7+hneNw==", + "dev": true, + "license": "LGPL-3.0+", + "engines": { + "node": ">= 18.0.0" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -15116,23 +15724,119 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "node_modules/ox": { + "version": "0.9.6", + "resolved": "https://registry.npmjs.org/ox/-/ox-0.9.6.tgz", + "integrity": "sha512-8SuCbHPvv2eZLYXrNmC0EC12rdzXQLdhnOMlHDW2wiCPLxBrOOJwX5L5E61by+UjTPOryqQiRSnjIKCI+GykKg==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "^1.11.0", + "@noble/ciphers": "^1.3.0", + "@noble/curves": "1.9.1", + "@noble/hashes": "^1.8.0", + "@scure/bip32": "^1.7.0", + "@scure/bip39": "^1.6.0", + "abitype": "^1.0.9", + "eventemitter3": "5.0.1" + }, + "peerDependencies": { + "typescript": ">=5.4.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/ox/node_modules/@adraffy/ens-normalize": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.11.1.tgz", + "integrity": "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/ox/node_modules/@noble/curves": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz", + "integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ox/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ox/node_modules/@scure/bip32": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", + "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.9.0", + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ox/node_modules/@scure/bip39": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz", + "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dependencies": { + "yocto-queue": "^0.1.0" + }, "engines": { - "node": ">=4" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { "node": ">=10" }, "funding": { @@ -15176,6 +15880,64 @@ "node": ">=6" } }, + "node_modules/pac-proxy-agent": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", + "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/quickjs-emscripten": "^0.23.0", + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "get-uri": "^6.0.1", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.6", + "pac-resolver": "^7.0.1", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/pac-resolver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", + "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", + "dev": true, + "license": "MIT", + "dependencies": { + "degenerator": "^5.0.0", + "netmask": "^2.0.2" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -15288,6 +16050,103 @@ "node": ">=0.10.0" } }, + "node_modules/patch-package": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.1.tgz", + "integrity": "sha512-VsKRIA8f5uqHQ7NGhwIna6Bx6D9s/1iXlA1hthBVBEbkq+t4kXD0HHt+rJhf/Z+Ci0F/HCB2hvn0qLdLG+Qxlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@yarnpkg/lockfile": "^1.1.0", + "chalk": "^4.1.2", + "ci-info": "^3.7.0", + "cross-spawn": "^7.0.3", + "find-yarn-workspace-root": "^2.0.0", + "fs-extra": "^10.0.0", + "json-stable-stringify": "^1.0.2", + "klaw-sync": "^6.0.0", + "minimist": "^1.2.6", + "open": "^7.4.2", + "semver": "^7.5.3", + "slash": "^2.0.0", + "tmp": "^0.2.4", + "yaml": "^2.2.2" + }, + "bin": { + "patch-package": "index.js" + }, + "engines": { + "node": ">=14", + "npm": ">5" + } + }, + "node_modules/patch-package/node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/patch-package/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/patch-package/node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/patch-package/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/patch-package/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -15642,6 +16501,60 @@ "node": ">= 4" } }, + "node_modules/proxy-agent": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", + "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "http-proxy-agent": "^7.0.1", + "https-proxy-agent": "^7.0.6", + "lru-cache": "^7.14.1", + "pac-proxy-agent": "^7.1.0", + "proxy-from-env": "^1.1.0", + "socks-proxy-agent": "^8.0.5" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/proxy-agent/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -17301,6 +18214,17 @@ "node": ">=8.10.0" } }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, "node_modules/snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -17428,6 +18352,46 @@ "node": ">=0.10.0" } }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/socks-proxy-agent/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/sol-digger": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/sol-digger/-/sol-digger-0.0.2.tgz", @@ -18867,10 +19831,11 @@ } }, "node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.14" } @@ -19455,6 +20420,117 @@ "extsprintf": "^1.2.0" } }, + "node_modules/viem": { + "version": "2.38.5", + "resolved": "https://registry.npmjs.org/viem/-/viem-2.38.5.tgz", + "integrity": "sha512-EU2olUnWd5kBK1t3BicwaamPHGUANRYetoDLSVzDy7XQ8o8UswItnkQbufe3xTcdRCtb2JYMwjlgHZZ7fUoLdA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/wevm" + } + ], + "license": "MIT", + "dependencies": { + "@noble/curves": "1.9.1", + "@noble/hashes": "1.8.0", + "@scure/bip32": "1.7.0", + "@scure/bip39": "1.6.0", + "abitype": "1.1.0", + "isows": "1.0.7", + "ox": "0.9.6", + "ws": "8.18.3" + }, + "peerDependencies": { + "typescript": ">=5.0.4" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/viem/node_modules/@noble/curves": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.1.tgz", + "integrity": "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/viem/node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/viem/node_modules/@scure/bip32": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.7.0.tgz", + "integrity": "sha512-E4FFX/N3f4B80AKWp5dP6ow+flD1LQZo/w8UnLGYZO674jS6YnYeepycOOksv+vLPSpgN35wgKgy+ybfTb2SMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.9.0", + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/viem/node_modules/@scure/bip39": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.6.0.tgz", + "integrity": "sha512-+lF0BbLiJNwVlev4eKelw1WWLaiKXw7sSl8T6FvBlWkdX+94aGJ4o8XjUdlyhTCjd8c+B3KT3JfS8P0bLRNU6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.8.0", + "@scure/base": "~1.2.5" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/viem/node_modules/ws": { + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/web3-utils": { "version": "1.10.4", "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.10.4.tgz", @@ -19785,6 +20861,19 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, + "node_modules/yaml": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", + "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + } + }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -19844,6 +20933,16 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", + "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index a9bcd92a..efde6fbb 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ }, "homepage": "https://github.com/CMTA/CMTAT", "devDependencies": { + "@fixdescriptorkit/ts-sdk": "^1.0.1", "@nomicfoundation/hardhat-chai-matchers": "^2.0.7", "@nomicfoundation/hardhat-network-helpers": "^1.0.11", "@openzeppelin/hardhat-upgrades": "^3.2.0", @@ -81,6 +82,7 @@ "hardhat-gas-reporter": "^1.0.9", "keccak256": "^1.0.6", "npm": "^10.2.5", + "patch-package": "^8.0.1", "prettier": "^2.8.7", "prettier-plugin-solidity": "^1.1.3", "sol2uml": "^2.5.20", @@ -92,6 +94,7 @@ "surya": "^0.4.11" }, "dependencies": { + "@fixdescriptorkit/contracts": "^1.0.0", "@openzeppelin/contracts": "5.4.0", "@openzeppelin/contracts-upgradeable": "5.4.0", "hardhat": "^2.24.0" diff --git a/test/fix/FixDescriptorEngine.test.js b/test/fix/FixDescriptorEngine.test.js new file mode 100644 index 00000000..edc71cb7 --- /dev/null +++ b/test/fix/FixDescriptorEngine.test.js @@ -0,0 +1,159 @@ +const { expect } = require('chai'); +const { ethers } = require('hardhat'); +const path = require('path'); +const { pathToFileURL } = require('url'); + +const FALLBACK_FIXPARSER_LICENSE = + 'FIXParser_QjtkyzBGvFLS0tLS1CRUdJTiBQR1AgU0lHTkVEIE1FU1NBR0UtLS0tLQpIYXNoOiBTSEE1MTIKCjguMy4zfHN3cEBuZXRoZXJtaW5kLmlvfDE3ODgzOTM2MDAwMDB8ZnJlZQotLS0tLUJFR0lOIFBHUCBTSUdOQVRVUkUtLS0tLQoKd3JzRUFSWUtBRzBGZ21pNGZYZ0pFQ0p3M09mWVRzVEpSUlFBQUFBQUFCd0FJSE5oYkhSQWJtOTBZWFJwCmIyNXpMbTl3Wlc1d1ozQnFjeTV2Y21jaHZ2SS9ZQnR1QlB1R3ozKzY3Y3hWcitPeEZveE8xNzhLL1dtdApUSlAxeGhZaEJDVmdXQ2dKSU50bVgrZVpnaUp3M09mWVRzVEpBQUNHYWdEK0o3ZTN4ZzByc0d4TVYwZlEKMzlIM29EUjFlTitPb2xCR2VKM002djkxVGhBQS8yeEFlSUlhbi9VaS9qT2xyNVdRdGdGR1BpemYrbjdwCmpZMWllZEJVTkxnTgo9eGZNWgotLS0tLUVORCBQRw=='; + +const SAMPLE_FIX_MESSAGE = [ + '8=FIX.5.0', + '35=D', + '55=USTB-2030-11-15', + '48=US000000AA11', + '22=4', + '167=TBOND', + '200=20301115', + '223=4.250', + '15=USD', + '453=[{448=ISSUER,452=1},{448=CUSTODIAN,452=24}]', + '10=000' +].join('|'); + +async function buildDescriptorData() { + if (!process.env.FIXPARSER_LICENSE_KEY) { + process.env.FIXPARSER_LICENSE_KEY = FALLBACK_FIXPARSER_LICENSE; + } + + const { parseFixDescriptor, buildCanonicalTree, encodeCanonicalCBOR, + enumerateLeaves, computeRoot, generateProof + } = await import("@fixdescriptorkit/ts-sdk"); + + const tree = parseFixDescriptor(SAMPLE_FIX_MESSAGE); + const canonical = buildCanonicalTree(tree); + const cborBytes = encodeCanonicalCBOR(canonical); + const leaves = enumerateLeaves(canonical); + const root = computeRoot(leaves); + + const symbolPath = [55]; + const symbolLeaf = leaves.find( + (leaf) => leaf.path.length === symbolPath.length && leaf.path.every((value, idx) => value === symbolPath[idx]) + ); + if (!symbolLeaf) { + throw new Error('Generated descriptor is missing the Symbol (tag 55) leaf'); + } + + const proofRaw = generateProof(leaves, symbolPath); + + const toHex = (bytes) => `0x${Buffer.from(bytes).toString('hex')}`; + + return { + descriptor: { + fixMajor: 5, + fixMinor: 0, + dictHash: ethers.keccak256(ethers.toUtf8Bytes('fix.dictionary.demo.v1')), + dictionaryContract: ethers.ZeroAddress, + fixRoot: root, + fixCBORPtr: ethers.ZeroAddress, + fixCBORLen: cborBytes.length, + fixURI: 'ipfs://fix/ustb-2030.json' + }, + cborHex: toHex(cborBytes), + leaves, + proof: { + pathCBOR: toHex(proofRaw.pathCBOR), + valueBytes: toHex(proofRaw.valueBytes), + proof: proofRaw.proof, + directions: proofRaw.directions.map((isRight) => !isRight) + }, + symbolValue: Buffer.from(proofRaw.valueBytes).toString('utf8'), + canonical, + cborBytes + }; +} + +describe('FixDescriptorEngine (ts-sdk integration)', function () { + let descriptorData; + + before(async function () { + descriptorData = await buildDescriptorData(); + }); + + async function deployEngineWithDescriptor() { + const Engine = await ethers.getContractFactory('FixDescriptorEngineMock'); + const engine = await Engine.deploy(); + await engine.waitForDeployment(); + + await expect( + engine.setFixDescriptor(descriptorData.descriptor, descriptorData.cborHex) + ).to.emit(engine, 'DescriptorSet'); + + return engine; + } + + it('stores canonical descriptor data derived from the ts-sdk', async function () { + const engine = await deployEngineWithDescriptor(); + + const stored = await engine.getFixDescriptor(); + + expect(stored.fixMajor).to.equal(descriptorData.descriptor.fixMajor); + expect(stored.fixMinor).to.equal(descriptorData.descriptor.fixMinor); + expect(stored.fixCBORLen).to.equal(descriptorData.descriptor.fixCBORLen); + expect(stored.fixRoot).to.equal(descriptorData.descriptor.fixRoot); + + expect(await engine.getFixRoot()).to.equal(descriptorData.descriptor.fixRoot); + expect(await engine.isInitialized()).to.equal(true); + + const chunkHex = await engine.getFixCBORChunk(0, 16); + expect(chunkHex).to.equal(`0x${Buffer.from(descriptorData.cborBytes.slice(0, 16)).toString('hex')}`); + }); + + it('verifies Merkle proofs generated by the ts-sdk', async function () { + const engine = await deployEngineWithDescriptor(); + + const { pathCBOR, valueBytes, proof, directions } = descriptorData.proof; + + expect( + await engine.verifyField(pathCBOR, valueBytes, proof, directions) + ).to.equal(true); + + expect( + await engine.verifyField(pathCBOR, valueBytes, proof, directions.map((flag) => !flag)) + ).to.equal(false); + + const tamperedValue = ethers.hexlify(ethers.toUtf8Bytes('USTB-FAKE')); + expect( + await engine.verifyField(pathCBOR, tamperedValue, proof, directions) + ).to.equal(false); + }); + + it('prevents descriptor re-initialisation', async function () { + const engine = await deployEngineWithDescriptor(); + + await expect( + engine.setFixDescriptor(descriptorData.descriptor, descriptorData.cborHex) + ).to.be.revertedWithCustomError(engine, 'DescriptorAlreadyInitialized'); + + const updatedDescriptor = { + ...descriptorData.descriptor, + fixRoot: ethers.keccak256(ethers.toUtf8Bytes('new-root')) + }; + + await expect( + engine.updateFixDescriptor(updatedDescriptor, descriptorData.cborHex) + ).to.emit(engine, 'DescriptorUpdated').withArgs( + updatedDescriptor.fixRoot, + updatedDescriptor.fixMajor, + updatedDescriptor.fixMinor + ); + }); + + it('provides human-readable output with contextual data', async function () { + const engine = await deployEngineWithDescriptor(); + const human = await engine.getHumanReadableDescriptor(); + + expect(human).to.include('FIX 5.0'); + expect(human).to.include(descriptorData.descriptor.fixRoot.slice(2, 10)); + }); +}); + diff --git a/test/fix/FixDescriptorEngineModule.test.js b/test/fix/FixDescriptorEngineModule.test.js new file mode 100644 index 00000000..5845257b --- /dev/null +++ b/test/fix/FixDescriptorEngineModule.test.js @@ -0,0 +1,95 @@ +const { expect } = require('chai'); +const { ethers } = require('hardhat'); + +describe('FixDescriptorEngineModule', function () { + let owner; + let other; + let harness; + let engine; + + beforeEach(async function () { + [owner, other] = await ethers.getSigners(); + + const Harness = await ethers.getContractFactory('FixDescriptorEngineModuleHarness'); + harness = await Harness.deploy(); + await harness.waitForDeployment(); + + const EngineMock = await ethers.getContractFactory('FixDescriptorEngineMock'); + engine = await EngineMock.deploy(); + await engine.waitForDeployment(); + + await harness.initialize(owner.address, ethers.ZeroAddress); + }); + + it('initializes with no engine configured', async function () { + expect(await harness.fixDescriptorEngine()).to.equal(ethers.ZeroAddress); + }); + + it('stores the engine address and emits an event', async function () { + const descriptor = { + fixMajor: 5, + fixMinor: 0, + dictHash: ethers.ZeroHash, + dictionaryContract: owner.address, + fixRoot: ethers.ZeroHash, + fixCBORPtr: ethers.ZeroAddress, + fixCBORLen: 0, + fixURI: 'ipfs://fixture/minimal' + }; + await engine.setFixDescriptor(descriptor, '0x'); + + await expect(harness.setFixDescriptorEngine(await engine.getAddress())) + .to.emit(harness, 'FixDescriptorEngine') + .withArgs(await engine.getAddress()); + + expect(await harness.fixDescriptorEngine()).to.equal(await engine.getAddress()); + }); + + it('allows reading descriptor data directly from the engine', async function () { + const cborData = ethers.toUtf8Bytes('mock-cbor-data'); + const descriptor = { + fixMajor: 5, + fixMinor: 0, + dictHash: ethers.keccak256(cborData), + dictionaryContract: owner.address, + fixRoot: ethers.keccak256(ethers.toUtf8Bytes('root')), + fixCBORPtr: ethers.ZeroAddress, + fixCBORLen: cborData.length, + fixURI: 'ipfs://fixture/fix.json' + }; + await engine.setFixDescriptor(descriptor, cborData); + await harness.setFixDescriptorEngine(await engine.getAddress()); + + const forwarded = await engine.getFixDescriptor(); + expect(forwarded.fixMajor).to.equal(5); + expect(forwarded.fixMinor).to.equal(0); + expect(forwarded.fixURI).to.equal('ipfs://fixture/fix.json'); + }); + + it('allows clearing the engine back to address(0)', async function () { + await harness.setFixDescriptorEngine(await engine.getAddress()); + await harness.setFixDescriptorEngine(ethers.ZeroAddress); + + expect(await harness.fixDescriptorEngine()).to.equal(ethers.ZeroAddress); + }); + + it('enforces FIX_DESCRIPTOR_ROLE for updates', async function () { + await expect( + harness.connect(other).setFixDescriptorEngine(await engine.getAddress()) + ).to.be.revertedWithCustomError(harness, 'AccessControlUnauthorizedAccount'); + + await harness.grantFixRole(other.address); + await expect(harness.connect(other).setFixDescriptorEngine(await engine.getAddress())) + .to.emit(harness, 'FixDescriptorEngine') + .withArgs(await engine.getAddress()); + }); + + it('reverts when setting the same engine twice', async function () { + await harness.setFixDescriptorEngine(await engine.getAddress()); + + await expect( + harness.setFixDescriptorEngine(await engine.getAddress()) + ).to.be.revertedWithCustomError(harness, 'CMTAT_FixDescriptorEngineModule_SameValue'); + }); +}); +