@4mica/sdk 0.5.5 → 1.0.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/contract.js CHANGED
@@ -4,12 +4,15 @@ exports.ContractGateway = void 0;
4
4
  const viem_1 = require("viem");
5
5
  const core4mica_1 = require("./abi/core4mica");
6
6
  const chain_1 = require("./chain");
7
+ const errors_1 = require("./errors");
7
8
  const utils_1 = require("./utils");
9
+ const DEFAULT_REMUNERATE_GAS_LIMIT = 8000000n;
8
10
  class ContractGateway {
9
11
  publicClient;
10
12
  walletClient;
11
13
  contract;
12
14
  erc20Cache = new Map();
15
+ txQueue = Promise.resolve();
13
16
  constructor(publicClient, walletClient, contract) {
14
17
  this.publicClient = publicClient;
15
18
  this.walletClient = walletClient;
@@ -22,7 +25,7 @@ class ContractGateway {
22
25
  });
23
26
  const rpcChainId = await publicClient.getChainId();
24
27
  if (rpcChainId !== Number(chainId)) {
25
- throw new Error(`Connected to chain ${rpcChainId}, expected ${chainId}`);
28
+ throw new errors_1.ContractError(`Connected to chain ${rpcChainId}, expected ${chainId}`);
26
29
  }
27
30
  const walletClient = (0, viem_1.createWalletClient)({
28
31
  transport: (0, viem_1.http)(rpcUrl),
@@ -49,28 +52,57 @@ class ContractGateway {
49
52
  }
50
53
  return this.erc20Cache.get(token);
51
54
  }
55
+ enqueueTx(fn) {
56
+ // Serialize transaction submissions to avoid nonce collisions.
57
+ const run = this.txQueue.then(fn, fn);
58
+ this.txQueue = run.then(() => undefined, () => undefined);
59
+ return run;
60
+ }
61
+ splitWaitOptions(waitOptions) {
62
+ if (!waitOptions) {
63
+ return { receipt: {} };
64
+ }
65
+ const { gas, timeout, pollingInterval } = waitOptions;
66
+ return {
67
+ gas,
68
+ receipt: {
69
+ ...(timeout !== undefined ? { timeout } : {}),
70
+ ...(pollingInterval !== undefined ? { pollingInterval } : {}),
71
+ },
72
+ };
73
+ }
52
74
  async getGuaranteeDomain() {
53
75
  return this.contract.read.guaranteeDomainSeparator();
54
76
  }
77
+ async getGuaranteeVersionConfig(version) {
78
+ const [, domainSeparator, decoder, enabled] = await this.contract.read.getGuaranteeVersionConfig([BigInt(version)]);
79
+ return { domainSeparator: domainSeparator, decoder: decoder, enabled };
80
+ }
55
81
  async approveErc20(token, amount, waitOptions) {
82
+ const { receipt } = this.splitWaitOptions(waitOptions);
56
83
  const erc20 = this.erc20(token);
57
84
  // spender address logic
58
85
  const spender = this.contract.address;
59
- const hash = await erc20.write.approve([spender, (0, utils_1.parseU256)(amount)]);
60
- return this.publicClient.waitForTransactionReceipt({ hash, ...(waitOptions ?? {}) });
86
+ const hash = await this.enqueueTx(() => erc20.write.approve([spender, (0, utils_1.parseU256)(amount)]));
87
+ return this.publicClient.waitForTransactionReceipt({ hash, ...receipt });
61
88
  }
62
89
  async deposit(amount, erc20Token, waitOptions) {
90
+ const { receipt } = this.splitWaitOptions(waitOptions);
63
91
  let hash;
64
92
  if (erc20Token) {
65
- hash = await this.contract.write.depositStablecoin([erc20Token, (0, utils_1.parseU256)(amount)]);
93
+ hash = await this.enqueueTx(() => this.contract.write.depositStablecoin([erc20Token, (0, utils_1.parseU256)(amount)]));
66
94
  }
67
95
  else {
68
- hash = await this.contract.write.deposit({ value: (0, utils_1.parseU256)(amount) });
96
+ hash = await this.enqueueTx(() => this.contract.write.deposit({ value: (0, utils_1.parseU256)(amount) }));
69
97
  }
70
- return this.publicClient.waitForTransactionReceipt({ hash, ...(waitOptions ?? {}) });
98
+ return this.publicClient.waitForTransactionReceipt({ hash, ...receipt });
71
99
  }
72
100
  async getUserAssets() {
73
- const addr = this.walletClient.account.address;
101
+ const account = this.walletClient.account;
102
+ if (!account) {
103
+ throw new errors_1.ContractError('wallet client has no account configured');
104
+ }
105
+ const addr = account.address;
74
106
  const result = await this.contract.read.getUserAllAssets([addr]);
75
107
  return result.map((a) => ({
76
108
  asset: a.asset,
@@ -90,55 +122,61 @@ class ContractGateway {
90
122
  };
91
123
  }
92
124
  async payTabEth(tabId, reqId, amount, recipient, waitOptions) {
125
+ const { receipt } = this.splitWaitOptions(waitOptions);
93
126
  const data = new TextEncoder().encode(`tab_id:${tabId.toString(16)};req_id:${reqId.toString(16)}`);
94
- const hash = await this.walletClient.sendTransaction({
127
+ const hash = await this.enqueueTx(() => this.walletClient.sendTransaction({
95
128
  to: recipient,
96
129
  value: (0, utils_1.parseU256)(amount),
97
130
  data: (0, utils_1.hexFromBytes)(data),
98
- });
99
- return this.publicClient.waitForTransactionReceipt({ hash, ...(waitOptions ?? {}) });
131
+ }));
132
+ return this.publicClient.waitForTransactionReceipt({ hash, ...receipt });
100
133
  }
101
134
  async payTabErc20(tabId, amount, erc20Token, recipient, waitOptions) {
102
- const hash = await this.contract.write.payTabInERC20Token([
135
+ const { receipt } = this.splitWaitOptions(waitOptions);
136
+ const hash = await this.enqueueTx(() => this.contract.write.payTabInERC20Token([
103
137
  (0, utils_1.parseU256)(tabId),
104
138
  erc20Token,
105
139
  (0, utils_1.parseU256)(amount),
106
140
  recipient,
107
- ]);
108
- return this.publicClient.waitForTransactionReceipt({ hash, ...(waitOptions ?? {}) });
141
+ ]));
142
+ return this.publicClient.waitForTransactionReceipt({ hash, ...receipt });
109
143
  }
110
144
  async requestWithdrawal(amount, erc20Token, waitOptions) {
145
+ const { receipt } = this.splitWaitOptions(waitOptions);
111
146
  const value = (0, utils_1.parseU256)(amount);
112
147
  let hash;
113
148
  if (erc20Token) {
114
- hash = await this.contract.write.requestWithdrawal([erc20Token, value]);
149
+ hash = await this.enqueueTx(() => this.contract.write.requestWithdrawal([erc20Token, value]));
115
150
  }
116
151
  else {
117
- hash = await this.contract.write.requestWithdrawal([value]);
152
+ hash = await this.enqueueTx(() => this.contract.write.requestWithdrawal([value]));
118
153
  }
119
- return this.publicClient.waitForTransactionReceipt({ hash, ...(waitOptions ?? {}) });
154
+ return this.publicClient.waitForTransactionReceipt({ hash, ...receipt });
120
155
  }
121
156
  async cancelWithdrawal(erc20Token, waitOptions) {
157
+ const { receipt } = this.splitWaitOptions(waitOptions);
122
158
  let hash;
123
159
  if (erc20Token) {
124
- hash = await this.contract.write.cancelWithdrawal([erc20Token]);
160
+ hash = await this.enqueueTx(() => this.contract.write.cancelWithdrawal([erc20Token]));
125
161
  }
126
162
  else {
127
- hash = await this.contract.write.cancelWithdrawal();
163
+ hash = await this.enqueueTx(() => this.contract.write.cancelWithdrawal());
128
164
  }
129
- return this.publicClient.waitForTransactionReceipt({ hash, ...(waitOptions ?? {}) });
165
+ return this.publicClient.waitForTransactionReceipt({ hash, ...receipt });
130
166
  }
131
167
  async finalizeWithdrawal(erc20Token, waitOptions) {
168
+ const { receipt } = this.splitWaitOptions(waitOptions);
132
169
  let hash;
133
170
  if (erc20Token) {
134
- hash = await this.contract.write.finalizeWithdrawal([erc20Token]);
171
+ hash = await this.enqueueTx(() => this.contract.write.finalizeWithdrawal([erc20Token]));
135
172
  }
136
173
  else {
137
- hash = await this.contract.write.finalizeWithdrawal();
174
+ hash = await this.enqueueTx(() => this.contract.write.finalizeWithdrawal());
138
175
  }
139
- return this.publicClient.waitForTransactionReceipt({ hash, ...(waitOptions ?? {}) });
176
+ return this.publicClient.waitForTransactionReceipt({ hash, ...receipt });
140
177
  }
141
178
  async remunerate(claimsBlob, signatureWords, waitOptions) {
179
+ const { gas, receipt } = this.splitWaitOptions(waitOptions);
142
180
  const sigStruct = {
143
181
  x_c0_a: (0, utils_1.hexFromBytes)(signatureWords[0]),
144
182
  x_c0_b: (0, utils_1.hexFromBytes)(signatureWords[1]),
@@ -149,8 +187,10 @@ class ContractGateway {
149
187
  y_c1_a: (0, utils_1.hexFromBytes)(signatureWords[6]),
150
188
  y_c1_b: (0, utils_1.hexFromBytes)(signatureWords[7]),
151
189
  };
152
- const hash = await this.contract.write.remunerate([(0, utils_1.hexFromBytes)(claimsBlob), sigStruct]);
153
- return this.publicClient.waitForTransactionReceipt({ hash, ...(waitOptions ?? {}) });
190
+ const hash = await this.enqueueTx(() => this.contract.write.remunerate([(0, utils_1.hexFromBytes)(claimsBlob), sigStruct], {
191
+ gas: gas ?? DEFAULT_REMUNERATE_GAS_LIMIT,
192
+ }));
193
+ return this.publicClient.waitForTransactionReceipt({ hash, ...receipt });
154
194
  }
155
195
  }
156
196
  exports.ContractGateway = ContractGateway;
package/dist/errors.d.ts CHANGED
@@ -1,8 +1,11 @@
1
+ /** Base class for all 4Mica SDK errors. Sets `error.name` to the subclass name. */
1
2
  export declare class FourMicaError extends Error {
2
3
  constructor(message: string);
3
4
  }
5
+ /** Thrown when the SDK configuration is invalid (e.g. missing required fields or bad URL). */
4
6
  export declare class ConfigError extends FourMicaError {
5
7
  }
8
+ /** Thrown when a 4Mica core RPC call fails. Includes the HTTP status and raw response body. */
6
9
  export declare class RpcError extends FourMicaError {
7
10
  readonly status?: number;
8
11
  readonly body?: unknown;
@@ -11,24 +14,34 @@ export declare class RpcError extends FourMicaError {
11
14
  body?: unknown;
12
15
  });
13
16
  }
17
+ /** Thrown when the SDK client fails to initialise (e.g. chain ID mismatch). */
14
18
  export declare class ClientInitializationError extends FourMicaError {
15
19
  }
20
+ /** Thrown when signing a payment claim fails (e.g. unsupported scheme, address mismatch). */
16
21
  export declare class SigningError extends FourMicaError {
17
22
  }
23
+ /** Thrown when an on-chain contract call fails or returns an unexpected result. */
18
24
  export declare class ContractError extends FourMicaError {
19
25
  }
26
+ /** Thrown when BLS certificate verification fails (e.g. domain mismatch, invalid encoding). */
20
27
  export declare class VerificationError extends FourMicaError {
21
28
  }
29
+ /** Thrown when an x402 HTTP payment flow encounters an error. */
22
30
  export declare class X402Error extends FourMicaError {
23
31
  }
32
+ /** Base class for authentication-related errors. */
24
33
  export declare class AuthError extends FourMicaError {
25
34
  }
35
+ /** Thrown when the auth URL is invalid or unreachable. */
26
36
  export declare class AuthUrlError extends AuthError {
27
37
  }
38
+ /** Thrown when a network-level error occurs during authentication. */
28
39
  export declare class AuthTransportError extends AuthError {
29
40
  }
41
+ /** Thrown when the auth server response cannot be decoded. */
30
42
  export declare class AuthDecodeError extends AuthError {
31
43
  }
44
+ /** Thrown when the auth server returns an error response. Includes HTTP status and body. */
32
45
  export declare class AuthApiError extends AuthError {
33
46
  readonly status?: number;
34
47
  readonly body?: unknown;
@@ -37,7 +50,9 @@ export declare class AuthApiError extends AuthError {
37
50
  body?: unknown;
38
51
  });
39
52
  }
53
+ /** Thrown when auth configuration is invalid. */
40
54
  export declare class AuthConfigError extends AuthError {
41
55
  }
56
+ /** Thrown when an authenticated operation is attempted without auth being configured. */
42
57
  export declare class AuthMissingConfigError extends AuthConfigError {
43
58
  }
package/dist/errors.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AuthMissingConfigError = exports.AuthConfigError = exports.AuthApiError = exports.AuthDecodeError = exports.AuthTransportError = exports.AuthUrlError = exports.AuthError = exports.X402Error = exports.VerificationError = exports.ContractError = exports.SigningError = exports.ClientInitializationError = exports.RpcError = exports.ConfigError = exports.FourMicaError = void 0;
4
+ /** Base class for all 4Mica SDK errors. Sets `error.name` to the subclass name. */
4
5
  class FourMicaError extends Error {
5
6
  constructor(message) {
6
7
  super(message);
@@ -8,9 +9,11 @@ class FourMicaError extends Error {
8
9
  }
9
10
  }
10
11
  exports.FourMicaError = FourMicaError;
12
+ /** Thrown when the SDK configuration is invalid (e.g. missing required fields or bad URL). */
11
13
  class ConfigError extends FourMicaError {
12
14
  }
13
15
  exports.ConfigError = ConfigError;
16
+ /** Thrown when a 4Mica core RPC call fails. Includes the HTTP status and raw response body. */
14
17
  class RpcError extends FourMicaError {
15
18
  status;
16
19
  body;
@@ -21,33 +24,43 @@ class RpcError extends FourMicaError {
21
24
  }
22
25
  }
23
26
  exports.RpcError = RpcError;
27
+ /** Thrown when the SDK client fails to initialise (e.g. chain ID mismatch). */
24
28
  class ClientInitializationError extends FourMicaError {
25
29
  }
26
30
  exports.ClientInitializationError = ClientInitializationError;
31
+ /** Thrown when signing a payment claim fails (e.g. unsupported scheme, address mismatch). */
27
32
  class SigningError extends FourMicaError {
28
33
  }
29
34
  exports.SigningError = SigningError;
35
+ /** Thrown when an on-chain contract call fails or returns an unexpected result. */
30
36
  class ContractError extends FourMicaError {
31
37
  }
32
38
  exports.ContractError = ContractError;
39
+ /** Thrown when BLS certificate verification fails (e.g. domain mismatch, invalid encoding). */
33
40
  class VerificationError extends FourMicaError {
34
41
  }
35
42
  exports.VerificationError = VerificationError;
43
+ /** Thrown when an x402 HTTP payment flow encounters an error. */
36
44
  class X402Error extends FourMicaError {
37
45
  }
38
46
  exports.X402Error = X402Error;
47
+ /** Base class for authentication-related errors. */
39
48
  class AuthError extends FourMicaError {
40
49
  }
41
50
  exports.AuthError = AuthError;
51
+ /** Thrown when the auth URL is invalid or unreachable. */
42
52
  class AuthUrlError extends AuthError {
43
53
  }
44
54
  exports.AuthUrlError = AuthUrlError;
55
+ /** Thrown when a network-level error occurs during authentication. */
45
56
  class AuthTransportError extends AuthError {
46
57
  }
47
58
  exports.AuthTransportError = AuthTransportError;
59
+ /** Thrown when the auth server response cannot be decoded. */
48
60
  class AuthDecodeError extends AuthError {
49
61
  }
50
62
  exports.AuthDecodeError = AuthDecodeError;
63
+ /** Thrown when the auth server returns an error response. Includes HTTP status and body. */
51
64
  class AuthApiError extends AuthError {
52
65
  status;
53
66
  body;
@@ -58,9 +71,11 @@ class AuthApiError extends AuthError {
58
71
  }
59
72
  }
60
73
  exports.AuthApiError = AuthApiError;
74
+ /** Thrown when auth configuration is invalid. */
61
75
  class AuthConfigError extends AuthError {
62
76
  }
63
77
  exports.AuthConfigError = AuthConfigError;
78
+ /** Thrown when an authenticated operation is attempted without auth being configured. */
64
79
  class AuthMissingConfigError extends AuthConfigError {
65
80
  }
66
81
  exports.AuthMissingConfigError = AuthMissingConfigError;
@@ -1,3 +1,25 @@
1
1
  import { PaymentGuaranteeClaims } from './models';
2
+ /**
3
+ * ABI-encode a {@link PaymentGuaranteeClaims} object into a hex string.
4
+ *
5
+ * Produces the outer `(uint64 version, bytes innerClaims)` envelope format expected
6
+ * by the Core4Mica contract. Supports V1 (10 fields) and V2 (18 fields with validation policy).
7
+ *
8
+ * @param claims - Decoded claims to encode. Must have `version` set to `1` or `2`.
9
+ * @returns `0x`-prefixed hex string of the ABI-encoded envelope.
10
+ * @throws {@link VerificationError} if `version` is not `1` or `2`, the domain is not 32 bytes,
11
+ * or V2 claims are missing `validationPolicy`.
12
+ */
2
13
  export declare function encodeGuaranteeClaims(claims: PaymentGuaranteeClaims): string;
14
+ /**
15
+ * Decode ABI-encoded guarantee claims into a {@link PaymentGuaranteeClaims} object.
16
+ *
17
+ * Accepts either the modern `(uint64 version, bytes innerClaims)` envelope or the legacy
18
+ * unwrapped V1 format (raw 320-byte ABI encoding without an outer envelope).
19
+ *
20
+ * @param data - Hex string or raw bytes of the ABI-encoded claims.
21
+ * @returns Decoded {@link PaymentGuaranteeClaims} with `version` set to `1` or `2`.
22
+ * @throws {@link VerificationError} if the data is too short, the inner length is wrong,
23
+ * or the encoded version number is unsupported.
24
+ */
3
25
  export declare function decodeGuaranteeClaims(data: string | Uint8Array): PaymentGuaranteeClaims;
package/dist/guarantee.js CHANGED
@@ -6,7 +6,8 @@ const viem_1 = require("viem");
6
6
  const errors_1 = require("./errors");
7
7
  const utils_1 = require("./utils");
8
8
  const CLAIMS_ENCODED_BYTES = 32 * 10;
9
- const WRAPPED_CLAIMS_BYTES = 32 * 2 + 32 + CLAIMS_ENCODED_BYTES;
9
+ // Minimum bytes for a valid outer envelope: uint64 + bytes offset + bytes length
10
+ const MIN_ENVELOPE_BYTES = 32 * 2 + 32;
10
11
  const CLAIM_TYPES = [
11
12
  { type: 'bytes32' },
12
13
  { type: 'uint256' },
@@ -19,6 +20,54 @@ const CLAIM_TYPES = [
19
20
  { type: 'uint64' },
20
21
  { type: 'uint64' },
21
22
  ];
23
+ // V2 adds: validation_registry_address, validation_request_hash, validation_chain_id,
24
+ // validator_address, validator_agent_id, min_validation_score,
25
+ // validation_subject_hash, required_validation_tag (dynamic string)
26
+ const CLAIM_TYPES_V2 = [
27
+ { type: 'bytes32' }, // domain
28
+ { type: 'uint256' }, // tab_id
29
+ { type: 'uint256' }, // req_id
30
+ { type: 'address' }, // client (user)
31
+ { type: 'address' }, // recipient
32
+ { type: 'uint256' }, // amount
33
+ { type: 'uint256' }, // total_amount
34
+ { type: 'address' }, // asset
35
+ { type: 'uint64' }, // timestamp
36
+ { type: 'uint64' }, // version
37
+ { type: 'address' }, // validation_registry_address
38
+ { type: 'bytes32' }, // validation_request_hash
39
+ { type: 'uint64' }, // validation_chain_id
40
+ { type: 'address' }, // validator_address
41
+ { type: 'uint256' }, // validator_agent_id
42
+ { type: 'uint8' }, // min_validation_score
43
+ { type: 'bytes32' }, // validation_subject_hash
44
+ { type: 'string' }, // required_validation_tag (dynamic)
45
+ ];
46
+ const CLAIM_TYPES_V2_TUPLE = [
47
+ {
48
+ type: 'tuple',
49
+ components: [
50
+ { name: 'domain', type: 'bytes32' },
51
+ { name: 'tabId', type: 'uint256' },
52
+ { name: 'reqId', type: 'uint256' },
53
+ { name: 'user', type: 'address' },
54
+ { name: 'recipient', type: 'address' },
55
+ { name: 'amount', type: 'uint256' },
56
+ { name: 'totalAmount', type: 'uint256' },
57
+ { name: 'asset', type: 'address' },
58
+ { name: 'timestamp', type: 'uint64' },
59
+ { name: 'claimsVersion', type: 'uint64' },
60
+ { name: 'validationRegistryAddress', type: 'address' },
61
+ { name: 'validationRequestHash', type: 'bytes32' },
62
+ { name: 'validationChainId', type: 'uint64' },
63
+ { name: 'validatorAddress', type: 'address' },
64
+ { name: 'validatorAgentId', type: 'uint256' },
65
+ { name: 'minValidationScore', type: 'uint8' },
66
+ { name: 'validationSubjectHash', type: 'bytes32' },
67
+ { name: 'requiredValidationTag', type: 'string' },
68
+ ],
69
+ },
70
+ ];
22
71
  function ensureDomainBytes(domain) {
23
72
  const bytes = typeof domain === 'string' ? (0, viem_1.toBytes)(domain) : domain;
24
73
  if (bytes.length !== 32) {
@@ -28,47 +77,115 @@ function ensureDomainBytes(domain) {
28
77
  }
29
78
  function normalizeHexBytes(data) {
30
79
  if (typeof data === 'string') {
31
- return (data.startsWith('0x') ? data : `0x${data}`);
80
+ return (0, utils_1.ensureHexPrefix)(data);
32
81
  }
33
82
  return (0, utils_1.hexFromBytes)(data);
34
83
  }
84
+ /**
85
+ * ABI-encode a {@link PaymentGuaranteeClaims} object into a hex string.
86
+ *
87
+ * Produces the outer `(uint64 version, bytes innerClaims)` envelope format expected
88
+ * by the Core4Mica contract. Supports V1 (10 fields) and V2 (18 fields with validation policy).
89
+ *
90
+ * @param claims - Decoded claims to encode. Must have `version` set to `1` or `2`.
91
+ * @returns `0x`-prefixed hex string of the ABI-encoded envelope.
92
+ * @throws {@link VerificationError} if `version` is not `1` or `2`, the domain is not 32 bytes,
93
+ * or V2 claims are missing `validationPolicy`.
94
+ */
35
95
  function encodeGuaranteeClaims(claims) {
36
- if (claims.version !== 1) {
37
- throw new errors_1.VerificationError(`unsupported guarantee claims version: ${claims.version}`);
96
+ if (claims.version === 1) {
97
+ const domain = ensureDomainBytes(claims.domain);
98
+ const encoded = (0, viem_1.encodeAbiParameters)(CLAIM_TYPES, [
99
+ (0, utils_1.hexFromBytes)(domain),
100
+ (0, utils_1.parseU256)(claims.tabId),
101
+ (0, utils_1.parseU256)(claims.reqId),
102
+ claims.userAddress,
103
+ claims.recipientAddress,
104
+ (0, utils_1.parseU256)(claims.amount),
105
+ (0, utils_1.parseU256)(claims.totalAmount),
106
+ claims.assetAddress,
107
+ BigInt(claims.timestamp),
108
+ BigInt(claims.version),
109
+ ]);
110
+ return (0, viem_1.encodeAbiParameters)([{ type: 'uint64' }, { type: 'bytes' }], [BigInt(claims.version), encoded]);
111
+ }
112
+ if (claims.version === 2) {
113
+ if (!claims.validationPolicy) {
114
+ throw new errors_1.VerificationError('V2 guarantee claims missing validationPolicy');
115
+ }
116
+ const p = claims.validationPolicy;
117
+ const domain = ensureDomainBytes(claims.domain);
118
+ const encoded = (0, viem_1.encodeAbiParameters)(CLAIM_TYPES_V2_TUPLE, [
119
+ {
120
+ domain: (0, utils_1.hexFromBytes)(domain),
121
+ tabId: (0, utils_1.parseU256)(claims.tabId),
122
+ reqId: (0, utils_1.parseU256)(claims.reqId),
123
+ user: claims.userAddress,
124
+ recipient: claims.recipientAddress,
125
+ amount: (0, utils_1.parseU256)(claims.amount),
126
+ totalAmount: (0, utils_1.parseU256)(claims.totalAmount),
127
+ asset: claims.assetAddress,
128
+ timestamp: BigInt(claims.timestamp),
129
+ claimsVersion: BigInt(claims.version),
130
+ validationRegistryAddress: p.validationRegistryAddress,
131
+ validationRequestHash: (0, utils_1.ensureHexPrefix)(p.validationRequestHash),
132
+ validationChainId: BigInt(p.validationChainId),
133
+ validatorAddress: p.validatorAddress,
134
+ validatorAgentId: p.validatorAgentId,
135
+ minValidationScore: p.minValidationScore,
136
+ validationSubjectHash: (0, utils_1.ensureHexPrefix)(p.validationSubjectHash),
137
+ requiredValidationTag: p.requiredValidationTag,
138
+ },
139
+ ]);
140
+ return (0, viem_1.encodeAbiParameters)([{ type: 'uint64' }, { type: 'bytes' }], [BigInt(claims.version), encoded]);
38
141
  }
39
- const domain = ensureDomainBytes(claims.domain);
40
- const encoded = (0, viem_1.encodeAbiParameters)(CLAIM_TYPES, [
41
- (0, utils_1.hexFromBytes)(domain),
42
- (0, utils_1.parseU256)(claims.tabId),
43
- (0, utils_1.parseU256)(claims.reqId),
44
- claims.userAddress,
45
- claims.recipientAddress,
46
- (0, utils_1.parseU256)(claims.amount),
47
- (0, utils_1.parseU256)(claims.totalAmount),
48
- claims.assetAddress,
49
- BigInt(claims.timestamp),
50
- BigInt(claims.version),
51
- ]);
52
- return (0, viem_1.encodeAbiParameters)([{ type: 'uint64' }, { type: 'bytes' }], [BigInt(claims.version), encoded]);
142
+ throw new errors_1.VerificationError(`unsupported guarantee claims version: ${claims.version}`);
53
143
  }
144
+ /**
145
+ * Decode ABI-encoded guarantee claims into a {@link PaymentGuaranteeClaims} object.
146
+ *
147
+ * Accepts either the modern `(uint64 version, bytes innerClaims)` envelope or the legacy
148
+ * unwrapped V1 format (raw 320-byte ABI encoding without an outer envelope).
149
+ *
150
+ * @param data - Hex string or raw bytes of the ABI-encoded claims.
151
+ * @returns Decoded {@link PaymentGuaranteeClaims} with `version` set to `1` or `2`.
152
+ * @throws {@link VerificationError} if the data is too short, the inner length is wrong,
153
+ * or the encoded version number is unsupported.
154
+ */
54
155
  function decodeGuaranteeClaims(data) {
55
156
  const hex = normalizeHexBytes(data);
56
157
  const byteLen = (hex.length - 2) / 2;
57
- let encoded;
158
+ // Try to decode as unwrapped V1 (legacy)
58
159
  if (byteLen === CLAIMS_ENCODED_BYTES) {
59
- encoded = hex;
160
+ return decodeV1Claims(hex);
60
161
  }
61
- else {
62
- if (byteLen !== WRAPPED_CLAIMS_BYTES) {
63
- throw new errors_1.VerificationError(`unexpected guarantee claims length: ${byteLen} bytes`);
64
- }
65
- const [version, wrapped] = (0, viem_1.decodeAbiParameters)([{ type: 'uint64' }, { type: 'bytes' }], hex);
66
- if (version !== 1n) {
67
- throw new errors_1.VerificationError(`unsupported guarantee claims version: ${version}`);
162
+ // Decode outer envelope to get version
163
+ if (byteLen < MIN_ENVELOPE_BYTES) {
164
+ throw new errors_1.VerificationError(`unexpected guarantee claims length: ${byteLen} bytes`);
165
+ }
166
+ const [version, wrapped] = (0, viem_1.decodeAbiParameters)([{ type: 'uint64' }, { type: 'bytes' }], hex);
167
+ if (version === 1n) {
168
+ const innerHex = wrapped;
169
+ const innerByteLen = (innerHex.length - 2) / 2;
170
+ if (innerByteLen !== CLAIMS_ENCODED_BYTES) {
171
+ throw new errors_1.VerificationError(`unexpected V1 claims inner length: ${innerByteLen} bytes`);
68
172
  }
69
- encoded = wrapped;
173
+ return decodeV1Claims(innerHex);
70
174
  }
71
- const [domain, tabId, reqId, user, recipient, amount, totalAmount, asset, timestamp, claimsVersion,] = (0, viem_1.decodeAbiParameters)(CLAIM_TYPES, encoded);
175
+ if (version === 2n) {
176
+ return decodeV2Claims(wrapped);
177
+ }
178
+ throw new errors_1.VerificationError(`unsupported guarantee claims version: ${version}`);
179
+ }
180
+ function decodeV1Claims(encoded) {
181
+ let decoded;
182
+ try {
183
+ decoded = (0, viem_1.decodeAbiParameters)(CLAIM_TYPES, encoded);
184
+ }
185
+ catch (err) {
186
+ throw new errors_1.VerificationError(`failed to decode V1 guarantee claims: ${String(err)}`);
187
+ }
188
+ const [domain, tabId, reqId, user, recipient, amount, totalAmount, asset, timestamp, claimsVersion,] = decoded;
72
189
  if (claimsVersion !== 1n) {
73
190
  throw new errors_1.VerificationError(`unsupported guarantee claims version: ${claimsVersion}`);
74
191
  }
@@ -85,3 +202,66 @@ function decodeGuaranteeClaims(data) {
85
202
  version: Number(claimsVersion),
86
203
  };
87
204
  }
205
+ function decodeV2Claims(encoded) {
206
+ try {
207
+ const [decoded] = (0, viem_1.decodeAbiParameters)(CLAIM_TYPES_V2_TUPLE, encoded);
208
+ return buildDecodedV2Claims(decoded);
209
+ }
210
+ catch (tupleErr) {
211
+ try {
212
+ const decoded = (0, viem_1.decodeAbiParameters)(CLAIM_TYPES_V2, encoded);
213
+ return buildDecodedV2Claims({
214
+ domain: decoded[0],
215
+ tabId: decoded[1],
216
+ reqId: decoded[2],
217
+ user: decoded[3],
218
+ recipient: decoded[4],
219
+ amount: decoded[5],
220
+ totalAmount: decoded[6],
221
+ asset: decoded[7],
222
+ timestamp: decoded[8],
223
+ claimsVersion: decoded[9],
224
+ validationRegistryAddress: decoded[10],
225
+ validationRequestHash: decoded[11],
226
+ validationChainId: decoded[12],
227
+ validatorAddress: decoded[13],
228
+ validatorAgentId: decoded[14],
229
+ minValidationScore: decoded[15],
230
+ validationSubjectHash: decoded[16],
231
+ requiredValidationTag: decoded[17],
232
+ });
233
+ }
234
+ catch (flatErr) {
235
+ throw new errors_1.VerificationError(`failed to decode V2 guarantee claims: ${String(tupleErr)}; fallback decode failed: ${String(flatErr)}`);
236
+ }
237
+ }
238
+ }
239
+ function buildDecodedV2Claims(decoded) {
240
+ const { domain, tabId, reqId, user, recipient, amount, totalAmount, asset, timestamp, claimsVersion, validationRegistryAddress, validationRequestHash, validationChainId, validatorAddress, validatorAgentId, minValidationScore, validationSubjectHash, requiredValidationTag, } = decoded;
241
+ if (claimsVersion !== 2n) {
242
+ throw new errors_1.VerificationError(`expected V2 claims version, got: ${claimsVersion}`);
243
+ }
244
+ const validationPolicy = {
245
+ validationRegistryAddress: validationRegistryAddress,
246
+ validationRequestHash: validationRequestHash,
247
+ validationChainId: Number(validationChainId),
248
+ validatorAddress: validatorAddress,
249
+ validatorAgentId: (0, utils_1.parseU256)(validatorAgentId),
250
+ minValidationScore: Number(minValidationScore),
251
+ validationSubjectHash: validationSubjectHash,
252
+ requiredValidationTag: requiredValidationTag,
253
+ };
254
+ return {
255
+ domain: (0, viem_1.toBytes)(domain),
256
+ userAddress: user,
257
+ recipientAddress: recipient,
258
+ tabId: (0, utils_1.parseU256)(tabId),
259
+ reqId: (0, utils_1.parseU256)(reqId),
260
+ amount: (0, utils_1.parseU256)(amount),
261
+ totalAmount: (0, utils_1.parseU256)(totalAmount),
262
+ assetAddress: asset,
263
+ timestamp: Number(timestamp),
264
+ version: Number(claimsVersion),
265
+ validationPolicy,
266
+ };
267
+ }
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ export * from './config';
4
4
  export * from './utils';
5
5
  export * from './models';
6
6
  export * from './payment';
7
+ export * from './validation';
7
8
  export * from './signing';
8
9
  export * from './rpc';
9
10
  export * from './auth';
package/dist/index.js CHANGED
@@ -20,6 +20,7 @@ __exportStar(require("./config"), exports);
20
20
  __exportStar(require("./utils"), exports);
21
21
  __exportStar(require("./models"), exports);
22
22
  __exportStar(require("./payment"), exports);
23
+ __exportStar(require("./validation"), exports);
23
24
  __exportStar(require("./signing"), exports);
24
25
  __exportStar(require("./rpc"), exports);
25
26
  __exportStar(require("./auth"), exports);