@augustdigital/sdk 9.2.2 → 9.3.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/core/analytics/version.d.ts +1 -1
- package/lib/core/analytics/version.js +1 -1
- package/lib/modules/vaults/getters.js +15 -1
- package/lib/sdk.d.ts +10 -0
- package/lib/services/untangled/utils.d.ts +30 -1
- package/lib/services/untangled/utils.js +87 -13
- package/lib/types/vaults.d.ts +10 -0
- package/package.json +1 -1
|
@@ -480,6 +480,16 @@ async function getVaultAllocations(vault, options) {
|
|
|
480
480
|
const netValue = { value: 0 };
|
|
481
481
|
let debankErr = false;
|
|
482
482
|
let unfilteredTokens = [];
|
|
483
|
+
// Untangled publishes the vault's asset composition as its own decomposition of
|
|
484
|
+
// tvlUsd, with no join to `byProtocol`. It therefore cannot be accumulated into
|
|
485
|
+
// `tokenExposure` alongside the synthesized position tokens without
|
|
486
|
+
// double-counting, so it replaces those rows below for Stellar vaults.
|
|
487
|
+
let untangledAssets = null;
|
|
488
|
+
// How much of `tokenExposure` the Untangled parse produced. Only that prefix is
|
|
489
|
+
// replaced: the accumulator is shared with the per-borrower loop that runs
|
|
490
|
+
// afterwards, and a borrower on a chain with portfolio coverage would otherwise
|
|
491
|
+
// have its rows silently dropped rather than double-counted.
|
|
492
|
+
let untangledTokenCount = 0;
|
|
483
493
|
const borrowerPortfolioFetchers = {
|
|
484
494
|
solana: async (borrower) => {
|
|
485
495
|
const portfolios = await (0, octavfi_1.fetchOctavfiPortfolios)([borrower]);
|
|
@@ -567,6 +577,8 @@ async function getVaultAllocations(vault, options) {
|
|
|
567
577
|
const untangledRes = (0, untangled_1.transformUntangledToDebank)(untangledExposure);
|
|
568
578
|
(0, debank_1.parseVaultLevelDebank)(untangledRes, protocolExposure, tokenExposure, vault, exposurePerCategory, netValue);
|
|
569
579
|
unfilteredTokens = untangledRes.subaccount.tokens;
|
|
580
|
+
untangledAssets = (0, untangled_1.untangledAssetExposure)(untangledExposure);
|
|
581
|
+
untangledTokenCount = tokenExposure.length;
|
|
570
582
|
defiPerBorrower[vault] = (0, debank_1.parseLoanLevelDebank)(untangledRes);
|
|
571
583
|
}
|
|
572
584
|
}
|
|
@@ -658,7 +670,9 @@ async function getVaultAllocations(vault, options) {
|
|
|
658
670
|
defi: protocolExposure,
|
|
659
671
|
cefi: cefiExposure,
|
|
660
672
|
otc: otcPositions,
|
|
661
|
-
tokens:
|
|
673
|
+
tokens: untangledAssets?.length
|
|
674
|
+
? [...untangledAssets, ...tokenExposure.slice(untangledTokenCount)]
|
|
675
|
+
: tokenExposure,
|
|
662
676
|
defiPerBorrower,
|
|
663
677
|
exposurePerCategory,
|
|
664
678
|
netValue: netValue.value,
|
package/lib/sdk.d.ts
CHANGED
|
@@ -23170,6 +23170,16 @@ declare type AsArray<T> = T extends readonly unknown[] ? T : never;
|
|
|
23170
23170
|
*/
|
|
23171
23171
|
export declare interface IVaultAllocations {
|
|
23172
23172
|
defi: IDebankProtocolExposure[];
|
|
23173
|
+
/**
|
|
23174
|
+
* The vault's asset composition, in USD (`amount` is a dollar value).
|
|
23175
|
+
*
|
|
23176
|
+
* On Stellar this is Untangled's `byAsset` breakdown, which decomposes the
|
|
23177
|
+
* same `tvlUsd` that `defi` does along a different axis — assets rather than
|
|
23178
|
+
* venues. The two do not join: which venue holds which asset is not published,
|
|
23179
|
+
* so this is the only place a Stellar vault's assets are stated, and reading a
|
|
23180
|
+
* per-venue asset split out of `exposurePerCategory` is not supported for
|
|
23181
|
+
* multi-asset Stellar vaults.
|
|
23182
|
+
*/
|
|
23173
23183
|
tokens: IDebankTokenExposure[];
|
|
23174
23184
|
cefi: IWSSubaccountCefi[];
|
|
23175
23185
|
unfilteredTokens: IDebankTokenExposure[];
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { IDebankTokenExposure } from '../../types';
|
|
1
2
|
import type { IDebankNormalizedResponse } from '../debank/utils';
|
|
2
3
|
import type { IUntangledVaultExposure } from './types';
|
|
3
4
|
/** Display metadata for an Untangled protocol slug. */
|
|
@@ -16,6 +17,31 @@ export interface IUntangledProtocolMetadata {
|
|
|
16
17
|
* instead of silently vanishing from the breakdown.
|
|
17
18
|
*/
|
|
18
19
|
export declare const UNTANGLED_PROTOCOL_REGISTRY: Record<string, IUntangledProtocolMetadata>;
|
|
20
|
+
/**
|
|
21
|
+
* The vault's asset composition, as vault-level token exposure.
|
|
22
|
+
*
|
|
23
|
+
* This is the one asset breakdown Untangled actually publishes, and it is
|
|
24
|
+
* authoritative: `byAsset` decomposes the same `tvlUsd` that `byProtocol` does.
|
|
25
|
+
* It is returned separately from {@link transformUntangledToDebank} because it
|
|
26
|
+
* cannot be folded into that structure — attributing an asset to a position
|
|
27
|
+
* would require a join the API does not provide, and adding these on top of the
|
|
28
|
+
* synthesized position tokens would double-count the vault.
|
|
29
|
+
*
|
|
30
|
+
* Negative rows are kept. A vault that has moved capital into another Upshift
|
|
31
|
+
* vault is reported holding the receipt token and owing the asset it spent —
|
|
32
|
+
* earnXLM carries `earnUSDC +$1,170,913` against `USDC -$1,170,271` — and those
|
|
33
|
+
* only sum back to `tvlUsd` together. Dropping the liability would overstate the
|
|
34
|
+
* vault's assets by the full size of the position.
|
|
35
|
+
*
|
|
36
|
+
* @param exposure - A resolved Untangled report, or `null`/`undefined` when the
|
|
37
|
+
* fetch failed.
|
|
38
|
+
* @returns One row per asset clearing `filterOutBySize` in absolute terms,
|
|
39
|
+
* largest first, with `amount` denominated in USD. Empty when `exposure` is
|
|
40
|
+
* absent. `id` carries the asset symbol rather than an address — Untangled
|
|
41
|
+
* reports assets by ticker and publishes no contract address for them — so it
|
|
42
|
+
* is safe as a display key and must not be read as an on-chain address.
|
|
43
|
+
*/
|
|
44
|
+
export declare function untangledAssetExposure(exposure: IUntangledVaultExposure | null | undefined): IDebankTokenExposure[];
|
|
19
45
|
/**
|
|
20
46
|
* Transform an Untangled vault exposure report into the DeBank-normalized shape
|
|
21
47
|
* that `parseVaultLevelDebank` and `parseLoanLevelDebank` consume.
|
|
@@ -29,7 +55,10 @@ export declare const UNTANGLED_PROTOCOL_REGISTRY: Record<string, IUntangledProto
|
|
|
29
55
|
* Mapping:
|
|
30
56
|
* - Each `byProtocol` entry becomes one position with a single supplying token,
|
|
31
57
|
* valued in USD (`price: 1`), on the chain from
|
|
32
|
-
* {@link UNTANGLED_PROTOCOL_REGISTRY}.
|
|
58
|
+
* {@link UNTANGLED_PROTOCOL_REGISTRY}. The token is labelled with the vault's
|
|
59
|
+
* asset only when one asset covers essentially all of it (see
|
|
60
|
+
* {@link vaultAssetSymbol}); otherwise it carries the value with no symbol,
|
|
61
|
+
* and {@link untangledAssetExposure} supplies the composition separately.
|
|
33
62
|
* - `stellar-wallet` becomes a wallet token, because custody cash is undeployed
|
|
34
63
|
* capital rather than protocol exposure.
|
|
35
64
|
* - `upshift` is split: the vault's own idle buffer (the `idle` location) joins
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.UNTANGLED_PROTOCOL_REGISTRY = void 0;
|
|
4
|
+
exports.untangledAssetExposure = untangledAssetExposure;
|
|
4
5
|
exports.transformUntangledToDebank = transformUntangledToDebank;
|
|
6
|
+
const core_1 = require("../../core/helpers/core");
|
|
5
7
|
/**
|
|
6
8
|
* Display metadata for the protocol slugs Untangled reports today.
|
|
7
9
|
*
|
|
@@ -42,6 +44,17 @@ const ASSET_MATCH_TOLERANCE = 0.001;
|
|
|
42
44
|
* values and orders of magnitude below any holding worth naming.
|
|
43
45
|
*/
|
|
44
46
|
const MIN_POSITION_USD = 0.01;
|
|
47
|
+
/**
|
|
48
|
+
* Share of `tvlUsd` the largest `byAsset` entry must cover before a synthesized
|
|
49
|
+
* position may be labelled with it.
|
|
50
|
+
*
|
|
51
|
+
* `byProtocol` and `byAsset` are two orthogonal decompositions of the same TVL,
|
|
52
|
+
* and Untangled publishes no join between them, so which venue holds which asset
|
|
53
|
+
* is genuinely unknown. Tagging every position with the largest asset is only
|
|
54
|
+
* honest when there is effectively nothing else in the vault; below that, the
|
|
55
|
+
* label would assert a holding the report does not support.
|
|
56
|
+
*/
|
|
57
|
+
const SINGLE_ASSET_SHARE = 0.99;
|
|
45
58
|
/**
|
|
46
59
|
* USD the vault holds in its own contract, summed from the `idle` locations.
|
|
47
60
|
*
|
|
@@ -107,14 +120,19 @@ const SYNTHETIC_TOKEN_DECIMALS = 7;
|
|
|
107
120
|
*
|
|
108
121
|
* `parseVaultLevelDebank` computes USD as `price * amount`, so a value already
|
|
109
122
|
* denominated in dollars is expressed as `price: 1, amount: <usd>`.
|
|
123
|
+
*
|
|
124
|
+
* @param symbol - The asset this slice holds, or `null` when the report cannot
|
|
125
|
+
* say. A null symbol still produces a token — the value has to reach
|
|
126
|
+
* `netValue` and the protocol totals either way — but carries no asset label,
|
|
127
|
+
* so consumers render the slice's value without naming an asset.
|
|
110
128
|
*/
|
|
111
129
|
function toDebankToken(symbol, chain, valueUsd, exposureType, protocolId) {
|
|
112
130
|
return {
|
|
113
|
-
id: symbol,
|
|
131
|
+
id: symbol ?? '',
|
|
114
132
|
chain,
|
|
115
|
-
name: symbol,
|
|
116
|
-
symbol,
|
|
117
|
-
optimized_symbol: symbol,
|
|
133
|
+
name: symbol ?? '',
|
|
134
|
+
symbol: symbol ?? '',
|
|
135
|
+
optimized_symbol: symbol ?? '',
|
|
118
136
|
decimals: SYNTHETIC_TOKEN_DECIMALS,
|
|
119
137
|
logo_url: '',
|
|
120
138
|
amount: valueUsd,
|
|
@@ -124,18 +142,71 @@ function toDebankToken(symbol, chain, valueUsd, exposureType, protocolId) {
|
|
|
124
142
|
};
|
|
125
143
|
}
|
|
126
144
|
/**
|
|
127
|
-
* The
|
|
145
|
+
* The symbol every synthesized position may be tagged with, or `null` when the
|
|
146
|
+
* report cannot support tagging them at all.
|
|
128
147
|
*
|
|
129
|
-
* Untangled splits assets vault-wide rather than per protocol, so
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
148
|
+
* Untangled splits assets vault-wide rather than per protocol, so the only
|
|
149
|
+
* defensible label is the dominant asset, and only while it covers essentially
|
|
150
|
+
* the whole vault ({@link SINGLE_ASSET_SHARE}). Gami earnXLM qualifies (99.98%
|
|
151
|
+
* XLM); earnUSDC does not (84.18% USDC, 15.82% deJTRSY), and labelling its
|
|
152
|
+
* positions USDC claimed the whole $25.3M was USDC while $4.0M of deJTRSY went
|
|
153
|
+
* unmentioned. Callers that get `null` emit no asset label and let
|
|
154
|
+
* {@link untangledAssetExposure} carry the composition instead.
|
|
155
|
+
*
|
|
156
|
+
* An exact per-protocol split needs `byProtocol[].assets[]` from Untangled.
|
|
133
157
|
*/
|
|
134
|
-
function
|
|
158
|
+
function vaultAssetSymbol(exposure) {
|
|
135
159
|
const dominant = (exposure.byAsset || []).reduce((best, asset) => !best || Number(asset?.valueUsd || 0) > Number(best.valueUsd || 0)
|
|
136
160
|
? asset
|
|
137
161
|
: best, null);
|
|
138
|
-
|
|
162
|
+
if (!dominant?.symbol)
|
|
163
|
+
return null;
|
|
164
|
+
const tvlUsd = Number(exposure.tvlUsd) || 0;
|
|
165
|
+
if (tvlUsd <= 0)
|
|
166
|
+
return null;
|
|
167
|
+
const share = (Number(dominant.valueUsd) || 0) / tvlUsd;
|
|
168
|
+
return share >= SINGLE_ASSET_SHARE ? dominant.symbol : null;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* The vault's asset composition, as vault-level token exposure.
|
|
172
|
+
*
|
|
173
|
+
* This is the one asset breakdown Untangled actually publishes, and it is
|
|
174
|
+
* authoritative: `byAsset` decomposes the same `tvlUsd` that `byProtocol` does.
|
|
175
|
+
* It is returned separately from {@link transformUntangledToDebank} because it
|
|
176
|
+
* cannot be folded into that structure — attributing an asset to a position
|
|
177
|
+
* would require a join the API does not provide, and adding these on top of the
|
|
178
|
+
* synthesized position tokens would double-count the vault.
|
|
179
|
+
*
|
|
180
|
+
* Negative rows are kept. A vault that has moved capital into another Upshift
|
|
181
|
+
* vault is reported holding the receipt token and owing the asset it spent —
|
|
182
|
+
* earnXLM carries `earnUSDC +$1,170,913` against `USDC -$1,170,271` — and those
|
|
183
|
+
* only sum back to `tvlUsd` together. Dropping the liability would overstate the
|
|
184
|
+
* vault's assets by the full size of the position.
|
|
185
|
+
*
|
|
186
|
+
* @param exposure - A resolved Untangled report, or `null`/`undefined` when the
|
|
187
|
+
* fetch failed.
|
|
188
|
+
* @returns One row per asset clearing `filterOutBySize` in absolute terms,
|
|
189
|
+
* largest first, with `amount` denominated in USD. Empty when `exposure` is
|
|
190
|
+
* absent. `id` carries the asset symbol rather than an address — Untangled
|
|
191
|
+
* reports assets by ticker and publishes no contract address for them — so it
|
|
192
|
+
* is safe as a display key and must not be read as an on-chain address.
|
|
193
|
+
*/
|
|
194
|
+
function untangledAssetExposure(exposure) {
|
|
195
|
+
if (!exposure)
|
|
196
|
+
return [];
|
|
197
|
+
const chain = exposure.vault?.chain || DEFAULT_CHAIN;
|
|
198
|
+
return (exposure.byAsset || [])
|
|
199
|
+
.filter((asset) => asset?.symbol && (0, core_1.filterOutBySize)(Math.abs(Number(asset.valueUsd) || 0)))
|
|
200
|
+
.map((asset) => ({
|
|
201
|
+
amount: Number(asset.valueUsd) || 0,
|
|
202
|
+
chain,
|
|
203
|
+
decimals: SYNTHETIC_TOKEN_DECIMALS,
|
|
204
|
+
id: asset.symbol,
|
|
205
|
+
logoUrl: '',
|
|
206
|
+
name: asset.symbol,
|
|
207
|
+
symbol: asset.symbol,
|
|
208
|
+
}))
|
|
209
|
+
.sort((a, b) => b.amount - a.amount);
|
|
139
210
|
}
|
|
140
211
|
/**
|
|
141
212
|
* Transform an Untangled vault exposure report into the DeBank-normalized shape
|
|
@@ -150,7 +221,10 @@ function dominantAssetSymbol(exposure) {
|
|
|
150
221
|
* Mapping:
|
|
151
222
|
* - Each `byProtocol` entry becomes one position with a single supplying token,
|
|
152
223
|
* valued in USD (`price: 1`), on the chain from
|
|
153
|
-
* {@link UNTANGLED_PROTOCOL_REGISTRY}.
|
|
224
|
+
* {@link UNTANGLED_PROTOCOL_REGISTRY}. The token is labelled with the vault's
|
|
225
|
+
* asset only when one asset covers essentially all of it (see
|
|
226
|
+
* {@link vaultAssetSymbol}); otherwise it carries the value with no symbol,
|
|
227
|
+
* and {@link untangledAssetExposure} supplies the composition separately.
|
|
154
228
|
* - `stellar-wallet` becomes a wallet token, because custody cash is undeployed
|
|
155
229
|
* capital rather than protocol exposure.
|
|
156
230
|
* - `upshift` is split: the vault's own idle buffer (the `idle` location) joins
|
|
@@ -184,7 +258,7 @@ function transformUntangledToDebank(exposure) {
|
|
|
184
258
|
if (!exposure) {
|
|
185
259
|
return { subaccount: { positions: [], tokens: [] } };
|
|
186
260
|
}
|
|
187
|
-
const symbol =
|
|
261
|
+
const symbol = vaultAssetSymbol(exposure);
|
|
188
262
|
const vaultChain = exposure.vault?.chain || DEFAULT_CHAIN;
|
|
189
263
|
const idleUsd = idleLocationUsd(exposure);
|
|
190
264
|
const positions = [];
|
package/lib/types/vaults.d.ts
CHANGED
|
@@ -143,6 +143,16 @@ export type IExposurePerCategory = {
|
|
|
143
143
|
*/
|
|
144
144
|
export interface IVaultAllocations {
|
|
145
145
|
defi: IDebankProtocolExposure[];
|
|
146
|
+
/**
|
|
147
|
+
* The vault's asset composition, in USD (`amount` is a dollar value).
|
|
148
|
+
*
|
|
149
|
+
* On Stellar this is Untangled's `byAsset` breakdown, which decomposes the
|
|
150
|
+
* same `tvlUsd` that `defi` does along a different axis — assets rather than
|
|
151
|
+
* venues. The two do not join: which venue holds which asset is not published,
|
|
152
|
+
* so this is the only place a Stellar vault's assets are stated, and reading a
|
|
153
|
+
* per-venue asset split out of `exposurePerCategory` is not supported for
|
|
154
|
+
* multi-asset Stellar vaults.
|
|
155
|
+
*/
|
|
146
156
|
tokens: IDebankTokenExposure[];
|
|
147
157
|
cefi: IWSSubaccountCefi[];
|
|
148
158
|
unfilteredTokens: IDebankTokenExposure[];
|