@rubicon-caliga/agent-sdk 0.1.1 → 0.1.2
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/README.md +21 -5
- package/dist/agent-client.test.d.ts +2 -0
- package/dist/agent-client.test.d.ts.map +1 -0
- package/dist/agent-client.test.js +110 -0
- package/dist/agent-client.test.js.map +1 -0
- package/dist/circle-cli-gateway-payment.d.ts +43 -0
- package/dist/circle-cli-gateway-payment.d.ts.map +1 -1
- package/dist/circle-cli-gateway-payment.js +42 -9
- package/dist/circle-cli-gateway-payment.js.map +1 -1
- package/dist/circle-cli-gateway-payment.test.js +110 -1
- package/dist/circle-cli-gateway-payment.test.js.map +1 -1
- package/package.json +1 -1
- package/src/agent-client.test.ts +118 -0
- package/src/circle-cli-gateway-payment.test.ts +129 -0
- package/src/circle-cli-gateway-payment.ts +64 -10
package/README.md
CHANGED
|
@@ -20,7 +20,7 @@ const hasCircleCliWallet = process.env.CIRCLE_AGENT_WALLET_ADDRESS;
|
|
|
20
20
|
const rubicon = new Rubicon({
|
|
21
21
|
paymentEngine: hasCircleCliWallet
|
|
22
22
|
? new CircleCliGatewayPaymentEngine({
|
|
23
|
-
|
|
23
|
+
agentWalletAddress: process.env.CIRCLE_AGENT_WALLET_ADDRESS as `0x${string}`,
|
|
24
24
|
})
|
|
25
25
|
: new StaticPaymentEngine(),
|
|
26
26
|
});
|
|
@@ -88,16 +88,32 @@ import Rubicon, { CircleCliGatewayPaymentEngine } from "@rubicon-caliga/agent-sd
|
|
|
88
88
|
const rubicon = new Rubicon({
|
|
89
89
|
baseUrl: process.env.RUBICON_GATEWAY_URL,
|
|
90
90
|
paymentEngine: new CircleCliGatewayPaymentEngine({
|
|
91
|
-
|
|
91
|
+
agentWalletAddress: process.env.CIRCLE_AGENT_WALLET_ADDRESS as `0x${string}` | undefined,
|
|
92
92
|
chain: "ARC-TESTNET",
|
|
93
93
|
}),
|
|
94
94
|
});
|
|
95
95
|
```
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
`CircleCliGatewayPaymentEngine` keeps two addresses distinct:
|
|
98
|
+
|
|
99
|
+
- `agentWalletAddress` is the Circle Agent Wallet passed to
|
|
100
|
+
`circle wallet sign typed-data --address`.
|
|
101
|
+
- `buyerWalletAddress` / `backingEOA` is the Gateway backing EOA used as the
|
|
102
|
+
x402 `TransferWithAuthorization.from` address.
|
|
103
|
+
|
|
104
|
+
When only `agentWalletAddress` is provided, the engine discovers the backing EOA
|
|
105
|
+
with `circle gateway balance --address <agentWalletAddress> --chain ARC-TESTNET --output json`.
|
|
106
|
+
When no address is provided, the engine first runs
|
|
98
107
|
`circle wallet list --chain ARC-TESTNET --type agent --output json` and uses the
|
|
99
|
-
sole Agent Wallet it finds
|
|
100
|
-
|
|
108
|
+
sole Agent Wallet it finds, then discovers its backing EOA with Gateway balance.
|
|
109
|
+
The older `walletAddress` option remains as an alias for `agentWalletAddress`.
|
|
110
|
+
If multiple Agent Wallets are present, pass the agent wallet explicitly.
|
|
111
|
+
|
|
112
|
+
Gateway/Nanopayments receipts may have empty `transactionHashes`. Treat
|
|
113
|
+
`settlementIds` as the primary proof of payment; scanner visibility is not
|
|
114
|
+
guaranteed because a successful nanopayment may not appear as a direct ERC-20
|
|
115
|
+
transfer to the seller. Seller dashboards should count Rubicon backend payment
|
|
116
|
+
receipts and Circle Gateway settlement IDs, not direct on-chain transfers.
|
|
101
117
|
|
|
102
118
|
The lower-level API-backed custody path is also available:
|
|
103
119
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-client.test.d.ts","sourceRoot":"","sources":["../src/agent-client.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { RubiconClient } from "./agent-client.js";
|
|
4
|
+
const paymentEngine = {
|
|
5
|
+
async createWordPayment() {
|
|
6
|
+
return { paymentPayload: { ok: true } };
|
|
7
|
+
},
|
|
8
|
+
};
|
|
9
|
+
test("run receipt preserves Gateway settlement receipt fields", async () => {
|
|
10
|
+
const fetcher = (async (input) => {
|
|
11
|
+
const url = String(input);
|
|
12
|
+
if (url.endsWith("/v1/sessions")) {
|
|
13
|
+
return jsonResponse({
|
|
14
|
+
sessionId: "session_1",
|
|
15
|
+
state: "active",
|
|
16
|
+
article: article(),
|
|
17
|
+
navigation: navigation(),
|
|
18
|
+
pricePerWordAtomic: "1",
|
|
19
|
+
maxArticlePriceAtomic: "10",
|
|
20
|
+
conversationId: "conversation_1",
|
|
21
|
+
wordPaymentAtomic: "1",
|
|
22
|
+
gatewayFeeBps: 0,
|
|
23
|
+
paymentRequired: { scheme: "exact" },
|
|
24
|
+
expiresAt: "2026-06-18T12:00:00.000Z",
|
|
25
|
+
wordsPaid: 0,
|
|
26
|
+
wordsDelivered: 0,
|
|
27
|
+
paidAtomic: "0",
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
if (url.endsWith("/v1/sessions/session_1/payments")) {
|
|
31
|
+
return jsonResponse({
|
|
32
|
+
accepted: true,
|
|
33
|
+
sequence: 0,
|
|
34
|
+
word: "Rubicon",
|
|
35
|
+
priceAtomic: "1",
|
|
36
|
+
wordsPaid: 1,
|
|
37
|
+
wordsDelivered: 1,
|
|
38
|
+
paidAtomic: "1",
|
|
39
|
+
completed: true,
|
|
40
|
+
transactionHashes: [],
|
|
41
|
+
settlementIds: ["settlement_1"],
|
|
42
|
+
payment: {
|
|
43
|
+
paymentId: "payment_1",
|
|
44
|
+
sessionId: "session_1",
|
|
45
|
+
articleId: "article_1",
|
|
46
|
+
sequence: 0,
|
|
47
|
+
meteringUnit: "word",
|
|
48
|
+
amountAtomic: "1",
|
|
49
|
+
currency: "USDC",
|
|
50
|
+
network: "eip155:5042002",
|
|
51
|
+
payTo: "0x3333333333333333333333333333333333333333",
|
|
52
|
+
transactionHashes: [],
|
|
53
|
+
settlementIds: ["settlement_1"],
|
|
54
|
+
buyerWalletAddress: "0x2222222222222222222222222222222222222222",
|
|
55
|
+
settledAt: "2026-06-18T12:00:00.000Z",
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
throw new Error(`Unexpected fetch: ${url}`);
|
|
60
|
+
});
|
|
61
|
+
const client = new RubiconClient({
|
|
62
|
+
baseUrl: "http://rubicon.test",
|
|
63
|
+
paymentEngine,
|
|
64
|
+
fetch: fetcher,
|
|
65
|
+
});
|
|
66
|
+
const receipt = await client.run({
|
|
67
|
+
articleId: "article_1",
|
|
68
|
+
maxSpendAtomic: "10",
|
|
69
|
+
});
|
|
70
|
+
assert.deepEqual(receipt.transactionHashes, []);
|
|
71
|
+
assert.deepEqual(receipt.settlementIds, ["settlement_1"]);
|
|
72
|
+
assert.equal(receipt.buyerWalletAddress, "0x2222222222222222222222222222222222222222");
|
|
73
|
+
assert.equal(receipt.sellerPayTo, "0x3333333333333333333333333333333333333333");
|
|
74
|
+
assert.equal(receipt.network, "eip155:5042002");
|
|
75
|
+
});
|
|
76
|
+
function jsonResponse(body) {
|
|
77
|
+
return new Response(JSON.stringify(body), {
|
|
78
|
+
status: 200,
|
|
79
|
+
headers: { "content-type": "application/json" },
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
function article() {
|
|
83
|
+
return {
|
|
84
|
+
articleId: "article_1",
|
|
85
|
+
creatorId: "creator_1",
|
|
86
|
+
creatorUsername: "creator",
|
|
87
|
+
title: "Title",
|
|
88
|
+
author: "Author",
|
|
89
|
+
state: "published",
|
|
90
|
+
totalWords: 1,
|
|
91
|
+
pricePerWordAtomic: "1",
|
|
92
|
+
maxArticlePriceAtomic: "1",
|
|
93
|
+
sections: [],
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function navigation() {
|
|
97
|
+
return {
|
|
98
|
+
articleId: "article_1",
|
|
99
|
+
sections: [],
|
|
100
|
+
sellerAgent: {
|
|
101
|
+
recommendedSectionId: "intro",
|
|
102
|
+
alternativeSectionIds: [],
|
|
103
|
+
rationale: "",
|
|
104
|
+
safeHints: [],
|
|
105
|
+
withheld: [],
|
|
106
|
+
},
|
|
107
|
+
stopConditions: [],
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=agent-client.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-client.test.js","sourceRoot":"","sources":["../src/agent-client.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,8 +5,24 @@ export interface CircleCliGatewayPaymentEngineOptions {
|
|
|
5
5
|
/**
|
|
6
6
|
* Agent Wallet address controlled by Circle CLI. When omitted, the engine
|
|
7
7
|
* resolves the sole agent wallet returned by `circle wallet list`.
|
|
8
|
+
*
|
|
9
|
+
* @deprecated Use `agentWalletAddress`. This alias is kept for existing SDK
|
|
10
|
+
* callers and is treated as the Circle CLI signing wallet, not the x402
|
|
11
|
+
* authorization `from` address.
|
|
8
12
|
*/
|
|
9
13
|
walletAddress?: `0x${string}`;
|
|
14
|
+
/**
|
|
15
|
+
* Agent Wallet address controlled by Circle CLI. This is passed to
|
|
16
|
+
* `circle wallet sign typed-data --address`.
|
|
17
|
+
*/
|
|
18
|
+
agentWalletAddress?: `0x${string}`;
|
|
19
|
+
/**
|
|
20
|
+
* Gateway backing EOA used as the x402 authorization `from` address. When
|
|
21
|
+
* omitted, the engine resolves it from `circle gateway balance`.
|
|
22
|
+
*/
|
|
23
|
+
buyerWalletAddress?: `0x${string}`;
|
|
24
|
+
/** Alias for `buyerWalletAddress`. */
|
|
25
|
+
backingEOA?: `0x${string}`;
|
|
10
26
|
/** Circle CLI chain name. Rubicon real reads settle on Arc Testnet by default. */
|
|
11
27
|
chain?: string;
|
|
12
28
|
/** Circle CLI binary name or path. */
|
|
@@ -14,6 +30,12 @@ export interface CircleCliGatewayPaymentEngineOptions {
|
|
|
14
30
|
/** Command runner injection point for tests or hosted agent sandboxes. */
|
|
15
31
|
runner?: CircleCliRunner;
|
|
16
32
|
}
|
|
33
|
+
interface TypedDataRequest {
|
|
34
|
+
domain: Record<string, unknown>;
|
|
35
|
+
types: Record<string, unknown>;
|
|
36
|
+
primaryType: string;
|
|
37
|
+
message: Record<string, unknown>;
|
|
38
|
+
}
|
|
17
39
|
/**
|
|
18
40
|
* Circle CLI / Agent Wallet payment engine. It creates the one-word x402
|
|
19
41
|
* payment payload for Rubicon's session-first flow and delegates EIP-712
|
|
@@ -26,6 +48,27 @@ export declare class CircleCliGatewayPaymentEngine implements AgentPaymentEngine
|
|
|
26
48
|
constructor(options?: CircleCliGatewayPaymentEngineOptions);
|
|
27
49
|
createWordPayment(session: StartSessionResponse): Promise<StreamPaymentRequest>;
|
|
28
50
|
}
|
|
51
|
+
export declare class CircleCliGatewaySigner {
|
|
52
|
+
private readonly options;
|
|
53
|
+
agentWalletAddress: `0x${string}`;
|
|
54
|
+
address: `0x${string}`;
|
|
55
|
+
private resolved;
|
|
56
|
+
private resolving?;
|
|
57
|
+
constructor(options: {
|
|
58
|
+
agentWalletAddress?: `0x${string}`;
|
|
59
|
+
buyerWalletAddress?: `0x${string}`;
|
|
60
|
+
chain: string;
|
|
61
|
+
command: string;
|
|
62
|
+
runner: CircleCliRunner;
|
|
63
|
+
});
|
|
64
|
+
ensureAddress(): Promise<void>;
|
|
65
|
+
signTypedData(typed: TypedDataRequest): Promise<`0x${string}`>;
|
|
66
|
+
private resolveAddress;
|
|
67
|
+
private resolveAgentWalletAddress;
|
|
68
|
+
private resolveBackingEOA;
|
|
69
|
+
}
|
|
29
70
|
export declare function parseCircleCliSignature(output: string): `0x${string}`;
|
|
30
71
|
export declare function parseCircleCliWalletAddress(output: string): `0x${string}`;
|
|
72
|
+
export declare function parseCircleGatewayBackingEOA(output: string): `0x${string}`;
|
|
73
|
+
export {};
|
|
31
74
|
//# sourceMappingURL=circle-cli-gateway-payment.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"circle-cli-gateway-payment.d.ts","sourceRoot":"","sources":["../src/circle-cli-gateway-payment.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAIvF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAK9D,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAEnF,MAAM,WAAW,oCAAoC;IACnD
|
|
1
|
+
{"version":3,"file":"circle-cli-gateway-payment.d.ts","sourceRoot":"","sources":["../src/circle-cli-gateway-payment.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAIvF,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAK9D,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAEnF,MAAM,WAAW,oCAAoC;IACnD;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,KAAK,MAAM,EAAE,CAAC;IAC9B;;;OAGG;IACH,kBAAkB,CAAC,EAAE,KAAK,MAAM,EAAE,CAAC;IACnC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,KAAK,MAAM,EAAE,CAAC;IACnC,sCAAsC;IACtC,UAAU,CAAC,EAAE,KAAK,MAAM,EAAE,CAAC;IAC3B,kFAAkF;IAClF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,MAAM,CAAC,EAAE,eAAe,CAAC;CAC1B;AAED,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED;;;;;GAKG;AACH,qBAAa,6BAA8B,YAAW,kBAAkB;IACtE,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoB;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAyB;gBAEpC,OAAO,GAAE,oCAAyC;IAcxD,iBAAiB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAStF;AAED,qBAAa,sBAAsB;IAO/B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAN1B,kBAAkB,EAAE,KAAK,MAAM,EAAE,CAAgD;IACjF,OAAO,EAAE,KAAK,MAAM,EAAE,CAAgD;IACtE,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,SAAS,CAAC,CAAgB;gBAGf,OAAO,EAAE;QACxB,kBAAkB,CAAC,EAAE,KAAK,MAAM,EAAE,CAAC;QACnC,kBAAkB,CAAC,EAAE,KAAK,MAAM,EAAE,CAAC;QACnC,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,EAAE,eAAe,CAAC;KACzB;IAaG,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ9B,aAAa,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,KAAK,MAAM,EAAE,CAAC;YAgBtD,cAAc;YASd,yBAAyB;YAczB,iBAAiB;CAahC;AAgBD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK,MAAM,EAAE,CAiBrE;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK,MAAM,EAAE,CAezE;AAED,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK,MAAM,EAAE,CAO1E"}
|
|
@@ -16,7 +16,8 @@ export class CircleCliGatewayPaymentEngine {
|
|
|
16
16
|
signer;
|
|
17
17
|
constructor(options = {}) {
|
|
18
18
|
this.signer = new CircleCliGatewaySigner({
|
|
19
|
-
|
|
19
|
+
agentWalletAddress: options.agentWalletAddress ?? options.walletAddress,
|
|
20
|
+
buyerWalletAddress: options.buyerWalletAddress ?? options.backingEOA,
|
|
20
21
|
chain: options.chain ?? "ARC-TESTNET",
|
|
21
22
|
command: options.command ?? "circle",
|
|
22
23
|
runner: options.runner ?? runCircleCli,
|
|
@@ -36,15 +37,21 @@ export class CircleCliGatewayPaymentEngine {
|
|
|
36
37
|
};
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
|
-
class CircleCliGatewaySigner {
|
|
40
|
+
export class CircleCliGatewaySigner {
|
|
40
41
|
options;
|
|
42
|
+
agentWalletAddress = "0x0000000000000000000000000000000000000000";
|
|
41
43
|
address = "0x0000000000000000000000000000000000000000";
|
|
42
44
|
resolved = false;
|
|
43
45
|
resolving;
|
|
44
46
|
constructor(options) {
|
|
45
47
|
this.options = options;
|
|
46
|
-
if (options.
|
|
47
|
-
this.
|
|
48
|
+
if (options.agentWalletAddress) {
|
|
49
|
+
this.agentWalletAddress = options.agentWalletAddress;
|
|
50
|
+
}
|
|
51
|
+
if (options.buyerWalletAddress) {
|
|
52
|
+
this.address = options.buyerWalletAddress;
|
|
53
|
+
}
|
|
54
|
+
if (options.agentWalletAddress && options.buyerWalletAddress) {
|
|
48
55
|
this.resolved = true;
|
|
49
56
|
}
|
|
50
57
|
}
|
|
@@ -64,7 +71,7 @@ class CircleCliGatewaySigner {
|
|
|
64
71
|
"typed-data",
|
|
65
72
|
serializeTypedData(toEip712Payload(typed)),
|
|
66
73
|
"--address",
|
|
67
|
-
this.
|
|
74
|
+
this.agentWalletAddress,
|
|
68
75
|
"--chain",
|
|
69
76
|
this.options.chain,
|
|
70
77
|
"--quiet",
|
|
@@ -72,6 +79,13 @@ class CircleCliGatewaySigner {
|
|
|
72
79
|
return parseCircleCliSignature(signature);
|
|
73
80
|
}
|
|
74
81
|
async resolveAddress() {
|
|
82
|
+
const agentWalletAddress = this.options.agentWalletAddress ?? (await this.resolveAgentWalletAddress());
|
|
83
|
+
this.agentWalletAddress = agentWalletAddress;
|
|
84
|
+
this.address =
|
|
85
|
+
this.options.buyerWalletAddress ?? (await this.resolveBackingEOA(agentWalletAddress));
|
|
86
|
+
this.resolved = true;
|
|
87
|
+
}
|
|
88
|
+
async resolveAgentWalletAddress() {
|
|
75
89
|
const output = await this.options.runner(this.options.command, [
|
|
76
90
|
"wallet",
|
|
77
91
|
"list",
|
|
@@ -82,9 +96,20 @@ class CircleCliGatewaySigner {
|
|
|
82
96
|
"--output",
|
|
83
97
|
"json",
|
|
84
98
|
]);
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
99
|
+
return parseCircleCliWalletAddress(output);
|
|
100
|
+
}
|
|
101
|
+
async resolveBackingEOA(agentWalletAddress) {
|
|
102
|
+
const output = await this.options.runner(this.options.command, [
|
|
103
|
+
"gateway",
|
|
104
|
+
"balance",
|
|
105
|
+
"--address",
|
|
106
|
+
agentWalletAddress,
|
|
107
|
+
"--chain",
|
|
108
|
+
this.options.chain,
|
|
109
|
+
"--output",
|
|
110
|
+
"json",
|
|
111
|
+
]);
|
|
112
|
+
return parseCircleGatewayBackingEOA(output);
|
|
88
113
|
}
|
|
89
114
|
}
|
|
90
115
|
async function runCircleCli(command, args) {
|
|
@@ -130,7 +155,15 @@ export function parseCircleCliWalletAddress(output) {
|
|
|
130
155
|
if (unique.length === 0) {
|
|
131
156
|
throw new Error("Circle CLI did not return an Agent Wallet address");
|
|
132
157
|
}
|
|
133
|
-
throw new Error("Multiple Circle Agent Wallets found; pass
|
|
158
|
+
throw new Error("Multiple Circle Agent Wallets found; pass agentWalletAddress explicitly");
|
|
159
|
+
}
|
|
160
|
+
export function parseCircleGatewayBackingEOA(output) {
|
|
161
|
+
const parsed = parseJson(output);
|
|
162
|
+
const backingEOA = findString(parsed, ["data.backingEOA", "backingEOA"]);
|
|
163
|
+
if (backingEOA && isAddress(backingEOA)) {
|
|
164
|
+
return backingEOA;
|
|
165
|
+
}
|
|
166
|
+
throw new Error("Circle Gateway balance did not return a backingEOA address");
|
|
134
167
|
}
|
|
135
168
|
function collectWalletCandidates(value) {
|
|
136
169
|
if (Array.isArray(value)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"circle-cli-gateway-payment.js","sourceRoot":"","sources":["../src/circle-cli-gateway-payment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,EAAE,mBAAmB,EAAE,MAAM,kCAAkC,CAAC;AACvE,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAE/E,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"circle-cli-gateway-payment.js","sourceRoot":"","sources":["../src/circle-cli-gateway-payment.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { test } from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
|
-
import { parseCircleCliSignature, parseCircleCliWalletAddress, } from "./circle-cli-gateway-payment.js";
|
|
3
|
+
import { CircleCliGatewaySigner, parseCircleGatewayBackingEOA, parseCircleCliSignature, parseCircleCliWalletAddress, } from "./circle-cli-gateway-payment.js";
|
|
4
4
|
test("parses quiet Circle CLI signatures", () => {
|
|
5
5
|
assert.equal(parseCircleCliSignature("0xabc123\n"), "0xabc123");
|
|
6
6
|
});
|
|
@@ -21,6 +21,115 @@ test("parses a sole wallet address from Circle CLI list output", () => {
|
|
|
21
21
|
},
|
|
22
22
|
})), "0x1111111111111111111111111111111111111111");
|
|
23
23
|
});
|
|
24
|
+
test("parses backing EOA from Circle Gateway balance output", () => {
|
|
25
|
+
assert.equal(parseCircleGatewayBackingEOA(JSON.stringify({
|
|
26
|
+
data: {
|
|
27
|
+
backingEOA: "0x2222222222222222222222222222222222222222",
|
|
28
|
+
},
|
|
29
|
+
})), "0x2222222222222222222222222222222222222222");
|
|
30
|
+
});
|
|
31
|
+
test("separates Circle CLI Agent Wallet address from x402 backing EOA", async () => {
|
|
32
|
+
const calls = [];
|
|
33
|
+
const signer = new CircleCliGatewaySigner({
|
|
34
|
+
agentWalletAddress: "0x1111111111111111111111111111111111111111",
|
|
35
|
+
chain: "ARC-TESTNET",
|
|
36
|
+
command: "circle",
|
|
37
|
+
runner: async (_command, args) => {
|
|
38
|
+
calls.push(args);
|
|
39
|
+
assert.deepEqual(args, [
|
|
40
|
+
"gateway",
|
|
41
|
+
"balance",
|
|
42
|
+
"--address",
|
|
43
|
+
"0x1111111111111111111111111111111111111111",
|
|
44
|
+
"--chain",
|
|
45
|
+
"ARC-TESTNET",
|
|
46
|
+
"--output",
|
|
47
|
+
"json",
|
|
48
|
+
]);
|
|
49
|
+
return JSON.stringify({
|
|
50
|
+
data: {
|
|
51
|
+
backingEOA: "0x2222222222222222222222222222222222222222",
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
await signer.ensureAddress();
|
|
57
|
+
assert.equal(signer.agentWalletAddress, "0x1111111111111111111111111111111111111111");
|
|
58
|
+
assert.equal(signer.address, "0x2222222222222222222222222222222222222222");
|
|
59
|
+
assert.equal(calls.length, 1);
|
|
60
|
+
});
|
|
61
|
+
test("discovers sole Agent Wallet and then its Gateway backing EOA", async () => {
|
|
62
|
+
const calls = [];
|
|
63
|
+
const signer = new CircleCliGatewaySigner({
|
|
64
|
+
chain: "ARC-TESTNET",
|
|
65
|
+
command: "circle",
|
|
66
|
+
runner: async (_command, args) => {
|
|
67
|
+
calls.push(args);
|
|
68
|
+
if (args[0] === "wallet") {
|
|
69
|
+
return JSON.stringify({
|
|
70
|
+
data: {
|
|
71
|
+
wallets: [{ address: "0x1111111111111111111111111111111111111111" }],
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return JSON.stringify({
|
|
76
|
+
data: {
|
|
77
|
+
backingEOA: "0x2222222222222222222222222222222222222222",
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
await signer.ensureAddress();
|
|
83
|
+
assert.equal(signer.agentWalletAddress, "0x1111111111111111111111111111111111111111");
|
|
84
|
+
assert.equal(signer.address, "0x2222222222222222222222222222222222222222");
|
|
85
|
+
assert.deepEqual(calls, [
|
|
86
|
+
["wallet", "list", "--chain", "ARC-TESTNET", "--type", "agent", "--output", "json"],
|
|
87
|
+
[
|
|
88
|
+
"gateway",
|
|
89
|
+
"balance",
|
|
90
|
+
"--address",
|
|
91
|
+
"0x1111111111111111111111111111111111111111",
|
|
92
|
+
"--chain",
|
|
93
|
+
"ARC-TESTNET",
|
|
94
|
+
"--output",
|
|
95
|
+
"json",
|
|
96
|
+
],
|
|
97
|
+
]);
|
|
98
|
+
});
|
|
99
|
+
test("typed data message.from uses backing EOA while CLI signs with Agent Wallet", async () => {
|
|
100
|
+
let signedPayload;
|
|
101
|
+
const signer = new CircleCliGatewaySigner({
|
|
102
|
+
agentWalletAddress: "0x1111111111111111111111111111111111111111",
|
|
103
|
+
buyerWalletAddress: "0x2222222222222222222222222222222222222222",
|
|
104
|
+
chain: "ARC-TESTNET",
|
|
105
|
+
command: "circle",
|
|
106
|
+
runner: async (_command, args) => {
|
|
107
|
+
const addressFlag = args.indexOf("--address");
|
|
108
|
+
assert.equal(args[addressFlag + 1], "0x1111111111111111111111111111111111111111");
|
|
109
|
+
signedPayload = JSON.parse(args[3] ?? "{}");
|
|
110
|
+
return "0xabc123";
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
await signer.ensureAddress();
|
|
114
|
+
assert.equal(signer.address, "0x2222222222222222222222222222222222222222");
|
|
115
|
+
await signer.signTypedData({
|
|
116
|
+
domain: { name: "USD Coin", version: "2", chainId: 5042002 },
|
|
117
|
+
types: {
|
|
118
|
+
TransferWithAuthorization: [
|
|
119
|
+
{ name: "from", type: "address" },
|
|
120
|
+
{ name: "to", type: "address" },
|
|
121
|
+
{ name: "value", type: "uint256" },
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
primaryType: "TransferWithAuthorization",
|
|
125
|
+
message: {
|
|
126
|
+
from: signer.address,
|
|
127
|
+
to: "0x3333333333333333333333333333333333333333",
|
|
128
|
+
value: 1n,
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
assert.equal((signedPayload?.message).from, "0x2222222222222222222222222222222222222222");
|
|
132
|
+
});
|
|
24
133
|
test("requires explicit wallet address when multiple Agent Wallets are present", () => {
|
|
25
134
|
assert.throws(() => parseCircleCliWalletAddress(JSON.stringify({
|
|
26
135
|
data: [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"circle-cli-gateway-payment.test.js","sourceRoot":"","sources":["../src/circle-cli-gateway-payment.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EACL,uBAAuB,EACvB,2BAA2B,GAC5B,MAAM,iCAAiC,CAAC;AAEzC,IAAI,CAAC,oCAAoC,EAAE,GAAG,EAAE;IAC9C,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,YAAY,CAAC,EAAE,UAAU,CAAC,CAAC;AAClE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,mCAAmC,EAAE,GAAG,EAAE;IAC7C,MAAM,CAAC,KAAK,CACV,uBAAuB,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC,EAC5E,UAAU,CACX,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,yCAAyC,EAAE,GAAG,EAAE;IACnD,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,uBAAuB,CAAC,QAAQ,CAAC,EAAE,wCAAwC,CAAC,CAAC;AACnG,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,0DAA0D,EAAE,GAAG,EAAE;IACpE,MAAM,CAAC,KAAK,CACV,2BAA2B,CACzB,IAAI,CAAC,SAAS,CAAC;QACb,IAAI,EAAE;YACJ,OAAO,EAAE;gBACP;oBACE,OAAO,EAAE,4CAA4C;iBACtD;aACF;SACF;KACF,CAAC,CACH,EACD,4CAA4C,CAC7C,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,0EAA0E,EAAE,GAAG,EAAE;IACpF,MAAM,CAAC,MAAM,CACX,GAAG,EAAE,CACH,2BAA2B,CACzB,IAAI,CAAC,SAAS,CAAC;QACb,IAAI,EAAE;YACJ,EAAE,OAAO,EAAE,4CAA4C,EAAE;YACzD,EAAE,OAAO,EAAE,4CAA4C,EAAE;SAC1D;KACF,CAAC,CACH,EACH,qCAAqC,CACtC,CAAC;AACJ,CAAC,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"circle-cli-gateway-payment.test.js","sourceRoot":"","sources":["../src/circle-cli-gateway-payment.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { RubiconClient } from "./agent-client.js";
|
|
4
|
+
import type { AgentPaymentEngine } from "./payment-engine.js";
|
|
5
|
+
|
|
6
|
+
const paymentEngine: AgentPaymentEngine = {
|
|
7
|
+
async createWordPayment() {
|
|
8
|
+
return { paymentPayload: { ok: true } };
|
|
9
|
+
},
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
test("run receipt preserves Gateway settlement receipt fields", async () => {
|
|
13
|
+
const fetcher = (async (input: Parameters<typeof fetch>[0]) => {
|
|
14
|
+
const url = String(input);
|
|
15
|
+
if (url.endsWith("/v1/sessions")) {
|
|
16
|
+
return jsonResponse({
|
|
17
|
+
sessionId: "session_1",
|
|
18
|
+
state: "active",
|
|
19
|
+
article: article(),
|
|
20
|
+
navigation: navigation(),
|
|
21
|
+
pricePerWordAtomic: "1",
|
|
22
|
+
maxArticlePriceAtomic: "10",
|
|
23
|
+
conversationId: "conversation_1",
|
|
24
|
+
wordPaymentAtomic: "1",
|
|
25
|
+
gatewayFeeBps: 0,
|
|
26
|
+
paymentRequired: { scheme: "exact" },
|
|
27
|
+
expiresAt: "2026-06-18T12:00:00.000Z",
|
|
28
|
+
wordsPaid: 0,
|
|
29
|
+
wordsDelivered: 0,
|
|
30
|
+
paidAtomic: "0",
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
if (url.endsWith("/v1/sessions/session_1/payments")) {
|
|
34
|
+
return jsonResponse({
|
|
35
|
+
accepted: true,
|
|
36
|
+
sequence: 0,
|
|
37
|
+
word: "Rubicon",
|
|
38
|
+
priceAtomic: "1",
|
|
39
|
+
wordsPaid: 1,
|
|
40
|
+
wordsDelivered: 1,
|
|
41
|
+
paidAtomic: "1",
|
|
42
|
+
completed: true,
|
|
43
|
+
transactionHashes: [],
|
|
44
|
+
settlementIds: ["settlement_1"],
|
|
45
|
+
payment: {
|
|
46
|
+
paymentId: "payment_1",
|
|
47
|
+
sessionId: "session_1",
|
|
48
|
+
articleId: "article_1",
|
|
49
|
+
sequence: 0,
|
|
50
|
+
meteringUnit: "word",
|
|
51
|
+
amountAtomic: "1",
|
|
52
|
+
currency: "USDC",
|
|
53
|
+
network: "eip155:5042002",
|
|
54
|
+
payTo: "0x3333333333333333333333333333333333333333",
|
|
55
|
+
transactionHashes: [],
|
|
56
|
+
settlementIds: ["settlement_1"],
|
|
57
|
+
buyerWalletAddress: "0x2222222222222222222222222222222222222222",
|
|
58
|
+
settledAt: "2026-06-18T12:00:00.000Z",
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
throw new Error(`Unexpected fetch: ${url}`);
|
|
63
|
+
}) as typeof fetch;
|
|
64
|
+
|
|
65
|
+
const client = new RubiconClient({
|
|
66
|
+
baseUrl: "http://rubicon.test",
|
|
67
|
+
paymentEngine,
|
|
68
|
+
fetch: fetcher,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const receipt = await client.run({
|
|
72
|
+
articleId: "article_1",
|
|
73
|
+
maxSpendAtomic: "10",
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
assert.deepEqual(receipt.transactionHashes, []);
|
|
77
|
+
assert.deepEqual(receipt.settlementIds, ["settlement_1"]);
|
|
78
|
+
assert.equal(receipt.buyerWalletAddress, "0x2222222222222222222222222222222222222222");
|
|
79
|
+
assert.equal(receipt.sellerPayTo, "0x3333333333333333333333333333333333333333");
|
|
80
|
+
assert.equal(receipt.network, "eip155:5042002");
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
function jsonResponse(body: unknown): Response {
|
|
84
|
+
return new Response(JSON.stringify(body), {
|
|
85
|
+
status: 200,
|
|
86
|
+
headers: { "content-type": "application/json" },
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function article() {
|
|
91
|
+
return {
|
|
92
|
+
articleId: "article_1",
|
|
93
|
+
creatorId: "creator_1",
|
|
94
|
+
creatorUsername: "creator",
|
|
95
|
+
title: "Title",
|
|
96
|
+
author: "Author",
|
|
97
|
+
state: "published",
|
|
98
|
+
totalWords: 1,
|
|
99
|
+
pricePerWordAtomic: "1",
|
|
100
|
+
maxArticlePriceAtomic: "1",
|
|
101
|
+
sections: [],
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function navigation() {
|
|
106
|
+
return {
|
|
107
|
+
articleId: "article_1",
|
|
108
|
+
sections: [],
|
|
109
|
+
sellerAgent: {
|
|
110
|
+
recommendedSectionId: "intro",
|
|
111
|
+
alternativeSectionIds: [],
|
|
112
|
+
rationale: "",
|
|
113
|
+
safeHints: [],
|
|
114
|
+
withheld: [],
|
|
115
|
+
},
|
|
116
|
+
stopConditions: [],
|
|
117
|
+
};
|
|
118
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { test } from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
3
|
import {
|
|
4
|
+
CircleCliGatewaySigner,
|
|
5
|
+
parseCircleGatewayBackingEOA,
|
|
4
6
|
parseCircleCliSignature,
|
|
5
7
|
parseCircleCliWalletAddress,
|
|
6
8
|
} from "./circle-cli-gateway-payment.js";
|
|
@@ -37,6 +39,133 @@ test("parses a sole wallet address from Circle CLI list output", () => {
|
|
|
37
39
|
);
|
|
38
40
|
});
|
|
39
41
|
|
|
42
|
+
test("parses backing EOA from Circle Gateway balance output", () => {
|
|
43
|
+
assert.equal(
|
|
44
|
+
parseCircleGatewayBackingEOA(
|
|
45
|
+
JSON.stringify({
|
|
46
|
+
data: {
|
|
47
|
+
backingEOA: "0x2222222222222222222222222222222222222222",
|
|
48
|
+
},
|
|
49
|
+
}),
|
|
50
|
+
),
|
|
51
|
+
"0x2222222222222222222222222222222222222222",
|
|
52
|
+
);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("separates Circle CLI Agent Wallet address from x402 backing EOA", async () => {
|
|
56
|
+
const calls: string[][] = [];
|
|
57
|
+
const signer = new CircleCliGatewaySigner({
|
|
58
|
+
agentWalletAddress: "0x1111111111111111111111111111111111111111",
|
|
59
|
+
chain: "ARC-TESTNET",
|
|
60
|
+
command: "circle",
|
|
61
|
+
runner: async (_command, args) => {
|
|
62
|
+
calls.push(args);
|
|
63
|
+
assert.deepEqual(args, [
|
|
64
|
+
"gateway",
|
|
65
|
+
"balance",
|
|
66
|
+
"--address",
|
|
67
|
+
"0x1111111111111111111111111111111111111111",
|
|
68
|
+
"--chain",
|
|
69
|
+
"ARC-TESTNET",
|
|
70
|
+
"--output",
|
|
71
|
+
"json",
|
|
72
|
+
]);
|
|
73
|
+
return JSON.stringify({
|
|
74
|
+
data: {
|
|
75
|
+
backingEOA: "0x2222222222222222222222222222222222222222",
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
await signer.ensureAddress();
|
|
82
|
+
|
|
83
|
+
assert.equal(signer.agentWalletAddress, "0x1111111111111111111111111111111111111111");
|
|
84
|
+
assert.equal(signer.address, "0x2222222222222222222222222222222222222222");
|
|
85
|
+
assert.equal(calls.length, 1);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("discovers sole Agent Wallet and then its Gateway backing EOA", async () => {
|
|
89
|
+
const calls: string[][] = [];
|
|
90
|
+
const signer = new CircleCliGatewaySigner({
|
|
91
|
+
chain: "ARC-TESTNET",
|
|
92
|
+
command: "circle",
|
|
93
|
+
runner: async (_command, args) => {
|
|
94
|
+
calls.push(args);
|
|
95
|
+
if (args[0] === "wallet") {
|
|
96
|
+
return JSON.stringify({
|
|
97
|
+
data: {
|
|
98
|
+
wallets: [{ address: "0x1111111111111111111111111111111111111111" }],
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
return JSON.stringify({
|
|
103
|
+
data: {
|
|
104
|
+
backingEOA: "0x2222222222222222222222222222222222222222",
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
await signer.ensureAddress();
|
|
111
|
+
|
|
112
|
+
assert.equal(signer.agentWalletAddress, "0x1111111111111111111111111111111111111111");
|
|
113
|
+
assert.equal(signer.address, "0x2222222222222222222222222222222222222222");
|
|
114
|
+
assert.deepEqual(calls, [
|
|
115
|
+
["wallet", "list", "--chain", "ARC-TESTNET", "--type", "agent", "--output", "json"],
|
|
116
|
+
[
|
|
117
|
+
"gateway",
|
|
118
|
+
"balance",
|
|
119
|
+
"--address",
|
|
120
|
+
"0x1111111111111111111111111111111111111111",
|
|
121
|
+
"--chain",
|
|
122
|
+
"ARC-TESTNET",
|
|
123
|
+
"--output",
|
|
124
|
+
"json",
|
|
125
|
+
],
|
|
126
|
+
]);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("typed data message.from uses backing EOA while CLI signs with Agent Wallet", async () => {
|
|
130
|
+
let signedPayload: Record<string, unknown> | undefined;
|
|
131
|
+
const signer = new CircleCliGatewaySigner({
|
|
132
|
+
agentWalletAddress: "0x1111111111111111111111111111111111111111",
|
|
133
|
+
buyerWalletAddress: "0x2222222222222222222222222222222222222222",
|
|
134
|
+
chain: "ARC-TESTNET",
|
|
135
|
+
command: "circle",
|
|
136
|
+
runner: async (_command, args) => {
|
|
137
|
+
const addressFlag = args.indexOf("--address");
|
|
138
|
+
assert.equal(args[addressFlag + 1], "0x1111111111111111111111111111111111111111");
|
|
139
|
+
signedPayload = JSON.parse(args[3] ?? "{}") as Record<string, unknown>;
|
|
140
|
+
return "0xabc123";
|
|
141
|
+
},
|
|
142
|
+
});
|
|
143
|
+
await signer.ensureAddress();
|
|
144
|
+
assert.equal(signer.address, "0x2222222222222222222222222222222222222222");
|
|
145
|
+
|
|
146
|
+
await signer.signTypedData({
|
|
147
|
+
domain: { name: "USD Coin", version: "2", chainId: 5042002 },
|
|
148
|
+
types: {
|
|
149
|
+
TransferWithAuthorization: [
|
|
150
|
+
{ name: "from", type: "address" },
|
|
151
|
+
{ name: "to", type: "address" },
|
|
152
|
+
{ name: "value", type: "uint256" },
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
primaryType: "TransferWithAuthorization",
|
|
156
|
+
message: {
|
|
157
|
+
from: signer.address,
|
|
158
|
+
to: "0x3333333333333333333333333333333333333333",
|
|
159
|
+
value: 1n,
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
assert.equal(
|
|
164
|
+
(signedPayload?.message as Record<string, unknown>).from,
|
|
165
|
+
"0x2222222222222222222222222222222222222222",
|
|
166
|
+
);
|
|
167
|
+
});
|
|
168
|
+
|
|
40
169
|
test("requires explicit wallet address when multiple Agent Wallets are present", () => {
|
|
41
170
|
assert.throws(
|
|
42
171
|
() =>
|
|
@@ -15,8 +15,24 @@ export interface CircleCliGatewayPaymentEngineOptions {
|
|
|
15
15
|
/**
|
|
16
16
|
* Agent Wallet address controlled by Circle CLI. When omitted, the engine
|
|
17
17
|
* resolves the sole agent wallet returned by `circle wallet list`.
|
|
18
|
+
*
|
|
19
|
+
* @deprecated Use `agentWalletAddress`. This alias is kept for existing SDK
|
|
20
|
+
* callers and is treated as the Circle CLI signing wallet, not the x402
|
|
21
|
+
* authorization `from` address.
|
|
18
22
|
*/
|
|
19
23
|
walletAddress?: `0x${string}`;
|
|
24
|
+
/**
|
|
25
|
+
* Agent Wallet address controlled by Circle CLI. This is passed to
|
|
26
|
+
* `circle wallet sign typed-data --address`.
|
|
27
|
+
*/
|
|
28
|
+
agentWalletAddress?: `0x${string}`;
|
|
29
|
+
/**
|
|
30
|
+
* Gateway backing EOA used as the x402 authorization `from` address. When
|
|
31
|
+
* omitted, the engine resolves it from `circle gateway balance`.
|
|
32
|
+
*/
|
|
33
|
+
buyerWalletAddress?: `0x${string}`;
|
|
34
|
+
/** Alias for `buyerWalletAddress`. */
|
|
35
|
+
backingEOA?: `0x${string}`;
|
|
20
36
|
/** Circle CLI chain name. Rubicon real reads settle on Arc Testnet by default. */
|
|
21
37
|
chain?: string;
|
|
22
38
|
/** Circle CLI binary name or path. */
|
|
@@ -44,7 +60,8 @@ export class CircleCliGatewayPaymentEngine implements AgentPaymentEngine {
|
|
|
44
60
|
|
|
45
61
|
constructor(options: CircleCliGatewayPaymentEngineOptions = {}) {
|
|
46
62
|
this.signer = new CircleCliGatewaySigner({
|
|
47
|
-
|
|
63
|
+
agentWalletAddress: options.agentWalletAddress ?? options.walletAddress,
|
|
64
|
+
buyerWalletAddress: options.buyerWalletAddress ?? options.backingEOA,
|
|
48
65
|
chain: options.chain ?? "ARC-TESTNET",
|
|
49
66
|
command: options.command ?? "circle",
|
|
50
67
|
runner: options.runner ?? runCircleCli,
|
|
@@ -66,21 +83,28 @@ export class CircleCliGatewayPaymentEngine implements AgentPaymentEngine {
|
|
|
66
83
|
}
|
|
67
84
|
}
|
|
68
85
|
|
|
69
|
-
class CircleCliGatewaySigner {
|
|
86
|
+
export class CircleCliGatewaySigner {
|
|
87
|
+
agentWalletAddress: `0x${string}` = "0x0000000000000000000000000000000000000000";
|
|
70
88
|
address: `0x${string}` = "0x0000000000000000000000000000000000000000";
|
|
71
89
|
private resolved = false;
|
|
72
90
|
private resolving?: Promise<void>;
|
|
73
91
|
|
|
74
92
|
constructor(
|
|
75
93
|
private readonly options: {
|
|
76
|
-
|
|
94
|
+
agentWalletAddress?: `0x${string}`;
|
|
95
|
+
buyerWalletAddress?: `0x${string}`;
|
|
77
96
|
chain: string;
|
|
78
97
|
command: string;
|
|
79
98
|
runner: CircleCliRunner;
|
|
80
99
|
},
|
|
81
100
|
) {
|
|
82
|
-
if (options.
|
|
83
|
-
this.
|
|
101
|
+
if (options.agentWalletAddress) {
|
|
102
|
+
this.agentWalletAddress = options.agentWalletAddress;
|
|
103
|
+
}
|
|
104
|
+
if (options.buyerWalletAddress) {
|
|
105
|
+
this.address = options.buyerWalletAddress;
|
|
106
|
+
}
|
|
107
|
+
if (options.agentWalletAddress && options.buyerWalletAddress) {
|
|
84
108
|
this.resolved = true;
|
|
85
109
|
}
|
|
86
110
|
}
|
|
@@ -101,7 +125,7 @@ class CircleCliGatewaySigner {
|
|
|
101
125
|
"typed-data",
|
|
102
126
|
serializeTypedData(toEip712Payload(typed)),
|
|
103
127
|
"--address",
|
|
104
|
-
this.
|
|
128
|
+
this.agentWalletAddress,
|
|
105
129
|
"--chain",
|
|
106
130
|
this.options.chain,
|
|
107
131
|
"--quiet",
|
|
@@ -110,6 +134,15 @@ class CircleCliGatewaySigner {
|
|
|
110
134
|
}
|
|
111
135
|
|
|
112
136
|
private async resolveAddress(): Promise<void> {
|
|
137
|
+
const agentWalletAddress =
|
|
138
|
+
this.options.agentWalletAddress ?? (await this.resolveAgentWalletAddress());
|
|
139
|
+
this.agentWalletAddress = agentWalletAddress;
|
|
140
|
+
this.address =
|
|
141
|
+
this.options.buyerWalletAddress ?? (await this.resolveBackingEOA(agentWalletAddress));
|
|
142
|
+
this.resolved = true;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private async resolveAgentWalletAddress(): Promise<`0x${string}`> {
|
|
113
146
|
const output = await this.options.runner(this.options.command, [
|
|
114
147
|
"wallet",
|
|
115
148
|
"list",
|
|
@@ -120,9 +153,21 @@ class CircleCliGatewaySigner {
|
|
|
120
153
|
"--output",
|
|
121
154
|
"json",
|
|
122
155
|
]);
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
156
|
+
return parseCircleCliWalletAddress(output);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
private async resolveBackingEOA(agentWalletAddress: `0x${string}`): Promise<`0x${string}`> {
|
|
160
|
+
const output = await this.options.runner(this.options.command, [
|
|
161
|
+
"gateway",
|
|
162
|
+
"balance",
|
|
163
|
+
"--address",
|
|
164
|
+
agentWalletAddress,
|
|
165
|
+
"--chain",
|
|
166
|
+
this.options.chain,
|
|
167
|
+
"--output",
|
|
168
|
+
"json",
|
|
169
|
+
]);
|
|
170
|
+
return parseCircleGatewayBackingEOA(output);
|
|
126
171
|
}
|
|
127
172
|
}
|
|
128
173
|
|
|
@@ -173,7 +218,16 @@ export function parseCircleCliWalletAddress(output: string): `0x${string}` {
|
|
|
173
218
|
if (unique.length === 0) {
|
|
174
219
|
throw new Error("Circle CLI did not return an Agent Wallet address");
|
|
175
220
|
}
|
|
176
|
-
throw new Error("Multiple Circle Agent Wallets found; pass
|
|
221
|
+
throw new Error("Multiple Circle Agent Wallets found; pass agentWalletAddress explicitly");
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function parseCircleGatewayBackingEOA(output: string): `0x${string}` {
|
|
225
|
+
const parsed = parseJson(output);
|
|
226
|
+
const backingEOA = findString(parsed, ["data.backingEOA", "backingEOA"]);
|
|
227
|
+
if (backingEOA && isAddress(backingEOA)) {
|
|
228
|
+
return backingEOA;
|
|
229
|
+
}
|
|
230
|
+
throw new Error("Circle Gateway balance did not return a backingEOA address");
|
|
177
231
|
}
|
|
178
232
|
|
|
179
233
|
function collectWalletCandidates(value: unknown): Record<string, unknown>[] {
|