- Introduced Aggregator.sol for Chainlink-compatible oracle functionality, including round-based updates and access control. - Added OracleWithCCIP.sol to extend Aggregator with CCIP cross-chain messaging capabilities. - Created .gitmodules to include OpenZeppelin contracts as a submodule. - Developed a comprehensive deployment guide in NEXT_STEPS_COMPLETE_GUIDE.md for Phase 2 and smart contract deployment. - Implemented Vite configuration for the orchestration portal, supporting both Vue and React frameworks. - Added server-side logic for the Multi-Cloud Orchestration Portal, including API endpoints for environment management and monitoring. - Created scripts for resource import and usage validation across non-US regions. - Added tests for CCIP error handling and integration to ensure robust functionality. - Included various new files and directories for the orchestration portal and deployment scripts.
140 lines
5.9 KiB
Solidity
140 lines
5.9 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.19;
|
|
|
|
import "@openzeppelin/contracts/access/AccessControl.sol";
|
|
import "./interfaces/IISO20022Router.sol";
|
|
import "./interfaces/IRailTriggerRegistry.sol";
|
|
import "./libraries/RailTypes.sol";
|
|
import "./libraries/ISO20022Types.sol";
|
|
|
|
/**
|
|
* @title ISO20022Router
|
|
* @notice Normalizes ISO-20022 messages into canonical on-chain format
|
|
* @dev Creates triggers in RailTriggerRegistry for both inbound and outbound messages
|
|
*/
|
|
contract ISO20022Router is IISO20022Router, AccessControl {
|
|
bytes32 public constant RAIL_OPERATOR_ROLE = keccak256("RAIL_OPERATOR_ROLE");
|
|
|
|
IRailTriggerRegistry public immutable triggerRegistry;
|
|
mapping(bytes32 => uint256) private _triggerIdByInstructionId; // instructionId => triggerId
|
|
|
|
/**
|
|
* @notice Initializes the router with registry address
|
|
* @param admin Address that will receive DEFAULT_ADMIN_ROLE
|
|
* @param triggerRegistry_ Address of RailTriggerRegistry contract
|
|
*/
|
|
constructor(address admin, address triggerRegistry_) {
|
|
_grantRole(DEFAULT_ADMIN_ROLE, admin);
|
|
require(triggerRegistry_ != address(0), "ISO20022Router: zero triggerRegistry");
|
|
triggerRegistry = IRailTriggerRegistry(triggerRegistry_);
|
|
}
|
|
|
|
/**
|
|
* @notice Submits an inbound ISO-20022 message (rail confirmation/notification)
|
|
* @dev Requires RAIL_OPERATOR_ROLE. Creates a trigger in CREATED state.
|
|
* @param m Canonical message struct
|
|
* @return triggerId The created trigger ID
|
|
*/
|
|
function submitInbound(
|
|
CanonicalMessage calldata m
|
|
) external override onlyRole(RAIL_OPERATOR_ROLE) returns (uint256 triggerId) {
|
|
require(m.msgType != bytes32(0), "ISO20022Router: zero msgType");
|
|
require(m.instructionId != bytes32(0), "ISO20022Router: zero instructionId");
|
|
require(m.accountRefId != bytes32(0), "ISO20022Router: zero accountRefId");
|
|
require(m.token != address(0), "ISO20022Router: zero token");
|
|
require(m.amount > 0, "ISO20022Router: zero amount");
|
|
|
|
// Determine rail from message type (simplified - in production, this would be more sophisticated)
|
|
RailTypes.Rail rail = _determineRailFromMessageType(m.msgType);
|
|
|
|
// Create trigger
|
|
IRailTriggerRegistry.Trigger memory trigger = IRailTriggerRegistry.Trigger({
|
|
id: 0, // Will be assigned by registry
|
|
rail: rail,
|
|
msgType: m.msgType,
|
|
accountRefId: m.accountRefId,
|
|
walletRefId: bytes32(0), // Will be resolved by orchestrator if needed
|
|
token: m.token,
|
|
amount: m.amount,
|
|
currencyCode: m.currencyCode,
|
|
instructionId: m.instructionId,
|
|
state: RailTypes.State.CREATED,
|
|
createdAt: 0, // Will be set by registry
|
|
updatedAt: 0 // Will be set by registry
|
|
});
|
|
|
|
triggerId = triggerRegistry.createTrigger(trigger);
|
|
_triggerIdByInstructionId[m.instructionId] = triggerId;
|
|
|
|
emit InboundSubmitted(triggerId, m.msgType, m.instructionId, m.accountRefId);
|
|
}
|
|
|
|
/**
|
|
* @notice Submits an outbound ISO-20022 message (rail initiation)
|
|
* @dev Requires RAIL_OPERATOR_ROLE. Creates a trigger in CREATED state.
|
|
* @param m Canonical message struct
|
|
* @return triggerId The created trigger ID
|
|
*/
|
|
function submitOutbound(
|
|
CanonicalMessage calldata m
|
|
) external override onlyRole(RAIL_OPERATOR_ROLE) returns (uint256 triggerId) {
|
|
require(m.msgType != bytes32(0), "ISO20022Router: zero msgType");
|
|
require(m.instructionId != bytes32(0), "ISO20022Router: zero instructionId");
|
|
require(m.accountRefId != bytes32(0), "ISO20022Router: zero accountRefId");
|
|
require(m.token != address(0), "ISO20022Router: zero token");
|
|
require(m.amount > 0, "ISO20022Router: zero amount");
|
|
|
|
// Determine rail from message type
|
|
RailTypes.Rail rail = _determineRailFromMessageType(m.msgType);
|
|
|
|
// Create trigger
|
|
IRailTriggerRegistry.Trigger memory trigger = IRailTriggerRegistry.Trigger({
|
|
id: 0, // Will be assigned by registry
|
|
rail: rail,
|
|
msgType: m.msgType,
|
|
accountRefId: m.accountRefId,
|
|
walletRefId: bytes32(0), // Will be resolved by orchestrator if needed
|
|
token: m.token,
|
|
amount: m.amount,
|
|
currencyCode: m.currencyCode,
|
|
instructionId: m.instructionId,
|
|
state: RailTypes.State.CREATED,
|
|
createdAt: 0, // Will be set by registry
|
|
updatedAt: 0 // Will be set by registry
|
|
});
|
|
|
|
triggerId = triggerRegistry.createTrigger(trigger);
|
|
_triggerIdByInstructionId[m.instructionId] = triggerId;
|
|
|
|
emit OutboundSubmitted(triggerId, m.msgType, m.instructionId, m.accountRefId);
|
|
}
|
|
|
|
/**
|
|
* @notice Returns the trigger ID for a given instruction ID
|
|
* @param instructionId The instruction ID
|
|
* @return The trigger ID (0 if not found)
|
|
*/
|
|
function getTriggerIdByInstructionId(bytes32 instructionId) external view override returns (uint256) {
|
|
return _triggerIdByInstructionId[instructionId];
|
|
}
|
|
|
|
/**
|
|
* @notice Determines the rail type from an ISO-20022 message type
|
|
* @dev Simplified implementation - in production, this would use a mapping table
|
|
* @param msgType The message type
|
|
* @return The rail type
|
|
*/
|
|
function _determineRailFromMessageType(bytes32 msgType) internal pure returns (RailTypes.Rail) {
|
|
// This is a simplified implementation
|
|
// In production, you would have a mapping table or more sophisticated logic
|
|
// For now, we'll use a default based on message family
|
|
if (msgType == ISO20022Types.PAIN_001 || msgType == ISO20022Types.PACS_008) {
|
|
// These are commonly used across rails, default to SWIFT
|
|
return RailTypes.Rail.SWIFT;
|
|
}
|
|
// Default to SWIFT for unknown types
|
|
return RailTypes.Rail.SWIFT;
|
|
}
|
|
}
|
|
|