@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.
- package/CHANGELOG.md +16 -0
- package/build/addressLookupTableUtil.d.ts +12 -0
- package/build/addressLookupTableUtil.js +32 -0
- package/build/addressLookupTableUtil.js.map +1 -0
- package/build/environment.d.ts +10 -0
- package/build/environment.js +13 -0
- package/build/environment.js.map +1 -0
- package/build/events.d.ts +339 -0
- package/build/events.js +231 -0
- package/build/events.js.map +1 -0
- package/build/flavors.d.ts +24 -0
- package/build/flavors.js +713 -0
- package/build/flavors.js.map +1 -0
- package/build/index.d.ts +11 -0
- package/build/index.js +45 -0
- package/build/index.js.map +1 -0
- package/build/lpPosition.d.ts +35 -0
- package/build/lpPosition.js +103 -0
- package/build/lpPosition.js.map +1 -0
- package/build/market.d.ts +567 -0
- package/build/market.js +1445 -0
- package/build/market.js.map +1 -0
- package/build/syPosition.d.ts +6 -0
- package/build/syPosition.js +115 -0
- package/build/syPosition.js.map +1 -0
- package/build/tokenUtil.d.ts +3 -0
- package/build/tokenUtil.js +23 -0
- package/build/tokenUtil.js.map +1 -0
- package/build/utils/altUtil.d.ts +8 -0
- package/build/utils/altUtil.js +35 -0
- package/build/utils/altUtil.js.map +1 -0
- package/build/utils/binSolver.d.ts +1 -0
- package/build/utils/binSolver.js +45 -0
- package/build/utils/binSolver.js.map +1 -0
- package/build/utils/binSolver.test.d.ts +1 -0
- package/build/utils/binSolver.test.js +15 -0
- package/build/utils/binSolver.test.js.map +1 -0
- package/build/utils/index.d.ts +6 -0
- package/build/utils/index.js +31 -0
- package/build/utils/index.js.map +1 -0
- package/build/utils/ix.d.ts +6 -0
- package/build/utils/ix.js +3 -0
- package/build/utils/ix.js.map +1 -0
- package/build/vault.d.ts +289 -0
- package/build/vault.js +615 -0
- package/build/vault.js.map +1 -0
- package/build/ytPosition.d.ts +86 -0
- package/build/ytPosition.js +231 -0
- package/build/ytPosition.js.map +1 -0
- package/jest.config.js +5 -0
- package/package.json +42 -0
- package/src/addressLookupTableUtil.ts +34 -0
- package/src/environment.ts +19 -0
- package/src/events.ts +595 -0
- package/src/flavors.ts +773 -0
- package/src/index.ts +11 -0
- package/src/lpPosition.ts +129 -0
- package/src/market.ts +2338 -0
- package/src/syPosition.ts +151 -0
- package/src/tokenUtil.ts +20 -0
- package/src/utils/altUtil.ts +47 -0
- package/src/utils/binSolver.test.ts +15 -0
- package/src/utils/binSolver.ts +44 -0
- package/src/utils/index.ts +32 -0
- package/src/utils/ix.ts +7 -0
- package/src/vault.ts +999 -0
- package/src/ytPosition.ts +313 -0
- 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
|
+
}
|
package/src/tokenUtil.ts
ADDED
|
@@ -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
|
+
}
|