@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,916 @@
|
|
|
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; }
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
var _chunkAI4ZNJLRjs = require('./chunk-AI4ZNJLR.js');
|
|
22
|
+
|
|
23
|
+
// src/modules/customers/management.ts
|
|
24
|
+
var CustomerManagement = class {
|
|
25
|
+
constructor(http) {
|
|
26
|
+
this.http = http;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* List customers for the current instance.
|
|
30
|
+
*/
|
|
31
|
+
async list(options = {}) {
|
|
32
|
+
const params = {
|
|
33
|
+
limit: _chunkAI4ZNJLRjs.clampInt.call(void 0, options.limit, 1, 200, 100),
|
|
34
|
+
offset: _chunkAI4ZNJLRjs.clampInt.call(void 0, options.offset, 0, Number.MAX_SAFE_INTEGER, 0),
|
|
35
|
+
includeArchived: options.includeArchived ? "true" : void 0,
|
|
36
|
+
search: options.search || void 0,
|
|
37
|
+
customerType: options.customerType || void 0
|
|
38
|
+
};
|
|
39
|
+
const { data, metadata } = await this.http.getWithMeta("/customers/api/manage", params);
|
|
40
|
+
const customers = Array.isArray(data) ? data : [];
|
|
41
|
+
return { customers, total: _nullishCoalesce(metadata.total, () => ( customers.length)) };
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get a customer by wallet address, ID, externalId, or referenceKey.
|
|
45
|
+
*
|
|
46
|
+
* Note: the backend routes 20-30 character identifiers to the internal-ID
|
|
47
|
+
* lookup, so an externalId in that length range will not resolve here -
|
|
48
|
+
* use `searchByExternalId`, which handles the ambiguity.
|
|
49
|
+
*/
|
|
50
|
+
async get(identifier) {
|
|
51
|
+
_chunkAI4ZNJLRjs.requireString.call(void 0, identifier, "identifier");
|
|
52
|
+
return this.http.get(`/customers/api/manage/${encodeURIComponent(identifier)}`);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Create a new customer record.
|
|
56
|
+
*/
|
|
57
|
+
async create(input) {
|
|
58
|
+
if (input.walletAddress) _chunkAI4ZNJLRjs.optionalAddress.call(void 0, input.walletAddress, "walletAddress");
|
|
59
|
+
return this.http.post("/customers/api/manage", input);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Update an existing customer.
|
|
63
|
+
*/
|
|
64
|
+
async update(customerId, input) {
|
|
65
|
+
_chunkAI4ZNJLRjs.requireString.call(void 0, customerId, "customerId");
|
|
66
|
+
return this.http.put(`/customers/api/manage/${encodeURIComponent(customerId)}`, input);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Archive (soft-delete) a customer.
|
|
70
|
+
*/
|
|
71
|
+
async archive(customerId) {
|
|
72
|
+
_chunkAI4ZNJLRjs.requireString.call(void 0, customerId, "customerId");
|
|
73
|
+
return this.http.delete(`/customers/api/manage/${encodeURIComponent(customerId)}`);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Link a wallet address to a customer created via referenceKey.
|
|
77
|
+
*/
|
|
78
|
+
async linkWallet(input) {
|
|
79
|
+
_chunkAI4ZNJLRjs.requireString.call(void 0, input.referenceKey, "referenceKey");
|
|
80
|
+
_chunkAI4ZNJLRjs.requireAddress.call(void 0, input.walletAddress, "walletAddress");
|
|
81
|
+
return this.http.post("/customers/api/manage/link-wallet", input);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Batch-fetch country codes for a list of wallet addresses.
|
|
85
|
+
*/
|
|
86
|
+
async batchGetCountries(addresses) {
|
|
87
|
+
if (!Array.isArray(addresses) || addresses.length === 0) {
|
|
88
|
+
return {};
|
|
89
|
+
}
|
|
90
|
+
return this.http.post("/customers/api/manage/countries/batch", { addresses });
|
|
91
|
+
}
|
|
92
|
+
// -----------------------------------------------------------------------
|
|
93
|
+
// Linked wallets (secondary wallets on one customer)
|
|
94
|
+
// -----------------------------------------------------------------------
|
|
95
|
+
/**
|
|
96
|
+
* List all wallets of a customer: the primary plus any linked wallets.
|
|
97
|
+
*/
|
|
98
|
+
async listWallets(customerId) {
|
|
99
|
+
_chunkAI4ZNJLRjs.requireString.call(void 0, customerId, "customerId");
|
|
100
|
+
return this.http.get(`/customers/api/manage/${encodeURIComponent(customerId)}/wallets`);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Link an additional wallet to a customer. The wallet must not be in use
|
|
104
|
+
* by any other customer in the instance.
|
|
105
|
+
*/
|
|
106
|
+
async addWallet(customerId, walletAddress, label) {
|
|
107
|
+
_chunkAI4ZNJLRjs.requireString.call(void 0, customerId, "customerId");
|
|
108
|
+
_chunkAI4ZNJLRjs.requireAddress.call(void 0, walletAddress, "walletAddress");
|
|
109
|
+
return this.http.post(`/customers/api/manage/${encodeURIComponent(customerId)}/wallets`, {
|
|
110
|
+
walletAddress,
|
|
111
|
+
label
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Remove a linked (non-primary) wallet from a customer.
|
|
116
|
+
*/
|
|
117
|
+
async removeWallet(customerId, walletAddress) {
|
|
118
|
+
_chunkAI4ZNJLRjs.requireString.call(void 0, customerId, "customerId");
|
|
119
|
+
_chunkAI4ZNJLRjs.requireAddress.call(void 0, walletAddress, "walletAddress");
|
|
120
|
+
return this.http.delete(`/customers/api/manage/${encodeURIComponent(customerId)}/wallets/${walletAddress}`);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Promote a linked wallet to primary. The previous primary becomes a
|
|
124
|
+
* linked wallet labeled "Previously primary".
|
|
125
|
+
*/
|
|
126
|
+
async promoteWallet(customerId, walletAddress) {
|
|
127
|
+
_chunkAI4ZNJLRjs.requireString.call(void 0, customerId, "customerId");
|
|
128
|
+
_chunkAI4ZNJLRjs.requireAddress.call(void 0, walletAddress, "walletAddress");
|
|
129
|
+
return this.http.post(`/customers/api/manage/${encodeURIComponent(customerId)}/wallets/${walletAddress}/promote`);
|
|
130
|
+
}
|
|
131
|
+
// -----------------------------------------------------------------------
|
|
132
|
+
// Convenience search helpers
|
|
133
|
+
// -----------------------------------------------------------------------
|
|
134
|
+
/**
|
|
135
|
+
* Search customers by name substring.
|
|
136
|
+
*/
|
|
137
|
+
async searchByName(name, options) {
|
|
138
|
+
return this.list({ ...options, search: name });
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Find a customer by wallet address.
|
|
142
|
+
*/
|
|
143
|
+
async searchByWallet(walletAddress) {
|
|
144
|
+
_chunkAI4ZNJLRjs.requireAddress.call(void 0, walletAddress, "walletAddress");
|
|
145
|
+
return this.get(walletAddress);
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Find a customer by external ID (your system's unique identifier).
|
|
149
|
+
*/
|
|
150
|
+
async searchByExternalId(externalId) {
|
|
151
|
+
_chunkAI4ZNJLRjs.requireString.call(void 0, externalId, "externalId");
|
|
152
|
+
try {
|
|
153
|
+
return await this.get(externalId);
|
|
154
|
+
} catch (err) {
|
|
155
|
+
if (_optionalChain([err, 'optionalAccess', _ => _.statusCode]) !== 404) throw err;
|
|
156
|
+
const { customers } = await this.list({ search: externalId, limit: 200 });
|
|
157
|
+
const match = customers.find((c) => c.externalId === externalId);
|
|
158
|
+
if (match) return match;
|
|
159
|
+
throw err;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Find a customer by reference key (for pre-wallet customers).
|
|
164
|
+
*/
|
|
165
|
+
async searchByReference(referenceKey) {
|
|
166
|
+
_chunkAI4ZNJLRjs.requireString.call(void 0, referenceKey, "referenceKey");
|
|
167
|
+
return this.get(referenceKey);
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// src/utils/manifest.ts
|
|
172
|
+
function manifestToVerifyInput(manifest, overrides = {}) {
|
|
173
|
+
if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
|
|
174
|
+
throw new (0, _chunkAI4ZNJLRjs.ValidationError)("manifest must be the manifest.json object produced by the KYC proof tool");
|
|
175
|
+
}
|
|
176
|
+
const walletAddress = _chunkAI4ZNJLRjs.requireAddress.call(void 0, _nullishCoalesce(overrides.walletAddress, () => ( manifest.walletAddress)), "manifest.walletAddress");
|
|
177
|
+
const kycHash = _chunkAI4ZNJLRjs.requireHash.call(void 0, manifest.kycHash, "manifest.kycHash");
|
|
178
|
+
const passthrough = _nullishCoalesce(manifest.passthrough, () => ( {}));
|
|
179
|
+
const country = _nullishCoalesce(overrides.country, () => ( passthrough.country));
|
|
180
|
+
if (!country || typeof country !== "string") {
|
|
181
|
+
throw new (0, _chunkAI4ZNJLRjs.ValidationError)(
|
|
182
|
+
"country is required: this manifest has no passthrough.country, pass { country } as an override"
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
walletAddress,
|
|
187
|
+
country,
|
|
188
|
+
kycHash,
|
|
189
|
+
investorType: _nullishCoalesce(overrides.investorType, () => ( passthrough.investorType)),
|
|
190
|
+
softExpiryDays: _nullishCoalesce(overrides.softExpiryDays, () => ( passthrough.softExpiryDays)),
|
|
191
|
+
hardExpiryDays: _nullishCoalesce(overrides.hardExpiryDays, () => ( passthrough.hardExpiryDays))
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// src/utils/register.ts
|
|
196
|
+
var MAX_ROUNDS = 6;
|
|
197
|
+
var HASH_RE = /^0x[0-9a-fA-F]{64}$/;
|
|
198
|
+
function asRecord(value) {
|
|
199
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
200
|
+
throw new (0, _chunkAI4ZNJLRjs.TrussetError)("The API returned no plan the SDK can sign. Nothing was signed", "UNSIGNABLE_PLAN", 0);
|
|
201
|
+
}
|
|
202
|
+
return value;
|
|
203
|
+
}
|
|
204
|
+
function bytesOf(hash) {
|
|
205
|
+
const out = new Uint8Array(32);
|
|
206
|
+
for (let i = 0; i < 32; i++) out[i] = parseInt(hash.slice(2 + i * 2, 4 + i * 2), 16);
|
|
207
|
+
return out;
|
|
208
|
+
}
|
|
209
|
+
function messageOf(error) {
|
|
210
|
+
if (error && typeof error === "object" && typeof error.message === "string") {
|
|
211
|
+
return error.message.split("\n")[0].slice(0, 200);
|
|
212
|
+
}
|
|
213
|
+
return String(error);
|
|
214
|
+
}
|
|
215
|
+
function unsignable(plan) {
|
|
216
|
+
const phase = typeof plan.phase === "string" ? ` (register phase ${plan.phase})` : "";
|
|
217
|
+
return new (0, _chunkAI4ZNJLRjs.TrussetError)(`The prepared write carries no transaction the SDK can sign${phase}, so nothing was signed`, "UNSIGNABLE_PLAN", 0);
|
|
218
|
+
}
|
|
219
|
+
async function executeAfter(chain, plan, options, earlier) {
|
|
220
|
+
try {
|
|
221
|
+
return await _chunkAI4ZNJLRjs.execute.call(void 0, chain, plan, options);
|
|
222
|
+
} catch (err) {
|
|
223
|
+
if (!earlier.some((t) => t.txHash !== null)) throw err;
|
|
224
|
+
if (err instanceof _chunkAI4ZNJLRjs.ConfirmationError) {
|
|
225
|
+
throw new (0, _chunkAI4ZNJLRjs.ConfirmationError)(`${err.message}. Earlier steps of this registration are on chain as well`, err.code === "CONFIRMATION_FAILED" ? "CONFIRMATION_FAILED" : "TRANSACTION_FAILED", [...earlier, ...err.transactions], err.txHash, err.failure);
|
|
226
|
+
}
|
|
227
|
+
throw new (0, _chunkAI4ZNJLRjs.ConfirmationError)(`${messageOf(err)}. Earlier steps of this registration are already on chain; see err.transactions for every hash sent`, "TRANSACTION_FAILED", earlier, null, err);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
async function runRegisterPlan(chain, options, spec) {
|
|
231
|
+
await _chunkAI4ZNJLRjs.preflightChain.call(void 0, chain, options);
|
|
232
|
+
const carry = {
|
|
233
|
+
signerAddress: await _chunkAI4ZNJLRjs.signerAddress.call(void 0, _chunkAI4ZNJLRjs.requireSigner.call(void 0, options.signer)),
|
|
234
|
+
claimSignatures: [..._nullishCoalesce(_optionalChain([spec, 'access', _2 => _2.seed, 'optionalAccess', _3 => _3.claimSignatures]), () => ( []))],
|
|
235
|
+
identities: { ..._nullishCoalesce(_optionalChain([spec, 'access', _4 => _4.seed, 'optionalAccess', _5 => _5.identities]), () => ( {})) }
|
|
236
|
+
};
|
|
237
|
+
const transactions = [];
|
|
238
|
+
const signedMessages = /* @__PURE__ */ new Set();
|
|
239
|
+
for (let round = 0; round < MAX_ROUNDS; round++) {
|
|
240
|
+
const plan = asRecord(await spec.build(carry));
|
|
241
|
+
if (plan.phase === "DEPLOY_IDENTITY") {
|
|
242
|
+
const steps = Array.isArray(plan.steps) ? plan.steps : [];
|
|
243
|
+
if (steps.length === 0) throw unsignable(plan);
|
|
244
|
+
for (const entry of steps) {
|
|
245
|
+
const step = asRecord(entry);
|
|
246
|
+
const wallet = step.walletAddress;
|
|
247
|
+
const deployment = _chunkAI4ZNJLRjs.planOf.call(void 0, { steps: [step] }, { confirmIndex: 0 });
|
|
248
|
+
if (typeof wallet !== "string" || !deployment || deployment.steps[0].transaction.to !== void 0) throw unsignable(plan);
|
|
249
|
+
const executed = await executeAfter(chain, deployment, options, transactions);
|
|
250
|
+
transactions.push(...executed.transactions);
|
|
251
|
+
const hash = executed.confirmHash;
|
|
252
|
+
let recorded;
|
|
253
|
+
try {
|
|
254
|
+
recorded = await spec.recordIdentity(wallet, hash);
|
|
255
|
+
} catch (err) {
|
|
256
|
+
throw new (0, _chunkAI4ZNJLRjs.ConfirmationError)(
|
|
257
|
+
`The ONCHAINID of ${wallet} was deployed in ${hash}, but Trusset has not recorded it: ${messageOf(err)}. Record it with identity.deployOnchainId('${wallet}', { txHash: '${hash}' }), then run this call again`,
|
|
258
|
+
"CONFIRMATION_FAILED",
|
|
259
|
+
[...transactions],
|
|
260
|
+
hash,
|
|
261
|
+
err
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
if (typeof _optionalChain([recorded, 'optionalAccess', _6 => _6.identity]) === "string") carry.identities[wallet] = recorded.identity;
|
|
265
|
+
}
|
|
266
|
+
continue;
|
|
267
|
+
}
|
|
268
|
+
if (plan.action === "SIGN_MESSAGES") {
|
|
269
|
+
const messages = Array.isArray(plan.messages) ? plan.messages : [];
|
|
270
|
+
if (messages.length === 0) throw unsignable(plan);
|
|
271
|
+
for (const entry of messages) {
|
|
272
|
+
const message = asRecord(entry);
|
|
273
|
+
const hash = message.messageHash;
|
|
274
|
+
if (message.scheme !== void 0 && message.scheme !== "eip191" || typeof hash !== "string" || !HASH_RE.test(hash) || typeof message.walletAddress !== "string") {
|
|
275
|
+
throw new (0, _chunkAI4ZNJLRjs.TrussetError)("The register asks for a claim signature in a form the SDK does not produce. Nothing was signed", "UNSIGNABLE_MESSAGE", 0);
|
|
276
|
+
}
|
|
277
|
+
const key = hash.toLowerCase();
|
|
278
|
+
if (signedMessages.has(key)) {
|
|
279
|
+
throw new (0, _chunkAI4ZNJLRjs.TrussetError)(`The register asked again for the claim of ${message.walletAddress} the signer already signed, so the signer is not accepted as a claim key of the trusted issuer. Nothing further was signed`, "CLAIM_SIGNATURE_REFUSED", 0);
|
|
280
|
+
}
|
|
281
|
+
const signature = await _chunkAI4ZNJLRjs.signMessage.call(void 0, chain, options, bytesOf(hash));
|
|
282
|
+
signedMessages.add(key);
|
|
283
|
+
carry.claimSignatures.push({
|
|
284
|
+
walletAddress: message.walletAddress,
|
|
285
|
+
signature,
|
|
286
|
+
...typeof message.claimType === "string" ? { claimType: message.claimType } : {}
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
const resolved = _chunkAI4ZNJLRjs.planOf.call(void 0, plan, spec.plan);
|
|
292
|
+
if (!resolved) throw unsignable(plan);
|
|
293
|
+
const final = await executeAfter(chain, resolved, options, transactions);
|
|
294
|
+
return { plan, steps: resolved.steps, final, transactions: [...transactions, ...final.transactions] };
|
|
295
|
+
}
|
|
296
|
+
throw new (0, _chunkAI4ZNJLRjs.TrussetError)(`The register plan did not settle into transactions after ${MAX_ROUNDS} rounds; nothing further was signed`, "PLAN_DID_NOT_SETTLE", 0);
|
|
297
|
+
}
|
|
298
|
+
async function settle(run, hash, retryWith, confirm) {
|
|
299
|
+
try {
|
|
300
|
+
const result = await confirm();
|
|
301
|
+
const base = result && typeof result === "object" ? result : { result };
|
|
302
|
+
return { ...base, transactions: run.transactions };
|
|
303
|
+
} catch (err) {
|
|
304
|
+
throw new (0, _chunkAI4ZNJLRjs.ConfirmationError)(
|
|
305
|
+
`${hash ? `Transaction ${hash} is` : "The signed transactions are"} on chain but Trusset has not recorded ${hash ? "it" : "them"}: ${messageOf(err)}. Confirm again with ${retryWith}`,
|
|
306
|
+
"CONFIRMATION_FAILED",
|
|
307
|
+
run.transactions,
|
|
308
|
+
hash,
|
|
309
|
+
err
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
async function confirmRun(run, confirm) {
|
|
314
|
+
const hash = run.final.confirmHash;
|
|
315
|
+
if (!hash) {
|
|
316
|
+
throw new (0, _chunkAI4ZNJLRjs.ConfirmationError)("The plan finished without a transaction to confirm. See err.transactions", "CONFIRMATION_FAILED", run.transactions, null, null);
|
|
317
|
+
}
|
|
318
|
+
return settle(run, hash, `{ txHash: '${hash}' }`, () => confirm(hash));
|
|
319
|
+
}
|
|
320
|
+
async function confirmRunSteps(run, confirm, retryWith) {
|
|
321
|
+
if (!run.final.transactions.some((t) => t.txHash !== null)) {
|
|
322
|
+
throw new (0, _chunkAI4ZNJLRjs.ConfirmationError)("The plan finished without a transaction to confirm. See err.transactions", "CONFIRMATION_FAILED", run.transactions, null, null);
|
|
323
|
+
}
|
|
324
|
+
return settle(run, null, retryWith, confirm);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// src/modules/customers/identity.ts
|
|
328
|
+
var ONCHAIN_WRITE_TIMEOUT_MS = 6e4;
|
|
329
|
+
var PROOF_CLAIMS_TIMEOUT_MS = 18e4;
|
|
330
|
+
var BUILD = { timeoutMs: ONCHAIN_WRITE_TIMEOUT_MS };
|
|
331
|
+
var CONFIRM = BUILD;
|
|
332
|
+
var PROOF_BUILD = { timeoutMs: PROOF_CLAIMS_TIMEOUT_MS };
|
|
333
|
+
var PROOF_CONFIRM = PROOF_BUILD;
|
|
334
|
+
var SIGNATURE_RE = /^0x[0-9a-fA-F]{130}$/;
|
|
335
|
+
var BASE64_SIG_RE = /^[A-Za-z0-9+/]{86}(==)?$/;
|
|
336
|
+
var HEX_SIG_RE = /^(0x)?[0-9a-fA-F]{128}$/;
|
|
337
|
+
function hasSigner(options) {
|
|
338
|
+
return Boolean(options && typeof options === "object" && "signer" in options);
|
|
339
|
+
}
|
|
340
|
+
function hasTxHash(options) {
|
|
341
|
+
return Boolean(options && typeof options === "object" && "txHash" in options && !("signer" in options));
|
|
342
|
+
}
|
|
343
|
+
function planFields(input) {
|
|
344
|
+
const out = {};
|
|
345
|
+
if (!input) return out;
|
|
346
|
+
const signerAddress2 = _chunkAI4ZNJLRjs.optionalAddress.call(void 0, input.signerAddress, "signerAddress");
|
|
347
|
+
if (signerAddress2) out.signerAddress = signerAddress2;
|
|
348
|
+
if (input.claimSignatures !== void 0) {
|
|
349
|
+
if (!Array.isArray(input.claimSignatures)) throw new (0, _chunkAI4ZNJLRjs.ValidationError)("claimSignatures must be a list of { walletAddress, signature, claimType }");
|
|
350
|
+
out.claimSignatures = input.claimSignatures.map((entry) => {
|
|
351
|
+
_chunkAI4ZNJLRjs.requireAddress.call(void 0, _optionalChain([entry, 'optionalAccess', _7 => _7.walletAddress]), "claimSignatures[].walletAddress");
|
|
352
|
+
if (typeof entry.signature !== "string" || !SIGNATURE_RE.test(entry.signature)) {
|
|
353
|
+
throw new (0, _chunkAI4ZNJLRjs.ValidationError)("claimSignatures[].signature must be a 65-byte hex signature (0x + 130 hex chars)");
|
|
354
|
+
}
|
|
355
|
+
return entry.claimType === void 0 ? { walletAddress: entry.walletAddress, signature: entry.signature } : { walletAddress: entry.walletAddress, signature: entry.signature, claimType: entry.claimType };
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
if (input.identities !== void 0) {
|
|
359
|
+
if (!input.identities || typeof input.identities !== "object" || Array.isArray(input.identities)) {
|
|
360
|
+
throw new (0, _chunkAI4ZNJLRjs.ValidationError)("identities must map each wallet address to its ONCHAINID address");
|
|
361
|
+
}
|
|
362
|
+
const identities = {};
|
|
363
|
+
for (const [wallet, identity] of Object.entries(input.identities)) {
|
|
364
|
+
identities[_chunkAI4ZNJLRjs.requireAddress.call(void 0, wallet, "identities key")] = _chunkAI4ZNJLRjs.requireAddress.call(void 0, identity, `identities[${wallet}]`);
|
|
365
|
+
}
|
|
366
|
+
out.identities = identities;
|
|
367
|
+
}
|
|
368
|
+
return out;
|
|
369
|
+
}
|
|
370
|
+
function withoutPlanFields(input) {
|
|
371
|
+
const rest = { ...input };
|
|
372
|
+
delete rest.signerAddress;
|
|
373
|
+
delete rest.claimSignatures;
|
|
374
|
+
delete rest.identities;
|
|
375
|
+
return rest;
|
|
376
|
+
}
|
|
377
|
+
function bytesToBase64(bytes) {
|
|
378
|
+
let binary = "";
|
|
379
|
+
for (let i = 0; i < bytes.length; i++) binary += String.fromCharCode(bytes[i]);
|
|
380
|
+
return btoa(binary);
|
|
381
|
+
}
|
|
382
|
+
function hexToBytes(hex) {
|
|
383
|
+
const body = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
384
|
+
const out = new Uint8Array(body.length / 2);
|
|
385
|
+
for (let i = 0; i < out.length; i++) out[i] = parseInt(body.slice(i * 2, i * 2 + 2), 16);
|
|
386
|
+
return out;
|
|
387
|
+
}
|
|
388
|
+
function manifestSigOf(value) {
|
|
389
|
+
if (value === void 0 || value === null || value === "") return void 0;
|
|
390
|
+
const hint = "manifestSig must be the bundle's 64-byte manifest.sig: pass readFileSync(path) or readFileSync(path).toString('base64'), not the file read as text";
|
|
391
|
+
if (value instanceof Uint8Array) {
|
|
392
|
+
if (value.length !== 64) throw new (0, _chunkAI4ZNJLRjs.ValidationError)(`${hint}. These bytes are ${value.length} long`);
|
|
393
|
+
return bytesToBase64(value);
|
|
394
|
+
}
|
|
395
|
+
if (typeof value !== "string") throw new (0, _chunkAI4ZNJLRjs.ValidationError)(hint);
|
|
396
|
+
const s = value.trim();
|
|
397
|
+
if (BASE64_SIG_RE.test(s)) return s;
|
|
398
|
+
if (HEX_SIG_RE.test(s)) return bytesToBase64(hexToBytes(s));
|
|
399
|
+
throw new (0, _chunkAI4ZNJLRjs.ValidationError)(hint);
|
|
400
|
+
}
|
|
401
|
+
function proofBody(input) {
|
|
402
|
+
const { manifestSig, ...rest } = withoutPlanFields(input);
|
|
403
|
+
const sig = manifestSigOf(manifestSig);
|
|
404
|
+
return sig === void 0 ? rest : { ...rest, manifestSig: sig };
|
|
405
|
+
}
|
|
406
|
+
var CustomerIdentity = class {
|
|
407
|
+
constructor(http, chain) {
|
|
408
|
+
this.http = http;
|
|
409
|
+
this.chain = _nullishCoalesce(chain, () => ( new (0, _chunkAI4ZNJLRjs.InstanceChain)(http)));
|
|
410
|
+
}
|
|
411
|
+
async verify(input, options) {
|
|
412
|
+
_chunkAI4ZNJLRjs.requireAddress.call(void 0, input.walletAddress, "walletAddress");
|
|
413
|
+
_chunkAI4ZNJLRjs.requireCountry.call(void 0, input.country, "country");
|
|
414
|
+
_chunkAI4ZNJLRjs.optionalHash.call(void 0, input.kycHash, "kycHash");
|
|
415
|
+
const plan = planFields(input);
|
|
416
|
+
const base = withoutPlanFields(input);
|
|
417
|
+
if (hasTxHash(options)) {
|
|
418
|
+
_chunkAI4ZNJLRjs.requireHash.call(void 0, options.txHash, "txHash");
|
|
419
|
+
return this.confirmVerify({ ...base, txHash: options.txHash });
|
|
420
|
+
}
|
|
421
|
+
if (!hasSigner(options)) {
|
|
422
|
+
return this.http.post("/customers/api/identity/verify", { ...base, ...plan }, BUILD);
|
|
423
|
+
}
|
|
424
|
+
let pinned = base.kycHash;
|
|
425
|
+
const run = await runRegisterPlan(this.chain, options, {
|
|
426
|
+
build: async (carry) => {
|
|
427
|
+
const prepared = await this.http.post("/customers/api/identity/verify", { ...base, ...pinned ? { kycHash: pinned } : {}, ...carry }, BUILD);
|
|
428
|
+
if (!pinned && typeof _optionalChain([prepared, 'optionalAccess', _8 => _8.kycHash]) === "string") pinned = prepared.kycHash;
|
|
429
|
+
return prepared;
|
|
430
|
+
},
|
|
431
|
+
recordIdentity: (wallet, txHash) => this.confirmOnchainId(wallet, txHash),
|
|
432
|
+
seed: plan
|
|
433
|
+
});
|
|
434
|
+
const kycHash = typeof run.plan.kycHash === "string" ? run.plan.kycHash : pinned;
|
|
435
|
+
return confirmRun(run, (txHash) => this.confirmVerify({ ...base, kycHash, txHash }));
|
|
436
|
+
}
|
|
437
|
+
async verifyFromManifest(manifest, overrides, options) {
|
|
438
|
+
const input = { ...manifestToVerifyInput(manifest, overrides), ...planFields(overrides) };
|
|
439
|
+
return options ? this.verify(input, options) : this.verify(input);
|
|
440
|
+
}
|
|
441
|
+
async batchVerify(entries, options) {
|
|
442
|
+
if (!Array.isArray(entries) || entries.length === 0 || entries.length > 500) {
|
|
443
|
+
throw new (0, _chunkAI4ZNJLRjs.ValidationError)("Provide 1-500 entries for batch verification");
|
|
444
|
+
}
|
|
445
|
+
for (const e of entries) {
|
|
446
|
+
_chunkAI4ZNJLRjs.requireAddress.call(void 0, e.walletAddress, "walletAddress");
|
|
447
|
+
_chunkAI4ZNJLRjs.requireCountry.call(void 0, e.country, "country");
|
|
448
|
+
_chunkAI4ZNJLRjs.optionalHash.call(void 0, e.kycHash, "kycHash");
|
|
449
|
+
}
|
|
450
|
+
if (hasTxHash(options)) {
|
|
451
|
+
_chunkAI4ZNJLRjs.requireHash.call(void 0, options.txHash, "txHash");
|
|
452
|
+
return this.confirmBatchVerify(entries, options.txHash);
|
|
453
|
+
}
|
|
454
|
+
if (!hasSigner(options)) {
|
|
455
|
+
return this.http.post("/customers/api/identity/batch-verify", { entries, ...planFields(options) }, BUILD);
|
|
456
|
+
}
|
|
457
|
+
let pinned = entries;
|
|
458
|
+
const run = await runRegisterPlan(this.chain, options, {
|
|
459
|
+
build: async (carry) => {
|
|
460
|
+
const prepared2 = await this.http.post("/customers/api/identity/batch-verify", { entries: pinned, ...carry }, BUILD);
|
|
461
|
+
pinned = pinEntryHashes(pinned, prepared2);
|
|
462
|
+
return prepared2;
|
|
463
|
+
},
|
|
464
|
+
recordIdentity: (wallet, txHash) => this.confirmOnchainId(wallet, txHash)
|
|
465
|
+
});
|
|
466
|
+
const prepared = run.plan;
|
|
467
|
+
const merged = mergePreparedEntries(entries, prepared);
|
|
468
|
+
return confirmRun(run, async (txHash) => withPrepareRejections(await this.confirmBatchVerify(merged.entries, txHash), prepared, merged.origin));
|
|
469
|
+
}
|
|
470
|
+
async revoke(walletAddress, options) {
|
|
471
|
+
_chunkAI4ZNJLRjs.requireAddress.call(void 0, walletAddress, "walletAddress");
|
|
472
|
+
if (hasTxHash(options)) {
|
|
473
|
+
_chunkAI4ZNJLRjs.requireHash.call(void 0, options.txHash, "txHash");
|
|
474
|
+
return this.confirmRevoke(walletAddress, options.txHash);
|
|
475
|
+
}
|
|
476
|
+
if (!hasSigner(options)) {
|
|
477
|
+
return this.http.post("/customers/api/identity/revoke", { walletAddress }, BUILD);
|
|
478
|
+
}
|
|
479
|
+
const run = await runRegisterPlan(this.chain, options, {
|
|
480
|
+
build: () => this.http.post("/customers/api/identity/revoke", { walletAddress }, BUILD),
|
|
481
|
+
recordIdentity: (wallet, txHash) => this.confirmOnchainId(wallet, txHash)
|
|
482
|
+
});
|
|
483
|
+
return confirmRun(run, (txHash) => this.confirmRevoke(walletAddress, txHash));
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Get combined on-chain and off-chain identity status.
|
|
487
|
+
*/
|
|
488
|
+
async getStatus(walletAddress) {
|
|
489
|
+
_chunkAI4ZNJLRjs.requireAddress.call(void 0, walletAddress, "walletAddress");
|
|
490
|
+
return this.http.get(`/customers/api/identity/status/${walletAddress}`);
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Check whether a token transfer between two addresses is compliant.
|
|
494
|
+
* Runs identity checks + all registered compliance modules.
|
|
495
|
+
*/
|
|
496
|
+
async canTransfer(input) {
|
|
497
|
+
_chunkAI4ZNJLRjs.requireAddress.call(void 0, input.from, "from");
|
|
498
|
+
_chunkAI4ZNJLRjs.requireAddress.call(void 0, input.to, "to");
|
|
499
|
+
return this.http.post("/customers/api/identity/can-transfer", input);
|
|
500
|
+
}
|
|
501
|
+
async deployOnchainId(walletAddress, options) {
|
|
502
|
+
_chunkAI4ZNJLRjs.requireAddress.call(void 0, walletAddress, "walletAddress");
|
|
503
|
+
if (hasTxHash(options)) {
|
|
504
|
+
_chunkAI4ZNJLRjs.requireHash.call(void 0, options.txHash, "txHash");
|
|
505
|
+
return this.confirmOnchainId(walletAddress, options.txHash);
|
|
506
|
+
}
|
|
507
|
+
if (!hasSigner(options)) {
|
|
508
|
+
return this.http.post("/customers/api/identity/onchainid", { walletAddress, ...planFields(options) }, BUILD);
|
|
509
|
+
}
|
|
510
|
+
await _chunkAI4ZNJLRjs.preflightChain.call(void 0, this.chain, options);
|
|
511
|
+
const signer = await _chunkAI4ZNJLRjs.signerAddress.call(void 0, _chunkAI4ZNJLRjs.requireSigner.call(void 0, options.signer));
|
|
512
|
+
const prepared = await this.http.post("/customers/api/identity/onchainid", { walletAddress, signerAddress: signer }, BUILD);
|
|
513
|
+
if (prepared.exists) return _chunkAI4ZNJLRjs.attachTransactions.call(void 0, prepared, []);
|
|
514
|
+
const plan = _chunkAI4ZNJLRjs.planOf.call(void 0, prepared, { confirmIndex: 0 });
|
|
515
|
+
if (!plan || plan.steps[0].transaction.to !== void 0) {
|
|
516
|
+
throw new (0, _chunkAI4ZNJLRjs.TrussetError)("The ONCHAINID build carries no identity deployment the SDK can sign, so nothing was signed", "UNSIGNABLE_PLAN", 0);
|
|
517
|
+
}
|
|
518
|
+
const executed = await _chunkAI4ZNJLRjs.execute.call(void 0, this.chain, plan, options);
|
|
519
|
+
return _chunkAI4ZNJLRjs.confirmExecuted.call(void 0, executed, (txHash) => this.confirmOnchainId(walletAddress, txHash));
|
|
520
|
+
}
|
|
521
|
+
// -----------------------------------------------------------------------
|
|
522
|
+
// On-chain claims
|
|
523
|
+
// -----------------------------------------------------------------------
|
|
524
|
+
/**
|
|
525
|
+
* List the active on-chain claims of a wallet.
|
|
526
|
+
*/
|
|
527
|
+
async getClaims(walletAddress) {
|
|
528
|
+
_chunkAI4ZNJLRjs.requireAddress.call(void 0, walletAddress, "walletAddress");
|
|
529
|
+
return this.http.get(`/customers/api/identity/claims/${walletAddress}`);
|
|
530
|
+
}
|
|
531
|
+
async addClaimsFromProof(input, options) {
|
|
532
|
+
_chunkAI4ZNJLRjs.requireAddress.call(void 0, input.walletAddress, "walletAddress");
|
|
533
|
+
if (!input.manifest || typeof input.manifest !== "object" || Array.isArray(input.manifest)) {
|
|
534
|
+
throw new (0, _chunkAI4ZNJLRjs.ValidationError)("manifest must be the manifest.json object produced by the KYC proof tool");
|
|
535
|
+
}
|
|
536
|
+
const plan = planFields(input);
|
|
537
|
+
const body = proofBody(input);
|
|
538
|
+
if (options && !hasSigner(options)) {
|
|
539
|
+
if (!Array.isArray(options.txResults) || options.txResults.length === 0) {
|
|
540
|
+
throw new (0, _chunkAI4ZNJLRjs.ValidationError)("txResults must list the signed claims as [{ claimType, txHash }]");
|
|
541
|
+
}
|
|
542
|
+
for (const r of options.txResults) _chunkAI4ZNJLRjs.requireHash.call(void 0, _optionalChain([r, 'optionalAccess', _9 => _9.txHash]), "txResults[].txHash");
|
|
543
|
+
return this.confirmProofClaims(body, options.txResults);
|
|
544
|
+
}
|
|
545
|
+
if (!options) {
|
|
546
|
+
return this.http.post("/customers/api/identity/claims/from-proof", { ...body, ...plan }, PROOF_BUILD);
|
|
547
|
+
}
|
|
548
|
+
const run = await runRegisterPlan(this.chain, options, {
|
|
549
|
+
build: (carry) => this.http.post("/customers/api/identity/claims/from-proof", { ...body, ...carry }, PROOF_BUILD),
|
|
550
|
+
recordIdentity: (wallet, txHash) => this.confirmOnchainId(wallet, txHash),
|
|
551
|
+
seed: plan,
|
|
552
|
+
plan: { confirmIndex: null }
|
|
553
|
+
});
|
|
554
|
+
const planned = Array.isArray(run.plan.steps) ? run.plan.steps : [];
|
|
555
|
+
const txResults = [];
|
|
556
|
+
run.final.transactions.forEach((sent, i) => {
|
|
557
|
+
const claimType = _optionalChain([planned, 'access', _10 => _10[i], 'optionalAccess', _11 => _11.claimType]);
|
|
558
|
+
if (claimType && sent.txHash) txResults.push({ claimType, txHash: sent.txHash });
|
|
559
|
+
});
|
|
560
|
+
return confirmRunSteps(run, () => this.confirmProofClaims(body, txResults), "{ txResults }");
|
|
561
|
+
}
|
|
562
|
+
async addClaim(input, options) {
|
|
563
|
+
_chunkAI4ZNJLRjs.requireAddress.call(void 0, input.walletAddress, "walletAddress");
|
|
564
|
+
if (input.claimType === void 0 || input.claimType === null) {
|
|
565
|
+
throw new (0, _chunkAI4ZNJLRjs.ValidationError)("claimType is required");
|
|
566
|
+
}
|
|
567
|
+
if (!input.dataHash && !input.dataInput) {
|
|
568
|
+
throw new (0, _chunkAI4ZNJLRjs.ValidationError)("Provide dataHash (32-byte hex) or dataInput (string) for the claim");
|
|
569
|
+
}
|
|
570
|
+
_chunkAI4ZNJLRjs.optionalHash.call(void 0, input.dataHash, "dataHash");
|
|
571
|
+
const plan = planFields(input);
|
|
572
|
+
const base = withoutPlanFields(input);
|
|
573
|
+
if (hasTxHash(options)) {
|
|
574
|
+
_chunkAI4ZNJLRjs.requireHash.call(void 0, options.txHash, "txHash");
|
|
575
|
+
return this.confirmAddClaim({ ...base, txHash: options.txHash });
|
|
576
|
+
}
|
|
577
|
+
if (!hasSigner(options)) {
|
|
578
|
+
return this.http.post("/customers/api/identity/add-claim", { ...base, ...plan }, BUILD);
|
|
579
|
+
}
|
|
580
|
+
const run = await runRegisterPlan(this.chain, options, {
|
|
581
|
+
build: (carry) => this.http.post("/customers/api/identity/add-claim", { ...base, ...carry }, BUILD),
|
|
582
|
+
recordIdentity: (wallet, txHash) => this.confirmOnchainId(wallet, txHash),
|
|
583
|
+
seed: plan
|
|
584
|
+
});
|
|
585
|
+
const dataHash = typeof run.plan.dataHash === "string" ? run.plan.dataHash : base.dataHash;
|
|
586
|
+
return confirmRun(run, (txHash) => this.confirmAddClaim({ ...base, dataHash, txHash }));
|
|
587
|
+
}
|
|
588
|
+
async revokeClaim(walletAddress, claimType, options) {
|
|
589
|
+
_chunkAI4ZNJLRjs.requireAddress.call(void 0, walletAddress, "walletAddress");
|
|
590
|
+
if (claimType === void 0 || claimType === null) {
|
|
591
|
+
throw new (0, _chunkAI4ZNJLRjs.ValidationError)("claimType is required");
|
|
592
|
+
}
|
|
593
|
+
if (hasTxHash(options)) {
|
|
594
|
+
_chunkAI4ZNJLRjs.requireHash.call(void 0, options.txHash, "txHash");
|
|
595
|
+
return this.confirmRevokeClaim(walletAddress, claimType, options.txHash);
|
|
596
|
+
}
|
|
597
|
+
if (!hasSigner(options)) {
|
|
598
|
+
return this.http.post("/customers/api/identity/revoke-claim", { walletAddress, claimType }, BUILD);
|
|
599
|
+
}
|
|
600
|
+
const run = await runRegisterPlan(this.chain, options, {
|
|
601
|
+
build: () => this.http.post("/customers/api/identity/revoke-claim", { walletAddress, claimType }, BUILD),
|
|
602
|
+
recordIdentity: (wallet, txHash) => this.confirmOnchainId(wallet, txHash)
|
|
603
|
+
});
|
|
604
|
+
return confirmRun(run, (txHash) => this.confirmRevokeClaim(walletAddress, claimType, txHash));
|
|
605
|
+
}
|
|
606
|
+
// -----------------------------------------------------------------------
|
|
607
|
+
// Confirmations: the backend reads the mined receipt, checks it targets the
|
|
608
|
+
// identity registry and the wallet it should, and only then records it.
|
|
609
|
+
// -----------------------------------------------------------------------
|
|
610
|
+
confirmVerify(body) {
|
|
611
|
+
return this.http.post("/customers/api/identity/verify", body, CONFIRM);
|
|
612
|
+
}
|
|
613
|
+
confirmBatchVerify(entries, txHash) {
|
|
614
|
+
return this.http.post("/customers/api/identity/batch-verify", { entries, txHash }, CONFIRM);
|
|
615
|
+
}
|
|
616
|
+
confirmRevoke(walletAddress, txHash) {
|
|
617
|
+
return this.http.post("/customers/api/identity/revoke", { walletAddress, txHash }, CONFIRM);
|
|
618
|
+
}
|
|
619
|
+
confirmAddClaim(body) {
|
|
620
|
+
return this.http.post("/customers/api/identity/add-claim", body, CONFIRM);
|
|
621
|
+
}
|
|
622
|
+
confirmRevokeClaim(walletAddress, claimType, txHash) {
|
|
623
|
+
return this.http.post("/customers/api/identity/revoke-claim", { walletAddress, claimType, txHash }, CONFIRM);
|
|
624
|
+
}
|
|
625
|
+
confirmProofClaims(body, txResults) {
|
|
626
|
+
return this.http.post("/customers/api/identity/claims/from-proof", { ...body, txResults }, PROOF_CONFIRM);
|
|
627
|
+
}
|
|
628
|
+
confirmOnchainId(walletAddress, txHash) {
|
|
629
|
+
return this.http.post("/customers/api/identity/onchainid/confirm", { walletAddress, txHash }, CONFIRM);
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
function mergePreparedEntries(entries, prepared) {
|
|
633
|
+
const accepted = Array.isArray(prepared.entries) ? prepared.entries : [];
|
|
634
|
+
const origin = [];
|
|
635
|
+
const merged = accepted.map((entry) => {
|
|
636
|
+
const index = entries.findIndex((e) => e.walletAddress.toLowerCase() === entry.walletAddress.toLowerCase());
|
|
637
|
+
origin.push(index);
|
|
638
|
+
return {
|
|
639
|
+
...index >= 0 ? entries[index] : {},
|
|
640
|
+
walletAddress: entry.walletAddress,
|
|
641
|
+
country: entry.country,
|
|
642
|
+
kycHash: entry.kycHash
|
|
643
|
+
};
|
|
644
|
+
});
|
|
645
|
+
return { entries: merged, origin };
|
|
646
|
+
}
|
|
647
|
+
function pinEntryHashes(entries, prepared) {
|
|
648
|
+
const derived = new Map((Array.isArray(_optionalChain([prepared, 'optionalAccess', _12 => _12.entries])) ? prepared.entries : []).map((e) => [e.walletAddress.toLowerCase(), e.kycHash]));
|
|
649
|
+
return entries.map((e) => {
|
|
650
|
+
const hash = _nullishCoalesce(e.kycHash, () => ( derived.get(e.walletAddress.toLowerCase())));
|
|
651
|
+
return hash ? { ...e, kycHash: hash } : e;
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
function withPrepareRejections(confirmed, prepared, origin) {
|
|
655
|
+
const early = Array.isArray(prepared.rejected) ? prepared.rejected : [];
|
|
656
|
+
const late = (Array.isArray(confirmed.rejected) ? confirmed.rejected : []).map((r) => ({ ...r, index: _nullishCoalesce(origin[r.index], () => ( r.index)) }));
|
|
657
|
+
const rejected = [...early, ...late].sort((a, b) => a.index - b.index);
|
|
658
|
+
return {
|
|
659
|
+
...confirmed,
|
|
660
|
+
submitted: typeof prepared.submitted === "number" ? prepared.submitted : confirmed.submitted,
|
|
661
|
+
rejectedCount: rejected.length,
|
|
662
|
+
rejected
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// src/modules/customers/id-links.ts
|
|
667
|
+
var CustomerIdLinks = class {
|
|
668
|
+
constructor(http) {
|
|
669
|
+
this.http = http;
|
|
670
|
+
}
|
|
671
|
+
/**
|
|
672
|
+
* Create a KYC verification link to send to an investor.
|
|
673
|
+
*/
|
|
674
|
+
async create(input) {
|
|
675
|
+
if (!input.recipientEmail && !input.recipientWallet && !input.recipientPhone) {
|
|
676
|
+
throw new (0, _chunkAI4ZNJLRjs.ValidationError)("At least one of recipientEmail, recipientWallet, or recipientPhone is required");
|
|
677
|
+
}
|
|
678
|
+
return this.http.post("/customers/api/id-links", input);
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* List all verification links for the current instance.
|
|
682
|
+
*/
|
|
683
|
+
async list(options = {}) {
|
|
684
|
+
const params = {
|
|
685
|
+
limit: _chunkAI4ZNJLRjs.clampInt.call(void 0, options.limit, 1, 200, 50),
|
|
686
|
+
offset: _chunkAI4ZNJLRjs.clampInt.call(void 0, options.offset, 0, Number.MAX_SAFE_INTEGER, 0),
|
|
687
|
+
status: options.status
|
|
688
|
+
};
|
|
689
|
+
const { data, metadata } = await this.http.getWithMeta("/customers/api/id-links", params);
|
|
690
|
+
const links = Array.isArray(data) ? data : [];
|
|
691
|
+
return { links, total: _nullishCoalesce(metadata.total, () => ( links.length)) };
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Revoke (expire) a verification link so it can no longer be used.
|
|
695
|
+
*/
|
|
696
|
+
async revoke(linkId) {
|
|
697
|
+
_chunkAI4ZNJLRjs.requireString.call(void 0, linkId, "linkId");
|
|
698
|
+
await this.http.put(`/customers/api/id-links/${encodeURIComponent(linkId)}/revoke`);
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* Resend a verification link via the specified channels.
|
|
702
|
+
*/
|
|
703
|
+
async resend(linkId, channels) {
|
|
704
|
+
_chunkAI4ZNJLRjs.requireString.call(void 0, linkId, "linkId");
|
|
705
|
+
if (!Array.isArray(channels) || channels.length === 0) {
|
|
706
|
+
throw new (0, _chunkAI4ZNJLRjs.ValidationError)("channels array is required (email and/or sms)");
|
|
707
|
+
}
|
|
708
|
+
return this.http.post(`/customers/api/id-links/${encodeURIComponent(linkId)}/resend`, { channels });
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
|
|
712
|
+
// src/modules/customers/sync.ts
|
|
713
|
+
var DEFAULT_BATCH_SIZE = 50;
|
|
714
|
+
var MAX_BATCH_SIZE = 200;
|
|
715
|
+
var MAX_VERIFY_BATCH = 500;
|
|
716
|
+
var CustomerSync = class {
|
|
717
|
+
constructor(http, identity) {
|
|
718
|
+
this.http = http;
|
|
719
|
+
this.manage = new CustomerManagement(http);
|
|
720
|
+
this.identity = _nullishCoalesce(identity, () => ( new CustomerIdentity(http)));
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Import customer records from an external system.
|
|
724
|
+
*
|
|
725
|
+
* Each record is mapped by `externalId`. If a customer with that externalId
|
|
726
|
+
* already exists, the `onConflict` strategy determines whether to skip or
|
|
727
|
+
* update.
|
|
728
|
+
*
|
|
729
|
+
* When `verifyOnChain` is true, records that include a `walletAddress` and
|
|
730
|
+
* `country` are batch-verified on-chain after the database import. That
|
|
731
|
+
* needs `signer`: verification transactions are signed by your wallet, not
|
|
732
|
+
* by Trusset.
|
|
733
|
+
*/
|
|
734
|
+
async importRecords(records, options = {}) {
|
|
735
|
+
if (!Array.isArray(records) || records.length === 0) {
|
|
736
|
+
throw new (0, _chunkAI4ZNJLRjs.ValidationError)("records must be a non-empty array");
|
|
737
|
+
}
|
|
738
|
+
if (options.verifyOnChain) {
|
|
739
|
+
_chunkAI4ZNJLRjs.requireSigner.call(void 0, options.signer);
|
|
740
|
+
}
|
|
741
|
+
const batchSize = _chunkAI4ZNJLRjs.clampInt.call(void 0, options.batchSize, 1, MAX_BATCH_SIZE, DEFAULT_BATCH_SIZE);
|
|
742
|
+
const onConflict = _nullishCoalesce(options.onConflict, () => ( "update"));
|
|
743
|
+
const result = { total: records.length, created: 0, updated: 0, skipped: 0, errors: [] };
|
|
744
|
+
const verifyQueue = [];
|
|
745
|
+
const verifyIds = [];
|
|
746
|
+
for (let i = 0; i < records.length; i += batchSize) {
|
|
747
|
+
const batch = records.slice(i, i + batchSize);
|
|
748
|
+
const batchResults = await Promise.allSettled(
|
|
749
|
+
batch.map((record) => this.processRecord(record, onConflict))
|
|
750
|
+
);
|
|
751
|
+
for (let j = 0; j < batchResults.length; j++) {
|
|
752
|
+
const settled = batchResults[j];
|
|
753
|
+
const record = batch[j];
|
|
754
|
+
if (settled.status === "fulfilled") {
|
|
755
|
+
const outcome = settled.value;
|
|
756
|
+
if (outcome.action === "created") result.created++;
|
|
757
|
+
else if (outcome.action === "updated") result.updated++;
|
|
758
|
+
else result.skipped++;
|
|
759
|
+
if (options.verifyOnChain && record.walletAddress && record.country && outcome.action !== "skipped") {
|
|
760
|
+
verifyQueue.push({
|
|
761
|
+
walletAddress: record.walletAddress,
|
|
762
|
+
country: record.country,
|
|
763
|
+
investorType: record.investorType,
|
|
764
|
+
kycHash: record.kycHash,
|
|
765
|
+
softExpiryDays: _optionalChain([options, 'access', _13 => _13.verificationDefaults, 'optionalAccess', _14 => _14.softExpiryDays]),
|
|
766
|
+
hardExpiryDays: _optionalChain([options, 'access', _15 => _15.verificationDefaults, 'optionalAccess', _16 => _16.hardExpiryDays])
|
|
767
|
+
});
|
|
768
|
+
verifyIds.push(record.externalId);
|
|
769
|
+
}
|
|
770
|
+
} else {
|
|
771
|
+
const err = settled.reason;
|
|
772
|
+
result.errors.push({
|
|
773
|
+
externalId: record.externalId,
|
|
774
|
+
code: _nullishCoalesce(_optionalChain([err, 'optionalAccess', _17 => _17.code]), () => ( "UNKNOWN")),
|
|
775
|
+
message: _nullishCoalesce(_optionalChain([err, 'optionalAccess', _18 => _18.message]), () => ( "Unknown error"))
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
_optionalChain([options, 'access', _19 => _19.onProgress, 'optionalCall', _20 => _20(Math.min(i + batchSize, records.length), records.length)]);
|
|
780
|
+
}
|
|
781
|
+
if (verifyQueue.length > 0) {
|
|
782
|
+
await this.runBatchVerification(verifyQueue, verifyIds, options, result);
|
|
783
|
+
}
|
|
784
|
+
return result;
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* Export all customers from Trusset as flat records.
|
|
788
|
+
* Useful for syncing data back into your system.
|
|
789
|
+
*/
|
|
790
|
+
async exportRecords(options = {}) {
|
|
791
|
+
const allRecords = [];
|
|
792
|
+
let offset = 0;
|
|
793
|
+
const limit = 200;
|
|
794
|
+
while (true) {
|
|
795
|
+
const { data, metadata } = await this.http.getWithMeta("/customers/api/manage", {
|
|
796
|
+
limit,
|
|
797
|
+
offset,
|
|
798
|
+
includeArchived: options.includeArchived ? "true" : void 0
|
|
799
|
+
});
|
|
800
|
+
const customers = Array.isArray(data) ? data : [];
|
|
801
|
+
if (customers.length === 0) break;
|
|
802
|
+
for (const c of customers) {
|
|
803
|
+
allRecords.push({
|
|
804
|
+
externalId: _nullishCoalesce(c.externalId, () => ( c.id)),
|
|
805
|
+
walletAddress: _nullishCoalesce(c.walletAddress, () => ( void 0)),
|
|
806
|
+
name: _nullishCoalesce(c.name, () => ( void 0)),
|
|
807
|
+
country: _nullishCoalesce(c.country, () => ( void 0)),
|
|
808
|
+
investorType: _nullishCoalesce(c.investorType, () => ( void 0)),
|
|
809
|
+
kycHash: _nullishCoalesce(c.kycHash, () => ( void 0)),
|
|
810
|
+
metadata: _nullishCoalesce(c.metadata, () => ( void 0))
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
offset += customers.length;
|
|
814
|
+
if (metadata.total !== void 0 && offset >= metadata.total) break;
|
|
815
|
+
}
|
|
816
|
+
return allRecords;
|
|
817
|
+
}
|
|
818
|
+
// -----------------------------------------------------------------------
|
|
819
|
+
// Internal
|
|
820
|
+
// -----------------------------------------------------------------------
|
|
821
|
+
async processRecord(record, onConflict) {
|
|
822
|
+
_chunkAI4ZNJLRjs.requireString.call(void 0, record.externalId, "externalId");
|
|
823
|
+
let existing = null;
|
|
824
|
+
try {
|
|
825
|
+
existing = await this.manage.searchByExternalId(record.externalId);
|
|
826
|
+
} catch (err) {
|
|
827
|
+
const code = _optionalChain([err, 'optionalAccess', _21 => _21.code]);
|
|
828
|
+
const statusCode = _optionalChain([err, 'optionalAccess', _22 => _22.statusCode]);
|
|
829
|
+
if (statusCode !== 404 && code !== "NOT_FOUND") throw err;
|
|
830
|
+
}
|
|
831
|
+
if (existing) {
|
|
832
|
+
if (onConflict === "skip") {
|
|
833
|
+
return { action: "skipped" };
|
|
834
|
+
}
|
|
835
|
+
await this.manage.update(existing.id, {
|
|
836
|
+
name: record.name,
|
|
837
|
+
country: record.country,
|
|
838
|
+
investorType: record.investorType,
|
|
839
|
+
walletAddress: record.walletAddress,
|
|
840
|
+
metadata: record.metadata
|
|
841
|
+
});
|
|
842
|
+
return { action: "updated" };
|
|
843
|
+
}
|
|
844
|
+
await this.manage.create({
|
|
845
|
+
externalId: record.externalId,
|
|
846
|
+
walletAddress: record.walletAddress,
|
|
847
|
+
name: record.name,
|
|
848
|
+
country: record.country,
|
|
849
|
+
investorType: record.investorType,
|
|
850
|
+
metadata: record.metadata
|
|
851
|
+
});
|
|
852
|
+
return { action: "created" };
|
|
853
|
+
}
|
|
854
|
+
async runBatchVerification(queue, ids, options, result) {
|
|
855
|
+
const signer = _chunkAI4ZNJLRjs.requireSigner.call(void 0, options.signer);
|
|
856
|
+
for (let i = 0; i < queue.length; i += MAX_VERIFY_BATCH) {
|
|
857
|
+
const batch = queue.slice(i, i + MAX_VERIFY_BATCH);
|
|
858
|
+
try {
|
|
859
|
+
const confirmed = await this.identity.batchVerify(batch, {
|
|
860
|
+
signer,
|
|
861
|
+
confirmations: options.confirmations
|
|
862
|
+
});
|
|
863
|
+
for (const rejection of confirmed.rejected) {
|
|
864
|
+
result.errors.push({
|
|
865
|
+
externalId: _nullishCoalesce(_nullishCoalesce(ids[i + rejection.index], () => ( rejection.walletAddress)), () => ( `entry ${i + rejection.index}`)),
|
|
866
|
+
code: rejection.code,
|
|
867
|
+
message: rejection.message
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
} catch (err) {
|
|
871
|
+
for (const [k, entry] of batch.entries()) {
|
|
872
|
+
result.errors.push({
|
|
873
|
+
externalId: _nullishCoalesce(ids[i + k], () => ( entry.walletAddress)),
|
|
874
|
+
code: "VERIFY_FAILED",
|
|
875
|
+
message: _nullishCoalesce(_optionalChain([err, 'optionalAccess', _23 => _23.message]), () => ( "On-chain batch verification failed"))
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
};
|
|
882
|
+
|
|
883
|
+
// src/modules/customers/verification-profiles.ts
|
|
884
|
+
var CustomerVerificationProfiles = class {
|
|
885
|
+
constructor(http) {
|
|
886
|
+
this.http = http;
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* List all verification profiles of the current instance,
|
|
890
|
+
* presets (RETAIL, PROFESSIONAL, ELIGIBLE_COUNTERPARTY) first.
|
|
891
|
+
*/
|
|
892
|
+
async list() {
|
|
893
|
+
return this.http.get("/customers/api/verification-profiles");
|
|
894
|
+
}
|
|
895
|
+
};
|
|
896
|
+
|
|
897
|
+
// src/modules/customers/index.ts
|
|
898
|
+
var Customers = class {
|
|
899
|
+
constructor(http, chain) {
|
|
900
|
+
this.manage = new CustomerManagement(http);
|
|
901
|
+
this.identity = new CustomerIdentity(http, chain);
|
|
902
|
+
this.idLinks = new CustomerIdLinks(http);
|
|
903
|
+
this.profiles = new CustomerVerificationProfiles(http);
|
|
904
|
+
this.sync = new CustomerSync(http, this.identity);
|
|
905
|
+
}
|
|
906
|
+
};
|
|
907
|
+
|
|
908
|
+
|
|
909
|
+
|
|
910
|
+
|
|
911
|
+
|
|
912
|
+
|
|
913
|
+
|
|
914
|
+
|
|
915
|
+
|
|
916
|
+
exports.CustomerManagement = CustomerManagement; exports.manifestToVerifyInput = manifestToVerifyInput; exports.CustomerIdentity = CustomerIdentity; exports.CustomerIdLinks = CustomerIdLinks; exports.CustomerSync = CustomerSync; exports.CustomerVerificationProfiles = CustomerVerificationProfiles; exports.Customers = Customers;
|