@xoxno/sdk-js 1.0.224 → 1.0.226

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 (67) hide show
  1. package/README.md +39 -0
  2. package/dist/cjs/sdk/stellar/index.d.ts +1 -0
  3. package/dist/cjs/sdk/stellar/read/__tests__/client.test.d.ts +1 -0
  4. package/dist/cjs/sdk/stellar/read/client.d.ts +19 -0
  5. package/dist/cjs/sdk/stellar/read/index.d.ts +4 -0
  6. package/dist/cjs/sdk/stellar/read/schemas.d.ts +370 -0
  7. package/dist/cjs/sdk/stellar/read/types.d.ts +83 -0
  8. package/dist/esm/index.js +3 -0
  9. package/dist/esm/sdk/index.js +198 -0
  10. package/dist/esm/sdk/stellar/admin.js +385 -0
  11. package/dist/esm/sdk/stellar/auth-policy.js +310 -0
  12. package/dist/esm/sdk/stellar/cctp.js +23 -0
  13. package/dist/esm/sdk/stellar/contracts.js +49 -0
  14. package/dist/esm/sdk/stellar/events/decode.js +415 -0
  15. package/dist/esm/sdk/stellar/events/id.js +62 -0
  16. package/dist/esm/sdk/stellar/events/index.js +2 -0
  17. package/dist/esm/sdk/stellar/governance.js +403 -0
  18. package/dist/esm/sdk/stellar/index.js +26 -0
  19. package/dist/esm/sdk/stellar/lending-api-types.js +3 -0
  20. package/dist/esm/sdk/stellar/lending-read-types.js +1 -0
  21. package/dist/esm/sdk/stellar/lending-read.js +341 -0
  22. package/dist/esm/sdk/stellar/lending.js +358 -0
  23. package/dist/esm/sdk/stellar/math/account.js +256 -0
  24. package/dist/esm/sdk/stellar/math/index.js +11 -0
  25. package/dist/esm/sdk/stellar/math/limits.js +273 -0
  26. package/dist/esm/sdk/stellar/math/projection.js +130 -0
  27. package/dist/esm/sdk/stellar/math/rates.js +62 -0
  28. package/dist/esm/sdk/stellar/math/scaled.js +156 -0
  29. package/dist/esm/sdk/stellar/position-mode.js +11 -0
  30. package/dist/esm/sdk/stellar/prepare.js +47 -0
  31. package/dist/esm/sdk/stellar/quote.js +87 -0
  32. package/dist/esm/sdk/stellar/read/client.js +82 -0
  33. package/dist/esm/sdk/stellar/read/index.js +3 -0
  34. package/dist/esm/sdk/stellar/read/schemas.js +158 -0
  35. package/dist/esm/sdk/stellar/read/types.js +1 -0
  36. package/dist/esm/sdk/stellar/repay-swap.js +12 -0
  37. package/dist/esm/sdk/stellar/route-verify.js +285 -0
  38. package/dist/esm/sdk/stellar/scval-encode.js +464 -0
  39. package/dist/esm/sdk/stellar/swap-history.js +46 -0
  40. package/dist/esm/sdk/stellar/swap-index.js +1 -0
  41. package/dist/esm/sdk/stellar/swap.js +117 -0
  42. package/dist/esm/sdk/stellar/xdr-compat.js +17 -0
  43. package/dist/esm/sdk/stellar-schema.js +129 -0
  44. package/dist/esm/sdk/swagger.js +1957 -0
  45. package/dist/esm/sdk/types.js +1 -0
  46. package/dist/esm/sdk/utils.js +21 -0
  47. package/dist/esm/utils/api.js +169 -0
  48. package/dist/esm/utils/const.js +11 -0
  49. package/dist/esm/utils/errors.js +20 -0
  50. package/dist/esm/utils/guards.js +29 -0
  51. package/dist/esm/utils/helpers.js +20 -0
  52. package/dist/esm/utils/regex.js +14 -0
  53. package/dist/esm/utils/soroban-errors.js +149 -0
  54. package/dist/index.cjs +1 -1
  55. package/dist/index.esm.js +1 -2
  56. package/dist/sdk/stellar/index.cjs +1 -1
  57. package/dist/sdk/stellar/index.d.ts +1 -0
  58. package/dist/sdk/stellar/index.esm.js +1 -1
  59. package/dist/sdk/stellar/read/__tests__/client.test.d.ts +1 -0
  60. package/dist/sdk/stellar/read/client.d.ts +19 -0
  61. package/dist/sdk/stellar/read/index.cjs +1 -0
  62. package/dist/sdk/stellar/read/index.d.ts +4 -0
  63. package/dist/sdk/stellar/read/index.esm.js +1 -0
  64. package/dist/sdk/stellar/read/schemas.d.ts +370 -0
  65. package/dist/sdk/stellar/read/types.d.ts +83 -0
  66. package/package.json +20 -4
  67. package/dist/index.esm.js.LICENSE.txt +0 -8
@@ -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
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./decode.js";
2
+ export * from "./id.js";