@xoxno/sdk-js 1.0.223 → 1.0.225
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/sdk/stellar/__tests__/admin.test.d.ts +0 -12
- package/dist/cjs/sdk/stellar/__tests__/builders-hubasset.test.d.ts +0 -11
- package/dist/cjs/sdk/stellar/__tests__/governance.test.d.ts +0 -4
- package/dist/cjs/sdk/stellar/__tests__/lending.test.d.ts +0 -20
- package/dist/cjs/sdk/stellar/__tests__/strategy-program.test.d.ts +0 -8
- package/dist/cjs/sdk/stellar/__tests__/swap-history.test.d.ts +1 -0
- package/dist/cjs/sdk/stellar/index.d.ts +1 -0
- package/dist/cjs/sdk/stellar/swap-history.d.ts +25 -0
- package/dist/cjs/sdk/stellar/swap-index.d.ts +2 -0
- package/dist/cjs/sdk/stellar/xdr-compat.d.ts +11 -0
- package/dist/esm/index.js +3 -0
- package/dist/esm/sdk/index.js +198 -0
- package/dist/esm/sdk/stellar/admin.js +385 -0
- package/dist/esm/sdk/stellar/auth-policy.js +310 -0
- package/dist/esm/sdk/stellar/cctp.js +23 -0
- package/dist/esm/sdk/stellar/contracts.js +49 -0
- package/dist/esm/sdk/stellar/events/decode.js +415 -0
- package/dist/esm/sdk/stellar/events/id.js +62 -0
- package/dist/esm/sdk/stellar/events/index.js +2 -0
- package/dist/esm/sdk/stellar/governance.js +403 -0
- package/dist/esm/sdk/stellar/index.js +25 -0
- package/dist/esm/sdk/stellar/lending-api-types.js +3 -0
- package/dist/esm/sdk/stellar/lending-read-types.js +1 -0
- package/dist/esm/sdk/stellar/lending-read.js +341 -0
- package/dist/esm/sdk/stellar/lending.js +358 -0
- package/dist/esm/sdk/stellar/math/account.js +256 -0
- package/dist/esm/sdk/stellar/math/index.js +11 -0
- package/dist/esm/sdk/stellar/math/limits.js +273 -0
- package/dist/esm/sdk/stellar/math/projection.js +130 -0
- package/dist/esm/sdk/stellar/math/rates.js +62 -0
- package/dist/esm/sdk/stellar/math/scaled.js +156 -0
- package/dist/esm/sdk/stellar/position-mode.js +11 -0
- package/dist/esm/sdk/stellar/prepare.js +47 -0
- package/dist/esm/sdk/stellar/quote.js +87 -0
- package/dist/esm/sdk/stellar/repay-swap.js +12 -0
- package/dist/esm/sdk/stellar/route-verify.js +285 -0
- package/dist/esm/sdk/stellar/scval-encode.js +464 -0
- package/dist/esm/sdk/stellar/swap-history.js +46 -0
- package/dist/esm/sdk/stellar/swap-index.js +1 -0
- package/dist/esm/sdk/stellar/swap.js +117 -0
- package/dist/esm/sdk/stellar/xdr-compat.js +17 -0
- package/dist/esm/sdk/stellar-schema.js +129 -0
- package/dist/esm/sdk/swagger.js +1957 -0
- package/dist/esm/sdk/types.js +1 -0
- package/dist/esm/sdk/utils.js +21 -0
- package/dist/esm/utils/api.js +169 -0
- package/dist/esm/utils/const.js +11 -0
- package/dist/esm/utils/errors.js +20 -0
- package/dist/esm/utils/guards.js +29 -0
- package/dist/esm/utils/helpers.js +20 -0
- package/dist/esm/utils/regex.js +14 -0
- package/dist/esm/utils/soroban-errors.js +149 -0
- package/dist/index.cjs +1 -1
- package/dist/index.esm.js +1 -2
- package/dist/sdk/stellar/__tests__/admin.test.d.ts +0 -12
- package/dist/sdk/stellar/__tests__/builders-hubasset.test.d.ts +0 -11
- package/dist/sdk/stellar/__tests__/governance.test.d.ts +0 -4
- package/dist/sdk/stellar/__tests__/lending.test.d.ts +0 -20
- package/dist/sdk/stellar/__tests__/strategy-program.test.d.ts +0 -8
- package/dist/sdk/stellar/__tests__/swap-history.test.d.ts +1 -0
- package/dist/sdk/stellar/index.cjs +1 -1
- package/dist/sdk/stellar/index.d.ts +1 -0
- package/dist/sdk/stellar/index.esm.js +1 -1
- package/dist/sdk/stellar/swap-history.d.ts +25 -0
- package/dist/sdk/stellar/swap-index.cjs +2 -0
- package/dist/sdk/stellar/swap-index.d.ts +2 -0
- package/dist/sdk/stellar/swap-index.esm.js +2 -0
- package/dist/sdk/stellar/swap-index.esm.js.LICENSE.txt +8 -0
- package/dist/sdk/stellar/xdr-compat.d.ts +11 -0
- package/package.json +16 -5
- /package/dist/{index.esm.js.LICENSE.txt → sdk/stellar/swap-index.cjs.LICENSE.txt} +0 -0
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decoders for the Stellar lending `#[contractevent]`s (controller, pool, and
|
|
3
|
+
* the price-aggregator's `config:asset_oracle`).
|
|
4
|
+
*
|
|
5
|
+
* Decodes core protocol topics plus pool `strategy:fee` and live controller
|
|
6
|
+
* config topics used by the indexer. Intentional omissions still return
|
|
7
|
+
* `null`: `config:spoke_asset` / `config:remove_spoke_asset` / `config:hub` /
|
|
8
|
+
* `strategy:blend_migration` (handled by az-functions own decoders).
|
|
9
|
+
*
|
|
10
|
+
* The public API takes base64-XDR strings (`decodeStellarLendingEvent(topicsB64,
|
|
11
|
+
* dataB64)`) and parses them with this SDK's bundled `@stellar/stellar-sdk`, so
|
|
12
|
+
* no live `xdr.ScVal` crosses the consumer boundary. Soroban RPC `getEvents`
|
|
13
|
+
* already delivers `topic[]` and `value` as base64 XDR — pass them straight
|
|
14
|
+
* through.
|
|
15
|
+
*
|
|
16
|
+
* Most contractevent `data` serializes as an `ScMap` keyed by field name, so
|
|
17
|
+
* `scValToNative` yields a snake_case-keyed object with `bigint` for i128/u64,
|
|
18
|
+
* `number` for u32, strkey strings for addresses, strings for symbols, `null`
|
|
19
|
+
* for absent `Option`s, and arrays for `Vec`s.
|
|
20
|
+
*
|
|
21
|
+
* The hot topics (`position:batch_update`, `market:batch_state_update`) use the
|
|
22
|
+
* vec-encoded ABI v2 instead: `data` is an `ScVec` whose field order IS the ABI,
|
|
23
|
+
* so `scValToNative` yields nested arrays decoded positionally.
|
|
24
|
+
*
|
|
25
|
+
* `market:batch_params_update` is likewise single-value: `data` is the updates
|
|
26
|
+
* `ScVec` directly (NOT an ScMap wrapping `updates`) — read `d`, not `d.updates`
|
|
27
|
+
* — though each entry is itself an ScMap.
|
|
28
|
+
*/ import { Buffer } from "buffer";
|
|
29
|
+
import { scValToNative, xdr } from "@stellar/stellar-sdk";
|
|
30
|
+
/**
|
|
31
|
+
* Serialize a live `xdr.ScVal` to base64 XDR for the string-only decoder API.
|
|
32
|
+
* Use this if you hold parsed ScVals (e.g. from the high-level RPC client);
|
|
33
|
+
* `.toXDR` is wire-format/structural, so it is safe across SDK copies.
|
|
34
|
+
*/ export const toBase64Xdr = (scv)=>scv.toXDR('base64');
|
|
35
|
+
const parse = (b64)=>xdr.ScVal.fromXDR(b64, 'base64');
|
|
36
|
+
/** Build the `"<domain>:<action>"` dispatch key from the event topic ScVals. */ export const stellarLendingDispatchKey = (topicsB64)=>topicsB64.map((t)=>String(scValToNative(parse(t)))).join(':');
|
|
37
|
+
// ---- native-value coercion (scValToNative output → normalized fields) -------
|
|
38
|
+
const dec = (v)=>typeof v === 'bigint' || typeof v === 'number' ? v.toString() : String(v);
|
|
39
|
+
const num = (v)=>typeof v === 'bigint' ? Number(v) : Number(v);
|
|
40
|
+
const str = (v)=>String(v);
|
|
41
|
+
const hex = (v)=>v instanceof Uint8Array ? Buffer.from(v).toString('hex') : Buffer.isBuffer(v) ? v.toString('hex') : String(v);
|
|
42
|
+
const POSITION_MODE = [
|
|
43
|
+
'None',
|
|
44
|
+
'Multiply',
|
|
45
|
+
'Long',
|
|
46
|
+
'Short'
|
|
47
|
+
];
|
|
48
|
+
/**
|
|
49
|
+
* PositionAction u32 discriminant → legacy action string (frozen wire table).
|
|
50
|
+
* Index IS the on-chain discriminant, so entries may only be appended.
|
|
51
|
+
*
|
|
52
|
+
* `liq_seize` (5) and `liq_credit` (15) are two halves of ONE share-credit
|
|
53
|
+
* seizure and must never be summed together: `liq_seize` is the liquidated
|
|
54
|
+
* account's debit, **gross** of the protocol fee, and `liq_credit` is the
|
|
55
|
+
* receiving account's credit, **net** of it (the fee is the difference).
|
|
56
|
+
* `SeizeMode::Transfer` emits only `liq_seize`, also gross.
|
|
57
|
+
*/ const POSITION_ACTION = [
|
|
58
|
+
'supply',
|
|
59
|
+
'borrow',
|
|
60
|
+
'withdraw',
|
|
61
|
+
'repay',
|
|
62
|
+
'liq_repay',
|
|
63
|
+
'liq_seize',
|
|
64
|
+
'multiply',
|
|
65
|
+
'param_upd',
|
|
66
|
+
'sw_debt_r',
|
|
67
|
+
'sw_col_wd',
|
|
68
|
+
'rp_col_wd',
|
|
69
|
+
'rp_col_r',
|
|
70
|
+
'close_wd',
|
|
71
|
+
'migrate',
|
|
72
|
+
'rp_col_net',
|
|
73
|
+
'liq_credit'
|
|
74
|
+
];
|
|
75
|
+
const positionMode = (v)=>POSITION_MODE[num(v)] ?? 'None';
|
|
76
|
+
// Unknown discriminants surface as their decimal string instead of throwing.
|
|
77
|
+
const positionAction = (v)=>POSITION_ACTION[num(v)] ?? dec(v);
|
|
78
|
+
const vec = (v)=>Array.isArray(v) ? v : [];
|
|
79
|
+
// ---- nested struct decoders -------------------------------------------------
|
|
80
|
+
const decodeMarketParams = (p)=>({
|
|
81
|
+
maxBorrowRateRay: dec(p.max_borrow_rate),
|
|
82
|
+
baseBorrowRateRay: dec(p.base_borrow_rate),
|
|
83
|
+
slope1Ray: dec(p.slope1),
|
|
84
|
+
slope2Ray: dec(p.slope2),
|
|
85
|
+
slope3Ray: dec(p.slope3),
|
|
86
|
+
midUtilizationRay: dec(p.mid_utilization),
|
|
87
|
+
optimalUtilizationRay: dec(p.optimal_utilization),
|
|
88
|
+
maxUtilizationRay: dec(p.max_utilization),
|
|
89
|
+
reserveFactorBps: num(p.reserve_factor),
|
|
90
|
+
isFlashloanable: Boolean(p.is_flashloanable),
|
|
91
|
+
flashloanFeeBps: num(p.flashloan_fee),
|
|
92
|
+
assetId: str(p.asset_id),
|
|
93
|
+
assetDecimals: num(p.asset_decimals)
|
|
94
|
+
});
|
|
95
|
+
/**
|
|
96
|
+
* Vec-encoded position delta. Deposit entries are arrays of 10
|
|
97
|
+
* `[action, hub_id, asset, scaled_amount_ray, index_ray, amount,
|
|
98
|
+
* liq_threshold_bps, liq_bonus_bps, ltv_bps, liq_fees_bps]`; borrow entries
|
|
99
|
+
* stop after `amount` (array of 6).
|
|
100
|
+
*/ const decodePositionDelta = (e, pt)=>{
|
|
101
|
+
const isBorrow = pt === 'Borrow';
|
|
102
|
+
return {
|
|
103
|
+
action: positionAction(e[0]),
|
|
104
|
+
positionType: pt,
|
|
105
|
+
hubId: num(e[1]),
|
|
106
|
+
asset: str(e[2]),
|
|
107
|
+
scaledAmountRay: dec(e[3]),
|
|
108
|
+
indexRay: dec(e[4]),
|
|
109
|
+
amount: dec(e[5]),
|
|
110
|
+
// Borrow deltas carry no collateral risk params — surface undefined, not 0.
|
|
111
|
+
liquidationThresholdBps: isBorrow ? undefined : num(e[6]),
|
|
112
|
+
liquidationBonusBps: isBorrow ? undefined : num(e[7]),
|
|
113
|
+
loanToValueBps: isBorrow ? undefined : num(e[8]),
|
|
114
|
+
liquidationFeesBps: isBorrow ? undefined : num(e[9])
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
/** Vec-encoded account attributes: array of 3 `[owner, spoke_id, mode]`. */ const decodeAccountAttributes = (a)=>({
|
|
118
|
+
owner: str(a[0]),
|
|
119
|
+
spokeId: num(a[1]),
|
|
120
|
+
mode: positionMode(a[2])
|
|
121
|
+
});
|
|
122
|
+
/** Vec-encoded market snapshot: array of 9
|
|
123
|
+
* `[hub_id, asset, timestamp_ms, supply_index, borrow_index, cash, supplied,
|
|
124
|
+
* borrowed, revenue]`. */ const decodeMarketSnapshot = (e)=>({
|
|
125
|
+
hubId: num(e[0]),
|
|
126
|
+
asset: str(e[1]),
|
|
127
|
+
timestamp: num(e[2]),
|
|
128
|
+
supplyIndexRay: dec(e[3]),
|
|
129
|
+
borrowIndexRay: dec(e[4]),
|
|
130
|
+
cash: dec(e[5]),
|
|
131
|
+
suppliedRay: dec(e[6]),
|
|
132
|
+
borrowedRay: dec(e[7]),
|
|
133
|
+
revenueRay: dec(e[8])
|
|
134
|
+
});
|
|
135
|
+
// `scValToNative` decodes Soroban enums to `[variantName, ...payload]` arrays
|
|
136
|
+
// (unit variants in mixed enums decode to a 1-element array).
|
|
137
|
+
const enumTag = (v)=>Array.isArray(v) ? str(v[0]) : str(v);
|
|
138
|
+
const enumVal = (v)=>Array.isArray(v) ? v[1] : undefined;
|
|
139
|
+
/** Decode on-chain `PriceKey` → `{ Token }` / `{ Ref }`. */ const decodePriceKey = (key)=>{
|
|
140
|
+
const tag = enumTag(key);
|
|
141
|
+
if (tag === 'Token') return {
|
|
142
|
+
Token: str(enumVal(key))
|
|
143
|
+
};
|
|
144
|
+
if (tag === 'Ref') return {
|
|
145
|
+
Ref: str(enumVal(key))
|
|
146
|
+
};
|
|
147
|
+
// scValToNative may already yield a map
|
|
148
|
+
if (key && typeof key === 'object' && !Array.isArray(key)) {
|
|
149
|
+
const o = key;
|
|
150
|
+
if (o.Token !== undefined) return {
|
|
151
|
+
Token: str(o.Token)
|
|
152
|
+
};
|
|
153
|
+
if (o.Ref !== undefined) return {
|
|
154
|
+
Ref: str(o.Ref)
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
Token: str(key)
|
|
159
|
+
};
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* `AssetOracle` (price-aggregator event `oracle` field) → API struct.
|
|
163
|
+
* Sources stay as raw decoded objects (Feed / Scaled / LpShare) so consumers
|
|
164
|
+
* can re-encode without losing shape.
|
|
165
|
+
*/ const decodeAssetOracle = (c)=>{
|
|
166
|
+
const tolerance = c.tolerance ?? {};
|
|
167
|
+
return {
|
|
168
|
+
assetDecimals: num(c.asset_decimals),
|
|
169
|
+
maxPriceStaleSeconds: num(c.max_price_stale_seconds),
|
|
170
|
+
sources: vec(c.sources).map((s)=>s),
|
|
171
|
+
tolerance: {
|
|
172
|
+
upperRatio: num(tolerance.upper_ratio_bps),
|
|
173
|
+
lowerRatio: num(tolerance.lower_ratio_bps)
|
|
174
|
+
},
|
|
175
|
+
independence: c.independence,
|
|
176
|
+
minSanityPriceWad: dec(c.min_sanity_price_wad),
|
|
177
|
+
maxSanityPriceWad: dec(c.max_sanity_price_wad)
|
|
178
|
+
};
|
|
179
|
+
};
|
|
180
|
+
const REGISTRY = {
|
|
181
|
+
'market:create': (d)=>({
|
|
182
|
+
topic: 'market:create',
|
|
183
|
+
data: {
|
|
184
|
+
hubId: num(d.hub_id),
|
|
185
|
+
baseAsset: str(d.base_asset),
|
|
186
|
+
maxBorrowRate: dec(d.max_borrow_rate),
|
|
187
|
+
baseBorrowRate: dec(d.base_borrow_rate),
|
|
188
|
+
slope1: dec(d.slope1),
|
|
189
|
+
slope2: dec(d.slope2),
|
|
190
|
+
slope3: dec(d.slope3),
|
|
191
|
+
midUtilization: dec(d.mid_utilization),
|
|
192
|
+
optimalUtilization: dec(d.optimal_utilization),
|
|
193
|
+
maxUtilization: dec(d.max_utilization),
|
|
194
|
+
reserveFactor: num(d.reserve_factor),
|
|
195
|
+
marketAddress: str(d.market_address)
|
|
196
|
+
}
|
|
197
|
+
}),
|
|
198
|
+
'market:params_update': (d)=>({
|
|
199
|
+
topic: 'market:params_update',
|
|
200
|
+
data: {
|
|
201
|
+
asset: str(d.asset),
|
|
202
|
+
maxBorrowRateRay: dec(d.max_borrow_rate),
|
|
203
|
+
baseBorrowRateRay: dec(d.base_borrow_rate),
|
|
204
|
+
slope1Ray: dec(d.slope1),
|
|
205
|
+
slope2Ray: dec(d.slope2),
|
|
206
|
+
slope3Ray: dec(d.slope3),
|
|
207
|
+
midUtilizationRay: dec(d.mid_utilization),
|
|
208
|
+
optimalUtilizationRay: dec(d.optimal_utilization),
|
|
209
|
+
maxUtilizationRay: dec(d.max_utilization),
|
|
210
|
+
reserveFactorBps: num(d.reserve_factor)
|
|
211
|
+
}
|
|
212
|
+
}),
|
|
213
|
+
// Vec-encoded (ABI v2): data is the entries Vec directly, each an array of 9.
|
|
214
|
+
'market:batch_state_update': (d)=>({
|
|
215
|
+
topic: 'market:batch_state_update',
|
|
216
|
+
data: {
|
|
217
|
+
updates: vec(d).map((u)=>decodeMarketSnapshot(vec(u)))
|
|
218
|
+
}
|
|
219
|
+
}),
|
|
220
|
+
// Vec-encoded (single-value): `data` is the updates `Vec` directly (NOT an
|
|
221
|
+
// ScMap wrapping an `updates` field), each entry an ScMap { asset, params }.
|
|
222
|
+
'market:batch_params_update': (d)=>({
|
|
223
|
+
topic: 'market:batch_params_update',
|
|
224
|
+
data: {
|
|
225
|
+
updates: vec(d).map((entry)=>{
|
|
226
|
+
const row = entry;
|
|
227
|
+
return {
|
|
228
|
+
hubId: num(row.hub_id),
|
|
229
|
+
asset: str(row.asset),
|
|
230
|
+
params: decodeMarketParams(row.params)
|
|
231
|
+
};
|
|
232
|
+
})
|
|
233
|
+
}
|
|
234
|
+
}),
|
|
235
|
+
// Vec-encoded (ABI v2): data = [account_id, attributes, deposits, borrows].
|
|
236
|
+
// `updates` merges all deposit entries first, then all borrow entries.
|
|
237
|
+
//
|
|
238
|
+
// One event = one account. A `SeizeMode::Credit` liquidation emits TWO of
|
|
239
|
+
// these — the liquidated account first, then the receiving account (supply
|
|
240
|
+
// side only, legs whose net credit is zero omitted). Consumers must not
|
|
241
|
+
// assume one batch per liquidation, and a `Credit(0)` receiver is announced
|
|
242
|
+
// ONLY through the second batch's `accountAttributes`: there is no
|
|
243
|
+
// account-creation event to discover it from.
|
|
244
|
+
'position:batch_update': (d)=>{
|
|
245
|
+
const v = vec(d);
|
|
246
|
+
return {
|
|
247
|
+
topic: 'position:batch_update',
|
|
248
|
+
data: {
|
|
249
|
+
accountId: dec(v[0]),
|
|
250
|
+
accountAttributes: decodeAccountAttributes(vec(v[1])),
|
|
251
|
+
updates: [
|
|
252
|
+
...vec(v[2]).map((e)=>decodePositionDelta(vec(e), 'Deposit')),
|
|
253
|
+
...vec(v[3]).map((e)=>decodePositionDelta(vec(e), 'Borrow'))
|
|
254
|
+
]
|
|
255
|
+
}
|
|
256
|
+
};
|
|
257
|
+
},
|
|
258
|
+
'position:flash_loan': (d)=>({
|
|
259
|
+
topic: 'position:flash_loan',
|
|
260
|
+
data: {
|
|
261
|
+
hubId: num(d.hub_id),
|
|
262
|
+
asset: str(d.asset),
|
|
263
|
+
receiver: str(d.receiver),
|
|
264
|
+
caller: str(d.caller),
|
|
265
|
+
amount: dec(d.amount),
|
|
266
|
+
fee: dec(d.fee)
|
|
267
|
+
}
|
|
268
|
+
}),
|
|
269
|
+
// Live price-aggregator: `{ key: PriceKey, oracle: AssetOracle }`.
|
|
270
|
+
'config:asset_oracle': (d)=>({
|
|
271
|
+
topic: 'config:asset_oracle',
|
|
272
|
+
data: {
|
|
273
|
+
key: decodePriceKey(d.key),
|
|
274
|
+
oracle: decodeAssetOracle(d.oracle)
|
|
275
|
+
}
|
|
276
|
+
}),
|
|
277
|
+
// Legacy pre-composable payload (historical ledgers only).
|
|
278
|
+
'config:oracle': (d)=>({
|
|
279
|
+
topic: 'config:oracle',
|
|
280
|
+
data: {
|
|
281
|
+
asset: str(d.asset),
|
|
282
|
+
oracle: decodeAssetOracle(d.config ?? d.oracle)
|
|
283
|
+
}
|
|
284
|
+
}),
|
|
285
|
+
// `repaidUsdWad` is the repayment the pool MEASURED, not the planned one:
|
|
286
|
+
// net of any overpayment refunded and of any shortfall from an
|
|
287
|
+
// under-delivering debt token, so it agrees with the batch's `liq_repay`
|
|
288
|
+
// legs. The event carries no seizure or protocol-fee figure at all.
|
|
289
|
+
'position:liquidation': (d)=>({
|
|
290
|
+
topic: 'position:liquidation',
|
|
291
|
+
data: {
|
|
292
|
+
liquidator: str(d.liquidator),
|
|
293
|
+
accountId: dec(d.account_id),
|
|
294
|
+
repaidUsdWad: dec(d.repaid_usd_wad),
|
|
295
|
+
bonusBps: dec(d.bonus_bps)
|
|
296
|
+
}
|
|
297
|
+
}),
|
|
298
|
+
'debt:bad_debt': (d)=>({
|
|
299
|
+
topic: 'debt:bad_debt',
|
|
300
|
+
data: {
|
|
301
|
+
accountId: dec(d.account_id),
|
|
302
|
+
totalBorrowUsdWad: dec(d.total_borrow_usd_wad),
|
|
303
|
+
totalCollateralUsdWad: dec(d.total_collateral_usd_wad)
|
|
304
|
+
}
|
|
305
|
+
}),
|
|
306
|
+
'strategy:initial_payment': (d)=>({
|
|
307
|
+
topic: 'strategy:initial_payment',
|
|
308
|
+
data: {
|
|
309
|
+
token: str(d.token),
|
|
310
|
+
amount: dec(d.amount),
|
|
311
|
+
usdValueWad: dec(d.usd_value_wad),
|
|
312
|
+
accountId: dec(d.account_id)
|
|
313
|
+
}
|
|
314
|
+
}),
|
|
315
|
+
'strategy:fee': (d)=>({
|
|
316
|
+
topic: 'strategy:fee',
|
|
317
|
+
data: {
|
|
318
|
+
hubId: num(d.hub_id),
|
|
319
|
+
asset: str(d.asset),
|
|
320
|
+
amount: dec(d.amount),
|
|
321
|
+
fee: dec(d.fee),
|
|
322
|
+
amountSent: dec(d.amount_sent)
|
|
323
|
+
}
|
|
324
|
+
}),
|
|
325
|
+
'config:approve_blend_pool': (d)=>({
|
|
326
|
+
topic: 'config:approve_blend_pool',
|
|
327
|
+
data: {
|
|
328
|
+
pool: str(d.pool ?? d.blend_pool ?? d.token),
|
|
329
|
+
approved: Boolean(d.approved ?? d.is_approved ?? true)
|
|
330
|
+
}
|
|
331
|
+
}),
|
|
332
|
+
'config:approve_token': (d)=>({
|
|
333
|
+
topic: 'config:approve_token',
|
|
334
|
+
data: {
|
|
335
|
+
wasmHash: hex(d.wasm_hash ?? d.pool),
|
|
336
|
+
approved: Boolean(d.approved)
|
|
337
|
+
}
|
|
338
|
+
}),
|
|
339
|
+
'config:swap_aggregator': (d)=>({
|
|
340
|
+
topic: 'config:swap_aggregator',
|
|
341
|
+
data: {
|
|
342
|
+
aggregator: str(d.aggregator ?? d.swap_aggregator)
|
|
343
|
+
}
|
|
344
|
+
}),
|
|
345
|
+
'config:price_aggregator': (d)=>({
|
|
346
|
+
topic: 'config:price_aggregator',
|
|
347
|
+
data: {
|
|
348
|
+
aggregator: str(d.aggregator ?? d.price_aggregator)
|
|
349
|
+
}
|
|
350
|
+
}),
|
|
351
|
+
'config:aggregator': (d)=>({
|
|
352
|
+
topic: 'config:aggregator',
|
|
353
|
+
data: {
|
|
354
|
+
aggregator: str(d.aggregator)
|
|
355
|
+
}
|
|
356
|
+
}),
|
|
357
|
+
'config:accumulator': (d)=>({
|
|
358
|
+
topic: 'config:accumulator',
|
|
359
|
+
data: {
|
|
360
|
+
accumulator: str(d.accumulator)
|
|
361
|
+
}
|
|
362
|
+
}),
|
|
363
|
+
'config:pool_template': (d)=>({
|
|
364
|
+
topic: 'config:pool_template',
|
|
365
|
+
data: {
|
|
366
|
+
wasmHash: hex(d.wasm_hash)
|
|
367
|
+
}
|
|
368
|
+
}),
|
|
369
|
+
'config:position_limits': (d)=>({
|
|
370
|
+
topic: 'config:position_limits',
|
|
371
|
+
data: {
|
|
372
|
+
maxSupplyPositions: num(d.max_supply_positions),
|
|
373
|
+
maxBorrowPositions: num(d.max_borrow_positions)
|
|
374
|
+
}
|
|
375
|
+
}),
|
|
376
|
+
'config:min_borrow_collateral': (d)=>({
|
|
377
|
+
topic: 'config:min_borrow_collateral',
|
|
378
|
+
data: {
|
|
379
|
+
minBorrowCollateralUsdWad: dec(d.min_borrow_collateral_usd_wad ?? d.floor_wad ?? d.value)
|
|
380
|
+
}
|
|
381
|
+
}),
|
|
382
|
+
'config:oracle_disabled': (d)=>({
|
|
383
|
+
topic: 'config:oracle_disabled',
|
|
384
|
+
data: {
|
|
385
|
+
asset: str(d.asset)
|
|
386
|
+
}
|
|
387
|
+
}),
|
|
388
|
+
'config:spoke': (d)=>{
|
|
389
|
+
const s = d.spoke;
|
|
390
|
+
return {
|
|
391
|
+
topic: 'config:spoke',
|
|
392
|
+
data: {
|
|
393
|
+
spokeId: num(s.spoke_id),
|
|
394
|
+
isDeprecated: Boolean(s.is_deprecated),
|
|
395
|
+
liquidationTargetHfWad: dec(s.liquidation_target_hf_wad),
|
|
396
|
+
healthFactorForMaxBonusWad: dec(s.hf_for_max_bonus_wad),
|
|
397
|
+
liquidationBonusFactorBps: num(s.liquidation_bonus_factor_bps)
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
/** Topic keys this SDK can decode. See the module header for the intentional
|
|
403
|
+
* omissions. */ export const STELLAR_LENDING_TOPICS = Object.freeze(Object.keys(REGISTRY));
|
|
404
|
+
/**
|
|
405
|
+
* Decode a Stellar lending controller event from its base64-XDR topics and
|
|
406
|
+
* data (exactly as Soroban RPC `getEvents` delivers them). Returns `null` for a
|
|
407
|
+
* topic this SDK does not decode (e.g. access-control `role_granted` events),
|
|
408
|
+
* so consumers can skip unknown events without throwing.
|
|
409
|
+
*/ export function decodeStellarLendingEvent(topicsB64, dataB64) {
|
|
410
|
+
const key = stellarLendingDispatchKey(topicsB64);
|
|
411
|
+
const decoder = REGISTRY[key];
|
|
412
|
+
if (!decoder) return null;
|
|
413
|
+
const native = scValToNative(parse(dataB64));
|
|
414
|
+
return decoder(native);
|
|
415
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stellar lending activity-id derivation: the inputs that compose
|
|
3
|
+
* `NftActivityDoc.id` (`${txHash}-${eventIdentifier}-${eventOrder}`).
|
|
4
|
+
*
|
|
5
|
+
* The activity-type string values mirror `@xoxno/types` `XoxnoLendingActivity`,
|
|
6
|
+
* inlined rather than imported since `@xoxno/types` does not re-export its enums
|
|
7
|
+
* on its top-level type surface.
|
|
8
|
+
*/ /**
|
|
9
|
+
* A child delta's contribution to `eventOrder`: `base * STRIDE + childIndex`.
|
|
10
|
+
* The stride bounds the number of child deltas per base event before two base
|
|
11
|
+
* events could collide.
|
|
12
|
+
*/ export const SYNTHETIC_EVENT_ORDER_STRIDE = 10000;
|
|
13
|
+
/**
|
|
14
|
+
* NFT identifier for a Stellar lending account: the position-NFT contract
|
|
15
|
+
* address with the decimal account id (== token id) appended, e.g.
|
|
16
|
+
* `CDVN5…LPSY-42`. The account IS an on-chain NFT since the NFT-ownership
|
|
17
|
+
* protocol; the collection is the contract itself and there is no synthetic
|
|
18
|
+
* ticker or hex encoding anymore.
|
|
19
|
+
*/ export function buildStellarLendingIdentifier(positionNftContract, accountId) {
|
|
20
|
+
return `${positionNftContract}-${BigInt(accountId).toString(10)}`;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Extract the base event order from a Soroban RPC event id (`<ledger>-<index>`).
|
|
24
|
+
* Returns 0 when the id is malformed or has no index segment.
|
|
25
|
+
*/ export function extractEventOrder(eventId) {
|
|
26
|
+
const parts = eventId.split('-');
|
|
27
|
+
return parts.length >= 2 ? parseInt(parts[1], 10) || 0 : 0;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Stable per-child ordering within one base event:
|
|
31
|
+
* `baseEventOrder * 10_000 + childIndex`. Keeps multiple deltas emitted by a
|
|
32
|
+
* single transaction deterministically ordered and collision-free.
|
|
33
|
+
*/ export function syntheticEventOrder(baseEventOrder, childIndex = 0) {
|
|
34
|
+
return baseEventOrder * SYNTHETIC_EVENT_ORDER_STRIDE + childIndex;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Map a position-delta `action` symbol to its `XoxnoLendingActivity` value.
|
|
38
|
+
* The tag list is authoritative in `rs-lending-xlm
|
|
39
|
+
* contracts/controller/src/events/mod.rs` (`PositionAction`).
|
|
40
|
+
*
|
|
41
|
+
* `liq_credit` is deliberately NOT `lendingLiquidateSeizeCollateral`. That
|
|
42
|
+
* activity renders as "Liquidated by / Liquidation", and a share-credit leg
|
|
43
|
+
* lands on the *liquidator's* receiving account, which was not liquidated —
|
|
44
|
+
* so it maps to the plain position update instead. It also keeps a
|
|
45
|
+
* protocol-wide sum over `lendingLiquidateSeizeCollateral` from
|
|
46
|
+
* double-counting the same seizure twice (gross on the liquidated account,
|
|
47
|
+
* net on the receiver). The raw `liq_credit` / `liq_seize` strings stay on
|
|
48
|
+
* the delta itself, so the two legs remain distinguishable downstream.
|
|
49
|
+
*/ export function mapStellarPositionActivityType(action) {
|
|
50
|
+
switch(action){
|
|
51
|
+
case 'liq_repay':
|
|
52
|
+
return 'lendingLiquidateRepayDebt';
|
|
53
|
+
case 'liq_seize':
|
|
54
|
+
return 'lendingLiquidateSeizeCollateral';
|
|
55
|
+
case 'liq_credit':
|
|
56
|
+
return 'lendingUpdateAccountPosition';
|
|
57
|
+
case 'param_upd':
|
|
58
|
+
return 'lendingUpdateAccountParameters';
|
|
59
|
+
default:
|
|
60
|
+
return 'lendingUpdateAccountPosition';
|
|
61
|
+
}
|
|
62
|
+
}
|