@upshiftfinance/sdk 0.2.0 → 0.4.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 +1 -1
- package/lib/abis/KycRegistry.d.ts +109 -0
- package/lib/abis/KycRegistry.js +73 -0
- package/lib/abis/index.d.ts +1 -0
- package/lib/abis/index.js +1 -0
- package/lib/adapters/sui/actions.d.ts +174 -0
- package/lib/adapters/sui/actions.js +382 -0
- package/lib/adapters/sui/getters.d.ts +166 -0
- package/lib/adapters/sui/getters.js +391 -0
- package/lib/adapters/sui/index.d.ts +95 -2
- package/lib/adapters/sui/index.js +92 -2
- package/lib/adapters/sui/targets.d.ts +35 -0
- package/lib/adapters/sui/targets.js +35 -0
- package/lib/core/analytics/method-taxonomy.d.ts +1 -1
- package/lib/core/analytics/method-taxonomy.js +7 -0
- package/lib/core/analytics/version.d.ts +1 -1
- package/lib/core/analytics/version.js +1 -1
- package/lib/core/constants/core.d.ts +5 -0
- package/lib/core/constants/core.js +5 -0
- package/lib/core/constants/kyc.d.ts +9 -0
- package/lib/core/constants/kyc.js +12 -0
- package/lib/core/constants/swap-router.js +24 -0
- package/lib/core/constants/web3.d.ts +2 -2
- package/lib/core/constants/web3.js +9 -2
- package/lib/core/errors/index.d.ts +77 -1
- package/lib/core/errors/index.js +94 -1
- package/lib/core/fetcher.d.ts +5 -0
- package/lib/core/fetcher.js +1 -1
- package/lib/core/helpers/chain-support.d.ts +2 -2
- package/lib/core/helpers/chain-support.js +3 -3
- package/lib/core/helpers/kyc.d.ts +47 -0
- package/lib/core/helpers/kyc.js +111 -0
- package/lib/core/helpers/revert-decode.js +3 -2
- package/lib/core/index.d.ts +2 -0
- package/lib/core/index.js +2 -0
- package/lib/main.d.ts +31 -3
- package/lib/main.js +32 -0
- package/lib/modules/api/kyc.d.ts +4 -0
- package/lib/modules/api/kyc.js +148 -0
- package/lib/modules/api/main.d.ts +63 -1
- package/lib/modules/api/main.js +64 -0
- package/lib/modules/vaults/getters.d.ts +3 -1
- package/lib/modules/vaults/getters.js +53 -9
- package/lib/modules/vaults/main.d.ts +39 -3
- package/lib/modules/vaults/main.js +79 -5
- package/lib/modules/vaults/types.d.ts +7 -1
- package/lib/modules/vaults/utils.js +4 -1
- package/lib/sdk.d.ts +15131 -14393
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.js +1 -0
- package/lib/types/kyc.d.ts +61 -0
- package/lib/types/kyc.js +3 -0
- package/lib/types/vaults.d.ts +2 -0
- package/lib/types/webserver.d.ts +5 -0
- package/package.json +1 -1
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.suiExecutionErrorToSdkError = suiExecutionErrorToSdkError;
|
|
37
|
+
exports.buildSuiDepositTransaction = buildSuiDepositTransaction;
|
|
38
|
+
exports.buildSuiRedeemTransaction = buildSuiRedeemTransaction;
|
|
39
|
+
exports.submitSuiTransaction = submitSuiTransaction;
|
|
40
|
+
const errors_1 = require("../../core/errors");
|
|
41
|
+
const chain_address_1 = require("../../core/helpers/chain-address");
|
|
42
|
+
const curator_alert_1 = require("../../core/logger/curator-alert");
|
|
43
|
+
const client_1 = require("./client");
|
|
44
|
+
const constants_1 = require("./constants");
|
|
45
|
+
const getters_1 = require("./getters");
|
|
46
|
+
const targets_1 = require("./targets");
|
|
47
|
+
const U64_MAX = 2n ** 64n - 1n;
|
|
48
|
+
/** Contract errors with a stable SDK reason, keyed `module::constant`. */
|
|
49
|
+
const ABORT_REASONS = {
|
|
50
|
+
'vault::EPaused': 'paused',
|
|
51
|
+
'vault::EBelowMinDeposit': 'below_min_deposit',
|
|
52
|
+
'vault::EAboveMaxDeposit': 'above_max_deposit',
|
|
53
|
+
'vault::EDepositCapExceeded': 'deposit_cap_exceeded',
|
|
54
|
+
'vault::EAboveMaxWithdrawal': 'above_max_withdrawal',
|
|
55
|
+
'vault::EInsufficientLiquidity': 'insufficient_liquidity',
|
|
56
|
+
'vault::EQueueAttached': 'queue_attached',
|
|
57
|
+
'vault::ESlippageExceeded': 'slippage',
|
|
58
|
+
'vault::EZeroShares': 'zero_output',
|
|
59
|
+
'vault::EZeroAssets': 'zero_output',
|
|
60
|
+
'versioning::EVersionRetired': 'version_retired',
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Map a failed Sui execution to an SDK error: a contract abort becomes an
|
|
64
|
+
* {@link UpshiftVaultAbortError} with a stable reason, anything else an
|
|
65
|
+
* {@link UpshiftSDKError} (`UNKNOWN`).
|
|
66
|
+
*
|
|
67
|
+
* @param executionError - `effects.status.error` of a failed transaction, or
|
|
68
|
+
* the `executionError` of a `SimulationError`.
|
|
69
|
+
* @param context - Structured context for the error (e.g. network, digest).
|
|
70
|
+
* @internal
|
|
71
|
+
*/
|
|
72
|
+
function suiExecutionErrorToSdkError(executionError, context = {}) {
|
|
73
|
+
const abort = executionError?.MoveAbort;
|
|
74
|
+
if (abort) {
|
|
75
|
+
const module = abort.location?.module;
|
|
76
|
+
const constant = abort.cleverError?.constantName;
|
|
77
|
+
const reason = ABORT_REASONS[`${module}::${constant}`] ?? 'other';
|
|
78
|
+
return new errors_1.UpshiftVaultAbortError(reason, `Sui vault rejected the transaction: ${reason}${constant ? ` (${module}::${constant})` : ` (abort ${abort.abortCode})`}.`, { module, constant }, { context: { ...context, abortCode: abort.abortCode } });
|
|
79
|
+
}
|
|
80
|
+
return new errors_1.UpshiftSDKError('UNKNOWN', `Sui transaction failed: ${executionError?.message ?? 'unknown error'}`, { context });
|
|
81
|
+
}
|
|
82
|
+
function assertSender(sender, method) {
|
|
83
|
+
if (!(0, chain_address_1.isSuiAddress)(sender)) {
|
|
84
|
+
throw new errors_1.UpshiftValidationError('INVALID_ADDRESS', `${method}: sender must be a Sui address (0x + 64 hex chars).`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function assertAmount(value, method, name, positive) {
|
|
88
|
+
if (value === undefined)
|
|
89
|
+
return;
|
|
90
|
+
if (typeof value !== 'bigint' ||
|
|
91
|
+
value < (positive ? 1n : 0n) ||
|
|
92
|
+
value > U64_MAX) {
|
|
93
|
+
throw new errors_1.UpshiftValidationError('INVALID_INPUT', `${method}: ${name} must be a bigint between ${positive ? 1 : 0} and 2^64 - 1.`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/** Move-call helper bound to one vault and its contracts. */
|
|
97
|
+
function vaultCalls(tx, ref, contracts) {
|
|
98
|
+
const typeArguments = (0, targets_1.suiVaultTypeArguments)(ref.assetCoinType, ref.shareCoinType);
|
|
99
|
+
return (target, args) => tx.moveCall({
|
|
100
|
+
target: (0, targets_1.suiMoveCallTarget)(contracts, target),
|
|
101
|
+
typeArguments,
|
|
102
|
+
arguments: [
|
|
103
|
+
tx.object(ref.vaultId),
|
|
104
|
+
tx.object(contracts.registryId),
|
|
105
|
+
...args,
|
|
106
|
+
],
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Dry-run `tx` against the network and return each command's return values.
|
|
111
|
+
* An abort, at build (gas estimation) or in the simulation, becomes a typed
|
|
112
|
+
* SDK error; `onAbort` sees it first.
|
|
113
|
+
*/
|
|
114
|
+
async function dryRun(config, build, method, onAbort) {
|
|
115
|
+
const context = { network: config.network, method };
|
|
116
|
+
try {
|
|
117
|
+
return await (0, client_1.withSuiClient)((0, client_1.resolveSuiNetworkConfig)(config), async (client) => {
|
|
118
|
+
const result = await client.core.simulateTransaction({
|
|
119
|
+
transaction: await build(),
|
|
120
|
+
include: { commandResults: true, effects: true },
|
|
121
|
+
});
|
|
122
|
+
if (result.$kind !== 'Transaction') {
|
|
123
|
+
throw suiExecutionErrorToSdkError(result.FailedTransaction.effects?.status.error ?? undefined, context);
|
|
124
|
+
}
|
|
125
|
+
return result.commandResults.map((c) => c.returnValues.map((v) => v.bcs));
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
const sdkError = err instanceof errors_1.UpshiftSDKError
|
|
130
|
+
? err
|
|
131
|
+
: err?.executionError
|
|
132
|
+
? suiExecutionErrorToSdkError(err.executionError, context)
|
|
133
|
+
: new errors_1.UpshiftValidationError('INVALID_INPUT', `${method}: could not build the transaction: ${err instanceof Error ? err.message : String(err)}`, { cause: err, context });
|
|
134
|
+
if (sdkError instanceof errors_1.UpshiftVaultAbortError)
|
|
135
|
+
onAbort?.(sdkError);
|
|
136
|
+
throw sdkError;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Build an unsigned deposit: charge the management fee, deposit with a minimum
|
|
141
|
+
* share amount, and send the shares to the sender — one transaction.
|
|
142
|
+
*
|
|
143
|
+
* The payment is `coinWithBalance(Asset)`: the wallet selects and merges the
|
|
144
|
+
* sender's coins when it signs. The fee is charged first so the deposit is
|
|
145
|
+
* priced on a current NAV.
|
|
146
|
+
*
|
|
147
|
+
* RPC cost: one dry run (plus the coin lookup it needs).
|
|
148
|
+
*
|
|
149
|
+
* @param params - Vault, amount, sender and optional minimum shares.
|
|
150
|
+
* @param sui - The SDK's `sui` config; applied only for `params.vault.network`.
|
|
151
|
+
* @returns The unsigned transaction, the shares the dry run minted and the
|
|
152
|
+
* minimum it enforces.
|
|
153
|
+
* @throws {@link UpshiftValidationError} for a malformed sender or vault, an
|
|
154
|
+
* out-of-range amount, unknown contract IDs, or a transaction that cannot be
|
|
155
|
+
* built (e.g. the sender lacks the asset).
|
|
156
|
+
* @throws {@link UpshiftVaultAbortError} when the vault would reject the
|
|
157
|
+
* deposit (`paused`, `below_min_deposit`, `above_max_deposit`,
|
|
158
|
+
* `deposit_cap_exceeded`, `zero_output`, `slippage`, `version_retired`).
|
|
159
|
+
* @throws {@link UpshiftNetworkError} when every Sui endpoint fails.
|
|
160
|
+
* @example
|
|
161
|
+
* ```ts
|
|
162
|
+
* const { transaction } = await buildSuiDepositTransaction(
|
|
163
|
+
* { vault: ref, amount: 1_000_000n, sender: account.address },
|
|
164
|
+
* sdkSuiConfig,
|
|
165
|
+
* );
|
|
166
|
+
* const { digest } = await dAppKit.signAndExecuteTransaction({ transaction });
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
async function buildSuiDepositTransaction(params, sui) {
|
|
170
|
+
const method = 'buildSuiDepositTransaction';
|
|
171
|
+
const { vault: ref, amount, sender, minSharesOut } = params;
|
|
172
|
+
assertSender(sender, method);
|
|
173
|
+
if (!(0, chain_address_1.isSuiAddress)(ref?.vaultId)) {
|
|
174
|
+
throw new errors_1.UpshiftValidationError('INVALID_ADDRESS', `${method}: vault.vaultId must be a Sui object ID.`);
|
|
175
|
+
}
|
|
176
|
+
assertAmount(amount, method, 'amount', true);
|
|
177
|
+
assertAmount(minSharesOut, method, 'minSharesOut', false);
|
|
178
|
+
const config = (0, getters_1.suiConfigForNetwork)(ref.network, sui);
|
|
179
|
+
const contracts = (0, targets_1.resolveSuiVaultContracts)(config);
|
|
180
|
+
const { Transaction: SuiTransaction, coinWithBalance } = await Promise.resolve().then(() => __importStar(require('@mysten/sui/transactions')));
|
|
181
|
+
const compose = (min, withPreview) => {
|
|
182
|
+
const tx = new SuiTransaction();
|
|
183
|
+
tx.setSender(sender);
|
|
184
|
+
const call = vaultCalls(tx, ref, contracts);
|
|
185
|
+
call('chargeManagementFee', [tx.object(constants_1.SUI_CLOCK_ID)]);
|
|
186
|
+
if (withPreview)
|
|
187
|
+
call('previewDeposit', [tx.pure.u64(amount)]);
|
|
188
|
+
const payment = tx.add(coinWithBalance({ type: ref.assetCoinType, balance: amount }));
|
|
189
|
+
const shares = call('depositChecked', [payment, tx.pure.u64(min)]);
|
|
190
|
+
tx.transferObjects([shares], sender);
|
|
191
|
+
return tx;
|
|
192
|
+
};
|
|
193
|
+
// The preview runs after the fee charge, so it matches the deposit exactly.
|
|
194
|
+
const results = await dryRun(config, async () => compose(0n, true), method);
|
|
195
|
+
const expectedShares = (0, getters_1.decodeSuiUint)(results[1][0], 8);
|
|
196
|
+
const min = minSharesOut ?? expectedShares;
|
|
197
|
+
return {
|
|
198
|
+
transaction: compose(min, false),
|
|
199
|
+
expectedShares,
|
|
200
|
+
minSharesOut: min,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Build an unsigned instant redemption: redeem shares with a minimum net
|
|
205
|
+
* asset amount and send the assets to the sender — one transaction.
|
|
206
|
+
*
|
|
207
|
+
* The shares are `coinWithBalance(Share)`. A vault with a withdrawal queue
|
|
208
|
+
* attached has instant redemption off; that fails here with `queue_attached`.
|
|
209
|
+
*
|
|
210
|
+
* Side effect on failure: when the vault rejects the dry run, the failure is
|
|
211
|
+
* reported to that vault's curator (fire-and-forget; see
|
|
212
|
+
* `monitoring.curatorAlerts`).
|
|
213
|
+
*
|
|
214
|
+
* RPC cost: one dry run (plus the coin lookup it needs).
|
|
215
|
+
*
|
|
216
|
+
* @param params - Vault, shares, sender and optional minimum assets.
|
|
217
|
+
* @param sui - The SDK's `sui` config; applied only for `params.vault.network`.
|
|
218
|
+
* @returns The unsigned transaction, the dry run's gross and net assets and
|
|
219
|
+
* the minimum it enforces.
|
|
220
|
+
* @throws {@link UpshiftValidationError} for a malformed sender or vault, an
|
|
221
|
+
* out-of-range amount, unknown contract IDs, or a transaction that cannot be
|
|
222
|
+
* built (e.g. the sender lacks the shares).
|
|
223
|
+
* @throws {@link UpshiftVaultAbortError} when the vault would reject the
|
|
224
|
+
* redemption (`queue_attached`, `paused`, `above_max_withdrawal`,
|
|
225
|
+
* `insufficient_liquidity`, `zero_output`, `slippage`, `version_retired`).
|
|
226
|
+
* @throws {@link UpshiftNetworkError} when every Sui endpoint fails.
|
|
227
|
+
*/
|
|
228
|
+
async function buildSuiRedeemTransaction(params, sui) {
|
|
229
|
+
const method = 'buildSuiRedeemTransaction';
|
|
230
|
+
const { vault: ref, shares, sender, minAssetsOut } = params;
|
|
231
|
+
assertSender(sender, method);
|
|
232
|
+
if (!(0, chain_address_1.isSuiAddress)(ref?.vaultId)) {
|
|
233
|
+
throw new errors_1.UpshiftValidationError('INVALID_ADDRESS', `${method}: vault.vaultId must be a Sui object ID.`);
|
|
234
|
+
}
|
|
235
|
+
assertAmount(shares, method, 'shares', true);
|
|
236
|
+
assertAmount(minAssetsOut, method, 'minAssetsOut', false);
|
|
237
|
+
const config = (0, getters_1.suiConfigForNetwork)(ref.network, sui);
|
|
238
|
+
const contracts = (0, targets_1.resolveSuiVaultContracts)(config);
|
|
239
|
+
const { Transaction: SuiTransaction, coinWithBalance } = await Promise.resolve().then(() => __importStar(require('@mysten/sui/transactions')));
|
|
240
|
+
const compose = (min, withPreview) => {
|
|
241
|
+
const tx = new SuiTransaction();
|
|
242
|
+
tx.setSender(sender);
|
|
243
|
+
const call = vaultCalls(tx, ref, contracts);
|
|
244
|
+
if (withPreview) {
|
|
245
|
+
call('previewRedemption', [tx.pure.u64(shares), tx.pure.bool(true)]);
|
|
246
|
+
}
|
|
247
|
+
const payment = tx.add(coinWithBalance({ type: ref.shareCoinType, balance: shares }));
|
|
248
|
+
const assets = call('redeemChecked', [payment, tx.pure.u64(min)]);
|
|
249
|
+
tx.transferObjects([assets], sender);
|
|
250
|
+
return tx;
|
|
251
|
+
};
|
|
252
|
+
const results = await dryRun(config, async () => compose(0n, true), method, (err) => (0, curator_alert_1.reportRedeemFailure)({
|
|
253
|
+
phase: 'simulation',
|
|
254
|
+
chain: 'sui',
|
|
255
|
+
network: ref.network,
|
|
256
|
+
contractId: ref.vaultId,
|
|
257
|
+
eoa: sender,
|
|
258
|
+
sharesRaw: shares.toString(),
|
|
259
|
+
error: err.message,
|
|
260
|
+
}));
|
|
261
|
+
const expectedGrossAssets = (0, getters_1.decodeSuiUint)(results[0][0], 8);
|
|
262
|
+
const expectedAssets = (0, getters_1.decodeSuiUint)(results[0][1], 8);
|
|
263
|
+
const min = minAssetsOut ?? expectedAssets;
|
|
264
|
+
return {
|
|
265
|
+
transaction: compose(min, false),
|
|
266
|
+
expectedGrossAssets,
|
|
267
|
+
expectedAssets,
|
|
268
|
+
minAssetsOut: min,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* The vault, sender and share amount of an instant redemption in a built
|
|
273
|
+
* transaction, for the curator alert; `null` when it is not one.
|
|
274
|
+
*/
|
|
275
|
+
function redemptionOf(data, fromBase64) {
|
|
276
|
+
for (const command of data.commands) {
|
|
277
|
+
const call = command.MoveCall;
|
|
278
|
+
if (call?.module !== 'coin_entry' || call.function !== 'redeem_checked')
|
|
279
|
+
continue;
|
|
280
|
+
const vaultArg = call.arguments[0];
|
|
281
|
+
const vaultInput = vaultArg?.$kind === 'Input' ? data.inputs[vaultArg.Input] : undefined;
|
|
282
|
+
const vaultId = vaultInput?.Object?.SharedObject?.objectId;
|
|
283
|
+
if (!vaultId)
|
|
284
|
+
return null;
|
|
285
|
+
// The shares are usually split off the sender's coins; read that amount.
|
|
286
|
+
let sharesRaw = '0';
|
|
287
|
+
const coinArg = call.arguments[2];
|
|
288
|
+
const source = coinArg?.$kind === 'NestedResult' || coinArg?.$kind === 'Result'
|
|
289
|
+
? data.commands[coinArg.$kind === 'Result'
|
|
290
|
+
? coinArg.Result
|
|
291
|
+
: coinArg.NestedResult[0]]
|
|
292
|
+
: undefined;
|
|
293
|
+
const amountArg = source?.SplitCoins?.amounts[0];
|
|
294
|
+
const amountInput = amountArg?.$kind === 'Input' ? data.inputs[amountArg.Input] : undefined;
|
|
295
|
+
const bytes = amountInput?.Pure?.bytes;
|
|
296
|
+
if (bytes) {
|
|
297
|
+
const raw = fromBase64(bytes);
|
|
298
|
+
if (raw.length === 8)
|
|
299
|
+
sharesRaw = (0, getters_1.decodeSuiUint)(raw, 8).toString();
|
|
300
|
+
}
|
|
301
|
+
return { vaultId, sharesRaw };
|
|
302
|
+
}
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Execute a signed transaction and wait until the network has indexed it.
|
|
307
|
+
*
|
|
308
|
+
* Side effect on failure: a failed instant redemption is reported to that
|
|
309
|
+
* vault's curator with its digest (fire-and-forget; see
|
|
310
|
+
* `monitoring.curatorAlerts`).
|
|
311
|
+
*
|
|
312
|
+
* RPC cost: one execution plus the confirmation poll.
|
|
313
|
+
*
|
|
314
|
+
* @param signed - Bytes and signature from the wallet's `signTransaction`.
|
|
315
|
+
* @param network - Network the transaction was built for.
|
|
316
|
+
* @param sui - The SDK's `sui` config; applied only for `network`.
|
|
317
|
+
* @returns The transaction digest once it succeeded and is indexed.
|
|
318
|
+
* @throws {@link UpshiftVaultAbortError} when the vault aborted it on chain.
|
|
319
|
+
* @throws {@link UpshiftSDKError} (`UNKNOWN`) when it failed for another
|
|
320
|
+
* reason; `context.digest` names it.
|
|
321
|
+
* @throws {@link UpshiftTransactionError} when it executed but was not
|
|
322
|
+
* indexed in time; check `signature` (the digest) before retrying.
|
|
323
|
+
* @throws {@link UpshiftNetworkError} when every Sui endpoint fails.
|
|
324
|
+
*/
|
|
325
|
+
async function submitSuiTransaction(signed, network, sui) {
|
|
326
|
+
if (typeof signed?.bytes !== 'string' ||
|
|
327
|
+
typeof signed.signature !== 'string') {
|
|
328
|
+
throw new errors_1.UpshiftValidationError('INVALID_INPUT', 'submitSuiTransaction: pass { bytes, signature } from the wallet, both base64.');
|
|
329
|
+
}
|
|
330
|
+
const config = (0, getters_1.suiConfigForNetwork)(network, sui);
|
|
331
|
+
const networkConfig = (0, client_1.resolveSuiNetworkConfig)(config);
|
|
332
|
+
const { Transaction: SuiTransaction } = await Promise.resolve().then(() => __importStar(require('@mysten/sui/transactions')));
|
|
333
|
+
const { fromBase64 } = await Promise.resolve().then(() => __importStar(require('@mysten/sui/utils')));
|
|
334
|
+
const bytes = fromBase64(signed.bytes);
|
|
335
|
+
// Executing the same signed bytes twice is idempotent, so endpoint failover is safe.
|
|
336
|
+
const result = await (0, client_1.withSuiClient)(networkConfig, (client) => client.core.executeTransaction({
|
|
337
|
+
transaction: bytes,
|
|
338
|
+
signatures: [signed.signature],
|
|
339
|
+
include: { effects: true },
|
|
340
|
+
})).catch((err) => {
|
|
341
|
+
if (err instanceof errors_1.UpshiftSDKError)
|
|
342
|
+
throw err;
|
|
343
|
+
// The node refused it before execution: a bad signature, or inputs that
|
|
344
|
+
// changed since signing (rebuild and sign again).
|
|
345
|
+
throw new errors_1.UpshiftSDKError('UNKNOWN', `submitSuiTransaction: the network rejected the transaction: ${err instanceof Error ? err.message : String(err)}`, { cause: err, context: { network } });
|
|
346
|
+
});
|
|
347
|
+
if (result.$kind !== 'Transaction') {
|
|
348
|
+
const failed = result.FailedTransaction;
|
|
349
|
+
const digest = failed.digest;
|
|
350
|
+
const error = suiExecutionErrorToSdkError(failed.effects?.status.error ?? undefined, { network, digest });
|
|
351
|
+
// Gas was still spent: wait (best effort) until the failure is indexed, so
|
|
352
|
+
// a retry built right after this sees the sender's new coin versions.
|
|
353
|
+
await (0, client_1.withSuiClient)(networkConfig, (client) => client.core.waitForTransaction({ result, timeout: 12_000 })).catch(() => undefined);
|
|
354
|
+
const data = SuiTransaction.from(bytes).getData();
|
|
355
|
+
const redemption = redemptionOf(data, fromBase64);
|
|
356
|
+
if (redemption && data.sender) {
|
|
357
|
+
(0, curator_alert_1.reportRedeemFailure)({
|
|
358
|
+
phase: 'submission',
|
|
359
|
+
chain: 'sui',
|
|
360
|
+
network,
|
|
361
|
+
contractId: redemption.vaultId,
|
|
362
|
+
eoa: data.sender,
|
|
363
|
+
sharesRaw: redemption.sharesRaw,
|
|
364
|
+
txHash: digest,
|
|
365
|
+
error: error.message,
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
throw error;
|
|
369
|
+
}
|
|
370
|
+
const digest = result.Transaction.digest;
|
|
371
|
+
try {
|
|
372
|
+
await (0, client_1.withSuiClient)(networkConfig, (client) =>
|
|
373
|
+
// Under the client's 15 s per-call bound, so a slow index is not mistaken
|
|
374
|
+
// for a dead endpoint.
|
|
375
|
+
client.core.waitForTransaction({ result, timeout: 12_000 }));
|
|
376
|
+
}
|
|
377
|
+
catch (err) {
|
|
378
|
+
throw new errors_1.UpshiftTransactionError(`Sui transaction ${digest} executed but was not confirmed in time.`, digest, { cause: err, context: { network } });
|
|
379
|
+
}
|
|
380
|
+
return digest;
|
|
381
|
+
}
|
|
382
|
+
//# sourceMappingURL=actions.js.map
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import type { ISuiConfig, ISuiNetwork, ITokenizedVault, IVault } from '../../types';
|
|
2
|
+
import type { IVaultBaseOptions } from '../../modules/vaults/types';
|
|
3
|
+
/** The vault a read is for: its object ID, coin types and network. */
|
|
4
|
+
export interface ISuiVaultRef {
|
|
5
|
+
/** Vault object ID (`0x` + 64 hex chars). */
|
|
6
|
+
vaultId: string;
|
|
7
|
+
/** Move type of the deposit coin. */
|
|
8
|
+
assetCoinType: string;
|
|
9
|
+
/** Move type of the vault's share coin. */
|
|
10
|
+
shareCoinType: string;
|
|
11
|
+
/** Network the vault lives on. */
|
|
12
|
+
network: ISuiNetwork;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A vault's on-chain state. Amounts are raw base units: assets in the deposit
|
|
16
|
+
* coin's decimals, shares in {@link ISuiVaultState.decimals} (the contract
|
|
17
|
+
* requires the share coin to use the deposit coin's decimals).
|
|
18
|
+
*/
|
|
19
|
+
export interface ISuiVaultState {
|
|
20
|
+
/** Net assets: idle + deployed AUM − collectable fees, floored at 0. */
|
|
21
|
+
totalAssets: bigint;
|
|
22
|
+
/** Minted shares, including shares escrowed in withdrawal requests. */
|
|
23
|
+
totalSupply: bigint;
|
|
24
|
+
/** Asset base units per `10^decimals` shares. */
|
|
25
|
+
sharePrice: bigint;
|
|
26
|
+
/** Share (and deposit coin) decimals. */
|
|
27
|
+
decimals: number;
|
|
28
|
+
/** Deposits, redemptions and new requests abort while paused. */
|
|
29
|
+
paused: boolean;
|
|
30
|
+
/** Whether a withdrawal queue is attached; instant `redeem` is off when it is. */
|
|
31
|
+
queueAttached: boolean;
|
|
32
|
+
/** Smallest accepted deposit, in asset base units. */
|
|
33
|
+
minDeposit: bigint;
|
|
34
|
+
/** Ceiling on total assets after a deposit, in asset base units. */
|
|
35
|
+
depositCap: bigint;
|
|
36
|
+
/** Largest single deposit, in asset base units. */
|
|
37
|
+
maxDepositAmount: bigint;
|
|
38
|
+
/** Largest single gross withdrawal, in asset base units. */
|
|
39
|
+
maxWithdrawalAmount: bigint;
|
|
40
|
+
/** Fee charged by instant `redeem`, in basis points of the gross amount. */
|
|
41
|
+
instantRedemptionFeeBps: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Decode a little-endian BCS unsigned integer of `size` bytes.
|
|
45
|
+
* @internal
|
|
46
|
+
*/
|
|
47
|
+
export declare function decodeSuiUint(bytes: Uint8Array, size: number): bigint;
|
|
48
|
+
/**
|
|
49
|
+
* Decode a BCS `bool`.
|
|
50
|
+
* @internal
|
|
51
|
+
*/
|
|
52
|
+
export declare function decodeSuiBool(bytes: Uint8Array): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Read the instant-redemption fee from the vault object's JSON fields: the fee
|
|
55
|
+
* sits in a nested struct that a transaction cannot return.
|
|
56
|
+
* @internal
|
|
57
|
+
*/
|
|
58
|
+
export declare function parseInstantRedemptionFeeBps(json: unknown): number;
|
|
59
|
+
/**
|
|
60
|
+
* The `sui` config to use for a vault on `network`: the consumer's config when
|
|
61
|
+
* it is for that network, else the network alone (built-in endpoint and
|
|
62
|
+
* contract IDs). A testnet override must never serve a mainnet vault.
|
|
63
|
+
* @internal
|
|
64
|
+
*/
|
|
65
|
+
export declare function suiConfigForNetwork(network: ISuiNetwork, sui?: ISuiConfig): ISuiConfig;
|
|
66
|
+
/**
|
|
67
|
+
* Read a vault's on-chain state.
|
|
68
|
+
*
|
|
69
|
+
* RPC cost: two requests, run in parallel: one transaction simulation and one
|
|
70
|
+
* object read. `getVaults` makes these two per Sui vault.
|
|
71
|
+
*
|
|
72
|
+
* @param ref - The vault and its coin types and network.
|
|
73
|
+
* @param sui - The SDK's `sui` config; applied only when its network matches
|
|
74
|
+
* `ref.network`.
|
|
75
|
+
* @returns The vault's state; see {@link ISuiVaultState} for units.
|
|
76
|
+
* @throws {@link UpshiftValidationError} (`INVALID_ADDRESS`) for a malformed
|
|
77
|
+
* vault ID, (`INVALID_INPUT`) when the contract IDs for the network are not
|
|
78
|
+
* known or the simulation aborts (wrong vault, coin types or package).
|
|
79
|
+
* @throws {@link UpshiftNetworkError} when every Sui endpoint fails.
|
|
80
|
+
* @example
|
|
81
|
+
* ```ts
|
|
82
|
+
* const state = await readSuiVaultState(
|
|
83
|
+
* { vaultId, assetCoinType, shareCoinType, network: 'testnet' },
|
|
84
|
+
* { network: 'testnet', packageId, originalPackageId, registryId },
|
|
85
|
+
* );
|
|
86
|
+
* ```
|
|
87
|
+
*/
|
|
88
|
+
export declare function readSuiVaultState(ref: ISuiVaultRef, sui?: ISuiConfig): Promise<ISuiVaultState>;
|
|
89
|
+
/**
|
|
90
|
+
* Shares a deposit would mint at the current price. Limits are not checked.
|
|
91
|
+
*
|
|
92
|
+
* RPC cost: one transaction simulation.
|
|
93
|
+
*
|
|
94
|
+
* @param ref - The vault and its coin types and network.
|
|
95
|
+
* @param assets - Deposit amount in asset base units.
|
|
96
|
+
* @param sui - The SDK's `sui` config; applied only for `ref.network`.
|
|
97
|
+
* @returns Shares in share base units.
|
|
98
|
+
* @throws {@link UpshiftValidationError} for a malformed vault ID, a negative
|
|
99
|
+
* or out-of-range amount, unknown contract IDs or an aborted simulation.
|
|
100
|
+
* @throws {@link UpshiftNetworkError} when every Sui endpoint fails.
|
|
101
|
+
*/
|
|
102
|
+
export declare function previewSuiDeposit(ref: ISuiVaultRef, assets: bigint, sui?: ISuiConfig): Promise<bigint>;
|
|
103
|
+
/**
|
|
104
|
+
* Assets a redemption of `shares` would pay at the current price.
|
|
105
|
+
*
|
|
106
|
+
* RPC cost: one transaction simulation.
|
|
107
|
+
*
|
|
108
|
+
* @param ref - The vault and its coin types and network.
|
|
109
|
+
* @param shares - Shares to redeem, in share base units.
|
|
110
|
+
* @param isInstant - `true` for instant `redeem` (charges the instant fee),
|
|
111
|
+
* `false` for a queued withdrawal (no fee).
|
|
112
|
+
* @param sui - The SDK's `sui` config; applied only for `ref.network`.
|
|
113
|
+
* @returns `gross` and `net` (after the instant fee) in asset base units.
|
|
114
|
+
* @throws {@link UpshiftValidationError} for a malformed vault ID, a negative
|
|
115
|
+
* or out-of-range amount, unknown contract IDs or an aborted simulation.
|
|
116
|
+
* @throws {@link UpshiftNetworkError} when every Sui endpoint fails.
|
|
117
|
+
*/
|
|
118
|
+
export declare function previewSuiRedemption(ref: ISuiVaultRef, shares: bigint, isInstant: boolean, sui?: ISuiConfig): Promise<{
|
|
119
|
+
gross: bigint;
|
|
120
|
+
net: bigint;
|
|
121
|
+
}>;
|
|
122
|
+
/**
|
|
123
|
+
* A wallet's balance of a share coin: coin objects plus the address balance.
|
|
124
|
+
*
|
|
125
|
+
* RPC cost: one balance read.
|
|
126
|
+
*
|
|
127
|
+
* @param owner - Wallet address (`0x` + 64 hex chars).
|
|
128
|
+
* @param shareCoinType - Move type of the vault's share coin.
|
|
129
|
+
* @param network - Network to read.
|
|
130
|
+
* @param sui - The SDK's `sui` config; applied only for `network`.
|
|
131
|
+
* @returns Shares in share base units.
|
|
132
|
+
* @throws {@link UpshiftValidationError} (`INVALID_ADDRESS`) for a malformed
|
|
133
|
+
* owner.
|
|
134
|
+
* @throws {@link UpshiftNetworkError} when every Sui endpoint fails.
|
|
135
|
+
*/
|
|
136
|
+
export declare function getSuiShareBalance(owner: string, shareCoinType: string, network: ISuiNetwork, sui?: ISuiConfig): Promise<bigint>;
|
|
137
|
+
/**
|
|
138
|
+
* The vault reference for a backend row, or `null` when the row has no usable
|
|
139
|
+
* `sui_vault_metadata`.
|
|
140
|
+
* @internal
|
|
141
|
+
*/
|
|
142
|
+
export declare function suiVaultRefFromRow(row: ITokenizedVault): ISuiVaultRef | null;
|
|
143
|
+
/**
|
|
144
|
+
* Build the SDK's `IVault` for a Sui vault from its backend row and, when the
|
|
145
|
+
* read succeeded, its on-chain state. Without state, total assets fall back
|
|
146
|
+
* to the backend's `latest_reported_tvl`, supply to zero, and the vault is
|
|
147
|
+
* marked deposit-paused so no deposit is offered on unread state.
|
|
148
|
+
* @internal
|
|
149
|
+
*/
|
|
150
|
+
export declare function buildSuiVault(row: ITokenizedVault, state: ISuiVaultState | null): IVault;
|
|
151
|
+
/**
|
|
152
|
+
* Build a Sui vault's `IVault` from its backend row plus on-chain state.
|
|
153
|
+
*
|
|
154
|
+
* A failed read (unknown contract IDs, every endpoint down, an aborted
|
|
155
|
+
* simulation) does not fail the call: the vault is built from the backend row
|
|
156
|
+
* alone (see {@link buildSuiVault}) and a warning is logged.
|
|
157
|
+
*
|
|
158
|
+
* RPC cost: as {@link readSuiVaultState}.
|
|
159
|
+
*
|
|
160
|
+
* @param row - Backend `tokenized_vault` row with `sui_vault_metadata`.
|
|
161
|
+
* @param options - Vault options; `options.sui` carries the SDK's `sui` config.
|
|
162
|
+
* @returns The vault.
|
|
163
|
+
* @throws {@link UpshiftValidationError} (`INVALID_INPUT`) when the row has no
|
|
164
|
+
* usable `sui_vault_metadata` (e.g. a legacy Ember/Gamma Sui vault).
|
|
165
|
+
*/
|
|
166
|
+
export declare function getSuiVault(row: ITokenizedVault, options?: IVaultBaseOptions): Promise<IVault>;
|