@catena/sdk 0.1.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/README.md +789 -358
- package/dist/{client-CHZMO00P.mjs → client-BSYVYJGm.mjs} +196 -21
- package/dist/{client-Cm2wNUuB.d.mts → client-B_Gi0aqH.d.mts} +270 -13
- package/dist/client.d.mts +2 -2
- package/dist/client.mjs +2 -2
- package/dist/movements.d.mts +139 -0
- package/dist/movements.mjs +334 -0
- package/dist/mpp.d.mts +42 -30
- package/dist/mpp.mjs +13 -14
- package/dist/{settlement-report-CT3EbtLL.mjs → settlement-report-DTxaAxE3.mjs} +22 -1
- package/dist/viem.d.mts +18 -18
- package/dist/viem.mjs +10 -15
- package/dist/x402.d.mts +21 -15
- package/dist/x402.mjs +8 -14
- package/package.json +9 -9
|
@@ -92,7 +92,7 @@ function p256SigningKey(point, privateKeyHex) {
|
|
|
92
92
|
}
|
|
93
93
|
//#endregion
|
|
94
94
|
//#region package.json
|
|
95
|
-
var version = "0.
|
|
95
|
+
var version = "0.4.0";
|
|
96
96
|
//#endregion
|
|
97
97
|
//#region src/user-agent.ts
|
|
98
98
|
const HTTP_TOKEN_RE = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
|
|
@@ -305,7 +305,12 @@ const accountBalanceResponseSchema = v.object({
|
|
|
305
305
|
balance: moneyJsonSchema,
|
|
306
306
|
balances: v.optional(v.object({
|
|
307
307
|
total: moneyJsonSchema,
|
|
308
|
-
available: moneyJsonSchema
|
|
308
|
+
available: moneyJsonSchema,
|
|
309
|
+
byNetwork: v.optional(v.array(v.object({
|
|
310
|
+
network: v.string(),
|
|
311
|
+
total: moneyJsonSchema,
|
|
312
|
+
available: moneyJsonSchema
|
|
313
|
+
})))
|
|
309
314
|
}))
|
|
310
315
|
});
|
|
311
316
|
const ACCOUNT_TRANSACTION_TYPES = [
|
|
@@ -351,6 +356,7 @@ const accountTransactionSchema = v.object({
|
|
|
351
356
|
amount: moneyJsonSchema,
|
|
352
357
|
fee: moneyJsonSchema,
|
|
353
358
|
currency: v.string(),
|
|
359
|
+
network: v.optional(v.string()),
|
|
354
360
|
counterpartyName: v.optional(v.string()),
|
|
355
361
|
method: v.optional(extensibleStringSchema(ACCOUNT_TRANSACTION_METHODS)),
|
|
356
362
|
memo: v.optional(v.string()),
|
|
@@ -371,6 +377,11 @@ const accountDepositAddressResponseSchema = v.object({
|
|
|
371
377
|
liquidationAddressId: v.optional(v.string()),
|
|
372
378
|
source: extensibleStringSchema(ACCOUNT_DEPOSIT_ADDRESS_SOURCES)
|
|
373
379
|
});
|
|
380
|
+
const walletCounterpartyRailSchema = v.object({
|
|
381
|
+
type: v.literal("wallet"),
|
|
382
|
+
walletAddress: v.custom((input) => typeof input === "string" && /^0x[0-9a-fA-F]{40}$/.test(input)),
|
|
383
|
+
network: v.custom((input) => typeof input === "string" && input.length > 0)
|
|
384
|
+
});
|
|
374
385
|
const counterpartySchema = v.object({
|
|
375
386
|
id: v.string(),
|
|
376
387
|
name: v.string(),
|
|
@@ -404,7 +415,19 @@ const intentNextActionSchema = v.object({
|
|
|
404
415
|
bodyHash: v.string(),
|
|
405
416
|
expiresAt: isoTimestampSchema,
|
|
406
417
|
prepareToken: v.string()
|
|
407
|
-
})
|
|
418
|
+
}),
|
|
419
|
+
movement: v.optional(v.object({
|
|
420
|
+
planId: v.string(),
|
|
421
|
+
signerAddress: v.string(),
|
|
422
|
+
digests: v.array(v.string()),
|
|
423
|
+
instructions: v.array(v.looseObject({
|
|
424
|
+
kind: v.string(),
|
|
425
|
+
network: v.string(),
|
|
426
|
+
signerAddress: v.string(),
|
|
427
|
+
digest: v.string(),
|
|
428
|
+
payload: v.record(v.string(), v.unknown())
|
|
429
|
+
}))
|
|
430
|
+
}))
|
|
408
431
|
});
|
|
409
432
|
const INTENT_ACTION_TYPES = [
|
|
410
433
|
"send",
|
|
@@ -415,6 +438,50 @@ const INTENT_ACTION_TYPES = [
|
|
|
415
438
|
"mpp",
|
|
416
439
|
"policy_override"
|
|
417
440
|
];
|
|
441
|
+
const CROSS_CHAIN_STATES = [
|
|
442
|
+
"burning",
|
|
443
|
+
"attesting",
|
|
444
|
+
"minting",
|
|
445
|
+
"ready",
|
|
446
|
+
"delivering",
|
|
447
|
+
"delivered",
|
|
448
|
+
"failed",
|
|
449
|
+
"manual_review"
|
|
450
|
+
];
|
|
451
|
+
const CROSS_CHAIN_NEXT_STEPS = [
|
|
452
|
+
"wait",
|
|
453
|
+
"continue",
|
|
454
|
+
"done",
|
|
455
|
+
"blocked"
|
|
456
|
+
];
|
|
457
|
+
const crossChainSendSchema = v.object({
|
|
458
|
+
id: v.string(),
|
|
459
|
+
sourceNetwork: v.string(),
|
|
460
|
+
destinationNetwork: v.string(),
|
|
461
|
+
state: extensibleStringSchema(CROSS_CHAIN_STATES),
|
|
462
|
+
nextStep: extensibleStringSchema(CROSS_CHAIN_NEXT_STEPS),
|
|
463
|
+
readyAt: v.nullable(isoTimestampSchema),
|
|
464
|
+
deliveredAt: v.nullable(isoTimestampSchema),
|
|
465
|
+
failureReason: v.nullable(v.string())
|
|
466
|
+
});
|
|
467
|
+
const MOVEMENT_NEXT_STEPS = [
|
|
468
|
+
"wait",
|
|
469
|
+
"continue",
|
|
470
|
+
"done",
|
|
471
|
+
"blocked"
|
|
472
|
+
];
|
|
473
|
+
const movementSchema = v.object({
|
|
474
|
+
planId: v.string(),
|
|
475
|
+
nextStep: extensibleStringSchema(MOVEMENT_NEXT_STEPS)
|
|
476
|
+
});
|
|
477
|
+
const movementContinuationSchema = v.object({ planId: v.string() });
|
|
478
|
+
const continueIntentResponseSchema = v.object({
|
|
479
|
+
id: v.string(),
|
|
480
|
+
status: intentStatusSchema,
|
|
481
|
+
crossChain: v.optional(crossChainSendSchema),
|
|
482
|
+
movement: v.optional(movementContinuationSchema),
|
|
483
|
+
nextAction: intentNextActionSchema
|
|
484
|
+
});
|
|
418
485
|
const wireIntentResultSchema = v.object({
|
|
419
486
|
id: v.string(),
|
|
420
487
|
accountId: v.optional(v.string()),
|
|
@@ -425,6 +492,8 @@ const wireIntentResultSchema = v.object({
|
|
|
425
492
|
replayed: v.optional(v.boolean(), false),
|
|
426
493
|
data: v.optional(v.nullable(v.record(v.string(), v.unknown())), null),
|
|
427
494
|
metadata: v.optional(v.object({ dataUrl: v.nullable(v.string()) })),
|
|
495
|
+
crossChain: v.optional(crossChainSendSchema),
|
|
496
|
+
movement: v.optional(movementSchema),
|
|
428
497
|
nextAction: v.optional(intentNextActionSchema)
|
|
429
498
|
});
|
|
430
499
|
function toIntentResult(wire) {
|
|
@@ -438,16 +507,41 @@ function x402CredentialFromIntentData(data) {
|
|
|
438
507
|
const legacy = v.safeParse(legacyX402IntentDataSchema, data);
|
|
439
508
|
return legacy.success ? legacy.output.x402.paymentSignature : void 0;
|
|
440
509
|
}
|
|
441
|
-
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
510
|
+
v.picklist(SEND_METHODS);
|
|
511
|
+
const sendActionSchemasByMethod = {
|
|
512
|
+
ach: v.object({
|
|
513
|
+
type: v.literal("send"),
|
|
514
|
+
accountId: v.optional(v.pipe(v.string(), v.minLength(1))),
|
|
515
|
+
counterpartyRailId: v.string(),
|
|
516
|
+
amount: v.string(),
|
|
517
|
+
description: v.optional(v.string()),
|
|
518
|
+
method: v.literal("ach"),
|
|
519
|
+
memo: v.optional(v.string())
|
|
520
|
+
}),
|
|
521
|
+
wire: v.object({
|
|
522
|
+
type: v.literal("send"),
|
|
523
|
+
accountId: v.optional(v.pipe(v.string(), v.minLength(1))),
|
|
524
|
+
counterpartyRailId: v.string(),
|
|
525
|
+
amount: v.string(),
|
|
526
|
+
description: v.optional(v.string()),
|
|
527
|
+
method: v.literal("wire"),
|
|
528
|
+
memo: v.optional(v.string())
|
|
529
|
+
}),
|
|
530
|
+
"on-chain": v.object({
|
|
531
|
+
type: v.literal("send"),
|
|
532
|
+
accountId: v.optional(v.pipe(v.string(), v.minLength(1))),
|
|
533
|
+
counterpartyRailId: v.string(),
|
|
534
|
+
amount: v.string(),
|
|
535
|
+
description: v.optional(v.string()),
|
|
536
|
+
method: v.literal("on-chain"),
|
|
537
|
+
memo: v.optional(v.string())
|
|
538
|
+
})
|
|
539
|
+
};
|
|
540
|
+
const sendActionSchema = v.variant("method", [
|
|
541
|
+
sendActionSchemasByMethod.ach,
|
|
542
|
+
sendActionSchemasByMethod.wire,
|
|
543
|
+
sendActionSchemasByMethod["on-chain"]
|
|
544
|
+
]);
|
|
451
545
|
const transferActionSchema = v.object({
|
|
452
546
|
type: v.literal("transfer"),
|
|
453
547
|
accountId: v.string(),
|
|
@@ -589,12 +683,16 @@ var ApiError = class extends Error {
|
|
|
589
683
|
status;
|
|
590
684
|
code;
|
|
591
685
|
details;
|
|
592
|
-
|
|
593
|
-
|
|
686
|
+
fields;
|
|
687
|
+
constructor(status, message, code, details, fields) {
|
|
688
|
+
const explanations = Object.entries(fields ?? {}).map(([path, explanation]) => path === "request" ? explanation : `${path}: ${explanation}`).join("; ");
|
|
689
|
+
const description = explanations ? `${message}: ${explanations}` : message;
|
|
690
|
+
super(code ? `API error ${status} [${code}]: ${description}` : `API error ${status}: ${description}`);
|
|
594
691
|
this.name = "ApiError";
|
|
595
692
|
this.status = status;
|
|
596
693
|
this.code = code;
|
|
597
694
|
this.details = details;
|
|
695
|
+
this.fields = fields;
|
|
598
696
|
}
|
|
599
697
|
};
|
|
600
698
|
var TimeoutError = class extends Error {
|
|
@@ -609,13 +707,14 @@ const errorBodySchema = v.object({
|
|
|
609
707
|
error: v.nullish(v.string()),
|
|
610
708
|
message: v.nullish(v.string()),
|
|
611
709
|
code: v.nullish(v.string()),
|
|
612
|
-
details: v.nullish(v.record(v.string(), v.unknown()))
|
|
710
|
+
details: v.nullish(v.record(v.string(), v.unknown())),
|
|
711
|
+
fields: v.fallback(v.nullish(v.pipe(v.unknown(), v.check((value) => !Array.isArray(value)), v.record(v.string(), v.string()))), void 0)
|
|
613
712
|
});
|
|
614
713
|
async function apiErrorFromResponse(res) {
|
|
615
714
|
const parsed = v.safeParse(errorBodySchema, await res.json().catch(() => void 0));
|
|
616
715
|
const body = parsed.success ? parsed.output : {};
|
|
617
|
-
if (body.error != null && body.message != null) return new ApiError(res.status, body.message, body.code ?? body.error, body.details ?? void 0);
|
|
618
|
-
return new ApiError(res.status, body.error ?? body.message ?? res.statusText, body.code ?? void 0, body.details ?? void 0);
|
|
716
|
+
if (body.error != null && body.message != null) return new ApiError(res.status, body.message, body.code ?? body.error, body.details ?? void 0, body.fields ?? void 0);
|
|
717
|
+
return new ApiError(res.status, body.error ?? body.message ?? res.statusText, body.code ?? void 0, body.details ?? void 0, body.fields ?? void 0);
|
|
619
718
|
}
|
|
620
719
|
async function parseApiResponse(res, schema) {
|
|
621
720
|
if (!res.ok) throw await apiErrorFromResponse(res);
|
|
@@ -635,6 +734,27 @@ var IntentSubmitError = class extends Error {
|
|
|
635
734
|
const DEFAULT_BASE_URL = "https://api.catena.com";
|
|
636
735
|
const DEFAULT_TIMEOUT_MS = 8e4;
|
|
637
736
|
const MAX_TIMEOUT_MS = 2147483647;
|
|
737
|
+
const digestBatchBodySchema = v.looseObject({ parameters: v.looseObject({ payloads: v.pipe(v.array(v.unknown()), v.minLength(1)) }) });
|
|
738
|
+
let movementsModule;
|
|
739
|
+
let movementsLoadFailure;
|
|
740
|
+
function loadMovements() {
|
|
741
|
+
movementsModule ??= import("./movements.mjs").then((loaded) => loaded, (reason) => {
|
|
742
|
+
movementsLoadFailure = reason;
|
|
743
|
+
});
|
|
744
|
+
return movementsModule;
|
|
745
|
+
}
|
|
746
|
+
async function continuationKinds() {
|
|
747
|
+
return await loadMovements() === void 0 ? "crossChain" : "crossChain, movement";
|
|
748
|
+
}
|
|
749
|
+
const digestBatchActivitySchema = v.looseObject({ type: v.literal("ACTIVITY_TYPE_SIGN_RAW_PAYLOADS") });
|
|
750
|
+
function signsADigestBatch(body) {
|
|
751
|
+
try {
|
|
752
|
+
const parsed = JSON.parse(body);
|
|
753
|
+
return v.safeParse(digestBatchBodySchema, parsed).success || v.safeParse(digestBatchActivitySchema, parsed).success;
|
|
754
|
+
} catch {
|
|
755
|
+
return false;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
638
758
|
var CatenaClient = class {
|
|
639
759
|
#baseUrl;
|
|
640
760
|
#userAgent;
|
|
@@ -769,7 +889,7 @@ var CatenaClient = class {
|
|
|
769
889
|
if (!nextAction) return toIntentResult(created);
|
|
770
890
|
let submitRequest;
|
|
771
891
|
try {
|
|
772
|
-
const stamp = await this.#stampNextAction(nextAction);
|
|
892
|
+
const stamp = await this.#stampNextAction(nextAction, params.action);
|
|
773
893
|
submitRequest = await this.#signedRequest({
|
|
774
894
|
method: "POST",
|
|
775
895
|
path: `/intents/${encodeURIComponent(created.id)}/submit-stamp`,
|
|
@@ -803,8 +923,9 @@ var CatenaClient = class {
|
|
|
803
923
|
headers: { "Idempotency-Key": params.idempotencyKey }
|
|
804
924
|
}, wireIntentResultSchema);
|
|
805
925
|
}
|
|
806
|
-
async #stampNextAction(nextAction) {
|
|
926
|
+
async #stampNextAction(nextAction, action) {
|
|
807
927
|
if (nextAction.signer.publicKeyHex.toLowerCase() !== this.#keypair.publicKeyHex.toLowerCase()) throw new Error(`Local signer public key does not match requested signer ${nextAction.signer.publicKeyHex}; linked credential has ${this.#keypair.publicKeyHex}`);
|
|
928
|
+
await this.#reviewMovement(nextAction, action);
|
|
808
929
|
const stamp = await new ApiKeyStamper({
|
|
809
930
|
apiPublicKey: this.#keypair.publicKeyHex,
|
|
810
931
|
apiPrivateKey: this.#keypair.privateKeyHex
|
|
@@ -815,6 +936,23 @@ var CatenaClient = class {
|
|
|
815
936
|
stampHeaderValue: stamp.stampHeaderValue
|
|
816
937
|
};
|
|
817
938
|
}
|
|
939
|
+
async #reviewMovement(nextAction, action) {
|
|
940
|
+
const movement = nextAction.movement;
|
|
941
|
+
if (movement === void 0) {
|
|
942
|
+
if (signsADigestBatch(nextAction.preparedBody.body)) throw new Error("Refusing to approve this payment: it would sign pre-hashed digests with nothing describing what they commit to. Nothing was signed.");
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
const movements = await loadMovements();
|
|
946
|
+
if (movements === void 0) throw new Error("Refusing to approve this payment: its digests cannot be checked without the optional `viem` peer dependency (>=2.24.0). Nothing was signed.", { cause: movementsLoadFailure });
|
|
947
|
+
const { verifyPreparedMovement } = movements;
|
|
948
|
+
const verdict = verifyPreparedMovement({
|
|
949
|
+
signerAddress: movement.signerAddress,
|
|
950
|
+
digests: movement.digests,
|
|
951
|
+
instructions: movement.instructions,
|
|
952
|
+
body: nextAction.preparedBody.body
|
|
953
|
+
}, action !== void 0 && "amount" in action ? { amount: action.amount } : void 0);
|
|
954
|
+
if (!verdict.ok) throw new Error(`Refusing to approve this payment: ${verdict.reason} Nothing was signed.`);
|
|
955
|
+
}
|
|
818
956
|
async #submitStamp(request, replayed) {
|
|
819
957
|
const intent = toIntentResult((await this.#dispatch(request, submitStampResponseSchema)).intent);
|
|
820
958
|
return replayed ? {
|
|
@@ -828,6 +966,43 @@ var CatenaClient = class {
|
|
|
828
966
|
path: `/intents/${encodeURIComponent(id)}`
|
|
829
967
|
}, wireIntentResultSchema));
|
|
830
968
|
}
|
|
969
|
+
async continueIntent(params) {
|
|
970
|
+
const prepared = await this.#request({
|
|
971
|
+
method: "POST",
|
|
972
|
+
path: `/intents/${encodeURIComponent(params.intentId)}/continue`,
|
|
973
|
+
headers: {
|
|
974
|
+
"Idempotency-Key": params.idempotencyKey ?? crypto.randomUUID(),
|
|
975
|
+
"Catena-Continuation-Kinds": await continuationKinds()
|
|
976
|
+
}
|
|
977
|
+
}, continueIntentResponseSchema);
|
|
978
|
+
let submitRequest;
|
|
979
|
+
try {
|
|
980
|
+
const stamp = await this.#stampNextAction(prepared.nextAction, params.action);
|
|
981
|
+
submitRequest = await this.#signedRequest({
|
|
982
|
+
method: "POST",
|
|
983
|
+
path: `/intents/${encodeURIComponent(params.intentId)}/submit-stamp`,
|
|
984
|
+
body: {
|
|
985
|
+
preparedBody: prepared.nextAction.preparedBody,
|
|
986
|
+
stamp
|
|
987
|
+
}
|
|
988
|
+
});
|
|
989
|
+
} catch (cause) {
|
|
990
|
+
throw new IntentSubmitError({
|
|
991
|
+
intentId: params.intentId,
|
|
992
|
+
outcome: "not-submitted",
|
|
993
|
+
cause
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
try {
|
|
997
|
+
return await this.#submitStamp(submitRequest, false);
|
|
998
|
+
} catch (cause) {
|
|
999
|
+
throw new IntentSubmitError({
|
|
1000
|
+
intentId: params.intentId,
|
|
1001
|
+
outcome: "unknown",
|
|
1002
|
+
cause
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
831
1006
|
async unlinkAgent() {
|
|
832
1007
|
await this.#request({
|
|
833
1008
|
method: "POST",
|
|
@@ -854,4 +1029,4 @@ function createCatenaClient(options = {}) {
|
|
|
854
1029
|
});
|
|
855
1030
|
}
|
|
856
1031
|
//#endregion
|
|
857
|
-
export {
|
|
1032
|
+
export { walletCounterpartyRailSchema as C, extractRelayableOffer as D, x402ResourceSchema as E, extractRelayableReceipt as O, SEND_METHODS as S, x402PaymentRequirementsSchema as T, MOVEMENT_NEXT_STEPS as _, ACCOUNT_AGGREGATION_SCOPES as a, POLICY_RULE_ACTIONS as b, ACCOUNT_TRANSACTION_MOVEMENT_STATUSES as c, ACTOR_AGGREGATION_SCOPES as d, COUNTERPARTY_ACTIONS as f, INTENT_ACTION_TYPES as g, CROSS_CHAIN_STATES as h, createCatenaClient as i, paymentCredentialFromIntentData as k, ACCOUNT_TRANSACTION_STATUSES as l, CROSS_CHAIN_NEXT_STEPS as m, IntentSubmitError as n, ACCOUNT_DEPOSIT_ADDRESS_SOURCES as o, COUNTERPARTY_RULE_MODES as p, TimeoutError as r, ACCOUNT_TRANSACTION_METHODS as s, ApiError as t, ACCOUNT_TRANSACTION_TYPES as u, POLICY_CAPABILITIES as v, x402CredentialFromIntentData as w, POLICY_RULE_TYPES as x, POLICY_OVERRIDE_CAPABILITIES as y };
|