Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions contracts/deployment/fix/CMTATStandaloneFix.sol
Original file line number Diff line number Diff line change
@@ -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_);
}
}

17 changes: 17 additions & 0 deletions contracts/deployment/fix/CMTATUpgradeableFix.sol
Original file line number Diff line number Diff line change
@@ -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();
}
}

41 changes: 41 additions & 0 deletions contracts/deployment/fix/CMTATUpgradeableUUPSFix.sol
Original file line number Diff line number Diff line change
@@ -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) {}
}

173 changes: 173 additions & 0 deletions contracts/engines/FixDescriptorEngine.sol
Original file line number Diff line number Diff line change
@@ -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;
}
}
75 changes: 75 additions & 0 deletions contracts/interfaces/engine/IFixDescriptorEngine.sol
Original file line number Diff line number Diff line change
@@ -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);
}
43 changes: 43 additions & 0 deletions contracts/interfaces/modules/IFixDescriptorEngineModule.sol
Original file line number Diff line number Diff line change
@@ -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_);
}
Loading