Consolidate webapp structure by merging nested components into the main repository
This commit is contained in:
759
docs/Smart_Contract_Interfaces.md
Normal file
759
docs/Smart_Contract_Interfaces.md
Normal file
@@ -0,0 +1,759 @@
|
||||
# Smart Contract Interface Specifications
|
||||
|
||||
## Overview
|
||||
This document defines the smart contract interfaces for the ISO-20022 Combo Flow system, including handler contracts for atomic execution, notary registry for codehash tracking, adapter registry for whitelisting, and integration patterns for atomicity (2PC, HTLC, conditional finality).
|
||||
|
||||
---
|
||||
|
||||
## 1. Handler/Aggregator Contract Interface
|
||||
|
||||
### Purpose
|
||||
The handler contract aggregates multiple DeFi protocol calls and DLT operations into a single atomic transaction. It executes steps sequentially, passing outputs between steps, and ensures atomicity across the entire workflow.
|
||||
|
||||
### Interface: `IComboHandler`
|
||||
|
||||
```solidity
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
interface IComboHandler {
|
||||
/**
|
||||
* @notice Execute a multi-step combo plan atomically
|
||||
* @param planId Unique identifier for the execution plan
|
||||
* @param steps Array of step configurations
|
||||
* @param signature User's cryptographic signature on the plan
|
||||
* @return success Whether execution completed successfully
|
||||
* @return receipts Array of transaction receipts for each step
|
||||
*/
|
||||
function executeCombo(
|
||||
bytes32 planId,
|
||||
Step[] calldata steps,
|
||||
bytes calldata signature
|
||||
) external returns (bool success, StepReceipt[] memory receipts);
|
||||
|
||||
/**
|
||||
* @notice Prepare phase for 2PC (two-phase commit)
|
||||
* @param planId Plan identifier
|
||||
* @param steps Execution steps
|
||||
* @return prepared Whether all steps are prepared
|
||||
*/
|
||||
function prepare(
|
||||
bytes32 planId,
|
||||
Step[] calldata steps
|
||||
) external returns (bool prepared);
|
||||
|
||||
/**
|
||||
* @notice Commit phase for 2PC
|
||||
* @param planId Plan identifier
|
||||
* @return committed Whether commit was successful
|
||||
*/
|
||||
function commit(bytes32 planId) external returns (bool committed);
|
||||
|
||||
/**
|
||||
* @notice Abort phase for 2PC (rollback)
|
||||
* @param planId Plan identifier
|
||||
*/
|
||||
function abort(bytes32 planId) external;
|
||||
|
||||
/**
|
||||
* @notice Get execution status for a plan
|
||||
* @param planId Plan identifier
|
||||
* @return status Execution status (PENDING, IN_PROGRESS, COMPLETE, FAILED, ABORTED)
|
||||
*/
|
||||
function getExecutionStatus(bytes32 planId) external view returns (ExecutionStatus status);
|
||||
}
|
||||
|
||||
struct Step {
|
||||
StepType stepType;
|
||||
bytes data; // Encoded step-specific parameters
|
||||
address target; // Target contract address (adapter or protocol)
|
||||
uint256 value; // ETH value to send (if applicable)
|
||||
}
|
||||
|
||||
enum StepType {
|
||||
BORROW,
|
||||
SWAP,
|
||||
REPAY,
|
||||
PAY,
|
||||
DEPOSIT,
|
||||
WITHDRAW,
|
||||
BRIDGE
|
||||
}
|
||||
|
||||
enum ExecutionStatus {
|
||||
PENDING,
|
||||
IN_PROGRESS,
|
||||
COMPLETE,
|
||||
FAILED,
|
||||
ABORTED
|
||||
}
|
||||
|
||||
struct StepReceipt {
|
||||
uint256 stepIndex;
|
||||
bool success;
|
||||
bytes returnData;
|
||||
uint256 gasUsed;
|
||||
}
|
||||
```
|
||||
|
||||
### Implementation Example: `ComboHandler.sol`
|
||||
|
||||
```solidity
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
|
||||
import "./IComboHandler.sol";
|
||||
import "./IAdapterRegistry.sol";
|
||||
import "./INotaryRegistry.sol";
|
||||
|
||||
contract ComboHandler is IComboHandler, Ownable, ReentrancyGuard {
|
||||
IAdapterRegistry public adapterRegistry;
|
||||
INotaryRegistry public notaryRegistry;
|
||||
|
||||
mapping(bytes32 => ExecutionState) public executions;
|
||||
|
||||
struct ExecutionState {
|
||||
ExecutionStatus status;
|
||||
uint256 currentStep;
|
||||
Step[] steps;
|
||||
bool prepared;
|
||||
}
|
||||
|
||||
constructor(address _adapterRegistry, address _notaryRegistry) {
|
||||
adapterRegistry = IAdapterRegistry(_adapterRegistry);
|
||||
notaryRegistry = INotaryRegistry(_notaryRegistry);
|
||||
}
|
||||
|
||||
function executeCombo(
|
||||
bytes32 planId,
|
||||
Step[] calldata steps,
|
||||
bytes calldata signature
|
||||
) external override nonReentrant returns (bool success, StepReceipt[] memory receipts) {
|
||||
require(executions[planId].status == ExecutionStatus.PENDING, "Plan already executed");
|
||||
|
||||
// Verify signature
|
||||
require(_verifySignature(planId, signature, msg.sender), "Invalid signature");
|
||||
|
||||
// Register with notary
|
||||
notaryRegistry.registerPlan(planId, steps, msg.sender);
|
||||
|
||||
executions[planId] = ExecutionState({
|
||||
status: ExecutionStatus.IN_PROGRESS,
|
||||
currentStep: 0,
|
||||
steps: steps,
|
||||
prepared: false
|
||||
});
|
||||
|
||||
receipts = new StepReceipt[](steps.length);
|
||||
|
||||
// Execute steps sequentially
|
||||
for (uint256 i = 0; i < steps.length; i++) {
|
||||
(bool stepSuccess, bytes memory returnData, uint256 gasUsed) = _executeStep(steps[i], i);
|
||||
|
||||
receipts[i] = StepReceipt({
|
||||
stepIndex: i,
|
||||
success: stepSuccess,
|
||||
returnData: returnData,
|
||||
gasUsed: gasUsed
|
||||
});
|
||||
|
||||
if (!stepSuccess) {
|
||||
executions[planId].status = ExecutionStatus.FAILED;
|
||||
revert("Step execution failed");
|
||||
}
|
||||
}
|
||||
|
||||
executions[planId].status = ExecutionStatus.COMPLETE;
|
||||
success = true;
|
||||
|
||||
// Finalize with notary
|
||||
notaryRegistry.finalizePlan(planId, true);
|
||||
}
|
||||
|
||||
function prepare(
|
||||
bytes32 planId,
|
||||
Step[] calldata steps
|
||||
) external override returns (bool prepared) {
|
||||
require(executions[planId].status == ExecutionStatus.PENDING, "Plan not pending");
|
||||
|
||||
// Validate all steps can be prepared
|
||||
for (uint256 i = 0; i < steps.length; i++) {
|
||||
require(_canPrepareStep(steps[i]), "Step cannot be prepared");
|
||||
}
|
||||
|
||||
executions[planId] = ExecutionState({
|
||||
status: ExecutionStatus.IN_PROGRESS,
|
||||
currentStep: 0,
|
||||
steps: steps,
|
||||
prepared: true
|
||||
});
|
||||
|
||||
prepared = true;
|
||||
}
|
||||
|
||||
function commit(bytes32 planId) external override returns (bool committed) {
|
||||
ExecutionState storage state = executions[planId];
|
||||
require(state.prepared, "Plan not prepared");
|
||||
require(state.status == ExecutionStatus.IN_PROGRESS, "Invalid state");
|
||||
|
||||
// Execute all prepared steps
|
||||
for (uint256 i = 0; i < state.steps.length; i++) {
|
||||
(bool success, , ) = _executeStep(state.steps[i], i);
|
||||
require(success, "Commit failed");
|
||||
}
|
||||
|
||||
state.status = ExecutionStatus.COMPLETE;
|
||||
committed = true;
|
||||
|
||||
notaryRegistry.finalizePlan(planId, true);
|
||||
}
|
||||
|
||||
function abort(bytes32 planId) external override {
|
||||
ExecutionState storage state = executions[planId];
|
||||
require(state.status == ExecutionStatus.IN_PROGRESS, "Cannot abort");
|
||||
|
||||
// Release any reserved funds/collateral
|
||||
_rollbackSteps(planId);
|
||||
|
||||
state.status = ExecutionStatus.ABORTED;
|
||||
notaryRegistry.finalizePlan(planId, false);
|
||||
}
|
||||
|
||||
function getExecutionStatus(bytes32 planId) external view override returns (ExecutionStatus) {
|
||||
return executions[planId].status;
|
||||
}
|
||||
|
||||
function _executeStep(Step memory step, uint256 stepIndex) internal returns (bool success, bytes memory returnData, uint256 gasUsed) {
|
||||
// Verify adapter is whitelisted
|
||||
require(adapterRegistry.isWhitelisted(step.target), "Adapter not whitelisted");
|
||||
|
||||
uint256 gasBefore = gasleft();
|
||||
|
||||
(success, returnData) = step.target.call{value: step.value}(
|
||||
abi.encodeWithSignature("executeStep(bytes)", step.data)
|
||||
);
|
||||
|
||||
gasUsed = gasBefore - gasleft();
|
||||
}
|
||||
|
||||
function _canPrepareStep(Step memory step) internal view returns (bool) {
|
||||
// Check if adapter supports prepare phase
|
||||
// Implementation depends on adapter interface
|
||||
return true;
|
||||
}
|
||||
|
||||
function _rollbackSteps(bytes32 planId) internal {
|
||||
// Release reserved funds, unlock collateral, etc.
|
||||
// Implementation depends on specific step types
|
||||
}
|
||||
|
||||
function _verifySignature(bytes32 planId, bytes calldata signature, address signer) internal pure returns (bool) {
|
||||
// Verify ECDSA signature
|
||||
bytes32 messageHash = keccak256(abi.encodePacked(planId));
|
||||
bytes32 ethSignedMessageHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", messageHash));
|
||||
address recovered = ecrecover(ethSignedMessageHash, v, r, s);
|
||||
return recovered == signer;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Notary Registry Contract Interface
|
||||
|
||||
### Purpose
|
||||
The notary registry contract stores codehashes, plan attestations, and provides immutable audit trails for compliance and non-repudiation.
|
||||
|
||||
### Interface: `INotaryRegistry`
|
||||
|
||||
```solidity
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
interface INotaryRegistry {
|
||||
/**
|
||||
* @notice Register a new execution plan
|
||||
* @param planId Unique plan identifier
|
||||
* @param steps Execution steps
|
||||
* @param creator Plan creator address
|
||||
* @return proofHash Cryptographic proof hash
|
||||
*/
|
||||
function registerPlan(
|
||||
bytes32 planId,
|
||||
Step[] calldata steps,
|
||||
address creator
|
||||
) external returns (bytes32 proofHash);
|
||||
|
||||
/**
|
||||
* @notice Finalize a plan execution (success or failure)
|
||||
* @param planId Plan identifier
|
||||
* @param success Whether execution succeeded
|
||||
*/
|
||||
function finalizePlan(bytes32 planId, bool success) external;
|
||||
|
||||
/**
|
||||
* @notice Register adapter codehash for security
|
||||
* @param adapter Address of adapter contract
|
||||
* @param codeHash Hash of adapter contract bytecode
|
||||
*/
|
||||
function registerCodeHash(address adapter, bytes32 codeHash) external;
|
||||
|
||||
/**
|
||||
* @notice Verify adapter codehash matches registered hash
|
||||
* @param adapter Adapter address
|
||||
* @return matches Whether codehash matches
|
||||
*/
|
||||
function verifyCodeHash(address adapter) external view returns (bool matches);
|
||||
|
||||
/**
|
||||
* @notice Get notary proof for a plan
|
||||
* @param planId Plan identifier
|
||||
* @return proof Notary proof structure
|
||||
*/
|
||||
function getProof(bytes32 planId) external view returns (NotaryProof memory proof);
|
||||
|
||||
/**
|
||||
* @notice Get all plans registered by a creator
|
||||
* @param creator Creator address
|
||||
* @return planIds Array of plan IDs
|
||||
*/
|
||||
function getPlansByCreator(address creator) external view returns (bytes32[] memory planIds);
|
||||
}
|
||||
|
||||
struct NotaryProof {
|
||||
bytes32 planId;
|
||||
bytes32 proofHash;
|
||||
address creator;
|
||||
uint256 registeredAt;
|
||||
uint256 finalizedAt;
|
||||
bool finalized;
|
||||
bool success;
|
||||
bytes32[] stepHashes;
|
||||
}
|
||||
```
|
||||
|
||||
### Implementation Example: `NotaryRegistry.sol`
|
||||
|
||||
```solidity
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||
import "./INotaryRegistry.sol";
|
||||
|
||||
contract NotaryRegistry is INotaryRegistry, Ownable {
|
||||
mapping(bytes32 => NotaryProof) public proofs;
|
||||
mapping(address => bytes32[]) public creatorPlans;
|
||||
mapping(address => bytes32) public codeHashes;
|
||||
|
||||
event PlanRegistered(bytes32 indexed planId, address creator, bytes32 proofHash);
|
||||
event PlanFinalized(bytes32 indexed planId, bool success);
|
||||
event CodeHashRegistered(address indexed adapter, bytes32 codeHash);
|
||||
|
||||
function registerPlan(
|
||||
bytes32 planId,
|
||||
Step[] calldata steps,
|
||||
address creator
|
||||
) external override returns (bytes32 proofHash) {
|
||||
require(proofs[planId].planId == bytes32(0), "Plan already registered");
|
||||
|
||||
bytes32[] memory stepHashes = new bytes32[](steps.length);
|
||||
for (uint256 i = 0; i < steps.length; i++) {
|
||||
stepHashes[i] = keccak256(abi.encode(steps[i]));
|
||||
}
|
||||
|
||||
bytes32 stepsHash = keccak256(abi.encode(stepHashes));
|
||||
proofHash = keccak256(abi.encodePacked(planId, creator, stepsHash, block.timestamp));
|
||||
|
||||
proofs[planId] = NotaryProof({
|
||||
planId: planId,
|
||||
proofHash: proofHash,
|
||||
creator: creator,
|
||||
registeredAt: block.timestamp,
|
||||
finalizedAt: 0,
|
||||
finalized: false,
|
||||
success: false,
|
||||
stepHashes: stepHashes
|
||||
});
|
||||
|
||||
creatorPlans[creator].push(planId);
|
||||
|
||||
emit PlanRegistered(planId, creator, proofHash);
|
||||
}
|
||||
|
||||
function finalizePlan(bytes32 planId, bool success) external override {
|
||||
NotaryProof storage proof = proofs[planId];
|
||||
require(proof.planId != bytes32(0), "Plan not registered");
|
||||
require(!proof.finalized, "Plan already finalized");
|
||||
|
||||
proof.finalized = true;
|
||||
proof.success = success;
|
||||
proof.finalizedAt = block.timestamp;
|
||||
|
||||
emit PlanFinalized(planId, success);
|
||||
}
|
||||
|
||||
function registerCodeHash(address adapter, bytes32 codeHash) external override onlyOwner {
|
||||
codeHashes[adapter] = codeHash;
|
||||
emit CodeHashRegistered(adapter, codeHash);
|
||||
}
|
||||
|
||||
function verifyCodeHash(address adapter) external view override returns (bool matches) {
|
||||
bytes32 registeredHash = codeHashes[adapter];
|
||||
if (registeredHash == bytes32(0)) return false;
|
||||
|
||||
bytes32 currentHash;
|
||||
assembly {
|
||||
currentHash := extcodehash(adapter)
|
||||
}
|
||||
|
||||
return currentHash == registeredHash;
|
||||
}
|
||||
|
||||
function getProof(bytes32 planId) external view override returns (NotaryProof memory) {
|
||||
return proofs[planId];
|
||||
}
|
||||
|
||||
function getPlansByCreator(address creator) external view override returns (bytes32[] memory) {
|
||||
return creatorPlans[creator];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Adapter Registry Contract Interface
|
||||
|
||||
### Purpose
|
||||
The adapter registry manages whitelisting/blacklisting of adapters (both DeFi protocols and Fiat/DTL connectors), tracks versions, and enforces upgrade controls.
|
||||
|
||||
### Interface: `IAdapterRegistry`
|
||||
|
||||
```solidity
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
interface IAdapterRegistry {
|
||||
/**
|
||||
* @notice Check if adapter is whitelisted
|
||||
* @param adapter Adapter contract address
|
||||
* @return whitelisted Whether adapter is whitelisted
|
||||
*/
|
||||
function isWhitelisted(address adapter) external view returns (bool whitelisted);
|
||||
|
||||
/**
|
||||
* @notice Register a new adapter
|
||||
* @param adapter Adapter contract address
|
||||
* @param adapterType Type of adapter (DEFI or FIAT_DTL)
|
||||
* @param version Adapter version string
|
||||
* @param metadata Additional metadata (IPFS hash, etc.)
|
||||
*/
|
||||
function registerAdapter(
|
||||
address adapter,
|
||||
AdapterType adapterType,
|
||||
string calldata version,
|
||||
bytes calldata metadata
|
||||
) external;
|
||||
|
||||
/**
|
||||
* @notice Whitelist an adapter
|
||||
* @param adapter Adapter contract address
|
||||
*/
|
||||
function whitelistAdapter(address adapter) external;
|
||||
|
||||
/**
|
||||
* @notice Blacklist an adapter
|
||||
* @param adapter Adapter contract address
|
||||
*/
|
||||
function blacklistAdapter(address adapter) external;
|
||||
|
||||
/**
|
||||
* @notice Get adapter information
|
||||
* @param adapter Adapter contract address
|
||||
* @return info Adapter information structure
|
||||
*/
|
||||
function getAdapterInfo(address adapter) external view returns (AdapterInfo memory info);
|
||||
|
||||
/**
|
||||
* @notice List all whitelisted adapters
|
||||
* @param adapterType Filter by type (0 = ALL)
|
||||
* @return adapters Array of adapter addresses
|
||||
*/
|
||||
function listAdapters(AdapterType adapterType) external view returns (address[] memory adapters);
|
||||
}
|
||||
|
||||
enum AdapterType {
|
||||
ALL,
|
||||
DEFI,
|
||||
FIAT_DTL
|
||||
}
|
||||
|
||||
struct AdapterInfo {
|
||||
address adapter;
|
||||
AdapterType adapterType;
|
||||
string version;
|
||||
bool whitelisted;
|
||||
bool blacklisted;
|
||||
uint256 registeredAt;
|
||||
bytes metadata;
|
||||
}
|
||||
```
|
||||
|
||||
### Implementation Example: `AdapterRegistry.sol`
|
||||
|
||||
```solidity
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
import "@openzeppelin/contracts/access/Ownable.sol";
|
||||
import "./IAdapterRegistry.sol";
|
||||
|
||||
contract AdapterRegistry is IAdapterRegistry, Ownable {
|
||||
mapping(address => AdapterInfo) public adapters;
|
||||
address[] public adapterList;
|
||||
|
||||
event AdapterRegistered(address indexed adapter, AdapterType adapterType, string version);
|
||||
event AdapterWhitelisted(address indexed adapter);
|
||||
event AdapterBlacklisted(address indexed adapter);
|
||||
|
||||
function registerAdapter(
|
||||
address adapter,
|
||||
AdapterType adapterType,
|
||||
string calldata version,
|
||||
bytes calldata metadata
|
||||
) external override onlyOwner {
|
||||
require(adapters[adapter].adapter == address(0), "Adapter already registered");
|
||||
|
||||
adapters[adapter] = AdapterInfo({
|
||||
adapter: adapter,
|
||||
adapterType: adapterType,
|
||||
version: version,
|
||||
whitelisted: false,
|
||||
blacklisted: false,
|
||||
registeredAt: block.timestamp,
|
||||
metadata: metadata
|
||||
});
|
||||
|
||||
adapterList.push(adapter);
|
||||
|
||||
emit AdapterRegistered(adapter, adapterType, version);
|
||||
}
|
||||
|
||||
function whitelistAdapter(address adapter) external override onlyOwner {
|
||||
require(adapters[adapter].adapter != address(0), "Adapter not registered");
|
||||
require(!adapters[adapter].blacklisted, "Adapter is blacklisted");
|
||||
|
||||
adapters[adapter].whitelisted = true;
|
||||
emit AdapterWhitelisted(adapter);
|
||||
}
|
||||
|
||||
function blacklistAdapter(address adapter) external override onlyOwner {
|
||||
require(adapters[adapter].adapter != address(0), "Adapter not registered");
|
||||
|
||||
adapters[adapter].blacklisted = true;
|
||||
adapters[adapter].whitelisted = false;
|
||||
emit AdapterBlacklisted(adapter);
|
||||
}
|
||||
|
||||
function isWhitelisted(address adapter) external view override returns (bool) {
|
||||
AdapterInfo memory info = adapters[adapter];
|
||||
return info.whitelisted && !info.blacklisted;
|
||||
}
|
||||
|
||||
function getAdapterInfo(address adapter) external view override returns (AdapterInfo memory) {
|
||||
return adapters[adapter];
|
||||
}
|
||||
|
||||
function listAdapters(AdapterType adapterType) external view override returns (address[] memory) {
|
||||
uint256 count = 0;
|
||||
for (uint256 i = 0; i < adapterList.length; i++) {
|
||||
if (adapterType == AdapterType.ALL || adapters[adapterList[i]].adapterType == adapterType) {
|
||||
if (adapters[adapterList[i]].whitelisted && !adapters[adapterList[i]].blacklisted) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
address[] memory result = new address[](count);
|
||||
uint256 index = 0;
|
||||
for (uint256 i = 0; i < adapterList.length; i++) {
|
||||
if (adapterType == AdapterType.ALL || adapters[adapterList[i]].adapterType == adapterType) {
|
||||
if (adapters[adapterList[i]].whitelisted && !adapters[adapterList[i]].blacklisted) {
|
||||
result[index] = adapterList[i];
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Integration Patterns for Atomicity
|
||||
|
||||
### Pattern A: Two-Phase Commit (2PC)
|
||||
|
||||
```solidity
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
contract TwoPhaseCommitHandler {
|
||||
enum Phase { PREPARE, COMMIT, ABORT }
|
||||
|
||||
mapping(bytes32 => Phase) public phases;
|
||||
|
||||
function prepare(bytes32 planId, Step[] calldata steps) external {
|
||||
// Mark assets as reserved
|
||||
// Store prepare state
|
||||
phases[planId] = Phase.PREPARE;
|
||||
}
|
||||
|
||||
function commit(bytes32 planId) external {
|
||||
require(phases[planId] == Phase.PREPARE, "Not prepared");
|
||||
// Execute all steps atomically
|
||||
phases[planId] = Phase.COMMIT;
|
||||
}
|
||||
|
||||
function abort(bytes32 planId) external {
|
||||
require(phases[planId] == Phase.PREPARE, "Not prepared");
|
||||
// Release reserved assets
|
||||
phases[planId] = Phase.ABORT;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern B: HTLC-like Pattern
|
||||
|
||||
```solidity
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
contract HTLCPattern {
|
||||
struct HTLC {
|
||||
bytes32 hashLock;
|
||||
address beneficiary;
|
||||
uint256 amount;
|
||||
uint256 expiry;
|
||||
bool claimed;
|
||||
}
|
||||
|
||||
mapping(bytes32 => HTLC) public htlcLocks;
|
||||
|
||||
function createHTLC(
|
||||
bytes32 planId,
|
||||
bytes32 hashLock,
|
||||
address beneficiary,
|
||||
uint256 amount,
|
||||
uint256 expiry
|
||||
) external {
|
||||
htlcLocks[planId] = HTLC({
|
||||
hashLock: hashLock,
|
||||
beneficiary: beneficiary,
|
||||
amount: amount,
|
||||
expiry: expiry,
|
||||
claimed: false
|
||||
});
|
||||
}
|
||||
|
||||
function claimHTLC(bytes32 planId, bytes32 preimage) external {
|
||||
HTLC storage htlc = htlcLocks[planId];
|
||||
require(keccak256(abi.encodePacked(preimage)) == htlc.hashLock, "Invalid preimage");
|
||||
require(block.timestamp < htlc.expiry, "Expired");
|
||||
require(!htlc.claimed, "Already claimed");
|
||||
|
||||
htlc.claimed = true;
|
||||
// Transfer funds
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern C: Conditional Finality via Notary
|
||||
|
||||
```solidity
|
||||
// SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.20;
|
||||
|
||||
contract ConditionalFinalityHandler {
|
||||
INotaryRegistry public notaryRegistry;
|
||||
|
||||
mapping(bytes32 => bool) public pendingFinalization;
|
||||
|
||||
function executeWithConditionalFinality(
|
||||
bytes32 planId,
|
||||
Step[] calldata steps
|
||||
) external {
|
||||
// Execute DLT steps
|
||||
// Mark as pending finalization
|
||||
pendingFinalization[planId] = true;
|
||||
|
||||
// Notary must co-sign after bank settlement
|
||||
}
|
||||
|
||||
function finalizeWithNotary(bytes32 planId, bytes calldata notarySignature) external {
|
||||
require(pendingFinalization[planId], "Not pending");
|
||||
require(notaryRegistry.verifyNotarySignature(planId, notarySignature), "Invalid notary signature");
|
||||
|
||||
// Complete finalization
|
||||
pendingFinality[planId] = false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Security Considerations
|
||||
|
||||
### Access Control
|
||||
- Use OpenZeppelin's `Ownable` or `AccessControl` for admin functions
|
||||
- Implement multi-sig for critical operations (adapter whitelisting, codehash registration)
|
||||
|
||||
### Reentrancy Protection
|
||||
- Use `ReentrancyGuard` for execute functions
|
||||
- Follow checks-effects-interactions pattern
|
||||
|
||||
### Upgradeability
|
||||
- Consider using proxy patterns (Transparent/UUPS) for upgradeable contracts
|
||||
- Implement timelocks for upgrades
|
||||
- Require multi-sig for upgrade approvals
|
||||
|
||||
### Codehash Verification
|
||||
- Register codehashes for all adapters
|
||||
- Verify codehash before execution
|
||||
- Prevent execution if codehash doesn't match
|
||||
|
||||
### Gas Optimization
|
||||
- Batch operations where possible
|
||||
- Use `calldata` instead of `memory` for arrays
|
||||
- Minimize storage operations
|
||||
|
||||
---
|
||||
|
||||
## 6. Testing Requirements
|
||||
|
||||
### Unit Tests
|
||||
- Test each interface function
|
||||
- Test error cases (invalid inputs, unauthorized access)
|
||||
- Test atomicity (all-or-nothing execution)
|
||||
|
||||
### Integration Tests
|
||||
- Test full workflow execution
|
||||
- Test 2PC prepare/commit/abort flows
|
||||
- Test notary integration
|
||||
- Test adapter registry whitelisting
|
||||
|
||||
### Fuzz Tests
|
||||
- Fuzz step configurations
|
||||
- Fuzz plan structures
|
||||
- Fuzz edge cases (empty steps, large arrays)
|
||||
|
||||
---
|
||||
|
||||
**Document Version**: 1.0
|
||||
**Last Updated**: 2025-01-15
|
||||
**Author**: Smart Contract Team
|
||||
|
||||
Reference in New Issue
Block a user