@upshiftfinance/sdk 0.3.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.
Files changed (54) hide show
  1. package/README.md +1 -1
  2. package/lib/abis/KycRegistry.d.ts +109 -0
  3. package/lib/abis/KycRegistry.js +73 -0
  4. package/lib/abis/index.d.ts +1 -0
  5. package/lib/abis/index.js +1 -0
  6. package/lib/adapters/sui/actions.d.ts +174 -0
  7. package/lib/adapters/sui/actions.js +382 -0
  8. package/lib/adapters/sui/getters.d.ts +166 -0
  9. package/lib/adapters/sui/getters.js +391 -0
  10. package/lib/adapters/sui/index.d.ts +95 -2
  11. package/lib/adapters/sui/index.js +92 -2
  12. package/lib/adapters/sui/targets.d.ts +35 -0
  13. package/lib/adapters/sui/targets.js +35 -0
  14. package/lib/core/analytics/method-taxonomy.d.ts +1 -1
  15. package/lib/core/analytics/method-taxonomy.js +7 -0
  16. package/lib/core/analytics/version.d.ts +1 -1
  17. package/lib/core/analytics/version.js +1 -1
  18. package/lib/core/constants/core.d.ts +5 -0
  19. package/lib/core/constants/core.js +5 -0
  20. package/lib/core/constants/kyc.d.ts +9 -0
  21. package/lib/core/constants/kyc.js +12 -0
  22. package/lib/core/constants/web3.d.ts +2 -2
  23. package/lib/core/constants/web3.js +9 -2
  24. package/lib/core/errors/index.d.ts +77 -1
  25. package/lib/core/errors/index.js +94 -1
  26. package/lib/core/fetcher.d.ts +5 -0
  27. package/lib/core/fetcher.js +1 -1
  28. package/lib/core/helpers/chain-support.d.ts +2 -2
  29. package/lib/core/helpers/chain-support.js +3 -3
  30. package/lib/core/helpers/kyc.d.ts +47 -0
  31. package/lib/core/helpers/kyc.js +111 -0
  32. package/lib/core/helpers/revert-decode.js +3 -2
  33. package/lib/core/index.d.ts +2 -0
  34. package/lib/core/index.js +2 -0
  35. package/lib/main.d.ts +31 -3
  36. package/lib/main.js +32 -0
  37. package/lib/modules/api/kyc.d.ts +4 -0
  38. package/lib/modules/api/kyc.js +148 -0
  39. package/lib/modules/api/main.d.ts +63 -1
  40. package/lib/modules/api/main.js +64 -0
  41. package/lib/modules/vaults/getters.d.ts +3 -1
  42. package/lib/modules/vaults/getters.js +53 -9
  43. package/lib/modules/vaults/main.d.ts +39 -3
  44. package/lib/modules/vaults/main.js +79 -5
  45. package/lib/modules/vaults/types.d.ts +7 -1
  46. package/lib/modules/vaults/utils.js +4 -1
  47. package/lib/sdk.d.ts +15131 -14393
  48. package/lib/types/index.d.ts +1 -0
  49. package/lib/types/index.js +1 -0
  50. package/lib/types/kyc.d.ts +61 -0
  51. package/lib/types/kyc.js +3 -0
  52. package/lib/types/vaults.d.ts +2 -0
  53. package/lib/types/webserver.d.ts +5 -0
  54. package/package.json +1 -1
@@ -139,6 +139,41 @@ exports.SUI_VAULT_TARGETS = {
139
139
  function: 'preview_redemption',
140
140
  params: ['&Vault<Asset, Share>', '&Registry', 'u64', 'bool'],
141
141
  },
142
+ decimals: {
143
+ module: 'vault',
144
+ function: 'decimals',
145
+ params: ['&Vault<Asset, Share>'],
146
+ },
147
+ paused: {
148
+ module: 'vault',
149
+ function: 'paused',
150
+ params: ['&Vault<Asset, Share>'],
151
+ },
152
+ queueAttached: {
153
+ module: 'vault',
154
+ function: 'queue_attached',
155
+ params: ['&Vault<Asset, Share>'],
156
+ },
157
+ minDeposit: {
158
+ module: 'vault',
159
+ function: 'min_deposit',
160
+ params: ['&Vault<Asset, Share>'],
161
+ },
162
+ depositCap: {
163
+ module: 'vault',
164
+ function: 'deposit_cap',
165
+ params: ['&Vault<Asset, Share>'],
166
+ },
167
+ maxDepositAmount: {
168
+ module: 'vault',
169
+ function: 'max_deposit_amount',
170
+ params: ['&Vault<Asset, Share>'],
171
+ },
172
+ maxWithdrawalAmount: {
173
+ module: 'vault',
174
+ function: 'max_withdrawal_amount',
175
+ params: ['&Vault<Asset, Share>'],
176
+ },
142
177
  };
143
178
  /**
144
179
  * Resolve the Upshift vault contract IDs for a Sui config: each `sui` override
@@ -3,7 +3,7 @@
3
3
  * Sentry tag so the partner-usage dashboard can roll calls up into
4
4
  * `read.*` / `write.*` / `network` / `bridge` buckets.
5
5
  */
6
- export type MethodCategory = 'read.vault' | 'read.position' | 'read.redemption' | 'read.price' | 'read.points' | 'read.subaccount' | 'read.staking' | 'read.layerzero' | 'read.solana' | 'read.stellar' | 'read.sui' | 'read.evm' | 'write.deposit' | 'write.redeem' | 'write.approve' | 'write.points' | 'write.stellar' | 'network' | 'bridge' | 'unknown';
6
+ export type MethodCategory = 'read.vault' | 'read.position' | 'read.redemption' | 'read.price' | 'read.points' | 'read.subaccount' | 'read.staking' | 'read.layerzero' | 'read.solana' | 'read.stellar' | 'read.sui' | 'read.evm' | 'write.deposit' | 'write.redeem' | 'write.approve' | 'write.points' | 'write.stellar' | 'write.sui' | 'read.kyc' | 'write.kyc' | 'network' | 'bridge' | 'unknown';
7
7
  /**
8
8
  * Map of public SDK method names to their {@link MethodCategory}.
9
9
  *
@@ -113,9 +113,16 @@ exports.METHOD_CATEGORIES = {
113
113
  getExplorerLink: 'read.stellar',
114
114
  isSuiAddress: 'read.sui',
115
115
  isSuiVault: 'read.sui',
116
+ // Broadcasts a signed Sui transaction, like Stellar's submitTransaction.
117
+ executeTransaction: 'write.sui',
116
118
  vaultAllowance: 'read.evm',
117
119
  sendersWhitelistAddress: 'read.evm',
118
120
  isWhitelisted: 'read.evm',
121
+ getKycStatus: 'read.evm',
122
+ getKycRecord: 'read.evm',
123
+ getKycChallenge: 'read.kyc',
124
+ createKycSession: 'write.kyc',
125
+ refreshKycSession: 'write.kyc',
119
126
  getDeposited: 'read.evm',
120
127
  getRemainingAllocations: 'read.evm',
121
128
  previewDeposit: 'read.vault',
@@ -3,4 +3,4 @@
3
3
  * Generated at build time from package.json version
4
4
  * This file is gitignored and regenerated by every build
5
5
  */
6
- export declare const SDK_VERSION = "0.3.0";
6
+ export declare const SDK_VERSION = "0.4.0";
@@ -6,5 +6,5 @@ exports.SDK_VERSION = void 0;
6
6
  * Generated at build time from package.json version
7
7
  * This file is gitignored and regenerated by every build
8
8
  */
9
- exports.SDK_VERSION = '0.3.0';
9
+ exports.SDK_VERSION = '0.4.0';
10
10
  //# sourceMappingURL=version.js.map
@@ -101,6 +101,11 @@ export declare const WEBSERVER_ENDPOINTS: {
101
101
  }) => string;
102
102
  };
103
103
  public: {
104
+ kyc: {
105
+ challenge: (wallet: IAddress, chainId: number) => string;
106
+ session: string;
107
+ sessionRefresh: string;
108
+ };
104
109
  integrations: {
105
110
  morpho: {
106
111
  apy: (subaccount: IAddress, vaultAddress: IAddress) => string;
@@ -168,6 +168,11 @@ exports.WEBSERVER_ENDPOINTS = {
168
168
  },
169
169
  },
170
170
  public: {
171
+ kyc: {
172
+ challenge: (wallet, chainId) => `/kyc/challenge?${new URLSearchParams({ wallet, chain_id: String(chainId) }).toString()}`,
173
+ session: '/kyc/session',
174
+ sessionRefresh: '/kyc/session/refresh',
175
+ },
171
176
  integrations: {
172
177
  morpho: {
173
178
  apy: (subaccount, vaultAddress) => `/integrations/morpho/apy?subaccount_address=${subaccountSegment(subaccount)}&vault_address=${(0, ethers_1.getAddress)(vaultAddress)}`,
@@ -0,0 +1,9 @@
1
+ import type { IAddress } from '../../types';
2
+ /**
3
+ * Address of the `KycRegistry` proxy on each chain.
4
+ *
5
+ * Missing keys mean no registry is deployed on that chain, so no vault there
6
+ * can gate on KYC and the KYC reads reject the chain. Empty until the first
7
+ * KYC vault launches.
8
+ */
9
+ export declare const KYC_REGISTRY_ADDRESSES: Readonly<Record<number, IAddress>>;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KYC_REGISTRY_ADDRESSES = void 0;
4
+ /**
5
+ * Address of the `KycRegistry` proxy on each chain.
6
+ *
7
+ * Missing keys mean no registry is deployed on that chain, so no vault there
8
+ * can gate on KYC and the KYC reads reject the chain. Empty until the first
9
+ * KYC vault launches.
10
+ */
11
+ exports.KYC_REGISTRY_ADDRESSES = {};
12
+ //# sourceMappingURL=kyc.js.map
@@ -46,7 +46,7 @@ export declare const NATIVE_ADDRESS = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEe
46
46
  * Decimal precision of the native gas token on every EVM chain this SDK
47
47
  * supports (ETH, Base, Arbitrum, Avalanche, Polygon, BNB, HyperEVM, Unichain,
48
48
  * Mezo, Monad, Plasma, Ink, Flare, Katana, Citrea, Fluent, Tempo, X Layer —
49
- * whose native OKB is also 18 decimals).
49
+ * whose native OKB is also 18 decimals — and RISE).
50
50
  *
51
51
  * All of them use 18-decimal native tokens, so this is a single constant rather
52
52
  * than a per-chain map. It exists because the native token has no ERC-20
@@ -88,7 +88,7 @@ export declare const MULTICALL3_ADDRESS = "0xcA11bde05977b3631167028862bE2a17397
88
88
  * Chains where the canonical Multicall3 deployment was **verified on-chain**
89
89
  * (`eth_getCode` returned the 3808-byte runtime at {@link MULTICALL3_ADDRESS};
90
90
  * checked per chain on 2026-07-14, Tempo checked separately on 2026-07-15,
91
- * X Layer on 2026-08-11) —
91
+ * X Layer on 2026-08-11, RISE on 2026-11-02) —
92
92
  * deterministic-deployer presence is NOT assumed. Chains outside this set
93
93
  * keep the per-call read path.
94
94
  *
@@ -54,7 +54,7 @@ exports.NATIVE_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
54
54
  * Decimal precision of the native gas token on every EVM chain this SDK
55
55
  * supports (ETH, Base, Arbitrum, Avalanche, Polygon, BNB, HyperEVM, Unichain,
56
56
  * Mezo, Monad, Plasma, Ink, Flare, Katana, Citrea, Fluent, Tempo, X Layer —
57
- * whose native OKB is also 18 decimals).
57
+ * whose native OKB is also 18 decimals — and RISE).
58
58
  *
59
59
  * All of them use 18-decimal native tokens, so this is a single constant rather
60
60
  * than a per-chain map. It exists because the native token has no ERC-20
@@ -179,6 +179,11 @@ exports.NETWORKS = {
179
179
  chainId: 196,
180
180
  explorer: 'https://xlayerscan.com',
181
181
  },
182
+ 4153: {
183
+ name: 'RISE',
184
+ chainId: 4153,
185
+ explorer: 'https://explorer.risechain.com',
186
+ },
182
187
  };
183
188
  exports.AVAILABLE_CHAINS = Object.keys(exports.NETWORKS).map((c) => Number(c));
184
189
  /**
@@ -191,7 +196,7 @@ exports.MULTICALL3_ADDRESS = '0xcA11bde05977b3631167028862bE2a173976CA11';
191
196
  * Chains where the canonical Multicall3 deployment was **verified on-chain**
192
197
  * (`eth_getCode` returned the 3808-byte runtime at {@link MULTICALL3_ADDRESS};
193
198
  * checked per chain on 2026-07-14, Tempo checked separately on 2026-07-15,
194
- * X Layer on 2026-08-11) —
199
+ * X Layer on 2026-08-11, RISE on 2026-11-02) —
195
200
  * deterministic-deployer presence is NOT assumed. Chains outside this set
196
201
  * keep the per-call read path.
197
202
  *
@@ -218,6 +223,7 @@ exports.MULTICALL3_VERIFIED_CHAINS = new Set([
218
223
  25363, // Fluent
219
224
  4217, // Tempo
220
225
  196, // X Layer
226
+ 4153, // RISE
221
227
  ]);
222
228
  /**
223
229
  * Fallbacks
@@ -247,5 +253,6 @@ exports.FALLBACK_RPC_URLS = {
247
253
  25363: ['https://rpc.fluent.xyz'],
248
254
  4217: ['https://rpc.mainnet.tempo.xyz'],
249
255
  196: ['https://rpc.xlayer.tech'],
256
+ 4153: ['https://rpc.risechain.com'],
250
257
  };
251
258
  //# sourceMappingURL=web3.js.map
@@ -15,7 +15,7 @@
15
15
  * ```
16
16
  */
17
17
  /** Stable error codes attached to SDK errors. */
18
- export type UpshiftErrorCode = 'AUTH_MISSING_KEY' | 'AUTH_INVALID_KEY' | 'AUTH_FORBIDDEN' | 'AUTH_UNAUTHORIZED' | 'INVALID_URL' | 'INVALID_INPUT' | 'INVALID_CHAIN' | 'INVALID_ADDRESS' | 'ACCOUNT_NOT_FUNDED' | 'NO_SIGNER' | 'NETWORK_ERROR' | 'TIMEOUT' | 'RATE_LIMITED' | 'SERVER_ERROR' | 'TRANSACTION_UNCONFIRMED' | 'HISTORY_UNAVAILABLE' | 'PRICE_UNAVAILABLE' | 'UNKNOWN';
18
+ export type UpshiftErrorCode = 'AUTH_MISSING_KEY' | 'AUTH_INVALID_KEY' | 'AUTH_FORBIDDEN' | 'AUTH_UNAUTHORIZED' | 'INVALID_URL' | 'INVALID_INPUT' | 'INVALID_CHAIN' | 'INVALID_ADDRESS' | 'ACCOUNT_NOT_FUNDED' | 'NO_SIGNER' | 'NETWORK_ERROR' | 'TIMEOUT' | 'RATE_LIMITED' | 'SERVER_ERROR' | 'TRANSACTION_UNCONFIRMED' | 'VAULT_ABORTED' | 'HISTORY_UNAVAILABLE' | 'PRICE_UNAVAILABLE' | 'UNKNOWN';
19
19
  /** Options accepted by every SDK error constructor. */
20
20
  export interface UpshiftErrorOptions {
21
21
  /** Original error preserved for stack-trace chaining. */
@@ -72,6 +72,29 @@ export declare class UpshiftServerError extends UpshiftSDKError {
72
72
  constructor(status: number, message: string, options?: UpshiftErrorOptions);
73
73
  toJSON(): Record<string, unknown>;
74
74
  }
75
+ /** Why a KYC API call failed, parsed from the backend's fixed `detail` sentence. */
76
+ export type UpshiftKycErrorReason = 'challenge_expired' | 'challenge_used' | 'invalid_signature' | 'session_expired' | 'identity_blocked' | 'wallet_bound' | 'wallet_limit' | 'other_chain' | 'other_provider' | 'not_verified_for_chain' | 'enrolment_in_progress' | 'registry_write_in_progress' | 'enrolment_timed_out' | 'no_registry' | 'rate_limited' | 'invalid_request' | 'unavailable' | 'unknown';
77
+ /**
78
+ * A KYC API call (`/kyc/challenge`, `/kyc/session`, `/kyc/session/refresh`)
79
+ * returned a non-2xx status.
80
+ *
81
+ * `retryable` means the same action may succeed shortly, but only with a new
82
+ * challenge: a session nonce is single use.
83
+ */
84
+ export declare class UpshiftKycError extends UpshiftSDKError {
85
+ /** HTTP status code returned by the backend. */
86
+ readonly status: number;
87
+ /** The backend's `detail` sentence, or `''` when the body had none. */
88
+ readonly detail: string;
89
+ readonly reason: UpshiftKycErrorReason;
90
+ readonly retryable: boolean;
91
+ /** From `Retry-After` on a 429. */
92
+ readonly retryAfterMs?: number;
93
+ constructor(status: number, detail: string, reason: UpshiftKycErrorReason, options?: UpshiftErrorOptions & {
94
+ retryAfterMs?: number;
95
+ });
96
+ toJSON(): Record<string, unknown>;
97
+ }
75
98
  /**
76
99
  * Transaction/position history required to compute a value could not be
77
100
  * retrieved or was empty, so the computed result would be wrong rather than
@@ -173,5 +196,58 @@ export declare class UpshiftTransactionError extends UpshiftSDKError {
173
196
  constructor(message: string, signature: string, options?: UpshiftErrorOptions);
174
197
  toJSON(): Record<string, unknown>;
175
198
  }
199
+ /**
200
+ * Why a vault contract rejected a transaction.
201
+ *
202
+ * - `paused` — the vault is paused.
203
+ * - `below_min_deposit` / `above_max_deposit` — the deposit is outside the
204
+ * vault's per-call bounds.
205
+ * - `deposit_cap_exceeded` — the deposit would take total assets past the cap.
206
+ * - `above_max_withdrawal` — the redemption's gross assets exceed the per-call
207
+ * maximum.
208
+ * - `insufficient_liquidity` — the vault's idle assets cannot pay it now.
209
+ * - `queue_attached` — instant redemption is off; the vault uses a
210
+ * withdrawal queue.
211
+ * - `slippage` — the output is below the minimum the transaction set.
212
+ * - `zero_output` — the amount is too small to mint shares or pay assets.
213
+ * - `version_retired` — the vault package version is retired; upgrade the SDK.
214
+ * - `other` — any other abort; see `module` and `constant`.
215
+ */
216
+ export type UpshiftVaultAbortReason = 'paused' | 'below_min_deposit' | 'above_max_deposit' | 'deposit_cap_exceeded' | 'above_max_withdrawal' | 'insufficient_liquidity' | 'queue_attached' | 'slippage' | 'zero_output' | 'version_retired' | 'other';
217
+ /**
218
+ * A vault contract rejected a transaction, in a dry run before signing or on
219
+ * chain. `reason` is stable to branch on; `module` and `constant` name the
220
+ * contract error.
221
+ *
222
+ * @example
223
+ * ```ts
224
+ * try {
225
+ * await sdk.sui.vaultRedeem(params);
226
+ * } catch (err) {
227
+ * if (err instanceof UpshiftVaultAbortError && err.reason === 'queue_attached') {
228
+ * // offer a withdrawal request instead
229
+ * }
230
+ * }
231
+ * ```
232
+ */
233
+ export declare class UpshiftVaultAbortError extends UpshiftSDKError {
234
+ /** Why the contract rejected the transaction. */
235
+ readonly reason: UpshiftVaultAbortReason;
236
+ /** Contract module that aborted, e.g. `vault`. */
237
+ readonly module?: string;
238
+ /** Contract error constant, e.g. `EPaused`. */
239
+ readonly constant?: string;
240
+ /**
241
+ * @param reason - Why the contract rejected the transaction.
242
+ * @param message - Human-readable description.
243
+ * @param abort - The aborting module and error constant, when known.
244
+ * @param options - Standard SDK error options.
245
+ */
246
+ constructor(reason: UpshiftVaultAbortReason, message: string, abort?: {
247
+ module?: string;
248
+ constant?: string;
249
+ }, options?: UpshiftErrorOptions);
250
+ toJSON(): Record<string, unknown>;
251
+ }
176
252
  /** Type guard. Works across realms (e.g. Web Worker / VM contexts). */
177
253
  export declare function isUpshiftSDKError(err: unknown): err is UpshiftSDKError;
@@ -16,7 +16,7 @@
16
16
  * ```
17
17
  */
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.UpshiftTransactionError = exports.UpshiftPriceUnavailableError = exports.UpshiftHistoryUnavailableError = exports.UpshiftServerError = exports.UpshiftRateLimitError = exports.UpshiftValidationError = exports.UpshiftTimeoutError = exports.UpshiftNetworkError = exports.UpshiftAuthError = exports.UpshiftSDKError = void 0;
19
+ exports.UpshiftVaultAbortError = exports.UpshiftTransactionError = exports.UpshiftPriceUnavailableError = exports.UpshiftHistoryUnavailableError = exports.UpshiftKycError = exports.UpshiftServerError = exports.UpshiftRateLimitError = exports.UpshiftValidationError = exports.UpshiftTimeoutError = exports.UpshiftNetworkError = exports.UpshiftAuthError = exports.UpshiftSDKError = void 0;
20
20
  exports.isUpshiftSDKError = isUpshiftSDKError;
21
21
  /** Base class for all SDK errors. */
22
22
  class UpshiftSDKError extends Error {
@@ -131,6 +131,53 @@ class UpshiftServerError extends UpshiftSDKError {
131
131
  }
132
132
  }
133
133
  exports.UpshiftServerError = UpshiftServerError;
134
+ /**
135
+ * A KYC API call (`/kyc/challenge`, `/kyc/session`, `/kyc/session/refresh`)
136
+ * returned a non-2xx status.
137
+ *
138
+ * `retryable` means the same action may succeed shortly, but only with a new
139
+ * challenge: a session nonce is single use.
140
+ */
141
+ class UpshiftKycError extends UpshiftSDKError {
142
+ /** HTTP status code returned by the backend. */
143
+ status;
144
+ /** The backend's `detail` sentence, or `''` when the body had none. */
145
+ detail;
146
+ reason;
147
+ retryable;
148
+ /** From `Retry-After` on a 429. */
149
+ retryAfterMs;
150
+ constructor(status, detail, reason, options = {}) {
151
+ super(status === 401
152
+ ? 'AUTH_UNAUTHORIZED'
153
+ : status === 403
154
+ ? 'AUTH_FORBIDDEN'
155
+ : status === 429
156
+ ? 'RATE_LIMITED'
157
+ : status >= 500
158
+ ? 'SERVER_ERROR'
159
+ : 'INVALID_INPUT', `KYC request failed (${status}): ${detail || reason}`, options);
160
+ this.name = 'UpshiftKycError';
161
+ this.status = status;
162
+ this.detail = detail;
163
+ this.reason = reason;
164
+ this.retryable =
165
+ reason === 'enrolment_in_progress' ||
166
+ reason === 'registry_write_in_progress' ||
167
+ reason === 'enrolment_timed_out';
168
+ this.retryAfterMs = options.retryAfterMs;
169
+ }
170
+ toJSON() {
171
+ return {
172
+ ...super.toJSON(),
173
+ status: this.status,
174
+ detail: this.detail,
175
+ reason: this.reason,
176
+ retryable: this.retryable,
177
+ };
178
+ }
179
+ }
180
+ exports.UpshiftKycError = UpshiftKycError;
134
181
  /**
135
182
  * Transaction/position history required to compute a value could not be
136
183
  * retrieved or was empty, so the computed result would be wrong rather than
@@ -247,6 +294,52 @@ class UpshiftTransactionError extends UpshiftSDKError {
247
294
  }
248
295
  }
249
296
  exports.UpshiftTransactionError = UpshiftTransactionError;
297
+ /**
298
+ * A vault contract rejected a transaction, in a dry run before signing or on
299
+ * chain. `reason` is stable to branch on; `module` and `constant` name the
300
+ * contract error.
301
+ *
302
+ * @example
303
+ * ```ts
304
+ * try {
305
+ * await sdk.sui.vaultRedeem(params);
306
+ * } catch (err) {
307
+ * if (err instanceof UpshiftVaultAbortError && err.reason === 'queue_attached') {
308
+ * // offer a withdrawal request instead
309
+ * }
310
+ * }
311
+ * ```
312
+ */
313
+ class UpshiftVaultAbortError extends UpshiftSDKError {
314
+ /** Why the contract rejected the transaction. */
315
+ reason;
316
+ /** Contract module that aborted, e.g. `vault`. */
317
+ module;
318
+ /** Contract error constant, e.g. `EPaused`. */
319
+ constant;
320
+ /**
321
+ * @param reason - Why the contract rejected the transaction.
322
+ * @param message - Human-readable description.
323
+ * @param abort - The aborting module and error constant, when known.
324
+ * @param options - Standard SDK error options.
325
+ */
326
+ constructor(reason, message, abort = {}, options) {
327
+ super('VAULT_ABORTED', message, options);
328
+ this.name = 'UpshiftVaultAbortError';
329
+ this.reason = reason;
330
+ this.module = abort.module;
331
+ this.constant = abort.constant;
332
+ }
333
+ toJSON() {
334
+ return {
335
+ ...super.toJSON(),
336
+ reason: this.reason,
337
+ module: this.module,
338
+ constant: this.constant,
339
+ };
340
+ }
341
+ }
342
+ exports.UpshiftVaultAbortError = UpshiftVaultAbortError;
250
343
  /** Type guard. Works across realms (e.g. Web Worker / VM contexts). */
251
344
  function isUpshiftSDKError(err) {
252
345
  return (err instanceof UpshiftSDKError ||
@@ -20,6 +20,11 @@ export type IFetchUpshiftOptions = {
20
20
  signal?: AbortSignal;
21
21
  /** Per-request timeout in ms. Defaults to `REQUEST_TIMEOUT_MS`. */
22
22
  timeoutMs?: number;
23
+ /**
24
+ * `fetchUpshiftPublic` only: return a non-2xx response instead of throwing,
25
+ * so the caller can read the error body.
26
+ */
27
+ returnErrorResponse?: boolean;
23
28
  /**
24
29
  * @deprecated No longer has any effect: an absent `apiKey` already sends an
25
30
  * anonymous request. Will be removed in a future major version; drop it.
@@ -529,7 +529,7 @@ async function fetchUpshiftPublic(relativeUrl, options) {
529
529
  tracked = true;
530
530
  const correlationId = readResponseHeader(res.headers, 'x-correlation-id');
531
531
  const logger = logger_1.Logger.getLogger();
532
- if (!res.ok) {
532
+ if (!res.ok && !options?.returnErrorResponse) {
533
533
  if (correlationId && logger) {
534
534
  logger.setTag('correlation_id', correlationId);
535
535
  }
@@ -79,8 +79,8 @@ export declare function assertKnownChainId(chainId: number | undefined, provider
79
79
  */
80
80
  export declare function assertEvmProviderConfigured(chainId: number | undefined, providers: Partial<Record<number, string>> | undefined, method: string): void;
81
81
  /**
82
- * Throw when a vault read targets Sui, whose vault reads this SDK does not
83
- * implement yet: either `chainId` is Sui's (`-2`) or `vault` is a Sui object ID
82
+ * Throw when a read that Sui vaults do not support (loans, allocations)
83
+ * targets Sui: either `chainId` is Sui's (`-2`) or `vault` is a Sui object ID
84
84
  * (`0x` + 64 hex chars), with or without a `chainId`.
85
85
  *
86
86
  * Call it before any EVM address check. Sui (`-2`) is a known chain, so it
@@ -115,8 +115,8 @@ function assertEvmProviderConfigured(chainId, providers, method) {
115
115
  `new UpshiftSDK({ providers: { ${chainId}: '<rpc-url>' } }).`, { context: { method, chainId } });
116
116
  }
117
117
  /**
118
- * Throw when a vault read targets Sui, whose vault reads this SDK does not
119
- * implement yet: either `chainId` is Sui's (`-2`) or `vault` is a Sui object ID
118
+ * Throw when a read that Sui vaults do not support (loans, allocations)
119
+ * targets Sui: either `chainId` is Sui's (`-2`) or `vault` is a Sui object ID
120
120
  * (`0x` + 64 hex chars), with or without a `chainId`.
121
121
  *
122
122
  * Call it before any EVM address check. Sui (`-2`) is a known chain, so it
@@ -134,6 +134,6 @@ function assertEvmProviderConfigured(chainId, providers, method) {
134
134
  function assertSuiReadsSupported(chainId, vault, method) {
135
135
  if (chainId !== web3_1.SUI_CHAIN_ID && !(vault && (0, chain_address_1.isSuiAddress)(vault)))
136
136
  return;
137
- throw new errors_1.UpshiftValidationError('INVALID_CHAIN', `${method}: Sui vault reads are not supported yet (chainId ${web3_1.SUI_CHAIN_ID}).`, { context: { method, chainId } });
137
+ throw new errors_1.UpshiftValidationError('INVALID_CHAIN', `${method} is not supported for Sui vaults (chainId ${web3_1.SUI_CHAIN_ID}).`, { context: { method, chainId } });
138
138
  }
139
139
  //# sourceMappingURL=chain-support.js.map
@@ -0,0 +1,47 @@
1
+ import type { IKycRecord, IKycStatus, IKycWalletOptions } from '../../types';
2
+ /**
3
+ * Read a wallet's KYC status from the chain's `KycRegistry` (`statusOf`).
4
+ *
5
+ * The registry decides whether a KYC vault accepts a deposit, so read it
6
+ * on-chain rather than trusting a cached backend status. `expired` covers both
7
+ * a lapsed and a revoked record; tell them apart with {@link getKycRecord}.
8
+ * Not cached: an approval lands when the registry writer sends it, and callers
9
+ * poll for it.
10
+ *
11
+ * @param options - {@link IKycWalletOptions}.
12
+ * @returns The wallet's {@link IKycStatus}.
13
+ * @throws {@link UpshiftValidationError} `INVALID_CHAIN` when no registry is
14
+ * deployed on `chainId` (see {@link KYC_REGISTRY_ADDRESSES}),
15
+ * `INVALID_ADDRESS` when `wallet` is not an EVM address.
16
+ * @throws {@link UpshiftSDKError} when the `statusOf` read fails or returns a
17
+ * status this SDK version does not know.
18
+ * @worstCaseRpcCalls 1 — one `statusOf(address)` call.
19
+ * @example
20
+ * ```ts
21
+ * const status = await getKycStatus({ chainId: 1, wallet, rpcUrl });
22
+ * if (status !== 'active') showVerifyPrompt();
23
+ * ```
24
+ */
25
+ export declare function getKycStatus({ chainId, wallet, rpcUrl, }: IKycWalletOptions): Promise<IKycStatus>;
26
+ /**
27
+ * Read a wallet's raw record from the chain's `KycRegistry` (`getRecord`).
28
+ *
29
+ * Use it with {@link getKycStatus} to show when access ends (`expiresAt`) and
30
+ * to tell a lapsed record (`expiresAt` in the past) from a revoked one. Not
31
+ * cached, for the same reason as {@link getKycStatus}.
32
+ *
33
+ * @param options - {@link IKycWalletOptions}.
34
+ * @returns The wallet's {@link IKycRecord}. A never-approved wallet returns
35
+ * all-zero timestamps and `blocked: false`.
36
+ * @throws {@link UpshiftValidationError} `INVALID_CHAIN` when no registry is
37
+ * deployed on `chainId`, `INVALID_ADDRESS` when `wallet` is not an EVM
38
+ * address.
39
+ * @throws {@link UpshiftSDKError} when the `getRecord` read fails.
40
+ * @worstCaseRpcCalls 1 — one `getRecord(address)` call.
41
+ * @example
42
+ * ```ts
43
+ * const { expiresAt } = await getKycRecord({ chainId: 1, wallet, rpcUrl });
44
+ * const verifiedUntil = new Date(expiresAt * 1000);
45
+ * ```
46
+ */
47
+ export declare function getKycRecord({ chainId, wallet, rpcUrl, }: IKycWalletOptions): Promise<IKycRecord>;
@@ -0,0 +1,111 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getKycStatus = getKycStatus;
4
+ exports.getKycRecord = getKycRecord;
5
+ const ethers_1 = require("ethers");
6
+ const abis_1 = require("../../abis");
7
+ const kyc_1 = require("../constants/kyc");
8
+ const errors_1 = require("../errors");
9
+ const web3_1 = require("./web3");
10
+ const KYC_STATUSES = [
11
+ 'none',
12
+ 'active',
13
+ 'expired',
14
+ 'blocked',
15
+ ];
16
+ function toKycStatus(raw, chainId) {
17
+ const status = KYC_STATUSES[Number(raw)];
18
+ if (!status) {
19
+ throw new errors_1.UpshiftSDKError('UNKNOWN', `KycRegistry on chain ${chainId} returned unknown status ${raw}`, { context: { chainId } });
20
+ }
21
+ return status;
22
+ }
23
+ function contractAt(method, chainId, address, abi, provider) {
24
+ const contract = (0, web3_1.createContract)({ address, abi, provider });
25
+ if (!contract) {
26
+ throw new errors_1.UpshiftSDKError('UNKNOWN', `${method}: could not create a contract at ${address} on chain ${chainId}`, { context: { method, chainId } });
27
+ }
28
+ return contract;
29
+ }
30
+ function connect(method, chainId, rpcUrl, wallets) {
31
+ const registryAddress = kyc_1.KYC_REGISTRY_ADDRESSES[chainId];
32
+ if (!registryAddress) {
33
+ throw new errors_1.UpshiftValidationError('INVALID_CHAIN', `${method}: no KycRegistry is deployed on chain ${chainId}`, { context: { method, chainId } });
34
+ }
35
+ for (const [field, value] of Object.entries(wallets)) {
36
+ if (!(0, ethers_1.isAddress)(value)) {
37
+ throw new errors_1.UpshiftValidationError('INVALID_ADDRESS', `${method}: ${field} is not a valid EVM address`, { context: { method, chainId, field } });
38
+ }
39
+ }
40
+ return contractAt(method, chainId, registryAddress, abis_1.ABI_KYC_REGISTRY, (0, web3_1.createProvider)(rpcUrl, chainId));
41
+ }
42
+ async function wrapRead(method, chainId, read) {
43
+ try {
44
+ return await read();
45
+ }
46
+ catch (e) {
47
+ if (e instanceof errors_1.UpshiftSDKError)
48
+ throw e;
49
+ throw new errors_1.UpshiftSDKError('UNKNOWN', `${method} failed: ${e instanceof Error ? e.message : 'Unknown error'}`, { cause: e, context: { chainId } });
50
+ }
51
+ }
52
+ /**
53
+ * Read a wallet's KYC status from the chain's `KycRegistry` (`statusOf`).
54
+ *
55
+ * The registry decides whether a KYC vault accepts a deposit, so read it
56
+ * on-chain rather than trusting a cached backend status. `expired` covers both
57
+ * a lapsed and a revoked record; tell them apart with {@link getKycRecord}.
58
+ * Not cached: an approval lands when the registry writer sends it, and callers
59
+ * poll for it.
60
+ *
61
+ * @param options - {@link IKycWalletOptions}.
62
+ * @returns The wallet's {@link IKycStatus}.
63
+ * @throws {@link UpshiftValidationError} `INVALID_CHAIN` when no registry is
64
+ * deployed on `chainId` (see {@link KYC_REGISTRY_ADDRESSES}),
65
+ * `INVALID_ADDRESS` when `wallet` is not an EVM address.
66
+ * @throws {@link UpshiftSDKError} when the `statusOf` read fails or returns a
67
+ * status this SDK version does not know.
68
+ * @worstCaseRpcCalls 1 — one `statusOf(address)` call.
69
+ * @example
70
+ * ```ts
71
+ * const status = await getKycStatus({ chainId: 1, wallet, rpcUrl });
72
+ * if (status !== 'active') showVerifyPrompt();
73
+ * ```
74
+ */
75
+ async function getKycStatus({ chainId, wallet, rpcUrl, }) {
76
+ const registry = connect('getKycStatus', chainId, rpcUrl, { wallet });
77
+ const raw = await wrapRead('getKycStatus', chainId, () => registry.statusOf(wallet));
78
+ return toKycStatus(raw, chainId);
79
+ }
80
+ /**
81
+ * Read a wallet's raw record from the chain's `KycRegistry` (`getRecord`).
82
+ *
83
+ * Use it with {@link getKycStatus} to show when access ends (`expiresAt`) and
84
+ * to tell a lapsed record (`expiresAt` in the past) from a revoked one. Not
85
+ * cached, for the same reason as {@link getKycStatus}.
86
+ *
87
+ * @param options - {@link IKycWalletOptions}.
88
+ * @returns The wallet's {@link IKycRecord}. A never-approved wallet returns
89
+ * all-zero timestamps and `blocked: false`.
90
+ * @throws {@link UpshiftValidationError} `INVALID_CHAIN` when no registry is
91
+ * deployed on `chainId`, `INVALID_ADDRESS` when `wallet` is not an EVM
92
+ * address.
93
+ * @throws {@link UpshiftSDKError} when the `getRecord` read fails.
94
+ * @worstCaseRpcCalls 1 — one `getRecord(address)` call.
95
+ * @example
96
+ * ```ts
97
+ * const { expiresAt } = await getKycRecord({ chainId: 1, wallet, rpcUrl });
98
+ * const verifiedUntil = new Date(expiresAt * 1000);
99
+ * ```
100
+ */
101
+ async function getKycRecord({ chainId, wallet, rpcUrl, }) {
102
+ const registry = connect('getKycRecord', chainId, rpcUrl, { wallet });
103
+ const record = await wrapRead('getKycRecord', chainId, () => registry.getRecord(wallet));
104
+ return {
105
+ blocked: record.blocked,
106
+ verifiedAt: Number(record.verifiedAt),
107
+ expiresAt: Number(record.expiresAt),
108
+ revokedAt: Number(record.revokedAt),
109
+ };
110
+ }
111
+ //# sourceMappingURL=kyc.js.map
@@ -99,8 +99,8 @@ const STANDARD_ERROR_SIGNATURES = [
99
99
  * ABIs already shipped by the SDK whose `error` fragments join the corpus.
100
100
  * Covers the Upshift vault estate: tokenized vaults (plus their
101
101
  * receipt / permit / whitelist companions), the SwapRouter, lending pools,
102
- * the fee oracle, the native-deposit wrapper, Multicall3, and the RWA redeem
103
- * subaccount.
102
+ * the fee oracle, the native-deposit wrapper, Multicall3, the RWA redeem
103
+ * subaccount, and the KYC registry.
104
104
  */
105
105
  const CORPUS_ABIS = [
106
106
  STANDARD_ERROR_SIGNATURES,
@@ -112,6 +112,7 @@ const CORPUS_ABIS = [
112
112
  abis_1.ABI_TOKENIZED_VAULT_V2_SENDER_ALLOCATION_WHITELIST,
113
113
  abis_1.ABI_MULTI_ASSET_NATIVE_DEPOSIT_WRAPPER,
114
114
  abis_1.ABI_SWAP_ROUTER,
115
+ abis_1.ABI_KYC_REGISTRY,
115
116
  abis_1.ABI_LENDING_POOL_V3,
116
117
  abis_1.ABI_FEE_ORACLE,
117
118
  abis_1.ABI_MULTICALL3,