@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
package/dist/index.mjs
CHANGED
|
@@ -1,38 +1,56 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
1
|
+
import {
|
|
2
|
+
CustomerIdLinks,
|
|
3
|
+
CustomerIdentity,
|
|
4
|
+
CustomerManagement,
|
|
5
|
+
CustomerSync,
|
|
6
|
+
CustomerVerificationProfiles,
|
|
7
|
+
Customers,
|
|
8
|
+
manifestToVerifyInput
|
|
9
|
+
} from "./chunk-OQG3PEF3.mjs";
|
|
10
|
+
import {
|
|
11
|
+
Lending,
|
|
12
|
+
LendingAccessLists,
|
|
13
|
+
LendingAssetData,
|
|
14
|
+
LendingAuctions,
|
|
15
|
+
LendingBots,
|
|
16
|
+
LendingDocuments,
|
|
17
|
+
LendingFees,
|
|
18
|
+
LendingHooks,
|
|
19
|
+
LendingInsurance,
|
|
20
|
+
LendingLiquidations,
|
|
21
|
+
LendingLiquidity,
|
|
22
|
+
LendingLoans,
|
|
23
|
+
LendingMarkets,
|
|
24
|
+
LendingOperators,
|
|
25
|
+
LendingOracle,
|
|
26
|
+
LendingPledges,
|
|
27
|
+
LendingRates,
|
|
28
|
+
LendingTerms,
|
|
29
|
+
LendingVaults
|
|
30
|
+
} from "./chunk-4MNJZAXE.mjs";
|
|
31
|
+
import {
|
|
32
|
+
Tokens
|
|
33
|
+
} from "./chunk-C7CANC53.mjs";
|
|
34
|
+
import {
|
|
35
|
+
LendingContext
|
|
36
|
+
} from "./chunk-DQCX2GYT.mjs";
|
|
37
|
+
import {
|
|
38
|
+
AuthError,
|
|
39
|
+
ConfirmationError,
|
|
40
|
+
NetworkError,
|
|
41
|
+
TrussetError,
|
|
42
|
+
ValidationError
|
|
43
|
+
} from "./chunk-3YF42MHV.mjs";
|
|
44
|
+
|
|
45
|
+
// src/version.ts
|
|
46
|
+
var SDK_VERSION = "0.5.0";
|
|
33
47
|
|
|
34
48
|
// src/utils/http.ts
|
|
35
49
|
var RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 429, 500, 502, 503, 504]);
|
|
50
|
+
var CONFIRM_RETRYABLE_STATUS = /* @__PURE__ */ new Set([408, 429, 502, 503, 504]);
|
|
51
|
+
var MAX_RETRY_AFTER_MS = 6e4;
|
|
52
|
+
var NOT_FOUND_DELAYS_MS = [3e3, 6e3, 12e3];
|
|
53
|
+
var EMPTY_METADATA = { requestId: "", timestamp: "" };
|
|
36
54
|
var silentLogger = {
|
|
37
55
|
debug() {
|
|
38
56
|
},
|
|
@@ -50,24 +68,40 @@ var HttpClient = class {
|
|
|
50
68
|
apiKey: config.apiKey,
|
|
51
69
|
timeoutMs: config.timeoutMs ?? 3e4,
|
|
52
70
|
maxRetries: config.maxRetries ?? 3,
|
|
53
|
-
logger: config.logger ?? silentLogger
|
|
71
|
+
logger: config.logger ?? silentLogger,
|
|
72
|
+
fetch: config.fetch ?? ((input, init) => fetch(input, init))
|
|
54
73
|
};
|
|
55
74
|
}
|
|
56
|
-
async get(path, params) {
|
|
57
|
-
const
|
|
58
|
-
return
|
|
75
|
+
async get(path, params, options) {
|
|
76
|
+
const { data } = await this.requestEnvelope("GET", this.buildUrl(path, params), void 0, options);
|
|
77
|
+
return data;
|
|
78
|
+
}
|
|
79
|
+
/** GET that also returns the response metadata (used for list totals). */
|
|
80
|
+
async getWithMeta(path, params) {
|
|
81
|
+
return this.requestEnvelope("GET", this.buildUrl(path, params));
|
|
82
|
+
}
|
|
83
|
+
async getBinary(path, params, options) {
|
|
84
|
+
return this.requestBinary(this.buildUrl(path, params), options);
|
|
85
|
+
}
|
|
86
|
+
async post(path, body, options) {
|
|
87
|
+
const { data } = await this.requestEnvelope("POST", this.buildUrl(path), body, options);
|
|
88
|
+
return data;
|
|
89
|
+
}
|
|
90
|
+
async put(path, body, options) {
|
|
91
|
+
const { data } = await this.requestEnvelope("PUT", this.buildUrl(path), body, options);
|
|
92
|
+
return data;
|
|
59
93
|
}
|
|
60
|
-
async
|
|
61
|
-
const
|
|
62
|
-
return
|
|
94
|
+
async patch(path, body, options) {
|
|
95
|
+
const { data } = await this.requestEnvelope("PATCH", this.buildUrl(path), body, options);
|
|
96
|
+
return data;
|
|
63
97
|
}
|
|
64
|
-
async
|
|
65
|
-
const
|
|
66
|
-
return
|
|
98
|
+
async delete(path, options) {
|
|
99
|
+
const { data } = await this.requestEnvelope("DELETE", this.buildUrl(path), void 0, options);
|
|
100
|
+
return data;
|
|
67
101
|
}
|
|
68
|
-
async
|
|
69
|
-
const
|
|
70
|
-
return
|
|
102
|
+
async postForm(path, form, params, options) {
|
|
103
|
+
const { data } = await this.requestEnvelope("POST", this.buildUrl(path, params), form, options);
|
|
104
|
+
return data;
|
|
71
105
|
}
|
|
72
106
|
// -----------------------------------------------------------------------
|
|
73
107
|
buildUrl(path, params) {
|
|
@@ -83,469 +117,175 @@ var HttpClient = class {
|
|
|
83
117
|
}
|
|
84
118
|
return url.toString();
|
|
85
119
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
120
|
+
retryClass(method, options) {
|
|
121
|
+
if (options?.retry) return options.retry;
|
|
122
|
+
return method === "GET" ? "read" : "write";
|
|
123
|
+
}
|
|
124
|
+
// Writes are never blindly retried: identity writes are two-phase
|
|
125
|
+
// (prepare calldata, then confirm a mined transaction), and retrying a
|
|
126
|
+
// confirm after an ambiguous failure (5xx, timeout, dropped connection)
|
|
127
|
+
// could record the same transaction twice. 429 is the exception - the
|
|
128
|
+
// server rejected the request before doing any work.
|
|
129
|
+
canRetry(kind, status) {
|
|
130
|
+
if (kind === "read") return status === void 0 || RETRYABLE_STATUS.has(status);
|
|
131
|
+
if (kind === "confirm") return status === void 0 || CONFIRM_RETRYABLE_STATUS.has(status);
|
|
132
|
+
return status === 429;
|
|
133
|
+
}
|
|
134
|
+
headers(jsonBody, accept) {
|
|
135
|
+
const headers = {
|
|
136
|
+
"x-api-key": this.config.apiKey,
|
|
137
|
+
"Accept": accept,
|
|
138
|
+
"User-Agent": `@trusset/sdk/${SDK_VERSION}`
|
|
139
|
+
};
|
|
140
|
+
if (jsonBody) headers["Content-Type"] = "application/json";
|
|
141
|
+
return headers;
|
|
142
|
+
}
|
|
143
|
+
async send(method, url, body, timeoutMs, accept) {
|
|
144
|
+
const controller = new AbortController();
|
|
145
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
146
|
+
const form = typeof FormData !== "undefined" && body instanceof FormData;
|
|
147
|
+
try {
|
|
148
|
+
return await this.config.fetch(url, {
|
|
149
|
+
method,
|
|
150
|
+
headers: this.headers(body !== void 0 && !form, accept),
|
|
151
|
+
body: body === void 0 ? void 0 : form ? body : JSON.stringify(body),
|
|
152
|
+
signal: controller.signal
|
|
153
|
+
});
|
|
154
|
+
} finally {
|
|
155
|
+
clearTimeout(timer);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
async requestEnvelope(method, url, body, options) {
|
|
159
|
+
const kind = this.retryClass(method, options);
|
|
160
|
+
const timeoutMs = Math.max(this.config.timeoutMs, options?.timeoutMs ?? 0);
|
|
161
|
+
return this.withRetries(kind, url, timeoutMs, async () => {
|
|
162
|
+
const res = await this.send(method, url, body, timeoutMs, "application/json");
|
|
163
|
+
await this.throwIfFailed(res);
|
|
164
|
+
let json;
|
|
165
|
+
try {
|
|
166
|
+
json = await res.json();
|
|
167
|
+
} catch {
|
|
168
|
+
throw new TrussetError(
|
|
169
|
+
`Server returned a non-JSON response (status ${res.status})`,
|
|
170
|
+
"BAD_RESPONSE",
|
|
171
|
+
res.status
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
if (!json.success) {
|
|
175
|
+
throw toApiError(json, res, `Request failed with status ${res.status}`);
|
|
176
|
+
}
|
|
177
|
+
return { data: json.data, metadata: json.metadata ?? EMPTY_METADATA };
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
async requestBinary(url, options) {
|
|
181
|
+
const timeoutMs = Math.max(this.config.timeoutMs, options?.timeoutMs ?? 0);
|
|
182
|
+
return this.withRetries(this.retryClass("GET", options), url, timeoutMs, async () => {
|
|
183
|
+
const res = await this.send("GET", url, void 0, timeoutMs, "*/*");
|
|
184
|
+
await this.throwIfFailed(res);
|
|
185
|
+
const contentType = res.headers.get("content-type") ?? "application/octet-stream";
|
|
186
|
+
if (contentType.includes("application/json")) {
|
|
187
|
+
const json = await res.json().catch(() => null);
|
|
188
|
+
throw toApiError(json, res, `Expected a document but the server answered with JSON (status ${res.status})`);
|
|
189
|
+
}
|
|
190
|
+
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
191
|
+
return { bytes, contentType, filename: filenameOf(res.headers.get("content-disposition")) };
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
async throwIfFailed(res) {
|
|
195
|
+
if (res.status === 401) {
|
|
196
|
+
const json = await res.json().catch(() => ({}));
|
|
197
|
+
throw new AuthError(
|
|
198
|
+
json.error?.message ?? "Authentication failed",
|
|
199
|
+
json.metadata?.requestId
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
if (res.status >= 400) {
|
|
203
|
+
const json = await res.json().catch(() => null);
|
|
204
|
+
throw toApiError(json, res, `Server returned ${res.status}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
async withRetries(kind, url, timeoutMs, attempt) {
|
|
208
|
+
let retries = 0;
|
|
209
|
+
let notFoundRetries = 0;
|
|
210
|
+
let delay = 0;
|
|
211
|
+
while (true) {
|
|
212
|
+
if (delay > 0) {
|
|
213
|
+
this.config.logger.debug(`Retry after ${delay}ms`, { url });
|
|
92
214
|
await sleep(delay);
|
|
93
215
|
}
|
|
94
216
|
try {
|
|
95
|
-
|
|
96
|
-
const timer = setTimeout(() => controller.abort(), this.config.timeoutMs);
|
|
97
|
-
const headers = {
|
|
98
|
-
"x-api-key": this.config.apiKey,
|
|
99
|
-
"Accept": "application/json",
|
|
100
|
-
"User-Agent": "@trusset/sdk/0.1.0"
|
|
101
|
-
};
|
|
102
|
-
if (body !== void 0) {
|
|
103
|
-
headers["Content-Type"] = "application/json";
|
|
104
|
-
}
|
|
105
|
-
const res = await fetch(url, {
|
|
106
|
-
method,
|
|
107
|
-
headers,
|
|
108
|
-
body: body !== void 0 ? JSON.stringify(body) : void 0,
|
|
109
|
-
signal: controller.signal
|
|
110
|
-
});
|
|
111
|
-
clearTimeout(timer);
|
|
112
|
-
if (res.status === 401) {
|
|
113
|
-
const json2 = await res.json().catch(() => ({}));
|
|
114
|
-
throw new AuthError(
|
|
115
|
-
json2.error?.message ?? "Authentication failed",
|
|
116
|
-
json2.metadata?.requestId
|
|
117
|
-
);
|
|
118
|
-
}
|
|
119
|
-
if (RETRYABLE_STATUS.has(res.status)) {
|
|
120
|
-
lastError = new TrussetError(
|
|
121
|
-
`Server returned ${res.status}`,
|
|
122
|
-
"SERVER_ERROR",
|
|
123
|
-
res.status
|
|
124
|
-
);
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
const json = await res.json();
|
|
128
|
-
if (!json.success) {
|
|
129
|
-
throw new TrussetError(
|
|
130
|
-
json.error?.message ?? `Request failed with status ${res.status}`,
|
|
131
|
-
json.error?.code ?? "REQUEST_FAILED",
|
|
132
|
-
res.status,
|
|
133
|
-
json.metadata?.requestId
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
return json.data;
|
|
217
|
+
return await attempt();
|
|
137
218
|
} catch (err) {
|
|
138
|
-
if (err instanceof
|
|
139
|
-
|
|
140
|
-
|
|
219
|
+
if (err instanceof AuthError) throw err;
|
|
220
|
+
if (err instanceof TrussetError && err.statusCode > 0) {
|
|
221
|
+
if (kind !== "read" && err.code === "TX_NOT_FOUND") {
|
|
222
|
+
if (notFoundRetries >= NOT_FOUND_DELAYS_MS.length) throw err;
|
|
223
|
+
delay = NOT_FOUND_DELAYS_MS[notFoundRetries];
|
|
224
|
+
notFoundRetries += 1;
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
if (!this.canRetry(kind, err.statusCode) || retries >= this.config.maxRetries) throw err;
|
|
228
|
+
retries += 1;
|
|
229
|
+
delay = err.statusCode === 429 && err.retryAfter !== null ? Math.min(err.retryAfter * 1e3, MAX_RETRY_AFTER_MS) : backoff(retries);
|
|
141
230
|
continue;
|
|
142
231
|
}
|
|
143
232
|
if (err instanceof TypeError || err instanceof DOMException && err.name === "AbortError") {
|
|
144
|
-
|
|
145
|
-
err instanceof DOMException ?
|
|
233
|
+
const netError = new NetworkError(
|
|
234
|
+
err instanceof DOMException ? `Request timed out after ${timeoutMs}ms` : `Network error: ${err.message}`
|
|
146
235
|
);
|
|
236
|
+
if (!this.canRetry(kind) || retries >= this.config.maxRetries) throw netError;
|
|
237
|
+
retries += 1;
|
|
238
|
+
delay = backoff(retries);
|
|
147
239
|
continue;
|
|
148
240
|
}
|
|
149
241
|
throw err;
|
|
150
242
|
}
|
|
151
243
|
}
|
|
152
|
-
throw lastError ?? new NetworkError("Request failed after retries");
|
|
153
244
|
}
|
|
154
245
|
};
|
|
155
|
-
function
|
|
156
|
-
|
|
246
|
+
function toApiError(json, res, fallback) {
|
|
247
|
+
const error = json?.error ?? {};
|
|
248
|
+
const { code, message, details, ...rest } = error;
|
|
249
|
+
const metadata = json?.metadata ?? {};
|
|
250
|
+
return new TrussetError(
|
|
251
|
+
typeof message === "string" ? message : fallback,
|
|
252
|
+
typeof code === "string" ? code : res.status >= 500 ? "SERVER_ERROR" : "REQUEST_FAILED",
|
|
253
|
+
res.status,
|
|
254
|
+
typeof metadata.requestId === "string" ? metadata.requestId : void 0,
|
|
255
|
+
{
|
|
256
|
+
details,
|
|
257
|
+
data: Object.keys(rest).length ? rest : null,
|
|
258
|
+
retryAfter: res.status === 429 ? retryAfterOf(res, metadata) : null
|
|
259
|
+
}
|
|
260
|
+
);
|
|
157
261
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
throw new ValidationError(`${field} is required and must be a non-empty string`);
|
|
164
|
-
}
|
|
165
|
-
return value.trim();
|
|
262
|
+
function retryAfterOf(res, metadata) {
|
|
263
|
+
const header = res.headers.get("retry-after");
|
|
264
|
+
if (header !== null && /^\d+$/.test(header.trim())) return Number(header.trim());
|
|
265
|
+
if (typeof metadata.retryAfter === "number" && Number.isFinite(metadata.retryAfter)) return Math.max(0, metadata.retryAfter);
|
|
266
|
+
return null;
|
|
166
267
|
}
|
|
167
|
-
function
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
return s;
|
|
268
|
+
function filenameOf(disposition) {
|
|
269
|
+
if (!disposition) return null;
|
|
270
|
+
const match = /filename="?([^";]+)"?/i.exec(disposition);
|
|
271
|
+
return match ? match[1] : null;
|
|
173
272
|
}
|
|
174
|
-
function
|
|
175
|
-
|
|
176
|
-
return requireAddress(value, field);
|
|
273
|
+
function backoff(attempt) {
|
|
274
|
+
return Math.min(1e3 * Math.pow(2, attempt - 1), 1e4);
|
|
177
275
|
}
|
|
178
|
-
function
|
|
179
|
-
|
|
180
|
-
if (s.length < 2 || s.length > 3) {
|
|
181
|
-
throw new ValidationError(`${field} must be a 2-3 letter country code`);
|
|
182
|
-
}
|
|
183
|
-
return s.toUpperCase();
|
|
184
|
-
}
|
|
185
|
-
function clampInt(value, min, max, fallback) {
|
|
186
|
-
if (value === void 0 || value === null) return fallback;
|
|
187
|
-
const n = Math.floor(value);
|
|
188
|
-
if (isNaN(n)) return fallback;
|
|
189
|
-
return Math.max(min, Math.min(max, n));
|
|
276
|
+
function sleep(ms) {
|
|
277
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
190
278
|
}
|
|
191
279
|
|
|
192
|
-
// src/modules/customers/management.ts
|
|
193
|
-
var CustomerManagement = class {
|
|
194
|
-
constructor(http) {
|
|
195
|
-
this.http = http;
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* List customers for the current instance.
|
|
199
|
-
*/
|
|
200
|
-
async list(options = {}) {
|
|
201
|
-
const params = {
|
|
202
|
-
limit: clampInt(options.limit, 1, 200, 100),
|
|
203
|
-
offset: clampInt(options.offset, 0, Number.MAX_SAFE_INTEGER, 0),
|
|
204
|
-
includeArchived: options.includeArchived ? "true" : void 0,
|
|
205
|
-
search: options.search || void 0
|
|
206
|
-
};
|
|
207
|
-
return this.http.get("/customers/api/manage", params);
|
|
208
|
-
}
|
|
209
|
-
/**
|
|
210
|
-
* Get a customer by wallet address, ID, externalId, or referenceKey.
|
|
211
|
-
*/
|
|
212
|
-
async get(identifier) {
|
|
213
|
-
requireString(identifier, "identifier");
|
|
214
|
-
return this.http.get(`/customers/api/manage/${encodeURIComponent(identifier)}`);
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* Create a new customer record.
|
|
218
|
-
*/
|
|
219
|
-
async create(input) {
|
|
220
|
-
if (input.walletAddress) optionalAddress(input.walletAddress, "walletAddress");
|
|
221
|
-
return this.http.post("/customers/api/manage", input);
|
|
222
|
-
}
|
|
223
|
-
/**
|
|
224
|
-
* Update an existing customer.
|
|
225
|
-
*/
|
|
226
|
-
async update(customerId, input) {
|
|
227
|
-
requireString(customerId, "customerId");
|
|
228
|
-
return this.http.put(`/customers/api/manage/${encodeURIComponent(customerId)}`, input);
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* Archive (soft-delete) a customer.
|
|
232
|
-
*/
|
|
233
|
-
async archive(customerId) {
|
|
234
|
-
requireString(customerId, "customerId");
|
|
235
|
-
return this.http.delete(`/customers/api/manage/${encodeURIComponent(customerId)}`);
|
|
236
|
-
}
|
|
237
|
-
/**
|
|
238
|
-
* Link a wallet address to a customer created via referenceKey.
|
|
239
|
-
*/
|
|
240
|
-
async linkWallet(input) {
|
|
241
|
-
requireString(input.referenceKey, "referenceKey");
|
|
242
|
-
requireAddress(input.walletAddress, "walletAddress");
|
|
243
|
-
return this.http.post("/customers/api/manage/link-wallet", input);
|
|
244
|
-
}
|
|
245
|
-
/**
|
|
246
|
-
* Batch-fetch country codes for a list of wallet addresses.
|
|
247
|
-
*/
|
|
248
|
-
async batchGetCountries(addresses) {
|
|
249
|
-
if (!Array.isArray(addresses) || addresses.length === 0) {
|
|
250
|
-
return {};
|
|
251
|
-
}
|
|
252
|
-
return this.http.post("/customers/api/manage/countries/batch", { addresses });
|
|
253
|
-
}
|
|
254
|
-
// -----------------------------------------------------------------------
|
|
255
|
-
// Convenience search helpers
|
|
256
|
-
// -----------------------------------------------------------------------
|
|
257
|
-
/**
|
|
258
|
-
* Search customers by name substring.
|
|
259
|
-
*/
|
|
260
|
-
async searchByName(name, options) {
|
|
261
|
-
return this.list({ ...options, search: name });
|
|
262
|
-
}
|
|
263
|
-
/**
|
|
264
|
-
* Find a customer by wallet address.
|
|
265
|
-
*/
|
|
266
|
-
async searchByWallet(walletAddress) {
|
|
267
|
-
requireAddress(walletAddress, "walletAddress");
|
|
268
|
-
return this.get(walletAddress);
|
|
269
|
-
}
|
|
270
|
-
/**
|
|
271
|
-
* Find a customer by external ID (your system's unique identifier).
|
|
272
|
-
*/
|
|
273
|
-
async searchByExternalId(externalId) {
|
|
274
|
-
requireString(externalId, "externalId");
|
|
275
|
-
return this.get(externalId);
|
|
276
|
-
}
|
|
277
|
-
/**
|
|
278
|
-
* Find a customer by reference key (for pre-wallet customers).
|
|
279
|
-
*/
|
|
280
|
-
async searchByReference(referenceKey) {
|
|
281
|
-
requireString(referenceKey, "referenceKey");
|
|
282
|
-
return this.get(referenceKey);
|
|
283
|
-
}
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
// src/modules/customers/identity.ts
|
|
287
|
-
var CustomerIdentity = class {
|
|
288
|
-
constructor(http) {
|
|
289
|
-
this.http = http;
|
|
290
|
-
}
|
|
291
|
-
/**
|
|
292
|
-
* Verify a customer's identity on-chain via the IdentityRegistry contract.
|
|
293
|
-
* Requires a configured relayer on the instance.
|
|
294
|
-
*/
|
|
295
|
-
async verify(input) {
|
|
296
|
-
requireAddress(input.walletAddress, "walletAddress");
|
|
297
|
-
requireCountry(input.country, "country");
|
|
298
|
-
return this.http.post("/customers/api/identity/verify", input);
|
|
299
|
-
}
|
|
300
|
-
/**
|
|
301
|
-
* Batch-verify up to 500 identities in a single transaction.
|
|
302
|
-
* Requires a configured relayer.
|
|
303
|
-
*/
|
|
304
|
-
async batchVerify(entries) {
|
|
305
|
-
if (!Array.isArray(entries) || entries.length === 0 || entries.length > 500) {
|
|
306
|
-
throw new ValidationError("Provide 1-500 entries for batch verification");
|
|
307
|
-
}
|
|
308
|
-
for (const e of entries) {
|
|
309
|
-
requireAddress(e.walletAddress, "walletAddress");
|
|
310
|
-
requireCountry(e.country, "country");
|
|
311
|
-
}
|
|
312
|
-
return this.http.post("/customers/api/identity/batch-verify", { entries });
|
|
313
|
-
}
|
|
314
|
-
/**
|
|
315
|
-
* Revoke an identity on-chain. Requires a configured relayer.
|
|
316
|
-
*/
|
|
317
|
-
async revoke(walletAddress) {
|
|
318
|
-
requireAddress(walletAddress, "walletAddress");
|
|
319
|
-
return this.http.post("/customers/api/identity/revoke", { walletAddress });
|
|
320
|
-
}
|
|
321
|
-
/**
|
|
322
|
-
* Get combined on-chain and off-chain identity status.
|
|
323
|
-
*/
|
|
324
|
-
async getStatus(walletAddress) {
|
|
325
|
-
requireAddress(walletAddress, "walletAddress");
|
|
326
|
-
return this.http.get(`/customers/api/identity/status/${walletAddress}`);
|
|
327
|
-
}
|
|
328
|
-
/**
|
|
329
|
-
* Check whether a token transfer between two addresses is compliant.
|
|
330
|
-
* Runs identity checks + all registered compliance modules.
|
|
331
|
-
*/
|
|
332
|
-
async canTransfer(input) {
|
|
333
|
-
requireAddress(input.from, "from");
|
|
334
|
-
requireAddress(input.to, "to");
|
|
335
|
-
return this.http.post("/customers/api/identity/can-transfer", input);
|
|
336
|
-
}
|
|
337
|
-
};
|
|
338
|
-
|
|
339
|
-
// src/modules/customers/id-links.ts
|
|
340
|
-
var CustomerIdLinks = class {
|
|
341
|
-
constructor(http) {
|
|
342
|
-
this.http = http;
|
|
343
|
-
}
|
|
344
|
-
/**
|
|
345
|
-
* Create a KYC verification link to send to an investor.
|
|
346
|
-
*/
|
|
347
|
-
async create(input) {
|
|
348
|
-
if (!input.recipientEmail && !input.recipientWallet && !input.recipientPhone) {
|
|
349
|
-
throw new ValidationError("At least one of recipientEmail, recipientWallet, or recipientPhone is required");
|
|
350
|
-
}
|
|
351
|
-
return this.http.post("/customers/api/id-links", input);
|
|
352
|
-
}
|
|
353
|
-
/**
|
|
354
|
-
* List all verification links for the current instance.
|
|
355
|
-
*/
|
|
356
|
-
async list(options = {}) {
|
|
357
|
-
const params = {
|
|
358
|
-
limit: clampInt(options.limit, 1, 200, 50),
|
|
359
|
-
offset: clampInt(options.offset, 0, Number.MAX_SAFE_INTEGER, 0),
|
|
360
|
-
status: options.status
|
|
361
|
-
};
|
|
362
|
-
return this.http.get("/customers/api/id-links", params);
|
|
363
|
-
}
|
|
364
|
-
/**
|
|
365
|
-
* Revoke (expire) a verification link so it can no longer be used.
|
|
366
|
-
*/
|
|
367
|
-
async revoke(linkId) {
|
|
368
|
-
requireString(linkId, "linkId");
|
|
369
|
-
await this.http.put(`/customers/api/id-links/${encodeURIComponent(linkId)}/revoke`);
|
|
370
|
-
}
|
|
371
|
-
/**
|
|
372
|
-
* Resend a verification link via the specified channels.
|
|
373
|
-
*/
|
|
374
|
-
async resend(linkId, channels) {
|
|
375
|
-
requireString(linkId, "linkId");
|
|
376
|
-
if (!Array.isArray(channels) || channels.length === 0) {
|
|
377
|
-
throw new ValidationError("channels array is required (email and/or sms)");
|
|
378
|
-
}
|
|
379
|
-
return this.http.post(`/customers/api/id-links/${encodeURIComponent(linkId)}/resend`, { channels });
|
|
380
|
-
}
|
|
381
|
-
};
|
|
382
|
-
|
|
383
|
-
// src/modules/customers/sync.ts
|
|
384
|
-
var DEFAULT_BATCH_SIZE = 50;
|
|
385
|
-
var MAX_BATCH_SIZE = 200;
|
|
386
|
-
var MAX_VERIFY_BATCH = 500;
|
|
387
|
-
var CustomerSync = class {
|
|
388
|
-
constructor(http) {
|
|
389
|
-
this.http = http;
|
|
390
|
-
}
|
|
391
|
-
/**
|
|
392
|
-
* Import customer records from an external system.
|
|
393
|
-
*
|
|
394
|
-
* Each record is mapped by `externalId`. If a customer with that externalId
|
|
395
|
-
* already exists, the `onConflict` strategy determines whether to skip or
|
|
396
|
-
* update.
|
|
397
|
-
*
|
|
398
|
-
* When `verifyOnChain` is true, records that include a `walletAddress` and
|
|
399
|
-
* `country` will be batch-verified on-chain after the database import.
|
|
400
|
-
*/
|
|
401
|
-
async importRecords(records, options = {}) {
|
|
402
|
-
if (!Array.isArray(records) || records.length === 0) {
|
|
403
|
-
throw new ValidationError("records must be a non-empty array");
|
|
404
|
-
}
|
|
405
|
-
const batchSize = clampInt(options.batchSize, 1, MAX_BATCH_SIZE, DEFAULT_BATCH_SIZE);
|
|
406
|
-
const onConflict = options.onConflict ?? "update";
|
|
407
|
-
const result = { total: records.length, created: 0, updated: 0, skipped: 0, errors: [] };
|
|
408
|
-
const verifyQueue = [];
|
|
409
|
-
for (let i = 0; i < records.length; i += batchSize) {
|
|
410
|
-
const batch = records.slice(i, i + batchSize);
|
|
411
|
-
const batchResults = await Promise.allSettled(
|
|
412
|
-
batch.map((record) => this.processRecord(record, onConflict))
|
|
413
|
-
);
|
|
414
|
-
for (let j = 0; j < batchResults.length; j++) {
|
|
415
|
-
const settled = batchResults[j];
|
|
416
|
-
const record = batch[j];
|
|
417
|
-
if (settled.status === "fulfilled") {
|
|
418
|
-
const outcome = settled.value;
|
|
419
|
-
if (outcome.action === "created") result.created++;
|
|
420
|
-
else if (outcome.action === "updated") result.updated++;
|
|
421
|
-
else result.skipped++;
|
|
422
|
-
if (options.verifyOnChain && record.walletAddress && record.country && outcome.action !== "skipped") {
|
|
423
|
-
verifyQueue.push({
|
|
424
|
-
walletAddress: record.walletAddress,
|
|
425
|
-
country: record.country,
|
|
426
|
-
investorType: record.investorType,
|
|
427
|
-
softExpiryDays: options.verificationDefaults?.softExpiryDays,
|
|
428
|
-
hardExpiryDays: options.verificationDefaults?.hardExpiryDays
|
|
429
|
-
});
|
|
430
|
-
}
|
|
431
|
-
} else {
|
|
432
|
-
const err = settled.reason;
|
|
433
|
-
result.errors.push({
|
|
434
|
-
externalId: record.externalId,
|
|
435
|
-
code: err?.code ?? "UNKNOWN",
|
|
436
|
-
message: err?.message ?? "Unknown error"
|
|
437
|
-
});
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
options.onProgress?.(Math.min(i + batchSize, records.length), records.length);
|
|
441
|
-
}
|
|
442
|
-
if (verifyQueue.length > 0) {
|
|
443
|
-
await this.runBatchVerification(verifyQueue, result);
|
|
444
|
-
}
|
|
445
|
-
return result;
|
|
446
|
-
}
|
|
447
|
-
/**
|
|
448
|
-
* Export all customers from Trusset as flat records.
|
|
449
|
-
* Useful for syncing data back into your system.
|
|
450
|
-
*/
|
|
451
|
-
async exportRecords(options = {}) {
|
|
452
|
-
const allRecords = [];
|
|
453
|
-
let offset = 0;
|
|
454
|
-
const limit = 200;
|
|
455
|
-
while (true) {
|
|
456
|
-
const page = await this.http.get("/customers/api/manage", {
|
|
457
|
-
limit,
|
|
458
|
-
offset,
|
|
459
|
-
includeArchived: options.includeArchived ? "true" : void 0
|
|
460
|
-
});
|
|
461
|
-
if (!page.customers || page.customers.length === 0) break;
|
|
462
|
-
for (const c of page.customers) {
|
|
463
|
-
allRecords.push({
|
|
464
|
-
externalId: c.externalId ?? c.id,
|
|
465
|
-
walletAddress: c.walletAddress ?? void 0,
|
|
466
|
-
name: c.name ?? void 0,
|
|
467
|
-
country: c.country ?? void 0,
|
|
468
|
-
investorType: c.investorType ?? void 0,
|
|
469
|
-
metadata: c.metadata ?? void 0
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
offset += page.customers.length;
|
|
473
|
-
if (offset >= page.total) break;
|
|
474
|
-
}
|
|
475
|
-
return allRecords;
|
|
476
|
-
}
|
|
477
|
-
// -----------------------------------------------------------------------
|
|
478
|
-
// Internal
|
|
479
|
-
// -----------------------------------------------------------------------
|
|
480
|
-
async processRecord(record, onConflict) {
|
|
481
|
-
requireString(record.externalId, "externalId");
|
|
482
|
-
let existing = null;
|
|
483
|
-
try {
|
|
484
|
-
existing = await this.http.get(
|
|
485
|
-
`/customers/api/manage/${encodeURIComponent(record.externalId)}`
|
|
486
|
-
);
|
|
487
|
-
} catch (err) {
|
|
488
|
-
const code = err?.code;
|
|
489
|
-
const statusCode = err?.statusCode;
|
|
490
|
-
if (statusCode !== 404 && code !== "NOT_FOUND") throw err;
|
|
491
|
-
}
|
|
492
|
-
if (existing) {
|
|
493
|
-
if (onConflict === "skip") {
|
|
494
|
-
return { action: "skipped" };
|
|
495
|
-
}
|
|
496
|
-
await this.http.put(`/customers/api/manage/${encodeURIComponent(existing.id)}`, {
|
|
497
|
-
name: record.name,
|
|
498
|
-
country: record.country,
|
|
499
|
-
investorType: record.investorType,
|
|
500
|
-
walletAddress: record.walletAddress,
|
|
501
|
-
metadata: record.metadata
|
|
502
|
-
});
|
|
503
|
-
return { action: "updated" };
|
|
504
|
-
}
|
|
505
|
-
await this.http.post("/customers/api/manage", {
|
|
506
|
-
externalId: record.externalId,
|
|
507
|
-
walletAddress: record.walletAddress,
|
|
508
|
-
name: record.name,
|
|
509
|
-
country: record.country,
|
|
510
|
-
investorType: record.investorType,
|
|
511
|
-
metadata: record.metadata
|
|
512
|
-
});
|
|
513
|
-
return { action: "created" };
|
|
514
|
-
}
|
|
515
|
-
async runBatchVerification(queue, result) {
|
|
516
|
-
for (let i = 0; i < queue.length; i += MAX_VERIFY_BATCH) {
|
|
517
|
-
const batch = queue.slice(i, i + MAX_VERIFY_BATCH);
|
|
518
|
-
try {
|
|
519
|
-
await this.http.post("/customers/api/identity/batch-verify", { entries: batch });
|
|
520
|
-
} catch (err) {
|
|
521
|
-
for (const entry of batch) {
|
|
522
|
-
result.errors.push({
|
|
523
|
-
externalId: entry.walletAddress,
|
|
524
|
-
code: "VERIFY_FAILED",
|
|
525
|
-
message: err?.message ?? "On-chain batch verification failed"
|
|
526
|
-
});
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
};
|
|
532
|
-
|
|
533
|
-
// src/modules/customers/index.ts
|
|
534
|
-
var Customers = class {
|
|
535
|
-
constructor(http) {
|
|
536
|
-
this.manage = new CustomerManagement(http);
|
|
537
|
-
this.identity = new CustomerIdentity(http);
|
|
538
|
-
this.idLinks = new CustomerIdLinks(http);
|
|
539
|
-
this.sync = new CustomerSync(http);
|
|
540
|
-
}
|
|
541
|
-
};
|
|
542
|
-
|
|
543
280
|
// src/client.ts
|
|
544
281
|
var TrussetClient = class {
|
|
545
282
|
constructor(config) {
|
|
546
283
|
if (!config.apiKey || typeof config.apiKey !== "string" || config.apiKey.length < 20) {
|
|
547
284
|
throw new ValidationError("A valid apiKey is required");
|
|
548
285
|
}
|
|
286
|
+
if (!config.apiKey.startsWith("trusset_")) {
|
|
287
|
+
throw new ValidationError("apiKey must be a Trusset instance API key (they start with 'trusset_')");
|
|
288
|
+
}
|
|
549
289
|
this.http = new HttpClient({
|
|
550
290
|
apiKey: config.apiKey,
|
|
551
291
|
baseUrl: config.baseUrl,
|
|
@@ -553,18 +293,44 @@ var TrussetClient = class {
|
|
|
553
293
|
maxRetries: config.maxRetries,
|
|
554
294
|
logger: config.logger
|
|
555
295
|
});
|
|
556
|
-
|
|
296
|
+
const context = new LendingContext(this.http);
|
|
297
|
+
this.customers = new Customers(this.http, context);
|
|
298
|
+
this.lending = new Lending(this.http, context);
|
|
299
|
+
this.tokens = new Tokens(context);
|
|
557
300
|
}
|
|
558
301
|
};
|
|
559
302
|
export {
|
|
560
303
|
AuthError,
|
|
304
|
+
ConfirmationError,
|
|
561
305
|
CustomerIdLinks,
|
|
562
306
|
CustomerIdentity,
|
|
563
307
|
CustomerManagement,
|
|
564
308
|
CustomerSync,
|
|
309
|
+
CustomerVerificationProfiles,
|
|
565
310
|
Customers,
|
|
311
|
+
Lending,
|
|
312
|
+
LendingAccessLists,
|
|
313
|
+
LendingAssetData,
|
|
314
|
+
LendingAuctions,
|
|
315
|
+
LendingBots,
|
|
316
|
+
LendingDocuments,
|
|
317
|
+
LendingFees,
|
|
318
|
+
LendingHooks,
|
|
319
|
+
LendingInsurance,
|
|
320
|
+
LendingLiquidations,
|
|
321
|
+
LendingLiquidity,
|
|
322
|
+
LendingLoans,
|
|
323
|
+
LendingMarkets,
|
|
324
|
+
LendingOperators,
|
|
325
|
+
LendingOracle,
|
|
326
|
+
LendingPledges,
|
|
327
|
+
LendingRates,
|
|
328
|
+
LendingTerms,
|
|
329
|
+
LendingVaults,
|
|
566
330
|
NetworkError,
|
|
331
|
+
Tokens,
|
|
567
332
|
TrussetClient,
|
|
568
333
|
TrussetError,
|
|
569
|
-
ValidationError
|
|
334
|
+
ValidationError,
|
|
335
|
+
manifestToVerifyInput
|
|
570
336
|
};
|