Smart Contract Integration

Overview

This document outlines the smart contract architecture for the SKYOCEAN platform MVP. Smart contracts serve as the backbone for executing and enforcing trade agreements, managing document verification, facilitating financier participation, and maintaining immutable records of transaction histories. These contracts work in tandem with the Knowledge Assets stored in the DKG to provide a complete solution.

Smart Contract Architecture

Reusable Contract Architecture

For the SKYOCEAN platform, we employ a reusable contract architecture rather than deploying new contracts for each transaction, which would be prohibitively expensive. Our approach utilizes factory patterns and registry contracts that can handle multiple transactions of various types (CAD, on-chain deposits, partial payments, etc.).

Core Contract Components

The platform uses the following core contracts:

  1. Trade Agreement Contract - Creates and manages transaction records within a single contract
  2. Document Verification Registry - Handles document submission and verification for all transactions
  3. Token Management System - Manages the staking and release of financier tokens across all trades
  4. Knowledge Asset Registry - Maps blockchain transaction IDs to DKG Knowledge Assets

Contract Roles Explained

Below is a detailed explanation of each core contract:

Trade Agreement Contract:
This contract is responsible for creating and managing trade transactions on the blockchain. It assigns unique transaction IDs, orchestrates state transitions (such as financing completion, payment initiation, and transaction finalization), and emits events that trigger further processing by other system components.

Document Verification Registry:
Even though document verification is performed off-chain by the DKG, this contract records on-chain events related to document submission and verification. It captures the status updates once the DKG confirms that all required documents have been received and verified, ensuring an immutable record that triggers subsequent actions in the transaction flow.

Token Management System:
This contract manages all token-related operations, including staking, releasing tokens with profit shares upon successful completion, and returning tokens in case of transaction cancellations. It works closely with the Trade Agreement Contract to ensure that financier participation is securely recorded.

Knowledge Asset Registry:
Acting like a specialized oracle, this contract bridges the on-chain and off-chain worlds by linking blockchain transaction IDs to their corresponding Knowledge Assets stored in the DKG. It provides a secure reference to rich, verified off-chain data while leaving the actual document verification to the DKG.

Transaction Flow Approach

Rather than deploying new contracts per transaction, our system:

  1. Generates a unique transaction ID for each trade
  2. Registers the transaction in the appropriate registries
  3. Maps the transaction ID to the Knowledge Asset UAL
  4. Maintains transaction state through mappings and structs
  5. Coordinates state changes through events and controlled access

Trade Agreement Contract

The Trade Agreement Contract creates and manages all trade transactions within a single contract:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract TradeAgreementContract {
    // Platform administration
    address public platform;
    address public documentVerificationRegistry;
    address public tokenManagementSystem;
    
    // Transaction states
    enum TransactionState {
        Created,
        FinancingSecured,
        DocumentsSubmitted,
        DocumentsVerified,
        PaymentInitiated,        // Generic payment initiated state
        PaymentConfirmed,        // Generic payment confirmed state
        TokensReleased,
        Completed,
        Disputed,
        Cancelled
    }
    
    // Payment methods
    enum PaymentMethod {
        CAD,                     // Cash Against Documents
        DepositWithBalance,      // Deposit with remaining balance at destination
        LetterOfCredit,          // Traditional LC
        FullPaymentUpfront,      // 100% payment before shipment
        OnChainEscrow            // On-chain escrow payment
    }
    
    // Financier structure
    struct Financier {
        address wallet;
        uint256 stakedAmount;
        uint256 expectedReturn;
        bool hasWithdrawn;
    }
    
    // Transaction structure
    struct Transaction {
        string knowledgeAssetUAL;
        string buyerOffChainId;  // Off-chain ID for CAD or can be empty if buyer has wallet
        address buyerWallet;     // Wallet address for on-chain payments or empty for CAD
        address seller;
        PaymentMethod paymentMethod;
        uint256 targetFinancingAmount;
        uint256 financierRewardRate;  // in basis points (e.g., 350 = 3.5%)
        uint256 totalStaked;
        TransactionState state;
        uint256 createdAt;
        uint256 completedAt;
        mapping(address => bool) isFinancier;
        uint256 financierCount;
    }
    
    // Transaction storage - mapping from transaction ID to Transaction
    mapping(bytes32 => Transaction) public transactions;
    
    // Array of financiers for each transaction
    mapping(bytes32 => Financier[]) public transactionFinanciers;
    
    // Active transaction IDs
    bytes32[] public activeTransactionIds;
    
    // Transaction counter for generating IDs
    uint256 private transactionCounter;
    
    // Events
    event TransactionCreated(bytes32 transactionId, string knowledgeAssetUAL, PaymentMethod paymentMethod);
    event FinancierAdded(bytes32 transactionId, address financier, uint256 amount, uint256 expectedReturn);
    event FinancingCompleted(bytes32 transactionId, uint256 totalAmount);
    event DocumentsSubmitted(bytes32 transactionId);
    event DocumentsVerified(bytes32 transactionId);
    event PaymentInitiated(bytes32 transactionId);
    event PaymentConfirmed(bytes32 transactionId, uint256 amount);
    event TokensReleased(bytes32 transactionId, uint256 totalAmount);
    event TransactionCompleted(bytes32 transactionId);
    event DisputeRaised(bytes32 transactionId, string reason);
    event TransactionCancelled(bytes32 transactionId);
    
    // Modifiers
    modifier onlyPlatform() {
        require(msg.sender == platform, "Only the platform can call this function");
        _;
    }
    
    modifier onlyDocVerification() {
        require(msg.sender == documentVerificationRegistry, "Only the document verification registry can call this function");
        _;
    }
    
    modifier validTransaction(bytes32 transactionId) {
        require(transactions[transactionId].createdAt > 0, "Transaction does not exist");
        _;
    }
    
    modifier inState(bytes32 transactionId, TransactionState state) {
        require(transactions[transactionId].state == state, "Invalid transaction state");
        _;
    }
    
    constructor(address _platform) {
        platform = _platform;
        transactionCounter = 0;
    }
    
    function setDocumentVerificationRegistry(address _registry) external onlyPlatform {
        documentVerificationRegistry = _registry;
    }
    
    function setTokenManagementSystem(address _system) external onlyPlatform {
        tokenManagementSystem = _system;
    }
    
    // Generate a unique transaction ID
    function generateTransactionId(string memory knowledgeAssetUAL) private returns (bytes32) {
        bytes32 transactionId = keccak256(abi.encodePacked(knowledgeAssetUAL, block.timestamp, transactionCounter));
        transactionCounter++;
        return transactionId;
    }
    
    // Create a new transaction
    function createTransaction(
        string memory knowledgeAssetUAL,
        string memory buyerOffChainId,
        address buyerWallet,
        address seller,
        uint8 paymentMethodCode,
        uint256 targetFinancingAmount,
        uint256 financierRewardRate
    ) external onlyPlatform returns (bytes32) {
        // Generate transaction ID
        bytes32 transactionId = generateTransactionId(knowledgeAssetUAL);
        
        // Validate payment method
        require(paymentMethodCode <= uint8(PaymentMethod.OnChainEscrow), "Invalid payment method");
        PaymentMethod paymentMethod = PaymentMethod(paymentMethodCode);
        
        // For CAD payments, buyer off-chain ID is required
        if (paymentMethod == PaymentMethod.CAD) {
            require(bytes(buyerOffChainId).length > 0, "Buyer off-chain ID required for CAD payment");
        }
        
        // For on-chain payments, buyer wallet is required
        if (paymentMethod == PaymentMethod.OnChainEscrow || 
            paymentMethod == PaymentMethod.FullPaymentUpfront || 
            paymentMethod == PaymentMethod.DepositWithBalance) {
            require(buyerWallet != address(0), "Buyer wallet required for on-chain payment");
        }
        
        // Create transaction
        Transaction storage newTx = transactions[transactionId];
        newTx.knowledgeAssetUAL = knowledgeAssetUAL;
        newTx.buyerOffChainId = buyerOffChainId;
        newTx.buyerWallet = buyerWallet;
        newTx.seller = seller;
        newTx.paymentMethod = paymentMethod;
        newTx.targetFinancingAmount = targetFinancingAmount;
        newTx.financierRewardRate = financierRewardRate;
        newTx.totalStaked = 0;
        newTx.state = TransactionState.Created;
        newTx.createdAt = block.timestamp;
        newTx.financierCount = 0;
        
        // Add to active transactions
        activeTransactionIds.push(transactionId);
        
        emit TransactionCreated(transactionId, knowledgeAssetUAL, paymentMethod);
        
        return transactionId;
    }
    
    // Add a financier to a transaction
    function addFinancier(
        bytes32 transactionId,
        address financierWallet,
        uint256 amount
    ) external onlyPlatform validTransaction(transactionId) inState(transactionId, TransactionState.Created) {
        Transaction storage transaction = transactions[transactionId];
        
        require(amount > 0, "Staked amount must be greater than 0");
        require(!transaction.isFinancier[financierWallet], "Financier already added");
        
        // Calculate expected return based on reward rate
        uint256 expectedReturn = amount + (amount * transaction.financierRewardRate / 10000);
        
        // Add financier to the transaction
        Financier memory newFinancier = Financier({
            wallet: financierWallet,
            stakedAmount: amount,
            expectedReturn: expectedReturn,
            hasWithdrawn: false
        });
        
        transactionFinanciers[transactionId].push(newFinancier);
        transaction.isFinancier[financierWallet] = true;
        transaction.financierCount++;
        transaction.totalStaked += amount;
        
        emit FinancierAdded(transactionId, financierWallet, amount, expectedReturn);
        
        // Check if financing target is reached
        if (transaction.totalStaked >= transaction.targetFinancingAmount) {
            transaction.state = TransactionState.FinancingSecured;
            emit FinancingCompleted(transactionId, transaction.totalStaked);
        }
    }
    
    // Document verification state management functions
    function documentsSubmitted(bytes32 transactionId) 
        external 
        onlyDocVerification 
        validTransaction(transactionId) 
        inState(transactionId, TransactionState.FinancingSecured) 
    {
        transactions[transactionId].state = TransactionState.DocumentsSubmitted;
        emit DocumentsSubmitted(transactionId);
    }
    
    function documentsVerified(bytes32 transactionId) 
        external 
        onlyDocVerification 
        validTransaction(transactionId) 
        inState(transactionId, TransactionState.DocumentsSubmitted) 
    {
        transactions[transactionId].state = TransactionState.DocumentsVerified;
        emit DocumentsVerified(transactionId);
    }
    
    // Payment handling functions based on payment method
    
    // For on-chain payments
    function depositPayment(bytes32 transactionId) 
        external 
        payable 
        validTransaction(transactionId) 
        inState(transactionId, TransactionState.DocumentsVerified) 
    {
        Transaction storage transaction = transactions[transactionId];
        
        // Only buyer can deposit for on-chain methods
        require(msg.sender == transaction.buyerWallet, "Only buyer can deposit");
        
        // Payment method must be on-chain
        require(
            transaction.paymentMethod == PaymentMethod.OnChainEscrow || 
            transaction.paymentMethod == PaymentMethod.FullPaymentUpfront ||
            transaction.paymentMethod == PaymentMethod.DepositWithBalance, 
            "Invalid payment method for on-chain deposit"
        );
        
        // Value must match target amount for full payments
        if (transaction.paymentMethod == PaymentMethod.OnChainEscrow || 
            transaction.paymentMethod == PaymentMethod.FullPaymentUpfront) {
            require(msg.value == transaction.targetFinancingAmount, "Incorrect payment amount");
        }
        
        transaction.state = TransactionState.PaymentConfirmed;
        
        emit PaymentInitiated(transactionId);
        emit PaymentConfirmed(transactionId, msg.value);
    }
    
    // For off-chain payments (CAD)
    function confirmOffChainPayment(bytes32 transactionId) 
        external 
        onlyPlatform 
        validTransaction(transactionId) 
        inState(transactionId, TransactionState.DocumentsVerified) 
    {
        Transaction storage transaction = transactions[transactionId];
        
        // Payment method must be CAD or LC
        require(
            transaction.paymentMethod == PaymentMethod.CAD || 
            transaction.paymentMethod == PaymentMethod.LetterOfCredit, 
            "Invalid payment method for off-chain confirmation"
        );
        
        transaction.state = TransactionState.PaymentConfirmed;
        
        emit PaymentConfirmed(transactionId, transaction.targetFinancingAmount);
    }
    
    // Release tokens to financiers after payment confirmation
    function releaseTokens(bytes32 transactionId) 
        external 
        onlyPlatform 
        validTransaction(transactionId) 
        inState(transactionId, TransactionState.PaymentConfirmed) 
    {
        Transaction storage transaction = transactions[transactionId];
        
        require(tokenManagementSystem != address(0), "Token management system not set");
        
        transaction.state = TransactionState.TokensReleased;
        
        // Call the token management system to release tokens to financiers
        ITokenManagementSystem(tokenManagementSystem).releaseTokensToFinanciers(
            transactionId,
            transactionFinanciers[transactionId]
        );
        
        emit TokensReleased(transactionId, transaction.totalStaked);
    }
    
    // Complete the transaction
    function completeTransaction(bytes32 transactionId) 
        external 
        onlyPlatform 
        validTransaction(transactionId) 
        inState(transactionId, TransactionState.TokensReleased) 
    {
        Transaction storage transaction = transactions[transactionId];
        transaction.state = TransactionState.Completed;
        transaction.completedAt = block.timestamp;
        
        emit TransactionCompleted(transactionId);
    }
    
    // Dispute and cancellation functions
    function raiseDispute(bytes32 transactionId, string memory reason) 
        external 
        validTransaction(transactionId) 
    {
        Transaction storage transaction = transactions[transactionId];
        
        require(
            msg.sender == transaction.seller || 
            msg.sender == platform, 
            "Only seller or platform can raise disputes"
        );
        
        require(
            transaction.state != TransactionState.Completed && 
            transaction.state != TransactionState.Cancelled, 
            "Transaction already finalized"
        );
        
        transaction.state = TransactionState.Disputed;
        
        emit DisputeRaised(transactionId, reason);
    }
    
    function cancelTransaction(bytes32 transactionId) 
        external 
        onlyPlatform 
        validTransaction(transactionId) 
    {
        Transaction storage transaction = transactions[transactionId];
        
        require(transaction.state != TransactionState.Completed, "Transaction already completed");
        
        // Return tokens to financiers if they have staked
        if (transaction.state == TransactionState.FinancingSecured || 
            transaction.state == TransactionState.DocumentsSubmitted || 
            transaction.state == TransactionState.DocumentsVerified) {
            
            ITokenManagementSystem(tokenManagementSystem).returnStakedTokens(
                transactionId,
                transactionFinanciers[transactionId]
            );
        }
        
        transaction.state = TransactionState.Cancelled;
        
        emit TransactionCancelled(transactionId);
    }
    
    // Getter functions for transaction data
    function getTransactionState(bytes32 transactionId) external view returns (TransactionState) {
        return transactions[transactionId].state;
    }
    
    function getTransactionPaymentMethod(bytes32 transactionId) external view returns (PaymentMethod) {
        return transactions[transactionId].paymentMethod;
    }
    
    function getTransactionFinanciers(bytes32 transactionId) external view returns (uint256) {
        return transactions[transactionId].financierCount;
    }
    
    function getTransactionKnowledgeAssetUAL(bytes32 transactionId) external view returns (string memory) {
        return transactions[transactionId].knowledgeAssetUAL;
    }
    
    function getActiveTransactionsCount() external view returns (uint256) {
        return activeTransactionIds.length;
    }
    
    // Get summary of transaction details
    function getTransactionSummary(bytes32 transactionId) 
        external 
        view 
        validTransaction(transactionId) 
        returns (
            string memory knowledgeAssetUAL,
            string memory buyerOffChainId,
            address buyerWallet,
            address seller,
            uint8 paymentMethod,
            uint256 targetFinancingAmount,
            uint256 totalStaked,
            uint8 state,
            uint256 createdAt,
            uint256 completedAt,
            uint256 financierCount
        ) 
    {
        Transaction storage txn = transactions[transactionId];
        
        return (
            txn.knowledgeAssetUAL,
            txn.buyerOffChainId,
            txn.buyerWallet,
            txn.seller,
            uint8(txn.paymentMethod),
            txn.targetFinancingAmount,
            txn.totalStaked,
            uint8(txn.state),
            txn.createdAt,
            txn.completedAt,
            txn.financierCount
        );
    }
}

interface ITokenManagementSystem {
    function releaseTokensToFinanciers(bytes32 transactionId, TradeAgreementContract.Financier[] calldata financiers) external;
    function returnStakedTokens(bytes32 transactionId, TradeAgreementContract.Financier[] calldata financiers) external;
}

Document Verification Registry

The Document Verification Registry handles documents for all transactions:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

interface ITradeAgreementContract {
    function documentsSubmitted(bytes32 transactionId) external;
    function documentsVerified(bytes32 transactionId) external;
}

contract DocumentVerificationRegistry {
    // Platform administration
    address public platform;
    address public tradeAgreementContract;
    
    // Document structure
    struct Document {
        string documentType;
        string documentHash;
        string ipfsHash;
        uint256 timestamp;
        bool verified;
    }
    
    // Document storage by transaction ID and document type
    mapping(bytes32 => mapping(string => Document)) public documents;
    
    // Required documents for each transaction
    mapping(bytes32 => string[]) public requiredDocuments;
    
    // Document submission and verification tracking
    mapping(bytes32 => mapping(string => bool)) public documentSubmitted;
    mapping(bytes32 => mapping(string => bool)) public documentVerified;
    
    // Events
    event DocumentSubmitted(bytes32 transactionId, string documentType, string documentHash, string ipfsHash);
    event DocumentVerified(bytes32 transactionId, string documentType, string documentHash);
    event AllDocumentsVerified(bytes32 transactionId);
    
    // Modifiers
    modifier onlyPlatform() {
        require(msg.sender == platform, "Only the platform can call this function");
        _;
    }
    
    modifier validTransaction(bytes32 transactionId) {
        require(requiredDocuments[transactionId].length > 0, "Transaction not registered");
        _;
    }
    
    constructor(address _platform) {
        platform = _platform;
    }
    
    function setTradeAgreementContract(address _contract) external onlyPlatform {
        tradeAgreementContract = _contract;
    }
    
    // Register a new transaction with its required documents
    function registerTransaction(bytes32 transactionId, string[] memory _requiredDocuments) external onlyPlatform {
        require(requiredDocuments[transactionId].length == 0, "Transaction already registered");
        require(_requiredDocuments.length > 0, "Required documents list cannot be empty");
        
        requiredDocuments[transactionId] = _requiredDocuments;
    }
    
    // Submit a document for a transaction
    function submitDocument(
        bytes32 transactionId,
        string memory documentType,
        string memory documentHash,
        string memory ipfsHash
    ) external onlyPlatform validTransaction(transactionId) {
        require(isRequiredDocument(transactionId, documentType), "Document type not required for this transaction");
        require(!documentSubmitted[transactionId][documentType], "Document already submitted");
        
        documents[transactionId][documentType] = Document({
            documentType: documentType,
            documentHash: documentHash,
            ipfsHash: ipfsHash,
            timestamp: block.timestamp,
            verified: false
        });
        
        documentSubmitted[transactionId][documentType] = true;
        
        emit DocumentSubmitted(transactionId, documentType, documentHash, ipfsHash);
        
        if (allDocumentsSubmitted(transactionId)) {
            ITradeAgreementContract(tradeAgreementContract).documentsSubmitted(transactionId);
        }
    }
    
    // Verify a document for a transaction
    function verifyDocument(bytes32 transactionId, string memory documentType) 
        external 
        onlyPlatform 
        validTransaction(transactionId) 
    {
        require(documentSubmitted[transactionId][documentType], "Document not submitted");
        require(!documentVerified[transactionId][documentType], "Document already verified");
        
        documentVerified[transactionId][documentType] = true;
        documents[transactionId][documentType].verified = true;
        
        emit DocumentVerified(transactionId, documentType, documents[transactionId][documentType].documentHash);
        
        if (allDocumentsVerified(transactionId)) {
            ITradeAgreementContract(tradeAgreementContract).documentsVerified(transactionId);
            emit AllDocumentsVerified(transactionId);
        }
    }
    
    // Check if a document type is required for a transaction
    function isRequiredDocument(bytes32 transactionId, string memory documentType) public view returns (bool) {
        string[] memory docs = requiredDocuments[transactionId];
        for (uint i = 0; i < docs.length; i++) {
            if (keccak256(bytes(docs[i])) == keccak256(bytes(documentType))) {
                return true;
            }
        }
        return false;
    }
    
    // Check if all required documents have been submitted for a transaction
    function allDocumentsSubmitted(bytes32 transactionId) public view returns (bool) {
        string[] memory docs = requiredDocuments[transactionId];
        for (uint i = 0; i < docs.length; i++) {
            if (!documentSubmitted[transactionId][docs[i]]) {
                return false;
            }
        }
        return true;
    }
    
    // Check if all submitted documents have been verified for a transaction
    function allDocumentsVerified(bytes32 transactionId) public view returns (bool) {
        string[] memory docs = requiredDocuments[transactionId];
        for (uint i = 0; i < docs.length; i++) {
            if (!documentVerified[transactionId][docs[i]]) {
                return false;
            }
        }
        return true;
    }
    
    // Get document details
    function getDocumentDetails(bytes32 transactionId, string memory documentType) 
        external 
        view 
        returns (
            string memory,
            string memory,
            string memory,
            uint256,
            bool
        ) 
    {
        Document memory doc = documents[transactionId][documentType];
        return (
            doc.documentType,
            doc.documentHash,
            doc.ipfsHash,
            doc.timestamp,
            doc.verified
        );
    }
    
    // Get all required documents for a transaction
    function getRequiredDocuments(bytes32 transactionId) external view returns (string[] memory) {
        return requiredDocuments[transactionId];
    }
}

Token Management System

The Token Management System handles token operations for all transactions:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract TokenManagementSystem {
    // Platform administration
    address public platform;
    address public tradeAgreementContract;
    address public tokenAddress;
    
    // Transaction token accounting
    mapping(bytes32 => uint256) public transactionStakedAmount;
    mapping(bytes32 => mapping(address => uint256)) public financierStakedAmount;
    mapping(bytes32 => address[]) public transactionFinanciers;
    
    // Events
    event TokensStaked(bytes32 transactionId, address financier, uint256 amount);
    event TokensReleased(bytes32 transactionId, address financier, uint256 amount, uint256 profit);
    event TokensReturned(bytes32 transactionId, address financier, uint256 amount);
    
    // Modifiers
    modifier onlyPlatform() {
        require(msg.sender == platform, "Only the platform can call this function");
        _;
    }
    
    modifier onlyTradeAgreementContract() {
        require(msg.sender == tradeAgreementContract, "Only the trade agreement contract can call this function");
        _;
    }
    
    constructor(address _platform, address _tokenAddress) {
        platform = _platform;
        tokenAddress = _tokenAddress;
    }
    
    function setTradeAgreementContract(address _contract) external onlyPlatform {
        tradeAgreementContract = _contract;
    }
    
    // Stake tokens for a transaction
    function stakeTokens(bytes32 transactionId, address financier, uint256 amount) external onlyPlatform {
        require(amount > 0, "Amount must be greater than 0");
        
        // Transfer tokens from financier to this contract
        bool success = IERC20(tokenAddress).transferFrom(financier, address(this), amount);
        require(success, "Token transfer failed");
        
        // Record staking
        if (financierStakedAmount[transactionId][financier] == 0) {
            transactionFinanciers[transactionId].push(financier);
        }
        
        financierStakedAmount[transactionId][financier] += amount;
        transactionStakedAmount[transactionId] += amount;
        
        emit TokensStaked(transactionId, financier, amount);
    }
    
    // Release tokens with profit after successful transaction
    function releaseTokensToFinanciers(
        bytes32 transactionId, 
        TradeAgreementContract.Financier[] calldata financiers
    ) external onlyTradeAgreementContract {
        for (uint i = 0; i < financiers.length; i++) {
            TradeAgreementContract.Financier memory financier = financiers[i];
            
            if (!financier.hasWithdrawn && financier.stakedAmount > 0) {
                // Transfer original amount + profit to financier
                bool success = IERC20(tokenAddress).transfer(financier.wallet, financier.expectedReturn);
                require(success, "Token release failed");
                
                uint256 profit = financier.expectedReturn - financier.stakedAmount;
                emit TokensReleased(transactionId, financier.wallet, financier.stakedAmount, profit);
            }
        }
    }
    
    // Return staked tokens in case of cancellation
    function returnStakedTokens(
        bytes32 transactionId, 
        TradeAgreementContract.Financier[] calldata financiers
    ) external onlyTradeAgreementContract {
        for (uint i = 0; i < financiers.length; i++) {
            TradeAgreementContract.Financier memory financier = financiers[i];
            
            if (!financier.hasWithdrawn && financier.stakedAmount > 0) {
                // Return only the staked amount, no profit
                bool success = IERC20(tokenAddress).transfer(financier.wallet, financier.stakedAmount);
                require(success, "Token return failed");
                
                emit TokensReturned(transactionId, financier.wallet, financier.stakedAmount);
            }
        }
    }
    
    // Get transaction total staked amount
    function getTransactionStakedAmount(bytes32 transactionId) external view returns (uint256) {
        return transactionStakedAmount[transactionId];
    }
    
    // Get financier staked amount for a transaction
    function getFinancierStakedAmount(bytes32 transactionId, address financier) external view returns (uint256) {
        return financierStakedAmount[transactionId][financier];
    }
    
    // Get contract token balance
    function getContractBalance() external view returns (uint256) {
        return IERC20(tokenAddress).balanceOf(address(this));
    }
}

// Interface for accessing Financier struct from Trade Agreement Contract
interface TradeAgreementContract {
    struct Financier {
        address wallet;
        uint256 stakedAmount;
        uint256 expectedReturn;
        bool hasWithdrawn;
    }
}

Knowledge Asset Registry

The Knowledge Asset Registry handles the bidirectional link between blockchain transaction IDs and DKG Knowledge Assets:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract KnowledgeAssetRegistry {
    address public platform;
    
    // Mapping from transaction ID to Knowledge Asset UAL
    mapping(bytes32 => string) public transactionToKnowledgeAsset;
    
    // Mapping from Knowledge Asset UAL to transaction ID
    mapping(string => bytes32) public knowledgeAssetToTransaction;
    
    // Events
    event KnowledgeAssetLinked(bytes32 indexed transactionId, string knowledgeAssetUAL);
    event KnowledgeAssetUpdated(string knowledgeAssetUAL, string updateType, string details);
    
    // Modifiers
    modifier onlyPlatform() {
        require(msg.sender == platform, "Only the platform can call this function");
        _;
    }
    
    constructor(address _platform) {
        platform = _platform;
    }
    
    function linkKnowledgeAsset(bytes32 transactionId, string memory knowledgeAssetUAL) external onlyPlatform {
        transactionToKnowledgeAsset[transactionId] = knowledgeAssetUAL;
        knowledgeAssetToTransaction[knowledgeAssetUAL] = transactionId;
        
        emit KnowledgeAssetLinked(transactionId, knowledgeAssetUAL);
    }
    
    function recordKnowledgeAssetUpdate(string memory knowledgeAssetUAL, string memory updateType, string memory details) external onlyPlatform {
        require(bytes(transactionToKnowledgeAsset[knowledgeAssetToTransaction[knowledgeAssetUAL]]).length > 0, "Knowledge Asset not linked");
        
        emit KnowledgeAssetUpdated(knowledgeAssetUAL, updateType, details);
    }
    
    function getTransactionForKnowledgeAsset(string memory knowledgeAssetUAL) external view returns (bytes32) {
        return knowledgeAssetToTransaction[knowledgeAssetUAL];
    }
    
    function getKnowledgeAssetForTransaction(bytes32 transactionId) external view returns (string memory) {
        return transactionToKnowledgeAsset[transactionId];
    }
    
    function isKnowledgeAssetLinked(string memory knowledgeAssetUAL) external view returns (bool) {
        return knowledgeAssetToTransaction[knowledgeAssetUAL] != bytes32(0);
    }
}

API Integration

To interact with these smart contracts and integrate them with the Knowledge Assets in the DKG:

Transaction Creation API

// Create a new trade transaction
async function createTransaction(
  knowledgeAssetUAL, 
  buyerInfo,  // Either off-chain ID or wallet address depending on payment method
  sellerAddress, 
  paymentMethod,  // 0: CAD, 1: DepositWithBalance, 2: LC, 3: FullPayment, 4: OnChainEscrow
  targetFinancingAmount,
  financierRewardRate
) {
  try {
    // 1. Determine buyer details based on payment method
    let buyerOffChainId = "";
    let buyerWallet = "0x0000000000000000000000000000000000000000";
    
    if (paymentMethod === 0 || paymentMethod === 2) { // CAD or LC
      buyerOffChainId = buyerInfo;
    } else { // On-chain payment methods
      buyerWallet = buyerInfo;
    }
    
    // 2. Create transaction in the Trade Agreement Contract
    const factory = await getTradeAgreementContract();
    const tx = await factory.createTransaction(
      knowledgeAssetUAL,
      buyerOffChainId,
      buyerWallet,
      sellerAddress,
      paymentMethod,
      targetFinancingAmount,
      financierRewardRate
    );
    
    const receipt = await tx.wait();
    
    // 3. Get transaction ID from event logs
    const event = receipt.events.find(e => e.event === 'TransactionCreated');
    const transactionId = event.args.transactionId;
    
    // 4. Get required documents from the Knowledge Asset
    const requiredDocs = await getRequiredDocumentsFromKA(knowledgeAssetUAL);
    
    // 5. Register transaction in Document Verification Registry
    const docRegistry = await getDocumentVerificationRegistry();
    await docRegistry.registerTransaction(transactionId, requiredDocs);
    
    // 6. Link Knowledge Asset in the registry
    const kaRegistry = await getKnowledgeAssetRegistry();
    await kaRegistry.linkKnowledgeAsset(transactionId, knowledgeAssetUAL);
    
    // 7. Update the Knowledge Asset with transaction ID
    await updateKnowledgeAssetWithTransactionId(
      knowledgeAssetUAL,
      transactionId
    );
    
    return {
      transactionId,
      knowledgeAssetUAL,
      paymentMethod,
      success: true
    };
  } catch (error) {
    console.error("Error creating transaction:", error);
    return {
      error: error.message,
      success: false
    };
  }
}

Financier Participation API

// Add a financier to a transaction
async function addFinancierToTransaction(
  transactionId,
  financierWalletAddress,
  stakeAmount
) {
  try {
    // 1. Get contract instances
    const factory = await getTradeAgreementContract();
    const tokenSystem = await getTokenManagementSystem();
    
    // 2. First stake the tokens in the token management system
    const tx1 = await tokenSystem.stakeTokens(transactionId, financierWalletAddress, stakeAmount);
    await tx1.wait();
    
    // 3. Then add the financier to the transaction in the factory
    const tx2 = await factory.addFinancier(transactionId, financierWalletAddress, stakeAmount);
    await tx2.wait();
    
    // 4. Get the Knowledge Asset UAL
    const kaRegistry = await getKnowledgeAssetRegistry();
    const knowledgeAssetUAL = await kaRegistry.getKnowledgeAssetForTransaction(transactionId);
    
    // 5. Update the Knowledge Asset with financier information
    await updateKnowledgeAssetWithFinancier(
      knowledgeAssetUAL,
      financierWalletAddress,
      stakeAmount
    );
    
    // 6. Check if financing is completed
    const state = await factory.getTransactionState(transactionId);
    const isFinancingCompleted = state === 1; // FinancingSecured state
    
    if (isFinancingCompleted) {
      await updateKnowledgeAssetWithFinancingCompleted(
        knowledgeAssetUAL
      );
    }
    
    return {
      transactionId,
      financierAddress: financierWalletAddress,
      stakedAmount: stakeAmount,
      financingCompleted: isFinancingCompleted,
      success: true
    };
  } catch (error) {
    console.error("Error adding financier:", error);
    return {
      error: error.message,
      success: false
    };
  }
}

Document Submission and Verification API

// Submit a document for a transaction
async function submitDocument(
  transactionId,
  documentType,
  documentFile
) {
  try {
    // 1. Calculate document hash
    const documentHash = await calculateFileHash(documentFile);
    
    // 2. Upload document to IPFS
    const ipfsHash = await uploadToIPFS(documentFile);
    
    // 3. Get the Knowledge Asset UAL
    const kaRegistry = await getKnowledgeAssetRegistry();
    const knowledgeAssetUAL = await kaRegistry.getKnowledgeAssetForTransaction(transactionId);
    
    // 4. Update the Knowledge Asset with document information in DKG
    await updateKnowledgeAssetWithDocument(
      knowledgeAssetUAL,
      documentType,
      documentHash,
      ipfsHash
    );
    
    // 5. After DKG update, submit document reference to the registry
    const docRegistry = await getDocumentVerificationRegistry();
    const tx = await docRegistry.submitDocument(
      transactionId,
      documentType,
      documentHash,
      ipfsHash
    );
    await tx.wait();
    
    return {
      transactionId,
      documentType,
      documentHash,
      ipfsHash,
      success: true
    };
  } catch (error) {
    console.error("Error submitting document:", error);
    return {
      error: error.message,
      success: false
    };
  }
}

// Record document verification after DKG verification process
async function recordDocumentVerification(
  transactionId,
  documentType,
  verificationResult,
  verifierDetails
) {
  try {
    // 1. Get the Knowledge Asset UAL
    const kaRegistry = await getKnowledgeAssetRegistry();
    const knowledgeAssetUAL = await kaRegistry.getKnowledgeAssetForTransaction(transactionId);
    
    // 2. Verification already happened in DKG - update the Knowledge Asset with verification details
    await updateKnowledgeAssetWithVerificationDetails(
      knowledgeAssetUAL,
      documentType,
      verificationResult,
      verifierDetails
    );
    
    // 3. Now record this verification in the blockchain
    const docRegistry = await getDocumentVerificationRegistry();
    const tx = await docRegistry.verifyDocument(
      transactionId,
      documentType
    );
    await tx.wait();
    
    // 4. Check if all documents verified
    const allVerified = await docRegistry.allDocumentsVerified(transactionId);
    
    // 5. If all documents are verified, update the Knowledge Asset accordingly
    if (allVerified) {
      await updateKnowledgeAssetWithAllDocumentsVerified(
        knowledgeAssetUAL
      );
    }
    
    return {
      transactionId,
      documentType,
      allVerified,
      success: true
    };
  } catch (error) {
    console.error("Error recording document verification:", error);
    return {
      error: error.message,
      success: false
    };
  }
}

Document Status Monitoring API

// Listen for document status changes in DKG and update blockchain
async function monitorDocumentStatus(knowledgeAssetUAL) {
  try {
    // 1. Set up listener for document status changes in DKG
    const edgeNode = await getEdgeNodeConnection();
    
    edgeNode.on('documentStatusChanged', async (documentEvent) => {
      // 2. When status changes in DKG, update the blockchain
      if (documentEvent.knowledgeAssetUAL === knowledgeAssetUAL) {
        // 3. Get transaction ID from KA registry
        const kaRegistry = await getKnowledgeAssetRegistry();
        const transactionId = await kaRegistry.getTransactionForKnowledgeAsset(knowledgeAssetUAL);
        
        // 4. Update blockchain based on document type and new status
        if (documentEvent.status === 'VERIFIED') {
          await recordDocumentVerification(
            transactionId,
            documentEvent.documentType,
            documentEvent.verificationResult,
            documentEvent.verifierDetails
          );
        }
      }
    });
    
    return {
      knowledgeAssetUAL,
      monitoring: true,
      success: true
    };
  } catch (error) {
    console.error("Error setting up document status monitoring:", error);
    return {
      error: error.message,
      success: false
    };
  }
}

Payment Processing API

// Process on-chain payment
async function processOnChainPayment(
  transactionId,
  buyerWalletAddress,
  paymentAmount
) {
  try {
    // 1. Get contract instances
    const factory = await getTradeAgreementContract();
    
    // 2. Get transaction details to verify payment
    const txDetails = await factory.getTransactionSummary(transactionId);
    const paymentMethod = txDetails[4]; // PaymentMethod
    
    // 3. Verify payment method is on-chain
    if (paymentMethod < 1 || paymentMethod > 4 || paymentMethod === 2) {
      throw new Error("Invalid payment method for on-chain payment");
    }
    
    // 4. Process the payment
    const tx = await factory.depositPayment(transactionId, {
      from: buyerWalletAddress,
      value: paymentAmount
    });
    
    await tx.wait();
    
    // 5. Get the Knowledge Asset UAL
    const kaRegistry = await getKnowledgeAssetRegistry();
    const knowledgeAssetUAL = await kaRegistry.getKnowledgeAssetForTransaction(transactionId);
    
    // 6. Update the Knowledge Asset with payment information
    await updateKnowledgeAssetWithPayment(
      knowledgeAssetUAL,
      paymentAmount,
      "on-chain"
    );
    
    return {
      transactionId,
      paymentAmount,
      paymentMethod: "on-chain",
      success: true
    };
  } catch (error) {
    console.error("Error processing on-chain payment:", error);
    return {
      error: error.message,
      success: false
    };
  }
}

// Confirm off-chain payment (CAD or LC)
async function confirmOffChainPayment(
  transactionId,
  paymentAmount,
  paymentReference
) {
  try {
    // 1. Get contract instances
    const factory = await getTradeAgreementContract();
    
    // 2. Get transaction details to verify payment method
    const txDetails = await factory.getTransactionSummary(transactionId);
    const paymentMethod = txDetails[4]; // PaymentMethod
    
    // 3. Verify payment method is off-chain
    if (paymentMethod !== 0 && paymentMethod !== 2) {
      throw new Error("Invalid payment method for off-chain confirmation");
    }
    
    // 4. Confirm the off-chain payment
    const tx = await factory.confirmOffChainPayment(transactionId);
    await tx.wait();
    
    // 5. Get the Knowledge Asset UAL
    const kaRegistry = await getKnowledgeAssetRegistry();
    const knowledgeAssetUAL = await kaRegistry.getKnowledgeAssetForTransaction(transactionId);
    
    // 6. Update the Knowledge Asset with payment information
    await updateKnowledgeAssetWithPayment(
      knowledgeAssetUAL,
      paymentAmount,
      paymentMethod === 0 ? "CAD" : "LC",
      paymentReference
    );
    
    return {
      transactionId,
      paymentAmount,
      paymentMethod: paymentMethod === 0 ? "CAD" : "LC",
      paymentReference,
      success: true
    };
  } catch (error) {
    console.error("Error confirming off-chain payment:", error);
    return {
      error: error.message,
      success: false
    };
  }
}

Token Release API

// Release tokens to financiers
async function releaseTokensToFinanciers(
  transactionId
) {
  try {
    // 1. Get contract instances
    const factory = await getTradeAgreementContract();
    
    // 2. Release the tokens
    const tx = await factory.releaseTokens(transactionId);
    await tx.wait();
    
    // 3. Get the Knowledge Asset UAL
    const kaRegistry = await getKnowledgeAssetRegistry();
    const knowledgeAssetUAL = await kaRegistry.getKnowledgeAssetForTransaction(transactionId);
    
    // 4. Update the Knowledge Asset with token release information
    await updateKnowledgeAssetWithTokenRelease(
      knowledgeAssetUAL,
      transactionId
    );
    
    return {
      transactionId,
      success: true
    };
  } catch (error) {
    console.error("Error releasing tokens:", error);
    return {
      error: error.message,
      success: false
    };
  }
}

Transaction Completion API

// Complete a transaction
async function completeTransaction(
  transactionId
) {
  try {
    // 1. Get contract instances
    const factory = await getTradeAgreementContract();
    
    // 2. Complete the transaction
    const tx = await factory.completeTransaction(transactionId);
    await tx.wait();
    
    // 3. Get the Knowledge Asset UAL
    const kaRegistry = await getKnowledgeAssetRegistry();
    const knowledgeAssetUAL = await kaRegistry.getKnowledgeAssetForTransaction(transactionId);
    
    // 4. Update the Knowledge Asset with completion information
    await updateKnowledgeAssetWithCompletion(
      knowledgeAssetUAL,
      transactionId
    );
    
    return {
      transactionId,
      completedAt: Date.now(),
      success: true
    };
  } catch (error) {
    console.error("Error completing transaction:", error);
    return {
      error: error.message,
      success: false
    };
  }
}

// Handle transaction disputes
async function raiseDispute(
  transactionId,
  reason,
  disputeRaiserAddress
) {
  try {
    // 1. Get contract instances
    const factory = await getTradeAgreementContract();
    
    // 2. Raise the dispute
    const tx = await factory.raiseDispute(transactionId, reason, {
      from: disputeRaiserAddress
    });
    await tx.wait();
    
    // 3. Get the Knowledge Asset UAL
    const kaRegistry = await getKnowledgeAssetRegistry();
    const knowledgeAssetUAL = await kaRegistry.getKnowledgeAssetForTransaction(transactionId);
    
    // 4. Update the Knowledge Asset with dispute information
    await updateKnowledgeAssetWithDispute(
      knowledgeAssetUAL,
      transactionId,
      reason,
      disputeRaiserAddress
    );
    
    return {
      transactionId,
      reason,
      disputeRaiser: disputeRaiserAddress,
      disputedAt: Date.now(),
      success: true
    };
  } catch (error) {
    console.error("Error raising dispute:", error);
    return {
      error: error.message,
      success: false
    };
  }
}

// Cancel a transaction
async function cancelTransaction(
  transactionId
) {
  try {
    // 1. Get contract instances
    const factory = await getTradeAgreementContract();
    
    // 2. Cancel the transaction
    const tx = await factory.cancelTransaction(transactionId);
    await tx.wait();
    
    // 3. Get the Knowledge Asset UAL
    const kaRegistry = await getKnowledgeAssetRegistry();
    const knowledgeAssetUAL = await kaRegistry.getKnowledgeAssetForTransaction(transactionId);
    
    // 4. Update the Knowledge Asset with cancellation information
    await updateKnowledgeAssetWithCancellation(
      knowledgeAssetUAL,
      transactionId
    );
    
    return {
      transactionId,
      cancelledAt: Date.now(),
      success: true
    };
  } catch (error) {
    console.error("Error cancelling transaction:", error);
    return {
      error: error.message,
      success: false
    };
  }
}

DKG Integration

The integration with the Decentralized Knowledge Graph (DKG) is achieved through the Knowledge Asset Registry and API functions that update Knowledge Assets based on blockchain events.

Bidirectional Flow Between DKG and Blockchain

The integration follows a bidirectional flow as outlined in the system architecture:

  1. DKG → Blockchain Flow:
    • Edge Node processes document uploads and verifications
    • When document status changes (e.g., verification completed) in DKG, the blockchain is updated
    • Smart contracts reflect the verification status but do not perform verification
  2. Blockchain → DKG Flow:
    • Smart contracts emit events for state changes (e.g., payments)
    • Web application listens for these events and updates DKG Knowledge Assets
    • The DKG always maintains the latest state synchronized with blockchain events

Knowledge Asset Update Functions

// Update Knowledge Asset with transaction ID
async function updateKnowledgeAssetWithTransactionId(
  knowledgeAssetUAL,
  transactionId
) {
  try {
    // 1. Get the Knowledge Asset from the DKG
    const knowledgeAsset = await fetchKnowledgeAsset(knowledgeAssetUAL);
    
    // 2. Update the Knowledge Asset with transaction information
    knowledgeAsset.blockchainMetadata = {
      ...knowledgeAsset.blockchainMetadata,
      transactionId: transactionId,
      status: "Created",
      createdAt: Date.now()
    };
    
    // 3. Save the updated Knowledge Asset
    await saveKnowledgeAsset(knowledgeAsset);
    
    // 4. Record the update in the blockchain
    const kaRegistry = await getKnowledgeAssetRegistry();
    await kaRegistry.recordKnowledgeAssetUpdate(
      knowledgeAssetUAL,
      "TransactionCreated",
      JSON.stringify({ transactionId })
    );
    
    return true;
  } catch (error) {
    console.error("Error updating Knowledge Asset:", error);
    return false;
  }
}

// Update Knowledge Asset with document verification from DKG
async function updateKnowledgeAssetWithVerificationDetails(
  knowledgeAssetUAL,
  documentType,
  verificationResult,
  verifierDetails
) {
  try {
    // 1. Get the Knowledge Asset from the DKG
    const knowledgeAsset = await fetchKnowledgeAsset(knowledgeAssetUAL);
    
    // 2. Find the document in the Knowledge Asset
    const documentIndex = knowledgeAsset.documents.findIndex(doc => doc.type === documentType);
    
    if (documentIndex >= 0) {
      // 3. Update the document verification status
      knowledgeAsset.documents[documentIndex].verificationStatus = 'VERIFIED';
      knowledgeAsset.documents[documentIndex].verificationResult = verificationResult;
      knowledgeAsset.documents[documentIndex].verifierDetails = verifierDetails;
      knowledgeAsset.documents[documentIndex].verifiedAt = Date.now();
    }
    
    // 4. Save the updated Knowledge Asset
    await saveKnowledgeAsset(knowledgeAsset);
    
    return true;
  } catch (error) {
    console.error("Error updating Knowledge Asset with verification:", error);
    return false;
  }
}

// Other Knowledge Asset update functions follow similar patterns
// These functions ensure that the DKG Knowledge Assets reflect
// the current state of blockchain transactions, providing a
// complete solution that combines on-chain enforcement with
// off-chain data and document management

Blockchain Event Listening

// Listen for blockchain events and update DKG
function listenForBlockchainEvents() {
  // Get contract instances
  const factory = getTradeAgreementContract();
  
  // Listen for document verification events
  factory.on("DocumentsVerified", async (transactionId) => {
    // 1. Get the Knowledge Asset UAL
    const kaRegistry = await getKnowledgeAssetRegistry();
    const knowledgeAssetUAL = await kaRegistry.getKnowledgeAssetForTransaction(transactionId);
    
    // 2. Update the Knowledge Asset with verification status
    await updateKnowledgeAssetWithAllDocumentsVerified(knowledgeAssetUAL);
  });
  
  // Listen for payment events
  factory.on("PaymentConfirmed", async (transactionId, amount) => {
    // 1. Get the Knowledge Asset UAL
    const kaRegistry = await getKnowledgeAssetRegistry();
    const knowledgeAssetUAL = await kaRegistry.getKnowledgeAssetForTransaction(transactionId);
    
    // 2. Update the Knowledge Asset with payment information
    await updateKnowledgeAssetWithPayment(
      knowledgeAssetUAL,
      amount,
      "CONFIRMED"
    );
  });
  
  // Add more event listeners as needed
}

Conclusion

The SKYOCEAN smart contract architecture provides a scalable, cost-effective solution for managing trade agreements, document verification, financier participation, and transaction execution. By using a reusable contract design rather than deploying new contracts for each transaction, the platform dramatically reduces gas costs while maintaining the security and immutability benefits of blockchain technology.

The integration between smart contracts and the DKG Knowledge Assets creates a powerful system where:

  1. Smart contracts handle the enforcement of transaction rules and financial flows
  2. Knowledge Assets store the rich, detailed data associated with each transaction, including document verification
  3. The Knowledge Asset Registry provides the link between on-chain and off-chain components
  4. API functions enable seamless interaction with both systems

Key points to understand about this architecture:

  1. Document verification happens in the DKG system, not in the smart contracts
  2. Smart contracts record the verification status after being notified of changes in the DKG
  3. This bidirectional flow ensures that both systems remain synchronized
  4. The Web Application orchestrates the communication between DKG and blockchain

This architecture enables the SKYOCEAN platform to handle complex trade financing scenarios while providing transparency, security, and efficiency to all participants in the ecosystem.

Note on Solidity Syntax Highlighting:
If Solidity code blocks appear as plain text, ensure your markdown renderer is configured to highlight Solidity. For example, in Jekyll, verify that your _config.yml is set up to use Rouge (or PrismJS with a solidity plugin) for syntax highlighting.