@exponent-labs/loopscale-deserializer 0.9.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/src/index.ts ADDED
@@ -0,0 +1,504 @@
1
+ import * as borsh from "@coral-xyz/borsh"
2
+ import { Connection, PublicKey } from "@solana/web3.js"
3
+ import BN from "bn.js" // eslint-disable-line @typescript-eslint/no-unused-vars
4
+ import Decimal from "decimal.js"
5
+
6
+ import {
7
+ CapMonitor,
8
+ CapMonitorFields,
9
+ CapMonitorJSON,
10
+ CollateralData,
11
+ CollateralDataFields,
12
+ CollateralDataJSON,
13
+ Duration,
14
+ ExternalYieldAccounts,
15
+ ExternalYieldAccountsFields,
16
+ ExternalYieldAccountsJSON,
17
+ Ledger,
18
+ LedgerFields,
19
+ LedgerJSON,
20
+ } from "./types"
21
+
22
+ export { CapMonitor, CollateralData, Duration, ExternalYieldAccounts, Ledger }
23
+
24
+ export const PROGRAM_ID = new PublicKey("1oopBoJG58DgkUVKkEzKgyG9dvRmpgeEm1AVjoHkF78")
25
+
26
+ const POD_DECIMAL_SCALE = new Decimal(10).pow(18)
27
+
28
+ /** Convert a PodDecimal (3 x u64 LE = 192-bit LE integer scaled by 10^18) to Decimal */
29
+ export function podDecimalToDecimal(parts: BN[]): Decimal {
30
+ const lo = BigInt(parts[0].toString())
31
+ const mid = BigInt(parts[1].toString()) << BigInt(64)
32
+ const hi = BigInt(parts[2].toString()) << BigInt(128)
33
+ const raw = lo + mid + hi
34
+ return new Decimal(raw.toString()).div(POD_DECIMAL_SCALE)
35
+ }
36
+
37
+ // ============================================================================
38
+ // Loan
39
+ // ============================================================================
40
+
41
+ export interface LoanFields {
42
+ version: number
43
+ bump: number
44
+ loanStatus: number
45
+ borrower: PublicKey
46
+ nonce: BN
47
+ startTime: BN
48
+ ledgers: Array<LedgerFields>
49
+ collateral: Array<CollateralDataFields>
50
+ weightMatrix: Array<Array<number>>
51
+ ltvMatrix: Array<Array<number>>
52
+ lqtMatrix: Array<Array<number>>
53
+ }
54
+
55
+ export interface LoanJSON {
56
+ version: number
57
+ bump: number
58
+ loanStatus: number
59
+ borrower: string
60
+ nonce: string
61
+ startTime: string
62
+ ledgers: Array<LedgerJSON>
63
+ collateral: Array<CollateralDataJSON>
64
+ weightMatrix: Array<Array<number>>
65
+ ltvMatrix: Array<Array<number>>
66
+ lqtMatrix: Array<Array<number>>
67
+ }
68
+
69
+ export class Loan {
70
+ readonly version: number
71
+ readonly bump: number
72
+ readonly loanStatus: number
73
+ readonly borrower: PublicKey
74
+ readonly nonce: BN
75
+ readonly startTime: BN
76
+ readonly ledgers: Array<Ledger>
77
+ readonly collateral: Array<CollateralData>
78
+ readonly weightMatrix: Array<Array<number>>
79
+ readonly ltvMatrix: Array<Array<number>>
80
+ readonly lqtMatrix: Array<Array<number>>
81
+
82
+ static readonly discriminator = Buffer.from([20, 195, 70, 117, 165, 227, 182, 1])
83
+
84
+ static readonly layout = borsh.struct([
85
+ borsh.u8("version"),
86
+ borsh.u8("bump"),
87
+ borsh.u8("loanStatus"),
88
+ borsh.publicKey("borrower"),
89
+ borsh.u64("nonce"),
90
+ borsh.u64("startTime"),
91
+ borsh.array(Ledger.layout(), 5, "ledgers"),
92
+ borsh.array(CollateralData.layout(), 5, "collateral"),
93
+ borsh.array(borsh.array(borsh.u32(), 5), 5, "weightMatrix"),
94
+ borsh.array(borsh.array(borsh.u32(), 5), 5, "ltvMatrix"),
95
+ borsh.array(borsh.array(borsh.u32(), 5), 5, "lqtMatrix"),
96
+ ])
97
+
98
+ constructor(fields: LoanFields) {
99
+ this.version = fields.version
100
+ this.bump = fields.bump
101
+ this.loanStatus = fields.loanStatus
102
+ this.borrower = fields.borrower
103
+ this.nonce = fields.nonce
104
+ this.startTime = fields.startTime
105
+ this.ledgers = fields.ledgers.map((l) => new Ledger({ ...l }))
106
+ this.collateral = fields.collateral.map((c) => new CollateralData({ ...c }))
107
+ this.weightMatrix = fields.weightMatrix
108
+ this.ltvMatrix = fields.ltvMatrix
109
+ this.lqtMatrix = fields.lqtMatrix
110
+ }
111
+
112
+ static async fetch(c: Connection, address: PublicKey, programId: PublicKey = PROGRAM_ID): Promise<Loan | null> {
113
+ const info = await c.getAccountInfo(address)
114
+
115
+ if (info === null) {
116
+ return null
117
+ }
118
+ if (!info.owner.equals(programId)) {
119
+ throw new Error("account doesn't belong to this program")
120
+ }
121
+
122
+ return this.decode(info.data)
123
+ }
124
+
125
+ static async fetchMultiple(
126
+ c: Connection,
127
+ addresses: PublicKey[],
128
+ programId: PublicKey = PROGRAM_ID,
129
+ ): Promise<Array<Loan | null>> {
130
+ const infos = await c.getMultipleAccountsInfo(addresses)
131
+
132
+ return infos.map((info) => {
133
+ if (info === null) {
134
+ return null
135
+ }
136
+ if (!info.owner.equals(programId)) {
137
+ throw new Error("account doesn't belong to this program")
138
+ }
139
+
140
+ return this.decode(info.data)
141
+ })
142
+ }
143
+
144
+ static decode(data: Buffer): Loan {
145
+ if (!data.slice(0, 8).equals(Loan.discriminator)) {
146
+ throw new Error("invalid account discriminator")
147
+ }
148
+
149
+ const dec = Loan.layout.decode(data.slice(8))
150
+
151
+ return new Loan({
152
+ version: dec.version,
153
+ bump: dec.bump,
154
+ loanStatus: dec.loanStatus,
155
+ borrower: dec.borrower,
156
+ nonce: dec.nonce,
157
+ startTime: dec.startTime,
158
+ ledgers: dec.ledgers.map((l: any) => Ledger.fromDecoded(l)), // eslint-disable-line @typescript-eslint/no-explicit-any
159
+ collateral: dec.collateral.map((c: any) => CollateralData.fromDecoded(c)), // eslint-disable-line @typescript-eslint/no-explicit-any
160
+ weightMatrix: dec.weightMatrix,
161
+ ltvMatrix: dec.ltvMatrix,
162
+ lqtMatrix: dec.lqtMatrix,
163
+ })
164
+ }
165
+
166
+ toJSON(): LoanJSON {
167
+ return {
168
+ version: this.version,
169
+ bump: this.bump,
170
+ loanStatus: this.loanStatus,
171
+ borrower: this.borrower.toString(),
172
+ nonce: this.nonce.toString(),
173
+ startTime: this.startTime.toString(),
174
+ ledgers: this.ledgers.map((l) => l.toJSON()),
175
+ collateral: this.collateral.map((c) => c.toJSON()),
176
+ weightMatrix: this.weightMatrix,
177
+ ltvMatrix: this.ltvMatrix,
178
+ lqtMatrix: this.lqtMatrix,
179
+ }
180
+ }
181
+
182
+ static fromJSON(obj: LoanJSON): Loan {
183
+ return new Loan({
184
+ version: obj.version,
185
+ bump: obj.bump,
186
+ loanStatus: obj.loanStatus,
187
+ borrower: new PublicKey(obj.borrower),
188
+ nonce: new BN(obj.nonce),
189
+ startTime: new BN(obj.startTime),
190
+ ledgers: obj.ledgers.map((l) => Ledger.fromJSON(l)),
191
+ collateral: obj.collateral.map((c) => CollateralData.fromJSON(c)),
192
+ weightMatrix: obj.weightMatrix,
193
+ ltvMatrix: obj.ltvMatrix,
194
+ lqtMatrix: obj.lqtMatrix,
195
+ })
196
+ }
197
+ }
198
+
199
+ // ============================================================================
200
+ // Strategy
201
+ // ============================================================================
202
+
203
+ export interface StrategyFields {
204
+ version: number
205
+ nonce: PublicKey
206
+ bump: number
207
+ principalMint: PublicKey
208
+ lender: PublicKey
209
+ originationsEnabled: number
210
+ externalYieldSource: number
211
+ /** PodDecimal: 24 bytes stored as 3 x u64 */
212
+ interestPerSecond: Array<BN>
213
+ lastAccruedTimestamp: BN
214
+ liquidityBuffer: BN
215
+ tokenBalance: BN
216
+ interestFee: BN
217
+ principalFee: BN
218
+ originationFee: BN
219
+ originationCap: BN
220
+ externalYieldAmount: BN
221
+ currentDeployedAmount: BN
222
+ outstandingInterestAmount: BN
223
+ feeClaimable: BN
224
+ cumulativePrincipalOriginated: BN
225
+ cumulativeInterestAccrued: BN
226
+ cumulativeLoanCount: BN
227
+ activeLoanCount: BN
228
+ marketInformation: PublicKey
229
+ collateralMap: Array<Array<BN>>
230
+ externalYieldAccounts: ExternalYieldAccountsFields
231
+ supplyMonitor: CapMonitorFields
232
+ withdrawMonitor: CapMonitorFields
233
+ borrowMonitor: CapMonitorFields
234
+ }
235
+
236
+ export interface StrategyJSON {
237
+ version: number
238
+ nonce: string
239
+ bump: number
240
+ principalMint: string
241
+ lender: string
242
+ originationsEnabled: number
243
+ externalYieldSource: number
244
+ interestPerSecond: Array<string>
245
+ lastAccruedTimestamp: string
246
+ liquidityBuffer: string
247
+ tokenBalance: string
248
+ interestFee: string
249
+ principalFee: string
250
+ originationFee: string
251
+ originationCap: string
252
+ externalYieldAmount: string
253
+ currentDeployedAmount: string
254
+ outstandingInterestAmount: string
255
+ feeClaimable: string
256
+ cumulativePrincipalOriginated: string
257
+ cumulativeInterestAccrued: string
258
+ cumulativeLoanCount: string
259
+ activeLoanCount: string
260
+ marketInformation: string
261
+ collateralMap: Array<Array<string>>
262
+ externalYieldAccounts: ExternalYieldAccountsJSON
263
+ supplyMonitor: CapMonitorJSON
264
+ withdrawMonitor: CapMonitorJSON
265
+ borrowMonitor: CapMonitorJSON
266
+ }
267
+
268
+ export class Strategy {
269
+ readonly version: number
270
+ readonly nonce: PublicKey
271
+ readonly bump: number
272
+ readonly principalMint: PublicKey
273
+ readonly lender: PublicKey
274
+ readonly originationsEnabled: number
275
+ readonly externalYieldSource: number
276
+ readonly interestPerSecond: Array<BN>
277
+ readonly lastAccruedTimestamp: BN
278
+ readonly liquidityBuffer: BN
279
+ readonly tokenBalance: BN
280
+ readonly interestFee: BN
281
+ readonly principalFee: BN
282
+ readonly originationFee: BN
283
+ readonly originationCap: BN
284
+ readonly externalYieldAmount: BN
285
+ readonly currentDeployedAmount: BN
286
+ readonly outstandingInterestAmount: BN
287
+ readonly feeClaimable: BN
288
+ readonly cumulativePrincipalOriginated: BN
289
+ readonly cumulativeInterestAccrued: BN
290
+ readonly cumulativeLoanCount: BN
291
+ readonly activeLoanCount: BN
292
+ readonly marketInformation: PublicKey
293
+ readonly collateralMap: Array<Array<BN>>
294
+ readonly externalYieldAccounts: ExternalYieldAccounts
295
+ readonly supplyMonitor: CapMonitor
296
+ readonly withdrawMonitor: CapMonitor
297
+ readonly borrowMonitor: CapMonitor
298
+
299
+ static readonly discriminator = Buffer.from([174, 110, 39, 119, 82, 106, 169, 102])
300
+
301
+ static readonly layout = borsh.struct([
302
+ borsh.u8("version"),
303
+ borsh.publicKey("nonce"),
304
+ borsh.u8("bump"),
305
+ borsh.publicKey("principalMint"),
306
+ borsh.publicKey("lender"),
307
+ borsh.u8("originationsEnabled"),
308
+ borsh.u8("externalYieldSource"),
309
+ borsh.array(borsh.u64(), 3, "interestPerSecond"),
310
+ borsh.u64("lastAccruedTimestamp"),
311
+ borsh.u64("liquidityBuffer"),
312
+ borsh.u64("tokenBalance"),
313
+ borsh.u64("interestFee"),
314
+ borsh.u64("principalFee"),
315
+ borsh.u64("originationFee"),
316
+ borsh.u64("originationCap"),
317
+ borsh.u64("externalYieldAmount"),
318
+ borsh.u64("currentDeployedAmount"),
319
+ borsh.u64("outstandingInterestAmount"),
320
+ borsh.u64("feeClaimable"),
321
+ borsh.u128("cumulativePrincipalOriginated"),
322
+ borsh.u128("cumulativeInterestAccrued"),
323
+ borsh.u64("cumulativeLoanCount"),
324
+ borsh.u64("activeLoanCount"),
325
+ borsh.publicKey("marketInformation"),
326
+ borsh.array(borsh.array(borsh.u64(), 5), 200, "collateralMap"),
327
+ ExternalYieldAccounts.layout("externalYieldAccounts"),
328
+ CapMonitor.layout("supplyMonitor"),
329
+ CapMonitor.layout("withdrawMonitor"),
330
+ CapMonitor.layout("borrowMonitor"),
331
+ ])
332
+
333
+ constructor(fields: StrategyFields) {
334
+ this.version = fields.version
335
+ this.nonce = fields.nonce
336
+ this.bump = fields.bump
337
+ this.principalMint = fields.principalMint
338
+ this.lender = fields.lender
339
+ this.originationsEnabled = fields.originationsEnabled
340
+ this.externalYieldSource = fields.externalYieldSource
341
+ this.interestPerSecond = fields.interestPerSecond
342
+ this.lastAccruedTimestamp = fields.lastAccruedTimestamp
343
+ this.liquidityBuffer = fields.liquidityBuffer
344
+ this.tokenBalance = fields.tokenBalance
345
+ this.interestFee = fields.interestFee
346
+ this.principalFee = fields.principalFee
347
+ this.originationFee = fields.originationFee
348
+ this.originationCap = fields.originationCap
349
+ this.externalYieldAmount = fields.externalYieldAmount
350
+ this.currentDeployedAmount = fields.currentDeployedAmount
351
+ this.outstandingInterestAmount = fields.outstandingInterestAmount
352
+ this.feeClaimable = fields.feeClaimable
353
+ this.cumulativePrincipalOriginated = fields.cumulativePrincipalOriginated
354
+ this.cumulativeInterestAccrued = fields.cumulativeInterestAccrued
355
+ this.cumulativeLoanCount = fields.cumulativeLoanCount
356
+ this.activeLoanCount = fields.activeLoanCount
357
+ this.marketInformation = fields.marketInformation
358
+ this.collateralMap = fields.collateralMap
359
+ this.externalYieldAccounts = new ExternalYieldAccounts({ ...fields.externalYieldAccounts })
360
+ this.supplyMonitor = new CapMonitor({ ...fields.supplyMonitor })
361
+ this.withdrawMonitor = new CapMonitor({ ...fields.withdrawMonitor })
362
+ this.borrowMonitor = new CapMonitor({ ...fields.borrowMonitor })
363
+ }
364
+
365
+ static async fetch(c: Connection, address: PublicKey, programId: PublicKey = PROGRAM_ID): Promise<Strategy | null> {
366
+ const info = await c.getAccountInfo(address)
367
+
368
+ if (info === null) {
369
+ return null
370
+ }
371
+ if (!info.owner.equals(programId)) {
372
+ throw new Error("account doesn't belong to this program")
373
+ }
374
+
375
+ return this.decode(info.data)
376
+ }
377
+
378
+ static async fetchMultiple(
379
+ c: Connection,
380
+ addresses: PublicKey[],
381
+ programId: PublicKey = PROGRAM_ID,
382
+ ): Promise<Array<Strategy | null>> {
383
+ const infos = await c.getMultipleAccountsInfo(addresses)
384
+
385
+ return infos.map((info) => {
386
+ if (info === null) {
387
+ return null
388
+ }
389
+ if (!info.owner.equals(programId)) {
390
+ throw new Error("account doesn't belong to this program")
391
+ }
392
+
393
+ return this.decode(info.data)
394
+ })
395
+ }
396
+
397
+ static decode(data: Buffer): Strategy {
398
+ if (!data.slice(0, 8).equals(Strategy.discriminator)) {
399
+ throw new Error("invalid account discriminator")
400
+ }
401
+
402
+ const dec = Strategy.layout.decode(data.slice(8))
403
+
404
+ return new Strategy({
405
+ version: dec.version,
406
+ nonce: dec.nonce,
407
+ bump: dec.bump,
408
+ principalMint: dec.principalMint,
409
+ lender: dec.lender,
410
+ originationsEnabled: dec.originationsEnabled,
411
+ externalYieldSource: dec.externalYieldSource,
412
+ interestPerSecond: dec.interestPerSecond,
413
+ lastAccruedTimestamp: dec.lastAccruedTimestamp,
414
+ liquidityBuffer: dec.liquidityBuffer,
415
+ tokenBalance: dec.tokenBalance,
416
+ interestFee: dec.interestFee,
417
+ principalFee: dec.principalFee,
418
+ originationFee: dec.originationFee,
419
+ originationCap: dec.originationCap,
420
+ externalYieldAmount: dec.externalYieldAmount,
421
+ currentDeployedAmount: dec.currentDeployedAmount,
422
+ outstandingInterestAmount: dec.outstandingInterestAmount,
423
+ feeClaimable: dec.feeClaimable,
424
+ cumulativePrincipalOriginated: dec.cumulativePrincipalOriginated,
425
+ cumulativeInterestAccrued: dec.cumulativeInterestAccrued,
426
+ cumulativeLoanCount: dec.cumulativeLoanCount,
427
+ activeLoanCount: dec.activeLoanCount,
428
+ marketInformation: dec.marketInformation,
429
+ collateralMap: dec.collateralMap,
430
+ externalYieldAccounts: ExternalYieldAccounts.fromDecoded(dec.externalYieldAccounts),
431
+ supplyMonitor: CapMonitor.fromDecoded(dec.supplyMonitor),
432
+ withdrawMonitor: CapMonitor.fromDecoded(dec.withdrawMonitor),
433
+ borrowMonitor: CapMonitor.fromDecoded(dec.borrowMonitor),
434
+ })
435
+ }
436
+
437
+ toJSON(): StrategyJSON {
438
+ return {
439
+ version: this.version,
440
+ nonce: this.nonce.toString(),
441
+ bump: this.bump,
442
+ principalMint: this.principalMint.toString(),
443
+ lender: this.lender.toString(),
444
+ originationsEnabled: this.originationsEnabled,
445
+ externalYieldSource: this.externalYieldSource,
446
+ interestPerSecond: this.interestPerSecond.map((item) => item.toString()),
447
+ lastAccruedTimestamp: this.lastAccruedTimestamp.toString(),
448
+ liquidityBuffer: this.liquidityBuffer.toString(),
449
+ tokenBalance: this.tokenBalance.toString(),
450
+ interestFee: this.interestFee.toString(),
451
+ principalFee: this.principalFee.toString(),
452
+ originationFee: this.originationFee.toString(),
453
+ originationCap: this.originationCap.toString(),
454
+ externalYieldAmount: this.externalYieldAmount.toString(),
455
+ currentDeployedAmount: this.currentDeployedAmount.toString(),
456
+ outstandingInterestAmount: this.outstandingInterestAmount.toString(),
457
+ feeClaimable: this.feeClaimable.toString(),
458
+ cumulativePrincipalOriginated: this.cumulativePrincipalOriginated.toString(),
459
+ cumulativeInterestAccrued: this.cumulativeInterestAccrued.toString(),
460
+ cumulativeLoanCount: this.cumulativeLoanCount.toString(),
461
+ activeLoanCount: this.activeLoanCount.toString(),
462
+ marketInformation: this.marketInformation.toString(),
463
+ collateralMap: this.collateralMap.map((row) => row.map((item) => item.toString())),
464
+ externalYieldAccounts: this.externalYieldAccounts.toJSON(),
465
+ supplyMonitor: this.supplyMonitor.toJSON(),
466
+ withdrawMonitor: this.withdrawMonitor.toJSON(),
467
+ borrowMonitor: this.borrowMonitor.toJSON(),
468
+ }
469
+ }
470
+
471
+ static fromJSON(obj: StrategyJSON): Strategy {
472
+ return new Strategy({
473
+ version: obj.version,
474
+ nonce: new PublicKey(obj.nonce),
475
+ bump: obj.bump,
476
+ principalMint: new PublicKey(obj.principalMint),
477
+ lender: new PublicKey(obj.lender),
478
+ originationsEnabled: obj.originationsEnabled,
479
+ externalYieldSource: obj.externalYieldSource,
480
+ interestPerSecond: obj.interestPerSecond.map((item) => new BN(item)),
481
+ lastAccruedTimestamp: new BN(obj.lastAccruedTimestamp),
482
+ liquidityBuffer: new BN(obj.liquidityBuffer),
483
+ tokenBalance: new BN(obj.tokenBalance),
484
+ interestFee: new BN(obj.interestFee),
485
+ principalFee: new BN(obj.principalFee),
486
+ originationFee: new BN(obj.originationFee),
487
+ originationCap: new BN(obj.originationCap),
488
+ externalYieldAmount: new BN(obj.externalYieldAmount),
489
+ currentDeployedAmount: new BN(obj.currentDeployedAmount),
490
+ outstandingInterestAmount: new BN(obj.outstandingInterestAmount),
491
+ feeClaimable: new BN(obj.feeClaimable),
492
+ cumulativePrincipalOriginated: new BN(obj.cumulativePrincipalOriginated),
493
+ cumulativeInterestAccrued: new BN(obj.cumulativeInterestAccrued),
494
+ cumulativeLoanCount: new BN(obj.cumulativeLoanCount),
495
+ activeLoanCount: new BN(obj.activeLoanCount),
496
+ marketInformation: new PublicKey(obj.marketInformation),
497
+ collateralMap: obj.collateralMap.map((row) => row.map((item) => new BN(item))),
498
+ externalYieldAccounts: ExternalYieldAccounts.fromJSON(obj.externalYieldAccounts),
499
+ supplyMonitor: CapMonitor.fromJSON(obj.supplyMonitor),
500
+ withdrawMonitor: CapMonitor.fromJSON(obj.withdrawMonitor),
501
+ borrowMonitor: CapMonitor.fromJSON(obj.borrowMonitor),
502
+ })
503
+ }
504
+ }