> For the complete documentation index, see [llms.txt](https://docs.arcv.network/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.arcv.network/4.-the-4-stage-verification-engine/4.4-settlement-and-disbursement.md).

# 4.4 Cryptographic Settlement

Stage 4 freezes accepted records, establishes an archival commitment, registers provenance, and reconciles native-USDC payment. It coordinates two independent systems: permanent storage and Arc transaction execution. An upload receipt does not transfer contributor funds, and an Arc payout does not prove that remote content is retrievable or correct.

**Implementation status:** `ARCVDataBountyRegistry.sol` implements funded campaigns, single-payable-record registration, persistent identifier reservations, and role-authorized native-value payouts. Production archival coordination, Merkle-proof verification, batch payment distribution, and treasury fee routing are not implemented in that contract. The following design preserves those boundaries instead of presenting planned features as deployed guarantees.

## Acceptance, archival, and payment state machine

```
Final accepted record + authenticated Stage 3 receipt
                           |
             freeze ordering / encoding / access policy
                           |
           canonical records -> Merkle root -> batch manifest
                           |
             serialize -> compress/encrypt as authorized
                           |
                 hash exact stored artifact bytes
                           |
             upload through Arweave-settling integration
                           |
          verify identifier, inclusion, and retrieved digest
                           |
      BUNDLER_ROLE: submitDataset(bountyId, hash, txId, contributor)
                           |
                        Pending
                       /       \
          rejectDataset         validateAndDisburse
                 |                    |
              Rejected          transaction receipt
                                      |
                        status + event + state reconciliation
                                      |
                              finality policy satisfied
                                      v
                                     Paid
```

An upload may succeed while registration fails. A registration may succeed while the coordinator loses its RPC response. A recipient can reject a transfer. The service therefore needs a durable operation journal and a reconciler, not an assumption that every stage succeeds atomically.

Assign a stable operation identity before upload. Record intended artifact digest, assignment, recipient, registry, and chain. Recovery must inspect existing archive and chain evidence before creating a second artifact or signing a replacement operation.

## Canonical record and batch construction

Only final sanitized versions enter the archive set. A record commits to the task version, response ordering, outcome, ratings, rationale, optional patch, and approved non-sensitive provenance references. It must not include provider API credentials, raw behavioral traces, hidden benchmark answers, or unredacted intake bodies.

Declare the serialization profile and sort records by a unique stable record identifier using a specified byte order. Reject duplicate identifiers. Specify UTF-8 encoding, JSON primitive rules, and line termination. A JSONL profile can use one canonical JSON object followed by one LF byte per record; the newline contributes to the file digest even when excluded from the individual record's leaf encoding.

Use independently named commitments:

| Commitment              | Bytes or structure covered              | Purpose                                   |
| ----------------------- | --------------------------------------- | ----------------------------------------- |
| Record SHA-256          | One canonical accepted record           | Version identity                          |
| Merkle root             | Ordered canonical record leaves         | Inclusion proof                           |
| Plaintext batch digest  | Exact serialized dataset bytes          | Decoded-content verification              |
| Stored artifact SHA-256 | Exact compressed/encrypted upload bytes | Gateway integrity verification            |
| Manifest digest         | Exact manifest bytes                    | Bind file hashes, formats, and provenance |

Compression changes bytes. Gzip headers can contain timestamps or filenames; pin deterministic settings where reproducibility is needed. Encryption also changes bytes and may deliberately use randomized nonces. Freeze and hash the actual encrypted output; never reuse an AES-GCM nonce to make ciphertext deterministic.

A manifest should declare encoding and compression, record count, uncompressed size, stored size, shard digests, Merkle algorithm/version, leaf count, and access policy. Do not place a file's own hash inside the same byte sequence being hashed. A content artifact can be hashed first and referenced by a separately hashed manifest.

## Merkle tree algorithm and inclusion proofs

The following proposed SHA-256 profile separates leaves from internal nodes:

```
leaf_i = SHA-256(0x00 || canonical_record_bytes_i)
node   = SHA-256(0x01 || left_child_32_bytes || right_child_32_bytes)
root   = sole remaining node
```

At an odd-width level, duplicate the final node. Reject an empty batch. For one record, the root is its leaf hash. Bind the leaf count and algorithm version in the manifest: duplicate-last padding can otherwise make different logical tree sizes ambiguous. Do not treat this root as interchangeable with an OpenZeppelin sorted-pair or Keccak tree.

The complete reference below calculates roots and ordered proofs. Proof consumers must obtain a trusted root and count from an authenticated manifest and verify its binding to registry evidence. Verifying against an attacker-supplied root proves nothing about the registry.

```python
import hashlib

def sha256(data: bytes) -> bytes:
    return hashlib.sha256(data).digest()

def leaves(records: list[bytes]) -> list[bytes]:
    if not records or any(not isinstance(r, bytes) for r in records):
        raise ValueError("A nonempty list of byte records is required")
    return [sha256(b"\x00" + r) for r in records]

def parent(left: bytes, right: bytes) -> bytes:
    return sha256(b"\x01" + left + right)

def root(records: list[bytes]) -> bytes:
    level = leaves(records)
    while len(level) > 1:
        if len(level) % 2:
            level.append(level[-1])
        level = [
            parent(level[i], level[i + 1])
            for i in range(0, len(level), 2)
        ]
    return level[0]

def proof(records: list[bytes], index: int) -> list[bytes]:
    level = leaves(records)
    if not 0 <= index < len(level):
        raise IndexError("Leaf index out of range")
    siblings = []
    while len(level) > 1:
        if len(level) % 2:
            level.append(level[-1])
        siblings.append(level[index ^ 1])
        level = [
            parent(level[i], level[i + 1])
            for i in range(0, len(level), 2)
        ]
        index //= 2
    return siblings

def verify(
    record: bytes,
    index: int,
    count: int,
    siblings: list[bytes],
    expected_root: bytes,
) -> bool:
    if count < 1 or not 0 <= index < count or len(expected_root) != 32:
        return False
    value = sha256(b"\x00" + record)
    width = count
    for sibling in siblings:
        if width <= 1 or len(sibling) != 32:
            return False
        if index % 2 == 0:
            if index + 1 >= width and sibling != value:
                return False
            value = parent(value, sibling)
        else:
            value = parent(sibling, value)
        index //= 2
        width = (width + 1) // 2
    return width == 1 and value == expected_root

if __name__ == "__main__":
    records = [b'{"id":1}', b'{"id":2}', b'{"id":3}']
    expected = root(records)
    for index, record in enumerate(records):
        siblings = proof(records, index)
        assert verify(record, index, len(records), siblings, expected)
        assert not verify(record + b" ", index, len(records), siblings, expected)
    print(expected.hex())
```

An inclusion proof establishes membership of specific bytes in the committed tree. It does not prove their factual correctness, license, or payment. The current registry has no Merkle-root field or proof verifier; a manifest may carry a root off-chain, but the contract will not interpret it.

## Archival through Arweave and Irys-related infrastructure

Select an integration explicitly documented to settle the intended artifact on Arweave. A gateway serves retrieval; its hostname does not establish upload settlement. Irys product and network selection must be pinned and verified rather than inferred from the historical association between a bundler and Arweave. Do not substitute another storage network while describing its receipt as an Arweave transaction.

Before upload, record the target network, uploader version, funding identity, content type, compression, digest convention, confidentiality policy, and inclusion-evidence requirements. Restricted datasets need actual encryption and authorized key delivery, not merely an “encrypted” UI badge. Never archive decryption keys with ciphertext.

Persist the returned artifact identifier as arweave\_tx\_id only after resolving its semantics. A bundled data-item identifier may differ from the parent base-layer transaction. Preserve the relationship and verify inclusion using the selected integration's documented evidence. The registry accepts a 43-character base64url-shaped string; that syntactic test does not establish existence or inclusion.

Retrieve the artifact independently, enforce size bounds, and recompute SHA-256 over exact stored bytes. If it is compressed, verify the stored-byte commitment before bounded decompression, then verify declared plaintext digests. A fast hot-cache response does not replace this verification or demonstrate permanent storage.

Arweave's [storage endowment model](https://www.arweave.org/files/arweave-lightpaper.pdf) describes upfront funding based on roughly 200 years of replicated storage at prevailing costs, with assumptions intended to support longer retention. This is an economic durability design, not an unconditional SLA guaranteeing every gateway or dataset for 200+ years. Record inclusion evidence and monitor retrieval health without claiming certainty about centuries of future availability.

## Payable-record granularity

The current contract treats one datasetId as one payable sample, one contributor, and one fixed campaign reward. It does not distribute one batch payment across all workers. Pending registration reserves one funded slot.

Global storage-ID uniqueness also prevents reusing one batch Arweave identifier for many contributor registrations. A compatible integration can archive uniquely identified per-payable-unit envelopes and aggregate references into a training manifest. Such envelopes must actually exist and bind their accepted record and recipient; invented identifiers do not satisfy provenance.

A true shared-batch payout design would require new storage and payment semantics, inclusion-proof handling, and individual claim replay protection. The Merkle profile above describes an archival commitment, not a hidden implementation of that new contract.

## Registration and role-authorized settlement

The bundler calls the existing interface:

```solidity
function submitDataset(
    uint256 bountyId,
    bytes32 dataSha256,
    string calldata arweaveTxId,
    address contributor
) external returns (uint256 datasetId);

function validateAndDisburse(uint256 datasetId) external;

function rejectDataset(
    uint256 datasetId,
    string calldata reason
) external;
```

These are interface signatures; the implementation restricts submission to BUNDLER\_ROLE and both review operations to VALIDATOR\_AGENT\_ROLE. The constructor grants only DEFAULT\_ADMIN\_ROLE. Administrators must explicitly grant operational roles.

Registration verifies campaign existence, a valid nonzero recipient other than the registry itself, a nonzero digest, storage-ID syntax, uniqueness, and capacity. It trusts the authorized bundler's hash and identifier; it does not fetch or hash the payload.

Before settlement, the transaction service verifies chain ID 5042, the intended registry address and deployed code, ABI, role membership, pending status, campaign reward, recipient, and matching acceptance receipt. The validator signs an ordinary EVM transaction. The current function does not verify an EIP-712 contributor signature, judge threshold, or multi-agent quorum.

[Arc's network configuration](https://docs.arc.io/arc/references/connect-to-arc) specifies native USDC with 18 decimals on chain 5042. A reward of 0.25 USDC is therefore 250000000000000000 native base units. Do not use six-decimal ERC-20 accounting for msg.value. The signer separately needs native-USDC gas funding.

## Replay defense and escrow invariants

The implemented public mapping is `usedHashes(bytes32)`. There is no `registeredHashes(bytes32)` getter in the current ABI. Registration sets usedHashes and the Keccak-keyed usedArweaveTxIds reservation before creating a Pending dataset.

A successful payout requires Pending status and makes the following transition, for fixed reward r:

```
status'          = Paid
pendingCount'    = pendingCount - 1
paidCount'       = paidCount + 1
remainingEscrow' = remainingEscrow - r
totalEscrow'     = totalEscrow - r
native transfer = r to registered contributor
```

For a funded campaign with target N and paid count a:

```
0 <= pendingCount + paidCount <= N
remainingEscrow = (N - a) * r
totalEscrow = sum(remainingEscrow across campaigns)
contract native balance >= totalEscrow
```

Unexpected forced native transfers may make balance exceed liabilities; they do not increase campaign allocations. Solidity checked arithmetic, funded-capacity reservation, status gating, and the fixed reward prevent arbitrary over-disbursement under the implemented transitions.

The function updates state before the external native transfer and uses ReentrancyGuard. If the recipient rejects the payment, PayoutFailed reverts the entire transaction and restores accounting. A retry after a successful payout instead fails DatasetNotPending. Status transitions prevent duplicate payment of the same dataset; hash/ID reservations prevent duplicate registration under the same supplied identifiers.

Rejection decrements pendingCount and preserves escrow. It does not refund the sponsor and does not clear identifier reservations. No cancelBounty or expiry refund exists in this implementation. Logical duplicate work with different bytes still requires off-chain assignment-level replay defense.

## Treasury routing: current contract and proposed policy

Current settlement transfers the full reward to the contributor. It makes no treasury transfer, swap, burn, or staking distribution. Funding requires exactly targetCount multiplied by rewardPerSample. Sending an added fee to createBounty fails its exact-value check.

[Protocol Revenue Engine](/6.-protocol-economics-and-tokenomics/6.2-protocol-revenue-engine.md) specifies a proposed 10% gross settlement take-rate. For a future implementation using integer base units, a clearly defined waterfall could be:

```
G = gross settlement amount
F = floor(G * 1000 / 10000)
C = G - F
T = floor(F / 2)
B = F - T
G = C + T + B
```

C is contributor net, T is treasury allocation, and B is buyback allocation. The final remainder goes to B in this example. For G=1 USDC, C=0.90, T=0.05, and B=0.05 USDC. This is a proposed gross-fee model, not permission to reduce a current promised reward. If a campaign advertises a net worker reward, funding and display rules must explicitly preserve it.

A future fee-routing contract must specify recipients, caps, configuration authority, rounding, events, failed-transfer behavior, and liability conservation. A DEX buyback should not be confused with the atomic contributor transfer or allowed to introduce an unspecified swap-failure dependency into payment. The current frontend's illustrative 5% estimate is a separate inconsistency; it is not enforcement of the proposed 10% design.

## Finality, retries, and reconciliation

Record transaction hash, sender nonce, replacement relationships, receipt status, block number/hash, decoded DatasetPaid event, and resulting dataset state. Apply the deployment's documented finality policy rather than inventing a universal confirmation count. A broadcast hash is not settlement success.

| Failure                        | Required coordinator action                                                  |
| ------------------------------ | ---------------------------------------------------------------------------- |
| Upload response lost           | Recover operation/receipt before uploading again                             |
| Retrieved digest differs       | Quarantine and block registration                                            |
| Registration reports duplicate | Reconcile existing record and recipient; do not bypass by arbitrary mutation |
| Capacity exhausted             | Hold work and reconcile funded slots                                         |
| Settlement response lost       | Inspect transaction and dataset state before retry                           |
| Recipient transfer reverts     | Preserve Pending state; investigate without silently changing recipient      |
| Role revoked                   | Stop signing and alert operations                                            |
| Chain evidence changes         | Reconcile affected journal entries under finality policy                     |
| Provider or validator outage   | Preserve durable queue; do not invent a timeout refund                       |

Concurrent transaction workers must coordinate signer nonces. A replacement transaction is part of the original logical operation, not a second payout entitlement. Keep acceptance, archival, and settlement receipts separate but linked so operators can identify precisely which boundary failed.

The resulting provenance proves specified byte relationships and authorized state transitions. Its trust assumptions still include role administration, operator key custody, evaluator policy, and archival evidence verification. Continue to [Contract Architecture](/5.-smart-contracts-and-arc-network-settlement/5.1-contract-architecture.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.arcv.network/4.-the-4-stage-verification-engine/4.4-settlement-and-disbursement.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
