uvd-x402-sdk 2.69.0 → 2.71.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uvd-x402-sdk",
3
- "version": "2.69.0",
3
+ "version": "2.71.0",
4
4
  "description": "x402 Payment SDK - Gasless crypto payments across 25 blockchains via Ultravioleta facilitator. Supports EVM (including Scroll, SKALE Base, Robinhood Chain), Solana, Fogo, Stellar, NEAR, Algorand, Sui, and XRP Ledger. Features: ERC-8004 Trustless Agents, Escrow/Refunds, multi-stablecoin (USDC, EURC, AUSD, PYUSD, USDT, USDG).",
5
5
  "author": "Ultravioleta DAO <ultravioletadao@gmail.com>",
6
6
  "license": "MIT",
@@ -3071,6 +3071,166 @@ export type Erc8004Network =
3071
3071
  // Solana (uses QuantuLabs 8004-solana Anchor program + ATOM Engine)
3072
3072
  | 'solana' | 'solana-devnet';
3073
3073
 
3074
+ /**
3075
+ * Networks where the facilitator serves the RELAYED feedback rail, i.e. where
3076
+ * Execution Market has deployed a `FeedbackDelegate` and the facilitator
3077
+ * verified it on-chain (code present, and its `REPUTATION_REGISTRY()` reads
3078
+ * back that network's registry).
3079
+ *
3080
+ * Anywhere else `POST /feedback/evm/prepare` answers 400 — and it should. An
3081
+ * invented delegate address would send a type-4 transaction to an account with
3082
+ * no code behind it, and in the EVM a `.call()` to an address with no code
3083
+ * RETURNS SUCCESS. The failure would look exactly like a rating that rated
3084
+ * nobody.
3085
+ *
3086
+ * `avalanche` is absent and is not waiting to join: the C-Chain rejects the
3087
+ * transaction type itself (`-32000 transaction type not supported`), so there
3088
+ * is nothing to deploy against. Anchor the rating on a chain that supports
3089
+ * EIP-7702; the payment stays where it was made.
3090
+ */
3091
+ export const RELAYED_FEEDBACK_NETWORKS: readonly Erc8004Network[] = [
3092
+ 'base',
3093
+ 'ethereum',
3094
+ 'polygon',
3095
+ 'arbitrum',
3096
+ 'optimism',
3097
+ 'celo',
3098
+ 'bsc',
3099
+ 'monad',
3100
+ 'base-sepolia',
3101
+ ] as const;
3102
+
3103
+ /**
3104
+ * Whether `network` serves the rater-authored feedback rail.
3105
+ *
3106
+ * Lets a caller route without paying a round trip for a 400. The facilitator
3107
+ * re-checks the delegate on-chain on every request regardless — this list is a
3108
+ * routing hint, never the authority.
3109
+ */
3110
+ export function supportsRelayedFeedback(network: string): boolean {
3111
+ return (RELAYED_FEEDBACK_NETWORKS as readonly string[]).includes(wireNetwork(network));
3112
+ }
3113
+
3114
+ /**
3115
+ * An EIP-7702 authorization, as a wallet produces it.
3116
+ *
3117
+ * Needed only the first time a rater rates: it points their EOA at the
3118
+ * `FeedbackDelegate`. Once delegated, `prepare` answers `delegated: true` and
3119
+ * the submission carries no authorization at all.
3120
+ */
3121
+ export interface RelayAuthorizationParams {
3122
+ /**
3123
+ * Chain the authorization is for.
3124
+ *
3125
+ * `0` is EIP-7702's wildcard and is valid on EVERY chain — a far broader
3126
+ * grant than pinning this one. Send the chain id `prepare` returned.
3127
+ */
3128
+ chainId: number;
3129
+ /**
3130
+ * The delegate the account is pointed at. Must be the address `prepare`
3131
+ * offered; the facilitator refuses anything else before it pays for a
3132
+ * transaction.
3133
+ */
3134
+ address: string;
3135
+ /** The rater account's nonce at the moment the authorization executes */
3136
+ nonce: number;
3137
+ yParity: number;
3138
+ r: string;
3139
+ s: string;
3140
+ }
3141
+
3142
+ /**
3143
+ * Request body for `POST /feedback/evm/prepare`.
3144
+ *
3145
+ * `rater` is the address that will appear on-chain as the author, which is the
3146
+ * whole point of this rail.
3147
+ */
3148
+ export interface PrepareRelayFeedbackRequest {
3149
+ x402Version: 1 | 2;
3150
+ network: Erc8004Network;
3151
+ feedback: FeedbackParams & { rater: string };
3152
+ }
3153
+
3154
+ /**
3155
+ * Response from `POST /feedback/evm/prepare`.
3156
+ *
3157
+ * Everything the rater has to sign so the CHAIN records them as the author
3158
+ * while the facilitator pays the gas.
3159
+ */
3160
+ export interface PrepareRelayFeedbackResponse {
3161
+ success: boolean;
3162
+ /** The `FeedbackDelegate` the rater's EOA must be delegated to */
3163
+ delegate?: string;
3164
+ /** Registry calldata the rater is authorising, hex-encoded */
3165
+ data?: string;
3166
+ /**
3167
+ * The value the rater's signature must recover against.
3168
+ *
3169
+ * **The EIP-191 envelope is already applied here.** A holder of a raw key
3170
+ * signs this directly as a prehash (viem's `sign({ hash })`, ethers'
3171
+ * `signingKey.sign`). A WALLET must not be handed this value: `personal_sign`
3172
+ * applies the envelope itself, so it gets wrapped twice and recovers an
3173
+ * address that is not the rater. Wallets sign {@link signingPayload}.
3174
+ */
3175
+ digest?: string;
3176
+ /**
3177
+ * The same hash with the envelope still OFF — what a wallet signs.
3178
+ *
3179
+ * `keccak256('\x19Ethereum Signed Message:\n32' || signingPayload)` is
3180
+ * exactly {@link digest}, so a client can check the two against each other
3181
+ * rather than rebuilding the preimage from `data`.
3182
+ *
3183
+ * Requires facilitator v1.95.0+. Older facilitators omit it; a client that
3184
+ * needs it should fail loudly rather than fall back to signing `digest`
3185
+ * through a wallet, which produces a well-formed signature that authorises
3186
+ * nobody.
3187
+ */
3188
+ signingPayload?: string;
3189
+ /**
3190
+ * Unix seconds after which the authorisation is void. Short on purpose:
3191
+ * relaying is permissionless, so a signed authorisation is live in the wild
3192
+ * until it expires.
3193
+ */
3194
+ deadline?: number;
3195
+ /** Single-use value binding this authorisation. Echo it back on submit */
3196
+ nonce?: string;
3197
+ /**
3198
+ * Whether the account is already delegated. When `false` the submission MUST
3199
+ * carry an `authorization`.
3200
+ */
3201
+ delegated: boolean;
3202
+ /** The account nonce to put in the EIP-7702 authorization, when needed */
3203
+ accountNonce?: number;
3204
+ chainId: number;
3205
+ error?: string;
3206
+ network: Erc8004Network;
3207
+ }
3208
+
3209
+ /**
3210
+ * Request body for `POST /feedback/evm/submit`.
3211
+ *
3212
+ * The feedback parameters are not redundant with `prepare`: the facilitator
3213
+ * rebuilds the registry calldata from them and requires the rater's signature
3214
+ * to cover exactly that. It does not relay calldata it was handed.
3215
+ */
3216
+ export interface SubmitRelayFeedbackRequest {
3217
+ x402Version: 1 | 2;
3218
+ network: Erc8004Network;
3219
+ feedback: FeedbackParams & { rater: string };
3220
+ /** The deadline `prepare` returned */
3221
+ deadline: number;
3222
+ /** The single-use nonce `prepare` returned */
3223
+ nonce: string;
3224
+ /**
3225
+ * The rater's signature. It must recover to `rater` over `digest` — so
3226
+ * either a raw-key prehash signature over `digest`, or a wallet
3227
+ * `personal_sign` over `signingPayload`. Not `personal_sign` over `digest`.
3228
+ */
3229
+ signature: string;
3230
+ /** Required only when `prepare` answered `delegated: false` */
3231
+ authorization?: RelayAuthorizationParams;
3232
+ }
3233
+
3074
3234
  /**
3075
3235
  * Proof of payment returned when settling with ERC-8004 extension
3076
3236
  */
@@ -3759,6 +3919,14 @@ export class Erc8004Client {
3759
3919
  *
3760
3920
  * Requires proof of payment for authorized feedback submission.
3761
3921
  *
3922
+ * @deprecated On this route the facilitator is the AUTHOR: the registry
3923
+ * records `msg.sender`, and that is the facilitator's wallet — which can also
3924
+ * revoke what it wrote. On the networks in {@link RELAYED_FEEDBACK_NETWORKS}
3925
+ * use {@link Erc8004Client.prepareRelayedFeedback} +
3926
+ * {@link Erc8004Client.submitRelayedFeedback} instead, which record the RATER
3927
+ * as author. This route still works and is not going away without notice: it
3928
+ * is the only one available where no `FeedbackDelegate` is deployed.
3929
+ *
3762
3930
  * @param request - Feedback request with agent ID, value, and proof
3763
3931
  * @returns Feedback response with transaction hash
3764
3932
  *
@@ -3824,6 +3992,155 @@ export class Erc8004Client {
3824
3992
  }
3825
3993
  }
3826
3994
 
3995
+ /**
3996
+ * Ask the facilitator what the rater must sign to author a rating.
3997
+ *
3998
+ * Step 1 of the rater-authored rail. Writes nothing on-chain and costs
3999
+ * nothing: it reads the delegate, the rater's delegation state and their
4000
+ * account nonce, then hands back a digest, a deadline and a single-use nonce.
4001
+ *
4002
+ * Why this exists: the ERC-8004 Reputation Registry records `msg.sender` as
4003
+ * the author, and the deployed implementation has no delegation path — no
4004
+ * `giveFeedbackWithSignature`, no ERC-2771 forwarder. So a rating the
4005
+ * facilitator relays the ordinary way is a rating attributed to the
4006
+ * FACILITATOR. EIP-7702 fixes it without touching the registry: the rater
4007
+ * delegates their own EOA to the `FeedbackDelegate` and the transaction is
4008
+ * sent TO THE RATER'S ADDRESS, so the registry sees the rater while the
4009
+ * facilitator pays.
4010
+ *
4011
+ * What to do with the answer:
4012
+ * 1. Produce the rater's signature. **Which value you sign depends on how you
4013
+ * sign it**, and getting it wrong yields a well-formed signature that
4014
+ * authorises nobody:
4015
+ * - raw key: sign `digest` as a **prehash**. It already carries the
4016
+ * EIP-191 envelope.
4017
+ * - wallet: `personal_sign` over `signingPayload`. `personal_sign` adds the
4018
+ * envelope itself, so signing `digest` with it wraps the value TWICE and
4019
+ * recovers a stranger — the only symptom is `relay_bad_signature`.
4020
+ * 2. If `delegated` is `false`, also produce an EIP-7702 authorization over
4021
+ * `(chainId, delegate, accountNonce)`.
4022
+ * 3. Hand both to {@link submitRelayedFeedback} with the SAME feedback
4023
+ * parameters, `deadline` and `nonce`.
4024
+ *
4025
+ * @param request - Network, rater address and feedback parameters
4026
+ * @returns Everything needed to sign, including whether an EIP-7702
4027
+ * authorization is still required
4028
+ *
4029
+ * @example
4030
+ * ```ts
4031
+ * const prep = await erc8004.prepareRelayedFeedback({
4032
+ * x402Version: 1,
4033
+ * network: 'base',
4034
+ * feedback: { agentId: 18896, value: 95, tag1: 'quality', rater: raterAddress },
4035
+ * });
4036
+ * // prep.delegated === false -> an EIP-7702 authorization is required
4037
+ * ```
4038
+ */
4039
+ async prepareRelayedFeedback(
4040
+ request: PrepareRelayFeedbackRequest,
4041
+ ): Promise<PrepareRelayFeedbackResponse> {
4042
+ const url = `${this.baseUrl}/feedback/evm/prepare`;
4043
+
4044
+ const controller = new AbortController();
4045
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
4046
+
4047
+ try {
4048
+ const response = await fetch(url, {
4049
+ method: 'POST',
4050
+ headers: {
4051
+ 'Content-Type': 'application/json',
4052
+ 'Accept': 'application/json',
4053
+ },
4054
+ body: JSON.stringify({ ...request, network: wireNetwork(request.network) }),
4055
+ signal: controller.signal,
4056
+ });
4057
+
4058
+ clearTimeout(timeoutId);
4059
+
4060
+ if (!response.ok) {
4061
+ const errorText = await response.text();
4062
+ return {
4063
+ success: false,
4064
+ delegated: false,
4065
+ chainId: 0,
4066
+ error: `Facilitator error: ${response.status} - ${errorText}`,
4067
+ network: request.network,
4068
+ };
4069
+ }
4070
+
4071
+ return await response.json();
4072
+ } catch (error) {
4073
+ clearTimeout(timeoutId);
4074
+ return {
4075
+ success: false,
4076
+ delegated: false,
4077
+ chainId: 0,
4078
+ error: error instanceof Error ? error.message : 'Unknown error',
4079
+ network: request.network,
4080
+ };
4081
+ }
4082
+ }
4083
+
4084
+ /**
4085
+ * Relay a rater-authored rating; the facilitator pays the gas.
4086
+ *
4087
+ * Step 2 of the rater-authored rail. The on-chain record that comes out of it
4088
+ * has the RATER as `msg.sender`, so `getClients(agentId)` shows the rater
4089
+ * rather than the facilitator.
4090
+ *
4091
+ * Pass back the same feedback parameters, `deadline` and `nonce` that
4092
+ * {@link prepareRelayedFeedback} returned. They are not redundant: the
4093
+ * facilitator rebuilds the registry calldata from them and requires the
4094
+ * rater's signature to cover exactly that.
4095
+ *
4096
+ * `authorization` is required only when `prepare` answered
4097
+ * `delegated: false`. One that names a different delegate than the one
4098
+ * `prepare` offered is refused before any gas is spent.
4099
+ *
4100
+ * @param request - Feedback parameters plus the rater's signature
4101
+ * @returns Feedback response with the transaction hash
4102
+ */
4103
+ async submitRelayedFeedback(
4104
+ request: SubmitRelayFeedbackRequest,
4105
+ ): Promise<FeedbackResponse> {
4106
+ const url = `${this.baseUrl}/feedback/evm/submit`;
4107
+
4108
+ const controller = new AbortController();
4109
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
4110
+
4111
+ try {
4112
+ const response = await fetch(url, {
4113
+ method: 'POST',
4114
+ headers: {
4115
+ 'Content-Type': 'application/json',
4116
+ 'Accept': 'application/json',
4117
+ },
4118
+ body: JSON.stringify({ ...request, network: wireNetwork(request.network) }),
4119
+ signal: controller.signal,
4120
+ });
4121
+
4122
+ clearTimeout(timeoutId);
4123
+
4124
+ if (!response.ok) {
4125
+ const errorText = await response.text();
4126
+ return {
4127
+ success: false,
4128
+ error: `Facilitator error: ${response.status} - ${errorText}`,
4129
+ network: request.network,
4130
+ };
4131
+ }
4132
+
4133
+ return await response.json();
4134
+ } catch (error) {
4135
+ clearTimeout(timeoutId);
4136
+ return {
4137
+ success: false,
4138
+ error: error instanceof Error ? error.message : 'Unknown error',
4139
+ network: request.network,
4140
+ };
4141
+ }
4142
+ }
4143
+
3827
4144
  /**
3828
4145
  * Revoke previously submitted feedback
3829
4146
  *