@exponent-labs/exponent-sdk 0.0.3

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 (68) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/build/addressLookupTableUtil.d.ts +12 -0
  3. package/build/addressLookupTableUtil.js +32 -0
  4. package/build/addressLookupTableUtil.js.map +1 -0
  5. package/build/environment.d.ts +10 -0
  6. package/build/environment.js +13 -0
  7. package/build/environment.js.map +1 -0
  8. package/build/events.d.ts +339 -0
  9. package/build/events.js +231 -0
  10. package/build/events.js.map +1 -0
  11. package/build/flavors.d.ts +24 -0
  12. package/build/flavors.js +713 -0
  13. package/build/flavors.js.map +1 -0
  14. package/build/index.d.ts +11 -0
  15. package/build/index.js +45 -0
  16. package/build/index.js.map +1 -0
  17. package/build/lpPosition.d.ts +35 -0
  18. package/build/lpPosition.js +103 -0
  19. package/build/lpPosition.js.map +1 -0
  20. package/build/market.d.ts +567 -0
  21. package/build/market.js +1445 -0
  22. package/build/market.js.map +1 -0
  23. package/build/syPosition.d.ts +6 -0
  24. package/build/syPosition.js +115 -0
  25. package/build/syPosition.js.map +1 -0
  26. package/build/tokenUtil.d.ts +3 -0
  27. package/build/tokenUtil.js +23 -0
  28. package/build/tokenUtil.js.map +1 -0
  29. package/build/utils/altUtil.d.ts +8 -0
  30. package/build/utils/altUtil.js +35 -0
  31. package/build/utils/altUtil.js.map +1 -0
  32. package/build/utils/binSolver.d.ts +1 -0
  33. package/build/utils/binSolver.js +45 -0
  34. package/build/utils/binSolver.js.map +1 -0
  35. package/build/utils/binSolver.test.d.ts +1 -0
  36. package/build/utils/binSolver.test.js +15 -0
  37. package/build/utils/binSolver.test.js.map +1 -0
  38. package/build/utils/index.d.ts +6 -0
  39. package/build/utils/index.js +31 -0
  40. package/build/utils/index.js.map +1 -0
  41. package/build/utils/ix.d.ts +6 -0
  42. package/build/utils/ix.js +3 -0
  43. package/build/utils/ix.js.map +1 -0
  44. package/build/vault.d.ts +289 -0
  45. package/build/vault.js +615 -0
  46. package/build/vault.js.map +1 -0
  47. package/build/ytPosition.d.ts +86 -0
  48. package/build/ytPosition.js +231 -0
  49. package/build/ytPosition.js.map +1 -0
  50. package/jest.config.js +5 -0
  51. package/package.json +42 -0
  52. package/src/addressLookupTableUtil.ts +34 -0
  53. package/src/environment.ts +19 -0
  54. package/src/events.ts +595 -0
  55. package/src/flavors.ts +773 -0
  56. package/src/index.ts +11 -0
  57. package/src/lpPosition.ts +129 -0
  58. package/src/market.ts +2338 -0
  59. package/src/syPosition.ts +151 -0
  60. package/src/tokenUtil.ts +20 -0
  61. package/src/utils/altUtil.ts +47 -0
  62. package/src/utils/binSolver.test.ts +15 -0
  63. package/src/utils/binSolver.ts +44 -0
  64. package/src/utils/index.ts +32 -0
  65. package/src/utils/ix.ts +7 -0
  66. package/src/vault.ts +999 -0
  67. package/src/ytPosition.ts +313 -0
  68. package/tsconfig.json +38 -0
@@ -0,0 +1,151 @@
1
+ import { web3 } from "@coral-xyz/anchor"
2
+ import {
3
+ makeFlavorJitoRestakingSync,
4
+ makeFlavorKaminoSync,
5
+ makeFlavorMarginfiSync,
6
+ makeFlavorPerenaSync,
7
+ } from "./flavors"
8
+ import { ExponentFetcher, SyPosition } from "@exponent-labs/exponent-fetcher"
9
+ import { MarginfiSyPda } from "@exponent-labs/marginfi-sy-pda"
10
+ import { getPosition, KAMINO_STANDARD_PROGRAM_ID } from "@exponent-labs/kamino-lend-standard"
11
+ import { JitoRestakingSyPda } from "@exponent-labs/jito-restaking-sy-pda"
12
+ import { PROGRAM_ID as PERENA_STANDARD_PROGRAM_ID } from "@exponent-labs/perena-sy-idl"
13
+ import { PROGRAM_ID as GENERIC_STANDARD_PROGRAM_ID } from "@exponent-labs/generic-sy-idl"
14
+ import {
15
+ FlavorMarginfi,
16
+ FlavorKamino,
17
+ SyPositionJson,
18
+ IFlavorState,
19
+ FlavorJitoRestaking,
20
+ FlavorPerena,
21
+ FlavorGeneric,
22
+ } from "@exponent-labs/exponent-types"
23
+ import { PROGRAM_ID as JITO_RESTAKING_SY_PROGRAM_ID } from "@exponent-labs/jito-restaking-sy-idl"
24
+ import { PerenaSyPda } from "@exponent-labs/perena-sy-pda"
25
+ import { GenericSyPda } from "@exponent-labs/generic-sy-pda"
26
+
27
+ export function deserializeSyPosition(syPosition: SyPositionJson): SyPosition {
28
+ return {
29
+ balanceSy: BigInt(syPosition.balanceSy),
30
+ owner: new web3.PublicKey(syPosition.owner),
31
+ emissions: syPosition.emissions.map((emission) => ({
32
+ lastSeenIndex: emission.lastSeenIndex,
33
+ mint: new web3.PublicKey(emission.mint),
34
+ staged: BigInt(emission.staged),
35
+ })),
36
+ }
37
+ }
38
+
39
+ export function serializeSyPostion(syPostion: SyPosition): SyPositionJson {
40
+ return {
41
+ balanceSy: syPostion.balanceSy.toString(),
42
+ owner: syPostion.owner.toString(),
43
+ emissions: syPostion.emissions.map((emission) => ({
44
+ ...emission,
45
+ mint: emission.mint.toString(),
46
+ staged: emission.staged.toString(),
47
+ })),
48
+ }
49
+ }
50
+
51
+ export async function makeSyPosition(
52
+ fetcher: ExponentFetcher,
53
+ flavor: IFlavorState,
54
+ syProgramId: web3.PublicKey,
55
+ owner: web3.PublicKey,
56
+ ): Promise<SyPosition> {
57
+ if (flavor.flavor === "marginfi") {
58
+ const marginfiFlavor = makeFlavorMarginfiSync(flavor)
59
+ return makeSyPositionMarginfi(fetcher, syProgramId, marginfiFlavor, owner)
60
+ } else if (flavor.flavor === "kamino") {
61
+ const kaminoFlavor = makeFlavorKaminoSync(flavor)
62
+ return makeSyPositionKamino(fetcher, kaminoFlavor, owner)
63
+ } else if (flavor.flavor === "jitoRestaking") {
64
+ const jitoRestakingFlavor = makeFlavorJitoRestakingSync(flavor)
65
+ return makeSyPositionJitoRestaking(fetcher, jitoRestakingFlavor, owner)
66
+ } else if (flavor.flavor === "perena") {
67
+ const perenaFlavor = makeFlavorPerenaSync(flavor)
68
+ return makeSyPositionPerena(fetcher, perenaFlavor, owner)
69
+ } else if (flavor.flavor === "generic") {
70
+ return makeSyPositionGeneric(fetcher, flavor as FlavorGeneric, owner)
71
+ }
72
+
73
+ throw new Error("Unsupported flavor")
74
+ }
75
+
76
+ async function makeSyPositionMarginfi(
77
+ fetcher: ExponentFetcher,
78
+ marginfiProgramId: web3.PublicKey,
79
+ flavor: FlavorMarginfi,
80
+ owner: web3.PublicKey,
81
+ ): Promise<SyPosition> {
82
+ const pda = new MarginfiSyPda(marginfiProgramId)
83
+ const bank = flavor.bank
84
+ const positionAddress = pda.position({ bank, signer: owner })
85
+ const position = await fetcher.fetchMarginfiSyPosition(positionAddress)
86
+ return position
87
+ }
88
+
89
+ async function makeSyPositionKamino(
90
+ fetcher: ExponentFetcher,
91
+ flavor: FlavorKamino,
92
+ owner: web3.PublicKey,
93
+ ): Promise<SyPosition> {
94
+ const reserve = flavor.kaminoSyState.account.kaminoReserve
95
+ const positionAddress = getPosition(reserve, owner, KAMINO_STANDARD_PROGRAM_ID)
96
+ const position = await fetcher.fetchKaminoSyPosition(positionAddress)
97
+ return position
98
+ }
99
+
100
+ async function makeSyPositionJitoRestaking(
101
+ fetcher: ExponentFetcher,
102
+ flavor: FlavorJitoRestaking,
103
+ owner: web3.PublicKey,
104
+ ): Promise<SyPosition> {
105
+ const jitoVault = flavor.jitoSyState.account.jitoVault
106
+ const pda = new JitoRestakingSyPda(new web3.PublicKey(JITO_RESTAKING_SY_PROGRAM_ID))
107
+ const positionAddress = pda.position({ jitoVault, owner })
108
+ const position = await fetcher.fetchJitoRestakingSyPosition(positionAddress)
109
+ return position
110
+ }
111
+
112
+ async function makeSyPositionPerena(
113
+ fetcher: ExponentFetcher,
114
+ flavor: FlavorPerena,
115
+ owner: web3.PublicKey,
116
+ ): Promise<SyPosition> {
117
+ const perenaStablePool = flavor.perenaSyState.account.perenaStablePool
118
+ const pda = new PerenaSyPda(new web3.PublicKey(PERENA_STANDARD_PROGRAM_ID))
119
+ const positionAddress = pda.position({ owner, perenaStablePool })
120
+ const position = await fetcher.fetchPerenaSyPosition(positionAddress)
121
+ return position
122
+ }
123
+
124
+ async function makeSyPositionGeneric(
125
+ fetcher: ExponentFetcher,
126
+ flavor: FlavorGeneric,
127
+ owner: web3.PublicKey,
128
+ ): Promise<SyPosition> {
129
+ const yieldBearingMint = flavor.genericSyState.account.yieldBearingMint
130
+ const interfaceType = flavor.genericSyState.account.interfaceType
131
+
132
+ let interfaceIndex: number
133
+ if ("pyth" in interfaceType) {
134
+ interfaceIndex = 0 // InterfaceType.Pyth
135
+ } else if ("splStakePool" in interfaceType) {
136
+ interfaceIndex = 1 // InterfaceType.SplStakePool
137
+ } else if ("one" in interfaceType) {
138
+ interfaceIndex = 2 // InterfaceType.One
139
+ } else if ("fragmetric" in interfaceType) {
140
+ interfaceIndex = 3 // InterfaceType.Fragmetric
141
+ } else if ("meteora" in interfaceType) {
142
+ interfaceIndex = 4 // InterfaceType.Meteora
143
+ } else {
144
+ throw new Error("Unsupported interface type")
145
+ }
146
+
147
+ const pda = new GenericSyPda(new web3.PublicKey(GENERIC_STANDARD_PROGRAM_ID), interfaceIndex)
148
+ const positionAddress = pda.position({ owner, yieldBearingMint })
149
+ const position = await fetcher.fetchGenericSyPosition(positionAddress)
150
+ return position
151
+ }
@@ -0,0 +1,20 @@
1
+ import { web3 } from "@coral-xyz/anchor"
2
+ import { MintLayout } from "@solana/spl-token"
3
+
4
+ export async function getMintSupply(cnx: web3.Connection, address: web3.PublicKey): Promise<bigint> {
5
+ const mintInfo = await cnx.getAccountInfo(address)
6
+ if (mintInfo === null) {
7
+ throw new Error("Failed to find mint account")
8
+ }
9
+ return MintLayout.decode(mintInfo.data).supply
10
+ }
11
+
12
+ export async function getMultipleMintSupply(cnx: web3.Connection, addresses: web3.PublicKey[]): Promise<bigint[]> {
13
+ const mintInfos = await cnx.getMultipleAccountsInfo(addresses)
14
+ return mintInfos.map((mintInfo) => {
15
+ if (mintInfo === null) {
16
+ throw new Error("Failed to find mint account")
17
+ }
18
+ return MintLayout.decode(mintInfo.data).supply
19
+ })
20
+ }
@@ -0,0 +1,47 @@
1
+ import * as web3 from "@solana/web3.js"
2
+ import { CpiAccountsRaw, CpiAccountIndexes, CpiAccountIndex, AccountInfo } from "@exponent-labs/exponent-types"
3
+
4
+ export function extendAddressLookupTable(
5
+ oldAlt: web3.PublicKey[],
6
+ c: CpiAccountsRaw,
7
+ ): {
8
+ cpiAccountIndexes: CpiAccountIndexes
9
+ addressLookupTableExtension: web3.PublicKey[]
10
+ } {
11
+ const allPubkeys = getAllPubkeysFromCpiAccountsRaw(c)
12
+
13
+ const newPubkeys = allPubkeys.filter((p) => !oldAlt.some((o) => o.equals(p)))
14
+ const addressLookupTableExtension = Array.from(new Set(newPubkeys))
15
+
16
+ const newAlt = [...oldAlt, ...addressLookupTableExtension]
17
+
18
+ // Find index in ALT for a pubkey
19
+ const toIndex = (pubkey: web3.PublicKey) => newAlt.map((x) => x.toBase58()).indexOf(pubkey.toBase58())
20
+
21
+ // convert an AccountInfo to a CpiAccountIndex
22
+ const toCpiAccountIndex = (a: AccountInfo): CpiAccountIndex => ({
23
+ altIndex: toIndex(a.pubkey),
24
+ isSigner: a.isSigner,
25
+ isWritable: a.isWritable,
26
+ })
27
+
28
+ const cpiAccountIndexes: CpiAccountIndexes = {
29
+ getSyState: c.getSyState.map(toCpiAccountIndex),
30
+ withdrawSy: c.withdrawSy.map(toCpiAccountIndex),
31
+ depositSy: c.depositSy.map(toCpiAccountIndex),
32
+ claimEmission: c.claimEmission.map((a) => a.map(toCpiAccountIndex)),
33
+ getPositionState: c.getPositionState.map(toCpiAccountIndex),
34
+ }
35
+
36
+ return {
37
+ cpiAccountIndexes,
38
+ addressLookupTableExtension,
39
+ }
40
+ }
41
+
42
+ /** Get all pubkeys from CpiAccountsRaw */
43
+ export function getAllPubkeysFromCpiAccountsRaw(c: CpiAccountsRaw): web3.PublicKey[] {
44
+ return [...c.getSyState, ...c.withdrawSy, ...c.depositSy, ...c.claimEmission.flat(), ...c.getPositionState].map(
45
+ (a) => a.pubkey,
46
+ )
47
+ }
@@ -0,0 +1,15 @@
1
+ import { binSolver } from "./binSolver"
2
+
3
+ describe("bin solver", () => {
4
+ it("should return the index of the target value in the array", () => {
5
+ const fn = (x: number) => x * x
6
+ const a1 = binSolver(fn, 16, 3)
7
+ expect(a1).toBeCloseTo(4, 2)
8
+
9
+ const a2 = binSolver(fn, 16, 1)
10
+ expect(a2).toBeCloseTo(4, 2)
11
+
12
+ const a3 = binSolver(fn, 16, 10)
13
+ expect(a3).toBeCloseTo(4, 2)
14
+ })
15
+ })
@@ -0,0 +1,44 @@
1
+ export function binSolver(fn: (x: number) => number, target: number, guess: number, eps: number = 0.01) {
2
+ let low = guess
3
+ let high = guess
4
+ let iterations = 0
5
+ const MAX_ITERATIONS = 1000 // Prevent infinite loops
6
+
7
+ // check edge case
8
+ if (fn(guess) === target) {
9
+ return guess
10
+ }
11
+
12
+ // Expand the range until we encompass the target value
13
+ while (fn(low) > target || fn(high) < target) {
14
+ if (fn(low) > target) {
15
+ high = low
16
+ low = low / 2
17
+ } else {
18
+ low = high
19
+ high = high * 2
20
+ }
21
+ iterations++
22
+ if (iterations >= MAX_ITERATIONS) return null
23
+ }
24
+
25
+ // Perform binary search within the expanded range
26
+ while (low < high && iterations < MAX_ITERATIONS) {
27
+ const mid = (low + high) / 2
28
+ const value = fn(mid)
29
+
30
+ if (Math.abs(value - target) < eps) {
31
+ return mid // Found a close enough value
32
+ }
33
+
34
+ if (value < target) {
35
+ low = mid + Number.EPSILON // Ensure progress
36
+ } else {
37
+ high = mid - Number.EPSILON // Ensure progress
38
+ }
39
+
40
+ iterations++
41
+ }
42
+
43
+ return null // No solution found within the iteration limit
44
+ }
@@ -0,0 +1,32 @@
1
+ import { web3 } from "@coral-xyz/anchor"
2
+ import { EXPONENT_ADMIN_PROGRAM_ID, ExponentAdminPda } from "@exponent-labs/exponent-admin-pda"
3
+ export { InstructionAccounts } from "./ix"
4
+
5
+ export function uniqueRemainingAccounts(xs: web3.AccountMeta[]): web3.AccountMeta[] {
6
+ const seen: Record<string, web3.AccountMeta> = {}
7
+
8
+ for (const x of xs) {
9
+ const k = x.pubkey.toBase58()
10
+
11
+ if (!seen[k]) {
12
+ seen[k] = x
13
+ continue
14
+ }
15
+
16
+ const v = seen[k]
17
+ v.isWritable = v.isWritable || x.isWritable
18
+ v.isSigner = v.isSigner || x.isSigner
19
+ }
20
+
21
+ return Object.values(seen)
22
+ }
23
+
24
+ /** Find the authority for event emit with self-cpi */
25
+ export function emitEventAuthority(programId: web3.PublicKey) {
26
+ return web3.PublicKey.findProgramAddressSync([Buffer.from("__event_authority")], programId)[0]
27
+ }
28
+
29
+ export function getExponentAdminStatePda() {
30
+ const exponentAdminPda = new ExponentAdminPda(EXPONENT_ADMIN_PROGRAM_ID)
31
+ return exponentAdminPda.exponentAdmin()
32
+ }
@@ -0,0 +1,7 @@
1
+ import { web3 } from "@coral-xyz/anchor"
2
+
3
+ /** Generic type for accounts used for instructions */
4
+ export type InstructionAccounts<T> = {
5
+ mainAccounts: T
6
+ remainingAccounts: web3.AccountMeta[]
7
+ }