@t2000/sdk 5.0.0 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.cjs +293 -51
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.cts +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +293 -51
- package/dist/browser.js.map +1 -1
- package/dist/index.cjs +132 -51
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +133 -52
- package/dist/index.js.map +1 -1
- package/dist/{types-CgZWnHhs.d.cts → types-C01EGu76.d.cts} +8 -0
- package/dist/{types-CgZWnHhs.d.ts → types-C01EGu76.d.ts} +8 -0
- package/package.json +3 -4
package/dist/browser.cjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var transactions = require('@mysten/sui/transactions');
|
|
4
|
+
var utils = require('@mysten/sui/utils');
|
|
3
5
|
require('@mysten/sui/grpc');
|
|
4
6
|
require('@mysten/sui/graphql');
|
|
5
|
-
var utils = require('@mysten/sui/utils');
|
|
6
7
|
var aggregatorSdk = require('@cetusprotocol/aggregator-sdk');
|
|
7
8
|
require('bn.js');
|
|
8
9
|
|
|
@@ -106,6 +107,34 @@ var init_errors = __esm({
|
|
|
106
107
|
});
|
|
107
108
|
|
|
108
109
|
// src/token-registry.ts
|
|
110
|
+
var token_registry_exports = {};
|
|
111
|
+
__export(token_registry_exports, {
|
|
112
|
+
COIN_REGISTRY: () => exports.COIN_REGISTRY,
|
|
113
|
+
ETH_TYPE: () => exports.ETH_TYPE,
|
|
114
|
+
IKA_TYPE: () => exports.IKA_TYPE,
|
|
115
|
+
LOFI_TYPE: () => exports.LOFI_TYPE,
|
|
116
|
+
MANIFEST_TYPE: () => exports.MANIFEST_TYPE,
|
|
117
|
+
NAVX_TYPE: () => exports.NAVX_TYPE,
|
|
118
|
+
SUI_TYPE: () => exports.SUI_TYPE,
|
|
119
|
+
TOKEN_MAP: () => exports.TOKEN_MAP,
|
|
120
|
+
USDC_TYPE: () => exports.USDC_TYPE,
|
|
121
|
+
USDE_TYPE: () => exports.USDE_TYPE,
|
|
122
|
+
USDSUI_TYPE: () => exports.USDSUI_TYPE,
|
|
123
|
+
USDT_TYPE: () => exports.USDT_TYPE,
|
|
124
|
+
WAL_TYPE: () => exports.WAL_TYPE,
|
|
125
|
+
WBTC_TYPE: () => exports.WBTC_TYPE,
|
|
126
|
+
getCoinMeta: () => getCoinMeta,
|
|
127
|
+
getDecimalsForCoinType: () => getDecimalsForCoinType,
|
|
128
|
+
isInRegistry: () => isInRegistry,
|
|
129
|
+
resolveSymbol: () => resolveSymbol,
|
|
130
|
+
resolveTokenType: () => resolveTokenType
|
|
131
|
+
});
|
|
132
|
+
function getCoinMeta(coinType) {
|
|
133
|
+
return BY_TYPE.get(coinType);
|
|
134
|
+
}
|
|
135
|
+
function isInRegistry(coinType) {
|
|
136
|
+
return BY_TYPE.has(coinType);
|
|
137
|
+
}
|
|
109
138
|
function getDecimalsForCoinType(coinType) {
|
|
110
139
|
const direct = BY_TYPE.get(coinType);
|
|
111
140
|
if (direct) return direct.decimals;
|
|
@@ -188,6 +217,136 @@ var init_token_registry = __esm({
|
|
|
188
217
|
}
|
|
189
218
|
});
|
|
190
219
|
|
|
220
|
+
// src/wallet/coinSelection.ts
|
|
221
|
+
var coinSelection_exports = {};
|
|
222
|
+
__export(coinSelection_exports, {
|
|
223
|
+
fetchAllCoins: () => fetchAllCoins,
|
|
224
|
+
selectAndSplitCoin: () => selectAndSplitCoin,
|
|
225
|
+
selectSuiCoin: () => selectSuiCoin
|
|
226
|
+
});
|
|
227
|
+
async function fetchAllCoins(client, owner, coinType) {
|
|
228
|
+
const [balance, ids] = await Promise.all([
|
|
229
|
+
client.core.getBalance({ owner, coinType }),
|
|
230
|
+
(async () => {
|
|
231
|
+
const out = [];
|
|
232
|
+
let cursor;
|
|
233
|
+
let hasNext = true;
|
|
234
|
+
while (hasNext) {
|
|
235
|
+
const page = await client.core.listCoins({ owner, coinType, cursor: cursor ?? void 0 });
|
|
236
|
+
for (const c of page.objects) out.push(c.objectId);
|
|
237
|
+
cursor = page.cursor;
|
|
238
|
+
hasNext = page.hasNextPage;
|
|
239
|
+
}
|
|
240
|
+
return out;
|
|
241
|
+
})()
|
|
242
|
+
]);
|
|
243
|
+
return { ids, totalBalance: BigInt(balance.balance.balance) };
|
|
244
|
+
}
|
|
245
|
+
async function selectAndSplitCoin(tx, client, owner, coinType, amount, options = {}) {
|
|
246
|
+
if (options.sponsoredContext) {
|
|
247
|
+
return selectCoinObjectsOnly(
|
|
248
|
+
tx,
|
|
249
|
+
client,
|
|
250
|
+
owner,
|
|
251
|
+
coinType,
|
|
252
|
+
amount,
|
|
253
|
+
options.allowSwapAll ?? true,
|
|
254
|
+
options.mergeCache
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
const balanceResp = await client.core.getBalance({ owner, coinType });
|
|
258
|
+
const totalBalance = BigInt(balanceResp.balance.balance);
|
|
259
|
+
if (totalBalance === 0n) {
|
|
260
|
+
throw new exports.T2000Error("INSUFFICIENT_BALANCE", `No balance found for ${coinType}`);
|
|
261
|
+
}
|
|
262
|
+
const allowSwapAll = options.allowSwapAll ?? true;
|
|
263
|
+
if (amount !== "all" && amount > totalBalance && !allowSwapAll) {
|
|
264
|
+
throw new exports.T2000Error("INSUFFICIENT_BALANCE", `Insufficient balance for ${coinType}`, {
|
|
265
|
+
available: totalBalance.toString(),
|
|
266
|
+
required: amount.toString()
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
const requested = amount === "all" ? totalBalance : amount;
|
|
270
|
+
const swapAll = amount === "all" || requested >= totalBalance;
|
|
271
|
+
const effectiveAmount = swapAll ? totalBalance : requested;
|
|
272
|
+
const coin = transactions.coinWithBalance({ type: coinType, balance: effectiveAmount })(tx);
|
|
273
|
+
return { coin, effectiveAmount, swapAll };
|
|
274
|
+
}
|
|
275
|
+
async function selectCoinObjectsOnly(tx, client, owner, coinType, amount, allowSwapAll, mergeCache) {
|
|
276
|
+
const cached = mergeCache?.get(coinType);
|
|
277
|
+
if (cached) {
|
|
278
|
+
const requested2 = amount === "all" ? cached.remaining : amount;
|
|
279
|
+
if (cached.remaining === 0n || requested2 > cached.remaining) {
|
|
280
|
+
throw new exports.T2000Error(
|
|
281
|
+
"ADDRESS_BALANCE_UNSPONSORABLE",
|
|
282
|
+
`Not enough ${coinType} in coin objects to cover all legs of this sponsored bundle. The remaining funds are in your address balance, which sponsored transactions can't access yet.`,
|
|
283
|
+
{ remaining: cached.remaining.toString(), requested: requested2.toString(), coinType }
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
const swapAll2 = amount === "all" || requested2 >= cached.remaining;
|
|
287
|
+
const effectiveAmount2 = swapAll2 ? cached.remaining : requested2;
|
|
288
|
+
const coin2 = swapAll2 ? cached.primary : tx.splitCoins(cached.primary, [effectiveAmount2])[0];
|
|
289
|
+
cached.remaining -= effectiveAmount2;
|
|
290
|
+
return { coin: coin2, effectiveAmount: effectiveAmount2, swapAll: swapAll2 };
|
|
291
|
+
}
|
|
292
|
+
const objects = [];
|
|
293
|
+
let coinObjectTotal = 0n;
|
|
294
|
+
let cursor;
|
|
295
|
+
let hasNext = true;
|
|
296
|
+
while (hasNext) {
|
|
297
|
+
const page = await client.core.listCoins({ owner, coinType, cursor: cursor ?? void 0 });
|
|
298
|
+
for (const c of page.objects) {
|
|
299
|
+
objects.push({ objectId: c.objectId, balance: BigInt(c.balance) });
|
|
300
|
+
coinObjectTotal += BigInt(c.balance);
|
|
301
|
+
}
|
|
302
|
+
cursor = page.cursor;
|
|
303
|
+
hasNext = page.hasNextPage;
|
|
304
|
+
}
|
|
305
|
+
const unsponsorable = () => new exports.T2000Error(
|
|
306
|
+
"ADDRESS_BALANCE_UNSPONSORABLE",
|
|
307
|
+
`These funds are in your address balance, which sponsored transactions can't access yet. (Funds received via gasless transfers land there.) This will work once the gas sponsor adds address-balance support.`,
|
|
308
|
+
{ coinObjectTotal: coinObjectTotal.toString(), coinType }
|
|
309
|
+
);
|
|
310
|
+
if (coinObjectTotal === 0n) {
|
|
311
|
+
throw unsponsorable();
|
|
312
|
+
}
|
|
313
|
+
const requested = amount === "all" ? coinObjectTotal : amount;
|
|
314
|
+
if (requested > coinObjectTotal) {
|
|
315
|
+
if (allowSwapAll && amount === "all") ; else {
|
|
316
|
+
throw unsponsorable();
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
const swapAll = amount === "all" || requested >= coinObjectTotal;
|
|
320
|
+
const effectiveAmount = swapAll ? coinObjectTotal : requested;
|
|
321
|
+
const [first, ...rest] = objects;
|
|
322
|
+
const primary = tx.object(first.objectId);
|
|
323
|
+
if (rest.length > 0) {
|
|
324
|
+
tx.mergeCoins(
|
|
325
|
+
primary,
|
|
326
|
+
rest.map((o) => tx.object(o.objectId))
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
const coin = swapAll ? primary : tx.splitCoins(primary, [effectiveAmount])[0];
|
|
330
|
+
mergeCache?.set(coinType, {
|
|
331
|
+
primary,
|
|
332
|
+
remaining: coinObjectTotal - effectiveAmount
|
|
333
|
+
});
|
|
334
|
+
return { coin, effectiveAmount, swapAll };
|
|
335
|
+
}
|
|
336
|
+
async function selectSuiCoin(tx, client, owner, amountMist, sponsoredContext, mergeCache) {
|
|
337
|
+
if (sponsoredContext) {
|
|
338
|
+
const { SUI_TYPE: SUI_TYPE2 } = await Promise.resolve().then(() => (init_token_registry(), token_registry_exports));
|
|
339
|
+
return selectCoinObjectsOnly(tx, client, owner, SUI_TYPE2, amountMist, false, mergeCache);
|
|
340
|
+
}
|
|
341
|
+
const [coin] = tx.splitCoins(tx.gas, [amountMist]);
|
|
342
|
+
return { coin, effectiveAmount: amountMist, swapAll: false };
|
|
343
|
+
}
|
|
344
|
+
var init_coinSelection = __esm({
|
|
345
|
+
"src/wallet/coinSelection.ts"() {
|
|
346
|
+
init_errors();
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
|
|
191
350
|
// src/wallet/keypairSigner.ts
|
|
192
351
|
var KeypairSigner = class {
|
|
193
352
|
constructor(keypair) {
|
|
@@ -247,12 +406,8 @@ var ZkLoginSigner = class {
|
|
|
247
406
|
}
|
|
248
407
|
};
|
|
249
408
|
|
|
250
|
-
// src/
|
|
251
|
-
|
|
252
|
-
const raw = challenge?.request?.amount;
|
|
253
|
-
const n = typeof raw === "string" ? Number(raw) : typeof raw === "number" ? raw : Number.NaN;
|
|
254
|
-
return Number.isFinite(n) ? n : void 0;
|
|
255
|
-
}
|
|
409
|
+
// src/wallet/pay.ts
|
|
410
|
+
init_errors();
|
|
256
411
|
|
|
257
412
|
// src/wallet/executeTx.ts
|
|
258
413
|
async function executeTx(client, signer, buildTx, options = {}) {
|
|
@@ -280,45 +435,122 @@ async function executeTx(client, signer, buildTx, options = {}) {
|
|
|
280
435
|
// src/wallet/pay.ts
|
|
281
436
|
async function payWithMpp(args) {
|
|
282
437
|
const { signer, client, options } = args;
|
|
283
|
-
const { Mppx } = await import('mppx/client');
|
|
284
|
-
const { sui, USDC } = await import('@suimpp/mpp/client');
|
|
285
|
-
const { SuiGrpcClient: SuiGrpcClient2 } = await import('@mysten/sui/grpc');
|
|
286
|
-
const signerAddress = signer.getAddress();
|
|
287
|
-
let paymentDigest;
|
|
288
|
-
let gasCostSui = 0;
|
|
289
|
-
let chargedAmount;
|
|
290
|
-
const network = client.network === "testnet" ? "testnet" : "mainnet";
|
|
291
|
-
const grpcBaseUrl = network === "testnet" ? "https://fullnode.testnet.sui.io" : "https://fullnode.mainnet.sui.io";
|
|
292
|
-
const grpcClient = new SuiGrpcClient2({ baseUrl: grpcBaseUrl, network });
|
|
293
|
-
const mppx = Mppx.create({
|
|
294
|
-
polyfill: false,
|
|
295
|
-
onChallenge: async (challenge) => {
|
|
296
|
-
const parsed = parseChallengeAmount(challenge);
|
|
297
|
-
if (parsed !== void 0) chargedAmount = parsed;
|
|
298
|
-
return void 0;
|
|
299
|
-
},
|
|
300
|
-
methods: [sui({
|
|
301
|
-
client,
|
|
302
|
-
currency: USDC,
|
|
303
|
-
signer: {
|
|
304
|
-
toSuiAddress: () => signerAddress,
|
|
305
|
-
signPersonalMessage: (bytes) => signer.signPersonalMessage(bytes)
|
|
306
|
-
},
|
|
307
|
-
execute: async (tx) => {
|
|
308
|
-
const result = await executeTx(client, signer, () => tx, { buildClient: grpcClient });
|
|
309
|
-
paymentDigest = result.digest;
|
|
310
|
-
gasCostSui = result.gasCostSui;
|
|
311
|
-
return { digest: result.digest };
|
|
312
|
-
}
|
|
313
|
-
})]
|
|
314
|
-
});
|
|
315
438
|
const method = (options.method ?? "GET").toUpperCase();
|
|
316
439
|
const canHaveBody = method !== "GET" && method !== "HEAD";
|
|
317
|
-
const
|
|
440
|
+
const reqInit = {
|
|
318
441
|
method,
|
|
319
442
|
headers: options.headers,
|
|
320
443
|
body: canHaveBody ? options.body : void 0
|
|
444
|
+
};
|
|
445
|
+
const probe = await fetch(options.url, reqInit);
|
|
446
|
+
if (probe.status !== 402) {
|
|
447
|
+
return finalize(probe, { paid: false });
|
|
448
|
+
}
|
|
449
|
+
const requirements = await pickSuiExactRequirements(probe, client.network);
|
|
450
|
+
if (!requirements) {
|
|
451
|
+
throw new exports.T2000Error(
|
|
452
|
+
"FACILITATOR_REJECTION",
|
|
453
|
+
`Endpoint returned 402 without an x402 'exact' / sui:${client.network} payment requirement. This SDK only speaks the x402 dialect.`
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
return payViaX402({ signer, client, options, reqInit, requirements });
|
|
457
|
+
}
|
|
458
|
+
async function pickSuiExactRequirements(response, network) {
|
|
459
|
+
try {
|
|
460
|
+
const body = await response.clone().json();
|
|
461
|
+
const want = `sui:${network === "testnet" ? "testnet" : "mainnet"}`;
|
|
462
|
+
return body.accepts?.find((a) => a.scheme === "exact" && a.network === want);
|
|
463
|
+
} catch {
|
|
464
|
+
return void 0;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
async function payViaX402(args) {
|
|
468
|
+
const { signer, client, options, reqInit, requirements } = args;
|
|
469
|
+
const { buildX402SignedPayment, X402_PAYMENT_HEADER, X402_PAYMENT_RESPONSE_HEADER } = await import('@suimpp/mpp/x402');
|
|
470
|
+
const amountRaw = BigInt(requirements.maxAmountRequired);
|
|
471
|
+
const migrationGasSui = await ensureAddressBalanceCovers({
|
|
472
|
+
signer,
|
|
473
|
+
client,
|
|
474
|
+
asset: requirements.asset,
|
|
475
|
+
amountRaw
|
|
476
|
+
});
|
|
477
|
+
const signerAdapter = {
|
|
478
|
+
toSuiAddress: () => signer.getAddress(),
|
|
479
|
+
signTransaction: (bytes) => signer.signTransaction(bytes)
|
|
480
|
+
};
|
|
481
|
+
const { header } = await buildX402SignedPayment({ requirements, signer: signerAdapter });
|
|
482
|
+
const res = await fetch(options.url, {
|
|
483
|
+
...reqInit,
|
|
484
|
+
headers: { ...options.headers ?? {}, [X402_PAYMENT_HEADER]: header }
|
|
321
485
|
});
|
|
486
|
+
const settleHeader = res.headers.get(X402_PAYMENT_RESPONSE_HEADER);
|
|
487
|
+
const paid = !!settleHeader;
|
|
488
|
+
let digest;
|
|
489
|
+
if (settleHeader) {
|
|
490
|
+
try {
|
|
491
|
+
digest = JSON.parse(new TextDecoder().decode(utils.fromBase64(settleHeader))).transaction;
|
|
492
|
+
} catch {
|
|
493
|
+
digest = void 0;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
const result = await finalize(res, { paid });
|
|
497
|
+
if (!paid) return { ...result, dialect: "x402" };
|
|
498
|
+
return {
|
|
499
|
+
...result,
|
|
500
|
+
dialect: "x402",
|
|
501
|
+
cost: atomicToHuman(amountRaw, await assetDecimals(requirements.asset)),
|
|
502
|
+
gasCostSui: migrationGasSui,
|
|
503
|
+
receipt: digest ? { reference: digest, timestamp: (/* @__PURE__ */ new Date()).toISOString() } : result.receipt
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
async function ensureAddressBalanceCovers(args) {
|
|
507
|
+
const { signer, client, asset, amountRaw } = args;
|
|
508
|
+
const owner = signer.getAddress();
|
|
509
|
+
const balanceResp = await client.core.getBalance({ owner, coinType: asset });
|
|
510
|
+
const total = BigInt(balanceResp.balance.balance);
|
|
511
|
+
if (total < amountRaw) {
|
|
512
|
+
throw new exports.T2000Error("INSUFFICIENT_BALANCE", `Insufficient ${asset} to pay`, {
|
|
513
|
+
available: total.toString(),
|
|
514
|
+
required: amountRaw.toString()
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
let coinSum = 0n;
|
|
518
|
+
let cursor;
|
|
519
|
+
let hasNext = true;
|
|
520
|
+
while (hasNext) {
|
|
521
|
+
const page = await client.core.listCoins({ owner, coinType: asset, cursor: cursor ?? void 0 });
|
|
522
|
+
for (const c of page.objects) coinSum += BigInt(c.balance);
|
|
523
|
+
cursor = page.cursor;
|
|
524
|
+
hasNext = page.hasNextPage;
|
|
525
|
+
}
|
|
526
|
+
const addressBalance = total - coinSum;
|
|
527
|
+
if (addressBalance >= amountRaw) return 0;
|
|
528
|
+
const shortfall = amountRaw - addressBalance;
|
|
529
|
+
const { Transaction: Transaction2 } = await import('@mysten/sui/transactions');
|
|
530
|
+
const { selectAndSplitCoin: selectAndSplitCoin2 } = await Promise.resolve().then(() => (init_coinSelection(), coinSelection_exports));
|
|
531
|
+
const grpcClient = await makeGrpcBuildClient(client);
|
|
532
|
+
const migration = await executeTx(
|
|
533
|
+
client,
|
|
534
|
+
signer,
|
|
535
|
+
async () => {
|
|
536
|
+
const tx = new Transaction2();
|
|
537
|
+
const { coin } = await selectAndSplitCoin2(tx, client, owner, asset, shortfall, {
|
|
538
|
+
sponsoredContext: true,
|
|
539
|
+
// coin-objects-only — never the address balance
|
|
540
|
+
allowSwapAll: false
|
|
541
|
+
});
|
|
542
|
+
tx.moveCall({
|
|
543
|
+
target: "0x2::coin::send_funds",
|
|
544
|
+
typeArguments: [asset],
|
|
545
|
+
arguments: [coin, tx.pure.address(owner)]
|
|
546
|
+
});
|
|
547
|
+
return tx;
|
|
548
|
+
},
|
|
549
|
+
{ buildClient: grpcClient }
|
|
550
|
+
);
|
|
551
|
+
return migration.gasCostSui;
|
|
552
|
+
}
|
|
553
|
+
async function finalize(response, opts) {
|
|
322
554
|
const contentType = response.headers.get("content-type") ?? "";
|
|
323
555
|
let body;
|
|
324
556
|
try {
|
|
@@ -326,15 +558,25 @@ async function payWithMpp(args) {
|
|
|
326
558
|
} catch {
|
|
327
559
|
body = null;
|
|
328
560
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
561
|
+
return { status: response.status, body, paid: opts.paid };
|
|
562
|
+
}
|
|
563
|
+
async function makeGrpcBuildClient(client) {
|
|
564
|
+
const { SuiGrpcClient: SuiGrpcClient2 } = await import('@mysten/sui/grpc');
|
|
565
|
+
const network = client.network === "testnet" ? "testnet" : "mainnet";
|
|
566
|
+
const baseUrl = network === "testnet" ? "https://fullnode.testnet.sui.io" : "https://fullnode.mainnet.sui.io";
|
|
567
|
+
return new SuiGrpcClient2({ baseUrl, network });
|
|
568
|
+
}
|
|
569
|
+
function atomicToHuman(raw, decimals) {
|
|
570
|
+
return Number(raw) / 10 ** decimals;
|
|
571
|
+
}
|
|
572
|
+
async function assetDecimals(coinType) {
|
|
573
|
+
try {
|
|
574
|
+
const { getDecimalsForCoinType: getDecimalsForCoinType2 } = await Promise.resolve().then(() => (init_token_registry(), token_registry_exports));
|
|
575
|
+
const d = getDecimalsForCoinType2(coinType);
|
|
576
|
+
return typeof d === "number" ? d : 6;
|
|
577
|
+
} catch {
|
|
578
|
+
return 6;
|
|
579
|
+
}
|
|
338
580
|
}
|
|
339
581
|
|
|
340
582
|
// src/browser.ts
|
|
@@ -676,7 +918,7 @@ function toBase64(bytes) {
|
|
|
676
918
|
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
677
919
|
return btoa(binary);
|
|
678
920
|
}
|
|
679
|
-
function
|
|
921
|
+
function fromBase642(b64) {
|
|
680
922
|
const binary = atob(b64);
|
|
681
923
|
const bytes = new Uint8Array(binary.length);
|
|
682
924
|
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
@@ -826,7 +1068,7 @@ exports.findSwapRoute = findSwapRoute;
|
|
|
826
1068
|
exports.formatAssetAmount = formatAssetAmount;
|
|
827
1069
|
exports.formatSui = formatSui;
|
|
828
1070
|
exports.formatUsd = formatUsd;
|
|
829
|
-
exports.fromBase64 =
|
|
1071
|
+
exports.fromBase64 = fromBase642;
|
|
830
1072
|
exports.getDecimals = getDecimals;
|
|
831
1073
|
exports.getDecimalsForCoinType = getDecimalsForCoinType;
|
|
832
1074
|
exports.mapMoveAbortCode = mapMoveAbortCode;
|