> For the complete documentation index, see [llms.txt](https://docs.konnex.world/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.konnex.world/understand-konnex/stablecoins-integration.md).

# Stablecoins integration (mainnet design)

Konnex is a robot‑native network where every task, escrow, penalty, and reward settles in stablecoins by default. Stablecoins provide a predictable payment rail for machine commerce, while KNX remains the network token for validator security, governance, and fees.

## Stablecoin‑Native Flow (overview)

![Stablecoin settlement flow](/files/YYo9amfceFHHjMnODARc)

The flow shows:

* Payer tops up (optionally via card on‑/off‑ramp) and locks stablecoins into the task escrow.
* Executor submits PoPW evidence to Validators; a ScoreRoot is posted.
* Escrow settles: payouts or refunds (and penalties) are released; a small fee can be routed to a treasury/buyback in KNX.

## Role Split: Stablecoins vs KNX

* Stablecoins — settlement currency for escrows, payouts, penalties; supports fiat on/off‑ramps and cross‑chain liquidity.
* KNX — staking, governance, and network fees; a small fee from stablecoin flows is auto‑swapped to KNX (buyback/treasury).

## Contract Settlement Types (Stablecoin‑Native)

* Fixed‑price — escrow a single stablecoin amount and release on success.
* Metered — usage‑based debits (seconds, meters, frames) from escrow.
* Milestone — staged stablecoin releases across checkpoints.

Minimum task fields: `rewardStable`, `stakeStable`, `deadline`, `penaltyStable`, `PoPW` requirement bound to JobID.

## PoPW‑Linked Settlement & Penalties

1. Lock — payer funds on‑chain stablecoin escrow.
2. Perform — executor records sensor evidence (GPS, camera, IMU, torque, temperature).
3. Prove — submit PoPW bundle referencing JobID and deadline.
4. Verify — validators publish a ScoreRoot (pass/fail + metrics).
5. Settle — on pass, release stablecoins; on fail, apply `penaltyStable` and refunds (fees in KNX).

## Multi‑Token Staking & Robotic Bonds

* KNX stake — aligns executors with validator security and enables slashing.
* Stablecoin bond — compensates counterparties for non‑performance.

Third‑party stakers can supply stablecoins to robotic bond pools and earn yield, with risk priced by reputation. Required stablecoin stake roughly scales as `S = kappa / sqrt(T)` where `T` is trust score.

## Cross‑Chain Liquidity

Stablecoins from other chains can be bridged into Konnex via approved routes. Tasks are chain‑agnostic to source funds.

## Card On/Off‑Ramp

1. Charge — pay with card in app.
2. Mint — processor converts fiat to stablecoins to the payer’s Konnex address.
3. Escrow — stablecoins locked in the task contract.
4. Payout — after PoPW verification, stablecoins are released.
5. Redeem/Spend — spend via card, redeem to bank, or reuse on‑chain.

## Compliance by Design

* Optional KYC/AML gates (market/DAO configurable).
* Full audit trail of packets, PoPW, and stablecoin flows.

## Developer Surface (API Sketch)

* `lock(jobId, rewardStable, stakeStable)`
* `prove(jobId, PoPWBundleRef)`
* `settle(jobId)`
* `bond(jobId, amountStable)`

## Economics & Fee Flywheel

* Stablecoins for users, KNX for the network.
* Auto‑buyback: small fee on stablecoin flows swaps to KNX for treasury/burn.

## Key Metrics

* Latency: prove → stablecoin release (target single‑block after validation).
* Disputes: <1% and auto‑resolved.
* Velocity: avg stablecoin tx/robot/day.
* Card share: tasks funded via on‑ramp.
* Collateral efficiency: required `stakeStable` falls as trust `T` rises.

## Solidity Contracts

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

interface IScoreRegistry {
    // Returns (pass, score, scoreRoot) for a given jobId
    function getVerificationResult(bytes32 jobId) external view returns (bool, uint256, bytes32);
}

interface IAccessController {
    function isAllowed(address user) external view returns (bool);
}

interface IFeeCollector {
    // Stablecoin fee receiver used for KNX buyback/treasury flows
    function receiveStableFee(address token, uint256 amount) external;
}

contract FeeCollector is IFeeCollector, Ownable {
    using SafeERC20 for IERC20;

    event StableFeeReceived(address indexed token, uint256 amount);
    event Sweep(address indexed token, address indexed to, uint256 amount);

    // Receives fees in stablecoin; swapping to KNX is implemented off this contract (routers/aggregators vary by deployment)
    function receiveStableFee(address token, uint256 amount) external override {
        IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
        emit StableFeeReceived(token, amount);
    }

    // Owner can sweep accumulated balances to treasury/buyback executor as needed
    function sweep(address token, address to, uint256 amount) external onlyOwner {
        IERC20(token).safeTransfer(to, amount);
        emit Sweep(token, to, amount);
    }
}

contract KonnexSettlementManager is ReentrancyGuard, Ownable {
    using SafeERC20 for IERC20;

    enum SettlementType {
        FixedPrice,
        Metered
        // Milestone can be added with indexed amounts per checkpoint
    }

    enum JobStatus {
        None,
        Locked,
        Proven,
        Settled
    }

    struct Job {
        // Core IDs and actors
        bytes32 jobId;
        address payer;
        address executor;
        // Economic params (stablecoin‑native)
        uint256 rewardStable;      // gross reward escrowed by payer (stablecoins)
        uint256 stakeStable;       // executor stake/bond amount in stablecoins (can come from third‑party stakers)
        uint256 penaltyStable;     // penalty applied on fail, funded from stake/bonds
        uint256 deadline;          // unix deadline for proof submission
        SettlementType settlementType;
        // Lifecycle
        JobStatus status;
        bytes32 proofRef;          // e.g., PoPW CID/reference
        // Metered: amount debited from escrow over time (<= rewardStable)
        uint256 meteredDebited;
        // Accounting
        uint256 totalThirdPartyBond; // total bonded by third‑party stakers for this job
    }

    struct BondStake {
        uint256 amount;
        bool claimed;
    }

    IERC20 public immutable stableToken;  // primary settlement token (e.g., USDC)
    IScoreRegistry public scoreRegistry;  // validator score/decision source
    IAccessController public accessGate;  // optional KYC/ACL gate (can be address(0))
    IFeeCollector public feeCollector;    // receives stable fees for KNX buyback/treasury

    // Fees (basis points) taken from executor payout on pass; sent in stable to FeeCollector
    uint16 public stableFeeBps = 50; // 0.50%
    uint16 public bondYieldBps = 25; // 0.25% yield to third‑party bond providers on pass (taken from reward escrow, before executor payout)
    uint16 private constant BPS_DENOM = 10_000;

    // jobId => Job
    mapping(bytes32 => Job) public jobs;

    // jobId => list of bond stakers (to enable distribution on settlement)
    mapping(bytes32 => address[]) private bondStakers;
    // jobId => staker => BondStake
    mapping(bytes32 => mapping(address => BondStake)) public bondLedger;

    // Events
    event Locked(bytes32 indexed jobId, address indexed payer, address indexed executor, SettlementType stype, uint256 reward, uint256 stake, uint256 penalty, uint256 deadline);
    event Bonded(bytes32 indexed jobId, address indexed staker, uint256 amount);
    event Proved(bytes32 indexed jobId, address indexed executor, bytes32 proofRef);
    event MeteredDebit(bytes32 indexed jobId, uint256 amount, uint256 totalDebited);
    event Settled(bytes32 indexed jobId, bool pass, uint256 executorPayout, uint256 penaltyPaid, uint256 feePaid, uint256 bondYieldPaid);
    event AccessGateUpdated(address indexed gate);
    event ScoreRegistryUpdated(address indexed registry);
    event FeeCollectorUpdated(address indexed collector);
    event FeesUpdated(uint16 stableFeeBps, uint16 bondYieldBps);

    constructor(
        address stableToken_,
        address scoreRegistry_,
        address feeCollector_
    ) {
        require(stableToken_ != address(0) && scoreRegistry_ != address(0) && feeCollector_ != address(0), "zero addr");
        stableToken = IERC20(stableToken_);
        scoreRegistry = IScoreRegistry(scoreRegistry_);
        feeCollector = IFeeCollector(feeCollector_);
    }

    // Optional: enable/disable KYC/ACL markets
    function setAccessGate(address gate) external onlyOwner {
        accessGate = IAccessController(gate);
        emit AccessGateUpdated(gate);
    }

    function setScoreRegistry(address reg) external onlyOwner {
        require(reg != address(0), "zero addr");
        scoreRegistry = IScoreRegistry(reg);
        emit ScoreRegistryUpdated(reg);
    }

    function setFeeCollector(address collector) external onlyOwner {
        require(collector != address(0), "zero addr");
        feeCollector = IFeeCollector(collector);
        emit FeeCollectorUpdated(collector);
    }

    function setFees(uint16 stableFeeBps_, uint16 bondYieldBps_) external onlyOwner {
        require(stableFeeBps_ <= 500 && bondYieldBps_ <= 1000, "fee caps"); // sane caps
        stableFeeBps = stableFeeBps_;
        bondYieldBps = bondYieldBps_;
        emit FeesUpdated(stableFeeBps_, bondYieldBps_);
    }

    // lock: payer escrows reward; executor escrows stake (directly or via third‑party bonds)
    function lock(
        bytes32 jobId,
        address executor,
        uint256 rewardStable,
        uint256 stakeStable,
        uint256 penaltyStable,
        uint256 deadline,
        SettlementType stype
    ) external nonReentrant {
        require(jobs[jobId].status == JobStatus.None, "exists");
        require(executor != address(0) && rewardStable > 0 && deadline > block.timestamp, "invalid");
        _checkAccess(msg.sender);
        _checkAccess(executor);

        // Create job
        Job storage j = jobs[jobId];
        j.jobId = jobId;
        j.payer = msg.sender;
        j.executor = executor;
        j.rewardStable = rewardStable;
        j.stakeStable = stakeStable;
        j.penaltyStable = penaltyStable;
        j.deadline = deadline;
        j.settlementType = stype;
        j.status = JobStatus.Locked;

        // Transfer reward escrow from payer to contract
        stableToken.safeTransferFrom(msg.sender, address(this), rewardStable);

        // If executor supplies stake directly (not via bonds), transfer from executor now
        if (stakeStable > 0) {
            stableToken.safeTransferFrom(executor, address(this), stakeStable);
        }

        emit Locked(jobId, msg.sender, executor, stype, rewardStable, stakeStable, penaltyStable, deadline);
    }

    // Third‑party bond providers can contribute to executor's stake (robotic bonds)
    function bond(bytes32 jobId, uint256 amount) external nonReentrant {
        Job storage j = jobs[jobId];
        require(j.status == JobStatus.Locked, "job not lock");
        require(amount > 0, "zero");
        _checkAccess(msg.sender);

        // Pull funds
        stableToken.safeTransferFrom(msg.sender, address(this), amount);

        // Track staker and amount
        if (bondLedger[jobId][msg.sender].amount == 0) {
            bondStakers[jobId].push(msg.sender);
        }
        bondLedger[jobId][msg.sender].amount += amount;
        j.totalThirdPartyBond += amount;

        emit Bonded(jobId, msg.sender, amount);
    }

    // Metered debit during execution (e.g., usage‐based spending from payer escrow)
    function debitMetered(bytes32 jobId, uint256 amount) external nonReentrant {
        Job storage j = jobs[jobId];
        require(j.status == JobStatus.Locked || j.status == JobStatus.Proven, "bad status");
        require(j.settlementType == SettlementType.Metered, "not metered");
        require(msg.sender == j.payer, "only payer");
        require(j.meteredDebited + amount <= j.rewardStable, "exceeds escrow");
        j.meteredDebited += amount;
        emit MeteredDebit(jobId, amount, j.meteredDebited);
    }

    // prove: executor submits PoPW reference before deadline
    function prove(bytes32 jobId, bytes32 proofRef) external nonReentrant {
        Job storage j = jobs[jobId];
        require(j.status == JobStatus.Locked || j.status == JobStatus.Proven, "bad status");
        require(msg.sender == j.executor, "only exec");
        require(block.timestamp <= j.deadline, "deadline");
        j.proofRef = proofRef;
        j.status = JobStatus.Proven;
        emit Proved(jobId, msg.sender, proofRef);
    }

    // settle: checks validator decision and routes funds accordingly
    function settle(bytes32 jobId) external nonReentrant {
        Job storage j = jobs[jobId];
        require(j.status == JobStatus.Locked || j.status == JobStatus.Proven, "bad status");

        (bool pass,,) = scoreRegistry.getVerificationResult(jobId);

        uint256 totalStake = j.stakeStable + j.totalThirdPartyBond;
        uint256 executorPayout = 0;
        uint256 penaltyPaid = 0;
        uint256 feePaid = 0;
        uint256 bondYieldPaid = 0;

        if (pass) {
            // Calculate gross payout from reward escrow (metered tasks pay out only the debited portion)
            uint256 gross = (j.settlementType == SettlementType.Metered) ? j.meteredDebited : j.rewardStable;

            // Bond yield carved out from gross reward for stakers
            uint256 yieldForBonds = (j.totalThirdPartyBond > 0) ? (gross * bondYieldBps) / BPS_DENOM : 0;
            bondYieldPaid = yieldForBonds;

            // Fee on executor payout (after bond yield)
            uint256 baseForExec = gross - yieldForBonds;
            feePaid = (baseForExec * stableFeeBps) / BPS_DENOM;
            executorPayout = baseForExec - feePaid;

            // Distribute: bond yield to stakers pro‑rata, fee to FeeCollector, remainder to executor
            _payBondProRata(jobId, yieldForBonds, true /* include principal on pass */);
            if (feePaid > 0) {
                // Send fee in stablecoin to FeeCollector for KNX buyback/treasury
                stableToken.safeApprove(address(feeCollector), 0);
                stableToken.safeApprove(address(feeCollector), feePaid);
                feeCollector.receiveStableFee(address(stableToken), feePaid);
            }
            if (executorPayout > 0) {
                stableToken.safeTransfer(j.executor, executorPayout);
            }

            // Return executor's direct stake (if any)
            if (j.stakeStable > 0) {
                stableToken.safeTransfer(j.executor, j.stakeStable);
            }

            // Refund any unspent escrow to payer (metered remainder or dust)
            uint256 spent = gross;
            if (j.rewardStable > spent) {
                stableToken.safeTransfer(j.payer, j.rewardStable - spent);
            }
        } else {
            // Fail: apply penalty from total stake (executor stake + third‑party bonds), pay to payer
            uint256 slashed = j.penaltyStable > totalStake ? totalStake : j.penaltyStable;
            penaltyPaid = slashed;

            if (slashed > 0) {
                // Slash executor's direct stake first, then third‑party bonds
                uint256 fromExecStake = slashed > j.stakeStable ? j.stakeStable : slashed;
                if (fromExecStake > 0) {
                    j.stakeStable -= fromExecStake;
                    stableToken.safeTransfer(j.payer, fromExecStake);
                    slashed -= fromExecStake;
                }
                if (slashed > 0 && j.totalThirdPartyBond > 0) {
                    _slashBondProRata(jobId, slashed);
                    stableToken.safeTransfer(j.payer, slashed);
                }
            }

            // Refund full reward escrow to payer
            if (j.rewardStable > 0) {
                stableToken.safeTransfer(j.payer, j.rewardStable);
            }

            // Return any remaining direct stake to executor
            if (j.stakeStable > 0) {
                stableToken.safeTransfer(j.executor, j.stakeStable);
            }

            // Return remaining bonds (principal only, no yield) to stakers
            _payBondProRata(jobId, 0, true /* include principal */);
        }

        j.status = JobStatus.Settled;
        emit Settled(jobId, pass, executorPayout, penaltyPaid, feePaid, bondYieldPaid);

        // Zero out escrowed amounts to avoid double‑spends in any edge path
        j.rewardStable = 0;
        j.stakeStable = 0;
        j.totalThirdPartyBond = 0;
    }

    // Internal helpers

    function _checkAccess(address user) internal view {
        if (address(accessGate) != address(0)) {
            require(accessGate.isAllowed(user), "acl");
        }
    }

    function _payBondProRata(bytes32 jobId, uint256 yieldAmount, bool includePrincipal) internal {
        address[] memory stakers = bondStakers[jobId];
        if (stakers.length == 0) {
            return;
        }

        // Compute total principal remaining for this job
        uint256 totalPrincipal = 0;
        for (uint256 i = 0; i < stakers.length; i++) {
            totalPrincipal += bondLedger[jobId][stakers[i]].amount;
        }
        if (totalPrincipal == 0 && yieldAmount == 0) {
            return;
        }

        for (uint256 i = 0; i < stakers.length; i++) {
            address staker = stakers[i];
            BondStake storage b = bondLedger[jobId][staker];
            if (b.claimed || b.amount == 0) continue;

            uint256 principalShare = b.amount;
            uint256 yieldShare = (yieldAmount > 0 && totalPrincipal > 0) ? (yieldAmount * principalShare) / totalPrincipal : 0;

            uint256 payout = yieldShare + (includePrincipal ? principalShare : 0);
            if (payout > 0) {
                // Zero out before transfer to guard reentrancy
                b.claimed = true;
                b.amount = 0;
                stableToken.safeTransfer(staker, payout);
            }
        }
        // Clear staker list to free storage in future deployments (optional micro‑opt)
        delete bondStakers[jobId];
    }

    function _slashBondProRata(bytes32 jobId, uint256 slashAmount) internal {
        address[] memory stakers = bondStakers[jobId];
        if (stakers.length == 0 || slashAmount == 0) {
            return;
        }

        uint256 totalPrincipal = 0;
        for (uint256 i = 0; i < stakers.length; i++) {
            totalPrincipal += bondLedger[jobId][stakers[i]].amount;
        }
        if (totalPrincipal == 0) return;

        for (uint256 i = 0; i < stakers.length; i++) {
            address staker = stakers[i];
            BondStake storage b = bondLedger[jobId][staker];
            if (b.amount == 0) continue;
            uint256 slashShare = (slashAmount * b.amount) / totalPrincipal;
            if (slashShare > b.amount) slashShare = b.amount;
            b.amount -= slashShare;
        }
    }
}
```

### Notes

* The `KonnexSettlementManager` implements:
  * `lock(jobId, executor, rewardStable, stakeStable, penaltyStable, deadline, stype)` — payer escrows reward; executor may escrow stake directly; third‑party stakers add stake via `bond`.
  * `bond(jobId, amount)` — third‑party stakers add stablecoin bonds that secure execution and earn yield on pass.
  * `prove(jobId, proofRef)` — executor posts a PoPW bundle reference before `deadline`.
  * `debitMetered(jobId, amount)` — optional usage‑based debits for metered contracts.
  * `settle(jobId)` — reads validator decision from `IScoreRegistry`, applies penalties or releases funds, sends a stablecoin fee to `FeeCollector` for KNX buyback/treasury.
* The `FeeCollector` is a simple stablecoin sink. Teams typically wire this to a swap/buyback executor off‑chain or via per‑chain router adapters.
* To add milestones, track `uint256[] milestoneAmounts` and a checkpoint index per job, and debit per milestone similarly to `debitMetered`.
