Skip to content

Commit d7042f3

Browse files
docs(svm): document facilitator fee payer guards; tests(svm): add fee payer validation tests and fix mocks to Instruction accounts array; chore: align high-level tests with signer-aware verification (#546)
1 parent b5fcb22 commit d7042f3

File tree

6 files changed

+335
-32
lines changed

6 files changed

+335
-32
lines changed

specs/schemes/exact/scheme_exact.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,15 @@ amount of funds they need to be transferred.
1212
- An LLM paying to use a tool
1313

1414
## Appendix
15+
16+
## Critical Validation Requirements
17+
18+
While implementation details vary by network, facilitators MUST enforce security constraints that prevent sponsorship abuse. Examples include:
19+
20+
### SVM
21+
22+
- Fee payer safety: the fee payer MUST NOT appear as an account in sensitive instructions or be the transfer authority/source.
23+
- Destination correctness: the receiver MUST match the `payTo` derived destination for the specified `asset`.
24+
- Amount exactness: the transferred amount MUST equal `maxAmountRequired`.
25+
26+
Network-specific rules and exact instruction layouts are defined in the per-network scheme documents. For Solana (SVM), see `scheme_exact_svm.md`.

specs/schemes/exact/scheme_exact_svm.md

Lines changed: 46 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,23 @@ This scheme facilitates payments of a specific amount of an SPL token on the Sol
1010

1111
## Protocol Flow
1212

13-
The protocol flow for `exact` on Solana is client-driven.
13+
The protocol flow for `exact` on Solana is client-driven.
1414

1515
1. **Client** makes an HTTP request to a **Resource Server**.
1616
2. **Resource Server** responds with a `402 Payment Required` status. The response body contains the `paymentRequirements` for the `exact` scheme. Critically, the `extra` field in the requirements contains a **feePayer** which is the public address of the identity that will pay the fee for the transaction. This will typically be the facilitator.
17-
3. **Client** creates a transaction that contains a transfer of an asset to the resource server's wallet address for a specified amount.
17+
3. **Client** creates a transaction that contains a transfer of an asset to the resource server's wallet address for a specified amount.
1818
4. **Client** signs the transaction with their wallet. This results in a partially signed transaction (since the signature of the facilitator that will sponsor the transaction is still missing).
1919
5. **Client** serializes the partially signed transaction and encodes it as a Base64 string.
2020
6. **Client** sends a new HTTP request to the resource server with the `X-PAYMENT` header containing the Base64-encoded partially-signed transaction payload.
2121
7. **Resource Server** receives the request and forwards the `X-PAYMENT` header and `paymentRequirements` to a **Facilitator Server's** `/verify` endpoint.
2222
8. **Facilitator** decodes and deserializes the proposed transaction.
2323
9. **Facilitator** inspects the transaction to ensure it is valid and only contains the expected payment instruction.
24-
10. **Facilitator** returns a response to the **Resource Server** verifying the **client** transaction.
24+
10. **Facilitator** returns a response to the **Resource Server** verifying the **client** transaction.
2525
11. **Resource Server**, upon successful verification, forwards the payload to the facilitator's `/settle` endpoint.
2626
12. **Facilitator Server** provides its final signature as the `feePayer` and submits the now fully-signed transaction to the Solana network.
2727
13. Upon successful on-chain settlement, the **Facilitator Server** responds to the **Resource Server**.
2828
14. **Resource Server** grants the **Client** access to the resource in its response.
2929

30-
3130
## `PaymentRequirements` for `exact`
3231

3332
In addition to the standard x402 `PaymentRequirements` fields, the `exact` scheme on Solana requires the following inside the `extra` field:
@@ -50,13 +49,12 @@ In addition to the standard x402 `PaymentRequirements` fields, the `exact` schem
5049
}
5150
```
5251

53-
- `asset`: The public key of the token mint.
54-
- `extra.feePayer`: The public key of the account that will pay for the transaction fees. This is typically the facilitator's public key.
55-
52+
- `asset`: The public key of the token mint.
53+
- `extra.feePayer`: The public key of the account that will pay for the transaction fees. This is typically the facilitator's public key.
5654

5755
## `X-PAYMENT` Header Payload
5856

59-
The `X-PAYMENT` header is base64 encoded and sent in the request from the client to the resource server when paying for a resource.
57+
The `X-PAYMENT` header is base64 encoded and sent in the request from the client to the resource server when paying for a resource.
6058

6159
Once decoded, the `X-PAYMENT` header is a JSON string with the following properties:
6260

@@ -73,7 +71,6 @@ Once decoded, the `X-PAYMENT` header is a JSON string with the following propert
7371

7472
The `payload` field contains the base64-encoded, serialized, **partially-signed** versioned Solana transaction.
7573

76-
7774
## `X-PAYMENT-RESPONSE` Header Payload
7875

7976
The `X-PAYMENT-RESPONSE` header is base64 encoded and returned to the client from the resource server.
@@ -87,4 +84,43 @@ Once decoded, the `X-PAYMENT-RESPONSE` is a JSON string with the following prope
8784
"network": "solana" | "solana-devnet",
8885
"payer": "base58 encoded public address of the transaction fee payer"
8986
}
90-
```
87+
```
88+
89+
## Facilitator Verification Rules (MUST)
90+
91+
A facilitator verifying an `exact`-scheme SVM payment MUST enforce all of the following checks before sponsoring and signing the transaction:
92+
93+
1. Instruction layout
94+
95+
- The decompiled transaction MUST contain either 3 or 4 instructions in this exact order:
96+
1. Compute Budget: Set Compute Unit Limit
97+
2. Compute Budget: Set Compute Unit Price
98+
3. Optional: Associated Token Account Create (when the destination ATA does not yet exist)
99+
4. SPL Token or Token-2022 TransferChecked
100+
101+
2. Fee payer (facilitator) safety
102+
103+
- The configured fee payer address MUST NOT appear in the `accounts` of any instruction in the transaction.
104+
- The fee payer MUST NOT be the `authority` for the TransferChecked instruction.
105+
- The fee payer MUST NOT be the `source` of the transferred funds.
106+
107+
3. Compute budget validity
108+
109+
- The program for instructions (1) and (2) MUST be `ComputeBudget` with the correct discriminators (2 = SetLimit, 3 = SetPrice).
110+
- The compute unit price MUST be bounded to prevent gas abuse. The reference implementation enforces ≤ 5 lamports per compute unit.
111+
112+
4. Transfer intent and destination
113+
114+
- The TransferChecked program MUST be either `spl-token` or `token-2022`.
115+
- Destination MUST equal the Associated Token Account PDA for `(owner = payTo, mint = asset)` under the selected token program.
116+
117+
5. Account existence
118+
119+
- The `source` ATA MUST exist.
120+
- The destination ATA MUST exist if and only if the Create ATA instruction is NOT present in the transaction. If Create ATA is present, the destination ATA MAY be absent prior to execution.
121+
122+
6. Amount
123+
124+
- The `amount` in TransferChecked MUST equal `maxAmountRequired` exactly.
125+
126+
These checks are security-critical to ensure the fee payer cannot be tricked into transferring their own funds or sponsoring unintended actions. Implementations MAY introduce stricter limits (e.g., lower compute price caps) but MUST NOT relax the above constraints.

specs/x402-specification.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ Each scheme defines:
218218
- Settlement and validation procedures
219219
- Scheme-specific requirements in the `extra` field of `PaymentRequirements`
220220

221-
**6.1 Exact Scheme**
221+
**6.1 Exact Scheme (EVM overview)**
222222

223223
The "exact" scheme uses EIP-3009 (Transfer with Authorization) to enable secure, gasless transfers of specific amounts of ERC-20 tokens.
224224

@@ -254,6 +254,18 @@ The facilitator performs the following verification steps:
254254

255255
Settlement is performed by calling the `transferWithAuthorization` function on the ERC-20 contract with the signature and authorization parameters provided in the payment payload.
256256

257+
**6.2 Exact Scheme (SVM overview)**
258+
259+
For Solana (SVM), the `exact` scheme is implemented using `TransferChecked` for SPL tokens. Critical verification requirements include:
260+
261+
- Enforcing a strict instruction layout (Compute Unit Limit, Compute Unit Price, optional ATA Create, TransferChecked)
262+
- Ensuring the facilitator fee payer does not appear in any instruction accounts and is not the transfer `authority` or `source`
263+
- Bounding compute unit price to mitigate gas abuse
264+
- Verifying the destination ATA matches the `payTo`/`asset` PDA and account existence rules
265+
- Requiring the transfer `amount` to exactly equal `maxAmountRequired`
266+
267+
Full SVM details are specified in `specs/schemes/exact/scheme_exact_svm.md`.
268+
257269
**7. Facilitator Interface**
258270

259271
The facilitator provides HTTP REST APIs for payment verification and settlement. This allows resource servers to delegate blockchain operations to trusted third parties or host the endpoints themselves. Note that while the core x402 protocol is transport-agnostic, facilitator APIs are currently standardized as HTTP endpoints.

0 commit comments

Comments
 (0)