Repository navigation
Describe chain payment modes, profile, validation and enablement - #383
Merged
Merged
Conversation
added 7 commits
October 5, 2026 10:36
A client could only learn a batch's price by creating a payment intent, and could not see which payment features a dispatcher offers or page its own intents. POST /payment/quote prices a batch with the same function the intent uses and stops there: it writes nothing and issues no auth key; a batch the intent would refuse is quoted with every problem on the order it concerns and no total. The intent response carries the same quote of the prices it stored, so a client can compare the two exactly. GET /me reports the payment methods admitted now, whether chain payments are enabled and whether allowances are offered; GET /me/orders pages the account's intents with their orders, settlements and admitted runs. The API contract moves to 1.15 and the Go client gains Quote and Orders. GitHub issues #214 and #215.
…work Compose the corrected receipts and transfers head with the corrected Sui adapter head (prepared transfers with a persisted identity, coin selection without repeated objects) as the base of the payout reservation and refund change, which uses both. GitHub issue #80.
Compose the quotes and economics head with the corrected receipts and transfers head (dispatcher schema 22) as the base of the allowance grants change, which adds schema 23 above it and reports settlements from the ledger both carry. GitHub issue #83.
Dispatcher schema 23 adds immutable allowance grants per account. With allowances enabled, a TEST intent is created by one guarded insert inside the intent transaction when the sum of grants less the price of settled orders (credit or refund alike) and of reserved orders covers the new price; a refusal answers 429 allowance_exceeded and writes nothing, and expired unused intents of the account are released first. Nothing is granted at sign-up and grants do not renew. Add the allowance view, the operator grant route (session only, idempotent by key) and a read-only earnings view for executor owners, with scopes, OpenAPI, documentation and client types. Disabled in every shipped configuration. GitHub issues #83 and #215.
Executor payouts zeroed the balance after the transfer, so earnings credited in between were lost; refunds and payouts held a SQL transaction across the chain call, and a transaction-wide refund wrote order states unconditionally and refunded credited orders. Give payouts and refunds one lifecycle: prepare the transfer (a chain read), commit the local decision together with a reserved chain transfer row carrying the digest, the signed transaction and its signature, broadcast the signed transaction under a two-minute bound, and record the outcome by a conditional write: sent, failed (a failed payout releases its amount in the same write) or unknown. Reconcile sent, unknown and stale reserved rows on the payout loop's ticker with one lookup: confirmed with the expected effects, failed, or resubmitted once from the identical stored bytes when the chain does not know the transaction; anything unverified stays unknown. A payout reserves exactly the balance it read and is refused while another is open; refunds change the transaction status and each outstanding order conditionally, with a refund settlement row, before the chain call. The maintenance pass settles chain refunds of failed runs through the same lifecycle and never selects an order with a refund transfer again. GitHub issues #80 and #81.
The operations page explained the transfer lifecycle and reconciliation but not what each payment method records, which chain profile this build supports, what the dispatcher refuses at startup or how an operator enables chain payments. Add those sections: the records of each method in both modes; the supported profile (Sui Testnet, the testnet USDC coin type to verify against the issuer's published identifier, testnet SUI for gas, no mainnet, no deposits) and the custody of the dispatcher's key by the host administrator through a dedicated deployment key; the startup checks of every [sui] field and of the keystore, and the later checks of the stored cursor and the indexer's coverage; and the enablement checklist, an explicit operator decision that nothing in the repository performs, ending in a rehearsal with one payment and one payout and listing the checks that stay open until a live service confirms them. Document the [sui] keys in the configuration reference and link the page from the documentation index. GitHub issue #87.
The page named the reconciliation interval and the stale bound only in passing and said nothing about restoring a database. Add a schedules list: the daily payout tick at local midnight, reconciliation every minute over at most 100 open transfers with reserved rows stale after ten minutes, the settlement pass every 30 seconds over at most 32 orders, and the two-minute submission bound after which an outcome is unknown. Add a restore caveat under enablement: the backup page covers the TEST state only, a database restored from before its last chain transfer brings back balances and owed refunds that were already paid and nothing in the dispatcher prevents paying them again, so chain payments are switched off and the restored database is reconciled against the address's chain history before the payout or settlement pass runs again. GitHub issue #87.
ctfbruce
marked this pull request as ready for review
October 5, 2026 10:21
added 8 commits
October 5, 2026 13:05
A quote priced an order whose start timestamp was impossible, or whose policy required ICMP from an executor without it, without any error, although the submission refuses both deterministically; the quote's contract is to report every problem the dispatcher would report. Apply the existing start-timestamp validation and the read-only capability check in the pricing path, so that a quote and an intent report these problems alike as per-order field errors; the quote still reserves nothing and the submission still validates again. Correct the Go client's description of Quote: the high-level submission does not expose the intent's quote, so callers compare prices through the quote itself or the raw intent response. GitHub issues #214 and #215.
The remaining allowance was computed by unchecked 64-bit arithmetic over three separately aggregated totals, so an account whose settled TEST orders summed to the 64-bit maximum, with a further order reserved, was shown a wrapped positive balance once allowances were enabled; such history is reachable, since an executor may announce the maximum price and an order priced at it is accepted. Check that each aggregate and the subtraction fit before an allowance or a grant response is produced, and answer a typed range refusal when the history cannot be represented; nothing is wrapped or clamped, and the reservation keeps failing closed on its single sum. GitHub issues #83 and #215.
After a refund decision was committed, the transfer lifecycle returned no outcome to its caller, so a buyer refused during maintenance was told the payment had been refunded even when the transfer failed or its outcome was unknown, and later requests repeated it. Return the recorded outcome and word the refusal as sent, pending confirmation, or recorded but not sent and owed; a repeated request reads the refund transfer's state. The order stays unspendable after the decision. A reconciliation lookup ran under the daemon's lifetime context on an SDK path with no deadline, so one lookup that never completed stopped every later row and all payouts. Bound each lookup; a timeout is recorded as an unknown outcome with the reservation kept, and the pass continues with the next row. GitHub issues #80 and #81.
Bring the payout and refund branch at its corrected head under the page, so that the page is published with the behaviour it describes: the refund outcome reported to a refused buyer and the bounded reconciliation lookups, together with the adapter's id validation. GitHub issue #87.
Bring the allowances branch at its corrected head under the page, so that the configuration reference keeps both of its new sections, usage allowances and chain payments, which land on the same lines. GitHub issue #87.
ctfbruce
marked this pull request as draft
October 5, 2026 12:10
added 3 commits
October 5, 2026 14:16
Bring the quotes and economics branch at its composed head, which now carries the payout and refund lifecycle and the corrected adapter, under the allowance grants change, so that the composed tree builds and tests as one; the allowance tests adapt to the payment handler constructor that reports a configuration error. GitHub issues #83 and #215.
Bring the allowances branch at its composed head, which carries the quotes change on the payout branch and tests built for the payment handler constructor that reports a configuration error, under the page, so that the composed tree builds and tests as one. GitHub issue #87.
ctfbruce
marked this pull request as ready for review
October 5, 2026 13:30
added 4 commits
October 5, 2026 15:47
Keep the payment profile and enablement documentation together with the current allowance, payout recovery and main updates. GitHub issue #87.
ctfbruce
marked this pull request as draft
October 5, 2026 14:03
ctfbruce
marked this pull request as ready for review
October 5, 2026 14:17
This was referenced Oct 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Behaviour
Documentation only.
docs/operations/payments.mdgains, around the existing lifecycle and reconciliation sections (unchanged):Modes and what each records: the disabled and enabled modes (every shipped configuration is disabled), and the rows each payment method writes (
transactions,debuglet_order,order_settlements,earnings,payment_receiptswith its five dispositions,chain_transfers, the listener cursor).Supported chain profile: Sui Testnet, the testnet USDC coin type the dispatcher carries (to be verified against the issuer's published identifier before enabling), testnet SUI for gas with the budget the transfer sets, no mainnet operation, no customer deposits; custody of the dispatcher's key by the host administrator through a dedicated deployment key in the keystore file, never a personal wallet.
Startup validation: the refusal of each
[sui]field and of the keystore (the key must belong to the configured address), and the later checks of the stored cursor and of the indexer's reported coverage.Enabling chain payments: an explicit operator decision that nothing in the repository or its CI performs; six steps ending in a rehearsal with one payment and one payout, and the checks that remain open until a live service confirms them (the GraphQL
availableRangeandEvent.sequenceNumberfields, the stream and GraphQL event positions agreeing, catch-up receipts without a checkpoint, the receipt layout and coin type string, the computed digest against the chain's).Schedules: the daily payout tick at local midnight, reconciliation every minute over at most 100 open transfers with reserved rows stale after ten minutes, the settlement pass every 30 seconds over at most 32 orders, and the two-minute submission bound.
Backups and restore: the backup page covers the TEST state only; a database restored from before its last chain transfer brings back balances and owed refunds that were already paid and nothing in the dispatcher prevents paying them again, so chain payments are switched off and the restored database reconciled against the address's chain history first.
docs/operations/configuration.mddocuments the[sui]keys and their startup checks;docs/README.mdlinks the page.Proof
Every statement is taken from the code on this branch (validation messages, coin type constant, gas budget, log lines, dispositions and states); the link check passes (66 links, 0 broken). No daemon, wallet or network is involved.
Upgrade
None.
Stacked on #381 (the payout and refund lifecycle the page describes); its commits show in the diff against
mainuntil it merges.Limits
The enablement checklist describes operator steps; it performs nothing and no live-network step has been run. Usage allowances are documented with their own change (#380).
GitHub issue #87.
Candidate CI
The branch then took the corrected payout branch and the corrected allowances branch by merge (4a0cfce, then e5c2d9f with the allowances branch's composed head; the configuration reference keeps both new sections, usage allowances then chain payments); Run 37310342339 on e5c2d9f: 16 of 16 lanes and
requiredsucceeded. Run 37294990364 on 2cffd28: 16 of 16 lanes andrequiredsucceeded (the first head's run passed on attempt 2 after thelocallane's knownTestInstalledRecovery/terminatedfailure on code this branch does not touch).