thebuyside-x402-agent 0.2.0 → 0.4.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/dist/chains/adapter.d.ts +36 -10
- package/dist/chains/adapter.d.ts.map +1 -1
- package/dist/chains/adapter.js +10 -8
- package/dist/chains/adapter.js.map +1 -1
- package/dist/chains/base-usdc.d.ts +3 -2
- package/dist/chains/base-usdc.d.ts.map +1 -1
- package/dist/chains/base-usdc.js +9 -2
- package/dist/chains/base-usdc.js.map +1 -1
- package/dist/chains/solana-usdc.d.ts +51 -0
- package/dist/chains/solana-usdc.d.ts.map +1 -0
- package/dist/chains/solana-usdc.js +131 -0
- package/dist/chains/solana-usdc.js.map +1 -0
- package/dist/config.d.ts +11 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +13 -2
- package/dist/config.js.map +1 -1
- package/dist/gateway.d.ts +15 -2
- package/dist/gateway.d.ts.map +1 -1
- package/dist/gateway.js +19 -2
- package/dist/gateway.js.map +1 -1
- package/dist/index.js +0 -0
- package/dist/policy/confirm.d.ts +7 -0
- package/dist/policy/confirm.d.ts.map +1 -1
- package/dist/policy/confirm.js +38 -2
- package/dist/policy/confirm.js.map +1 -1
- package/dist/registry/federation.d.ts +99 -0
- package/dist/registry/federation.d.ts.map +1 -0
- package/dist/registry/federation.js +421 -0
- package/dist/registry/federation.js.map +1 -0
- package/dist/registry/types.d.ts +7 -0
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/server.js +2 -2
- package/dist/server.js.map +1 -1
- package/dist/signer/env-key.d.ts +3 -2
- package/dist/signer/env-key.d.ts.map +1 -1
- package/dist/signer/env-key.js +1 -0
- package/dist/signer/env-key.js.map +1 -1
- package/dist/signer/env-solana-key.d.ts +28 -0
- package/dist/signer/env-solana-key.d.ts.map +1 -0
- package/dist/signer/env-solana-key.js +66 -0
- package/dist/signer/env-solana-key.js.map +1 -0
- package/dist/signer/signer.d.ts +24 -9
- package/dist/signer/signer.d.ts.map +1 -1
- package/dist/signer/signer.js +9 -8
- package/dist/signer/signer.js.map +1 -1
- package/dist/tools/discover.d.ts +10 -4
- package/dist/tools/discover.d.ts.map +1 -1
- package/dist/tools/discover.js +59 -9
- package/dist/tools/discover.js.map +1 -1
- package/dist/tools/fetch.d.ts.map +1 -1
- package/dist/tools/fetch.js +8 -4
- package/dist/tools/fetch.js.map +1 -1
- package/dist/tools/wallet_status.d.ts.map +1 -1
- package/dist/tools/wallet_status.js +15 -1
- package/dist/tools/wallet_status.js.map +1 -1
- package/dist/x402/client.d.ts +6 -2
- package/dist/x402/client.d.ts.map +1 -1
- package/dist/x402/client.js +93 -32
- package/dist/x402/client.js.map +1 -1
- package/dist/x402/types.d.ts +17 -6
- package/dist/x402/types.d.ts.map +1 -1
- package/package.json +6 -1
package/dist/chains/adapter.d.ts
CHANGED
|
@@ -1,33 +1,59 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ChainAdapter — encapsulates per-chain knowledge: network identifier
|
|
3
|
-
* matching
|
|
4
|
-
* payload for an x402 payment.
|
|
3
|
+
* matching and how to build the payment payload for an x402 challenge.
|
|
5
4
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* The interface is a discriminated union because EVM and SVM (Solana)
|
|
6
|
+
* have fundamentally different signing models — EVM signs EIP-712 typed
|
|
7
|
+
* data and produces an EIP-3009 authorization, SVM partially-signs a
|
|
8
|
+
* full Solana transaction. Branching on `kind` keeps each chain's path
|
|
9
|
+
* straightforward; widening to a single shared shape would force a lot
|
|
10
|
+
* of conditionals + casts down the call chain.
|
|
8
11
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* from here.
|
|
12
|
+
* Adding a new chain = add a sibling interface here and a class in the
|
|
13
|
+
* same shape. The protocol loop (`payAndFetch`) is the only place that
|
|
14
|
+
* needs a new branch.
|
|
13
15
|
*/
|
|
14
16
|
import type { Address } from 'viem';
|
|
17
|
+
import type { VersionedTransaction } from '@solana/web3.js';
|
|
15
18
|
import type { Eip712TypedData } from '../signer/signer.js';
|
|
16
19
|
import type { Authorization, PaymentRequirements } from '../x402/types.js';
|
|
17
|
-
export
|
|
20
|
+
export type ChainKind = 'evm' | 'svm';
|
|
21
|
+
export interface ChainAdapterBase {
|
|
18
22
|
/** Short identifier used in logs and receipts (e.g. 'base', 'solana'). */
|
|
19
23
|
readonly id: string;
|
|
24
|
+
/** Discriminator for which signing pipeline this adapter slots into. */
|
|
25
|
+
readonly kind: ChainKind;
|
|
20
26
|
/** Returns true if this adapter handles the network ID in a 402 challenge. */
|
|
21
27
|
matches(network: string): boolean;
|
|
28
|
+
}
|
|
29
|
+
export interface EvmChainAdapter extends ChainAdapterBase {
|
|
30
|
+
readonly kind: 'evm';
|
|
22
31
|
/**
|
|
23
32
|
* Build the data to sign and the authorization body for a given payment
|
|
24
33
|
* requirement. The gateway then signs `typedData` with its `Signer`,
|
|
25
34
|
* combines the signature with `authorization`, and base64-encodes the
|
|
26
|
-
*
|
|
35
|
+
* result into the v1 X-PAYMENT (or v2 PAYMENT-SIGNATURE) header.
|
|
27
36
|
*/
|
|
28
37
|
buildPayment(reqs: PaymentRequirements, payerAddress: Address): {
|
|
29
38
|
typedData: Eip712TypedData;
|
|
30
39
|
authorization: Authorization;
|
|
31
40
|
};
|
|
32
41
|
}
|
|
42
|
+
export interface SolanaChainAdapter extends ChainAdapterBase {
|
|
43
|
+
readonly kind: 'svm';
|
|
44
|
+
/**
|
|
45
|
+
* Build a partially-signed Solana transaction for the payment. Returns
|
|
46
|
+
* the unsigned tx with the seller-provided feePayer already set; the
|
|
47
|
+
* gateway's SolanaSigner adds the buyer's signature and the result is
|
|
48
|
+
* base64-encoded into the v2 `payload.transaction` field.
|
|
49
|
+
*
|
|
50
|
+
* Async because ATA derivation can require an RPC roundtrip in some
|
|
51
|
+
* implementations (this one derives PDAs synchronously, but keeping the
|
|
52
|
+
* shape async leaves room for future RPC-backed lookups).
|
|
53
|
+
*/
|
|
54
|
+
buildPayment(reqs: PaymentRequirements, payerPubkey: string): Promise<{
|
|
55
|
+
tx: VersionedTransaction;
|
|
56
|
+
}>;
|
|
57
|
+
}
|
|
58
|
+
export type ChainAdapter = EvmChainAdapter | SolanaChainAdapter;
|
|
33
59
|
//# sourceMappingURL=adapter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/chains/adapter.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/chains/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAE3E,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK,CAAC;AAEtC,MAAM,WAAW,gBAAgB;IAC/B,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,8EAA8E;IAC9E,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;CACnC;AAED,MAAM,WAAW,eAAgB,SAAQ,gBAAgB;IACvD,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB;;;;;OAKG;IACH,YAAY,CACV,IAAI,EAAE,mBAAmB,EACzB,YAAY,EAAE,OAAO,GACpB;QAAE,SAAS,EAAE,eAAe,CAAC;QAAC,aAAa,EAAE,aAAa,CAAA;KAAE,CAAC;CACjE;AAED,MAAM,WAAW,kBAAmB,SAAQ,gBAAgB;IAC1D,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IACrB;;;;;;;;;OASG;IACH,YAAY,CACV,IAAI,EAAE,mBAAmB,EACzB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;QAAE,EAAE,EAAE,oBAAoB,CAAA;KAAE,CAAC,CAAC;CAC1C;AAED,MAAM,MAAM,YAAY,GAAG,eAAe,GAAG,kBAAkB,CAAC"}
|
package/dist/chains/adapter.js
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ChainAdapter — encapsulates per-chain knowledge: network identifier
|
|
3
|
-
* matching
|
|
4
|
-
* payload for an x402 payment.
|
|
3
|
+
* matching and how to build the payment payload for an x402 challenge.
|
|
5
4
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* The interface is a discriminated union because EVM and SVM (Solana)
|
|
6
|
+
* have fundamentally different signing models — EVM signs EIP-712 typed
|
|
7
|
+
* data and produces an EIP-3009 authorization, SVM partially-signs a
|
|
8
|
+
* full Solana transaction. Branching on `kind` keeps each chain's path
|
|
9
|
+
* straightforward; widening to a single shared shape would force a lot
|
|
10
|
+
* of conditionals + casts down the call chain.
|
|
8
11
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* from here.
|
|
12
|
+
* Adding a new chain = add a sibling interface here and a class in the
|
|
13
|
+
* same shape. The protocol loop (`payAndFetch`) is the only place that
|
|
14
|
+
* needs a new branch.
|
|
13
15
|
*/
|
|
14
16
|
export {};
|
|
15
17
|
//# sourceMappingURL=adapter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../src/chains/adapter.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../src/chains/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}
|
|
@@ -10,9 +10,10 @@
|
|
|
10
10
|
import type { Address } from 'viem';
|
|
11
11
|
import type { Eip712TypedData } from '../signer/signer.js';
|
|
12
12
|
import type { Authorization, PaymentRequirements } from '../x402/types.js';
|
|
13
|
-
import type {
|
|
14
|
-
export declare class BaseUsdcAdapter implements
|
|
13
|
+
import type { EvmChainAdapter } from './adapter.js';
|
|
14
|
+
export declare class BaseUsdcAdapter implements EvmChainAdapter {
|
|
15
15
|
readonly id = "base";
|
|
16
|
+
readonly kind: "evm";
|
|
16
17
|
matches(network: string): boolean;
|
|
17
18
|
buildPayment(reqs: PaymentRequirements, payerAddress: Address): {
|
|
18
19
|
typedData: Eip712TypedData;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-usdc.d.ts","sourceRoot":"","sources":["../../src/chains/base-usdc.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAO,MAAM,MAAM,CAAC;AAEzC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC3E,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"base-usdc.d.ts","sourceRoot":"","sources":["../../src/chains/base-usdc.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAO,MAAM,MAAM,CAAC;AAEzC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC3E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAapD,qBAAa,eAAgB,YAAW,eAAe;IACrD,QAAQ,CAAC,EAAE,UAAU;IACrB,QAAQ,CAAC,IAAI,EAAG,KAAK,CAAU;IAE/B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAIjC,YAAY,CACV,IAAI,EAAE,mBAAmB,EACzB,YAAY,EAAE,OAAO,GACpB;QAAE,SAAS,EAAE,eAAe,CAAC;QAAC,aAAa,EAAE,aAAa,CAAA;KAAE;CA2ChE"}
|
package/dist/chains/base-usdc.js
CHANGED
|
@@ -21,6 +21,7 @@ const TRANSFER_WITH_AUTHORIZATION_TYPES = {
|
|
|
21
21
|
};
|
|
22
22
|
export class BaseUsdcAdapter {
|
|
23
23
|
id = 'base';
|
|
24
|
+
kind = 'evm';
|
|
24
25
|
matches(network) {
|
|
25
26
|
return network === 'base' || network === 'eip155:8453';
|
|
26
27
|
}
|
|
@@ -28,10 +29,16 @@ export class BaseUsdcAdapter {
|
|
|
28
29
|
const validAfter = 0n;
|
|
29
30
|
const validBefore = BigInt(Math.floor(Date.now() / 1000) + reqs.maxTimeoutSeconds);
|
|
30
31
|
const nonce = `0x${randomBytes(32).toString('hex')}`;
|
|
32
|
+
// EIP-712 domain identity comes from `extra.name` / `extra.version`.
|
|
33
|
+
// Critical: Base mainnet USDC's name is "USD Coin" (NOT "USDC"); Base
|
|
34
|
+
// Sepolia uses "USDC" — sellers MUST set both correctly in the challenge.
|
|
35
|
+
const evmExtra = reqs.extra;
|
|
36
|
+
const extraName = typeof evmExtra.name === 'string' ? evmExtra.name : '';
|
|
37
|
+
const extraVersion = typeof evmExtra.version === 'string' ? evmExtra.version : '';
|
|
31
38
|
const typedData = {
|
|
32
39
|
domain: {
|
|
33
|
-
name:
|
|
34
|
-
version:
|
|
40
|
+
name: extraName,
|
|
41
|
+
version: extraVersion,
|
|
35
42
|
chainId: base.id,
|
|
36
43
|
verifyingContract: reqs.asset,
|
|
37
44
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-usdc.js","sourceRoot":"","sources":["../../src/chains/base-usdc.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAKnC,MAAM,iCAAiC,GAAG;IACxC,yBAAyB,EAAE;QACzB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE;QACjC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE;QAC/B,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE;QAClC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE;QACvC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE;QACxC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE;KACnC;CACO,CAAC;AAEX,MAAM,OAAO,eAAe;IACjB,EAAE,GAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"base-usdc.js","sourceRoot":"","sources":["../../src/chains/base-usdc.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAKnC,MAAM,iCAAiC,GAAG;IACxC,yBAAyB,EAAE;QACzB,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE;QACjC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE;QAC/B,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE;QAClC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE;QACvC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE;QACxC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE;KACnC;CACO,CAAC;AAEX,MAAM,OAAO,eAAe;IACjB,EAAE,GAAG,MAAM,CAAC;IACZ,IAAI,GAAG,KAAc,CAAC;IAE/B,OAAO,CAAC,OAAe;QACrB,OAAO,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,aAAa,CAAC;IACzD,CAAC;IAED,YAAY,CACV,IAAyB,EACzB,YAAqB;QAErB,MAAM,UAAU,GAAG,EAAE,CAAC;QACtB,MAAM,WAAW,GAAG,MAAM,CACxB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,iBAAiB,CACvD,CAAC;QACF,MAAM,KAAK,GAAG,KAAK,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAS,CAAC;QAE5D,qEAAqE;QACrE,sEAAsE;QACtE,0EAA0E;QAC1E,MAAM,QAAQ,GAAG,IAAI,CAAC,KAA8C,CAAC;QACrE,MAAM,SAAS,GAAG,OAAO,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QACzE,MAAM,YAAY,GAAG,OAAO,QAAQ,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QAClF,MAAM,SAAS,GAAoB;YACjC,MAAM,EAAE;gBACN,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE,YAAY;gBACrB,OAAO,EAAE,IAAI,CAAC,EAAE;gBAChB,iBAAiB,EAAE,IAAI,CAAC,KAAK;aAC9B;YACD,KAAK,EAAE,iCAAiC;YACxC,WAAW,EAAE,2BAA2B;YACxC,OAAO,EAAE;gBACP,IAAI,EAAE,YAAY;gBAClB,EAAE,EAAE,IAAI,CAAC,KAAK;gBACd,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC;gBACrC,UAAU;gBACV,WAAW;gBACX,KAAK;aACN;SACF,CAAC;QAEF,MAAM,aAAa,GAAkB;YACnC,IAAI,EAAE,YAAY;YAClB,EAAE,EAAE,IAAI,CAAC,KAAK;YACd,KAAK,EAAE,IAAI,CAAC,iBAAiB;YAC7B,UAAU,EAAE,UAAU,CAAC,QAAQ,EAAE;YACjC,WAAW,EAAE,WAAW,CAAC,QAAQ,EAAE;YACnC,KAAK;SACN,CAAC;QAEF,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC;IACtC,CAAC;CACF"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SolanaUsdcAdapter — Solana mainnet, USDC SPL transfer, x402 v2 `exact` SVM.
|
|
3
|
+
*
|
|
4
|
+
* Builds the partially-signed transaction that the buyer signs and the
|
|
5
|
+
* facilitator (the seller's `extra.feePayer`) co-signs and broadcasts.
|
|
6
|
+
*
|
|
7
|
+
* Required instruction order per spec (scheme_exact_svm.md):
|
|
8
|
+
* 1. ComputeBudget: SetComputeUnitLimit
|
|
9
|
+
* 2. ComputeBudget: SetComputeUnitPrice
|
|
10
|
+
* 3. SPL Token TransferChecked (source ATA → seller ATA)
|
|
11
|
+
* 4. Memo — value from `extra.memo` if present, else a random 16-byte
|
|
12
|
+
* hex nonce
|
|
13
|
+
*
|
|
14
|
+
* The buyer is NOT the feePayer — the seller's facilitator covers SOL gas.
|
|
15
|
+
* Critical safety rule (also enforced explicitly below): the feePayer
|
|
16
|
+
* pubkey MUST NOT appear in any instruction's accounts list, otherwise
|
|
17
|
+
* the buyer would be implicitly signing as the feePayer.
|
|
18
|
+
*
|
|
19
|
+
* Asset scope: this adapter only handles the USDC mint on Solana mainnet.
|
|
20
|
+
* Other SPL tokens or the Token-2022 program would need a sibling
|
|
21
|
+
* adapter — the wire-level scheme is the same but decimals and program
|
|
22
|
+
* ID differ.
|
|
23
|
+
*
|
|
24
|
+
* Blockhash: a versioned tx requires a recent blockhash. We fetch one
|
|
25
|
+
* from a public Solana RPC at build time. Configurable via
|
|
26
|
+
* X402_SOLANA_RPC; default is api.mainnet-beta.solana.com. Cached for
|
|
27
|
+
* a short window since Solana blockhashes are valid for ~60 seconds
|
|
28
|
+
* and an x402 round trip typically fits well inside that.
|
|
29
|
+
*/
|
|
30
|
+
import { VersionedTransaction } from '@solana/web3.js';
|
|
31
|
+
import type { PaymentRequirements } from '../x402/types.js';
|
|
32
|
+
import type { SolanaChainAdapter } from './adapter.js';
|
|
33
|
+
export type SolanaUsdcAdapterOptions = {
|
|
34
|
+
/** Override the Solana RPC URL. Defaults to mainnet-beta. */
|
|
35
|
+
rpcUrl?: string;
|
|
36
|
+
/** Test seam — fetch a blockhash without a real RPC. */
|
|
37
|
+
blockhashFetcher?: () => Promise<string>;
|
|
38
|
+
};
|
|
39
|
+
export declare class SolanaUsdcAdapter implements SolanaChainAdapter {
|
|
40
|
+
readonly id = "solana";
|
|
41
|
+
readonly kind: "svm";
|
|
42
|
+
private readonly rpcUrl;
|
|
43
|
+
private readonly blockhashFetcher?;
|
|
44
|
+
constructor(opts?: SolanaUsdcAdapterOptions);
|
|
45
|
+
matches(network: string): boolean;
|
|
46
|
+
buildPayment(reqs: PaymentRequirements, payerPubkey: string): Promise<{
|
|
47
|
+
tx: VersionedTransaction;
|
|
48
|
+
}>;
|
|
49
|
+
private fetchBlockhash;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=solana-usdc.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"solana-usdc.d.ts","sourceRoot":"","sources":["../../src/chains/solana-usdc.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAGH,OAAO,EAML,oBAAoB,EACrB,MAAM,iBAAiB,CAAC;AAMzB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAavD,MAAM,MAAM,wBAAwB,GAAG;IACrC,6DAA6D;IAC7D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wDAAwD;IACxD,gBAAgB,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1C,CAAC;AAEF,qBAAa,iBAAkB,YAAW,kBAAkB;IAC1D,QAAQ,CAAC,EAAE,YAAY;IACvB,QAAQ,CAAC,IAAI,EAAG,KAAK,CAAU;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAwB;gBAE9C,IAAI,GAAE,wBAA6B;IAK/C,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAO3B,YAAY,CAChB,IAAI,EAAE,mBAAmB,EACzB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;QAAE,EAAE,EAAE,oBAAoB,CAAA;KAAE,CAAC;YA4E1B,cAAc;CAM7B"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SolanaUsdcAdapter — Solana mainnet, USDC SPL transfer, x402 v2 `exact` SVM.
|
|
3
|
+
*
|
|
4
|
+
* Builds the partially-signed transaction that the buyer signs and the
|
|
5
|
+
* facilitator (the seller's `extra.feePayer`) co-signs and broadcasts.
|
|
6
|
+
*
|
|
7
|
+
* Required instruction order per spec (scheme_exact_svm.md):
|
|
8
|
+
* 1. ComputeBudget: SetComputeUnitLimit
|
|
9
|
+
* 2. ComputeBudget: SetComputeUnitPrice
|
|
10
|
+
* 3. SPL Token TransferChecked (source ATA → seller ATA)
|
|
11
|
+
* 4. Memo — value from `extra.memo` if present, else a random 16-byte
|
|
12
|
+
* hex nonce
|
|
13
|
+
*
|
|
14
|
+
* The buyer is NOT the feePayer — the seller's facilitator covers SOL gas.
|
|
15
|
+
* Critical safety rule (also enforced explicitly below): the feePayer
|
|
16
|
+
* pubkey MUST NOT appear in any instruction's accounts list, otherwise
|
|
17
|
+
* the buyer would be implicitly signing as the feePayer.
|
|
18
|
+
*
|
|
19
|
+
* Asset scope: this adapter only handles the USDC mint on Solana mainnet.
|
|
20
|
+
* Other SPL tokens or the Token-2022 program would need a sibling
|
|
21
|
+
* adapter — the wire-level scheme is the same but decimals and program
|
|
22
|
+
* ID differ.
|
|
23
|
+
*
|
|
24
|
+
* Blockhash: a versioned tx requires a recent blockhash. We fetch one
|
|
25
|
+
* from a public Solana RPC at build time. Configurable via
|
|
26
|
+
* X402_SOLANA_RPC; default is api.mainnet-beta.solana.com. Cached for
|
|
27
|
+
* a short window since Solana blockhashes are valid for ~60 seconds
|
|
28
|
+
* and an x402 round trip typically fits well inside that.
|
|
29
|
+
*/
|
|
30
|
+
import { randomBytes } from 'node:crypto';
|
|
31
|
+
import { ComputeBudgetProgram, Connection, PublicKey, TransactionInstruction, TransactionMessage, VersionedTransaction, } from '@solana/web3.js';
|
|
32
|
+
import { createTransferCheckedInstruction, getAssociatedTokenAddressSync, TOKEN_PROGRAM_ID, } from '@solana/spl-token';
|
|
33
|
+
const SOLANA_MAINNET_GENESIS = '5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp';
|
|
34
|
+
const USDC_MAINNET_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';
|
|
35
|
+
const USDC_DECIMALS = 6;
|
|
36
|
+
const MEMO_PROGRAM_ID = new PublicKey('MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr');
|
|
37
|
+
const DEFAULT_RPC_URL = 'https://api.mainnet-beta.solana.com';
|
|
38
|
+
/** Compute budget — generous enough for transferChecked + memo + a touch of slack. */
|
|
39
|
+
const COMPUTE_UNIT_LIMIT = 60_000;
|
|
40
|
+
/** Micro-lamports per CU. 1000 = ~0.0006 SOL on a 60k CU tx; adequate priority. */
|
|
41
|
+
const COMPUTE_UNIT_PRICE = 1_000;
|
|
42
|
+
export class SolanaUsdcAdapter {
|
|
43
|
+
id = 'solana';
|
|
44
|
+
kind = 'svm';
|
|
45
|
+
rpcUrl;
|
|
46
|
+
blockhashFetcher;
|
|
47
|
+
constructor(opts = {}) {
|
|
48
|
+
this.rpcUrl = opts.rpcUrl ?? process.env.X402_SOLANA_RPC ?? DEFAULT_RPC_URL;
|
|
49
|
+
this.blockhashFetcher = opts.blockhashFetcher;
|
|
50
|
+
}
|
|
51
|
+
matches(network) {
|
|
52
|
+
return (network === 'solana' ||
|
|
53
|
+
network === `solana:${SOLANA_MAINNET_GENESIS}`);
|
|
54
|
+
}
|
|
55
|
+
async buildPayment(reqs, payerPubkey) {
|
|
56
|
+
if (reqs.asset !== USDC_MAINNET_MINT) {
|
|
57
|
+
throw new Error(`solana-usdc adapter only handles the USDC mint (${USDC_MAINNET_MINT}); ` +
|
|
58
|
+
`seller asked for asset=${reqs.asset}. Different SPL token or Token-2022 ` +
|
|
59
|
+
`would need a sibling adapter.`);
|
|
60
|
+
}
|
|
61
|
+
const feePayerStr = readFeePayer(reqs.extra);
|
|
62
|
+
if (!feePayerStr) {
|
|
63
|
+
throw new Error('solana 402 challenge missing required `extra.feePayer` — the seller ' +
|
|
64
|
+
'must declare a facilitator pubkey to cover SOL gas. The x402 SVM ' +
|
|
65
|
+
'spec does not allow the buyer to be the fee payer.');
|
|
66
|
+
}
|
|
67
|
+
const payer = new PublicKey(payerPubkey);
|
|
68
|
+
const seller = new PublicKey(reqs.payTo);
|
|
69
|
+
const feePayer = new PublicKey(feePayerStr);
|
|
70
|
+
const mint = new PublicKey(reqs.asset);
|
|
71
|
+
if (feePayer.equals(payer)) {
|
|
72
|
+
throw new Error('seller-declared feePayer equals the buyer pubkey — refusing to sign. ' +
|
|
73
|
+
'Per scheme_exact_svm.md the buyer must not appear as feePayer.');
|
|
74
|
+
}
|
|
75
|
+
const sourceAta = getAssociatedTokenAddressSync(mint, payer);
|
|
76
|
+
const destAta = getAssociatedTokenAddressSync(mint, seller);
|
|
77
|
+
// Spec: feePayer MUST NOT appear in any instruction's accounts.
|
|
78
|
+
if (feePayer.equals(sourceAta) || feePayer.equals(destAta) || feePayer.equals(seller) || feePayer.equals(mint)) {
|
|
79
|
+
throw new Error('feePayer collides with an instruction account (source ATA, dest ATA, ' +
|
|
80
|
+
'seller, or mint) — refusing to build tx. This violates the SVM scheme ' +
|
|
81
|
+
'safety rule.');
|
|
82
|
+
}
|
|
83
|
+
const memoText = readMemo(reqs.extra) ?? randomHexNonce(16);
|
|
84
|
+
const amount = BigInt(reqs.maxAmountRequired);
|
|
85
|
+
const instructions = [
|
|
86
|
+
ComputeBudgetProgram.setComputeUnitLimit({ units: COMPUTE_UNIT_LIMIT }),
|
|
87
|
+
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: COMPUTE_UNIT_PRICE }),
|
|
88
|
+
createTransferCheckedInstruction(sourceAta, mint, destAta, payer, amount, USDC_DECIMALS, [], TOKEN_PROGRAM_ID),
|
|
89
|
+
new TransactionInstruction({
|
|
90
|
+
programId: MEMO_PROGRAM_ID,
|
|
91
|
+
keys: [],
|
|
92
|
+
data: Buffer.from(memoText, 'utf8'),
|
|
93
|
+
}),
|
|
94
|
+
];
|
|
95
|
+
const recentBlockhash = await this.fetchBlockhash();
|
|
96
|
+
const messageV0 = new TransactionMessage({
|
|
97
|
+
payerKey: feePayer,
|
|
98
|
+
recentBlockhash,
|
|
99
|
+
instructions,
|
|
100
|
+
}).compileToV0Message();
|
|
101
|
+
const tx = new VersionedTransaction(messageV0);
|
|
102
|
+
return { tx };
|
|
103
|
+
}
|
|
104
|
+
async fetchBlockhash() {
|
|
105
|
+
if (this.blockhashFetcher)
|
|
106
|
+
return this.blockhashFetcher();
|
|
107
|
+
const conn = new Connection(this.rpcUrl, 'confirmed');
|
|
108
|
+
const { blockhash } = await conn.getLatestBlockhash('confirmed');
|
|
109
|
+
return blockhash;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function readFeePayer(extra) {
|
|
113
|
+
if (extra && typeof extra === 'object' && 'feePayer' in extra) {
|
|
114
|
+
const f = extra.feePayer;
|
|
115
|
+
if (typeof f === 'string' && f.length > 0)
|
|
116
|
+
return f;
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
function readMemo(extra) {
|
|
121
|
+
if (extra && typeof extra === 'object' && 'memo' in extra) {
|
|
122
|
+
const m = extra.memo;
|
|
123
|
+
if (typeof m === 'string' && m.length > 0)
|
|
124
|
+
return m;
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
function randomHexNonce(byteLen) {
|
|
129
|
+
return randomBytes(byteLen).toString('hex');
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=solana-usdc.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"solana-usdc.js","sourceRoot":"","sources":["../../src/chains/solana-usdc.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Config loader. Reads `.env` (via dotenv) and validates env vars at startup.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Wallet keys are optional at boot — the gateway boots and lists tools
|
|
5
|
+
* without any keys; payment-shaped tools simply error if asked to pay
|
|
6
|
+
* on a chain without a configured signer. Users can configure EVM only,
|
|
7
|
+
* Solana only, or both.
|
|
6
8
|
*
|
|
7
9
|
* `.env` is loaded by absolute path (relative to this file) so the gateway
|
|
8
10
|
* works correctly when spawned from any cwd — e.g. by Claude Desktop, where
|
|
@@ -10,8 +12,14 @@
|
|
|
10
12
|
*/
|
|
11
13
|
import type { Hex } from 'viem';
|
|
12
14
|
export type Config = {
|
|
13
|
-
/** Hex 0x-prefixed private key, or null if unset / placeholder. */
|
|
15
|
+
/** Hex 0x-prefixed EVM private key, or null if unset / placeholder. */
|
|
14
16
|
payerPrivateKey: Hex | null;
|
|
17
|
+
/**
|
|
18
|
+
* Solana secret key in either base58 (Phantom export) or
|
|
19
|
+
* JSON-array (solana-keygen output) format. Stored as the raw string;
|
|
20
|
+
* the signer decodes + validates. Null if unset / placeholder.
|
|
21
|
+
*/
|
|
22
|
+
payerSolanaKey: string | null;
|
|
15
23
|
};
|
|
16
24
|
export declare function loadConfig(): Config;
|
|
17
25
|
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,MAAM,CAAC;AAMhC,MAAM,MAAM,MAAM,GAAG;IACnB,uEAAuE;IACvE,eAAe,EAAE,GAAG,GAAG,IAAI,CAAC;IAC5B;;;;OAIG;IACH,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B,CAAC;AAoBF,wBAAgB,UAAU,IAAI,MAAM,CAKnC"}
|
package/dist/config.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Config loader. Reads `.env` (via dotenv) and validates env vars at startup.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Wallet keys are optional at boot — the gateway boots and lists tools
|
|
5
|
+
* without any keys; payment-shaped tools simply error if asked to pay
|
|
6
|
+
* on a chain without a configured signer. Users can configure EVM only,
|
|
7
|
+
* Solana only, or both.
|
|
6
8
|
*
|
|
7
9
|
* `.env` is loaded by absolute path (relative to this file) so the gateway
|
|
8
10
|
* works correctly when spawned from any cwd — e.g. by Claude Desktop, where
|
|
@@ -24,9 +26,18 @@ function parsePrivateKey(raw) {
|
|
|
24
26
|
}
|
|
25
27
|
return raw;
|
|
26
28
|
}
|
|
29
|
+
function parseSolanaKey(raw) {
|
|
30
|
+
if (!raw)
|
|
31
|
+
return null;
|
|
32
|
+
const trimmed = raw.trim();
|
|
33
|
+
if (trimmed === '' || trimmed === 'PASTE_YOUR_SOLANA_KEY_HERE')
|
|
34
|
+
return null;
|
|
35
|
+
return trimmed;
|
|
36
|
+
}
|
|
27
37
|
export function loadConfig() {
|
|
28
38
|
return {
|
|
29
39
|
payerPrivateKey: parsePrivateKey(process.env.X402_PAYER_PRIVATE_KEY),
|
|
40
|
+
payerSolanaKey: parseSolanaKey(process.env.X402_PAYER_SOLANA_KEY),
|
|
30
41
|
};
|
|
31
42
|
}
|
|
32
43
|
//# sourceMappingURL=config.js.map
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,QAAQ,CAAC;AAG9C,MAAM,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACrD,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AACxC,UAAU,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;AAanD,SAAS,eAAe,CAAC,GAAuB;IAC9C,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,IAAI,GAAG,KAAK,+BAA+B;QAAE,OAAO,IAAI,CAAC;IACzD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CACb,iFAAiF,CAClF,CAAC;IACJ,CAAC;IACD,OAAO,GAAU,CAAC;AACpB,CAAC;AAED,SAAS,cAAc,CAAC,GAAuB;IAC7C,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,IAAI,OAAO,KAAK,EAAE,IAAI,OAAO,KAAK,4BAA4B;QAAE,OAAO,IAAI,CAAC;IAC5E,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO;QACL,eAAe,EAAE,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC;QACpE,cAAc,EAAE,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;KAClE,CAAC;AACJ,CAAC"}
|
package/dist/gateway.d.ts
CHANGED
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
* Built once at server startup and passed by reference to each tool's
|
|
5
5
|
* register function. Keeps tool signatures stable as we add modules
|
|
6
6
|
* (analytics, hosted-wallet, multi-chain, etc.) over time.
|
|
7
|
+
*
|
|
8
|
+
* Multi-chain (M8): the gateway holds at most one signer per chain
|
|
9
|
+
* kind (`evm`, `svm`). Either, both, or neither may be configured —
|
|
10
|
+
* the tools error clearly when an MCP call would need a signer that
|
|
11
|
+
* isn't set.
|
|
7
12
|
*/
|
|
8
13
|
import type { ChainAdapter } from './chains/adapter.js';
|
|
9
14
|
import type { Config } from './config.js';
|
|
@@ -11,16 +16,24 @@ import { Allowlist } from './policy/allowlist.js';
|
|
|
11
16
|
import { CapPolicy } from './policy/caps.js';
|
|
12
17
|
import { ConfirmPolicy } from './policy/confirm.js';
|
|
13
18
|
import { Receipts } from './policy/receipts.js';
|
|
19
|
+
import { Federation } from './registry/federation.js';
|
|
14
20
|
import { Registry } from './registry/lookup.js';
|
|
15
|
-
import type {
|
|
21
|
+
import type { EvmSigner, SolanaSigner } from './signer/signer.js';
|
|
22
|
+
export type Signers = {
|
|
23
|
+
evm: EvmSigner | null;
|
|
24
|
+
svm: SolanaSigner | null;
|
|
25
|
+
};
|
|
16
26
|
export type Gateway = {
|
|
17
|
-
|
|
27
|
+
signers: Signers;
|
|
18
28
|
chains: ChainAdapter[];
|
|
19
29
|
allowlist: Allowlist;
|
|
20
30
|
receipts: Receipts;
|
|
21
31
|
caps: CapPolicy;
|
|
22
32
|
confirm: ConfirmPolicy;
|
|
23
33
|
registry: Registry;
|
|
34
|
+
federation: Federation;
|
|
24
35
|
};
|
|
25
36
|
export declare function buildGateway(config: Config): Promise<Gateway>;
|
|
37
|
+
/** True if at least one chain has a configured signer. */
|
|
38
|
+
export declare function hasAnySigner(s: Signers): boolean;
|
|
26
39
|
//# sourceMappingURL=gateway.d.ts.map
|
package/dist/gateway.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"gateway.d.ts","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAGhD,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElE,MAAM,MAAM,OAAO,GAAG;IACpB,GAAG,EAAE,SAAS,GAAG,IAAI,CAAC;IACtB,GAAG,EAAE,YAAY,GAAG,IAAI,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,SAAS,EAAE,SAAS,CAAC;IACrB,QAAQ,EAAE,QAAQ,CAAC;IACnB,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,aAAa,CAAC;IACvB,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,UAAU,CAAC;CACxB,CAAC;AAEF,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAmBnE;AAED,0DAA0D;AAC1D,wBAAgB,YAAY,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAEhD"}
|
package/dist/gateway.js
CHANGED
|
@@ -4,25 +4,42 @@
|
|
|
4
4
|
* Built once at server startup and passed by reference to each tool's
|
|
5
5
|
* register function. Keeps tool signatures stable as we add modules
|
|
6
6
|
* (analytics, hosted-wallet, multi-chain, etc.) over time.
|
|
7
|
+
*
|
|
8
|
+
* Multi-chain (M8): the gateway holds at most one signer per chain
|
|
9
|
+
* kind (`evm`, `svm`). Either, both, or neither may be configured —
|
|
10
|
+
* the tools error clearly when an MCP call would need a signer that
|
|
11
|
+
* isn't set.
|
|
7
12
|
*/
|
|
8
13
|
import { BaseUsdcAdapter } from './chains/base-usdc.js';
|
|
14
|
+
import { SolanaUsdcAdapter } from './chains/solana-usdc.js';
|
|
9
15
|
import { Allowlist } from './policy/allowlist.js';
|
|
10
16
|
import { CapPolicy } from './policy/caps.js';
|
|
11
17
|
import { ConfirmPolicy } from './policy/confirm.js';
|
|
12
18
|
import { Receipts } from './policy/receipts.js';
|
|
19
|
+
import { Federation } from './registry/federation.js';
|
|
13
20
|
import { Registry } from './registry/lookup.js';
|
|
14
21
|
import { EnvKeySigner } from './signer/env-key.js';
|
|
22
|
+
import { EnvSolanaKeySigner } from './signer/env-solana-key.js';
|
|
15
23
|
export async function buildGateway(config) {
|
|
16
24
|
const receipts = Receipts.fromEnv();
|
|
17
25
|
const registry = await Registry.load();
|
|
26
|
+
const signers = {
|
|
27
|
+
evm: config.payerPrivateKey ? new EnvKeySigner(config.payerPrivateKey) : null,
|
|
28
|
+
svm: config.payerSolanaKey ? new EnvSolanaKeySigner(config.payerSolanaKey) : null,
|
|
29
|
+
};
|
|
18
30
|
return {
|
|
19
|
-
|
|
20
|
-
chains: [new BaseUsdcAdapter()],
|
|
31
|
+
signers,
|
|
32
|
+
chains: [new BaseUsdcAdapter(), new SolanaUsdcAdapter()],
|
|
21
33
|
allowlist: Allowlist.fromEnv({ defaultHosts: registry.hosts() }),
|
|
22
34
|
receipts,
|
|
23
35
|
caps: CapPolicy.fromEnv(receipts),
|
|
24
36
|
confirm: ConfirmPolicy.fromEnv(),
|
|
25
37
|
registry,
|
|
38
|
+
federation: Federation.fromEnv(),
|
|
26
39
|
};
|
|
27
40
|
}
|
|
41
|
+
/** True if at least one chain has a configured signer. */
|
|
42
|
+
export function hasAnySigner(s) {
|
|
43
|
+
return s.evm !== null || s.svm !== null;
|
|
44
|
+
}
|
|
28
45
|
//# sourceMappingURL=gateway.js.map
|
package/dist/gateway.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway.js","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"gateway.js","sourceRoot":"","sources":["../src/gateway.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAG5D,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAmBhE,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAc;IAC/C,MAAM,QAAQ,GAAG,QAAQ,CAAC,OAAO,EAAE,CAAC;IACpC,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAEvC,MAAM,OAAO,GAAY;QACvB,GAAG,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,YAAY,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,IAAI;QAC7E,GAAG,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,kBAAkB,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,IAAI;KAClF,CAAC;IAEF,OAAO;QACL,OAAO;QACP,MAAM,EAAE,CAAC,IAAI,eAAe,EAAE,EAAE,IAAI,iBAAiB,EAAE,CAAC;QACxD,SAAS,EAAE,SAAS,CAAC,OAAO,CAAC,EAAE,YAAY,EAAE,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC;QAChE,QAAQ;QACR,IAAI,EAAE,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC;QACjC,OAAO,EAAE,aAAa,CAAC,OAAO,EAAE;QAChC,QAAQ;QACR,UAAU,EAAE,UAAU,CAAC,OAAO,EAAE;KACjC,CAAC;AACJ,CAAC;AAED,0DAA0D;AAC1D,MAAM,UAAU,YAAY,CAAC,CAAU;IACrC,OAAO,CAAC,CAAC,GAAG,KAAK,IAAI,IAAI,CAAC,CAAC,GAAG,KAAK,IAAI,CAAC;AAC1C,CAAC"}
|
package/dist/index.js
CHANGED
|
File without changes
|
package/dist/policy/confirm.d.ts
CHANGED
|
@@ -38,6 +38,13 @@ export type ConfirmAskArgs = {
|
|
|
38
38
|
url: string;
|
|
39
39
|
todaySpentAtomic: bigint;
|
|
40
40
|
dailyCapAtomic: bigint;
|
|
41
|
+
/**
|
|
42
|
+
* Optional `extensions.*` metadata from the 402 challenge. When the seller
|
|
43
|
+
* (or an index like CDP Bazaar) embeds listing metadata in the challenge,
|
|
44
|
+
* we surface a short summary in the prompt so the user has a trust signal
|
|
45
|
+
* beyond the bare URL.
|
|
46
|
+
*/
|
|
47
|
+
extensions?: Record<string, unknown>;
|
|
41
48
|
};
|
|
42
49
|
export declare class ConfirmPolicy {
|
|
43
50
|
readonly config: ConfirmConfig;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"confirm.d.ts","sourceRoot":"","sources":["../../src/policy/confirm.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIzE,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7C,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,WAAW,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,WAAgC,CAAC;AAEpE,MAAM,MAAM,eAAe,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3E,MAAM,MAAM,cAAc,GAAG;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"confirm.d.ts","sourceRoot":"","sources":["../../src/policy/confirm.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAIzE,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7C,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,WAAW,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,WAAgC,CAAC;AAEpE,MAAM,MAAM,eAAe,GAAG;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3E,MAAM,MAAM,cAAc,GAAG;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC,CAAC;AAEF,qBAAa,aAAa;aAGI,MAAM,EAAE,aAAa;IAFjD,OAAO,CAAC,mBAAmB,CAAS;gBAER,MAAM,EAAE,aAAa;IAEjD,MAAM,CAAC,OAAO,IAAI,aAAa;IAqB/B,mEAAmE;IACnE,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO;IAW5C;;;;;OAKG;IACG,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;CAyD7E"}
|
package/dist/policy/confirm.js
CHANGED
|
@@ -121,11 +121,47 @@ function formatPrompt(args) {
|
|
|
121
121
|
return args.url;
|
|
122
122
|
}
|
|
123
123
|
})();
|
|
124
|
-
|
|
124
|
+
const lines = [
|
|
125
125
|
`Confirm payment to ${args.host}`,
|
|
126
126
|
` Amount: $${formatUsdcAtomic(args.amountAtomic)} USDC (Base mainnet)`,
|
|
127
127
|
` Resource: ${args.method} ${path}`,
|
|
128
128
|
` Today: $${formatUsdcAtomic(args.todaySpentAtomic)} of $${formatUsdcAtomic(args.dailyCapAtomic)} daily cap`,
|
|
129
|
-
]
|
|
129
|
+
];
|
|
130
|
+
const meta = formatExtensions(args.extensions);
|
|
131
|
+
if (meta)
|
|
132
|
+
lines.push(meta);
|
|
133
|
+
return lines.join('\n');
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Render `extensions.*` metadata for the prompt. Currently knows the
|
|
137
|
+
* `bazaar` namespace (CDP Bazaar listings); other namespaces fall through
|
|
138
|
+
* to a generic key-list. Returns null when there's nothing useful to show.
|
|
139
|
+
*
|
|
140
|
+
* Only surfaces string/number values to keep the prompt readable and
|
|
141
|
+
* resistant to nested-object spam from a hostile seller.
|
|
142
|
+
*/
|
|
143
|
+
function formatExtensions(extensions) {
|
|
144
|
+
if (!extensions || Object.keys(extensions).length === 0)
|
|
145
|
+
return null;
|
|
146
|
+
const bazaar = extensions.bazaar;
|
|
147
|
+
if (bazaar && typeof bazaar === 'object') {
|
|
148
|
+
const b = bazaar;
|
|
149
|
+
const parts = [];
|
|
150
|
+
if (typeof b.name === 'string')
|
|
151
|
+
parts.push(b.name);
|
|
152
|
+
if (typeof b.category === 'string')
|
|
153
|
+
parts.push(`category: ${b.category}`);
|
|
154
|
+
if (typeof b.listingId === 'string')
|
|
155
|
+
parts.push(`listing: ${b.listingId}`);
|
|
156
|
+
if (parts.length > 0)
|
|
157
|
+
return ` Bazaar: ${parts.join(' • ')}`;
|
|
158
|
+
}
|
|
159
|
+
const flat = Object.entries(extensions)
|
|
160
|
+
.filter(([, v]) => typeof v === 'string' || typeof v === 'number')
|
|
161
|
+
.slice(0, 3)
|
|
162
|
+
.map(([k, v]) => `${k}=${v}`);
|
|
163
|
+
if (flat.length === 0)
|
|
164
|
+
return null;
|
|
165
|
+
return ` Meta: ${flat.join(', ')}`;
|
|
130
166
|
}
|
|
131
167
|
//# sourceMappingURL=confirm.js.map
|