@trusset/sdk 0.1.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +175 -171
- package/dist/base-BYEauvBZ.d.mts +111 -0
- package/dist/base-zODt0Kf5.d.ts +111 -0
- package/dist/chunk-2KHUPARH.js +2260 -0
- package/dist/chunk-3YF42MHV.mjs +678 -0
- package/dist/chunk-4MNJZAXE.mjs +2260 -0
- package/dist/chunk-7HFMHYRA.js +107 -0
- package/dist/chunk-ACXAKW5E.js +916 -0
- package/dist/chunk-AI4ZNJLR.js +678 -0
- package/dist/chunk-C7CANC53.mjs +208 -0
- package/dist/chunk-DQCX2GYT.mjs +107 -0
- package/dist/chunk-GWMT6UU7.js +208 -0
- package/dist/chunk-OQG3PEF3.mjs +916 -0
- package/dist/flow-Cz8A09rn.d.mts +171 -0
- package/dist/flow-Cz8A09rn.d.ts +171 -0
- package/dist/index-DKUSzkBN.d.ts +956 -0
- package/dist/index-Dl4U8vUM.d.mts +956 -0
- package/dist/index.d.mts +39 -448
- package/dist/index.d.ts +39 -448
- package/dist/index.js +264 -534
- package/dist/index.mjs +249 -483
- package/dist/modules/customers/index.d.mts +2 -0
- package/dist/modules/customers/index.d.ts +2 -0
- package/dist/modules/customers/index.js +17 -0
- package/dist/modules/customers/index.mjs +17 -0
- package/dist/modules/lending/index.d.mts +6768 -0
- package/dist/modules/lending/index.d.ts +6768 -0
- package/dist/modules/lending/index.js +44 -0
- package/dist/modules/lending/index.mjs +44 -0
- package/dist/modules/tokens/index.d.mts +476 -0
- package/dist/modules/tokens/index.d.ts +476 -0
- package/dist/modules/tokens/index.js +8 -0
- package/dist/modules/tokens/index.mjs +8 -0
- package/package.json +71 -35
|
@@ -0,0 +1,678 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/utils/errors.ts
|
|
2
|
+
var TrussetError = class _TrussetError extends Error {
|
|
3
|
+
constructor(message, code, statusCode, requestId, info) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "TrussetError";
|
|
6
|
+
this.code = code;
|
|
7
|
+
this.statusCode = statusCode;
|
|
8
|
+
this.requestId = _nullishCoalesce(requestId, () => ( null));
|
|
9
|
+
this.details = _nullishCoalesce(_optionalChain([info, 'optionalAccess', _ => _.details]), () => ( null));
|
|
10
|
+
this.data = _nullishCoalesce(_optionalChain([info, 'optionalAccess', _2 => _2.data]), () => ( null));
|
|
11
|
+
this.retryAfter = _nullishCoalesce(_optionalChain([info, 'optionalAccess', _3 => _3.retryAfter]), () => ( null));
|
|
12
|
+
Object.setPrototypeOf(this, _TrussetError.prototype);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
var AuthError = class _AuthError extends TrussetError {
|
|
16
|
+
constructor(message, requestId) {
|
|
17
|
+
super(message, "AUTH_FAILED", 401, requestId);
|
|
18
|
+
this.name = "AuthError";
|
|
19
|
+
Object.setPrototypeOf(this, _AuthError.prototype);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
var ValidationError = class _ValidationError extends TrussetError {
|
|
23
|
+
constructor(message) {
|
|
24
|
+
super(message, "VALIDATION_ERROR", 400);
|
|
25
|
+
this.name = "ValidationError";
|
|
26
|
+
Object.setPrototypeOf(this, _ValidationError.prototype);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
var NetworkError = class _NetworkError extends TrussetError {
|
|
30
|
+
constructor(message) {
|
|
31
|
+
super(message, "NETWORK_ERROR", 0);
|
|
32
|
+
this.name = "NetworkError";
|
|
33
|
+
Object.setPrototypeOf(this, _NetworkError.prototype);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
var ConfirmationError = class _ConfirmationError extends TrussetError {
|
|
37
|
+
constructor(message, code, transactions, txHash, failure) {
|
|
38
|
+
const inner = failure instanceof TrussetError ? failure : null;
|
|
39
|
+
super(message, code, inner ? inner.statusCode : 0, _nullishCoalesce(_optionalChain([inner, 'optionalAccess', _4 => _4.requestId]), () => ( void 0)), inner ? { details: inner.details, data: inner.data, retryAfter: inner.retryAfter } : void 0);
|
|
40
|
+
this.name = "ConfirmationError";
|
|
41
|
+
this.transactions = transactions;
|
|
42
|
+
this.txHash = txHash;
|
|
43
|
+
this.failure = failure;
|
|
44
|
+
Object.setPrototypeOf(this, _ConfirmationError.prototype);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// src/utils/keccak.ts
|
|
49
|
+
var MASK = (1n << 64n) - 1n;
|
|
50
|
+
var ROUND_CONSTANTS = [
|
|
51
|
+
0x0000000000000001n,
|
|
52
|
+
0x0000000000008082n,
|
|
53
|
+
0x800000000000808an,
|
|
54
|
+
0x8000000080008000n,
|
|
55
|
+
0x000000000000808bn,
|
|
56
|
+
0x0000000080000001n,
|
|
57
|
+
0x8000000080008081n,
|
|
58
|
+
0x8000000000008009n,
|
|
59
|
+
0x000000000000008an,
|
|
60
|
+
0x0000000000000088n,
|
|
61
|
+
0x0000000080008009n,
|
|
62
|
+
0x000000008000000an,
|
|
63
|
+
0x000000008000808bn,
|
|
64
|
+
0x800000000000008bn,
|
|
65
|
+
0x8000000000008089n,
|
|
66
|
+
0x8000000000008003n,
|
|
67
|
+
0x8000000000008002n,
|
|
68
|
+
0x8000000000000080n,
|
|
69
|
+
0x000000000000800an,
|
|
70
|
+
0x800000008000000an,
|
|
71
|
+
0x8000000080008081n,
|
|
72
|
+
0x8000000000008080n,
|
|
73
|
+
0x0000000080000001n,
|
|
74
|
+
0x8000000080008008n
|
|
75
|
+
];
|
|
76
|
+
var ROTATIONS = [
|
|
77
|
+
0,
|
|
78
|
+
1,
|
|
79
|
+
62,
|
|
80
|
+
28,
|
|
81
|
+
27,
|
|
82
|
+
36,
|
|
83
|
+
44,
|
|
84
|
+
6,
|
|
85
|
+
55,
|
|
86
|
+
20,
|
|
87
|
+
3,
|
|
88
|
+
10,
|
|
89
|
+
43,
|
|
90
|
+
25,
|
|
91
|
+
39,
|
|
92
|
+
41,
|
|
93
|
+
45,
|
|
94
|
+
15,
|
|
95
|
+
21,
|
|
96
|
+
8,
|
|
97
|
+
18,
|
|
98
|
+
2,
|
|
99
|
+
61,
|
|
100
|
+
56,
|
|
101
|
+
14
|
|
102
|
+
];
|
|
103
|
+
var RATE_BYTES = 136;
|
|
104
|
+
function rotl(value, shift) {
|
|
105
|
+
if (shift === 0) return value;
|
|
106
|
+
const s = BigInt(shift);
|
|
107
|
+
return (value << s | value >> 64n - s) & MASK;
|
|
108
|
+
}
|
|
109
|
+
function permute(state) {
|
|
110
|
+
const c = new Array(5);
|
|
111
|
+
const b = new Array(25);
|
|
112
|
+
for (let round = 0; round < 24; round++) {
|
|
113
|
+
for (let x = 0; x < 5; x++) {
|
|
114
|
+
c[x] = state[x] ^ state[x + 5] ^ state[x + 10] ^ state[x + 15] ^ state[x + 20];
|
|
115
|
+
}
|
|
116
|
+
for (let x = 0; x < 5; x++) {
|
|
117
|
+
const d = c[(x + 4) % 5] ^ rotl(c[(x + 1) % 5], 1);
|
|
118
|
+
for (let y = 0; y < 25; y += 5) state[y + x] ^= d;
|
|
119
|
+
}
|
|
120
|
+
for (let x = 0; x < 5; x++) {
|
|
121
|
+
for (let y = 0; y < 5; y++) {
|
|
122
|
+
const index = x + 5 * y;
|
|
123
|
+
b[y + 5 * ((2 * x + 3 * y) % 5)] = rotl(state[index], ROTATIONS[index]);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
for (let x = 0; x < 5; x++) {
|
|
127
|
+
for (let y = 0; y < 25; y += 5) {
|
|
128
|
+
state[y + x] = b[y + x] ^ ~b[y + (x + 1) % 5] & MASK & b[y + (x + 2) % 5];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
state[0] ^= ROUND_CONSTANTS[round];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
function keccak256(input) {
|
|
135
|
+
const padded = new Uint8Array(Math.ceil((input.length + 1) / RATE_BYTES) * RATE_BYTES);
|
|
136
|
+
padded.set(input);
|
|
137
|
+
padded[input.length] ^= 1;
|
|
138
|
+
padded[padded.length - 1] ^= 128;
|
|
139
|
+
const state = new Array(25).fill(0n);
|
|
140
|
+
for (let offset = 0; offset < padded.length; offset += RATE_BYTES) {
|
|
141
|
+
for (let lane = 0; lane < RATE_BYTES / 8; lane++) {
|
|
142
|
+
let value = 0n;
|
|
143
|
+
for (let byte = 7; byte >= 0; byte--) {
|
|
144
|
+
value = value << 8n | BigInt(padded[offset + lane * 8 + byte]);
|
|
145
|
+
}
|
|
146
|
+
state[lane] ^= value;
|
|
147
|
+
}
|
|
148
|
+
permute(state);
|
|
149
|
+
}
|
|
150
|
+
const out = new Uint8Array(32);
|
|
151
|
+
for (let lane = 0; lane < 4; lane++) {
|
|
152
|
+
let value = state[lane];
|
|
153
|
+
for (let byte = 0; byte < 8; byte++) {
|
|
154
|
+
out[lane * 8 + byte] = Number(value & 0xffn);
|
|
155
|
+
value >>= 8n;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return out;
|
|
159
|
+
}
|
|
160
|
+
function keccak256Hex(text) {
|
|
161
|
+
const bytes = keccak256(new TextEncoder().encode(text));
|
|
162
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
163
|
+
}
|
|
164
|
+
function toChecksumAddress(address) {
|
|
165
|
+
const lower = address.toLowerCase().replace(/^0x/, "");
|
|
166
|
+
const hash = keccak256Hex(lower);
|
|
167
|
+
let out = "0x";
|
|
168
|
+
for (let i = 0; i < lower.length; i++) {
|
|
169
|
+
out += parseInt(hash[i], 16) >= 8 ? lower[i].toUpperCase() : lower[i];
|
|
170
|
+
}
|
|
171
|
+
return out;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// src/utils/validation.ts
|
|
175
|
+
var ETH_ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
|
|
176
|
+
var BYTES32_RE = /^0x[0-9a-fA-F]{64}$/;
|
|
177
|
+
function requireString(value, field) {
|
|
178
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
179
|
+
throw new ValidationError(`${field} is required and must be a non-empty string`);
|
|
180
|
+
}
|
|
181
|
+
return value.trim();
|
|
182
|
+
}
|
|
183
|
+
function requireAddress(value, field) {
|
|
184
|
+
const s = requireString(value, field);
|
|
185
|
+
if (!ETH_ADDRESS_RE.test(s)) {
|
|
186
|
+
throw new ValidationError(`${field} must be a valid Ethereum address`);
|
|
187
|
+
}
|
|
188
|
+
return s;
|
|
189
|
+
}
|
|
190
|
+
function optionalAddress(value, field) {
|
|
191
|
+
if (value === void 0 || value === null || value === "") return void 0;
|
|
192
|
+
return requireAddress(value, field);
|
|
193
|
+
}
|
|
194
|
+
function requireCountry(value, field) {
|
|
195
|
+
const s = requireString(value, field);
|
|
196
|
+
if (s.length < 2 || s.length > 3) {
|
|
197
|
+
throw new ValidationError(`${field} must be a 2-3 letter country code`);
|
|
198
|
+
}
|
|
199
|
+
return s.toUpperCase();
|
|
200
|
+
}
|
|
201
|
+
function requireHash(value, field) {
|
|
202
|
+
const s = requireString(value, field);
|
|
203
|
+
if (!BYTES32_RE.test(s)) {
|
|
204
|
+
throw new ValidationError(`${field} must be a 32-byte hex string (0x + 64 hex chars)`);
|
|
205
|
+
}
|
|
206
|
+
return s;
|
|
207
|
+
}
|
|
208
|
+
function optionalHash(value, field) {
|
|
209
|
+
if (value === void 0 || value === null || value === "") return void 0;
|
|
210
|
+
return requireHash(value, field);
|
|
211
|
+
}
|
|
212
|
+
function clampInt(value, min, max, fallback) {
|
|
213
|
+
if (value === void 0 || value === null) return fallback;
|
|
214
|
+
const n = Math.floor(value);
|
|
215
|
+
if (isNaN(n)) return fallback;
|
|
216
|
+
return Math.max(min, Math.min(max, n));
|
|
217
|
+
}
|
|
218
|
+
var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
|
|
219
|
+
var DECIMAL_RE = /^\d+(\.\d+)?$/;
|
|
220
|
+
var INTEGER_STRING_RE = /^\d+$/;
|
|
221
|
+
var ID_RE = /^[A-Za-z0-9_-]{1,100}$/;
|
|
222
|
+
function requireWallet(value, field, options = {}) {
|
|
223
|
+
if (value === void 0 || value === null || typeof value === "string" && value.trim() === "") {
|
|
224
|
+
throw new ValidationError(`${field} is required`);
|
|
225
|
+
}
|
|
226
|
+
const raw = typeof value === "string" ? value.trim() : "";
|
|
227
|
+
if (!ETH_ADDRESS_RE.test(raw)) {
|
|
228
|
+
throw new ValidationError(`${field} must be a 42-character address: 0x followed by 40 hex characters`);
|
|
229
|
+
}
|
|
230
|
+
if (!options.allowZero && raw.toLowerCase() === ZERO_ADDRESS) {
|
|
231
|
+
throw new ValidationError(`${field} must not be the zero address`);
|
|
232
|
+
}
|
|
233
|
+
const body = raw.slice(2);
|
|
234
|
+
const mixedCase = body !== body.toLowerCase() && body !== body.toUpperCase();
|
|
235
|
+
if (mixedCase && toChecksumAddress(raw) !== raw) {
|
|
236
|
+
throw new ValidationError(`${field} fails its EIP-55 checksum, which almost always means a character was mistyped. Check it against its source, or send it in all-lowercase`);
|
|
237
|
+
}
|
|
238
|
+
return raw;
|
|
239
|
+
}
|
|
240
|
+
function optionalWallet(value, field, options = {}) {
|
|
241
|
+
if (value === void 0 || value === null || value === "") return void 0;
|
|
242
|
+
return requireWallet(value, field, options);
|
|
243
|
+
}
|
|
244
|
+
function requireAmount(value, field, options = {}) {
|
|
245
|
+
if (typeof value !== "string") {
|
|
246
|
+
throw new ValidationError(`${field} must be a decimal string such as "100" or "12.5", never a JavaScript number`);
|
|
247
|
+
}
|
|
248
|
+
const s = value.trim();
|
|
249
|
+
if (!DECIMAL_RE.test(s)) {
|
|
250
|
+
throw new ValidationError(`${field} must be written as digits with an optional decimal fraction, such as "100" or "12.5"`);
|
|
251
|
+
}
|
|
252
|
+
if (!options.allowZero && !/[1-9]/.test(s)) {
|
|
253
|
+
throw new ValidationError(`${field} must be greater than zero`);
|
|
254
|
+
}
|
|
255
|
+
return s;
|
|
256
|
+
}
|
|
257
|
+
function optionalAmount(value, field, options = {}) {
|
|
258
|
+
if (value === void 0 || value === null) return void 0;
|
|
259
|
+
return requireAmount(value, field, options);
|
|
260
|
+
}
|
|
261
|
+
function requireIntegerString(value, field) {
|
|
262
|
+
if (typeof value !== "string" || !INTEGER_STRING_RE.test(value.trim())) {
|
|
263
|
+
throw new ValidationError(`${field} must be a whole number written as a string of digits`);
|
|
264
|
+
}
|
|
265
|
+
return value.trim();
|
|
266
|
+
}
|
|
267
|
+
function requireInt(value, field, min, max) {
|
|
268
|
+
if (typeof value !== "number" || !Number.isInteger(value)) {
|
|
269
|
+
throw new ValidationError(`${field} must be a whole number`);
|
|
270
|
+
}
|
|
271
|
+
if (value < min || value > max) {
|
|
272
|
+
throw new ValidationError(`${field} must be between ${min} and ${max}`);
|
|
273
|
+
}
|
|
274
|
+
return value;
|
|
275
|
+
}
|
|
276
|
+
function optionalInt(value, field, min, max) {
|
|
277
|
+
if (value === void 0 || value === null) return void 0;
|
|
278
|
+
return requireInt(value, field, min, max);
|
|
279
|
+
}
|
|
280
|
+
function requireBoolean(value, field) {
|
|
281
|
+
if (typeof value !== "boolean") {
|
|
282
|
+
throw new ValidationError(`${field} must be true or false`);
|
|
283
|
+
}
|
|
284
|
+
return value;
|
|
285
|
+
}
|
|
286
|
+
function optionalBoolean(value, field) {
|
|
287
|
+
if (value === void 0 || value === null) return void 0;
|
|
288
|
+
return requireBoolean(value, field);
|
|
289
|
+
}
|
|
290
|
+
function requireOneOf(value, field, allowed) {
|
|
291
|
+
if (typeof value !== "string" || !allowed.includes(value)) {
|
|
292
|
+
throw new ValidationError(`${field} must be one of ${allowed.join(", ")}`);
|
|
293
|
+
}
|
|
294
|
+
return value;
|
|
295
|
+
}
|
|
296
|
+
function optionalOneOf(value, field, allowed) {
|
|
297
|
+
if (value === void 0 || value === null) return void 0;
|
|
298
|
+
return requireOneOf(value, field, allowed);
|
|
299
|
+
}
|
|
300
|
+
function requireTxHash(value, field = "txHash") {
|
|
301
|
+
if (typeof value !== "string" || !BYTES32_RE.test(value.trim())) {
|
|
302
|
+
throw new ValidationError(`${field} must be the hash of the broadcast transaction: 0x followed by 64 hex characters`);
|
|
303
|
+
}
|
|
304
|
+
return value.trim();
|
|
305
|
+
}
|
|
306
|
+
function requirePositiveId(value, field) {
|
|
307
|
+
if (typeof value === "number" && Number.isSafeInteger(value) && value > 0) return String(value);
|
|
308
|
+
if (typeof value === "string" && INTEGER_STRING_RE.test(value.trim()) && Number(value.trim()) > 0 && Number.isSafeInteger(Number(value.trim()))) {
|
|
309
|
+
return value.trim();
|
|
310
|
+
}
|
|
311
|
+
throw new ValidationError(`${field} must be a positive whole number`);
|
|
312
|
+
}
|
|
313
|
+
function requireMarketId(value) {
|
|
314
|
+
const s = requireString(value, "marketId");
|
|
315
|
+
if (ETH_ADDRESS_RE.test(s)) {
|
|
316
|
+
throw new ValidationError("marketId is the market record id that lending.markets.list() returns, not the contract address. Routes keyed by a contract address take marketAddress instead");
|
|
317
|
+
}
|
|
318
|
+
if (s.length > 100) {
|
|
319
|
+
throw new ValidationError("marketId must be at most 100 characters");
|
|
320
|
+
}
|
|
321
|
+
return s;
|
|
322
|
+
}
|
|
323
|
+
function requireRecordId(value, field) {
|
|
324
|
+
const s = requireString(value, field);
|
|
325
|
+
if (!ID_RE.test(s)) {
|
|
326
|
+
throw new ValidationError(`${field} must be the record id the API returned (letters, digits, underscores and hyphens, at most 100 characters)`);
|
|
327
|
+
}
|
|
328
|
+
return s;
|
|
329
|
+
}
|
|
330
|
+
function sameAddress(a, b) {
|
|
331
|
+
return a.toLowerCase() === b.toLowerCase();
|
|
332
|
+
}
|
|
333
|
+
function assertInputIsNotOptions(input, example) {
|
|
334
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) return;
|
|
335
|
+
const misplaced = ["signer", "txHash"].filter((key) => key in input);
|
|
336
|
+
if (misplaced.length > 0) {
|
|
337
|
+
throw new ValidationError(`${misplaced.join(" and ")} ${misplaced.length > 1 ? "go" : "goes"} in the options argument, the last one: ${example}. Passed in the input slot, nothing is signed or confirmed`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
function checkSignedPrice(value) {
|
|
341
|
+
if (value === void 0 || value === null) return void 0;
|
|
342
|
+
if (typeof value !== "object") throw new ValidationError("signedPrice must be the { price, timestamp, validUntil, signature } object the price signer produced");
|
|
343
|
+
if (typeof value.price !== "string" || !/^\d+$/.test(value.price)) throw new ValidationError("signedPrice.price must be the raw integer price as a string of digits");
|
|
344
|
+
requireInt(value.timestamp, "signedPrice.timestamp", 1, Number.MAX_SAFE_INTEGER);
|
|
345
|
+
requireInt(value.validUntil, "signedPrice.validUntil", 1, Number.MAX_SAFE_INTEGER);
|
|
346
|
+
if (typeof value.signature !== "string" || !/^0x[0-9a-fA-F]*$/.test(value.signature)) throw new ValidationError("signedPrice.signature must be 0x-prefixed hex");
|
|
347
|
+
return value;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// src/utils/sign.ts
|
|
351
|
+
function requireSigner(signer) {
|
|
352
|
+
if (!signer || typeof signer.sendTransaction !== "function") {
|
|
353
|
+
throw new ValidationError("signer must be a wallet that can send transactions (an ethers v5 Signer)");
|
|
354
|
+
}
|
|
355
|
+
return signer;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// src/utils/flow.ts
|
|
359
|
+
var DEFAULT_CONFIRMATIONS = 1;
|
|
360
|
+
var MAX_CONFIRMATIONS = 12;
|
|
361
|
+
var GAS_HEADROOM_PERCENT = 150n;
|
|
362
|
+
var ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
|
|
363
|
+
var HEX_RE = /^0x[0-9a-fA-F]*$/;
|
|
364
|
+
function isSignOptions(options) {
|
|
365
|
+
return Boolean(options && typeof options === "object" && "signer" in options);
|
|
366
|
+
}
|
|
367
|
+
function isConfirmOptions(options) {
|
|
368
|
+
return Boolean(options && typeof options === "object" && "txHash" in options && !("signer" in options));
|
|
369
|
+
}
|
|
370
|
+
function confirmHashOf(options) {
|
|
371
|
+
return requireTxHash(options.txHash);
|
|
372
|
+
}
|
|
373
|
+
function asRecord(value) {
|
|
374
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
375
|
+
}
|
|
376
|
+
function numberOf(value) {
|
|
377
|
+
if (typeof value === "number" && Number.isInteger(value)) return value;
|
|
378
|
+
if (typeof value === "string" && /^\d+$/.test(value)) return Number(value);
|
|
379
|
+
return void 0;
|
|
380
|
+
}
|
|
381
|
+
function quantityOf(value) {
|
|
382
|
+
if (typeof value === "string" && value.trim() !== "") return value.trim();
|
|
383
|
+
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
384
|
+
if (typeof value === "bigint") return value.toString();
|
|
385
|
+
return void 0;
|
|
386
|
+
}
|
|
387
|
+
function transactionOf(raw, chainHint) {
|
|
388
|
+
const inner = _nullishCoalesce(asRecord(raw.transaction), () => ( raw));
|
|
389
|
+
const data = inner.data;
|
|
390
|
+
if (typeof data !== "string" || !HEX_RE.test(data)) return null;
|
|
391
|
+
const create = inner.create === true || raw.create === true || inner.to === null;
|
|
392
|
+
if (create && data.length <= 2) return null;
|
|
393
|
+
const to = inner.to;
|
|
394
|
+
if (!create && (typeof to !== "string" || !ADDRESS_RE.test(to))) return null;
|
|
395
|
+
const tx = { data };
|
|
396
|
+
if (!create) tx.to = to;
|
|
397
|
+
const value = quantityOf(inner.value);
|
|
398
|
+
if (value !== void 0) tx.value = value;
|
|
399
|
+
const chainId = _nullishCoalesce(_nullishCoalesce(numberOf(inner.chainId), () => ( numberOf(raw.chainId))), () => ( chainHint));
|
|
400
|
+
if (chainId !== void 0) tx.chainId = chainId;
|
|
401
|
+
const gasLimit = quantityOf(inner.gasLimit);
|
|
402
|
+
if (gasLimit !== void 0) tx.gasLimit = gasLimit;
|
|
403
|
+
return tx;
|
|
404
|
+
}
|
|
405
|
+
function functionNameOf(raw) {
|
|
406
|
+
if (typeof raw.functionName === "string") return raw.functionName;
|
|
407
|
+
if (typeof raw.key === "string") return raw.key;
|
|
408
|
+
const inner = asRecord(raw.transaction);
|
|
409
|
+
return inner && typeof inner.functionName === "string" ? inner.functionName : null;
|
|
410
|
+
}
|
|
411
|
+
function planOf(payload, options = {}) {
|
|
412
|
+
const p = asRecord(payload);
|
|
413
|
+
if (!p) return null;
|
|
414
|
+
const chainHint = numberOf(p.chainId);
|
|
415
|
+
const batch = p.mode === "batch";
|
|
416
|
+
if (Array.isArray(p.steps)) {
|
|
417
|
+
const optionalIndexes = new Set(Array.isArray(p.optionalStepIndexes) ? p.optionalStepIndexes.filter((n) => typeof n === "number") : []);
|
|
418
|
+
const steps = [];
|
|
419
|
+
p.steps.forEach((entry, index) => {
|
|
420
|
+
const raw = asRecord(entry);
|
|
421
|
+
const transaction = raw ? transactionOf(raw, chainHint) : null;
|
|
422
|
+
if (!raw || !transaction) {
|
|
423
|
+
throw new TrussetError(`Step ${index + 1} of the prepared plan carries no transaction the SDK can sign`, "UNSIGNABLE_STEP", 0);
|
|
424
|
+
}
|
|
425
|
+
steps.push({ transaction, functionName: functionNameOf(raw), optional: raw.optional === true || optionalIndexes.has(index) });
|
|
426
|
+
});
|
|
427
|
+
if (steps.length === 0) return null;
|
|
428
|
+
return { steps, confirmIndex: resolveConfirmIndex(p, steps, options), batch };
|
|
429
|
+
}
|
|
430
|
+
for (const candidate of [p.transaction ? p : null, asRecord(p.calldata), p]) {
|
|
431
|
+
if (!candidate) continue;
|
|
432
|
+
const transaction = transactionOf(candidate, chainHint);
|
|
433
|
+
if (transaction) {
|
|
434
|
+
const step = { transaction, functionName: _nullishCoalesce(functionNameOf(p), () => ( functionNameOf(candidate))), optional: false };
|
|
435
|
+
return { steps: [step], confirmIndex: options.confirmIndex === null ? null : 0, batch };
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
function resolveConfirmIndex(p, steps, options) {
|
|
441
|
+
if (options.confirmIndex === null) return null;
|
|
442
|
+
if (typeof options.confirmIndex === "number") return options.confirmIndex;
|
|
443
|
+
if (options.confirmIndex !== "last" && typeof p.confirmStepIndex === "number" && p.confirmStepIndex >= 0 && p.confirmStepIndex < steps.length) {
|
|
444
|
+
return p.confirmStepIndex;
|
|
445
|
+
}
|
|
446
|
+
for (let i = steps.length - 1; i >= 0; i--) {
|
|
447
|
+
if (!steps[i].optional) return i;
|
|
448
|
+
}
|
|
449
|
+
return steps.length - 1;
|
|
450
|
+
}
|
|
451
|
+
async function signerChainId(signer) {
|
|
452
|
+
if (typeof signer.getChainId === "function") return Number(await signer.getChainId());
|
|
453
|
+
const provider = signer.provider;
|
|
454
|
+
if (provider && typeof provider.getNetwork === "function") return Number((await provider.getNetwork()).chainId);
|
|
455
|
+
throw new ValidationError("signer must expose getChainId() so the SDK can check it is on the instance network (any ethers v5 Signer does)");
|
|
456
|
+
}
|
|
457
|
+
async function signerAddress(signer) {
|
|
458
|
+
if (typeof signer.getAddress !== "function") {
|
|
459
|
+
throw new ValidationError("signer must expose getAddress() (any ethers v5 Signer does)");
|
|
460
|
+
}
|
|
461
|
+
return await signer.getAddress();
|
|
462
|
+
}
|
|
463
|
+
async function walletOf(options, provided, field) {
|
|
464
|
+
const address = await signerAddress(requireSigner(options.signer));
|
|
465
|
+
if (provided !== void 0 && !sameAddress(provided, address)) {
|
|
466
|
+
throw new ValidationError(`${field} ${provided} is not the signer's wallet ${address}. The API checks balances, allowances and roles from ${field}, so it must be the wallet that signs`);
|
|
467
|
+
}
|
|
468
|
+
return _nullishCoalesce(provided, () => ( address));
|
|
469
|
+
}
|
|
470
|
+
async function assertChain(source, signer, stamped) {
|
|
471
|
+
const instanceChain = await source.instanceChainId();
|
|
472
|
+
for (const chainId of stamped) {
|
|
473
|
+
if (chainId !== void 0 && chainId !== instanceChain) {
|
|
474
|
+
throw new TrussetError(`The prepared transaction names chain ${chainId}, but this instance runs on chain ${instanceChain}. Nothing was signed`, "CHAIN_MISMATCH", 0);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
const signerChain = await signerChainId(signer);
|
|
478
|
+
if (signerChain !== instanceChain) {
|
|
479
|
+
throw new ValidationError(`The signer is connected to chain ${signerChain}, but this instance's transactions are for chain ${instanceChain}. Connect the signer to chain ${instanceChain}; the SDK never switches networks`);
|
|
480
|
+
}
|
|
481
|
+
return instanceChain;
|
|
482
|
+
}
|
|
483
|
+
function errorText(err) {
|
|
484
|
+
if (err && typeof err === "object") {
|
|
485
|
+
const e = err;
|
|
486
|
+
if (typeof e.reason === "string") return e.reason;
|
|
487
|
+
if (typeof e.code === "string" && typeof e.message === "string") return `${e.code}: ${e.message.split("\n")[0].slice(0, 200)}`;
|
|
488
|
+
if (typeof e.message === "string") return e.message.split("\n")[0].slice(0, 200);
|
|
489
|
+
}
|
|
490
|
+
return String(err);
|
|
491
|
+
}
|
|
492
|
+
async function sendAndWait(signer, transaction, chainId, confirmations) {
|
|
493
|
+
const request = { data: transaction.data, chainId };
|
|
494
|
+
if (transaction.to !== void 0) request.to = transaction.to;
|
|
495
|
+
if (transaction.value !== void 0) request.value = transaction.value;
|
|
496
|
+
if (transaction.gasLimit !== void 0) request.gasLimit = transaction.gasLimit;
|
|
497
|
+
else if (typeof signer.estimateGas === "function") {
|
|
498
|
+
const estimate = BigInt((await signer.estimateGas({ ...request })).toString());
|
|
499
|
+
request.gasLimit = ((estimate * GAS_HEADROOM_PERCENT + 99n) / 100n).toString();
|
|
500
|
+
}
|
|
501
|
+
const sent = await signer.sendTransaction(request);
|
|
502
|
+
try {
|
|
503
|
+
await sent.wait(confirmations);
|
|
504
|
+
return { hash: sent.hash };
|
|
505
|
+
} catch (err) {
|
|
506
|
+
const e = err;
|
|
507
|
+
if (e && e.code === "TRANSACTION_REPLACED" && e.cancelled === false && _optionalChain([e, 'access', _5 => _5.receipt, 'optionalAccess', _6 => _6.status]) === 1 && typeof _optionalChain([e, 'access', _7 => _7.replacement, 'optionalAccess', _8 => _8.hash]) === "string") {
|
|
508
|
+
return { hash: e.replacement.hash };
|
|
509
|
+
}
|
|
510
|
+
return { hash: sent.hash, error: err };
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
async function preflightChain(source, options) {
|
|
514
|
+
await assertChain(source, requireSigner(options.signer), []);
|
|
515
|
+
}
|
|
516
|
+
async function execute(source, plan, options) {
|
|
517
|
+
const signer = requireSigner(options.signer);
|
|
518
|
+
if (plan.batch) {
|
|
519
|
+
throw new ValidationError("This plan is a batch for one atomic wallet bundle (wallet_sendCalls). Prepare it without a signer, send it from a wallet that bundles calls, and confirm with { txHash } of the bundle transaction");
|
|
520
|
+
}
|
|
521
|
+
const confirmations = clampInt(options.confirmations, 1, MAX_CONFIRMATIONS, DEFAULT_CONFIRMATIONS);
|
|
522
|
+
const chainId = await assertChain(source, signer, plan.steps.map((s) => s.transaction.chainId));
|
|
523
|
+
const transactions = [];
|
|
524
|
+
for (let i = 0; i < plan.steps.length; i++) {
|
|
525
|
+
const step = plan.steps[i];
|
|
526
|
+
const broadcastSoFar = transactions.some((t) => t.txHash !== null);
|
|
527
|
+
let outcome;
|
|
528
|
+
try {
|
|
529
|
+
outcome = await sendAndWait(signer, step.transaction, chainId, confirmations);
|
|
530
|
+
} catch (err) {
|
|
531
|
+
outcome = { hash: null, error: err };
|
|
532
|
+
}
|
|
533
|
+
if (outcome.error === void 0) {
|
|
534
|
+
transactions.push({ functionName: step.functionName, txHash: outcome.hash, optional: step.optional });
|
|
535
|
+
continue;
|
|
536
|
+
}
|
|
537
|
+
transactions.push({ functionName: step.functionName, txHash: outcome.hash, optional: step.optional, error: errorText(outcome.error) });
|
|
538
|
+
if (step.optional) continue;
|
|
539
|
+
if (!broadcastSoFar && outcome.hash === null) throw outcome.error;
|
|
540
|
+
throw new ConfirmationError(
|
|
541
|
+
`Step ${i + 1} (${_nullishCoalesce(step.functionName, () => ( "transaction"))}) did not complete: ${errorText(outcome.error)}. ${broadcastSoFar ? "Earlier steps of this plan are already on chain; " : ""}see err.transactions for every hash sent`,
|
|
542
|
+
"TRANSACTION_FAILED",
|
|
543
|
+
transactions,
|
|
544
|
+
outcome.hash,
|
|
545
|
+
outcome.error
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
const confirmHash = plan.confirmIndex === null ? null : _nullishCoalesce(_optionalChain([transactions, 'access', _9 => _9[plan.confirmIndex], 'optionalAccess', _10 => _10.txHash]), () => ( null));
|
|
549
|
+
return { transactions, confirmHash };
|
|
550
|
+
}
|
|
551
|
+
async function confirmExecuted(executed, run) {
|
|
552
|
+
const hash = executed.confirmHash;
|
|
553
|
+
if (!hash) {
|
|
554
|
+
throw new ConfirmationError("The plan finished without a transaction to confirm. See err.transactions", "CONFIRMATION_FAILED", executed.transactions, null, null);
|
|
555
|
+
}
|
|
556
|
+
try {
|
|
557
|
+
const result = await run(hash);
|
|
558
|
+
return attachTransactions(result, executed.transactions);
|
|
559
|
+
} catch (err) {
|
|
560
|
+
throw new ConfirmationError(
|
|
561
|
+
`Transaction ${hash} is on chain but Trusset has not recorded it: ${errorText(err)}. Confirm it again with { txHash: '${hash}' }`,
|
|
562
|
+
"CONFIRMATION_FAILED",
|
|
563
|
+
executed.transactions,
|
|
564
|
+
hash,
|
|
565
|
+
err
|
|
566
|
+
);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
function attachTransactions(result, transactions) {
|
|
570
|
+
const base = result && typeof result === "object" ? result : { result };
|
|
571
|
+
return { ...base, transactions };
|
|
572
|
+
}
|
|
573
|
+
async function signTypedData(source, options, typedData) {
|
|
574
|
+
const signer = requireSigner(options.signer);
|
|
575
|
+
if (typeof signer._signTypedData !== "function") {
|
|
576
|
+
throw new ValidationError("signer must support EIP-712 typed data (_signTypedData, as every ethers v5 Wallet and JsonRpcSigner does)");
|
|
577
|
+
}
|
|
578
|
+
await assertChain(source, signer, [numberOf(_optionalChain([typedData, 'access', _11 => _11.domain, 'optionalAccess', _12 => _12.chainId]))]);
|
|
579
|
+
const types = { ...typedData.types };
|
|
580
|
+
delete types.EIP712Domain;
|
|
581
|
+
return await signer._signTypedData(typedData.domain, types, typedData.message);
|
|
582
|
+
}
|
|
583
|
+
async function signMessage(source, options, message) {
|
|
584
|
+
const signer = requireSigner(options.signer);
|
|
585
|
+
if (typeof signer.signMessage !== "function") {
|
|
586
|
+
throw new ValidationError("signer must support signMessage (every ethers v5 Signer does)");
|
|
587
|
+
}
|
|
588
|
+
await assertChain(source, signer, []);
|
|
589
|
+
return await signer.signMessage(message);
|
|
590
|
+
}
|
|
591
|
+
function splitSignature(signature) {
|
|
592
|
+
const hex = typeof signature === "string" ? signature.trim() : "";
|
|
593
|
+
if (/^0x[0-9a-fA-F]{130}$/.test(hex)) {
|
|
594
|
+
let v = parseInt(hex.slice(130, 132), 16);
|
|
595
|
+
if (v < 27) v += 27;
|
|
596
|
+
if (v !== 27 && v !== 28) throw new ValidationError("The signature carries an invalid recovery id");
|
|
597
|
+
return { v, r: `0x${hex.slice(2, 66)}`, s: `0x${hex.slice(66, 130)}` };
|
|
598
|
+
}
|
|
599
|
+
if (/^0x[0-9a-fA-F]{128}$/.test(hex)) {
|
|
600
|
+
const yParityAndS = BigInt(`0x${hex.slice(66, 130)}`);
|
|
601
|
+
const yParity = yParityAndS >> 255n;
|
|
602
|
+
const s = yParityAndS & (1n << 255n) - 1n;
|
|
603
|
+
return { v: yParity === 1n ? 28 : 27, r: `0x${hex.slice(2, 66)}`, s: `0x${s.toString(16).padStart(64, "0")}` };
|
|
604
|
+
}
|
|
605
|
+
throw new ValidationError("The signature must be 65 bytes (or 64 bytes in EIP-2098 compact form) of hex");
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// src/utils/chain.ts
|
|
609
|
+
var InstanceChain = class {
|
|
610
|
+
constructor(http) {
|
|
611
|
+
this.http = http;
|
|
612
|
+
this.chain = null;
|
|
613
|
+
}
|
|
614
|
+
instanceChainId() {
|
|
615
|
+
if (!this.chain) {
|
|
616
|
+
const pending = this.http.get("/instances/api/info").then((info) => {
|
|
617
|
+
const id = _optionalChain([info, 'optionalAccess', _13 => _13.network, 'optionalAccess', _14 => _14.chainId]);
|
|
618
|
+
if (typeof id !== "number" || !Number.isInteger(id)) {
|
|
619
|
+
throw new TrussetError("This instance runs on a network without an EVM chain id, so nothing can be signed for it", "CHAIN_UNRESOLVED", 0);
|
|
620
|
+
}
|
|
621
|
+
return id;
|
|
622
|
+
});
|
|
623
|
+
pending.catch(() => {
|
|
624
|
+
this.chain = null;
|
|
625
|
+
});
|
|
626
|
+
this.chain = pending;
|
|
627
|
+
}
|
|
628
|
+
return this.chain;
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
|
|
667
|
+
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
|
|
678
|
+
exports.TrussetError = TrussetError; exports.AuthError = AuthError; exports.ValidationError = ValidationError; exports.NetworkError = NetworkError; exports.ConfirmationError = ConfirmationError; exports.requireString = requireString; exports.requireAddress = requireAddress; exports.optionalAddress = optionalAddress; exports.requireCountry = requireCountry; exports.requireHash = requireHash; exports.optionalHash = optionalHash; exports.clampInt = clampInt; exports.requireWallet = requireWallet; exports.optionalWallet = optionalWallet; exports.requireAmount = requireAmount; exports.optionalAmount = optionalAmount; exports.requireIntegerString = requireIntegerString; exports.requireInt = requireInt; exports.optionalInt = optionalInt; exports.requireBoolean = requireBoolean; exports.optionalBoolean = optionalBoolean; exports.requireOneOf = requireOneOf; exports.optionalOneOf = optionalOneOf; exports.requireTxHash = requireTxHash; exports.requirePositiveId = requirePositiveId; exports.requireMarketId = requireMarketId; exports.requireRecordId = requireRecordId; exports.sameAddress = sameAddress; exports.assertInputIsNotOptions = assertInputIsNotOptions; exports.checkSignedPrice = checkSignedPrice; exports.InstanceChain = InstanceChain; exports.requireSigner = requireSigner; exports.isSignOptions = isSignOptions; exports.isConfirmOptions = isConfirmOptions; exports.confirmHashOf = confirmHashOf; exports.planOf = planOf; exports.signerAddress = signerAddress; exports.walletOf = walletOf; exports.preflightChain = preflightChain; exports.execute = execute; exports.confirmExecuted = confirmExecuted; exports.attachTransactions = attachTransactions; exports.signTypedData = signTypedData; exports.signMessage = signMessage; exports.splitSignature = splitSignature;
|