@capxul/sdk 4.20.0-beta.3 → 4.20.0-beta.31
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 +252 -33
- package/dist/access-B-2qmxF_.mjs +23227 -0
- package/dist/index-DidkO1aY.d.mts +34071 -0
- package/dist/index.d.mts +2 -653
- package/dist/index.mjs +84 -235
- package/dist/node/index.d.mts +9 -9
- package/dist/node/index.mjs +29 -38
- package/dist/organizations-wucUfgJr.mjs +12546 -0
- package/dist/permission-approvals-BLI_uadD.mjs +138 -0
- package/dist/production-2L76NWS5.mjs +4882 -0
- package/dist/testing/index.d.mts +32 -15
- package/dist/testing/index.mjs +296 -146
- package/package.json +48 -15
- package/dist/OAuthBearerAuthClient-C-ip-z8M.d.mts +0 -628
- package/dist/OAuthBearerAuthClient-DWkofxdZ.mjs +0 -2488
- package/dist/production-CJwxgI8u.d.mts +0 -3141
- package/dist/production-M4dWz5g4.mjs +0 -17131
|
@@ -1,2488 +0,0 @@
|
|
|
1
|
-
import { concat, encodeAbiParameters, encodeFunctionData, encodePacked, getContractAddress, keccak256, padHex, stringToHex, toBytes, toEventSelector, toFunctionSelector, toFunctionSignature } from "viem";
|
|
2
|
-
import { Context, Data, Effect, Layer, Result } from "effect";
|
|
3
|
-
//#region ../errors/src/secret-material.ts
|
|
4
|
-
const CREDENTIAL_FIELDS = /* @__PURE__ */ new Set([
|
|
5
|
-
"password",
|
|
6
|
-
"passwd",
|
|
7
|
-
"pwd",
|
|
8
|
-
"pass",
|
|
9
|
-
"passphrase",
|
|
10
|
-
"currentpassword",
|
|
11
|
-
"newpassword",
|
|
12
|
-
"confirmpassword",
|
|
13
|
-
"passwordconfirmation",
|
|
14
|
-
"passwordhash",
|
|
15
|
-
"hashedpassword",
|
|
16
|
-
"pin",
|
|
17
|
-
"privatekey",
|
|
18
|
-
"walletprivatekey",
|
|
19
|
-
"secretkey",
|
|
20
|
-
"signingkey",
|
|
21
|
-
"signingsecret",
|
|
22
|
-
"seed",
|
|
23
|
-
"seedphrase",
|
|
24
|
-
"mnemonic",
|
|
25
|
-
"recoveryphrase",
|
|
26
|
-
"recovery",
|
|
27
|
-
"recoverycode",
|
|
28
|
-
"recoverycodes",
|
|
29
|
-
"keyshare",
|
|
30
|
-
"signingmaterial",
|
|
31
|
-
"recoverymaterial",
|
|
32
|
-
"privatekeybytes",
|
|
33
|
-
"signingprivatekey",
|
|
34
|
-
"encryptionkey",
|
|
35
|
-
"decryptionkey",
|
|
36
|
-
"authtoken",
|
|
37
|
-
"apitoken",
|
|
38
|
-
"bearertoken",
|
|
39
|
-
"oauthtoken",
|
|
40
|
-
"csrftoken",
|
|
41
|
-
"clientassertion",
|
|
42
|
-
"token",
|
|
43
|
-
"accesstoken",
|
|
44
|
-
"refreshtoken",
|
|
45
|
-
"idtoken",
|
|
46
|
-
"sessiontoken",
|
|
47
|
-
"session",
|
|
48
|
-
"sessionkey",
|
|
49
|
-
"sessionsecret",
|
|
50
|
-
"authsessionid",
|
|
51
|
-
"sessioncredential",
|
|
52
|
-
"sessioncredentials",
|
|
53
|
-
"sessioncookie",
|
|
54
|
-
"auth",
|
|
55
|
-
"authentication",
|
|
56
|
-
"authorization",
|
|
57
|
-
"proxyauthorization",
|
|
58
|
-
"cookie",
|
|
59
|
-
"cookies",
|
|
60
|
-
"setcookie",
|
|
61
|
-
"apikey",
|
|
62
|
-
"xapikey",
|
|
63
|
-
"xauthtoken",
|
|
64
|
-
"clientsecret",
|
|
65
|
-
"secret",
|
|
66
|
-
"secrets",
|
|
67
|
-
"credential",
|
|
68
|
-
"credentials",
|
|
69
|
-
"signature",
|
|
70
|
-
"otp",
|
|
71
|
-
"otpcode",
|
|
72
|
-
"totpsecret",
|
|
73
|
-
"onetimecode",
|
|
74
|
-
"onetimepassword",
|
|
75
|
-
"verificationcode",
|
|
76
|
-
"authcode",
|
|
77
|
-
"authorizationcode",
|
|
78
|
-
"oauthcode",
|
|
79
|
-
"codeverifier",
|
|
80
|
-
"devicecode",
|
|
81
|
-
"usercode",
|
|
82
|
-
"continuitytoken",
|
|
83
|
-
"refreshattemptid",
|
|
84
|
-
"revocationtoken"
|
|
85
|
-
]);
|
|
86
|
-
/** Match credential fields by meaning, without hiding token addresses or replay IDs. */
|
|
87
|
-
function isCredentialField(name, context) {
|
|
88
|
-
const normalized = name.toLowerCase().replace(/[^a-z0-9]/gu, "");
|
|
89
|
-
return CREDENTIAL_FIELDS.has(normalized) || /(?:password(?:hash)?|passwd|passphrase|privatekey|signingkey|apikey|clientsecret|accesstoken|refreshtoken|sessiontoken)$/u.test(normalized) || context === "authentication" && (normalized === "code" || normalized === "sessionid") || context === "header" && /(?:token|secret|apikey|privatekey|signingkey|password|authorization|cookie|session)$/u.test(normalized);
|
|
90
|
-
}
|
|
91
|
-
function decodeUrlComponent(text) {
|
|
92
|
-
try {
|
|
93
|
-
return decodeURIComponent(text);
|
|
94
|
-
} catch {
|
|
95
|
-
return text;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
/** Preserve URL diagnostics while removing credentials from their declared components. */
|
|
99
|
-
function redactUrlSecrets(value, source) {
|
|
100
|
-
try {
|
|
101
|
-
const absolute = /^[a-z][a-z0-9+.-]*:/iu.test(value);
|
|
102
|
-
const protocolRelative = value.startsWith("//");
|
|
103
|
-
const url = new URL(value, "https://redaction.invalid");
|
|
104
|
-
if (url.protocol !== "https:" && url.protocol !== "http:") return maskText(value);
|
|
105
|
-
let changed = url.username.length > 0 || url.password.length > 0;
|
|
106
|
-
const maskParams = (params) => new URLSearchParams(Array.from(params, ([name, entry]) => {
|
|
107
|
-
const safeName = maskText(name);
|
|
108
|
-
const safeEntry = isCredentialField(name) || /^(?:code|draft|__posthog)$/iu.test(name) ? "[REDACTED]" : maskText(entry);
|
|
109
|
-
changed ||= safeName !== name || safeEntry !== entry;
|
|
110
|
-
return [safeName, safeEntry];
|
|
111
|
-
})).toString();
|
|
112
|
-
const provider = source === "alchemy" || source === "infura" || /(?:^|\.)(?:alchemy\.com|alchemyapi\.io|infura\.io)$/iu.test(url.hostname);
|
|
113
|
-
const rawPath = absolute || protocolRelative ? url.pathname : value.split(/[?#]/u)[0] ?? "";
|
|
114
|
-
const segments = rawPath.split("/");
|
|
115
|
-
let path = segments.map((segment, index) => {
|
|
116
|
-
const decoded = decodeUrlComponent(segment);
|
|
117
|
-
const masked = provider && /^v[23]$/u.test(decodeUrlComponent(segments[index - 1] ?? "")) && decoded.length > 0 ? "[REDACTED]" : maskText(decoded);
|
|
118
|
-
changed ||= masked !== decoded;
|
|
119
|
-
return masked === decoded ? segment : encodeURIComponent(masked);
|
|
120
|
-
}).join("/");
|
|
121
|
-
if (source === "gateway" && rawPath.length > 0) {
|
|
122
|
-
path = `${rawPath.startsWith("/") ? "/" : ""}${encodeURIComponent("[REDACTED]")}`;
|
|
123
|
-
changed = true;
|
|
124
|
-
}
|
|
125
|
-
const query = maskParams(url.searchParams);
|
|
126
|
-
const fragment = url.hash.slice(1);
|
|
127
|
-
const decodedFragment = decodeUrlComponent(fragment);
|
|
128
|
-
const maskedFragment = maskText(decodedFragment);
|
|
129
|
-
changed ||= maskedFragment !== decodedFragment;
|
|
130
|
-
const fragmentQuery = fragment.indexOf("?");
|
|
131
|
-
const fragmentPath = fragment.slice(0, fragmentQuery);
|
|
132
|
-
const maskedFragmentPath = maskText(decodeUrlComponent(fragmentPath));
|
|
133
|
-
changed ||= maskedFragmentPath !== decodeUrlComponent(fragmentPath);
|
|
134
|
-
const hash = fragmentQuery >= 0 ? `${maskedFragmentPath === decodeUrlComponent(fragmentPath) ? fragmentPath : encodeURIComponent(maskedFragmentPath)}?${maskParams(new URLSearchParams(fragment.slice(fragmentQuery + 1)))}` : fragment.includes("=") ? maskParams(new URLSearchParams(fragment)) : maskedFragment === decodedFragment ? fragment : encodeURIComponent(maskedFragment);
|
|
135
|
-
const origin = absolute ? url.origin : protocolRelative ? `//${url.host}` : "";
|
|
136
|
-
if (!changed) return value;
|
|
137
|
-
return `${origin}${path}${query ? `?${query}` : ""}${hash ? `#${hash}` : ""}`;
|
|
138
|
-
} catch {
|
|
139
|
-
if (source === "gateway") return "[REDACTED]";
|
|
140
|
-
return maskText(value.replace(/(https?:\/\/)[^/\s@]+@/giu, "$1[REDACTED]@").replace(/([?&#])([^=&#]+)=([^&#]*)/gu, (whole, separator, name) => isCredentialField(decodeUrlComponent(name)) || /^(?:code|draft|__posthog)$/iu.test(decodeUrlComponent(name)) ? `${separator}${name}=[REDACTED]` : whole));
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
const JSON_FIELD = /("(?:[^"\\]|\\.)*")(\s*:\s*)("(?:[^"\\]|\\.)*"|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|true|false|null)/gu;
|
|
144
|
-
const PROSE_FIELD = /(?=(\b([A-Za-z][A-Za-z0-9_-]*(?:[ \t]+[A-Za-z][A-Za-z0-9_-]*){0,3})\s*[:=]\s*((?:Bearer|Basic)[ \t]+[^\s,;"']+|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|[^\s,;&"']+)))/gu;
|
|
145
|
-
const EMBEDDED_URL = /(https?:\/\/[^\s"<>]*)/giu;
|
|
146
|
-
function isSecretLabel(name) {
|
|
147
|
-
return isCredentialField(name) || /^request[ _-]?body$/iu.test(name);
|
|
148
|
-
}
|
|
149
|
-
const JSON_ENCODING_MAX = 8;
|
|
150
|
-
const JSON_NESTING_MAX = 64;
|
|
151
|
-
const JSON_KEY = /("(?:[^"\\]|\\.)*")\s*:/gu;
|
|
152
|
-
const JSON_CONTAINER_START = /(?:\{\s*["}]|\[\s*[[\]{}"0-9tfn-])/uy;
|
|
153
|
-
function hasSecretJsonKey(value) {
|
|
154
|
-
for (const match of value.matchAll(JSON_KEY)) try {
|
|
155
|
-
if (match[1] !== void 0 && isSecretLabel(JSON.parse(match[1]))) return true;
|
|
156
|
-
} catch {}
|
|
157
|
-
return false;
|
|
158
|
-
}
|
|
159
|
-
/** Project a complete fragment once. Never retry all of its nested substrings. */
|
|
160
|
-
function projectJsonFragment(value, encodingDepth, overDepth) {
|
|
161
|
-
if (overDepth || encodingDepth >= JSON_ENCODING_MAX) return "[REDACTED]";
|
|
162
|
-
let parsed;
|
|
163
|
-
try {
|
|
164
|
-
parsed = JSON.parse(value);
|
|
165
|
-
} catch {
|
|
166
|
-
return hasSecretJsonKey(value) ? "[REDACTED]" : value;
|
|
167
|
-
}
|
|
168
|
-
try {
|
|
169
|
-
let changed = false;
|
|
170
|
-
const masked = JSON.stringify(parsed, (name, nested) => {
|
|
171
|
-
if (isSecretLabel(name)) {
|
|
172
|
-
changed ||= nested !== "[REDACTED]";
|
|
173
|
-
return "[REDACTED]";
|
|
174
|
-
}
|
|
175
|
-
if (typeof nested === "string") {
|
|
176
|
-
const projected = maskJsonFragments(nested, encodingDepth + 1);
|
|
177
|
-
changed ||= projected !== nested;
|
|
178
|
-
return projected;
|
|
179
|
-
}
|
|
180
|
-
return nested;
|
|
181
|
-
});
|
|
182
|
-
return changed ? masked : value;
|
|
183
|
-
} catch {
|
|
184
|
-
return "[REDACTED]";
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
/** Scan balanced object/array fragments once, respecting JSON quotes and escapes. */
|
|
188
|
-
function maskJsonFragments(value, encodingDepth = 0) {
|
|
189
|
-
if (value.trimStart().startsWith("\"")) try {
|
|
190
|
-
const decoded = JSON.parse(value);
|
|
191
|
-
if (typeof decoded === "string") {
|
|
192
|
-
if (encodingDepth >= JSON_ENCODING_MAX) return "\"[REDACTED]\"";
|
|
193
|
-
const masked = maskJsonFragments(decoded, encodingDepth + 1);
|
|
194
|
-
return masked === decoded ? value : JSON.stringify(masked);
|
|
195
|
-
}
|
|
196
|
-
} catch {}
|
|
197
|
-
let output = "";
|
|
198
|
-
let cursor = 0;
|
|
199
|
-
let start = -1;
|
|
200
|
-
let depth = 0;
|
|
201
|
-
let overDepth = false;
|
|
202
|
-
let quoted = false;
|
|
203
|
-
let escaped = false;
|
|
204
|
-
for (let index = 0; index < value.length; index++) {
|
|
205
|
-
const character = value[index];
|
|
206
|
-
if (start < 0) {
|
|
207
|
-
JSON_CONTAINER_START.lastIndex = index;
|
|
208
|
-
if (!JSON_CONTAINER_START.test(value)) continue;
|
|
209
|
-
start = index;
|
|
210
|
-
depth = 1;
|
|
211
|
-
overDepth = false;
|
|
212
|
-
continue;
|
|
213
|
-
}
|
|
214
|
-
if (quoted) {
|
|
215
|
-
if (escaped) escaped = false;
|
|
216
|
-
else if (character === "\\") escaped = true;
|
|
217
|
-
else if (character === "\"") quoted = false;
|
|
218
|
-
continue;
|
|
219
|
-
}
|
|
220
|
-
if (character === "\"") quoted = true;
|
|
221
|
-
else if (character === "{" || character === "[") {
|
|
222
|
-
depth++;
|
|
223
|
-
overDepth ||= depth > JSON_NESTING_MAX;
|
|
224
|
-
} else if (character === "}" || character === "]") {
|
|
225
|
-
depth--;
|
|
226
|
-
if (depth !== 0) continue;
|
|
227
|
-
output += value.slice(cursor, start) + projectJsonFragment(value.slice(start, index + 1), encodingDepth, overDepth);
|
|
228
|
-
cursor = index + 1;
|
|
229
|
-
start = -1;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
if (start >= 0) {
|
|
233
|
-
const fragment = value.slice(start);
|
|
234
|
-
output += value.slice(cursor, start) + (overDepth || hasSecretJsonKey(fragment) ? "[REDACTED]" : fragment);
|
|
235
|
-
cursor = value.length;
|
|
236
|
-
}
|
|
237
|
-
return output + value.slice(cursor);
|
|
238
|
-
}
|
|
239
|
-
function maskLabelledText(value) {
|
|
240
|
-
const jsonMasked = maskJsonFragments(value).replace(JSON_FIELD, (whole, quotedName, separator) => {
|
|
241
|
-
try {
|
|
242
|
-
return isSecretLabel(JSON.parse(quotedName)) ? `${quotedName}${separator}"[REDACTED]"` : whole;
|
|
243
|
-
} catch {
|
|
244
|
-
return whole;
|
|
245
|
-
}
|
|
246
|
-
});
|
|
247
|
-
let output = "";
|
|
248
|
-
let cursor = 0;
|
|
249
|
-
for (const match of jsonMasked.matchAll(PROSE_FIELD)) {
|
|
250
|
-
const [, whole, label, entry] = match;
|
|
251
|
-
if (whole === void 0 || label === void 0 || entry === void 0 || match.index < cursor || /^Bearer[ \t]+/iu.test(entry)) continue;
|
|
252
|
-
const words = label.split(/[ \t]+/u);
|
|
253
|
-
for (let index = 0; index < words.length; index++) {
|
|
254
|
-
const name = words.slice(index).join(" ");
|
|
255
|
-
if (isSecretLabel(name)) {
|
|
256
|
-
const prefix = words.slice(0, index).join(" ");
|
|
257
|
-
output += jsonMasked.slice(cursor, match.index) + `${prefix ? `${prefix} ` : ""}${name}=[REDACTED]`;
|
|
258
|
-
cursor = match.index + whole.length;
|
|
259
|
-
break;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
return output + jsonMasked.slice(cursor);
|
|
264
|
-
}
|
|
265
|
-
const TOKEN_MASKS = [
|
|
266
|
-
[/\bBearer\s+(?!(?:realm|error|error_description|scope|authorization_uri|resource|claims)\s*=)[^\s,;"']+/giu, "Bearer [REDACTED]"],
|
|
267
|
-
[/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\b/gu, "[REDACTED]"],
|
|
268
|
-
[/(^|[^a-fA-F0-9])(?<!0[xX])[a-fA-F0-9]{64}(?=$|[^a-fA-F0-9])/gu, "$1[REDACTED]"],
|
|
269
|
-
[/(?:(?:sk|pk|rk)_(?:live|test)|(?:phc|phx|ghp|gho|ghu|ghs|ghr|whsec)_|github_pat_|xox[baprs]-|AKIA[0-9A-Z]{16}|AIza)[A-Za-z0-9_-]*/giu, "[REDACTED]"]
|
|
270
|
-
];
|
|
271
|
-
/** URL components and malformed URLs use this non-recursive masking layer. */
|
|
272
|
-
function maskText(value) {
|
|
273
|
-
let text = maskLabelledText(value);
|
|
274
|
-
for (const [pattern, replacement] of TOKEN_MASKS) text = text.replace(pattern, replacement);
|
|
275
|
-
return text;
|
|
276
|
-
}
|
|
277
|
-
/** Reject only when the same semantic sanitizer removes credential material. */
|
|
278
|
-
function containsSensitiveMaterial(value) {
|
|
279
|
-
return redactSecrets(value) !== value;
|
|
280
|
-
}
|
|
281
|
-
/** Mask credential fields and embedded URLs while preserving surrounding evidence. */
|
|
282
|
-
function redactSecrets(value, source) {
|
|
283
|
-
return maskLabelledText(value).split(EMBEDDED_URL).map((part, index) => index % 2 === 1 ? redactUrlSecrets(part, source).replaceAll("%5BREDACTED%5D", "[REDACTED]") : maskText(part)).join("");
|
|
284
|
-
}
|
|
285
|
-
//#endregion
|
|
286
|
-
//#region ../errors/src/chain-cause.ts
|
|
287
|
-
/**
|
|
288
|
-
* Why an operation failed — the one flat vocabulary. The first four are the
|
|
289
|
-
* auth/provider diagnoses that predate this file. The rest name the chain
|
|
290
|
-
* path. `FailureMode` is derived from this list so a runtime membership check
|
|
291
|
-
* (the SDK's `$exception` projection, the observability schema) and the type
|
|
292
|
-
* can never drift apart.
|
|
293
|
-
*/
|
|
294
|
-
const FAILURE_MODES = [
|
|
295
|
-
"auth-origin-mismatch",
|
|
296
|
-
"stale-openfort-cache",
|
|
297
|
-
"app-env-allowlist",
|
|
298
|
-
"no-secure-context",
|
|
299
|
-
"signer-not-ready",
|
|
300
|
-
"rate-limited",
|
|
301
|
-
"timeout",
|
|
302
|
-
"upstream-down",
|
|
303
|
-
"revert",
|
|
304
|
-
"insufficient-balance",
|
|
305
|
-
"insufficient-allowance",
|
|
306
|
-
"rejected",
|
|
307
|
-
"unauthorized",
|
|
308
|
-
"forbidden",
|
|
309
|
-
"bad-request",
|
|
310
|
-
"method-unsupported",
|
|
311
|
-
"unknown"
|
|
312
|
-
];
|
|
313
|
-
function isFailureMode(value) {
|
|
314
|
-
return typeof value === "string" && FAILURE_MODES.includes(value);
|
|
315
|
-
}
|
|
316
|
-
/** Who answered. `alto` is the bundler; `gateway` is the AA gateway's own logic. */
|
|
317
|
-
const CHAIN_UPSTREAMS = [
|
|
318
|
-
"alchemy",
|
|
319
|
-
"infura",
|
|
320
|
-
"public",
|
|
321
|
-
"alto",
|
|
322
|
-
"gateway"
|
|
323
|
-
];
|
|
324
|
-
function isChainUpstream(value) {
|
|
325
|
-
return typeof value === "string" && CHAIN_UPSTREAMS.includes(value);
|
|
326
|
-
}
|
|
327
|
-
/**
|
|
328
|
-
* The response headers that name a refusal or let provider support find the
|
|
329
|
-
* request. Nothing else crosses: a header can carry a session or a key.
|
|
330
|
-
*/
|
|
331
|
-
const RESPONSE_HEADER_ALLOWLIST = [
|
|
332
|
-
"retry-after",
|
|
333
|
-
"www-authenticate",
|
|
334
|
-
"content-type",
|
|
335
|
-
"x-request-id",
|
|
336
|
-
"cf-ray",
|
|
337
|
-
"x-ratelimit-remaining",
|
|
338
|
-
"x-ratelimit-reset",
|
|
339
|
-
"server"
|
|
340
|
-
];
|
|
341
|
-
const RESPONSE_HEADER_VALUE_MAX = 128;
|
|
342
|
-
/**
|
|
343
|
-
* The allow-listed headers of one answer, masked and bounded. `read` is
|
|
344
|
-
* `Headers.get` or a lookup into a decoded record. Empty when none is present.
|
|
345
|
-
*/
|
|
346
|
-
function boundedResponseHeaders(read) {
|
|
347
|
-
const headers = {};
|
|
348
|
-
for (const name of RESPONSE_HEADER_ALLOWLIST) {
|
|
349
|
-
const value = read(name);
|
|
350
|
-
if (typeof value === "string" && value.length > 0) headers[name] = boundedEvidence(value, RESPONSE_HEADER_VALUE_MAX);
|
|
351
|
-
}
|
|
352
|
-
return headers;
|
|
353
|
-
}
|
|
354
|
-
const PROVIDER_MESSAGE_MAX = 300;
|
|
355
|
-
const METHOD_MAX = 64;
|
|
356
|
-
const REVERT_NAME_MAX = 64;
|
|
357
|
-
const REVERT_ARG_KEY_MAX = 32;
|
|
358
|
-
const REVERT_ARG_VALUE_MAX = 128;
|
|
359
|
-
const REVERT_ARGS_MAX = 8;
|
|
360
|
-
const SELECTOR_RE = /^0x(?:[0-9a-fA-F]{8})?$/u;
|
|
361
|
-
/** Mask credential-shaped content, then cap the length. Total on any string. */
|
|
362
|
-
function boundedEvidence(value, max) {
|
|
363
|
-
const text = redactSecrets(value);
|
|
364
|
-
return text.length <= max ? text : text.slice(0, max);
|
|
365
|
-
}
|
|
366
|
-
function isRecord(value) {
|
|
367
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
368
|
-
}
|
|
369
|
-
function decodeRevert(value) {
|
|
370
|
-
if (!isRecord(value)) return null;
|
|
371
|
-
if (value.name !== null && typeof value.name !== "string") return null;
|
|
372
|
-
if (typeof value.selector !== "string" || !SELECTOR_RE.test(value.selector)) return null;
|
|
373
|
-
if (!isRecord(value.args)) return null;
|
|
374
|
-
const args = {};
|
|
375
|
-
for (const [key, arg] of Object.entries(value.args).slice(0, REVERT_ARGS_MAX)) {
|
|
376
|
-
if (typeof arg !== "string") return null;
|
|
377
|
-
args[boundedEvidence(key, REVERT_ARG_KEY_MAX)] = boundedEvidence(arg, REVERT_ARG_VALUE_MAX);
|
|
378
|
-
}
|
|
379
|
-
return {
|
|
380
|
-
name: value.name === null ? null : boundedEvidence(value.name, REVERT_NAME_MAX),
|
|
381
|
-
selector: value.selector,
|
|
382
|
-
args
|
|
383
|
-
};
|
|
384
|
-
}
|
|
385
|
-
/**
|
|
386
|
-
* Decode a `ChainCause` from untrusted wire data (gateway JSON-RPC `error.data`,
|
|
387
|
-
* a Convex error payload). Every string is masked and bounded; an unrecognized
|
|
388
|
-
* shape yields null, because a consumer must never invent a cause.
|
|
389
|
-
*/
|
|
390
|
-
function decodeChainCause(value) {
|
|
391
|
-
if (!isRecord(value)) return null;
|
|
392
|
-
if (!isFailureMode(value.mode) || !isChainUpstream(value.upstream)) return null;
|
|
393
|
-
if (typeof value.method !== "string" || typeof value.retryable !== "boolean") return null;
|
|
394
|
-
const cause = {
|
|
395
|
-
mode: value.mode,
|
|
396
|
-
upstream: value.upstream,
|
|
397
|
-
method: boundedEvidence(value.method, METHOD_MAX),
|
|
398
|
-
retryable: value.retryable
|
|
399
|
-
};
|
|
400
|
-
if (typeof value.httpStatus === "number" && Number.isInteger(value.httpStatus)) cause.httpStatus = value.httpStatus;
|
|
401
|
-
if (typeof value.rpcCode === "number" && Number.isInteger(value.rpcCode)) cause.rpcCode = value.rpcCode;
|
|
402
|
-
const revert = decodeRevert(value.revert);
|
|
403
|
-
if (revert !== null) cause.revert = revert;
|
|
404
|
-
if (typeof value.providerMessage === "string") cause.providerMessage = boundedEvidence(value.providerMessage, PROVIDER_MESSAGE_MAX);
|
|
405
|
-
const attempts = decodeAttempts(value.attempts);
|
|
406
|
-
if (attempts !== null) cause.attempts = attempts;
|
|
407
|
-
if (isRecord(value.responseHeaders)) {
|
|
408
|
-
const raw = value.responseHeaders;
|
|
409
|
-
const headers = boundedResponseHeaders((name) => {
|
|
410
|
-
const header = raw[name];
|
|
411
|
-
return typeof header === "string" ? header : null;
|
|
412
|
-
});
|
|
413
|
-
if (Object.keys(headers).length > 0) cause.responseHeaders = headers;
|
|
414
|
-
}
|
|
415
|
-
return cause;
|
|
416
|
-
}
|
|
417
|
-
/** The attempt sequence, bounded. A malformed entry drops the sequence, never the cause. */
|
|
418
|
-
function decodeAttempts(value) {
|
|
419
|
-
if (!Array.isArray(value)) return null;
|
|
420
|
-
const attempts = [];
|
|
421
|
-
for (const entry of value.slice(0, 5)) {
|
|
422
|
-
if (!isRecord(entry) || !isChainUpstream(entry.upstream) || !isFailureMode(entry.mode)) return null;
|
|
423
|
-
const duration = entry.durationMs;
|
|
424
|
-
if (typeof duration !== "number" || !Number.isFinite(duration) || duration < 0) return null;
|
|
425
|
-
attempts.push({
|
|
426
|
-
upstream: entry.upstream,
|
|
427
|
-
mode: entry.mode,
|
|
428
|
-
...typeof entry.httpStatus === "number" && Number.isInteger(entry.httpStatus) ? { httpStatus: entry.httpStatus } : {},
|
|
429
|
-
...typeof entry.rpcCode === "number" && Number.isInteger(entry.rpcCode) ? { rpcCode: entry.rpcCode } : {},
|
|
430
|
-
durationMs: Math.round(duration)
|
|
431
|
-
});
|
|
432
|
-
}
|
|
433
|
-
return attempts.length === 0 ? null : attempts;
|
|
434
|
-
}
|
|
435
|
-
/**
|
|
436
|
-
* The cause as flat PostHog properties. Same keys from every producer, so one
|
|
437
|
-
* filter (`failure_mode = 'rate-limited'`) finds the gateway's, the backend's,
|
|
438
|
-
* and the SDK's rows alike.
|
|
439
|
-
*/
|
|
440
|
-
function chainCauseProperties(chain) {
|
|
441
|
-
return {
|
|
442
|
-
failure_mode: chain.mode,
|
|
443
|
-
chain_upstream: chain.upstream,
|
|
444
|
-
chain_method: chain.method,
|
|
445
|
-
chain_retryable: chain.retryable,
|
|
446
|
-
...chain.httpStatus === void 0 ? {} : { chain_http_status: chain.httpStatus },
|
|
447
|
-
...chain.rpcCode === void 0 ? {} : { chain_rpc_code: chain.rpcCode },
|
|
448
|
-
...chain.revert === void 0 ? {} : {
|
|
449
|
-
chain_revert_name: chain.revert.name ?? "unknown",
|
|
450
|
-
chain_revert_selector: chain.revert.selector,
|
|
451
|
-
chain_revert_args: chain.revert.args
|
|
452
|
-
},
|
|
453
|
-
...chain.providerMessage === void 0 ? {} : { chain_provider_message: chain.providerMessage },
|
|
454
|
-
...chain.attempts === void 0 ? {} : { chain_attempts: chain.attempts.map((attempt) => ({
|
|
455
|
-
upstream: attempt.upstream,
|
|
456
|
-
failure_mode: attempt.mode,
|
|
457
|
-
...attempt.httpStatus === void 0 ? {} : { http_status: attempt.httpStatus },
|
|
458
|
-
...attempt.rpcCode === void 0 ? {} : { rpc_code: attempt.rpcCode },
|
|
459
|
-
duration_ms: attempt.durationMs
|
|
460
|
-
})) },
|
|
461
|
-
...chain.responseHeaders === void 0 ? {} : { chain_response_headers: chain.responseHeaders }
|
|
462
|
-
};
|
|
463
|
-
}
|
|
464
|
-
/**
|
|
465
|
-
* Safe (safe-contracts v1.4.1) revert codes. The contracts revert with these
|
|
466
|
-
* short strings to save gas; the sentence lives in the Safe docs
|
|
467
|
-
* (`docs/error_codes.md`). Every Safe on the Capxul rail is this version.
|
|
468
|
-
*/
|
|
469
|
-
const SAFE_REVERT_REASONS = {
|
|
470
|
-
GS000: "could not finish initialization",
|
|
471
|
-
GS001: "threshold needs to be defined",
|
|
472
|
-
GS010: "not enough gas to execute Safe transaction",
|
|
473
|
-
GS011: "could not pay gas costs with ether",
|
|
474
|
-
GS012: "could not pay gas costs with token",
|
|
475
|
-
GS013: "Safe transaction failed when gasPrice and safeTxGas were 0",
|
|
476
|
-
GS020: "signatures data too short",
|
|
477
|
-
GS021: "invalid contract signature location: inside static part",
|
|
478
|
-
GS022: "invalid contract signature location: length not present",
|
|
479
|
-
GS023: "invalid contract signature location: data not complete",
|
|
480
|
-
GS024: "invalid contract signature provided",
|
|
481
|
-
GS025: "hash has not been approved",
|
|
482
|
-
GS026: "invalid owner provided",
|
|
483
|
-
GS030: "only owners can approve a hash",
|
|
484
|
-
GS031: "method can only be called from this contract",
|
|
485
|
-
GS100: "modules have already been initialized",
|
|
486
|
-
GS101: "invalid module address provided",
|
|
487
|
-
GS102: "module has already been added",
|
|
488
|
-
GS103: "invalid prevModule, module pair provided",
|
|
489
|
-
GS104: "method can only be called from an enabled module",
|
|
490
|
-
GS105: "invalid starting point for fetching paginated modules",
|
|
491
|
-
GS106: "invalid page size for fetching paginated modules",
|
|
492
|
-
GS200: "owners have already been set up",
|
|
493
|
-
GS201: "threshold cannot exceed owner count",
|
|
494
|
-
GS202: "threshold needs to be greater than 0",
|
|
495
|
-
GS203: "invalid owner address provided",
|
|
496
|
-
GS204: "address is already an owner",
|
|
497
|
-
GS205: "invalid prevOwner, owner pair provided",
|
|
498
|
-
GS300: "guard does not implement IERC165",
|
|
499
|
-
GS400: "fallback handler cannot be set to self"
|
|
500
|
-
};
|
|
501
|
-
/**
|
|
502
|
-
* `Error(GS205: invalid prevOwner, owner pair provided)`, or the bare selector
|
|
503
|
-
* when the name is unknown. Safe codes travel only as the `message` of an
|
|
504
|
-
* `Error(string)` revert, so only that argument is expanded from
|
|
505
|
-
* {@link SAFE_REVERT_REASONS}; every other argument reads `name=value`, even
|
|
506
|
-
* when a custom error's field happens to hold a `GS` string.
|
|
507
|
-
*/
|
|
508
|
-
function revertSummaryText(revert) {
|
|
509
|
-
if (revert.name === null) return revert.selector === "0x" ? "empty revert" : `unknown selector ${revert.selector}`;
|
|
510
|
-
const args = Object.entries(revert.args).map(([name, value]) => {
|
|
511
|
-
const reason = revert.name === "Error" && name === "message" ? SAFE_REVERT_REASONS[value] : void 0;
|
|
512
|
-
return reason === void 0 ? `${name}=${value}` : `${value}: ${reason}`;
|
|
513
|
-
}).join(", ");
|
|
514
|
-
return `${revert.name}(${args})`;
|
|
515
|
-
}
|
|
516
|
-
/**
|
|
517
|
-
* The evidence behind the one word, for an issue name: ` (HTTP 403)` or
|
|
518
|
-
* ` (JSON-RPC -32000)`. Empty when the cause carries neither. The status
|
|
519
|
-
* names the provider's front door and the code names the node's verdict;
|
|
520
|
-
* `forbidden` alone made an operator open the issue to learn it was a 403.
|
|
521
|
-
*/
|
|
522
|
-
function chainEvidenceLabel(chain) {
|
|
523
|
-
if (chain === void 0) return "";
|
|
524
|
-
if (chain.httpStatus !== void 0 && chain.httpStatus !== 200) return ` (HTTP ${String(chain.httpStatus)})`;
|
|
525
|
-
if (chain.rpcCode !== void 0) return ` (JSON-RPC ${String(chain.rpcCode)})`;
|
|
526
|
-
return "";
|
|
527
|
-
}
|
|
528
|
-
/**
|
|
529
|
-
* The error-tracking fingerprint for a failure: the code, the one-word cause,
|
|
530
|
-
* and the operation. PostHog groups `$exception` rows by this value, so one
|
|
531
|
-
* issue is one cause at one operation instead of every provider failure in
|
|
532
|
-
* one bucket (money-path audit 2026-09-02, row P1).
|
|
533
|
-
*/
|
|
534
|
-
function failureFingerprint(code, mode, operation) {
|
|
535
|
-
return `${code}:${mode ?? "none"}:${operation}`;
|
|
536
|
-
}
|
|
537
|
-
//#endregion
|
|
538
|
-
//#region ../errors/src/errors.ts
|
|
539
|
-
const CAPXUL_ERROR_CODES = [
|
|
540
|
-
"NOT_AUTHENTICATED",
|
|
541
|
-
"EMAIL_DELIVERY_FAILED",
|
|
542
|
-
"PROFILE_NOT_FOUND",
|
|
543
|
-
"SMART_ACCOUNT_MISSING",
|
|
544
|
-
"PLAYER_NOT_FOUND",
|
|
545
|
-
"ACCOUNT_NOT_FOUND",
|
|
546
|
-
"ACCOUNT_CLAIMED_BY_OTHER_SIGNER",
|
|
547
|
-
"PROVIDER_ERROR",
|
|
548
|
-
"CAPABILITY_UNAVAILABLE",
|
|
549
|
-
"INVALID_INPUT",
|
|
550
|
-
"ENV_MISSING",
|
|
551
|
-
"NOT_IMPLEMENTED",
|
|
552
|
-
"VERIFICATION_REQUIRED",
|
|
553
|
-
"INSUFFICIENT_BALANCE",
|
|
554
|
-
"INVALID_RECIPIENT",
|
|
555
|
-
"ROLE_PERMISSION_DENIED",
|
|
556
|
-
"TRANSACTION_FAILED",
|
|
557
|
-
"RATE_LIMITED",
|
|
558
|
-
"NETWORK_ERROR",
|
|
559
|
-
"UNKNOWN",
|
|
560
|
-
"OTP_EXPIRED",
|
|
561
|
-
"SIGNER_REJECTED",
|
|
562
|
-
"CANCELLED",
|
|
563
|
-
"WRONG_STATE",
|
|
564
|
-
"STALE_EPOCH",
|
|
565
|
-
"SUPERSEDED",
|
|
566
|
-
"WORK_DIED",
|
|
567
|
-
"ACTOR_STOPPED"
|
|
568
|
-
];
|
|
569
|
-
/**
|
|
570
|
-
* The error codes that represent an expected product outcome rather than a
|
|
571
|
-
* defect. The SDK and backend observation boundaries both classify failures
|
|
572
|
-
* against this set to route expected outcomes to their own PostHog event
|
|
573
|
-
* stream; keeping the single copy here (adjacent to `CAPXUL_ERROR_CODES`, so a
|
|
574
|
-
* code rename forces this set to move with it) stops the two sides of the wire
|
|
575
|
-
* from drifting and silently splitting one outcome across two streams.
|
|
576
|
-
*/
|
|
577
|
-
const EXPECTED_OPERATION_OUTCOMES = /* @__PURE__ */ new Set([
|
|
578
|
-
"INVALID_INPUT",
|
|
579
|
-
"NOT_AUTHENTICATED",
|
|
580
|
-
"SMART_ACCOUNT_MISSING",
|
|
581
|
-
"ACCOUNT_CLAIMED_BY_OTHER_SIGNER",
|
|
582
|
-
"CANCELLED",
|
|
583
|
-
"SIGNER_REJECTED",
|
|
584
|
-
"VERIFICATION_REQUIRED",
|
|
585
|
-
"INSUFFICIENT_BALANCE",
|
|
586
|
-
"INVALID_RECIPIENT",
|
|
587
|
-
"ROLE_PERMISSION_DENIED",
|
|
588
|
-
"CAPABILITY_UNAVAILABLE",
|
|
589
|
-
"RATE_LIMITED",
|
|
590
|
-
"OTP_EXPIRED",
|
|
591
|
-
"WRONG_STATE",
|
|
592
|
-
"STALE_EPOCH",
|
|
593
|
-
"SUPERSEDED",
|
|
594
|
-
"ACTOR_STOPPED"
|
|
595
|
-
]);
|
|
596
|
-
var CapxulError = class extends Error {
|
|
597
|
-
code;
|
|
598
|
-
details;
|
|
599
|
-
correlationId;
|
|
600
|
-
layer;
|
|
601
|
-
/** The one-word cause (`FailureMode`). `chain.mode` when a chain cause is set. */
|
|
602
|
-
mode;
|
|
603
|
-
/** The structured chain-path cause. Never rewritten by a later layer. */
|
|
604
|
-
chain;
|
|
605
|
-
constructor(code, message, options = {}) {
|
|
606
|
-
super(message, "cause" in options ? { cause: options.cause } : void 0);
|
|
607
|
-
this.name = "CapxulError";
|
|
608
|
-
this.code = code;
|
|
609
|
-
if (options.details !== void 0) this.details = options.details;
|
|
610
|
-
if (options.correlationId !== void 0) this.correlationId = options.correlationId;
|
|
611
|
-
if (options.layer !== void 0) this.layer = options.layer;
|
|
612
|
-
const legacy = options.details?.failure_mode;
|
|
613
|
-
const mode = options.chain?.mode ?? (isFailureMode(options.mode) ? options.mode : void 0) ?? (isFailureMode(legacy) ? legacy : void 0);
|
|
614
|
-
if (mode !== void 0) this.mode = mode;
|
|
615
|
-
if (options.chain !== void 0) this.chain = options.chain;
|
|
616
|
-
}
|
|
617
|
-
};
|
|
618
|
-
function isCapxulError(value) {
|
|
619
|
-
return value instanceof CapxulError;
|
|
620
|
-
}
|
|
621
|
-
function deserializeCapxulError(serialized) {
|
|
622
|
-
return new CapxulError(serialized.code, serialized.message, compactErrorOptions({
|
|
623
|
-
details: serialized.details,
|
|
624
|
-
correlationId: serialized.correlationId,
|
|
625
|
-
layer: serialized.layer,
|
|
626
|
-
mode: serialized.mode,
|
|
627
|
-
chain: serialized.chain === void 0 ? void 0 : decodeChainCause(serialized.chain) ?? void 0
|
|
628
|
-
}));
|
|
629
|
-
}
|
|
630
|
-
function compactErrorOptions(options) {
|
|
631
|
-
const result = {};
|
|
632
|
-
if ("cause" in options) result.cause = options.cause;
|
|
633
|
-
if (options.details !== void 0) result.details = options.details;
|
|
634
|
-
if (options.correlationId !== void 0) result.correlationId = options.correlationId;
|
|
635
|
-
if (options.layer !== void 0) result.layer = options.layer;
|
|
636
|
-
if (options.mode !== void 0) result.mode = options.mode;
|
|
637
|
-
if (options.chain !== void 0) result.chain = options.chain;
|
|
638
|
-
return result;
|
|
639
|
-
}
|
|
640
|
-
const NETWORK_DETAIL_LABEL_RE = /^[A-Za-z][A-Za-z0-9_.-]{0,63}$/u;
|
|
641
|
-
const NETWORK_TARGET_HOST_RE = /^[A-Za-z0-9.[\]:-]{1,253}$/u;
|
|
642
|
-
const DOCUMENT_VISIBILITY = /* @__PURE__ */ new Set(["hidden", "visible"]);
|
|
643
|
-
const BROWSER_LIFECYCLE_EVENTS = /* @__PURE__ */ new Set([
|
|
644
|
-
"visibility-visible",
|
|
645
|
-
"visibility-hidden",
|
|
646
|
-
"pagehide",
|
|
647
|
-
"pageshow",
|
|
648
|
-
"online",
|
|
649
|
-
"offline"
|
|
650
|
-
]);
|
|
651
|
-
function networkErrorDetails(operation, options) {
|
|
652
|
-
const details = { operation };
|
|
653
|
-
if (options === void 0) return details;
|
|
654
|
-
if (typeof options.provider === "string" && NETWORK_DETAIL_LABEL_RE.test(options.provider)) details.provider = options.provider;
|
|
655
|
-
if (typeof options.target_host === "string" && NETWORK_TARGET_HOST_RE.test(options.target_host)) details.target_host = options.target_host;
|
|
656
|
-
if (isFailureMode(options.failure_mode)) details.failure_mode = options.failure_mode;
|
|
657
|
-
if (options.reason === "connection-lost-in-flight") details.reason = options.reason;
|
|
658
|
-
if (typeof options.connection_id === "string" && NETWORK_DETAIL_LABEL_RE.test(options.connection_id)) details.connection_id = options.connection_id;
|
|
659
|
-
for (const key of [
|
|
660
|
-
"connection_count",
|
|
661
|
-
"connection_retries",
|
|
662
|
-
"close_code",
|
|
663
|
-
"ms_since_browser_event"
|
|
664
|
-
]) {
|
|
665
|
-
const value = options[key];
|
|
666
|
-
if (typeof value === "number" && Number.isSafeInteger(value) && value >= 0 && (key !== "close_code" || value <= 4999)) details[key] = value;
|
|
667
|
-
}
|
|
668
|
-
for (const key of [
|
|
669
|
-
"close_was_clean",
|
|
670
|
-
"close_reason_present",
|
|
671
|
-
"navigator_online"
|
|
672
|
-
]) {
|
|
673
|
-
const value = options[key];
|
|
674
|
-
if (typeof value === "boolean") details[key] = value;
|
|
675
|
-
}
|
|
676
|
-
if (DOCUMENT_VISIBILITY.has(options.document_visibility ?? "")) details.document_visibility = options.document_visibility;
|
|
677
|
-
if (BROWSER_LIFECYCLE_EVENTS.has(options.last_browser_event ?? "")) details.last_browser_event = options.last_browser_event;
|
|
678
|
-
return details;
|
|
679
|
-
}
|
|
680
|
-
const Errors = {
|
|
681
|
-
notAuthenticated: (message, opts) => new CapxulError("NOT_AUTHENTICATED", message ?? "Not authenticated", opts?.failure_mode ? { details: { failure_mode: opts.failure_mode } } : void 0),
|
|
682
|
-
emailDeliveryFailed: (detail) => new CapxulError("EMAIL_DELIVERY_FAILED", "Failed to send email", { details: { detail } }),
|
|
683
|
-
profileNotFound: (authUserId) => new CapxulError("PROFILE_NOT_FOUND", `Profile not found for user ${authUserId}`, { details: { authUserId } }),
|
|
684
|
-
smartAccountMissing: (authUserId) => new CapxulError("SMART_ACCOUNT_MISSING", "Smart account not provisioned", { details: { authUserId } }),
|
|
685
|
-
playerNotFound: (playerId) => new CapxulError("PLAYER_NOT_FOUND", playerId ? `Openfort player ${playerId} not found` : "Openfort player not found", playerId === void 0 ? void 0 : { details: { playerId } }),
|
|
686
|
-
accountNotFound: (accountId) => new CapxulError("ACCOUNT_NOT_FOUND", accountId ? `Openfort account ${accountId} not found` : "Openfort account not found", accountId === void 0 ? void 0 : { details: { accountId } }),
|
|
687
|
-
/**
|
|
688
|
-
* The Safe at this email's address is deployed and its owner is neither the
|
|
689
|
-
* Capxul bootstrap owner nor the caller's signer. The claim
|
|
690
|
-
* `swapOwner(bootstrap → signer)` cannot succeed: only the on-chain owner
|
|
691
|
-
* can hand the account over. Read from `getOwners()` before any
|
|
692
|
-
* UserOperation is built (2026-09-02), so no simulation and no retry.
|
|
693
|
-
*/
|
|
694
|
-
accountClaimedByOtherSigner: (details) => new CapxulError("ACCOUNT_CLAIMED_BY_OTHER_SIGNER", "This account is already owned by a different signer.", { details }),
|
|
695
|
-
providerError: (provider, operation, cause, opts) => {
|
|
696
|
-
const details = {
|
|
697
|
-
...opts?.details,
|
|
698
|
-
provider,
|
|
699
|
-
operation
|
|
700
|
-
};
|
|
701
|
-
const mode = opts?.chain?.mode ?? opts?.failure_mode;
|
|
702
|
-
if (mode !== void 0) details.failure_mode = mode;
|
|
703
|
-
if (opts?.httpStatus !== void 0) details.httpStatus = opts.httpStatus;
|
|
704
|
-
return new CapxulError("PROVIDER_ERROR", `Provider error: ${provider} ${operation}`, {
|
|
705
|
-
cause,
|
|
706
|
-
details,
|
|
707
|
-
...mode === void 0 ? {} : { mode },
|
|
708
|
-
...opts?.chain === void 0 ? {} : { chain: opts.chain }
|
|
709
|
-
});
|
|
710
|
-
},
|
|
711
|
-
capabilityUnavailable: (provider, operation) => new CapxulError("CAPABILITY_UNAVAILABLE", "Provider capability is unavailable", { details: {
|
|
712
|
-
provider,
|
|
713
|
-
operation
|
|
714
|
-
} }),
|
|
715
|
-
invalidInput: (field, reason) => new CapxulError("INVALID_INPUT", `Invalid ${field}: ${reason}`, { details: {
|
|
716
|
-
field,
|
|
717
|
-
reason
|
|
718
|
-
} }),
|
|
719
|
-
envMissing: (name) => new CapxulError("ENV_MISSING", `Environment variable ${name} not configured`, { details: { name } }),
|
|
720
|
-
notImplemented: (domain, method) => new CapxulError("NOT_IMPLEMENTED", `${domain}.${method} is not yet implemented. This feature is planned for a future release.`, { details: {
|
|
721
|
-
domain,
|
|
722
|
-
method
|
|
723
|
-
} }),
|
|
724
|
-
/**
|
|
725
|
-
* Sibling factory to {@link Errors.providerError} for the per-state timeout
|
|
726
|
-
* path in flows. Same `PROVIDER_ERROR` code as
|
|
727
|
-
* `providerError`, plus a `details.reason: "timeout"` discriminator so
|
|
728
|
-
* downstream observers can distinguish failure modes without parsing the
|
|
729
|
-
* message string. The redacted message names the timeout budget; the
|
|
730
|
-
* native `cause` carries the same information for `reportError` fidelity.
|
|
731
|
-
*/
|
|
732
|
-
providerTimeout: (provider, operation, timeoutMs) => new CapxulError("PROVIDER_ERROR", `Provider error: ${provider} ${operation} (timeout exceeded ${timeoutMs}ms)`, {
|
|
733
|
-
details: {
|
|
734
|
-
provider,
|
|
735
|
-
operation,
|
|
736
|
-
reason: "timeout"
|
|
737
|
-
},
|
|
738
|
-
cause: /* @__PURE__ */ new Error(`timeout: ${operation} exceeded ${timeoutMs}ms`)
|
|
739
|
-
}),
|
|
740
|
-
verificationRequired: (details) => {
|
|
741
|
-
return new CapxulError("VERIFICATION_REQUIRED", "rail" in details ? `Verification is required before ${details.rail} can use ${details.currentKind}.` : `Verification tier ${details.requiredTier} is required.`, { details });
|
|
742
|
-
},
|
|
743
|
-
insufficientBalance: (asset, available, required) => new CapxulError("INSUFFICIENT_BALANCE", `Insufficient ${asset} balance`, { details: {
|
|
744
|
-
asset,
|
|
745
|
-
available,
|
|
746
|
-
required
|
|
747
|
-
} }),
|
|
748
|
-
invalidRecipient: (reason) => new CapxulError("INVALID_RECIPIENT", `Invalid recipient: ${reason}`, { details: { reason } }),
|
|
749
|
-
/**
|
|
750
|
-
* The org Zodiac Roles modifier REFUSED the spend on-chain (G4 · #547): the
|
|
751
|
-
* member's role condition (per-tx cap, per-day allowance, allowed recipient,
|
|
752
|
-
* or membership) was violated, so `execTransactionWithRole` reverted. This is
|
|
753
|
-
* a PERMISSION denial — explicitly NOT an `INSUFFICIENT_BALANCE` (the treasury
|
|
754
|
-
* held the funds; the role's authority is what bound). `reason` discriminates
|
|
755
|
-
* the violated condition (`over_cap` / `daily_cap` / `not_member` /
|
|
756
|
-
* `disallowed_recipient` / `condition_violation`); leak-safe — no on-chain
|
|
757
|
-
* identifiers ever enter the details.
|
|
758
|
-
*/
|
|
759
|
-
rolePermissionDenied: (details) => new CapxulError("ROLE_PERMISSION_DENIED", `Org role denied this spend on-chain (${details.reason}).`, { details: details.operation === void 0 ? { reason: details.reason } : {
|
|
760
|
-
reason: details.reason,
|
|
761
|
-
operation: details.operation
|
|
762
|
-
} }),
|
|
763
|
-
/**
|
|
764
|
-
* A transaction (or sponsored UserOp) failed. `details.reason` discriminates
|
|
765
|
-
* the failure mode for callers that must distinguish a CONFIRMED on-chain
|
|
766
|
-
* revert (`"onchain_revert"` — the op executed and reverted, e.g. a Zodiac
|
|
767
|
-
* Roles condition violation) from an inconclusive infra failure. A confirmed
|
|
768
|
-
* revert is the ONLY mode the org spend port may map to a roles denial.
|
|
769
|
-
*/
|
|
770
|
-
transactionFailed: (operation, cause, extra) => new CapxulError("TRANSACTION_FAILED", `Transaction failed: ${operation}`, {
|
|
771
|
-
cause,
|
|
772
|
-
details: extra?.reason === void 0 ? { operation } : {
|
|
773
|
-
operation,
|
|
774
|
-
reason: extra.reason
|
|
775
|
-
}
|
|
776
|
-
}),
|
|
777
|
-
rateLimited: (details) => new CapxulError("RATE_LIMITED", "Rate limit exceeded", details === void 0 ? void 0 : { details: { ...details } }),
|
|
778
|
-
networkError: (operation, cause, opts) => {
|
|
779
|
-
const mode = isFailureMode(opts?.failure_mode) ? opts.failure_mode : void 0;
|
|
780
|
-
return new CapxulError("NETWORK_ERROR", `Network error during ${operation}`, {
|
|
781
|
-
cause,
|
|
782
|
-
details: networkErrorDetails(operation, opts),
|
|
783
|
-
...mode === void 0 ? {} : { mode }
|
|
784
|
-
});
|
|
785
|
-
},
|
|
786
|
-
unknown: (cause) => new CapxulError("UNKNOWN", "Unknown error", { cause }),
|
|
787
|
-
otpExpired: (details) => new CapxulError("OTP_EXPIRED", "Verification code has expired. Request a new one.", details === void 0 ? void 0 : { details: { ...details } }),
|
|
788
|
-
signerRejected: (details) => new CapxulError("SIGNER_REJECTED", "Signer rejected the request.", {
|
|
789
|
-
cause: details.cause,
|
|
790
|
-
details: details.reason === void 0 ? { source: details.source } : {
|
|
791
|
-
source: details.source,
|
|
792
|
-
reason: details.reason
|
|
793
|
-
}
|
|
794
|
-
}),
|
|
795
|
-
superseded: (details) => new CapxulError("SUPERSEDED", "Operation was superseded.", { details: {
|
|
796
|
-
operation: details.operation,
|
|
797
|
-
reason: details.reason,
|
|
798
|
-
...details.executionId === void 0 ? {} : { executionId: details.executionId }
|
|
799
|
-
} }),
|
|
800
|
-
cancelled: (details) => new CapxulError("CANCELLED", "Operation was cancelled.", details === void 0 ? void 0 : { details: { ...details } }),
|
|
801
|
-
/**
|
|
802
|
-
* Method called from a flow state where its precondition fails (TA16). The
|
|
803
|
-
* SDK's method API short-circuits with this error before driving the
|
|
804
|
-
* internal state machine. `currentState` is the Effect-machine snapshot
|
|
805
|
-
* tag (stringified from the SDK's actor-shell snapshot; see
|
|
806
|
-
* `packages/errors/CONTEXT.md`); `validStates`
|
|
807
|
-
* enumerates the states the method accepts.
|
|
808
|
-
*/
|
|
809
|
-
wrongState: (details) => new CapxulError("WRONG_STATE", `${details.method} called from state '${details.currentState}'; valid states: ${details.validStates.join(", ")}`, { details: {
|
|
810
|
-
...details,
|
|
811
|
-
validStates: [...details.validStates]
|
|
812
|
-
} })
|
|
813
|
-
};
|
|
814
|
-
//#endregion
|
|
815
|
-
//#region ../errors/src/convex-error-decoding.ts
|
|
816
|
-
const KNOWN_CODES = new Set(CAPXUL_ERROR_CODES);
|
|
817
|
-
function isCapxulCode(value) {
|
|
818
|
-
return typeof value === "string" && KNOWN_CODES.has(value);
|
|
819
|
-
}
|
|
820
|
-
function reconstruct(serialized) {
|
|
821
|
-
if (!isCapxulCode(serialized.code)) return null;
|
|
822
|
-
const payload = {
|
|
823
|
-
code: serialized.code,
|
|
824
|
-
message: typeof serialized.message === "string" ? serialized.message : String(serialized.code),
|
|
825
|
-
...typeof serialized.details === "object" && serialized.details !== null && !Array.isArray(serialized.details) ? { details: serialized.details } : {},
|
|
826
|
-
...typeof serialized.correlationId === "string" ? { correlationId: serialized.correlationId } : {},
|
|
827
|
-
...typeof serialized.layer === "string" ? { layer: serialized.layer } : {},
|
|
828
|
-
...isFailureMode(serialized.mode) ? { mode: serialized.mode } : {}
|
|
829
|
-
};
|
|
830
|
-
const chain = decodeChainCause(serialized.chain);
|
|
831
|
-
return deserializeCapxulError(chain === null ? payload : {
|
|
832
|
-
...payload,
|
|
833
|
-
chain
|
|
834
|
-
});
|
|
835
|
-
}
|
|
836
|
-
function decodeConvexError(err) {
|
|
837
|
-
if (err === null || err === void 0) return null;
|
|
838
|
-
if (err instanceof CapxulError) return err;
|
|
839
|
-
if (typeof err !== "object") return null;
|
|
840
|
-
const record = err;
|
|
841
|
-
if (!("data" in record)) return null;
|
|
842
|
-
const data = record.data;
|
|
843
|
-
if (typeof data === "object" && data !== null) return reconstruct(data);
|
|
844
|
-
if (typeof data === "string") try {
|
|
845
|
-
const parsed = JSON.parse(data);
|
|
846
|
-
if (typeof parsed === "object" && parsed !== null) return reconstruct(parsed);
|
|
847
|
-
} catch {}
|
|
848
|
-
return null;
|
|
849
|
-
}
|
|
850
|
-
//#endregion
|
|
851
|
-
//#region ../errors/src/index.ts
|
|
852
|
-
/** Canonical handle syntax shared by validation and branded constructors. */
|
|
853
|
-
const HANDLE_RE = /^[a-z0-9_-]{3,30}$/;
|
|
854
|
-
//#endregion
|
|
855
|
-
//#region ../config/src/handle.ts
|
|
856
|
-
const RESERVED_HANDLES = /* @__PURE__ */ new Set([
|
|
857
|
-
"admin",
|
|
858
|
-
"api",
|
|
859
|
-
"capxul",
|
|
860
|
-
"help",
|
|
861
|
-
"root",
|
|
862
|
-
"support",
|
|
863
|
-
"system",
|
|
864
|
-
"www"
|
|
865
|
-
]);
|
|
866
|
-
function normalizeHandle(raw) {
|
|
867
|
-
return raw.trim().toLowerCase().replace(/^@/, "");
|
|
868
|
-
}
|
|
869
|
-
function validateHandle(raw) {
|
|
870
|
-
const handle = normalizeHandle(raw);
|
|
871
|
-
if (!HANDLE_RE.test(handle)) throw Errors.invalidInput("handle", "must be 3-30 characters of a-z, 0-9, underscore, or hyphen");
|
|
872
|
-
if (RESERVED_HANDLES.has(handle)) throw Errors.invalidInput("handle", "handle is reserved");
|
|
873
|
-
return handle;
|
|
874
|
-
}
|
|
875
|
-
//#endregion
|
|
876
|
-
//#region ../types/src/index.ts
|
|
877
|
-
const ASSET_ID_RE = /^eip155:([1-9][0-9]*)\/erc20:(0x[0-9a-f]{40})$/;
|
|
878
|
-
const EVM_ADDRESS_RE = /^0x[0-9a-f]{40}$/i;
|
|
879
|
-
const BYTES32_RE = /^0x[0-9a-f]{64}$/i;
|
|
880
|
-
const ZERO_BYTES32 = `0x${"0".repeat(64)}`;
|
|
881
|
-
const PUBLISHABLE_KEY_PATTERN = /^cap_pk_(test|live)_[0-9A-HJKMNP-TV-Z]{32}$/;
|
|
882
|
-
const SUPPORTED_CURRENCIES = [
|
|
883
|
-
{
|
|
884
|
-
code: "USD",
|
|
885
|
-
symbol: "$",
|
|
886
|
-
name: "US Dollar"
|
|
887
|
-
},
|
|
888
|
-
{
|
|
889
|
-
code: "NGN",
|
|
890
|
-
symbol: "NGN",
|
|
891
|
-
name: "Nigerian Naira"
|
|
892
|
-
},
|
|
893
|
-
{
|
|
894
|
-
code: "GHS",
|
|
895
|
-
symbol: "GHS",
|
|
896
|
-
name: "Ghanaian Cedi"
|
|
897
|
-
},
|
|
898
|
-
{
|
|
899
|
-
code: "KES",
|
|
900
|
-
symbol: "KSh",
|
|
901
|
-
name: "Kenyan Shilling"
|
|
902
|
-
},
|
|
903
|
-
{
|
|
904
|
-
code: "UGX",
|
|
905
|
-
symbol: "USh",
|
|
906
|
-
name: "Ugandan Shilling"
|
|
907
|
-
}
|
|
908
|
-
];
|
|
909
|
-
const SUPPORTED_CURRENCY_CODES = SUPPORTED_CURRENCIES.map((currency) => currency.code);
|
|
910
|
-
const CURRENCY_SYMBOLS = Object.fromEntries(SUPPORTED_CURRENCIES.map((currency) => [currency.code, currency.symbol]));
|
|
911
|
-
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
912
|
-
const COUNTRY_CODE_RE = /^[A-Z]{2}$/;
|
|
913
|
-
const ACCOUNT_ID_RE = /^account_[0-9A-Za-z]+$/;
|
|
914
|
-
const PARTY_ID_RE = /^party_[0-9A-Za-z]+$/;
|
|
915
|
-
const APP_ID_RE = /^app_[0-7][0-9A-HJKMNP-TV-Z]{25}$/;
|
|
916
|
-
const CLIENT_REQUEST_ID_RE = /^creq_[0-7][0-9A-HJKMNP-TV-Z]{25}$/;
|
|
917
|
-
const CLIENT_GRANT_ID_RE = /^cgrant_[0-7][0-9A-HJKMNP-TV-Z]{25}$/;
|
|
918
|
-
const TX_HASH_RE = /^0x[0-9a-f]{64}$/i;
|
|
919
|
-
const WEI_RE = /^[0-9]+$/;
|
|
920
|
-
Math.floor(Number.MAX_SAFE_INTEGER / 1e3);
|
|
921
|
-
function toAddress(raw) {
|
|
922
|
-
if (typeof raw !== "string" || !EVM_ADDRESS_RE.test(raw)) throw Errors.invalidInput("address", invalidValueReason("invalid EVM address format", raw));
|
|
923
|
-
return raw.toLowerCase();
|
|
924
|
-
}
|
|
925
|
-
function toEmail(raw) {
|
|
926
|
-
if (typeof raw !== "string" || !EMAIL_RE.test(raw)) throw Errors.invalidInput("email", invalidValueReason("must look like an email address", raw));
|
|
927
|
-
return raw.toLowerCase();
|
|
928
|
-
}
|
|
929
|
-
function toAuthUserId(raw) {
|
|
930
|
-
return toNonEmptyStringBrand(raw, "authUserId");
|
|
931
|
-
}
|
|
932
|
-
function toAccountId(raw) {
|
|
933
|
-
if (typeof raw !== "string" || !ACCOUNT_ID_RE.test(raw)) throw Errors.invalidInput("accountId", invalidValueReason("must be account_ plus an alphanumeric id", raw));
|
|
934
|
-
return raw;
|
|
935
|
-
}
|
|
936
|
-
function toPartyId(raw) {
|
|
937
|
-
if (typeof raw !== "string" || !PARTY_ID_RE.test(raw)) throw Errors.invalidInput("partyId", invalidValueReason("must be party_ plus an alphanumeric id", raw));
|
|
938
|
-
return raw;
|
|
939
|
-
}
|
|
940
|
-
function toHandle(raw) {
|
|
941
|
-
if (typeof raw !== "string" || !HANDLE_RE.test(raw)) throw Errors.invalidInput("handle", invalidValueReason("must be 3-30 characters of a-z, 0-9, underscore, or hyphen", raw));
|
|
942
|
-
return raw;
|
|
943
|
-
}
|
|
944
|
-
function toBudgetId(raw) {
|
|
945
|
-
return toNonEmptyStringBrand(raw, "budgetId");
|
|
946
|
-
}
|
|
947
|
-
function toPayrollRunId(raw) {
|
|
948
|
-
return toNonEmptyStringBrand(raw, "payrollRunId");
|
|
949
|
-
}
|
|
950
|
-
function toPayrollGroupId(raw) {
|
|
951
|
-
return toNonEmptyStringBrand(raw, "payrollGroupId");
|
|
952
|
-
}
|
|
953
|
-
function toOrgId(raw) {
|
|
954
|
-
return toNonEmptyStringBrand(raw, "orgId");
|
|
955
|
-
}
|
|
956
|
-
function toAppId(raw) {
|
|
957
|
-
if (typeof raw !== "string" || !APP_ID_RE.test(raw)) throw Errors.invalidInput("appId", invalidValueReason("must be app_ plus a ULID", raw));
|
|
958
|
-
return raw;
|
|
959
|
-
}
|
|
960
|
-
function toAllowedOrigin(raw) {
|
|
961
|
-
if (typeof raw !== "string") throw Errors.invalidInput("allowedOrigin", "must be an http or https origin string");
|
|
962
|
-
const normalized = normalizeAllowedOrigin(raw);
|
|
963
|
-
if (normalized === null) throw Errors.invalidInput("allowedOrigin", invalidValueReason("must be an http or https origin", raw));
|
|
964
|
-
return normalized;
|
|
965
|
-
}
|
|
966
|
-
function toDurationMs(raw) {
|
|
967
|
-
if (typeof raw !== "number" || !Number.isSafeInteger(raw) || raw < 0) throw Errors.invalidInput("duration", invalidValueReason("must be a non-negative safe integer", raw));
|
|
968
|
-
return raw;
|
|
969
|
-
}
|
|
970
|
-
function toPublishableKey(raw) {
|
|
971
|
-
if (typeof raw !== "string" || !PUBLISHABLE_KEY_PATTERN.test(raw)) throw Errors.invalidInput("publishableKey", invalidValueReason("must match cap_pk_(test|live) plus 32 Crockford base32 chars", raw));
|
|
972
|
-
return raw;
|
|
973
|
-
}
|
|
974
|
-
function toTxHash(raw) {
|
|
975
|
-
if (typeof raw !== "string" || !TX_HASH_RE.test(raw)) throw Errors.invalidInput("txHash", invalidValueReason("must be 0x + 64 hex chars", raw));
|
|
976
|
-
return raw.toLowerCase();
|
|
977
|
-
}
|
|
978
|
-
function toEpochMs(raw) {
|
|
979
|
-
assertSafeNonNegativeInteger(raw, "epochMs");
|
|
980
|
-
return raw;
|
|
981
|
-
}
|
|
982
|
-
function toEpochSeconds(raw) {
|
|
983
|
-
assertSafeNonNegativeInteger(raw, "epochSeconds");
|
|
984
|
-
return raw;
|
|
985
|
-
}
|
|
986
|
-
/** Validate an already canonical identity without changing signed input. */
|
|
987
|
-
function toAssetId(raw) {
|
|
988
|
-
const match = typeof raw === "string" ? ASSET_ID_RE.exec(raw) : null;
|
|
989
|
-
if (match === null || match[0] !== raw || !Number.isSafeInteger(Number(match[1]))) throw Errors.invalidInput("assetId", "must be a canonical ERC-20 CAIP-19 identifier");
|
|
990
|
-
if (match[2] === "0x0000000000000000000000000000000000000000") throw Errors.invalidInput("assetId", "must identify a nonzero token contract");
|
|
991
|
-
return raw;
|
|
992
|
-
}
|
|
993
|
-
/** Build an identity from verified chain facts. Normalize the token address. */
|
|
994
|
-
function assetIdFor(chainId, tokenAddress) {
|
|
995
|
-
return toAssetId(`eip155:${toChainId(chainId)}/erc20:${toAddress(tokenAddress)}`);
|
|
996
|
-
}
|
|
997
|
-
function toChainId(raw) {
|
|
998
|
-
if (typeof raw !== "number" || !Number.isSafeInteger(raw) || raw <= 0) throw Errors.invalidInput("chainId", invalidValueReason("must be a positive safe integer", raw));
|
|
999
|
-
return raw;
|
|
1000
|
-
}
|
|
1001
|
-
function toWeiAmount(raw) {
|
|
1002
|
-
if (typeof raw !== "string" || !WEI_RE.test(raw)) throw Errors.invalidInput("weiAmount", invalidValueReason("must be a non-negative integer string", raw));
|
|
1003
|
-
return raw;
|
|
1004
|
-
}
|
|
1005
|
-
function toCountryCode(raw) {
|
|
1006
|
-
if (typeof raw !== "string") throw Errors.invalidInput("countryCode", "must be a string");
|
|
1007
|
-
const upper = raw.toUpperCase();
|
|
1008
|
-
if (!COUNTRY_CODE_RE.test(upper)) throw Errors.invalidInput("countryCode", invalidValueReason("must be a 2-letter ISO 3166-1 alpha-2 code", raw));
|
|
1009
|
-
return upper;
|
|
1010
|
-
}
|
|
1011
|
-
function toCurrencyCode(raw) {
|
|
1012
|
-
if (typeof raw !== "string" || !SUPPORTED_CURRENCY_CODES.includes(raw)) throw Errors.invalidInput("currencyCode", invalidValueReason("unsupported currency", raw));
|
|
1013
|
-
return raw;
|
|
1014
|
-
}
|
|
1015
|
-
function currencySymbolFor(code) {
|
|
1016
|
-
const symbol = CURRENCY_SYMBOLS[code];
|
|
1017
|
-
if (symbol === void 0) throw Errors.invalidInput("currencyCode", `no symbol registered for "${String(code)}"`);
|
|
1018
|
-
return symbol;
|
|
1019
|
-
}
|
|
1020
|
-
function toTesterKind(raw) {
|
|
1021
|
-
if (raw !== "human" && raw !== "e2e" && raw !== "agent") throw Errors.invalidInput("testerKind", "must be human, e2e, or agent");
|
|
1022
|
-
return raw;
|
|
1023
|
-
}
|
|
1024
|
-
function toKycTier(raw) {
|
|
1025
|
-
if (typeof raw !== "number" || !Number.isInteger(raw) || raw < 0 || raw > 3) throw Errors.invalidInput("kycTier", invalidValueReason("must be an integer in [0, 3]", raw));
|
|
1026
|
-
return raw;
|
|
1027
|
-
}
|
|
1028
|
-
function toRoleKey(raw) {
|
|
1029
|
-
if (typeof raw !== "string" || !BYTES32_RE.test(raw)) throw Errors.invalidInput("roleKey", invalidValueReason("must be 0x + 64 hex chars", raw));
|
|
1030
|
-
return raw.toLowerCase();
|
|
1031
|
-
}
|
|
1032
|
-
function toSessionToken(raw) {
|
|
1033
|
-
if (typeof raw !== "string" || raw.length === 0) throw Errors.invalidInput("token", "must be a non-empty string");
|
|
1034
|
-
return raw;
|
|
1035
|
-
}
|
|
1036
|
-
function toJwtToken(raw) {
|
|
1037
|
-
if (typeof raw !== "string" || raw.length === 0) throw Errors.invalidInput("jwtToken", "must be a non-empty string");
|
|
1038
|
-
return raw;
|
|
1039
|
-
}
|
|
1040
|
-
function toNonEmptyStringBrand(raw, field) {
|
|
1041
|
-
if (typeof raw !== "string" || raw.length === 0) throw Errors.invalidInput(field, "must be a non-empty string");
|
|
1042
|
-
return raw;
|
|
1043
|
-
}
|
|
1044
|
-
function assertSafeNonNegativeInteger(raw, field) {
|
|
1045
|
-
if (typeof raw !== "number" || !Number.isSafeInteger(raw) || raw < 0) throw Errors.invalidInput(field, invalidValueReason("must be a non-negative safe integer", raw));
|
|
1046
|
-
}
|
|
1047
|
-
function normalizeAllowedOrigin(raw) {
|
|
1048
|
-
let parsed;
|
|
1049
|
-
try {
|
|
1050
|
-
parsed = new URL(raw);
|
|
1051
|
-
} catch {
|
|
1052
|
-
return null;
|
|
1053
|
-
}
|
|
1054
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return null;
|
|
1055
|
-
if (parsed.hostname.includes("*")) return null;
|
|
1056
|
-
return parsed.origin;
|
|
1057
|
-
}
|
|
1058
|
-
function invalidValueReason(prefix, raw) {
|
|
1059
|
-
if (typeof raw === "string") return `${prefix}: ${raw.slice(0, 40)}`;
|
|
1060
|
-
return `${prefix}: ${String(raw)}`;
|
|
1061
|
-
}
|
|
1062
|
-
//#endregion
|
|
1063
|
-
//#region ../config/src/tokens.ts
|
|
1064
|
-
/** `TestUSDC` ("USDX") — Base Sepolia, 6 decimals, open `mint`. (Canon §1.) */
|
|
1065
|
-
const USDX_ADDRESS_BASE_SEPOLIA = "0xf09fcbb6df9f8f918e58f9c8ab15a76ade862b77";
|
|
1066
|
-
const USDX_DEPLOYMENT_START_BLOCK = 39860173;
|
|
1067
|
-
/**
|
|
1068
|
-
* The ERC-20 `symbol()` the deployed `TestUSDC` contract returns
|
|
1069
|
-
* (`packages/contracts/src/TestUSDC.sol:10`). It travels with the address and
|
|
1070
|
-
* the decimals so a holdings row can never carry one token's address with
|
|
1071
|
-
* another token's display symbol. This is the display symbol, not the
|
|
1072
|
-
* currency: see `USDX_CURRENCY` below.
|
|
1073
|
-
*/
|
|
1074
|
-
const USDX_SYMBOL = "USDX";
|
|
1075
|
-
const SYNTHETIC_TEST_ASSET_SOURCE = "synthetic-test-liquidity";
|
|
1076
|
-
const USDX_ASSET = {
|
|
1077
|
-
assetId: assetIdFor(84532, USDX_ADDRESS_BASE_SEPOLIA),
|
|
1078
|
-
chainId: toChainId(84532),
|
|
1079
|
-
symbol: USDX_SYMBOL,
|
|
1080
|
-
peg: toCurrencyCode("USD"),
|
|
1081
|
-
tokenAddress: USDX_ADDRESS_BASE_SEPOLIA,
|
|
1082
|
-
deploymentStartBlock: USDX_DEPLOYMENT_START_BLOCK,
|
|
1083
|
-
decimals: 6,
|
|
1084
|
-
source: "settlement"
|
|
1085
|
-
};
|
|
1086
|
-
/**
|
|
1087
|
-
* The settlement tier: rows the exact-transfer path and the Movement scanner
|
|
1088
|
-
* admit, each carrying the legacy `currency` projection those readers expect.
|
|
1089
|
-
* Adding an owned test asset here would put candidate liquidity on the
|
|
1090
|
-
* money-write path, so M03's rows go to the candidate tier below instead.
|
|
1091
|
-
*/
|
|
1092
|
-
const CONFIGURED_MONEY_ASSETS = [{
|
|
1093
|
-
...USDX_ASSET,
|
|
1094
|
-
currency: USDX_ASSET.peg
|
|
1095
|
-
}];
|
|
1096
|
-
/**
|
|
1097
|
-
* The candidate tier: five owned test assets, every field read back from Base
|
|
1098
|
-
* Sepolia after the deployment that created it (chain 84532, blocks
|
|
1099
|
-
* 46,465,978–46,465,982). Real contracts, authored liquidity, no settlement
|
|
1100
|
-
* admission — so rollback is removing consumers, never relabelling a
|
|
1101
|
-
* deployed address or deleting its history.
|
|
1102
|
-
*
|
|
1103
|
-
* `symbol` is the display string the owned contract returns. A
|
|
1104
|
-
* provider-supported USDC would be a different contract at a different
|
|
1105
|
-
* address returning the same symbol, which is why the resolvers below key on
|
|
1106
|
-
* `assetId`, or on chain plus address, and never on a symbol.
|
|
1107
|
-
*/
|
|
1108
|
-
const SYNTHETIC_TEST_ASSETS = [
|
|
1109
|
-
{
|
|
1110
|
-
assetId: assetIdFor(84532, "0xb3d8566fb90f7df939f4bc08a09b19047117f549"),
|
|
1111
|
-
chainId: toChainId(84532),
|
|
1112
|
-
tokenAddress: "0xb3d8566fb90f7df939f4bc08a09b19047117f549",
|
|
1113
|
-
symbol: "USDC",
|
|
1114
|
-
decimals: 6,
|
|
1115
|
-
peg: toCurrencyCode("USD"),
|
|
1116
|
-
deploymentStartBlock: 46465978,
|
|
1117
|
-
deploymentTransaction: "0xa7fb8bb9d1fd1cc5fe2fc42fa4cdccace7742efae8ea99f091a283e5709343de",
|
|
1118
|
-
source: SYNTHETIC_TEST_ASSET_SOURCE
|
|
1119
|
-
},
|
|
1120
|
-
{
|
|
1121
|
-
assetId: assetIdFor(84532, "0x7ddcfb6aaffb9908c4c2db308b784c269eec8a01"),
|
|
1122
|
-
chainId: toChainId(84532),
|
|
1123
|
-
tokenAddress: "0x7ddcfb6aaffb9908c4c2db308b784c269eec8a01",
|
|
1124
|
-
symbol: "USDT",
|
|
1125
|
-
decimals: 6,
|
|
1126
|
-
peg: toCurrencyCode("USD"),
|
|
1127
|
-
deploymentStartBlock: 46465979,
|
|
1128
|
-
deploymentTransaction: "0xa17ad76fe8b24cdf1fbcff245c3eb022444710cf9fa7b5ee59818be654f4ae10",
|
|
1129
|
-
source: SYNTHETIC_TEST_ASSET_SOURCE
|
|
1130
|
-
},
|
|
1131
|
-
{
|
|
1132
|
-
assetId: assetIdFor(84532, "0x1d93d525f73453fe18eebf044e8a3954bfe3721e"),
|
|
1133
|
-
chainId: toChainId(84532),
|
|
1134
|
-
tokenAddress: "0x1d93d525f73453fe18eebf044e8a3954bfe3721e",
|
|
1135
|
-
symbol: "WETH",
|
|
1136
|
-
decimals: 18,
|
|
1137
|
-
peg: null,
|
|
1138
|
-
deploymentStartBlock: 46465980,
|
|
1139
|
-
deploymentTransaction: "0xcb9b2d024686fac93ec1243ccf02d3567fd531da46176115660cba2ba644b09d",
|
|
1140
|
-
source: SYNTHETIC_TEST_ASSET_SOURCE
|
|
1141
|
-
},
|
|
1142
|
-
{
|
|
1143
|
-
assetId: assetIdFor(84532, "0x8fe52c70aa9f7b6d74d8bb26eca33d87413cb624"),
|
|
1144
|
-
chainId: toChainId(84532),
|
|
1145
|
-
tokenAddress: "0x8fe52c70aa9f7b6d74d8bb26eca33d87413cb624",
|
|
1146
|
-
symbol: "WBTC",
|
|
1147
|
-
decimals: 8,
|
|
1148
|
-
peg: null,
|
|
1149
|
-
deploymentStartBlock: 46465981,
|
|
1150
|
-
deploymentTransaction: "0x728ba7b430ffd141f83d0ed5ce429d050049d7b097c936fd11e9c572d1243854",
|
|
1151
|
-
source: SYNTHETIC_TEST_ASSET_SOURCE
|
|
1152
|
-
},
|
|
1153
|
-
{
|
|
1154
|
-
assetId: assetIdFor(84532, "0x5865fe9787ac214feb5facaebaec06969ac0a9a3"),
|
|
1155
|
-
chainId: toChainId(84532),
|
|
1156
|
-
tokenAddress: "0x5865fe9787ac214feb5facaebaec06969ac0a9a3",
|
|
1157
|
-
symbol: "cNGN",
|
|
1158
|
-
decimals: 6,
|
|
1159
|
-
peg: toCurrencyCode("NGN"),
|
|
1160
|
-
deploymentStartBlock: 46465982,
|
|
1161
|
-
deploymentTransaction: "0xcedf254bcef57fc64189261a5ab1161bc4fde370e470933f627c4d8ddb5a2e26",
|
|
1162
|
-
source: SYNTHETIC_TEST_ASSET_SOURCE
|
|
1163
|
-
}
|
|
1164
|
-
];
|
|
1165
|
-
toChainId(84532);
|
|
1166
|
-
/**
|
|
1167
|
-
* Every known asset in ONE identity space. The tiers above declare admission;
|
|
1168
|
-
* this is the only list an identity resolves against, so an asset is never
|
|
1169
|
-
* two different things depending on which list a caller happened to read.
|
|
1170
|
-
*/
|
|
1171
|
-
const MONEY_ASSET_REGISTRY = [...CONFIGURED_MONEY_ASSETS, ...SYNTHETIC_TEST_ASSETS];
|
|
1172
|
-
CONFIGURED_MONEY_ASSETS.map((asset) => asset.tokenAddress.toLowerCase());
|
|
1173
|
-
/** The registry row for `assetId` across every tier, or `null`. */
|
|
1174
|
-
function configuredMoneyAssetById(assetId) {
|
|
1175
|
-
const canonical = toAssetId(assetId);
|
|
1176
|
-
return MONEY_ASSET_REGISTRY.find((asset) => asset.assetId === canonical) ?? null;
|
|
1177
|
-
}
|
|
1178
|
-
//#endregion
|
|
1179
|
-
//#region ../config/src/org-payments.ts
|
|
1180
|
-
/** Zodiac MultiSendCallOnly module on Base Sepolia. */
|
|
1181
|
-
const MULTI_SEND_CALL_ONLY = "0x9641d764fc13c8b624c04430c7356c1c7c8102e2";
|
|
1182
|
-
//#endregion
|
|
1183
|
-
//#region ../config/src/safe.ts
|
|
1184
|
-
const BASE_SEPOLIA_CHAIN_ID = 84532;
|
|
1185
|
-
const SAFE_PROXY_FACTORY = "0x4e1DCf7AD4e460CfD30791CCC4F9c8a4f820ec67";
|
|
1186
|
-
const SAFE_L2_SINGLETON = "0x29fcB43b46531BcA003ddC8FCB67FFE91900C762";
|
|
1187
|
-
const SAFE_MODULE_SETUP = "0x2dd68b007B46fBe91B9A7c3EDa5A7a1063cB5b47";
|
|
1188
|
-
const SAFE_4337_MODULE = "0x75cf11467937ce3F2f357CE24ffc3DBF8fD5c226";
|
|
1189
|
-
const MULTI_SEND = "0x38869bf66a61cF6bDB996A6aE40D5853Fd43B526";
|
|
1190
|
-
/**
|
|
1191
|
-
* The Capxul bootstrap owner — the sole `setup()` owner of every unclaimed
|
|
1192
|
-
* personal Safe under derivation v2. This address is part of the CREATE2
|
|
1193
|
-
* preimage of EVERY personal Safe address: treat it exactly like
|
|
1194
|
-
* `SAFE_PROXY_FACTORY` (changing it is a derivation-version event → v3).
|
|
1195
|
-
* The private key lives backend-only (`CAPXUL_BOOTSTRAP_OWNER_PRIVATE_KEY`)
|
|
1196
|
-
* and signs exactly one kind of operation: the claim userOp that deploys the
|
|
1197
|
-
* Safe and swaps this owner out for the user's signer (PRD #462). The earlier
|
|
1198
|
-
* ClaimGuard derivation-v3 placeholder (#461) is closed; future mainnet-scale
|
|
1199
|
-
* hardening should be filed against the current payments/claim architecture.
|
|
1200
|
-
*/
|
|
1201
|
-
const CAPXUL_BOOTSTRAP_OWNER = "0x425fca96f4a3e18fb80974e38096b5da460f2704";
|
|
1202
|
-
const SAFE_PROXY_CREATION_CODE = "0x608060405234801561001057600080fd5b506040516101e63803806101e68339818101604052602081101561003357600080fd5b8101908080519060200190929190505050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614156100ca576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260228152602001806101c46022913960400191505060405180910390fd5b806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505060ab806101196000396000f3fe608060405273ffffffffffffffffffffffffffffffffffffffff600054167fa619486e0000000000000000000000000000000000000000000000000000000060003514156050578060005260206000f35b3660008037600080366000845af43d6000803e60008114156070573d6000fd5b3d6000f3fea264697066735822122003d1488ee65e08fa41e58e888a9865554c535f2c77126a82cb4c0f917f31441364736f6c63430007060033496e76616c69642073696e676c65746f6e20616464726573732070726f7669646564";
|
|
1203
|
-
/**
|
|
1204
|
-
* Normalize an email for use as a Capxul Safe salt input.
|
|
1205
|
-
*
|
|
1206
|
-
* Trims surrounding whitespace and lowercases the address so that
|
|
1207
|
-
* `" User+Demo@Example.COM "` and `"user+demo@example.com"` derive
|
|
1208
|
-
* the same `safeAddress`. This is the canonical normalization rule —
|
|
1209
|
-
* any future change here would shift every existing user's Safe
|
|
1210
|
-
* address, so the rule is pinned by the fixture matrix in
|
|
1211
|
-
* `packages/config/tests/safe-address-conformance.test.ts`.
|
|
1212
|
-
*
|
|
1213
|
-
* @param email - The raw email address to normalize
|
|
1214
|
-
* @returns The trimmed, lowercased email
|
|
1215
|
-
*/
|
|
1216
|
-
function normalizeSafeSaltEmail(email) {
|
|
1217
|
-
return email.trim().toLowerCase();
|
|
1218
|
-
}
|
|
1219
|
-
/**
|
|
1220
|
-
* Compute the deterministic Safe salt nonce for a `(namespace, email)`
|
|
1221
|
-
* pair. The salt nonce is the `bigint` reading of
|
|
1222
|
-
* `keccak256(namespace + ":" + normalizeSafeSaltEmail(email))`.
|
|
1223
|
-
*
|
|
1224
|
-
* Namespace defaults to `CAPXUL_SAFE_SALT_NAMESPACE`
|
|
1225
|
-
* (`"capxul:staging:email:v1"`) — the current Capxul scheme. Pass a
|
|
1226
|
-
* different namespace only for pinned pre-v2 artifacts
|
|
1227
|
-
* (`CAPXUL_SAFE_SALT_NAMESPACE_V1`, faucet Safe) or when bumping the scheme.
|
|
1228
|
-
*
|
|
1229
|
-
* @param input.email - The email to derive the salt nonce from
|
|
1230
|
-
* @param input.namespace - Optional namespace override (defaults to `CAPXUL_SAFE_SALT_NAMESPACE`)
|
|
1231
|
-
* @returns The Capxul-namespaced salt nonce as a `bigint`
|
|
1232
|
-
*/
|
|
1233
|
-
function computeCapxulSafeSaltNonce(input) {
|
|
1234
|
-
const namespace = input.namespace ?? "capxul:staging:email:v1";
|
|
1235
|
-
const normalizedEmail = normalizeSafeSaltEmail(input.email);
|
|
1236
|
-
return BigInt(keccak256(toBytes(`${namespace}:${normalizedEmail}`)));
|
|
1237
|
-
}
|
|
1238
|
-
const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
1239
|
-
const safeSetupAbi = [{
|
|
1240
|
-
type: "function",
|
|
1241
|
-
name: "setup",
|
|
1242
|
-
stateMutability: "nonpayable",
|
|
1243
|
-
inputs: [
|
|
1244
|
-
{
|
|
1245
|
-
name: "_owners",
|
|
1246
|
-
type: "address[]"
|
|
1247
|
-
},
|
|
1248
|
-
{
|
|
1249
|
-
name: "_threshold",
|
|
1250
|
-
type: "uint256"
|
|
1251
|
-
},
|
|
1252
|
-
{
|
|
1253
|
-
name: "to",
|
|
1254
|
-
type: "address"
|
|
1255
|
-
},
|
|
1256
|
-
{
|
|
1257
|
-
name: "data",
|
|
1258
|
-
type: "bytes"
|
|
1259
|
-
},
|
|
1260
|
-
{
|
|
1261
|
-
name: "fallbackHandler",
|
|
1262
|
-
type: "address"
|
|
1263
|
-
},
|
|
1264
|
-
{
|
|
1265
|
-
name: "paymentToken",
|
|
1266
|
-
type: "address"
|
|
1267
|
-
},
|
|
1268
|
-
{
|
|
1269
|
-
name: "payment",
|
|
1270
|
-
type: "uint256"
|
|
1271
|
-
},
|
|
1272
|
-
{
|
|
1273
|
-
name: "paymentReceiver",
|
|
1274
|
-
type: "address"
|
|
1275
|
-
}
|
|
1276
|
-
],
|
|
1277
|
-
outputs: []
|
|
1278
|
-
}];
|
|
1279
|
-
const enableModulesAbi = [{
|
|
1280
|
-
type: "function",
|
|
1281
|
-
name: "enableModules",
|
|
1282
|
-
stateMutability: "nonpayable",
|
|
1283
|
-
inputs: [{
|
|
1284
|
-
name: "modules",
|
|
1285
|
-
type: "address[]"
|
|
1286
|
-
}],
|
|
1287
|
-
outputs: []
|
|
1288
|
-
}];
|
|
1289
|
-
const multiSendAbi = [{
|
|
1290
|
-
type: "function",
|
|
1291
|
-
name: "multiSend",
|
|
1292
|
-
stateMutability: "payable",
|
|
1293
|
-
inputs: [{
|
|
1294
|
-
name: "transactions",
|
|
1295
|
-
type: "bytes"
|
|
1296
|
-
}],
|
|
1297
|
-
outputs: []
|
|
1298
|
-
}];
|
|
1299
|
-
function buildCapxulSafeInitializer(owner) {
|
|
1300
|
-
const enableModulesCall = encodeFunctionData({
|
|
1301
|
-
abi: enableModulesAbi,
|
|
1302
|
-
functionName: "enableModules",
|
|
1303
|
-
args: [[SAFE_4337_MODULE]]
|
|
1304
|
-
});
|
|
1305
|
-
const enableModulesByteLen = BigInt((enableModulesCall.length - 2) / 2);
|
|
1306
|
-
const multiSendPayload = encodePacked([
|
|
1307
|
-
"uint8",
|
|
1308
|
-
"address",
|
|
1309
|
-
"uint256",
|
|
1310
|
-
"uint256",
|
|
1311
|
-
"bytes"
|
|
1312
|
-
], [
|
|
1313
|
-
1,
|
|
1314
|
-
SAFE_MODULE_SETUP,
|
|
1315
|
-
0n,
|
|
1316
|
-
enableModulesByteLen,
|
|
1317
|
-
enableModulesCall
|
|
1318
|
-
]);
|
|
1319
|
-
const setupData = encodeFunctionData({
|
|
1320
|
-
abi: multiSendAbi,
|
|
1321
|
-
functionName: "multiSend",
|
|
1322
|
-
args: [multiSendPayload]
|
|
1323
|
-
});
|
|
1324
|
-
return encodeFunctionData({
|
|
1325
|
-
abi: safeSetupAbi,
|
|
1326
|
-
functionName: "setup",
|
|
1327
|
-
args: [
|
|
1328
|
-
[owner],
|
|
1329
|
-
1n,
|
|
1330
|
-
MULTI_SEND,
|
|
1331
|
-
setupData,
|
|
1332
|
-
SAFE_4337_MODULE,
|
|
1333
|
-
ZERO_ADDRESS,
|
|
1334
|
-
0n,
|
|
1335
|
-
ZERO_ADDRESS
|
|
1336
|
-
]
|
|
1337
|
-
});
|
|
1338
|
-
}
|
|
1339
|
-
const ADDRESS_REGEX = /^0x[0-9a-fA-F]{40}$/;
|
|
1340
|
-
function validateAndNormalizeAddress(field, value) {
|
|
1341
|
-
const address = value.toLowerCase();
|
|
1342
|
-
if (!ADDRESS_REGEX.test(address)) throw Errors.invalidInput(field, `must be 0x + 40 hex chars, got ${value}`);
|
|
1343
|
-
return address;
|
|
1344
|
-
}
|
|
1345
|
-
/**
|
|
1346
|
-
* Pure derivation of the Capxul personal Safe address — **v2: email only**
|
|
1347
|
-
* (PRD #462). The Safe v1.4.1 (SafeL2) + 4337-module initializer's sole owner
|
|
1348
|
-
* is the fixed `CAPXUL_BOOTSTRAP_OWNER`, so the address is a pure function of
|
|
1349
|
-
* the (normalized) email and exists before any user signer does. The user's
|
|
1350
|
-
* signer is installed at claim time via `swapOwner` — signer rotation can
|
|
1351
|
-
* never move the address (org-domain rule D7, now system-wide).
|
|
1352
|
-
*
|
|
1353
|
-
* Returns the lowercased `0x` + 40 hex string (raw — no brand applied so this
|
|
1354
|
-
* function is callable from both `@capxul/config` consumers that don't have
|
|
1355
|
-
* `@capxul/types` available, and from packages that re-brand at their own
|
|
1356
|
-
* boundary).
|
|
1357
|
-
*
|
|
1358
|
-
* Algorithm: exactly mirrors `permissionless.toSafeSmartAccount({ version:
|
|
1359
|
-
* "1.4.1", safeSingletonAddress: SAFE_L2_SINGLETON, owners: [BOOTSTRAP] })`
|
|
1360
|
-
* + the Capxul-canonical module setup (delegatecall into
|
|
1361
|
-
* `SafeModuleSetup.enableModules([SAFE_4337_MODULE])` via the MultiSend lane
|
|
1362
|
-
* during proxy setup). The proxy creation code is pinned at
|
|
1363
|
-
* `SAFE_PROXY_CREATION_CODE`; the salt is Capxul-namespaced (v2) via
|
|
1364
|
-
* `computeCapxulSafeSaltNonce({ email })`.
|
|
1365
|
-
*
|
|
1366
|
-
* Canonical: SDK deployment adapters and
|
|
1367
|
-
* `packages/backend/convex/smartAccount/mutations.ts:provision` import this
|
|
1368
|
-
* function; the conformance test at
|
|
1369
|
-
* `packages/config/tests/safe-address-conformance.test.ts` pins the matrix.
|
|
1370
|
-
*/
|
|
1371
|
-
function deriveCapxulSafeAddress(input) {
|
|
1372
|
-
return deriveCapxulSafeAddressForOwner({
|
|
1373
|
-
ownerAddress: CAPXUL_BOOTSTRAP_OWNER,
|
|
1374
|
-
email: input.email
|
|
1375
|
-
});
|
|
1376
|
-
}
|
|
1377
|
-
/**
|
|
1378
|
-
* Owner-parametrized derivation core. Two callers only:
|
|
1379
|
-
* 1. `deriveCapxulSafeAddress` (canonical v2: owner = bootstrap, namespace v2);
|
|
1380
|
-
* 2. the backend faucet Safe (`testUsdcMint.ts`: owner = deployer EOA,
|
|
1381
|
-
* namespace pinned to `CAPXUL_SAFE_SALT_NAMESPACE_V1` so its deployed
|
|
1382
|
-
* address never moves).
|
|
1383
|
-
* Do NOT introduce new owner-parametrized lanes — per-user owners in the
|
|
1384
|
-
* derivation is exactly the v1 mistake v2 removes.
|
|
1385
|
-
*/
|
|
1386
|
-
function deriveCapxulSafeAddressForOwner(input) {
|
|
1387
|
-
const initializer = buildCapxulSafeInitializer(validateAndNormalizeAddress("ownerAddress", input.ownerAddress));
|
|
1388
|
-
const saltNonce = computeCapxulSafeSaltNonce(input.namespace === void 0 ? { email: input.email } : {
|
|
1389
|
-
email: input.email,
|
|
1390
|
-
namespace: input.namespace
|
|
1391
|
-
});
|
|
1392
|
-
const salt = keccak256(encodePacked(["bytes32", "uint256"], [keccak256(initializer), saltNonce]));
|
|
1393
|
-
const deploymentCode = concat([SAFE_PROXY_CREATION_CODE, encodeAbiParameters([{ type: "uint256" }], [BigInt(SAFE_L2_SINGLETON)])]);
|
|
1394
|
-
return getContractAddress({
|
|
1395
|
-
opcode: "CREATE2",
|
|
1396
|
-
from: SAFE_PROXY_FACTORY,
|
|
1397
|
-
salt,
|
|
1398
|
-
bytecodeHash: keccak256(deploymentCode)
|
|
1399
|
-
}).toLowerCase();
|
|
1400
|
-
}
|
|
1401
|
-
/**
|
|
1402
|
-
* Normalize a raw email for binding id + Safe salt derivation.
|
|
1403
|
-
* Delegates to {@link normalizeSafeSaltEmail} so binding resolution and
|
|
1404
|
-
* `deriveCapxulSafeAddress` never drift apart.
|
|
1405
|
-
*/
|
|
1406
|
-
function normalizeBindingEmail(email) {
|
|
1407
|
-
return normalizeSafeSaltEmail(email);
|
|
1408
|
-
}
|
|
1409
|
-
//#endregion
|
|
1410
|
-
//#region ../config/src/capxul-payments-v2.ts
|
|
1411
|
-
/** The exact compiled CapxulPaymentsV2 ABI. */
|
|
1412
|
-
const CAPXUL_PAYMENTS_V2_ABI = [
|
|
1413
|
-
{
|
|
1414
|
-
type: "function",
|
|
1415
|
-
name: "cancel",
|
|
1416
|
-
inputs: [{
|
|
1417
|
-
name: "commitmentId",
|
|
1418
|
-
type: "uint256",
|
|
1419
|
-
internalType: "uint256"
|
|
1420
|
-
}, {
|
|
1421
|
-
name: "documentHash",
|
|
1422
|
-
type: "bytes32",
|
|
1423
|
-
internalType: "bytes32"
|
|
1424
|
-
}],
|
|
1425
|
-
outputs: [],
|
|
1426
|
-
stateMutability: "nonpayable"
|
|
1427
|
-
},
|
|
1428
|
-
{
|
|
1429
|
-
type: "function",
|
|
1430
|
-
name: "claim",
|
|
1431
|
-
inputs: [{
|
|
1432
|
-
name: "commitmentId",
|
|
1433
|
-
type: "uint256",
|
|
1434
|
-
internalType: "uint256"
|
|
1435
|
-
}],
|
|
1436
|
-
outputs: [],
|
|
1437
|
-
stateMutability: "nonpayable"
|
|
1438
|
-
},
|
|
1439
|
-
{
|
|
1440
|
-
type: "function",
|
|
1441
|
-
name: "claimableAmount",
|
|
1442
|
-
inputs: [{
|
|
1443
|
-
name: "commitmentId",
|
|
1444
|
-
type: "uint256",
|
|
1445
|
-
internalType: "uint256"
|
|
1446
|
-
}],
|
|
1447
|
-
outputs: [{
|
|
1448
|
-
name: "",
|
|
1449
|
-
type: "uint256",
|
|
1450
|
-
internalType: "uint256"
|
|
1451
|
-
}],
|
|
1452
|
-
stateMutability: "view"
|
|
1453
|
-
},
|
|
1454
|
-
{
|
|
1455
|
-
type: "function",
|
|
1456
|
-
name: "commitments",
|
|
1457
|
-
inputs: [{
|
|
1458
|
-
name: "",
|
|
1459
|
-
type: "uint256",
|
|
1460
|
-
internalType: "uint256"
|
|
1461
|
-
}],
|
|
1462
|
-
outputs: [
|
|
1463
|
-
{
|
|
1464
|
-
name: "settlementId",
|
|
1465
|
-
type: "bytes32",
|
|
1466
|
-
internalType: "bytes32"
|
|
1467
|
-
},
|
|
1468
|
-
{
|
|
1469
|
-
name: "creator",
|
|
1470
|
-
type: "address",
|
|
1471
|
-
internalType: "address"
|
|
1472
|
-
},
|
|
1473
|
-
{
|
|
1474
|
-
name: "recipient",
|
|
1475
|
-
type: "address",
|
|
1476
|
-
internalType: "address"
|
|
1477
|
-
},
|
|
1478
|
-
{
|
|
1479
|
-
name: "token",
|
|
1480
|
-
type: "address",
|
|
1481
|
-
internalType: "address"
|
|
1482
|
-
},
|
|
1483
|
-
{
|
|
1484
|
-
name: "totalAmount",
|
|
1485
|
-
type: "uint256",
|
|
1486
|
-
internalType: "uint256"
|
|
1487
|
-
},
|
|
1488
|
-
{
|
|
1489
|
-
name: "claimed",
|
|
1490
|
-
type: "uint256",
|
|
1491
|
-
internalType: "uint256"
|
|
1492
|
-
},
|
|
1493
|
-
{
|
|
1494
|
-
name: "startTime",
|
|
1495
|
-
type: "uint48",
|
|
1496
|
-
internalType: "uint48"
|
|
1497
|
-
},
|
|
1498
|
-
{
|
|
1499
|
-
name: "cliffTime",
|
|
1500
|
-
type: "uint48",
|
|
1501
|
-
internalType: "uint48"
|
|
1502
|
-
},
|
|
1503
|
-
{
|
|
1504
|
-
name: "endTime",
|
|
1505
|
-
type: "uint48",
|
|
1506
|
-
internalType: "uint48"
|
|
1507
|
-
},
|
|
1508
|
-
{
|
|
1509
|
-
name: "kind",
|
|
1510
|
-
type: "uint16",
|
|
1511
|
-
internalType: "uint16"
|
|
1512
|
-
},
|
|
1513
|
-
{
|
|
1514
|
-
name: "documentHash",
|
|
1515
|
-
type: "bytes32",
|
|
1516
|
-
internalType: "bytes32"
|
|
1517
|
-
},
|
|
1518
|
-
{
|
|
1519
|
-
name: "cancelled",
|
|
1520
|
-
type: "bool",
|
|
1521
|
-
internalType: "bool"
|
|
1522
|
-
}
|
|
1523
|
-
],
|
|
1524
|
-
stateMutability: "view"
|
|
1525
|
-
},
|
|
1526
|
-
{
|
|
1527
|
-
type: "function",
|
|
1528
|
-
name: "createCommitment",
|
|
1529
|
-
inputs: [{
|
|
1530
|
-
name: "input",
|
|
1531
|
-
type: "tuple",
|
|
1532
|
-
internalType: "struct CapxulPaymentsV2.CommitmentInput",
|
|
1533
|
-
components: [
|
|
1534
|
-
{
|
|
1535
|
-
name: "settlementId",
|
|
1536
|
-
type: "bytes32",
|
|
1537
|
-
internalType: "bytes32"
|
|
1538
|
-
},
|
|
1539
|
-
{
|
|
1540
|
-
name: "recipient",
|
|
1541
|
-
type: "address",
|
|
1542
|
-
internalType: "address"
|
|
1543
|
-
},
|
|
1544
|
-
{
|
|
1545
|
-
name: "token",
|
|
1546
|
-
type: "address",
|
|
1547
|
-
internalType: "address"
|
|
1548
|
-
},
|
|
1549
|
-
{
|
|
1550
|
-
name: "totalAmount",
|
|
1551
|
-
type: "uint256",
|
|
1552
|
-
internalType: "uint256"
|
|
1553
|
-
},
|
|
1554
|
-
{
|
|
1555
|
-
name: "startTime",
|
|
1556
|
-
type: "uint48",
|
|
1557
|
-
internalType: "uint48"
|
|
1558
|
-
},
|
|
1559
|
-
{
|
|
1560
|
-
name: "cliffTime",
|
|
1561
|
-
type: "uint48",
|
|
1562
|
-
internalType: "uint48"
|
|
1563
|
-
},
|
|
1564
|
-
{
|
|
1565
|
-
name: "endTime",
|
|
1566
|
-
type: "uint48",
|
|
1567
|
-
internalType: "uint48"
|
|
1568
|
-
},
|
|
1569
|
-
{
|
|
1570
|
-
name: "kind",
|
|
1571
|
-
type: "uint16",
|
|
1572
|
-
internalType: "uint16"
|
|
1573
|
-
},
|
|
1574
|
-
{
|
|
1575
|
-
name: "documentHash",
|
|
1576
|
-
type: "bytes32",
|
|
1577
|
-
internalType: "bytes32"
|
|
1578
|
-
}
|
|
1579
|
-
]
|
|
1580
|
-
}],
|
|
1581
|
-
outputs: [{
|
|
1582
|
-
name: "commitmentId",
|
|
1583
|
-
type: "uint256",
|
|
1584
|
-
internalType: "uint256"
|
|
1585
|
-
}],
|
|
1586
|
-
stateMutability: "nonpayable"
|
|
1587
|
-
},
|
|
1588
|
-
{
|
|
1589
|
-
type: "function",
|
|
1590
|
-
name: "nextCommitmentId",
|
|
1591
|
-
inputs: [],
|
|
1592
|
-
outputs: [{
|
|
1593
|
-
name: "",
|
|
1594
|
-
type: "uint256",
|
|
1595
|
-
internalType: "uint256"
|
|
1596
|
-
}],
|
|
1597
|
-
stateMutability: "view"
|
|
1598
|
-
},
|
|
1599
|
-
{
|
|
1600
|
-
type: "function",
|
|
1601
|
-
name: "redirect",
|
|
1602
|
-
inputs: [
|
|
1603
|
-
{
|
|
1604
|
-
name: "commitmentId",
|
|
1605
|
-
type: "uint256",
|
|
1606
|
-
internalType: "uint256"
|
|
1607
|
-
},
|
|
1608
|
-
{
|
|
1609
|
-
name: "recipient",
|
|
1610
|
-
type: "address",
|
|
1611
|
-
internalType: "address"
|
|
1612
|
-
},
|
|
1613
|
-
{
|
|
1614
|
-
name: "documentHash",
|
|
1615
|
-
type: "bytes32",
|
|
1616
|
-
internalType: "bytes32"
|
|
1617
|
-
}
|
|
1618
|
-
],
|
|
1619
|
-
outputs: [],
|
|
1620
|
-
stateMutability: "nonpayable"
|
|
1621
|
-
},
|
|
1622
|
-
{
|
|
1623
|
-
type: "function",
|
|
1624
|
-
name: "send",
|
|
1625
|
-
inputs: [{
|
|
1626
|
-
name: "item",
|
|
1627
|
-
type: "tuple",
|
|
1628
|
-
internalType: "struct CapxulPaymentsV2.PaymentItem",
|
|
1629
|
-
components: [
|
|
1630
|
-
{
|
|
1631
|
-
name: "settlementId",
|
|
1632
|
-
type: "bytes32",
|
|
1633
|
-
internalType: "bytes32"
|
|
1634
|
-
},
|
|
1635
|
-
{
|
|
1636
|
-
name: "recipient",
|
|
1637
|
-
type: "address",
|
|
1638
|
-
internalType: "address"
|
|
1639
|
-
},
|
|
1640
|
-
{
|
|
1641
|
-
name: "token",
|
|
1642
|
-
type: "address",
|
|
1643
|
-
internalType: "address"
|
|
1644
|
-
},
|
|
1645
|
-
{
|
|
1646
|
-
name: "amount",
|
|
1647
|
-
type: "uint256",
|
|
1648
|
-
internalType: "uint256"
|
|
1649
|
-
},
|
|
1650
|
-
{
|
|
1651
|
-
name: "kind",
|
|
1652
|
-
type: "uint16",
|
|
1653
|
-
internalType: "uint16"
|
|
1654
|
-
},
|
|
1655
|
-
{
|
|
1656
|
-
name: "documentHash",
|
|
1657
|
-
type: "bytes32",
|
|
1658
|
-
internalType: "bytes32"
|
|
1659
|
-
}
|
|
1660
|
-
]
|
|
1661
|
-
}],
|
|
1662
|
-
outputs: [],
|
|
1663
|
-
stateMutability: "nonpayable"
|
|
1664
|
-
},
|
|
1665
|
-
{
|
|
1666
|
-
type: "function",
|
|
1667
|
-
name: "sendBatch",
|
|
1668
|
-
inputs: [{
|
|
1669
|
-
name: "items",
|
|
1670
|
-
type: "tuple[]",
|
|
1671
|
-
internalType: "struct CapxulPaymentsV2.PaymentItem[]",
|
|
1672
|
-
components: [
|
|
1673
|
-
{
|
|
1674
|
-
name: "settlementId",
|
|
1675
|
-
type: "bytes32",
|
|
1676
|
-
internalType: "bytes32"
|
|
1677
|
-
},
|
|
1678
|
-
{
|
|
1679
|
-
name: "recipient",
|
|
1680
|
-
type: "address",
|
|
1681
|
-
internalType: "address"
|
|
1682
|
-
},
|
|
1683
|
-
{
|
|
1684
|
-
name: "token",
|
|
1685
|
-
type: "address",
|
|
1686
|
-
internalType: "address"
|
|
1687
|
-
},
|
|
1688
|
-
{
|
|
1689
|
-
name: "amount",
|
|
1690
|
-
type: "uint256",
|
|
1691
|
-
internalType: "uint256"
|
|
1692
|
-
},
|
|
1693
|
-
{
|
|
1694
|
-
name: "kind",
|
|
1695
|
-
type: "uint16",
|
|
1696
|
-
internalType: "uint16"
|
|
1697
|
-
},
|
|
1698
|
-
{
|
|
1699
|
-
name: "documentHash",
|
|
1700
|
-
type: "bytes32",
|
|
1701
|
-
internalType: "bytes32"
|
|
1702
|
-
}
|
|
1703
|
-
]
|
|
1704
|
-
}],
|
|
1705
|
-
outputs: [],
|
|
1706
|
-
stateMutability: "nonpayable"
|
|
1707
|
-
},
|
|
1708
|
-
{
|
|
1709
|
-
type: "function",
|
|
1710
|
-
name: "vestedAmount",
|
|
1711
|
-
inputs: [{
|
|
1712
|
-
name: "commitmentId",
|
|
1713
|
-
type: "uint256",
|
|
1714
|
-
internalType: "uint256"
|
|
1715
|
-
}],
|
|
1716
|
-
outputs: [{
|
|
1717
|
-
name: "",
|
|
1718
|
-
type: "uint256",
|
|
1719
|
-
internalType: "uint256"
|
|
1720
|
-
}],
|
|
1721
|
-
stateMutability: "view"
|
|
1722
|
-
},
|
|
1723
|
-
{
|
|
1724
|
-
type: "event",
|
|
1725
|
-
name: "CommitmentCancelled",
|
|
1726
|
-
inputs: [
|
|
1727
|
-
{
|
|
1728
|
-
name: "commitmentId",
|
|
1729
|
-
type: "uint256",
|
|
1730
|
-
indexed: true,
|
|
1731
|
-
internalType: "uint256"
|
|
1732
|
-
},
|
|
1733
|
-
{
|
|
1734
|
-
name: "settlementId",
|
|
1735
|
-
type: "bytes32",
|
|
1736
|
-
indexed: true,
|
|
1737
|
-
internalType: "bytes32"
|
|
1738
|
-
},
|
|
1739
|
-
{
|
|
1740
|
-
name: "creator",
|
|
1741
|
-
type: "address",
|
|
1742
|
-
indexed: true,
|
|
1743
|
-
internalType: "address"
|
|
1744
|
-
},
|
|
1745
|
-
{
|
|
1746
|
-
name: "vestedUnclaimed",
|
|
1747
|
-
type: "uint256",
|
|
1748
|
-
indexed: false,
|
|
1749
|
-
internalType: "uint256"
|
|
1750
|
-
},
|
|
1751
|
-
{
|
|
1752
|
-
name: "unvestedReturned",
|
|
1753
|
-
type: "uint256",
|
|
1754
|
-
indexed: false,
|
|
1755
|
-
internalType: "uint256"
|
|
1756
|
-
},
|
|
1757
|
-
{
|
|
1758
|
-
name: "documentHash",
|
|
1759
|
-
type: "bytes32",
|
|
1760
|
-
indexed: false,
|
|
1761
|
-
internalType: "bytes32"
|
|
1762
|
-
}
|
|
1763
|
-
],
|
|
1764
|
-
anonymous: false
|
|
1765
|
-
},
|
|
1766
|
-
{
|
|
1767
|
-
type: "event",
|
|
1768
|
-
name: "CommitmentCreated",
|
|
1769
|
-
inputs: [
|
|
1770
|
-
{
|
|
1771
|
-
name: "commitmentId",
|
|
1772
|
-
type: "uint256",
|
|
1773
|
-
indexed: true,
|
|
1774
|
-
internalType: "uint256"
|
|
1775
|
-
},
|
|
1776
|
-
{
|
|
1777
|
-
name: "settlementId",
|
|
1778
|
-
type: "bytes32",
|
|
1779
|
-
indexed: true,
|
|
1780
|
-
internalType: "bytes32"
|
|
1781
|
-
},
|
|
1782
|
-
{
|
|
1783
|
-
name: "creator",
|
|
1784
|
-
type: "address",
|
|
1785
|
-
indexed: true,
|
|
1786
|
-
internalType: "address"
|
|
1787
|
-
},
|
|
1788
|
-
{
|
|
1789
|
-
name: "recipient",
|
|
1790
|
-
type: "address",
|
|
1791
|
-
indexed: false,
|
|
1792
|
-
internalType: "address"
|
|
1793
|
-
},
|
|
1794
|
-
{
|
|
1795
|
-
name: "token",
|
|
1796
|
-
type: "address",
|
|
1797
|
-
indexed: false,
|
|
1798
|
-
internalType: "address"
|
|
1799
|
-
},
|
|
1800
|
-
{
|
|
1801
|
-
name: "totalAmount",
|
|
1802
|
-
type: "uint256",
|
|
1803
|
-
indexed: false,
|
|
1804
|
-
internalType: "uint256"
|
|
1805
|
-
},
|
|
1806
|
-
{
|
|
1807
|
-
name: "startTime",
|
|
1808
|
-
type: "uint48",
|
|
1809
|
-
indexed: false,
|
|
1810
|
-
internalType: "uint48"
|
|
1811
|
-
},
|
|
1812
|
-
{
|
|
1813
|
-
name: "cliffTime",
|
|
1814
|
-
type: "uint48",
|
|
1815
|
-
indexed: false,
|
|
1816
|
-
internalType: "uint48"
|
|
1817
|
-
},
|
|
1818
|
-
{
|
|
1819
|
-
name: "endTime",
|
|
1820
|
-
type: "uint48",
|
|
1821
|
-
indexed: false,
|
|
1822
|
-
internalType: "uint48"
|
|
1823
|
-
},
|
|
1824
|
-
{
|
|
1825
|
-
name: "kind",
|
|
1826
|
-
type: "uint16",
|
|
1827
|
-
indexed: false,
|
|
1828
|
-
internalType: "uint16"
|
|
1829
|
-
},
|
|
1830
|
-
{
|
|
1831
|
-
name: "documentHash",
|
|
1832
|
-
type: "bytes32",
|
|
1833
|
-
indexed: false,
|
|
1834
|
-
internalType: "bytes32"
|
|
1835
|
-
}
|
|
1836
|
-
],
|
|
1837
|
-
anonymous: false
|
|
1838
|
-
},
|
|
1839
|
-
{
|
|
1840
|
-
type: "event",
|
|
1841
|
-
name: "CommitmentRedirected",
|
|
1842
|
-
inputs: [
|
|
1843
|
-
{
|
|
1844
|
-
name: "commitmentId",
|
|
1845
|
-
type: "uint256",
|
|
1846
|
-
indexed: true,
|
|
1847
|
-
internalType: "uint256"
|
|
1848
|
-
},
|
|
1849
|
-
{
|
|
1850
|
-
name: "settlementId",
|
|
1851
|
-
type: "bytes32",
|
|
1852
|
-
indexed: true,
|
|
1853
|
-
internalType: "bytes32"
|
|
1854
|
-
},
|
|
1855
|
-
{
|
|
1856
|
-
name: "oldRecipient",
|
|
1857
|
-
type: "address",
|
|
1858
|
-
indexed: true,
|
|
1859
|
-
internalType: "address"
|
|
1860
|
-
},
|
|
1861
|
-
{
|
|
1862
|
-
name: "newRecipient",
|
|
1863
|
-
type: "address",
|
|
1864
|
-
indexed: false,
|
|
1865
|
-
internalType: "address"
|
|
1866
|
-
},
|
|
1867
|
-
{
|
|
1868
|
-
name: "documentHash",
|
|
1869
|
-
type: "bytes32",
|
|
1870
|
-
indexed: false,
|
|
1871
|
-
internalType: "bytes32"
|
|
1872
|
-
}
|
|
1873
|
-
],
|
|
1874
|
-
anonymous: false
|
|
1875
|
-
},
|
|
1876
|
-
{
|
|
1877
|
-
type: "event",
|
|
1878
|
-
name: "FundsClaimed",
|
|
1879
|
-
inputs: [
|
|
1880
|
-
{
|
|
1881
|
-
name: "commitmentId",
|
|
1882
|
-
type: "uint256",
|
|
1883
|
-
indexed: true,
|
|
1884
|
-
internalType: "uint256"
|
|
1885
|
-
},
|
|
1886
|
-
{
|
|
1887
|
-
name: "settlementId",
|
|
1888
|
-
type: "bytes32",
|
|
1889
|
-
indexed: true,
|
|
1890
|
-
internalType: "bytes32"
|
|
1891
|
-
},
|
|
1892
|
-
{
|
|
1893
|
-
name: "recipient",
|
|
1894
|
-
type: "address",
|
|
1895
|
-
indexed: true,
|
|
1896
|
-
internalType: "address"
|
|
1897
|
-
},
|
|
1898
|
-
{
|
|
1899
|
-
name: "amount",
|
|
1900
|
-
type: "uint256",
|
|
1901
|
-
indexed: false,
|
|
1902
|
-
internalType: "uint256"
|
|
1903
|
-
},
|
|
1904
|
-
{
|
|
1905
|
-
name: "documentHash",
|
|
1906
|
-
type: "bytes32",
|
|
1907
|
-
indexed: false,
|
|
1908
|
-
internalType: "bytes32"
|
|
1909
|
-
}
|
|
1910
|
-
],
|
|
1911
|
-
anonymous: false
|
|
1912
|
-
},
|
|
1913
|
-
{
|
|
1914
|
-
type: "event",
|
|
1915
|
-
name: "PaymentSent",
|
|
1916
|
-
inputs: [
|
|
1917
|
-
{
|
|
1918
|
-
name: "settlementId",
|
|
1919
|
-
type: "bytes32",
|
|
1920
|
-
indexed: true,
|
|
1921
|
-
internalType: "bytes32"
|
|
1922
|
-
},
|
|
1923
|
-
{
|
|
1924
|
-
name: "sender",
|
|
1925
|
-
type: "address",
|
|
1926
|
-
indexed: true,
|
|
1927
|
-
internalType: "address"
|
|
1928
|
-
},
|
|
1929
|
-
{
|
|
1930
|
-
name: "recipient",
|
|
1931
|
-
type: "address",
|
|
1932
|
-
indexed: true,
|
|
1933
|
-
internalType: "address"
|
|
1934
|
-
},
|
|
1935
|
-
{
|
|
1936
|
-
name: "token",
|
|
1937
|
-
type: "address",
|
|
1938
|
-
indexed: false,
|
|
1939
|
-
internalType: "address"
|
|
1940
|
-
},
|
|
1941
|
-
{
|
|
1942
|
-
name: "amount",
|
|
1943
|
-
type: "uint256",
|
|
1944
|
-
indexed: false,
|
|
1945
|
-
internalType: "uint256"
|
|
1946
|
-
},
|
|
1947
|
-
{
|
|
1948
|
-
name: "kind",
|
|
1949
|
-
type: "uint16",
|
|
1950
|
-
indexed: false,
|
|
1951
|
-
internalType: "uint16"
|
|
1952
|
-
},
|
|
1953
|
-
{
|
|
1954
|
-
name: "documentHash",
|
|
1955
|
-
type: "bytes32",
|
|
1956
|
-
indexed: false,
|
|
1957
|
-
internalType: "bytes32"
|
|
1958
|
-
}
|
|
1959
|
-
],
|
|
1960
|
-
anonymous: false
|
|
1961
|
-
},
|
|
1962
|
-
{
|
|
1963
|
-
type: "error",
|
|
1964
|
-
name: "AlreadyCancelled",
|
|
1965
|
-
inputs: []
|
|
1966
|
-
},
|
|
1967
|
-
{
|
|
1968
|
-
type: "error",
|
|
1969
|
-
name: "AlreadyClaimed",
|
|
1970
|
-
inputs: []
|
|
1971
|
-
},
|
|
1972
|
-
{
|
|
1973
|
-
type: "error",
|
|
1974
|
-
name: "DocumentHashMismatch",
|
|
1975
|
-
inputs: [{
|
|
1976
|
-
name: "expected",
|
|
1977
|
-
type: "bytes32",
|
|
1978
|
-
internalType: "bytes32"
|
|
1979
|
-
}, {
|
|
1980
|
-
name: "supplied",
|
|
1981
|
-
type: "bytes32",
|
|
1982
|
-
internalType: "bytes32"
|
|
1983
|
-
}]
|
|
1984
|
-
},
|
|
1985
|
-
{
|
|
1986
|
-
type: "error",
|
|
1987
|
-
name: "InvalidTimeParams",
|
|
1988
|
-
inputs: []
|
|
1989
|
-
},
|
|
1990
|
-
{
|
|
1991
|
-
type: "error",
|
|
1992
|
-
name: "NotCreator",
|
|
1993
|
-
inputs: []
|
|
1994
|
-
},
|
|
1995
|
-
{
|
|
1996
|
-
type: "error",
|
|
1997
|
-
name: "NotRecipient",
|
|
1998
|
-
inputs: []
|
|
1999
|
-
},
|
|
2000
|
-
{
|
|
2001
|
-
type: "error",
|
|
2002
|
-
name: "NothingToClaim",
|
|
2003
|
-
inputs: []
|
|
2004
|
-
},
|
|
2005
|
-
{
|
|
2006
|
-
type: "error",
|
|
2007
|
-
name: "SettlementIdConsumed",
|
|
2008
|
-
inputs: [{
|
|
2009
|
-
name: "settlementId",
|
|
2010
|
-
type: "bytes32",
|
|
2011
|
-
internalType: "bytes32"
|
|
2012
|
-
}]
|
|
2013
|
-
},
|
|
2014
|
-
{
|
|
2015
|
-
type: "error",
|
|
2016
|
-
name: "TokenCallReturnedFalse",
|
|
2017
|
-
inputs: [{
|
|
2018
|
-
name: "token",
|
|
2019
|
-
type: "address",
|
|
2020
|
-
internalType: "address"
|
|
2021
|
-
}]
|
|
2022
|
-
},
|
|
2023
|
-
{
|
|
2024
|
-
type: "error",
|
|
2025
|
-
name: "TokenHasNoCode",
|
|
2026
|
-
inputs: [{
|
|
2027
|
-
name: "token",
|
|
2028
|
-
type: "address",
|
|
2029
|
-
internalType: "address"
|
|
2030
|
-
}]
|
|
2031
|
-
},
|
|
2032
|
-
{
|
|
2033
|
-
type: "error",
|
|
2034
|
-
name: "ZeroAddress",
|
|
2035
|
-
inputs: []
|
|
2036
|
-
},
|
|
2037
|
-
{
|
|
2038
|
-
type: "error",
|
|
2039
|
-
name: "ZeroAmount",
|
|
2040
|
-
inputs: []
|
|
2041
|
-
},
|
|
2042
|
-
{
|
|
2043
|
-
type: "error",
|
|
2044
|
-
name: "ZeroDocumentHash",
|
|
2045
|
-
inputs: []
|
|
2046
|
-
},
|
|
2047
|
-
{
|
|
2048
|
-
type: "error",
|
|
2049
|
-
name: "ZeroSettlementId",
|
|
2050
|
-
inputs: []
|
|
2051
|
-
}
|
|
2052
|
-
];
|
|
2053
|
-
/** Immutable CapxulPaymentsV2 deployment on Base Sepolia. */
|
|
2054
|
-
const CAPXUL_PAYMENTS_V2_ADDRESS = "0x7a5c25aB7ce91d7a475B71cd1ec33E230291f8a4";
|
|
2055
|
-
//#endregion
|
|
2056
|
-
//#region ../config/src/route.ts
|
|
2057
|
-
/** USDX on Base Sepolia — the only settlement token in v1. */
|
|
2058
|
-
const USDX_BASE_SEPOLIA_TOKEN = {
|
|
2059
|
-
chainId: BASE_SEPOLIA_CHAIN_ID,
|
|
2060
|
-
address: USDX_ADDRESS_BASE_SEPOLIA,
|
|
2061
|
-
decimals: 6
|
|
2062
|
-
};
|
|
2063
|
-
USDX_BASE_SEPOLIA_TOKEN.chainId;
|
|
2064
|
-
USDX_BASE_SEPOLIA_TOKEN.chainId;
|
|
2065
|
-
10n ** BigInt(6);
|
|
2066
|
-
/**
|
|
2067
|
-
* The bootstrap manage role. It administers people and roles, cannot spend,
|
|
2068
|
-
* and is not a signer of the Organization Safe; "Owner" implied the latter,
|
|
2069
|
-
* so the label is Admin (ruled 2026-09-06).
|
|
2070
|
-
*/
|
|
2071
|
-
const ADMIN_ROLE_LABEL = "Admin";
|
|
2072
|
-
function normalizeOrgRoleLabel(label) {
|
|
2073
|
-
const normalized = label.trim().replace(/\s+/g, " ");
|
|
2074
|
-
if (normalized.length === 0) throw Errors.invalidInput("role.label", "must be a non-empty role label");
|
|
2075
|
-
return normalized;
|
|
2076
|
-
}
|
|
2077
|
-
function orgRoleKeyForLabel(label) {
|
|
2078
|
-
return keccak256(toBytes(normalizeOrgRoleLabel(label).toLowerCase()));
|
|
2079
|
-
}
|
|
2080
|
-
padHex(stringToHex("FM_DAILY"), {
|
|
2081
|
-
size: 32,
|
|
2082
|
-
dir: "right"
|
|
2083
|
-
}), padHex(stringToHex("TL_DAILY"), {
|
|
2084
|
-
size: 32,
|
|
2085
|
-
dir: "right"
|
|
2086
|
-
});
|
|
2087
|
-
padHex(concat([MULTI_SEND_CALL_ONLY, "0x8d80ff0a"]), {
|
|
2088
|
-
dir: "right",
|
|
2089
|
-
size: 32
|
|
2090
|
-
});
|
|
2091
|
-
function paymentsV2Signature(name) {
|
|
2092
|
-
const item = CAPXUL_PAYMENTS_V2_ABI.find((candidate) => candidate.type === "function" && candidate.name === name);
|
|
2093
|
-
if (item === void 0) throw new Error(`CapxulPaymentsV2 ABI has no ${name} function`);
|
|
2094
|
-
return toFunctionSignature(item);
|
|
2095
|
-
}
|
|
2096
|
-
paymentsV2Signature("send");
|
|
2097
|
-
paymentsV2Signature("sendBatch");
|
|
2098
|
-
paymentsV2Signature("createCommitment");
|
|
2099
|
-
paymentsV2Signature("cancel");
|
|
2100
|
-
paymentsV2Signature("redirect");
|
|
2101
|
-
new Map([
|
|
2102
|
-
["assignRoles(address,bytes32[],bool[])", "AssignRoles(address,bytes32[],bool[])"],
|
|
2103
|
-
["allowTarget(bytes32,address,uint8)", "AllowTarget(bytes32,address,uint8)"],
|
|
2104
|
-
["scopeTarget(bytes32,address)", "ScopeTarget(bytes32,address)"],
|
|
2105
|
-
["revokeTarget(bytes32,address)", "RevokeTarget(bytes32,address)"],
|
|
2106
|
-
["allowFunction(bytes32,address,bytes4,uint8)", "AllowFunction(bytes32,address,bytes4,uint8)"],
|
|
2107
|
-
["scopeFunction(bytes32,address,bytes4,(uint8,uint8,uint8,bytes)[],uint8)", "ScopeFunction(bytes32,address,bytes4,(uint8,uint8,uint8,bytes)[],uint8)"],
|
|
2108
|
-
["revokeFunction(bytes32,address,bytes4)", "RevokeFunction(bytes32,address,bytes4)"],
|
|
2109
|
-
["setAllowance(bytes32,uint128,uint128,uint128,uint64,uint64)", "SetAllowance(bytes32,uint128,uint128,uint128,uint64,uint64)"]
|
|
2110
|
-
].map(([functionSignature, eventSignature]) => [toFunctionSelector(functionSignature), toEventSelector(eventSignature)]));
|
|
2111
|
-
//#endregion
|
|
2112
|
-
//#region src/ports/auth-client.ts
|
|
2113
|
-
var AuthClientError = class extends Data.TaggedError("AuthClientError") {};
|
|
2114
|
-
var AuthClientPortTag = class extends Context.Service()("@capxul/sdk/ports/AuthClientPort") {};
|
|
2115
|
-
//#endregion
|
|
2116
|
-
//#region src/adapters/auth-client/effect-port.ts
|
|
2117
|
-
function authClientPortFromPromiseAdapter(adapter) {
|
|
2118
|
-
return {
|
|
2119
|
-
canSendOtp: (input, options) => fromResult("canSendOtp", () => adapter.canSendOtp(input, options)),
|
|
2120
|
-
sendOtp: (input, options) => fromResult("sendOtp", () => adapter.sendOtp(input, options)),
|
|
2121
|
-
verifyOtp: (input, options) => fromResult("verifyOtp", () => adapter.verifyOtp(input, options)),
|
|
2122
|
-
getSession: (options) => fromResult("getSession", () => adapter.getSession(options)),
|
|
2123
|
-
signOut: (options) => fromResult("signOut", () => adapter.signOut(options)),
|
|
2124
|
-
getConvexJwt: (options) => fromResult("getConvexJwt", () => adapter.getConvexJwt(options))
|
|
2125
|
-
};
|
|
2126
|
-
}
|
|
2127
|
-
function fromResult(operation, run) {
|
|
2128
|
-
return Effect.tryPromise({
|
|
2129
|
-
try: run,
|
|
2130
|
-
catch: (cause) => new AuthClientError({
|
|
2131
|
-
operation,
|
|
2132
|
-
kind: "thrown",
|
|
2133
|
-
cause
|
|
2134
|
-
})
|
|
2135
|
-
}).pipe(Effect.flatMap((result) => result.ok ? Effect.succeed(result.value) : Effect.fail(new AuthClientError({
|
|
2136
|
-
operation,
|
|
2137
|
-
kind: errorKind(result.error),
|
|
2138
|
-
cause: result.error
|
|
2139
|
-
}))));
|
|
2140
|
-
}
|
|
2141
|
-
function errorKind(error) {
|
|
2142
|
-
if (error.code === "CANCELLED") return "cancelled";
|
|
2143
|
-
if (error.code === "NETWORK_ERROR") return "network";
|
|
2144
|
-
if (error.code === "INVALID_INPUT" || error.code === "OTP_EXPIRED") return "validation";
|
|
2145
|
-
return "provider";
|
|
2146
|
-
}
|
|
2147
|
-
//#endregion
|
|
2148
|
-
//#region src/ports/clock.ts
|
|
2149
|
-
var ClockError = class extends Data.TaggedError("ClockError") {};
|
|
2150
|
-
var ClockPortTag = class extends Context.Service()("@capxul/sdk/ports/ClockPort") {};
|
|
2151
|
-
//#endregion
|
|
2152
|
-
//#region src/adapters/clock/SystemClockAdapter.ts
|
|
2153
|
-
var SystemClockAdapter = class {
|
|
2154
|
-
now = Effect.try({
|
|
2155
|
-
try: () => toEpochMs(Date.now()),
|
|
2156
|
-
catch: (cause) => new ClockError({
|
|
2157
|
-
operation: "now",
|
|
2158
|
-
cause
|
|
2159
|
-
})
|
|
2160
|
-
});
|
|
2161
|
-
sleep(duration) {
|
|
2162
|
-
return Effect.callback((resume) => {
|
|
2163
|
-
const timeout = setTimeout(() => resume(Effect.void), duration);
|
|
2164
|
-
return Effect.sync(() => clearTimeout(timeout));
|
|
2165
|
-
});
|
|
2166
|
-
}
|
|
2167
|
-
};
|
|
2168
|
-
function SystemClockLayer() {
|
|
2169
|
-
return Layer.succeed(ClockPortTag, new SystemClockAdapter());
|
|
2170
|
-
}
|
|
2171
|
-
//#endregion
|
|
2172
|
-
//#region src/ports/auth-cache.ts
|
|
2173
|
-
var AuthCacheError = class extends Data.TaggedError("AuthCacheError") {};
|
|
2174
|
-
var AuthCachePortTag = class extends Context.Service()("@capxul/sdk/ports/AuthCachePort") {};
|
|
2175
|
-
//#endregion
|
|
2176
|
-
//#region src/adapters/auth-cache/serialization.ts
|
|
2177
|
-
function parseAuthSession(raw) {
|
|
2178
|
-
if (typeof raw !== "object" || raw === null) return null;
|
|
2179
|
-
const candidate = raw;
|
|
2180
|
-
try {
|
|
2181
|
-
return {
|
|
2182
|
-
authUserId: toAuthUserId(candidate.authUserId),
|
|
2183
|
-
email: toEmail(candidate.email),
|
|
2184
|
-
token: toSessionToken(candidate.token),
|
|
2185
|
-
expiresAt: toEpochMs(candidate.expiresAt)
|
|
2186
|
-
};
|
|
2187
|
-
} catch {
|
|
2188
|
-
return null;
|
|
2189
|
-
}
|
|
2190
|
-
}
|
|
2191
|
-
function parseCachedJwt(raw) {
|
|
2192
|
-
if (typeof raw !== "object" || raw === null) return null;
|
|
2193
|
-
const candidate = raw;
|
|
2194
|
-
try {
|
|
2195
|
-
return {
|
|
2196
|
-
token: toJwtToken(candidate.token),
|
|
2197
|
-
expEpochSeconds: toEpochSeconds(candidate.expEpochSeconds)
|
|
2198
|
-
};
|
|
2199
|
-
} catch {
|
|
2200
|
-
return null;
|
|
2201
|
-
}
|
|
2202
|
-
}
|
|
2203
|
-
//#endregion
|
|
2204
|
-
//#region src/adapters/auth-cache/BrowserAuthCacheAdapter.ts
|
|
2205
|
-
const SESSION_KEY = "capxul.session";
|
|
2206
|
-
const JWT_KEY = "capxul.jwt";
|
|
2207
|
-
var BrowserAuthCacheAdapter = class {
|
|
2208
|
-
storage;
|
|
2209
|
-
constructor(storage) {
|
|
2210
|
-
this.storage = storage;
|
|
2211
|
-
}
|
|
2212
|
-
getSession = authCacheTry("getSession", () => {
|
|
2213
|
-
const raw = this.storage.getItem(SESSION_KEY);
|
|
2214
|
-
if (raw === null) return null;
|
|
2215
|
-
try {
|
|
2216
|
-
return parseAuthSession(JSON.parse(raw));
|
|
2217
|
-
} catch {
|
|
2218
|
-
return null;
|
|
2219
|
-
}
|
|
2220
|
-
});
|
|
2221
|
-
setSession = (session) => authCacheTry("setSession", () => {
|
|
2222
|
-
this.storage.setItem(SESSION_KEY, JSON.stringify(session));
|
|
2223
|
-
});
|
|
2224
|
-
clearSession = authCacheTry("clearSession", () => {
|
|
2225
|
-
this.storage.removeItem(SESSION_KEY);
|
|
2226
|
-
});
|
|
2227
|
-
getJwt = authCacheTry("getJwt", () => {
|
|
2228
|
-
const raw = this.storage.getItem(JWT_KEY);
|
|
2229
|
-
if (raw === null) return null;
|
|
2230
|
-
try {
|
|
2231
|
-
return parseCachedJwt(JSON.parse(raw));
|
|
2232
|
-
} catch {
|
|
2233
|
-
return null;
|
|
2234
|
-
}
|
|
2235
|
-
});
|
|
2236
|
-
setJwt = (jwt) => authCacheTry("setJwt", () => {
|
|
2237
|
-
this.storage.setItem(JWT_KEY, JSON.stringify(jwt));
|
|
2238
|
-
});
|
|
2239
|
-
clearJwt = authCacheTry("clearJwt", () => {
|
|
2240
|
-
this.storage.removeItem(JWT_KEY);
|
|
2241
|
-
});
|
|
2242
|
-
};
|
|
2243
|
-
function toAuthCacheError(operation, cause) {
|
|
2244
|
-
return new AuthCacheError({
|
|
2245
|
-
operation,
|
|
2246
|
-
cause
|
|
2247
|
-
});
|
|
2248
|
-
}
|
|
2249
|
-
function authCacheTry(operation, run) {
|
|
2250
|
-
return Effect.try({
|
|
2251
|
-
try: run,
|
|
2252
|
-
catch: (cause) => toAuthCacheError(operation, cause)
|
|
2253
|
-
});
|
|
2254
|
-
}
|
|
2255
|
-
//#endregion
|
|
2256
|
-
//#region src/adapters/auth-cache/InMemoryAuthCacheAdapter.ts
|
|
2257
|
-
var InMemoryAuthCacheAdapter = class {
|
|
2258
|
-
session = null;
|
|
2259
|
-
jwt = null;
|
|
2260
|
-
getSession = Effect.sync(() => this.session);
|
|
2261
|
-
setSession = (session) => Effect.sync(() => {
|
|
2262
|
-
this.session = session;
|
|
2263
|
-
});
|
|
2264
|
-
clearSession = Effect.sync(() => {
|
|
2265
|
-
this.session = null;
|
|
2266
|
-
});
|
|
2267
|
-
getJwt = Effect.sync(() => this.jwt);
|
|
2268
|
-
setJwt = (jwt) => Effect.sync(() => {
|
|
2269
|
-
this.jwt = jwt;
|
|
2270
|
-
});
|
|
2271
|
-
clearJwt = Effect.sync(() => {
|
|
2272
|
-
this.jwt = null;
|
|
2273
|
-
});
|
|
2274
|
-
};
|
|
2275
|
-
Layer.effect(AuthCachePortTag, Effect.sync(() => new InMemoryAuthCacheAdapter()).pipe(Effect.mapError((cause) => new AuthCacheError({
|
|
2276
|
-
operation: "initialize",
|
|
2277
|
-
cause
|
|
2278
|
-
}))));
|
|
2279
|
-
//#endregion
|
|
2280
|
-
//#region src/adapters/_shared/clock.ts
|
|
2281
|
-
async function readClockNow(clock) {
|
|
2282
|
-
try {
|
|
2283
|
-
const result = await Effect.runPromise(Effect.result(clock.now));
|
|
2284
|
-
if (Result.isFailure(result)) return {
|
|
2285
|
-
ok: false,
|
|
2286
|
-
error: Errors.providerError("clock", "now", result.failure)
|
|
2287
|
-
};
|
|
2288
|
-
return {
|
|
2289
|
-
ok: true,
|
|
2290
|
-
value: result.success
|
|
2291
|
-
};
|
|
2292
|
-
} catch (cause) {
|
|
2293
|
-
return {
|
|
2294
|
-
ok: false,
|
|
2295
|
-
error: Errors.providerError("clock", "now", cause)
|
|
2296
|
-
};
|
|
2297
|
-
}
|
|
2298
|
-
}
|
|
2299
|
-
//#endregion
|
|
2300
|
-
//#region src/adapters/auth-client/OAuthBearerAuthClient.ts
|
|
2301
|
-
const PROVIDER = "agent-exchange";
|
|
2302
|
-
/** Re-exchange this far ahead of expiry, matching the JWT cache eviction margin. */
|
|
2303
|
-
const REFRESH_MARGIN_MS = 3e4;
|
|
2304
|
-
/**
|
|
2305
|
-
* `AuthSession.token` is a Better Auth session handle. This flow has none, and
|
|
2306
|
-
* the access token must never be copied into it, so the slot holds a placeholder.
|
|
2307
|
-
*/
|
|
2308
|
-
const OAUTH_SESSION_TOKEN = toSessionToken("oauth-bearer");
|
|
2309
|
-
var OAuthBearerAuthClient = class {
|
|
2310
|
-
#exchangeUrl;
|
|
2311
|
-
#exchangeSecret;
|
|
2312
|
-
#accessToken;
|
|
2313
|
-
#fetchImpl;
|
|
2314
|
-
#clock;
|
|
2315
|
-
#cached = null;
|
|
2316
|
-
constructor(input) {
|
|
2317
|
-
this.#exchangeUrl = input.exchangeUrl;
|
|
2318
|
-
this.#exchangeSecret = input.exchangeSecret;
|
|
2319
|
-
this.#accessToken = input.accessToken;
|
|
2320
|
-
this.#fetchImpl = input.fetch ?? fetch;
|
|
2321
|
-
this.#clock = input.clock ?? new SystemClockAdapter();
|
|
2322
|
-
}
|
|
2323
|
-
async canSendOtp() {
|
|
2324
|
-
return refuse("canSendOtp");
|
|
2325
|
-
}
|
|
2326
|
-
async sendOtp() {
|
|
2327
|
-
return refuse("sendOtp");
|
|
2328
|
-
}
|
|
2329
|
-
async verifyOtp() {
|
|
2330
|
-
return refuse("verifyOtp");
|
|
2331
|
-
}
|
|
2332
|
-
/**
|
|
2333
|
-
* The authorization server owns the token's lifetime, so revoking it there is
|
|
2334
|
-
* the sign-out. Reporting success here would claim a token was dropped.
|
|
2335
|
-
*/
|
|
2336
|
-
async signOut() {
|
|
2337
|
-
return refuse("signOut");
|
|
2338
|
-
}
|
|
2339
|
-
/**
|
|
2340
|
-
* The exchanged identity. Unlike the Better Auth adapters this never answers
|
|
2341
|
-
* `null`: the consumer holds a usable access token or a refused one, and a
|
|
2342
|
-
* refused one is a NOT_AUTHENTICATED failure, not the absence of a session.
|
|
2343
|
-
*/
|
|
2344
|
-
async getSession(options) {
|
|
2345
|
-
const exchange = await this.#exchange("getSession", options);
|
|
2346
|
-
if (!exchange.ok) return exchange;
|
|
2347
|
-
return {
|
|
2348
|
-
ok: true,
|
|
2349
|
-
value: {
|
|
2350
|
-
authUserId: toAuthUserId(exchange.value.authUserId),
|
|
2351
|
-
email: toEmail(exchange.value.email),
|
|
2352
|
-
token: OAUTH_SESSION_TOKEN,
|
|
2353
|
-
expiresAt: toEpochMs(exchange.value.expiresAt)
|
|
2354
|
-
}
|
|
2355
|
-
};
|
|
2356
|
-
}
|
|
2357
|
-
async getConvexJwt(options) {
|
|
2358
|
-
const exchange = await this.#exchange("getConvexJwt", options);
|
|
2359
|
-
if (!exchange.ok) return exchange;
|
|
2360
|
-
return {
|
|
2361
|
-
ok: true,
|
|
2362
|
-
value: {
|
|
2363
|
-
token: toJwtToken(exchange.value.token),
|
|
2364
|
-
expEpochSeconds: toEpochSeconds(Math.floor(exchange.value.expiresAt / 1e3))
|
|
2365
|
-
}
|
|
2366
|
-
};
|
|
2367
|
-
}
|
|
2368
|
-
/**
|
|
2369
|
-
* The cached exchange while it is fresh, otherwise a new one. A failed
|
|
2370
|
-
* exchange never replaces the cache, so a stale token is never served.
|
|
2371
|
-
*
|
|
2372
|
-
* lazy: no in-flight de-duplication — two concurrent reads past the margin
|
|
2373
|
-
* each mint a JWT. Minting is idempotent, so the cost is one extra request.
|
|
2374
|
-
*/
|
|
2375
|
-
async #exchange(operation, options) {
|
|
2376
|
-
if (options?.signal?.aborted) return {
|
|
2377
|
-
ok: false,
|
|
2378
|
-
error: Errors.cancelled({ operation })
|
|
2379
|
-
};
|
|
2380
|
-
const cached = this.#cached;
|
|
2381
|
-
if (cached !== null && options?.forceRefresh !== true) {
|
|
2382
|
-
const now = await readClockNow(this.#clock);
|
|
2383
|
-
if (!now.ok) return now;
|
|
2384
|
-
if (now.value < cached.expiresAt - REFRESH_MARGIN_MS) return {
|
|
2385
|
-
ok: true,
|
|
2386
|
-
value: cached
|
|
2387
|
-
};
|
|
2388
|
-
}
|
|
2389
|
-
return this.#postExchange(operation, options?.signal);
|
|
2390
|
-
}
|
|
2391
|
-
async #postExchange(operation, signal) {
|
|
2392
|
-
let res;
|
|
2393
|
-
try {
|
|
2394
|
-
res = await this.#fetchImpl(this.#exchangeUrl, {
|
|
2395
|
-
method: "POST",
|
|
2396
|
-
headers: {
|
|
2397
|
-
"content-type": "application/json",
|
|
2398
|
-
authorization: `Bearer ${this.#exchangeSecret}`
|
|
2399
|
-
},
|
|
2400
|
-
body: JSON.stringify({ accessToken: this.#accessToken }),
|
|
2401
|
-
...signal === void 0 ? {} : { signal }
|
|
2402
|
-
});
|
|
2403
|
-
} catch (cause) {
|
|
2404
|
-
return {
|
|
2405
|
-
ok: false,
|
|
2406
|
-
error: signal?.aborted === true || isAbortError(cause) ? Errors.cancelled({ operation }) : Errors.networkError(operation, cause)
|
|
2407
|
-
};
|
|
2408
|
-
}
|
|
2409
|
-
if (signal?.aborted === true) return {
|
|
2410
|
-
ok: false,
|
|
2411
|
-
error: Errors.cancelled({ operation })
|
|
2412
|
-
};
|
|
2413
|
-
const body = await res.json().catch(() => null);
|
|
2414
|
-
if (!res.ok) return {
|
|
2415
|
-
ok: false,
|
|
2416
|
-
error: exchangeRefused(operation, res.status, readErrorCode(body))
|
|
2417
|
-
};
|
|
2418
|
-
const exchange = readExchange(body);
|
|
2419
|
-
if (exchange === null) return {
|
|
2420
|
-
ok: false,
|
|
2421
|
-
error: Errors.providerError(PROVIDER, operation, /* @__PURE__ */ new Error("unexpected body"), { httpStatus: res.status })
|
|
2422
|
-
};
|
|
2423
|
-
this.#cached = exchange;
|
|
2424
|
-
return {
|
|
2425
|
-
ok: true,
|
|
2426
|
-
value: exchange
|
|
2427
|
-
};
|
|
2428
|
-
}
|
|
2429
|
-
};
|
|
2430
|
-
/**
|
|
2431
|
-
* `AuthClientPort` over the backend agent-token exchange. Build one per OAuth
|
|
2432
|
-
* access token and hand it to `createCapxulClient({ authClient })`.
|
|
2433
|
-
*/
|
|
2434
|
-
function oauthBearerAuthClient(input) {
|
|
2435
|
-
return authClientPortFromPromiseAdapter(new OAuthBearerAuthClient(input));
|
|
2436
|
-
}
|
|
2437
|
-
/** The OTP verbs belong to the email flow; `validStates` names it for the caller. */
|
|
2438
|
-
function refuse(method) {
|
|
2439
|
-
return {
|
|
2440
|
-
ok: false,
|
|
2441
|
-
error: Errors.wrongState({
|
|
2442
|
-
method,
|
|
2443
|
-
currentState: "oauth-bearer",
|
|
2444
|
-
validStates: ["email-otp"]
|
|
2445
|
-
})
|
|
2446
|
-
};
|
|
2447
|
-
}
|
|
2448
|
-
/**
|
|
2449
|
-
* The error catalog has no FORBIDDEN code, so 403 (`insufficient_scope`) and
|
|
2450
|
-
* 503 (`exchange_unconfigured`) both land on PROVIDER_ERROR. `httpStatus` and
|
|
2451
|
-
* `details.reason` carry which one the backend reported. Only 401 becomes
|
|
2452
|
-
* NOT_AUTHENTICATED, because only 401 is answered by a new access token.
|
|
2453
|
-
*/
|
|
2454
|
-
function exchangeRefused(operation, status, reason) {
|
|
2455
|
-
if (status === 401) return Errors.notAuthenticated("The exchange refused this OAuth access token");
|
|
2456
|
-
return Errors.providerError(PROVIDER, operation, /* @__PURE__ */ new Error(`HTTP ${status}`), {
|
|
2457
|
-
httpStatus: status,
|
|
2458
|
-
...reason === void 0 ? {} : { details: { reason } }
|
|
2459
|
-
});
|
|
2460
|
-
}
|
|
2461
|
-
/** An abort raised by something other than the caller's own signal. */
|
|
2462
|
-
function isAbortError(cause) {
|
|
2463
|
-
return readField(cause, "name") === "AbortError";
|
|
2464
|
-
}
|
|
2465
|
-
function readErrorCode(body) {
|
|
2466
|
-
const error = readField(body, "error");
|
|
2467
|
-
return typeof error === "string" ? error : void 0;
|
|
2468
|
-
}
|
|
2469
|
-
function readField(value, key) {
|
|
2470
|
-
if (typeof value !== "object" || value === null) return void 0;
|
|
2471
|
-
return value[key];
|
|
2472
|
-
}
|
|
2473
|
-
function readExchange(body) {
|
|
2474
|
-
if (typeof body !== "object" || body === null) return null;
|
|
2475
|
-
const { token, expiresAt, authUserId, email } = body;
|
|
2476
|
-
if (typeof token !== "string" || token.length === 0) return null;
|
|
2477
|
-
if (typeof expiresAt !== "number" || !Number.isFinite(expiresAt)) return null;
|
|
2478
|
-
if (typeof authUserId !== "string" || authUserId.length === 0) return null;
|
|
2479
|
-
if (typeof email !== "string" || email.length === 0) return null;
|
|
2480
|
-
return {
|
|
2481
|
-
token,
|
|
2482
|
-
expiresAt,
|
|
2483
|
-
authUserId,
|
|
2484
|
-
email
|
|
2485
|
-
};
|
|
2486
|
-
}
|
|
2487
|
-
//#endregion
|
|
2488
|
-
export { toPartyId as $, SUPPORTED_CURRENCY_CODES as A, toAuthUserId as B, ASSET_ID_RE as C, isChainUpstream as Ct, COUNTRY_CODE_RE as D, isCredentialField as Dt, CLIENT_REQUEST_ID_RE as E, containsSensitiveMaterial as Et, toAccountId as F, toDurationMs as G, toChainId as H, toAddress as I, toEpochSeconds as J, toEmail as K, toAllowedOrigin as L, ZERO_BYTES32 as M, assetIdFor as N, EMAIL_RE as O, redactSecrets as Ot, currencySymbolFor as P, toOrgId as Q, toAppId as R, APP_ID_RE as S, failureFingerprint as St, CLIENT_GRANT_ID_RE as T, revertSummaryText as Tt, toCountryCode as U, toBudgetId as V, toCurrencyCode as W, toJwtToken as X, toHandle as Y, toKycTier as Z, normalizeBindingEmail as _, FAILURE_MODES as _t, parseAuthSession as a, toTesterKind as at, configuredMoneyAssetById as b, chainEvidenceLabel as bt, AuthCachePortTag as c, validateHandle as ct, authClientPortFromPromiseAdapter as d, CAPXUL_ERROR_CODES as dt, toPayrollGroupId as et, AuthClientError as f, CapxulError as ft, CAPXUL_PAYMENTS_V2_ADDRESS as g, CHAIN_UPSTREAMS as gt, orgRoleKeyForLabel as h, isCapxulError as ht, BrowserAuthCacheAdapter as i, toSessionToken as it, WEI_RE as j, EVM_ADDRESS_RE as k, redactUrlSecrets as kt, SystemClockLayer as l, HANDLE_RE as lt, ADMIN_ROLE_LABEL as m, Errors as mt, readClockNow as n, toPublishableKey as nt, parseCachedJwt as o, toTxHash as ot, AuthClientPortTag as p, EXPECTED_OPERATION_OUTCOMES as pt, toEpochMs as q, InMemoryAuthCacheAdapter as r, toRoleKey as rt, AuthCacheError as s, toWeiAmount as st, oauthBearerAuthClient as t, toPayrollRunId as tt, ClockPortTag as u, decodeConvexError as ut, BASE_SEPOLIA_CHAIN_ID as v, boundedResponseHeaders as vt, BYTES32_RE as w, isFailureMode as wt, ACCOUNT_ID_RE as x, decodeChainCause as xt, deriveCapxulSafeAddress as y, chainCauseProperties as yt, toAssetId as z };
|