@augustdigital/sdk 9.0.0 → 9.2.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/lib/adapters/stellar/actions.d.ts +13 -0
- package/lib/adapters/stellar/actions.js +51 -2
- package/lib/adapters/stellar/index.d.ts +12 -0
- package/lib/adapters/stellar/index.js +12 -0
- package/lib/adapters/stellar/invocation.d.ts +55 -0
- package/lib/adapters/stellar/invocation.js +92 -0
- package/lib/adapters/stellar/soroban.d.ts +8 -0
- package/lib/adapters/stellar/soroban.js +18 -4
- package/lib/adapters/stellar/submit.d.ts +8 -0
- package/lib/adapters/stellar/submit.js +46 -0
- package/lib/core/analytics/env.d.ts +17 -0
- package/lib/core/analytics/env.js +88 -0
- package/lib/core/analytics/sentry.js +1 -21
- package/lib/core/analytics/version.d.ts +1 -1
- package/lib/core/analytics/version.js +1 -1
- package/lib/core/base.class.d.ts +22 -0
- package/lib/core/base.class.js +11 -0
- package/lib/core/logger/curator-alert.d.ts +217 -0
- package/lib/core/logger/curator-alert.js +276 -0
- package/lib/core/logger/index.d.ts +1 -0
- package/lib/core/logger/index.js +20 -1
- package/lib/main.d.ts +6 -0
- package/lib/main.js +6 -0
- package/lib/modules/vaults/getters.d.ts +9 -0
- package/lib/modules/vaults/getters.js +25 -0
- package/lib/sdk.d.ts +153 -0
- package/lib/services/untangled/fetcher.d.ts +40 -0
- package/lib/services/untangled/fetcher.js +110 -0
- package/lib/services/untangled/index.d.ts +3 -0
- package/lib/services/untangled/index.js +20 -0
- package/lib/services/untangled/types.d.ts +82 -0
- package/lib/services/untangled/types.js +10 -0
- package/lib/services/untangled/utils.d.ts +55 -0
- package/lib/services/untangled/utils.js +157 -0
- package/package.json +1 -1
package/lib/sdk.d.ts
CHANGED
|
@@ -15112,6 +15112,30 @@ export declare type ApproveResult =
|
|
|
15112
15112
|
kind: 'native';
|
|
15113
15113
|
};
|
|
15114
15114
|
|
|
15115
|
+
/**
|
|
15116
|
+
* Whether an alert would be sent right now.
|
|
15117
|
+
*
|
|
15118
|
+
* Quiet by default outside production, mirroring `analytics`: a partner's Jest
|
|
15119
|
+
* suite or `next dev` session exercising a failing redeem must not page a real
|
|
15120
|
+
* curator. Two gates are needed for that, because they cover different
|
|
15121
|
+
* runtimes — `NODE_ENV` is the Node one, and bundlers strip or stub
|
|
15122
|
+
* `process.env` in the browser, so a locally-served app is recognised by its
|
|
15123
|
+
* hostname instead (CLAUDE.md §5). Without that gate, a browser app run
|
|
15124
|
+
* against mainnet from any loopback or private origin — `localhost`, a
|
|
15125
|
+
* container on `172.20.0.0/16`, `[::1]` — pages a real curator on every
|
|
15126
|
+
* failed redeem. `isLocalhost` in `core/analytics/env` lists the ranges.
|
|
15127
|
+
*
|
|
15128
|
+
* `enabled: true` overrides both — it is the documented way to exercise the
|
|
15129
|
+
* path deliberately, and the SDK's own suite relies on it.
|
|
15130
|
+
*
|
|
15131
|
+
* The env var is read per call (not once at module load) so a consumer that
|
|
15132
|
+
* sets it after import is still honoured, and a test can set and unset it
|
|
15133
|
+
* without re-importing. It deliberately outranks an explicit `enabled: true`:
|
|
15134
|
+
* it is the kill switch, and an operator setting it should not have to find
|
|
15135
|
+
* and edit the construction site to be obeyed.
|
|
15136
|
+
*/
|
|
15137
|
+
export declare function areCuratorAlertsEnabled(): boolean;
|
|
15138
|
+
|
|
15115
15139
|
declare type AsArray<T> = T extends readonly unknown[] ? T : never;
|
|
15116
15140
|
|
|
15117
15141
|
/**
|
|
@@ -16047,6 +16071,12 @@ declare type AsArray<T> = T extends readonly unknown[] ? T : never;
|
|
|
16047
16071
|
* `cefi` / `otc` arrays back the drill-downs. See the "Rendering the Vault
|
|
16048
16072
|
* Exposure section" guide in the vaults docs for a faithful reproduction.
|
|
16049
16073
|
*
|
|
16074
|
+
* Stellar vaults resolve their DeFi exposure through the Untangled portfolio
|
|
16075
|
+
* API instead of DeBank — one extra HTTP call, covering the idle buffer,
|
|
16076
|
+
* Stellar protocol positions, custody wallets and bridged capital. If that
|
|
16077
|
+
* call fails the exposure legs come back empty rather than throwing, so CeFi,
|
|
16078
|
+
* OTC and loan allocations still resolve.
|
|
16079
|
+
*
|
|
16050
16080
|
* @param props - Vault address and chain ID
|
|
16051
16081
|
* @returns Detailed breakdown of vault allocations by category
|
|
16052
16082
|
* @example
|
|
@@ -17530,6 +17560,23 @@ declare type AsArray<T> = T extends readonly unknown[] ? T : never;
|
|
|
17530
17560
|
*/
|
|
17531
17561
|
export declare function computeArgShape(args: unknown[]): string[];
|
|
17532
17562
|
|
|
17563
|
+
/**
|
|
17564
|
+
* Apply constructor config. Called unconditionally by `AugustBase` so an
|
|
17565
|
+
* instance that omits `curatorAlerts` RESETS to the defaults rather than
|
|
17566
|
+
* inheriting a prior instance's settings — same contract as
|
|
17567
|
+
* `setPublicApiBaseUrl` and `setAttribution`.
|
|
17568
|
+
*
|
|
17569
|
+
* Also the supported opt-out for code that calls the `Stellar` namespace
|
|
17570
|
+
* functions directly without constructing an `AugustSDK` (the testnet flow in
|
|
17571
|
+
* the docs), and the only one that works in a browser, where the env var is
|
|
17572
|
+
* unreadable.
|
|
17573
|
+
*
|
|
17574
|
+
* @param config - `monitoring.curatorAlerts`, or `null` to restore defaults.
|
|
17575
|
+
* @param context - Facts about the reporting instance; omitted entirely by a
|
|
17576
|
+
* caller that only wants to disable reporting.
|
|
17577
|
+
*/
|
|
17578
|
+
export declare function configureCuratorAlerts(config: ICuratorAlertsConfig | null, context?: ICuratorAlertsContext): void;
|
|
17579
|
+
|
|
17533
17580
|
/**
|
|
17534
17581
|
* Query the vault's `convert_to_shares` to preview how many shares a deposit
|
|
17535
17582
|
* amount would yield. Returns the raw share amount as a string, or null on
|
|
@@ -17627,6 +17674,9 @@ declare type AsArray<T> = T extends readonly unknown[] ? T : never;
|
|
|
17627
17674
|
*/
|
|
17628
17675
|
export declare function crossChainVaultRedeem(props: ICrossChainRedeemRequest): Promise<ICrossChainRedeemResult>;
|
|
17629
17676
|
|
|
17677
|
+
/** Env equivalent of `monitoring.curatorAlerts.enabled: false`. Node only. */
|
|
17678
|
+
export declare const CURATOR_ALERTS_DISABLE_ENV_VAR = "AUGUST_SDK_DISABLE_CURATOR_ALERTS";
|
|
17679
|
+
|
|
17630
17680
|
/**
|
|
17631
17681
|
* Datetime
|
|
17632
17682
|
*/
|
|
@@ -19355,6 +19405,15 @@ declare type AsArray<T> = T extends readonly unknown[] ? T : never;
|
|
|
19355
19405
|
* Fetch comprehensive vault asset allocation breakdown.
|
|
19356
19406
|
* Includes DeFi protocols (via DeBank), CeFi balances, OTC positions, and loans.
|
|
19357
19407
|
* Categorizes exposures by borrowing, supplying, lending, and wallet holdings.
|
|
19408
|
+
*
|
|
19409
|
+
* DeFi exposure resolves per chain family: EVM vaults via DeBank, Solana
|
|
19410
|
+
* borrowers via Octav.fi, and Stellar vaults via the Untangled portfolio API —
|
|
19411
|
+
* one vault-level call covering the idle buffer, Stellar protocol positions,
|
|
19412
|
+
* custody wallets, and capital bridged to other chains. The Stellar path adds
|
|
19413
|
+
* exactly 1 HTTP call and zero for every other chain; when it fails, the
|
|
19414
|
+
* exposure section comes back empty and the CeFi, OTC and loan allocations are
|
|
19415
|
+
* still returned.
|
|
19416
|
+
*
|
|
19358
19417
|
* @param vault - Vault address
|
|
19359
19418
|
* @param options - RPC configuration
|
|
19360
19419
|
* @returns Detailed allocation data with exposure categorization
|
|
@@ -19810,6 +19869,19 @@ declare type AsArray<T> = T extends readonly unknown[] ? T : never;
|
|
|
19810
19869
|
* `redeem(shares: i128, receiver, owner, operator) -> i128`; divergence
|
|
19811
19870
|
* surfaces as a generic Soroban simulation error.
|
|
19812
19871
|
*
|
|
19872
|
+
* Side effect on failure: when the *vault* cannot serve the redemption — it
|
|
19873
|
+
* rejected the call, or its ledger state needs restoring — the failure is
|
|
19874
|
+
* reported to that vault's curator. Stellar vaults are instant-redeem only, so
|
|
19875
|
+
* this is the curator's only signal that their depositors cannot get out
|
|
19876
|
+
* (AUGUST-7162). Fire-and-forget and deduped; see
|
|
19877
|
+
* {@link reportRedeemFailure} and {@link REPORTED_BUILD_STAGES}.
|
|
19878
|
+
*
|
|
19879
|
+
* Note this includes a caller-caused rejection such as redeeming more shares
|
|
19880
|
+
* than the wallet holds: the contract trapping is what we can observe, and
|
|
19881
|
+
* telling that apart from a vault-side problem needs the vault's error
|
|
19882
|
+
* taxonomy. A bad address, an unfunded account, and an exhausted RPC failover
|
|
19883
|
+
* are excluded — none of those reached the vault.
|
|
19884
|
+
*
|
|
19813
19885
|
* @returns Base64-encoded XDR ready for wallet signing; pass the signed
|
|
19814
19886
|
* XDR to {@link submitStellarTransaction}.
|
|
19815
19887
|
*/
|
|
@@ -20409,6 +20481,46 @@ declare type AsArray<T> = T extends readonly unknown[] ? T : never;
|
|
|
20409
20481
|
hubOnlyReceipt?: boolean;
|
|
20410
20482
|
}
|
|
20411
20483
|
|
|
20484
|
+
/** Curator-alert reporting. Enabled by default; this is the opt-out. */
|
|
20485
|
+
export declare interface ICuratorAlertsConfig {
|
|
20486
|
+
/**
|
|
20487
|
+
* `false` stops emitting entirely. `true` forces emitting even in an
|
|
20488
|
+
* environment the SDK would otherwise stay quiet in — it bypasses **every**
|
|
20489
|
+
* environment gate: `NODE_ENV` of `development`/`test`, a browser served
|
|
20490
|
+
* from a loopback or private host, and `monitoring.env` other than `PROD`.
|
|
20491
|
+
* It does NOT lift the mainnet-only restriction; only `endpoint` does. A
|
|
20492
|
+
* `DEV` or test
|
|
20493
|
+
* integration that sets it and then fails a mainnet redeem pages a real
|
|
20494
|
+
* curator, so use it only when deliberately exercising the path (pair it
|
|
20495
|
+
* with `endpoint` to aim at a test relay). The only thing that still wins is
|
|
20496
|
+
* the {@link CURATOR_ALERTS_DISABLE_ENV_VAR} kill switch.
|
|
20497
|
+
*/
|
|
20498
|
+
enabled?: boolean;
|
|
20499
|
+
/**
|
|
20500
|
+
* Override the relay endpoint. Defaults to
|
|
20501
|
+
* {@link DEFAULT_CURATOR_ALERT_ENDPOINT}. Setting it also lifts the
|
|
20502
|
+
* mainnet-only restriction, so a staging relay can receive testnet failures.
|
|
20503
|
+
*/
|
|
20504
|
+
endpoint?: string;
|
|
20505
|
+
}
|
|
20506
|
+
|
|
20507
|
+
/** Ambient facts about the reporting SDK instance, supplied by `AugustBase`. */
|
|
20508
|
+
export declare interface ICuratorAlertsContext {
|
|
20509
|
+
/** Carried on the payload so August can tell partner traffic apart. */
|
|
20510
|
+
appName?: string;
|
|
20511
|
+
/**
|
|
20512
|
+
* `monitoring.env`. Anything other than `'PROD'` keeps the SDK quiet unless
|
|
20513
|
+
* `config.enabled` is explicitly `true`.
|
|
20514
|
+
*/
|
|
20515
|
+
environment?: IEnv;
|
|
20516
|
+
/**
|
|
20517
|
+
* Effective base URL of the August public API. Reports follow the API this
|
|
20518
|
+
* instance reads from, so a deployment pointed at a staging backend alerts
|
|
20519
|
+
* staging rather than production.
|
|
20520
|
+
*/
|
|
20521
|
+
apiBaseUrl?: string;
|
|
20522
|
+
}
|
|
20523
|
+
|
|
20412
20524
|
/**
|
|
20413
20525
|
* On-chain whitelist status of one subaccount linked to a vault, from
|
|
20414
20526
|
* `GET /curator/vaults/{vault_address}/whitelist` (backend
|
|
@@ -20954,6 +21066,27 @@ declare type AsArray<T> = T extends readonly unknown[] ? T : never;
|
|
|
20954
21066
|
declare interface IMonitoring extends IWSMonitorHeaders {
|
|
20955
21067
|
slackWebhookUrl?: string;
|
|
20956
21068
|
env?: IEnv;
|
|
21069
|
+
/**
|
|
21070
|
+
* Curator notifications for failed vault redemptions. Enabled by default
|
|
21071
|
+
* (opt-out model, like `analytics`).
|
|
21072
|
+
*
|
|
21073
|
+
* Stellar vaults are instant-redeem only, so when a redemption fails the
|
|
21074
|
+
* vault's curator has no other way to learn about it. The SDK relays the
|
|
21075
|
+
* failure to August's notification service, which routes it to that curator's
|
|
21076
|
+
* own channel — no credentials or curator identities are held here.
|
|
21077
|
+
*
|
|
21078
|
+
* Set `enabled: false` (or, in Node, the
|
|
21079
|
+
* `AUGUST_SDK_DISABLE_CURATOR_ALERTS` env var) to stop emitting; set
|
|
21080
|
+
* `endpoint` only to point a non-prod deployment at a test relay. Nothing is
|
|
21081
|
+
* emitted unless `monitoring.env` is `PROD`, `NODE_ENV` is neither
|
|
21082
|
+
* `development` nor `test`, and — in a browser, where `NODE_ENV` is not
|
|
21083
|
+
* readable — the page is not served from localhost.
|
|
21084
|
+
*
|
|
21085
|
+
* `enabled: true` is the one exception: it bypasses all three gates, so a
|
|
21086
|
+
* `DEV` or test integration that sets it can page a real curator. See
|
|
21087
|
+
* {@link ICuratorAlertsConfig}.
|
|
21088
|
+
*/
|
|
21089
|
+
curatorAlerts?: ICuratorAlertsConfig;
|
|
20957
21090
|
}
|
|
20958
21091
|
|
|
20959
21092
|
/* Excluded from this release type: IMulticall3Request */
|
|
@@ -26112,10 +26245,22 @@ declare type AsArray<T> = T extends readonly unknown[] ? T : never;
|
|
|
26112
26245
|
vaultDeposit(params: Omit<IStellarDepositParams, 'network'>): Promise<string>;
|
|
26113
26246
|
/**
|
|
26114
26247
|
* Build an unsigned redeem transaction for a Stellar vault.
|
|
26248
|
+
*
|
|
26249
|
+
* Side effect on failure: a redemption the vault cannot serve is reported to
|
|
26250
|
+
* that vault's curator, since Stellar vaults are instant-redeem only and this
|
|
26251
|
+
* is the curator's only signal. Fire-and-forget — it never delays or alters
|
|
26252
|
+
* the error you receive. On by default in production; see
|
|
26253
|
+
* `monitoring.curatorAlerts` and the Curator Notifications section of the
|
|
26254
|
+
* Stellar Actions guide for exactly what is sent and how to opt out.
|
|
26255
|
+
*
|
|
26115
26256
|
* @returns Base64-encoded XDR of the unsigned transaction.
|
|
26116
26257
|
*/
|
|
26117
26258
|
vaultRedeem(params: Omit<IStellarRedeemParams, 'network'>): Promise<string>;
|
|
26118
26259
|
/**
|
|
26260
|
+
* Side effect: a submitted transaction that the network reports as a failed
|
|
26261
|
+
* `redeem` operation is reported to that vault's curator, on the same
|
|
26262
|
+
* fire-and-forget terms as {@link StellarAdapter.vaultRedeem}.
|
|
26263
|
+
*
|
|
26119
26264
|
* Submit a signed Soroban transaction and poll until the network confirms it.
|
|
26120
26265
|
*
|
|
26121
26266
|
* Submits on the network this adapter was constructed with, so `signedXdr`
|
|
@@ -26227,6 +26372,14 @@ declare type AsArray<T> = T extends readonly unknown[] ? T : never;
|
|
|
26227
26372
|
* plus, when the result XDR decodes, a `resultCode` string holding the
|
|
26228
26373
|
* transaction-level reason (e.g. `"txBadSeq"`, `"txTooLate"`); `resultCode`
|
|
26229
26374
|
* is `undefined` when the code cannot be decoded.
|
|
26375
|
+
*
|
|
26376
|
+
* Side effect: a transaction whose `resultCode` is `txFailed` — the operation
|
|
26377
|
+
* itself ran and failed — and whose envelope shows a vault `redeem` is relayed
|
|
26378
|
+
* to that vault's curator (fire-and-forget, deduped — see
|
|
26379
|
+
* {@link reportRedeemFailure}). Nothing else alerts: an RPC-rejected
|
|
26380
|
+
* broadcast is a retryable race, a poll timeout is indeterminate (the
|
|
26381
|
+
* transaction may yet succeed), and any other result code means the redeem
|
|
26382
|
+
* never executed.
|
|
26230
26383
|
* @example
|
|
26231
26384
|
* ```ts
|
|
26232
26385
|
* try {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { IUntangledVaultExposure } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Fetch a vault's full capital exposure from the Untangled portfolio API.
|
|
4
|
+
*
|
|
5
|
+
* Untangled reconciles Stellar vaults end to end — the vault's idle buffer,
|
|
6
|
+
* capital deployed into Stellar protocols, custody wallet balances, and value
|
|
7
|
+
* bridged to other chains. DeBank and Octav.fi cover none of that, which is why
|
|
8
|
+
* Stellar vaults previously rendered an empty exposure breakdown.
|
|
9
|
+
*
|
|
10
|
+
* The endpoint is keyed by **vault** address and already aggregates every
|
|
11
|
+
* borrower, so it must be called once per vault — never once per borrower, which
|
|
12
|
+
* would multiply the same totals.
|
|
13
|
+
*
|
|
14
|
+
* Requests are plain `GET`s with no custom headers. That is deliberate: it keeps
|
|
15
|
+
* them preflight-free in the browser, where the API's `access-control-allow-origin: *`
|
|
16
|
+
* covers the simple request but a bare `OPTIONS` is rejected.
|
|
17
|
+
*
|
|
18
|
+
* Never throws. Every failure — unknown vault, upstream error, transport
|
|
19
|
+
* failure, malformed body — resolves to `null` so callers can degrade to an
|
|
20
|
+
* empty exposure section instead of losing the rest of their response.
|
|
21
|
+
*
|
|
22
|
+
* Worst case: exactly **1 HTTP call**. No retry, no caching (the API is edge
|
|
23
|
+
* cached at `s-maxage=60`, well inside its 60 req/min budget for one call per
|
|
24
|
+
* vault page load).
|
|
25
|
+
*
|
|
26
|
+
* @param vault - Vault address, e.g. a Stellar contract address (`C…`).
|
|
27
|
+
* @returns The resolved exposure report, or `null` when Untangled does not
|
|
28
|
+
* reconcile this vault or the request failed.
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const exposure = await fetchUntangledVaultExposure(
|
|
33
|
+
* 'CCL3WITWFFXIHV2I52ECV5DPIEOFSTU3PBPR53ILPLF2IP5KHECXRUTY',
|
|
34
|
+
* );
|
|
35
|
+
* // exposure?.tvlUsd → 24845637.05
|
|
36
|
+
* // exposure?.byProtocol → [{ protocolId: 'stellar-wallet', … }, { protocolId: 'blend', … }, …]
|
|
37
|
+
* const normalized = transformUntangledToDebank(exposure);
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function fetchUntangledVaultExposure(vault: string): Promise<IUntangledVaultExposure | null>;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchUntangledVaultExposure = fetchUntangledVaultExposure;
|
|
4
|
+
const core_1 = require("../../core");
|
|
5
|
+
const BASE_URL = 'https://api-portfolio-octopos-mainnet.untangled.finance';
|
|
6
|
+
/**
|
|
7
|
+
* Read a response body as JSON without throwing.
|
|
8
|
+
*
|
|
9
|
+
* Used on the error path, where a malformed or empty body must not mask the
|
|
10
|
+
* HTTP status we actually want to report.
|
|
11
|
+
*
|
|
12
|
+
* @param response - The fetch response to drain.
|
|
13
|
+
* @returns The parsed body, or `null` when it is absent or not JSON.
|
|
14
|
+
*/
|
|
15
|
+
async function readJsonSafely(response) {
|
|
16
|
+
try {
|
|
17
|
+
return await response.json();
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Fetch a vault's full capital exposure from the Untangled portfolio API.
|
|
25
|
+
*
|
|
26
|
+
* Untangled reconciles Stellar vaults end to end — the vault's idle buffer,
|
|
27
|
+
* capital deployed into Stellar protocols, custody wallet balances, and value
|
|
28
|
+
* bridged to other chains. DeBank and Octav.fi cover none of that, which is why
|
|
29
|
+
* Stellar vaults previously rendered an empty exposure breakdown.
|
|
30
|
+
*
|
|
31
|
+
* The endpoint is keyed by **vault** address and already aggregates every
|
|
32
|
+
* borrower, so it must be called once per vault — never once per borrower, which
|
|
33
|
+
* would multiply the same totals.
|
|
34
|
+
*
|
|
35
|
+
* Requests are plain `GET`s with no custom headers. That is deliberate: it keeps
|
|
36
|
+
* them preflight-free in the browser, where the API's `access-control-allow-origin: *`
|
|
37
|
+
* covers the simple request but a bare `OPTIONS` is rejected.
|
|
38
|
+
*
|
|
39
|
+
* Never throws. Every failure — unknown vault, upstream error, transport
|
|
40
|
+
* failure, malformed body — resolves to `null` so callers can degrade to an
|
|
41
|
+
* empty exposure section instead of losing the rest of their response.
|
|
42
|
+
*
|
|
43
|
+
* Worst case: exactly **1 HTTP call**. No retry, no caching (the API is edge
|
|
44
|
+
* cached at `s-maxage=60`, well inside its 60 req/min budget for one call per
|
|
45
|
+
* vault page load).
|
|
46
|
+
*
|
|
47
|
+
* @param vault - Vault address, e.g. a Stellar contract address (`C…`).
|
|
48
|
+
* @returns The resolved exposure report, or `null` when Untangled does not
|
|
49
|
+
* reconcile this vault or the request failed.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* const exposure = await fetchUntangledVaultExposure(
|
|
54
|
+
* 'CCL3WITWFFXIHV2I52ECV5DPIEOFSTU3PBPR53ILPLF2IP5KHECXRUTY',
|
|
55
|
+
* );
|
|
56
|
+
* // exposure?.tvlUsd → 24845637.05
|
|
57
|
+
* // exposure?.byProtocol → [{ protocolId: 'stellar-wallet', … }, { protocolId: 'blend', … }, …]
|
|
58
|
+
* const normalized = transformUntangledToDebank(exposure);
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
async function fetchUntangledVaultExposure(vault) {
|
|
62
|
+
try {
|
|
63
|
+
const response = await fetch(`${BASE_URL}/v1/vaults/${encodeURIComponent(vault)}/exposure`, { method: 'GET' });
|
|
64
|
+
if (!response.ok) {
|
|
65
|
+
const body = await readJsonSafely(response);
|
|
66
|
+
// 404 is the documented "we don't reconcile this vault" answer, not a
|
|
67
|
+
// fault — most vaults are not Untangled-covered. Keep it a breadcrumb so
|
|
68
|
+
// a known, permanent gap does not fill Sentry.
|
|
69
|
+
if (response.status === 404) {
|
|
70
|
+
core_1.Logger.log.warn('fetchUntangledVaultExposure:unresolved', {
|
|
71
|
+
vault,
|
|
72
|
+
status: response.status,
|
|
73
|
+
reason: body?.reason ?? 'unknown_vault',
|
|
74
|
+
});
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
core_1.Logger.log.error('fetchUntangledVaultExposure', new Error(`HTTP ${response.status} ${response.statusText}`), { vault, status: response.status });
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
const data = await response.json();
|
|
81
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) {
|
|
82
|
+
core_1.Logger.log.error('fetchUntangledVaultExposure.unexpected-type', new Error(`unexpected response type: ${typeof data}`), { vault });
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
if (data.resolved !== true) {
|
|
86
|
+
core_1.Logger.log.warn('fetchUntangledVaultExposure:unresolved', {
|
|
87
|
+
vault,
|
|
88
|
+
status: response.status,
|
|
89
|
+
reason: data.reason ?? 'unknown_vault',
|
|
90
|
+
});
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
if (!Array.isArray(data.byProtocol)) {
|
|
94
|
+
core_1.Logger.log.error('fetchUntangledVaultExposure.malformed', new Error('resolved response is missing byProtocol[]'), { vault });
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
if (data.stale) {
|
|
98
|
+
core_1.Logger.log.warn('fetchUntangledVaultExposure:stale', {
|
|
99
|
+
vault,
|
|
100
|
+
asOf: data.asOf,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return data;
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
core_1.Logger.log.error('fetchUntangledVaultExposure', error, { vault });
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=fetcher.js.map
|
|
@@ -0,0 +1,20 @@
|
|
|
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./fetcher"), exports);
|
|
18
|
+
__exportStar(require("./types"), exports);
|
|
19
|
+
__exportStar(require("./utils"), exports);
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Response types for the Untangled portfolio API.
|
|
3
|
+
*
|
|
4
|
+
* Shape verified live on 2026-09-01 against
|
|
5
|
+
* `GET https://api-portfolio-octopos-mainnet.untangled.finance/v1/vaults/{address}/exposure`
|
|
6
|
+
* for the Gami earnUSDC Stellar vault.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* One protocol slice of a vault's TVL.
|
|
10
|
+
*
|
|
11
|
+
* Untangled reports the whole vault, so the slices cover capital held in the
|
|
12
|
+
* vault contract itself, in custody wallets, and bridged to other chains — not
|
|
13
|
+
* just on-chain Stellar protocol positions.
|
|
14
|
+
*/
|
|
15
|
+
export interface IUntangledProtocolExposure {
|
|
16
|
+
/**
|
|
17
|
+
* Untangled protocol slug, e.g. `blend`, `templar`, `aquarius`. Two slugs are
|
|
18
|
+
* not protocols: `stellar-wallet` (custody wallets) and `upshift` (the vault's
|
|
19
|
+
* own idle buffer).
|
|
20
|
+
*/
|
|
21
|
+
protocolId: string;
|
|
22
|
+
/** USD value held in this protocol. */
|
|
23
|
+
valueUsd: number;
|
|
24
|
+
/** Share of the vault's `tvlUsd`, as a percentage (`23.54` means 23.54%). */
|
|
25
|
+
pct: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* One asset slice of a vault's TVL. Vault-wide — the API does not currently
|
|
29
|
+
* break assets down per protocol.
|
|
30
|
+
*/
|
|
31
|
+
export interface IUntangledAssetExposure {
|
|
32
|
+
/** Asset ticker, e.g. `USDC`, `XLM`. */
|
|
33
|
+
symbol: string;
|
|
34
|
+
/** USD value held in this asset across the whole vault. */
|
|
35
|
+
valueUsd: number;
|
|
36
|
+
/** Share of the vault's `tvlUsd`, as a percentage. */
|
|
37
|
+
pct: number;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* One address holding part of a vault's capital — the vault contract, a custody
|
|
41
|
+
* wallet, or a bridged account on another chain.
|
|
42
|
+
*/
|
|
43
|
+
export interface IUntangledLocation {
|
|
44
|
+
/** Address on `chain`. Not necessarily a Stellar address (NEAR accounts appear here). */
|
|
45
|
+
address: string;
|
|
46
|
+
/** Chain slug the address lives on, e.g. `stellar`, `near`. */
|
|
47
|
+
chain: string;
|
|
48
|
+
/** Human-readable label, e.g. `idle`, `ForDefi`, `Templar`. */
|
|
49
|
+
label: string;
|
|
50
|
+
/** USD value held at this address. */
|
|
51
|
+
valueUsd: number;
|
|
52
|
+
/** ISO-8601 timestamp this location was last reconciled. */
|
|
53
|
+
asOf: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* A resolved vault exposure report.
|
|
57
|
+
*
|
|
58
|
+
* Untangled answers with `{ resolved: false, reason }` and HTTP 404 for vaults
|
|
59
|
+
* it does not reconcile; `fetchUntangledVaultExposure` maps that to `null`, so
|
|
60
|
+
* `resolved` is always `true` on values that reach a caller.
|
|
61
|
+
*/
|
|
62
|
+
export interface IUntangledVaultExposure {
|
|
63
|
+
/** Always `true` — unresolved reports never reach callers. */
|
|
64
|
+
resolved: true;
|
|
65
|
+
/** The vault this report describes. */
|
|
66
|
+
vault: {
|
|
67
|
+
address: string;
|
|
68
|
+
chain: string;
|
|
69
|
+
};
|
|
70
|
+
/** Total USD value Untangled reconciled across every location. */
|
|
71
|
+
tvlUsd: number;
|
|
72
|
+
/** Protocol-level split of `tvlUsd`. */
|
|
73
|
+
byProtocol: IUntangledProtocolExposure[];
|
|
74
|
+
/** Asset-level split of `tvlUsd`, vault-wide. */
|
|
75
|
+
byAsset: IUntangledAssetExposure[];
|
|
76
|
+
/** Per-address drill-down of where the capital sits. */
|
|
77
|
+
locations: IUntangledLocation[];
|
|
78
|
+
/** ISO-8601 timestamp the report was assembled. */
|
|
79
|
+
asOf: string;
|
|
80
|
+
/** `true` when Untangled served a report it could not refresh in time. */
|
|
81
|
+
stale: boolean;
|
|
82
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Response types for the Untangled portfolio API.
|
|
4
|
+
*
|
|
5
|
+
* Shape verified live on 2026-09-01 against
|
|
6
|
+
* `GET https://api-portfolio-octopos-mainnet.untangled.finance/v1/vaults/{address}/exposure`
|
|
7
|
+
* for the Gami earnUSDC Stellar vault.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { IDebankNormalizedResponse } from '../debank/utils';
|
|
2
|
+
import type { IUntangledVaultExposure } from './types';
|
|
3
|
+
/** Display metadata for an Untangled protocol slug. */
|
|
4
|
+
export interface IUntangledProtocolMetadata {
|
|
5
|
+
/** Name rendered in the exposure breakdown. */
|
|
6
|
+
name: string;
|
|
7
|
+
/** Chain the protocol position lives on — not always the vault's own chain. */
|
|
8
|
+
chain: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Display metadata for the protocol slugs Untangled reports today.
|
|
12
|
+
*
|
|
13
|
+
* Untangled does not return protocol names or chains, so they are mapped here.
|
|
14
|
+
* Unknown slugs fall back to a capitalized slug on the vault's chain rather than
|
|
15
|
+
* being dropped, so a newly integrated protocol still shows up (unlabelled)
|
|
16
|
+
* instead of silently vanishing from the breakdown.
|
|
17
|
+
*/
|
|
18
|
+
export declare const UNTANGLED_PROTOCOL_REGISTRY: Record<string, IUntangledProtocolMetadata>;
|
|
19
|
+
/**
|
|
20
|
+
* Transform an Untangled vault exposure report into the DeBank-normalized shape
|
|
21
|
+
* that `parseVaultLevelDebank` and `parseLoanLevelDebank` consume.
|
|
22
|
+
*
|
|
23
|
+
* This is the Stellar equivalent of `transformOctavfiToDebank`: a foreign
|
|
24
|
+
* portfolio shape is mapped onto the one normalized structure the vault
|
|
25
|
+
* allocation parser understands, so Stellar vaults populate the same
|
|
26
|
+
* `exposurePerCategory` / `protocolExposure` / `tokenExposure` outputs as EVM
|
|
27
|
+
* vaults with no parser changes.
|
|
28
|
+
*
|
|
29
|
+
* Mapping:
|
|
30
|
+
* - Each `byProtocol` entry becomes one position with a single supplying token,
|
|
31
|
+
* valued in USD (`price: 1`), on the chain from
|
|
32
|
+
* {@link UNTANGLED_PROTOCOL_REGISTRY}.
|
|
33
|
+
* - `stellar-wallet` and `upshift` are summed into one wallet token, because
|
|
34
|
+
* both are undeployed capital rather than protocol exposure.
|
|
35
|
+
* - Dust is left in place: `filterOutBySize` in the parser applies the same
|
|
36
|
+
* threshold Stellar and EVM vaults share.
|
|
37
|
+
*
|
|
38
|
+
* `locations`, `asOf`, `stale` and `tvlUsd` are not consumed — TVL already comes
|
|
39
|
+
* from `getVaultTvl`, and freshness is not yet surfaced on `IVaultAllocations`.
|
|
40
|
+
*
|
|
41
|
+
* @param exposure - A resolved Untangled report, or `null`/`undefined` when the
|
|
42
|
+
* fetch failed.
|
|
43
|
+
* @returns A DeBank-normalized response. Empty positions and tokens when
|
|
44
|
+
* `exposure` is absent, so callers can pass a failed fetch straight through.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* const exposure = await fetchUntangledVaultExposure(vault);
|
|
49
|
+
* const normalized = transformUntangledToDebank(exposure);
|
|
50
|
+
* parseVaultLevelDebank(
|
|
51
|
+
* normalized, protocolExposure, tokenExposure, vault, exposurePerCategory, netValue,
|
|
52
|
+
* );
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export declare function transformUntangledToDebank(exposure: IUntangledVaultExposure | null | undefined): IDebankNormalizedResponse;
|