@rubicon-caliga/agent-sdk 0.1.0 → 0.1.1
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 +51 -7
- package/dist/agent-client.d.ts +4 -0
- package/dist/agent-client.d.ts.map +1 -1
- package/dist/agent-client.js +6 -0
- package/dist/agent-client.js.map +1 -1
- package/dist/circle-agent-wallet.d.ts +65 -0
- package/dist/circle-agent-wallet.d.ts.map +1 -0
- package/dist/circle-agent-wallet.js +156 -0
- package/dist/circle-agent-wallet.js.map +1 -0
- package/dist/circle-agent-wallet.test.d.ts +2 -0
- package/dist/circle-agent-wallet.test.d.ts.map +1 -0
- package/dist/circle-agent-wallet.test.js +106 -0
- package/dist/circle-agent-wallet.test.js.map +1 -0
- package/dist/circle-cli-gateway-payment.d.ts +31 -0
- package/dist/circle-cli-gateway-payment.d.ts.map +1 -0
- package/dist/circle-cli-gateway-payment.js +185 -0
- package/dist/circle-cli-gateway-payment.js.map +1 -0
- package/dist/circle-cli-gateway-payment.test.d.ts +2 -0
- package/dist/circle-cli-gateway-payment.test.d.ts.map +1 -0
- package/dist/circle-cli-gateway-payment.test.js +32 -0
- package/dist/circle-cli-gateway-payment.test.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/payment-engine.d.ts +0 -14
- package/dist/payment-engine.d.ts.map +1 -1
- package/dist/payment-engine.js +0 -35
- package/dist/payment-engine.js.map +1 -1
- package/dist/payment-engine.test.d.ts.map +1 -0
- package/package.json +11 -11
- package/src/agent-client.ts +10 -0
- package/src/circle-agent-wallet.test.ts +118 -0
- package/src/circle-agent-wallet.ts +210 -0
- package/src/circle-cli-gateway-payment.test.ts +53 -0
- package/src/circle-cli-gateway-payment.ts +230 -0
- package/src/index.ts +2 -0
- package/src/payment-engine.ts +0 -38
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { promisify } from "node:util";
|
|
3
|
+
import { registerBatchScheme } from "@circle-fin/x402-batching/client";
|
|
4
|
+
import { x402Client } from "@x402/core/client";
|
|
5
|
+
import { ExactEvmScheme } from "@x402/evm/exact/client";
|
|
6
|
+
import { serializeTypedData, toEip712Payload } from "./circle-agent-wallet.js";
|
|
7
|
+
const execFileAsync = promisify(execFile);
|
|
8
|
+
/**
|
|
9
|
+
* Circle CLI / Agent Wallet payment engine. It creates the one-word x402
|
|
10
|
+
* payment payload for Rubicon's session-first flow and delegates EIP-712
|
|
11
|
+
* signing to `circle wallet sign typed-data`, so agents never need raw private
|
|
12
|
+
* keys or hand-built x402 payloads.
|
|
13
|
+
*/
|
|
14
|
+
export class CircleCliGatewayPaymentEngine {
|
|
15
|
+
x402 = new x402Client();
|
|
16
|
+
signer;
|
|
17
|
+
constructor(options = {}) {
|
|
18
|
+
this.signer = new CircleCliGatewaySigner({
|
|
19
|
+
walletAddress: options.walletAddress,
|
|
20
|
+
chain: options.chain ?? "ARC-TESTNET",
|
|
21
|
+
command: options.command ?? "circle",
|
|
22
|
+
runner: options.runner ?? runCircleCli,
|
|
23
|
+
});
|
|
24
|
+
registerBatchScheme(this.x402, {
|
|
25
|
+
signer: this.signer,
|
|
26
|
+
fallbackScheme: new ExactEvmScheme(this.signer),
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
async createWordPayment(session) {
|
|
30
|
+
if (!session.paymentRequired) {
|
|
31
|
+
throw new Error("Session did not include an x402 one-word payment requirement");
|
|
32
|
+
}
|
|
33
|
+
await this.signer.ensureAddress();
|
|
34
|
+
return {
|
|
35
|
+
paymentPayload: await this.x402.createPaymentPayload(session.paymentRequired),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
class CircleCliGatewaySigner {
|
|
40
|
+
options;
|
|
41
|
+
address = "0x0000000000000000000000000000000000000000";
|
|
42
|
+
resolved = false;
|
|
43
|
+
resolving;
|
|
44
|
+
constructor(options) {
|
|
45
|
+
this.options = options;
|
|
46
|
+
if (options.walletAddress) {
|
|
47
|
+
this.address = options.walletAddress;
|
|
48
|
+
this.resolved = true;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async ensureAddress() {
|
|
52
|
+
if (this.resolved)
|
|
53
|
+
return;
|
|
54
|
+
if (!this.resolving) {
|
|
55
|
+
this.resolving = this.resolveAddress();
|
|
56
|
+
}
|
|
57
|
+
return this.resolving;
|
|
58
|
+
}
|
|
59
|
+
async signTypedData(typed) {
|
|
60
|
+
await this.ensureAddress();
|
|
61
|
+
const signature = await this.options.runner(this.options.command, [
|
|
62
|
+
"wallet",
|
|
63
|
+
"sign",
|
|
64
|
+
"typed-data",
|
|
65
|
+
serializeTypedData(toEip712Payload(typed)),
|
|
66
|
+
"--address",
|
|
67
|
+
this.address,
|
|
68
|
+
"--chain",
|
|
69
|
+
this.options.chain,
|
|
70
|
+
"--quiet",
|
|
71
|
+
]);
|
|
72
|
+
return parseCircleCliSignature(signature);
|
|
73
|
+
}
|
|
74
|
+
async resolveAddress() {
|
|
75
|
+
const output = await this.options.runner(this.options.command, [
|
|
76
|
+
"wallet",
|
|
77
|
+
"list",
|
|
78
|
+
"--chain",
|
|
79
|
+
this.options.chain,
|
|
80
|
+
"--type",
|
|
81
|
+
"agent",
|
|
82
|
+
"--output",
|
|
83
|
+
"json",
|
|
84
|
+
]);
|
|
85
|
+
const address = parseCircleCliWalletAddress(output);
|
|
86
|
+
this.address = address;
|
|
87
|
+
this.resolved = true;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async function runCircleCli(command, args) {
|
|
91
|
+
try {
|
|
92
|
+
const { stdout } = await execFileAsync(command, args, {
|
|
93
|
+
maxBuffer: 1024 * 1024,
|
|
94
|
+
});
|
|
95
|
+
return stdout;
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
99
|
+
throw new Error(`Circle CLI command failed. Ensure Circle CLI is installed, logged in, and has an Agent Wallet on the selected chain. ${message}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export function parseCircleCliSignature(output) {
|
|
103
|
+
const trimmed = output.trim();
|
|
104
|
+
if (isHexSignature(trimmed)) {
|
|
105
|
+
return trimmed;
|
|
106
|
+
}
|
|
107
|
+
let parsed;
|
|
108
|
+
try {
|
|
109
|
+
parsed = parseJson(trimmed);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
throw new Error("Circle CLI did not return a hex EIP-712 signature");
|
|
113
|
+
}
|
|
114
|
+
const signature = findString(parsed, ["signature", "signedData", "data.signature"]);
|
|
115
|
+
if (signature && isHexSignature(signature)) {
|
|
116
|
+
return signature;
|
|
117
|
+
}
|
|
118
|
+
throw new Error("Circle CLI did not return a hex EIP-712 signature");
|
|
119
|
+
}
|
|
120
|
+
export function parseCircleCliWalletAddress(output) {
|
|
121
|
+
const parsed = parseJson(output);
|
|
122
|
+
const wallets = collectWalletCandidates(parsed);
|
|
123
|
+
const addresses = wallets
|
|
124
|
+
.map((wallet) => findString(wallet, ["address", "walletAddress", "blockchainAddress"]))
|
|
125
|
+
.filter((address) => Boolean(address && isAddress(address)));
|
|
126
|
+
const unique = [...new Set(addresses.map((address) => address.toLowerCase()))];
|
|
127
|
+
if (unique.length === 1) {
|
|
128
|
+
return addresses.find((address) => address.toLowerCase() === unique[0]);
|
|
129
|
+
}
|
|
130
|
+
if (unique.length === 0) {
|
|
131
|
+
throw new Error("Circle CLI did not return an Agent Wallet address");
|
|
132
|
+
}
|
|
133
|
+
throw new Error("Multiple Circle Agent Wallets found; pass walletAddress explicitly");
|
|
134
|
+
}
|
|
135
|
+
function collectWalletCandidates(value) {
|
|
136
|
+
if (Array.isArray(value)) {
|
|
137
|
+
return value.filter(isRecord);
|
|
138
|
+
}
|
|
139
|
+
if (!isRecord(value)) {
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
for (const key of ["wallets", "items", "data"]) {
|
|
143
|
+
const nested = value[key];
|
|
144
|
+
if (Array.isArray(nested)) {
|
|
145
|
+
return nested.filter(isRecord);
|
|
146
|
+
}
|
|
147
|
+
if (isRecord(nested)) {
|
|
148
|
+
const deeper = collectWalletCandidates(nested);
|
|
149
|
+
if (deeper.length > 0)
|
|
150
|
+
return deeper;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return [value];
|
|
154
|
+
}
|
|
155
|
+
function findString(value, keys) {
|
|
156
|
+
for (const key of keys) {
|
|
157
|
+
const found = key.split(".").reduce((current, part) => {
|
|
158
|
+
if (!isRecord(current))
|
|
159
|
+
return undefined;
|
|
160
|
+
return current[part];
|
|
161
|
+
}, value);
|
|
162
|
+
if (typeof found === "string") {
|
|
163
|
+
return found;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return undefined;
|
|
167
|
+
}
|
|
168
|
+
function parseJson(value) {
|
|
169
|
+
try {
|
|
170
|
+
return JSON.parse(value);
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
throw new Error("Circle CLI returned non-JSON output");
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
function isHexSignature(value) {
|
|
177
|
+
return /^0x[0-9a-fA-F]+$/.test(value);
|
|
178
|
+
}
|
|
179
|
+
function isAddress(value) {
|
|
180
|
+
return /^0x[0-9a-fA-F]{40}$/.test(value);
|
|
181
|
+
}
|
|
182
|
+
function isRecord(value) {
|
|
183
|
+
return typeof value === "object" && value !== null;
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=circle-cli-gateway-payment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circle-cli-gateway-payment.js","sourceRoot":"","sources":["../src/circle-cli-gateway-payment.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circle-cli-gateway-payment.test.d.ts","sourceRoot":"","sources":["../src/circle-cli-gateway-payment.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { parseCircleCliSignature, parseCircleCliWalletAddress, } from "./circle-cli-gateway-payment.js";
|
|
4
|
+
test("parses quiet Circle CLI signatures", () => {
|
|
5
|
+
assert.equal(parseCircleCliSignature("0xabc123\n"), "0xabc123");
|
|
6
|
+
});
|
|
7
|
+
test("parses JSON Circle CLI signatures", () => {
|
|
8
|
+
assert.equal(parseCircleCliSignature(JSON.stringify({ data: { signature: "0xdef456" } })), "0xdef456");
|
|
9
|
+
});
|
|
10
|
+
test("rejects non-signature Circle CLI output", () => {
|
|
11
|
+
assert.throws(() => parseCircleCliSignature("signed"), /did not return a hex EIP-712 signature/);
|
|
12
|
+
});
|
|
13
|
+
test("parses a sole wallet address from Circle CLI list output", () => {
|
|
14
|
+
assert.equal(parseCircleCliWalletAddress(JSON.stringify({
|
|
15
|
+
data: {
|
|
16
|
+
wallets: [
|
|
17
|
+
{
|
|
18
|
+
address: "0x1111111111111111111111111111111111111111",
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
},
|
|
22
|
+
})), "0x1111111111111111111111111111111111111111");
|
|
23
|
+
});
|
|
24
|
+
test("requires explicit wallet address when multiple Agent Wallets are present", () => {
|
|
25
|
+
assert.throws(() => parseCircleCliWalletAddress(JSON.stringify({
|
|
26
|
+
data: [
|
|
27
|
+
{ address: "0x1111111111111111111111111111111111111111" },
|
|
28
|
+
{ address: "0x2222222222222222222222222222222222222222" },
|
|
29
|
+
],
|
|
30
|
+
})), /Multiple Circle Agent Wallets found/);
|
|
31
|
+
});
|
|
32
|
+
//# sourceMappingURL=circle-cli-gateway-payment.test.js.map
|
|
@@ -0,0 +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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export * from "./agent-client.js";
|
|
2
2
|
export * from "./payment-engine.js";
|
|
3
|
+
export * from "./circle-agent-wallet.js";
|
|
4
|
+
export * from "./circle-cli-gateway-payment.js";
|
|
3
5
|
export { RubiconClient as Rubicon, RubiconClient as default } from "./agent-client.js";
|
|
4
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,aAAa,IAAI,OAAO,EAAE,aAAa,IAAI,OAAO,EAAE,MAAM,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,cAAc,iCAAiC,CAAC;AAChD,OAAO,EAAE,aAAa,IAAI,OAAO,EAAE,aAAa,IAAI,OAAO,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export * from "./agent-client.js";
|
|
2
2
|
export * from "./payment-engine.js";
|
|
3
|
+
export * from "./circle-agent-wallet.js";
|
|
4
|
+
export * from "./circle-cli-gateway-payment.js";
|
|
3
5
|
export { RubiconClient as Rubicon, RubiconClient as default } from "./agent-client.js";
|
|
4
6
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,aAAa,IAAI,OAAO,EAAE,aAAa,IAAI,OAAO,EAAE,MAAM,mBAAmB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC;AACzC,cAAc,iCAAiC,CAAC;AAChD,OAAO,EAAE,aAAa,IAAI,OAAO,EAAE,aAAa,IAAI,OAAO,EAAE,MAAM,mBAAmB,CAAC"}
|
package/dist/payment-engine.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { StartSessionResponse, StreamPaymentRequest } from "@rubicon-caliga/core";
|
|
2
|
-
import { type GatewayClientConfig } from "@circle-fin/x402-batching/client";
|
|
3
2
|
/**
|
|
4
3
|
* Produces the payment payload for exactly one word. Called once per word by the
|
|
5
4
|
* SDK's read loop — application developers never assemble payments themselves.
|
|
@@ -16,17 +15,4 @@ export declare class StaticPaymentEngine implements AgentPaymentEngine {
|
|
|
16
15
|
constructor(network?: string);
|
|
17
16
|
createWordPayment(session: StartSessionResponse): Promise<StreamPaymentRequest>;
|
|
18
17
|
}
|
|
19
|
-
export type CircleGatewayPaymentEngineOptions = GatewayClientConfig;
|
|
20
|
-
/**
|
|
21
|
-
* Circle/x402 engine. Signs the gateway's one-word `paymentRequired` terms.
|
|
22
|
-
* Circle may batch settlement internally, but each signed payload corresponds to
|
|
23
|
-
* exactly one word.
|
|
24
|
-
*/
|
|
25
|
-
export declare class CircleGatewayPaymentEngine implements AgentPaymentEngine {
|
|
26
|
-
private readonly options;
|
|
27
|
-
private readonly client;
|
|
28
|
-
private readonly account;
|
|
29
|
-
constructor(options: CircleGatewayPaymentEngineOptions);
|
|
30
|
-
createWordPayment(session: StartSessionResponse): Promise<StreamPaymentRequest>;
|
|
31
|
-
}
|
|
32
18
|
//# sourceMappingURL=payment-engine.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"payment-engine.d.ts","sourceRoot":"","sources":["../src/payment-engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAEvF
|
|
1
|
+
{"version":3,"file":"payment-engine.d.ts","sourceRoot":"","sources":["../src/payment-engine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAEvF;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,iBAAiB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;CACjF;AAED;;;GAGG;AACH,qBAAa,mBAAoB,YAAW,kBAAkB;IAChD,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,SAAmB;IAEjD,iBAAiB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAWtF"}
|
package/dist/payment-engine.js
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import { x402Client } from "@x402/core/client";
|
|
2
|
-
import { registerBatchScheme } from "@circle-fin/x402-batching/client";
|
|
3
|
-
import { ExactEvmScheme } from "@x402/evm/exact/client";
|
|
4
|
-
import { privateKeyToAccount } from "viem/accounts";
|
|
5
1
|
/**
|
|
6
2
|
* Development engine. Declares the one-word amount without settling real funds,
|
|
7
3
|
* for use against a dev-mode gateway. NOT for production.
|
|
@@ -23,35 +19,4 @@ export class StaticPaymentEngine {
|
|
|
23
19
|
};
|
|
24
20
|
}
|
|
25
21
|
}
|
|
26
|
-
/**
|
|
27
|
-
* Circle/x402 engine. Signs the gateway's one-word `paymentRequired` terms.
|
|
28
|
-
* Circle may batch settlement internally, but each signed payload corresponds to
|
|
29
|
-
* exactly one word.
|
|
30
|
-
*/
|
|
31
|
-
export class CircleGatewayPaymentEngine {
|
|
32
|
-
options;
|
|
33
|
-
client = new x402Client();
|
|
34
|
-
account;
|
|
35
|
-
constructor(options) {
|
|
36
|
-
this.options = options;
|
|
37
|
-
this.account = privateKeyToAccount(this.options.privateKey);
|
|
38
|
-
// Recommended buyer integration (Circle x402 buyer how-to): register the
|
|
39
|
-
// gasless batched scheme with an `exact` fallback. `registerBatchScheme`
|
|
40
|
-
// wires a CompositeEvmScheme that uses Gateway batching when the seller
|
|
41
|
-
// supports it and falls back to a standard EIP-3009 `exact` payment
|
|
42
|
-
// otherwise — no per-request routing logic needed.
|
|
43
|
-
registerBatchScheme(this.client, {
|
|
44
|
-
signer: this.account,
|
|
45
|
-
fallbackScheme: new ExactEvmScheme(this.account),
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
async createWordPayment(session) {
|
|
49
|
-
if (!session.paymentRequired) {
|
|
50
|
-
throw new Error("Session did not include an x402 one-word payment requirement");
|
|
51
|
-
}
|
|
52
|
-
return {
|
|
53
|
-
paymentPayload: await this.client.createPaymentPayload(session.paymentRequired),
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
22
|
//# sourceMappingURL=payment-engine.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"payment-engine.js","sourceRoot":"","sources":["../src/payment-engine.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"payment-engine.js","sourceRoot":"","sources":["../src/payment-engine.ts"],"names":[],"mappings":"AAUA;;;GAGG;AACH,MAAM,OAAO,mBAAmB;IACD;IAA7B,YAA6B,UAAU,gBAAgB;QAA1B,YAAO,GAAP,OAAO,CAAmB;IAAG,CAAC;IAE3D,KAAK,CAAC,iBAAiB,CAAC,OAA6B;QACnD,OAAO;YACL,cAAc,EAAE;gBACd,MAAM,EAAE,oBAAoB;gBAC5B,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,YAAY,EAAE,OAAO,CAAC,iBAAiB;gBACvC,YAAY,EAAE,MAAM;aACrB;SACF,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payment-engine.test.d.ts","sourceRoot":"","sources":["../src/payment-engine.test.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rubicon-caliga/agent-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Client SDK for autonomous agents consuming per-word article streams via Rubicon x402.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -24,19 +24,19 @@
|
|
|
24
24
|
"publishConfig": {
|
|
25
25
|
"access": "public"
|
|
26
26
|
},
|
|
27
|
-
"scripts": {
|
|
28
|
-
"build": "tsc -p tsconfig.json",
|
|
29
|
-
"lint": "tsc -p tsconfig.json --noEmit",
|
|
30
|
-
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
31
|
-
"test": "node --test dist/**/*.test.js",
|
|
32
|
-
"prepublishOnly": "pnpm run build"
|
|
33
|
-
},
|
|
34
27
|
"dependencies": {
|
|
35
|
-
"@
|
|
28
|
+
"@circle-fin/developer-controlled-wallets": "^10.6.0",
|
|
36
29
|
"@circle-fin/x402-batching": "^3.1.2",
|
|
37
30
|
"@x402/core": "^2.15.0",
|
|
38
31
|
"@x402/evm": "^2.15.0",
|
|
39
32
|
"eventsource": "^4.0.0",
|
|
40
|
-
"viem": "^2.52.2"
|
|
33
|
+
"viem": "^2.52.2",
|
|
34
|
+
"@rubicon-caliga/core": "0.1.0"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsc -p tsconfig.json",
|
|
38
|
+
"lint": "tsc -p tsconfig.json --noEmit",
|
|
39
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
40
|
+
"test": "node --test dist/**/*.test.js"
|
|
41
41
|
}
|
|
42
|
-
}
|
|
42
|
+
}
|
package/src/agent-client.ts
CHANGED
|
@@ -39,6 +39,10 @@ export interface ReadReceipt {
|
|
|
39
39
|
amountPaidAtomic: `${bigint}`;
|
|
40
40
|
payments: WordPaymentReceipt[];
|
|
41
41
|
transactionHashes: string[];
|
|
42
|
+
settlementIds: string[];
|
|
43
|
+
buyerWalletAddress?: `0x${string}`;
|
|
44
|
+
sellerPayTo?: `0x${string}`;
|
|
45
|
+
network?: string;
|
|
42
46
|
text: string;
|
|
43
47
|
completed: boolean;
|
|
44
48
|
stopReason: "article_completed" | "stop_condition" | "budget_reached" | "max_words" | "aborted";
|
|
@@ -268,6 +272,7 @@ export class RubiconClient {
|
|
|
268
272
|
let wordsRead = 0;
|
|
269
273
|
let amountPaid = 0n;
|
|
270
274
|
const transactionHashes: string[] = [];
|
|
275
|
+
const settlementIds: string[] = [];
|
|
271
276
|
const payments: WordPaymentReceipt[] = [];
|
|
272
277
|
let stopReason: ReadReceipt["stopReason"] = "article_completed";
|
|
273
278
|
let completed = false;
|
|
@@ -280,6 +285,10 @@ export class RubiconClient {
|
|
|
280
285
|
amountPaidAtomic: `${amountPaid}`,
|
|
281
286
|
payments: [...payments],
|
|
282
287
|
transactionHashes: [...transactionHashes],
|
|
288
|
+
settlementIds: [...settlementIds],
|
|
289
|
+
buyerWalletAddress: [...payments].reverse().find((payment) => payment.buyerWalletAddress)?.buyerWalletAddress,
|
|
290
|
+
sellerPayTo: [...payments].reverse().find((payment) => payment.payTo)?.payTo,
|
|
291
|
+
network: [...payments].reverse().find((payment) => payment.network)?.network,
|
|
283
292
|
text,
|
|
284
293
|
completed,
|
|
285
294
|
stopReason,
|
|
@@ -326,6 +335,7 @@ export class RubiconClient {
|
|
|
326
335
|
payments.push(result.payment);
|
|
327
336
|
}
|
|
328
337
|
transactionHashes.push(...(result.transactionHashes ?? (result.transactionHash ? [result.transactionHash] : [])));
|
|
338
|
+
settlementIds.push(...(result.settlementIds ?? (result.settlementId ? [result.settlementId] : [])));
|
|
329
339
|
text = text ? `${text} ${result.word}` : result.word;
|
|
330
340
|
|
|
331
341
|
yield {
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { serializeTypedData, toEip712Payload } from "./circle-agent-wallet.js";
|
|
4
|
+
|
|
5
|
+
// The x402 schemes hand the signer viem-style typed data with no EIP712Domain
|
|
6
|
+
// entry; Circle's API needs the complete document. These pin the bridging.
|
|
7
|
+
|
|
8
|
+
const TRANSFER_TYPES = {
|
|
9
|
+
TransferWithAuthorization: [
|
|
10
|
+
{ name: "from", type: "address" },
|
|
11
|
+
{ name: "to", type: "address" },
|
|
12
|
+
{ name: "value", type: "uint256" },
|
|
13
|
+
],
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const EIP3009_TRANSFER_TYPES = {
|
|
17
|
+
TransferWithAuthorization: [
|
|
18
|
+
{ name: "from", type: "address" },
|
|
19
|
+
{ name: "to", type: "address" },
|
|
20
|
+
{ name: "value", type: "uint256" },
|
|
21
|
+
{ name: "validAfter", type: "uint256" },
|
|
22
|
+
{ name: "validBefore", type: "uint256" },
|
|
23
|
+
{ name: "nonce", type: "bytes32" },
|
|
24
|
+
],
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
test("injects EIP712Domain derived from the domain fields present", () => {
|
|
28
|
+
const payload = toEip712Payload({
|
|
29
|
+
domain: {
|
|
30
|
+
name: "USD Coin",
|
|
31
|
+
version: "2",
|
|
32
|
+
chainId: 5042002,
|
|
33
|
+
verifyingContract: "0xabc",
|
|
34
|
+
},
|
|
35
|
+
types: TRANSFER_TYPES,
|
|
36
|
+
primaryType: "TransferWithAuthorization",
|
|
37
|
+
message: { from: "0x1", to: "0x2", value: "5" },
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
assert.deepEqual(payload.types.EIP712Domain, [
|
|
41
|
+
{ name: "name", type: "string" },
|
|
42
|
+
{ name: "version", type: "string" },
|
|
43
|
+
{ name: "chainId", type: "uint256" },
|
|
44
|
+
{ name: "verifyingContract", type: "address" },
|
|
45
|
+
]);
|
|
46
|
+
// Original typed-data entries are preserved alongside the injected domain.
|
|
47
|
+
assert.deepEqual(payload.types.TransferWithAuthorization, TRANSFER_TYPES.TransferWithAuthorization);
|
|
48
|
+
assert.equal(payload.primaryType, "TransferWithAuthorization");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("only includes domain fields that are actually present", () => {
|
|
52
|
+
const payload = toEip712Payload({
|
|
53
|
+
domain: { name: "Test", chainId: 1337 },
|
|
54
|
+
types: TRANSFER_TYPES,
|
|
55
|
+
primaryType: "TransferWithAuthorization",
|
|
56
|
+
message: {},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
assert.deepEqual(payload.types.EIP712Domain, [
|
|
60
|
+
{ name: "name", type: "string" },
|
|
61
|
+
{ name: "chainId", type: "uint256" },
|
|
62
|
+
]);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("does not overwrite an EIP712Domain the caller already supplied", () => {
|
|
66
|
+
const provided = [{ name: "name", type: "string" }];
|
|
67
|
+
const payload = toEip712Payload({
|
|
68
|
+
domain: { name: "Test", chainId: 1337 },
|
|
69
|
+
types: { ...TRANSFER_TYPES, EIP712Domain: provided },
|
|
70
|
+
primaryType: "TransferWithAuthorization",
|
|
71
|
+
message: {},
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
assert.deepEqual(payload.types.EIP712Domain, provided);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("serializes bigint authorization fields (exact fallback) as decimal strings", () => {
|
|
78
|
+
// The `exact` scheme hands the signer bigint value/validAfter/validBefore;
|
|
79
|
+
// plain JSON.stringify would throw, so they must be coerced to strings.
|
|
80
|
+
const payload = toEip712Payload({
|
|
81
|
+
domain: { name: "USD Coin", chainId: 5042002 },
|
|
82
|
+
types: EIP3009_TRANSFER_TYPES,
|
|
83
|
+
primaryType: "TransferWithAuthorization",
|
|
84
|
+
message: {
|
|
85
|
+
from: "0x1",
|
|
86
|
+
to: "0x2",
|
|
87
|
+
value: 5n,
|
|
88
|
+
validAfter: 0n,
|
|
89
|
+
validBefore: 1893456000n,
|
|
90
|
+
nonce: "0xabc",
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const json = serializeTypedData(payload);
|
|
95
|
+
const parsed = JSON.parse(json);
|
|
96
|
+
assert.equal(parsed.message.value, "5");
|
|
97
|
+
assert.equal(parsed.message.validAfter, "0");
|
|
98
|
+
assert.equal(parsed.message.validBefore, "1893456000");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("removes message fields that are not declared by the primary type", () => {
|
|
102
|
+
const payload = toEip712Payload({
|
|
103
|
+
domain: { name: "USD Coin", chainId: 5042002 },
|
|
104
|
+
types: TRANSFER_TYPES,
|
|
105
|
+
primaryType: "TransferWithAuthorization",
|
|
106
|
+
message: {
|
|
107
|
+
from: "0x1",
|
|
108
|
+
to: "0x2",
|
|
109
|
+
value: "5",
|
|
110
|
+
validAfter: "0",
|
|
111
|
+
validBefore: "1893456000",
|
|
112
|
+
nonce: "0xabc",
|
|
113
|
+
authorization: { unexpected: true },
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
assert.deepEqual(payload.message, { from: "0x1", to: "0x2", value: "5" });
|
|
118
|
+
});
|