Skip to content
Integra Agentic Terms

Vectors

The shared test cases that fix the gate's rules byte for byte, and how both gates run them.

Two implementations of one rule can compute two hashes. The vectors prevent that. They are JSON files of inputs and expected outputs, shipped by @integraledger/lcp in its vectors/ directory, and every expected value in them comes from outside the code: a published specification's example, a standard's test value (the SHA-256 of abc from FIPS 180-2), or a value computed with independent tools such as GNU coreutils sha256sum, openssl dgst, eth-account and viem, each named in the file's about.

The TypeScript gate and the Python gate both run them. Where a vector names an expected hash, request, payment or decline, both gates must produce exactly that.

What they fix

FileWhat it fixes
core-vectors.jsonThe ATR's bytes and their hash: assembly, SHA-256 and hash comparison, including a record changed by one byte.
buyer.jsonThe gate's own rows, below: the comparison, the fetch bounds, the build, the finish and the agreement exchange.
A file per pairing, or per family of pairings (x402-exact-eip155-eip3009.json, x402-batch-settlement.json, …)The request each pairing builds with H, the payment the signer's answer completes, and where H sits in it. Each file's buyer section holds its pairings' changed-record rows (B2) and bound-check rows (B6); its noSignedPayment names each of its pairings that has no B6 row, and why.
mpp-challenge.jsonThe MPP challenge pieces every MPP pairing shares.
decoder-caps.jsonThe depth and size bounds of the rail decoders.

The buyer rows

buyer.json fixes an ATR A of 138 bytes, C (the same with byte 124 changed from 0x30 to 0x31), a seller document D advertising hash(A), the published Anvil development key as the payer, and a fixed clock.

RowWhat it fixes
B1hash(A) is 0x8b1e1225…10072938.
B2, B2bA link that serves C while D advertises hash(A): hash-mismatch, and the signer is never called.
B3, B4A body that is A parsed and re-serialised, by Python's json.dumps and by JavaScript's JSON.stringify: hash-mismatch. The gate hashes the bytes it received.
B5An advertised H in upper-case hex matches; the build receives the hash the gate computed, in lower case.
B6The request built from D (an EIP-3009 authorization whose nonce is hash(A)), its EIP-712 digest, the Anvil key's signature, and the payment finish returns.
B7check over B6's payment gives hash(A) with A, and signed-not-bound with C.
B8Where the seller advertises x402's payment-identifier, the payment carries a fresh 32-character identifier each time.
B9A payment whose signed contents carry another hash: signed-not-bound, and no payment is returned.
B10An http:// link: declined before any fetch.
B11A redirect, a 404 or a failed fetch: atr-unfetchable, and no signer call.
B12, B13A body of exactly 1 MiB is read and compared; one byte more, declared or streamed, is atr-too-large and the stream is cancelled.
B14A link that never answers: atr-unfetchable after 10 seconds.
B15A signer that throws: signer-failed.
B16An account on another network or chain: no-payable-option, with no fetch.
B17A binding the gate does not serve: pairing-not-supported.
B18A payment-identifier that already holds an id, or is not an object: the identifier cannot be written, and no payment is returned.
B19finish with a chosen from another pairing: pairing-not-supported, before anything is rebuilt.
B20confirm with inputs given and not an object: no-payable-option (x402/input-malformed), before the link is fetched.
B21An option on eip155:0, which is not an EIP-155 chain id: read serves no option (offer-unreadable, x402/no-payable-option), and the link is never fetched.
B22A link of 2,049 UTF-16 code units and 2,048 code points: offer-unreadable (x402/legal-context-malformed), before any fetch. The bound counts UTF-16 code units.
BA1A pairing whose payment is not a public proof: the gate returns the agreement payment for approval (its amount, asset, payTo and network); once approved, the agreement is paid first (one signature), then the payment (one signature).
BA2An agreement URL that advertises another hash: hash-mismatch, nothing signed, before any approval is asked.
BA3An approved agreement that stays 202: the same payment is re-sent every 2 seconds until maxTimeoutSeconds plus 180 seconds, then agreement-pending; the main payment is never signed.
BA4An approved agreement answered by a receipt for another hash: agreement-failed; the main payment is never signed.
BA5An http:// agreement URL: link-not-https, with no fetch.
BA6A pairing whose payment is itself a public proof ignores an agreement URL in the offer.
BA7transact returns the agreement's receipt beside the payment.
BA8An agreement payment the agent has not approved: the gate returns it for approval, one unpaid request, and signs and pays nothing.
BA9A payment request listing a Solana option, then an EVM one, to an EVM signer: the EVM option is the one returned for approval and paid.
BA10As BA9, pending: the exchange is bounded by the chosen EVM option's maxTimeoutSeconds plus 180 seconds, never the Solana option's.
BA11maxTimeoutSeconds written 60.0 is the number 60: the agreement is paid as in BA1.
BA12A pairing whose payment is not a public proof, in an offer that names no agreement URL: the link is fetched and compared, then agreement-not-offered, with no agreement URL fetched and nothing signed; a link serving C is hash-mismatch.
BA13A PAYMENT-REQUIRED written without its pad characters (requiredUnpadded): the 402 carries no readable PAYMENT-REQUIRED, so agreement-failed after one request to the agreement URL, with nothing signed or paid.
BA14An agreement URL that answers the unpaid request 202: agreement-pending after that one request, with nothing signed, paid or returned for approval.
BA15BA3's exchange with retry-after in each other form: one to nine digits wait that many seconds, at least 1; absent or in any other form, an HTTP-date included, the wait is 2 seconds; no request goes past the exchange's bound.
BA16agree with inputs given and not an object: no-payable-option (x402/input-malformed), before the agreement URL is fetched or the signer called.
BA17A receipt led by a byte order mark: agreement-failed once the agreement payment is sent, the decline carrying it as moved; the main payment is never signed.
BA18A PAYMENT-REQUIRED whose JSON follows a byte order mark, or with a space inside: agreement-failed after one request to the agreement URL, with nothing signed or paid.
BA19A PAYMENT-REQUIRED whose final character carries a non-zero pad bit (requiredPadBit): agreement-failed after one request to the agreement URL, with nothing signed or paid.
BE1The ATR served under a content coding (gzip, x-gzip, deflate, br, stacked, two gzip members, identity, gzip): atr-unfetchable before anything is hashed or signed; every request asks for Accept-Encoding: identity.
BE2The agreement URL's 402 or 200 answered under a content coding: agreement-failed; after the agreement payment is signed, the main payment never is.
BS1, BS2x402/exact/stellar and mpp/charge/stellar: a simulated transfer of another amount, on another token contract, or from another account than the payer is declined offer-unreadable (stellar/amount-mismatch, stellar/asset-mismatch, stellar/payer-mismatch) before the signer is called; the transfer the option asks for reaches the signer as stellar-auth.
BX1mpp/session/xrpl: a challenge whose amount is not a u64 of drops written in decimal (an array, a boolean, a number, a decimal point, a sign, 2^64) is mpp/request-malformed, so the gate declines no-payable-option and signs nothing; the amount "100" reaches the signer as xrpl-session-open.
BW1, BW2within on a held mpp/session/tempo channel, over a challenge whose opaque is not a flat string-to-string map: offer-unreadable (mpp/opaque-malformed), and nothing is signed. BW1's control, a well-formed opaque naming the held H, signs one voucher for the held channel.
BW3within for a refund of a held x402/batch-settlement/solana channel, with a computeUnitLimit, computeUnitPrice or recentBlockhash given and malformed: no-payable-option (x402/input-malformed), before the build and the signer call. Its control, with each value well formed, signs once.
BW4within with inputs given and not an object: no-payable-option (x402/input-malformed), before the build and the signer call.

Running them

From a checkout, after pnpm install:

pnpm --filter @integraledger/terms test
pnpm --filter @integraledger/terms-mcp test

cd agentic-terms-py
uv run --locked --python 3.11 pytest -q
uv run --locked --python 3.14 pytest -q

The TypeScript tests read the vectors from the installed @integraledger/lcp. The Python tests read the same files from agentic-terms/node_modules/@integraledger/lcp/vectors, so both gates always run one copy.

Edit on GitHub

Last updated on

On this page