Repository navigation
Add operator-issued usage allowances and owner earnings - #380
Merged
Merged
Conversation
added 5 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.
ctfbruce
marked this pull request as ready for review
October 5, 2026 10:03
added 3 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.
ctfbruce
marked this pull request as draft
October 5, 2026 11:22
ctfbruce
marked this pull request as ready for review
October 5, 2026 11:32
ctfbruce
marked this pull request as draft
October 5, 2026 11:58
added 5 commits
October 5, 2026 14:04
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 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.
ctfbruce
marked this pull request as ready for review
October 5, 2026 13:30
added 3 commits
October 5, 2026 15:47
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 7, 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
Operator-issued usage allowances for the TEST payment method (#83) and a read-only earnings view for executor owners (#215, operator side), both disabled in every shipped configuration:
allowance_grants: immutable grants (actor, reason, idempotency key) per account; update and delete are refused by triggers. An account's allowance is the sum of its grants;consumedis the price of its TEST orders that have a settlement row, credit or refund alike (a failed run does not return its units);reservedis the price of the other orders of its paid TEST intents.[allowance] enabled = true, an account's TEST intent is created by one guarded insert inside the existing intent transaction (granted - consumed - reserved >= price); a refusal is HTTP 429allowance_exceedednaming the remaining and required units and writes no transaction, order or owner row. Before the check, expired unused intents of the account (no claimed order) are releasedPaid -> Expired; an expired intent cannot be submitted.default_grant(30,000,000 units, the documented run arithmetic). Allowances are bookkeeping units, never money.GET /me/allowance(404 while disabled; scopeaccount:read),POST /operator/accounts/{id}/allowance(operator session only: 201 new, 200 for a repeated key with the same body, 409conflictotherwise, 404 unknown account or disabled),GET /operator/executors/{id}/earnings(owner only;payouts: "disabled"while chain payments are off; scopeexecutors:read).GET /mereports whether allowances are enabled. API stays 1.15; OpenAPI,docs/api.md, wire types and the Go client are extended.Proof
-race.Upgrade
Dispatcher schema 23 is required: stop the dispatcher, back up the database and run the explicit upgrade before starting this build. The minimum is raised although a deployment with allowances disabled never reads the table: a configuration-dependent minimum would be a new mechanism.
Stacked on #375 (quotes and economics) and #374 (schema 22); both are merged into this branch, so the diff against
mainshows their commits until they merge.Limits
remainingis negative for an account that used TEST units before any grant; it is reported, not clamped.allowance_out_of_rangeon the allowance, grant and intent routes (nothing recorded, nothing reserved); an operator reconciles that history.GitHub issues #83 and #215.
Candidate CI
Run 37292512236 on 768063f: 16 of 16 lanes and
requiredsucceeded on attempt 2; attempt 1 failed only in thelocallane onTestInstalledRecovery/terminated(the read-only storage inspection after a forced stop, a known failure of that lane on code this branch does not touch). The branch then took the quotes branch's correction by merge (bfafb78): run 37302493328, 16 of 16 lanes andrequiredsucceeded. Correction f48d364 (typed range refusal instead of wrapped arithmetic): run 37307311805, 16 of 16 lanes andrequiredsucceeded. The branch then took the quotes branch's composed head, which carries the payout branch and the corrected adapter, with its two test fixtures adapted to the constructor that reports a configuration error (8d15c1e): run 37310280152, 16 of 16 lanes andrequiredsucceeded.