Execution security for FAssets agents

Only the XRP payment FAssets asked for.

Signet reads the redemption obligation from Flare, derives the payment instead of accepting caller-supplied fields, checks XRPL for a prior payment, and returns execution evidence through FDC.

Network
Coston2 · live
FCC extension
66248 · registered
Execution
Simulated FCC
XRPL
Testnet · live
FDC
Proof on chain
Several redemption obligations converge into a single constrained gate, one exact XRP payment leaves it, and a proof path returns from the payment back to the gate.
How it works

One request in. One exact payment out.

Each step opens. Nothing here needs reading in order.

01 FAssets obligation The protocol states what is owed

Destination, amount, reference, destination tag and the payment window all come from FAssets protocol state, read on chain. Nobody types them.

02 Signet derives and observes The caller supplies a request id

authorizeRedemption(uint256,uint32) has no parameter for a payment field. Signet also observes the XRP ledger itself, across independent endpoints that must agree, before it will authorize anything.

03 Exact XRP payment One obligation, at most one payment

The signed transaction is persisted before submission, and the chain records at most one instruction dispatch per request and generation.

04 FDC proves the outcome Independently checkable on Flare

An XRPPayment attestation carries the memo and destination tag a generic payment proof would not, and FdcVerification accepted it on Coston2.

Why all four are load-bearing
  • Remove FAssets and there is no authoritative obligation. Signet degrades into enforcing a policy someone typed, which is a generic policy signer.
  • Remove FCC and a compromised host can sign arbitrary XRP.
  • Remove the XRPL observation and a payment already made by another party is invisible. That is incident 44928272, not a hypothetical.
  • Remove FDC and completion cannot be independently established on Flare. The operator's word becomes the evidence.
Try it · no wallet, no funds

Try to break it

Pick a redemption, see what FAssets determines, then try to change it. Every outcome below is what the deployed contract and the policy actually do.

Deterministic demo

The obligation from the composed run: what FAssets required, and what Signet derived from it.

What the caller supplies

Request id
45430134
Generation
0

What FAssets determines

You cannot type any of these. There is no parameter for them.

Destination rpa8bBa8GS1Zit6y9PcpQbahkqFahpWMyb FAssets
Amount 9950000 drops FAssets
Payment reference Derived from the request id by the protocol FAssets
Destination tag Not required for this obligation FAssets
Payment window 19895402 → 19895947 FAssets
Agent vault 0x165c62b4531d28e34c68a8b2acbf4d0421e4e028 FAssets
Source account rPvarExLQuuqtkMBfDHp3pNnESZ5HByXta Signet
XRPL result tesSUCCESS XRPL

Authorized, executed, and proven

Signed, submitted once, validated on XRPL Testnet, refused on replay by the ledger itself, and accepted by the FDC verifier on Coston2.

Live Coston2

Request 44928272, the incident. It is finished, so the contract refuses to build a payment for it.

What the caller supplies

Request id
44928272
Generation
1

What FAssets determines

You cannot type any of these. There is no parameter for them.

Destination Not produced FAssets
Amount Not produced FAssets
Payment window Not produced FAssets

Refused before any payment exists

FAssets does not report this request ACTIVE, so readCanonicalRedemptionById refuses and no instruction is built. Settled and expired obligations fail closed.

Signet policy

Now try to change it

Pick a move.

Impossible through the deployed API

The entry point is authorizeRedemption(uint256 requestId, uint32 generation). There is no destination parameter to change. A 256-run fuzz over caller addresses asserts one request id yields one payload for every caller.

Impossible through the deployed API

Same reason. The amount is read from the FAssets obligation by the contract, so a caller has nothing to alter.

Impossible through the deployed API

The reference is the obligation's identity on the ledger and is resolved on chain, not accepted from a caller.

Refused · S021_PAYMENT_ALREADY_OBSERVED

This is not hypothetical. It happened on live Coston2 with request 44928272, and it is why schema V2 requires the signing boundary's own XRP ledger observation.

Refused · S022_UNDERLYING_STATE_UNAVAILABLE

Refusing is the default. There is no input that authorizes without an observation, and too few agreeing sources counts as no observation.

Refused · S023_UNDERLYING_STATE_DISAGREEMENT

Endpoints that disagree fail closed, and this one is deliberately never retried automatically.

Refused · S024_UNDERLYING_OBSERVATION_STALE

An observation too old to rely on is refused rather than accepted with a warning.

Refused · the obligation is not ACTIVE

FAssets stops reporting a settled or expired request as ACTIVE, and readCanonicalRedemptionById refuses anything it does not. No instruction is built, so there is nothing to sign.

Refused by the XRP ledger itself

The identical signed blob resubmitted returns tefPAST_SEQ: the account sequence is already consumed. Signet does not have to be trusted for this one.

Operator

For agent operators who keep their own underlying account

The job: fulfil redemption obligations without leaving an unrestricted XRPL spending key available to a compromised operator host. Before Signet, a hot signer can authorize arbitrary XRP. With Signet, a request id goes in and a protocol-derived obligation comes out as the only admissible payment.

Connect or continue read-only

A wallet is optional and never implies agent status.

Inspect a redemption

Read the canonical obligation from Coston2 by request id.

Observe XRPL

Independent endpoints must agree before a decision is possible.

Preview the decision

Authorization or a typed refusal, with the reason code.

Open operator
Live incident · Coston2

The tests passed. The ledger proved us wrong.

A live redemption was still ACTIVE on Flare after the assigned agent had already paid it on XRPL. Signet read ACTIVE as unpaid and sent the same obligation again.

Before

ACTIVE meant authorize

Three duplicate-payment guards, all watching Flare.

Ledger 19825006

The agent had paid

From its own underlying address, on XRPL.

Ledger 19825042

Signet paid again

36 ledgers later. No third party lost funds, which is luck about the test setup.

Now

S021_PAYMENT_ALREADY_OBSERVED

V2 requires the signing boundary's own XRPL observation, bound into the commitment.

Read the full incident
Proof

What holds up, and what does not

Six of 34 claims. Each one links to its evidence and to everything it does not prove.

Verified testXRP

The exact XRP payment executed

A transaction template produced by the Signet reference model was signed by a rotatable RegularKey and reached validated success on XRPL Testnet, unmodified, carrying the FAssets payment …

View evidence
Verified coston2xrpl-testnet

FDC proof accepted on chain

An XRPL Testnet payment made by Signet was attested by the Flare Data Connector end to end: the request was paid for and submitted to FdcHub, the voting round finalized, the Merkle proof …

View evidence
Verified xrpl-testnetcoston2

Anyone can re-check a receipt

An independent verifier runs from a fresh clone with no credentials and checks a Signet receipt against sources this repository does not control: it asks every XRPL endpoint and requires …

View evidence
All 34 claims
Current deployment boundary

What this deployment is not

Five FCC states, tracked separately, because collapsing any two of them is how a project ends up implying attestation it does not have.

Extension Registered
Extension 66248 is registered on the live Coston2 FlareTeeManager, and the registry routes it to Signet's own instruction sender.
Machine Unavailable
No TEE machine is registered. Registration was attempted on the live chain and reverted OwnerNotAllowed: MachineManager is gated on an owner allowlist Flare maintains.
Production No
Promotion needs a registered machine, which the owner allowlist blocks, and real attestation, which this deployment does not have. Tracked separately from machine registration on purpose.
Simulated execution Available
The decision runs through the FCC extension contract as an ordinary local process. Accepted for hackathon judging when disclosed as simulated, which it is, everywhere.
Hardware attestation No
Nothing is hardware-attested. A running node reported platform TEST_PLATFORM and attestation magic_pass, which the pinned deployment docs name as rejection conditions.

Check it yourself

No wallet, no funds, no Docker, no GCP, no secrets. One command from a fresh clone.

make judge
→ 13 PASS · 0 FAIL · 2 UNVERIFIABLE