@lobstercove/lichen-sdk 1.0.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/README.md +50 -0
- package/dist/bincode.d.ts +5 -0
- package/dist/bincode.js +91 -0
- package/dist/bountyboard.d.ts +57 -0
- package/dist/bountyboard.js +205 -0
- package/dist/connection.d.ts +482 -0
- package/dist/connection.js +813 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +27 -0
- package/dist/keypair.d.ts +40 -0
- package/dist/keypair.js +69 -0
- package/dist/lichenid.d.ts +271 -0
- package/dist/lichenid.js +628 -0
- package/dist/lichenswap.d.ts +79 -0
- package/dist/lichenswap.js +311 -0
- package/dist/pq.d.ts +57 -0
- package/dist/pq.js +178 -0
- package/dist/publickey.d.ts +35 -0
- package/dist/publickey.js +71 -0
- package/dist/sporepay.d.ts +57 -0
- package/dist/sporepay.js +208 -0
- package/dist/sporevault.d.ts +43 -0
- package/dist/sporevault.js +176 -0
- package/dist/thalllend.d.ts +51 -0
- package/dist/thalllend.js +206 -0
- package/dist/transaction.d.ts +100 -0
- package/dist/transaction.js +202 -0
- package/package.json +56 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Connection } from './connection.js';
|
|
2
|
+
import { Keypair } from './keypair.js';
|
|
3
|
+
import { PublicKey } from './publickey.js';
|
|
4
|
+
export interface SporePayStream {
|
|
5
|
+
streamId: bigint;
|
|
6
|
+
sender: string;
|
|
7
|
+
recipient: string;
|
|
8
|
+
totalAmount: bigint;
|
|
9
|
+
withdrawnAmount: bigint;
|
|
10
|
+
startSlot: bigint;
|
|
11
|
+
endSlot: bigint;
|
|
12
|
+
cancelled: boolean;
|
|
13
|
+
createdSlot: bigint;
|
|
14
|
+
}
|
|
15
|
+
export interface SporePayStreamInfo extends SporePayStream {
|
|
16
|
+
cliffSlot: bigint;
|
|
17
|
+
}
|
|
18
|
+
export interface SporePayStats {
|
|
19
|
+
streamCount: number;
|
|
20
|
+
totalStreamed: number;
|
|
21
|
+
totalWithdrawn: number;
|
|
22
|
+
cancelCount: number;
|
|
23
|
+
paused: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface CreateStreamParams {
|
|
26
|
+
recipient: PublicKey | string;
|
|
27
|
+
totalAmount: number | bigint;
|
|
28
|
+
startSlot: number | bigint;
|
|
29
|
+
endSlot: number | bigint;
|
|
30
|
+
}
|
|
31
|
+
export interface CreateStreamWithCliffParams extends CreateStreamParams {
|
|
32
|
+
cliffSlot: number | bigint;
|
|
33
|
+
}
|
|
34
|
+
export interface WithdrawFromStreamParams {
|
|
35
|
+
streamId: number | bigint;
|
|
36
|
+
amount: number | bigint;
|
|
37
|
+
}
|
|
38
|
+
export interface TransferStreamParams {
|
|
39
|
+
streamId: number | bigint;
|
|
40
|
+
newRecipient: PublicKey | string;
|
|
41
|
+
}
|
|
42
|
+
export declare class SporePayClient {
|
|
43
|
+
private readonly connection;
|
|
44
|
+
private resolvedProgram?;
|
|
45
|
+
constructor(connection: Connection, programId?: PublicKey);
|
|
46
|
+
private callReadonly;
|
|
47
|
+
getProgramId(): Promise<PublicKey>;
|
|
48
|
+
getStream(streamId: number | bigint): Promise<SporePayStream | null>;
|
|
49
|
+
getStreamInfo(streamId: number | bigint): Promise<SporePayStreamInfo | null>;
|
|
50
|
+
getWithdrawable(streamId: number | bigint): Promise<bigint>;
|
|
51
|
+
getStats(): Promise<SporePayStats>;
|
|
52
|
+
createStream(sender: Keypair, params: CreateStreamParams): Promise<string>;
|
|
53
|
+
createStreamWithCliff(sender: Keypair, params: CreateStreamWithCliffParams): Promise<string>;
|
|
54
|
+
withdrawFromStream(recipient: Keypair, params: WithdrawFromStreamParams): Promise<string>;
|
|
55
|
+
cancelStream(sender: Keypair, streamId: number | bigint): Promise<string>;
|
|
56
|
+
transferStream(recipient: Keypair, params: TransferStreamParams): Promise<string>;
|
|
57
|
+
}
|
package/dist/sporepay.js
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { PublicKey } from './publickey.js';
|
|
2
|
+
const PROGRAM_SYMBOL_CANDIDATES = ['SPOREPAY', 'sporepay'];
|
|
3
|
+
const STREAM_SIZE = 105;
|
|
4
|
+
const STREAM_INFO_SIZE = 113;
|
|
5
|
+
const MAX_U64 = (1n << 64n) - 1n;
|
|
6
|
+
function normalizeAddress(value) {
|
|
7
|
+
return value instanceof PublicKey ? value : new PublicKey(value);
|
|
8
|
+
}
|
|
9
|
+
function normalizeUnsignedU64(value, fieldName) {
|
|
10
|
+
const normalized = typeof value === 'bigint'
|
|
11
|
+
? value
|
|
12
|
+
: Number.isSafeInteger(value) && value >= 0
|
|
13
|
+
? BigInt(value)
|
|
14
|
+
: null;
|
|
15
|
+
if (normalized === null || normalized < 0n || normalized > MAX_U64) {
|
|
16
|
+
throw new Error(`${fieldName} must be a u64-safe integer value`);
|
|
17
|
+
}
|
|
18
|
+
return normalized;
|
|
19
|
+
}
|
|
20
|
+
function u64LE(value, fieldName) {
|
|
21
|
+
const out = new Uint8Array(8);
|
|
22
|
+
new DataView(out.buffer).setBigUint64(0, normalizeUnsignedU64(value, fieldName), true);
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
function buildLayoutArgs(layout, chunks) {
|
|
26
|
+
const header = Uint8Array.from([0xAB, ...layout]);
|
|
27
|
+
const total = chunks.reduce((sum, chunk) => sum + chunk.length, header.length);
|
|
28
|
+
const out = new Uint8Array(total);
|
|
29
|
+
out.set(header, 0);
|
|
30
|
+
let offset = header.length;
|
|
31
|
+
for (const chunk of chunks) {
|
|
32
|
+
out.set(chunk, offset);
|
|
33
|
+
offset += chunk.length;
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
function encodeCreateStreamArgs(sender, params) {
|
|
38
|
+
return buildLayoutArgs([0x20, 0x20, 0x08, 0x08, 0x08], [
|
|
39
|
+
sender.toBytes(),
|
|
40
|
+
normalizeAddress(params.recipient).toBytes(),
|
|
41
|
+
u64LE(params.totalAmount, 'totalAmount'),
|
|
42
|
+
u64LE(params.startSlot, 'startSlot'),
|
|
43
|
+
u64LE(params.endSlot, 'endSlot'),
|
|
44
|
+
]);
|
|
45
|
+
}
|
|
46
|
+
function encodeCreateStreamWithCliffArgs(sender, params) {
|
|
47
|
+
return buildLayoutArgs([0x20, 0x20, 0x08, 0x08, 0x08, 0x08], [
|
|
48
|
+
sender.toBytes(),
|
|
49
|
+
normalizeAddress(params.recipient).toBytes(),
|
|
50
|
+
u64LE(params.totalAmount, 'totalAmount'),
|
|
51
|
+
u64LE(params.startSlot, 'startSlot'),
|
|
52
|
+
u64LE(params.endSlot, 'endSlot'),
|
|
53
|
+
u64LE(params.cliffSlot, 'cliffSlot'),
|
|
54
|
+
]);
|
|
55
|
+
}
|
|
56
|
+
function encodeWithdrawArgs(caller, params) {
|
|
57
|
+
return buildLayoutArgs([0x20, 0x08, 0x08], [
|
|
58
|
+
caller.toBytes(),
|
|
59
|
+
u64LE(params.streamId, 'streamId'),
|
|
60
|
+
u64LE(params.amount, 'amount'),
|
|
61
|
+
]);
|
|
62
|
+
}
|
|
63
|
+
function encodeCancelArgs(caller, streamId) {
|
|
64
|
+
return buildLayoutArgs([0x20, 0x08], [
|
|
65
|
+
caller.toBytes(),
|
|
66
|
+
u64LE(streamId, 'streamId'),
|
|
67
|
+
]);
|
|
68
|
+
}
|
|
69
|
+
function encodeTransferArgs(caller, params) {
|
|
70
|
+
return buildLayoutArgs([0x20, 0x20, 0x08], [
|
|
71
|
+
caller.toBytes(),
|
|
72
|
+
normalizeAddress(params.newRecipient).toBytes(),
|
|
73
|
+
u64LE(params.streamId, 'streamId'),
|
|
74
|
+
]);
|
|
75
|
+
}
|
|
76
|
+
function encodeStreamLookupArgs(streamId) {
|
|
77
|
+
return buildLayoutArgs([0x08], [u64LE(streamId, 'streamId')]);
|
|
78
|
+
}
|
|
79
|
+
function decodeReturnData(returnData) {
|
|
80
|
+
return Uint8Array.from(Buffer.from(returnData, 'base64'));
|
|
81
|
+
}
|
|
82
|
+
function readU64(bytes, offset) {
|
|
83
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
84
|
+
return view.getBigUint64(offset, true);
|
|
85
|
+
}
|
|
86
|
+
function ensureReturnCodeZero(result, functionName) {
|
|
87
|
+
const code = result.returnCode ?? 0;
|
|
88
|
+
if (code !== 0) {
|
|
89
|
+
throw new Error(result.error ?? `SporePay ${functionName} returned code ${code}`);
|
|
90
|
+
}
|
|
91
|
+
if (result.success === false && result.error) {
|
|
92
|
+
throw new Error(result.error);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function decodeStream(streamId, bytes) {
|
|
96
|
+
if (bytes.length < STREAM_SIZE) {
|
|
97
|
+
throw new Error('SporePay stream payload was shorter than expected');
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
streamId,
|
|
101
|
+
sender: PublicKey.fromBytes(bytes.slice(0, 32)).toBase58(),
|
|
102
|
+
recipient: PublicKey.fromBytes(bytes.slice(32, 64)).toBase58(),
|
|
103
|
+
totalAmount: readU64(bytes, 64),
|
|
104
|
+
withdrawnAmount: readU64(bytes, 72),
|
|
105
|
+
startSlot: readU64(bytes, 80),
|
|
106
|
+
endSlot: readU64(bytes, 88),
|
|
107
|
+
cancelled: bytes[96] === 1,
|
|
108
|
+
createdSlot: readU64(bytes, 97),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function decodeStreamInfo(streamId, bytes) {
|
|
112
|
+
if (bytes.length < STREAM_INFO_SIZE) {
|
|
113
|
+
throw new Error('SporePay stream-info payload was shorter than expected');
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
...decodeStream(streamId, bytes),
|
|
117
|
+
cliffSlot: readU64(bytes, 105),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
export class SporePayClient {
|
|
121
|
+
constructor(connection, programId) {
|
|
122
|
+
this.connection = connection;
|
|
123
|
+
this.resolvedProgram = programId;
|
|
124
|
+
}
|
|
125
|
+
async callReadonly(functionName, args) {
|
|
126
|
+
const programId = await this.getProgramId();
|
|
127
|
+
return this.connection.callReadonlyContract(programId, functionName, args);
|
|
128
|
+
}
|
|
129
|
+
async getProgramId() {
|
|
130
|
+
if (this.resolvedProgram) {
|
|
131
|
+
return this.resolvedProgram;
|
|
132
|
+
}
|
|
133
|
+
for (const symbol of PROGRAM_SYMBOL_CANDIDATES) {
|
|
134
|
+
try {
|
|
135
|
+
const entry = await this.connection.getSymbolRegistry(symbol);
|
|
136
|
+
if (entry?.program) {
|
|
137
|
+
this.resolvedProgram = new PublicKey(entry.program);
|
|
138
|
+
return this.resolvedProgram;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
// Try the next known registry alias.
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
throw new Error('Unable to resolve the SporePay program via getSymbolRegistry("SPOREPAY")');
|
|
146
|
+
}
|
|
147
|
+
async getStream(streamId) {
|
|
148
|
+
const normalizedStreamId = normalizeUnsignedU64(streamId, 'streamId');
|
|
149
|
+
const result = await this.callReadonly('get_stream', encodeStreamLookupArgs(normalizedStreamId));
|
|
150
|
+
if ((result.returnCode ?? 0) === 1 || !result.returnData) {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
ensureReturnCodeZero(result, 'get_stream');
|
|
154
|
+
return decodeStream(normalizedStreamId, decodeReturnData(result.returnData));
|
|
155
|
+
}
|
|
156
|
+
async getStreamInfo(streamId) {
|
|
157
|
+
const normalizedStreamId = normalizeUnsignedU64(streamId, 'streamId');
|
|
158
|
+
const result = await this.callReadonly('get_stream_info', encodeStreamLookupArgs(normalizedStreamId));
|
|
159
|
+
if ((result.returnCode ?? 0) === 1 || !result.returnData) {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
ensureReturnCodeZero(result, 'get_stream_info');
|
|
163
|
+
return decodeStreamInfo(normalizedStreamId, decodeReturnData(result.returnData));
|
|
164
|
+
}
|
|
165
|
+
async getWithdrawable(streamId) {
|
|
166
|
+
const result = await this.callReadonly('get_withdrawable', encodeStreamLookupArgs(streamId));
|
|
167
|
+
ensureReturnCodeZero(result, 'get_withdrawable');
|
|
168
|
+
if (!result.returnData) {
|
|
169
|
+
throw new Error('SporePay get_withdrawable did not return a balance');
|
|
170
|
+
}
|
|
171
|
+
return readU64(decodeReturnData(result.returnData), 0);
|
|
172
|
+
}
|
|
173
|
+
async getStats() {
|
|
174
|
+
const stats = await this.connection.getSporePayStats();
|
|
175
|
+
return {
|
|
176
|
+
streamCount: stats.stream_count ?? 0,
|
|
177
|
+
totalStreamed: stats.total_streamed ?? 0,
|
|
178
|
+
totalWithdrawn: stats.total_withdrawn ?? 0,
|
|
179
|
+
cancelCount: stats.cancel_count ?? 0,
|
|
180
|
+
paused: Boolean(stats.paused),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
async createStream(sender, params) {
|
|
184
|
+
const programId = await this.getProgramId();
|
|
185
|
+
const args = encodeCreateStreamArgs(sender.pubkey(), params);
|
|
186
|
+
return this.connection.callContract(sender, programId, 'create_stream', args);
|
|
187
|
+
}
|
|
188
|
+
async createStreamWithCliff(sender, params) {
|
|
189
|
+
const programId = await this.getProgramId();
|
|
190
|
+
const args = encodeCreateStreamWithCliffArgs(sender.pubkey(), params);
|
|
191
|
+
return this.connection.callContract(sender, programId, 'create_stream_with_cliff', args);
|
|
192
|
+
}
|
|
193
|
+
async withdrawFromStream(recipient, params) {
|
|
194
|
+
const programId = await this.getProgramId();
|
|
195
|
+
const args = encodeWithdrawArgs(recipient.pubkey(), params);
|
|
196
|
+
return this.connection.callContract(recipient, programId, 'withdraw_from_stream', args);
|
|
197
|
+
}
|
|
198
|
+
async cancelStream(sender, streamId) {
|
|
199
|
+
const programId = await this.getProgramId();
|
|
200
|
+
const args = encodeCancelArgs(sender.pubkey(), streamId);
|
|
201
|
+
return this.connection.callContract(sender, programId, 'cancel_stream', args);
|
|
202
|
+
}
|
|
203
|
+
async transferStream(recipient, params) {
|
|
204
|
+
const programId = await this.getProgramId();
|
|
205
|
+
const args = encodeTransferArgs(recipient.pubkey(), params);
|
|
206
|
+
return this.connection.callContract(recipient, programId, 'transfer_stream', args);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Connection } from './connection.js';
|
|
2
|
+
import { Keypair } from './keypair.js';
|
|
3
|
+
import { PublicKey } from './publickey.js';
|
|
4
|
+
export interface SporeVaultVaultStats {
|
|
5
|
+
totalAssets: bigint;
|
|
6
|
+
totalShares: bigint;
|
|
7
|
+
sharePriceE9: bigint;
|
|
8
|
+
strategyCount: bigint;
|
|
9
|
+
totalEarned: bigint;
|
|
10
|
+
feesEarned: bigint;
|
|
11
|
+
}
|
|
12
|
+
export interface SporeVaultUserPosition {
|
|
13
|
+
shares: bigint;
|
|
14
|
+
estimatedValue: bigint;
|
|
15
|
+
}
|
|
16
|
+
export interface SporeVaultStrategyInfo {
|
|
17
|
+
strategyType: bigint;
|
|
18
|
+
allocationPercent: bigint;
|
|
19
|
+
deployedAmount: bigint;
|
|
20
|
+
}
|
|
21
|
+
export interface SporeVaultStats {
|
|
22
|
+
totalAssets: number;
|
|
23
|
+
totalShares: number;
|
|
24
|
+
strategyCount: number;
|
|
25
|
+
totalEarned: number;
|
|
26
|
+
feesEarned: number;
|
|
27
|
+
protocolFees: number;
|
|
28
|
+
paused: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare class SporeVaultClient {
|
|
31
|
+
private readonly connection;
|
|
32
|
+
private resolvedProgram?;
|
|
33
|
+
constructor(connection: Connection, programId?: PublicKey);
|
|
34
|
+
private callReadonly;
|
|
35
|
+
getProgramId(): Promise<PublicKey>;
|
|
36
|
+
getVaultStats(): Promise<SporeVaultVaultStats>;
|
|
37
|
+
getUserPosition(user: PublicKey | string): Promise<SporeVaultUserPosition>;
|
|
38
|
+
getStrategyInfo(index: number | bigint): Promise<SporeVaultStrategyInfo | null>;
|
|
39
|
+
getStats(): Promise<SporeVaultStats>;
|
|
40
|
+
deposit(depositor: Keypair, amount: number | bigint): Promise<string>;
|
|
41
|
+
withdraw(depositor: Keypair, sharesToBurn: number | bigint): Promise<string>;
|
|
42
|
+
harvest(caller: Keypair): Promise<string>;
|
|
43
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { PublicKey } from './publickey.js';
|
|
2
|
+
const PROGRAM_SYMBOL_CANDIDATES = ['SPOREVAULT', 'sporevault', 'SporeVault', 'VAULT', 'vault'];
|
|
3
|
+
const MAX_U64 = (1n << 64n) - 1n;
|
|
4
|
+
function normalizeAddress(value) {
|
|
5
|
+
return value instanceof PublicKey ? value : new PublicKey(value);
|
|
6
|
+
}
|
|
7
|
+
function normalizeUnsignedU64(value, fieldName) {
|
|
8
|
+
const normalized = typeof value === 'bigint'
|
|
9
|
+
? value
|
|
10
|
+
: Number.isSafeInteger(value) && value >= 0
|
|
11
|
+
? BigInt(value)
|
|
12
|
+
: null;
|
|
13
|
+
if (normalized === null || normalized < 0n || normalized > MAX_U64) {
|
|
14
|
+
throw new Error(`${fieldName} must be a u64-safe integer value`);
|
|
15
|
+
}
|
|
16
|
+
return normalized;
|
|
17
|
+
}
|
|
18
|
+
function u64LE(value, fieldName) {
|
|
19
|
+
const out = new Uint8Array(8);
|
|
20
|
+
new DataView(out.buffer).setBigUint64(0, normalizeUnsignedU64(value, fieldName), true);
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
function buildLayoutArgs(layout, chunks) {
|
|
24
|
+
const header = Uint8Array.from([0xAB, ...layout]);
|
|
25
|
+
const total = chunks.reduce((sum, chunk) => sum + chunk.length, header.length);
|
|
26
|
+
const out = new Uint8Array(total);
|
|
27
|
+
out.set(header, 0);
|
|
28
|
+
let offset = header.length;
|
|
29
|
+
for (const chunk of chunks) {
|
|
30
|
+
out.set(chunk, offset);
|
|
31
|
+
offset += chunk.length;
|
|
32
|
+
}
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
function encodeUserAmountArgs(user, amount) {
|
|
36
|
+
return buildLayoutArgs([0x20, 0x08], [
|
|
37
|
+
user.toBytes(),
|
|
38
|
+
u64LE(amount, 'amount'),
|
|
39
|
+
]);
|
|
40
|
+
}
|
|
41
|
+
function encodeUserLookupArgs(user) {
|
|
42
|
+
return buildLayoutArgs([0x20], [normalizeAddress(user).toBytes()]);
|
|
43
|
+
}
|
|
44
|
+
function encodeIndexArgs(index) {
|
|
45
|
+
return buildLayoutArgs([0x08], [u64LE(index, 'index')]);
|
|
46
|
+
}
|
|
47
|
+
function decodeReturnData(returnData) {
|
|
48
|
+
return Uint8Array.from(Buffer.from(returnData, 'base64'));
|
|
49
|
+
}
|
|
50
|
+
function readU64(bytes, offset) {
|
|
51
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
52
|
+
return view.getBigUint64(offset, true);
|
|
53
|
+
}
|
|
54
|
+
function ensureReadonlySuccess(result, functionName, allowedReturnCodes = [0]) {
|
|
55
|
+
const code = result.returnCode ?? 0;
|
|
56
|
+
if (!allowedReturnCodes.includes(code)) {
|
|
57
|
+
throw new Error(result.error ?? `SporeVault ${functionName} returned code ${code}`);
|
|
58
|
+
}
|
|
59
|
+
if (result.success === false && result.error) {
|
|
60
|
+
throw new Error(result.error);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function decodeVaultStats(result) {
|
|
64
|
+
ensureReadonlySuccess(result, 'get_vault_stats');
|
|
65
|
+
if (!result.returnData) {
|
|
66
|
+
throw new Error('SporeVault get_vault_stats did not return vault data');
|
|
67
|
+
}
|
|
68
|
+
const bytes = decodeReturnData(result.returnData);
|
|
69
|
+
if (bytes.length < 48) {
|
|
70
|
+
throw new Error('SporeVault get_vault_stats payload was shorter than expected');
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
totalAssets: readU64(bytes, 0),
|
|
74
|
+
totalShares: readU64(bytes, 8),
|
|
75
|
+
sharePriceE9: readU64(bytes, 16),
|
|
76
|
+
strategyCount: readU64(bytes, 24),
|
|
77
|
+
totalEarned: readU64(bytes, 32),
|
|
78
|
+
feesEarned: readU64(bytes, 40),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function decodeUserPosition(result) {
|
|
82
|
+
ensureReadonlySuccess(result, 'get_user_position');
|
|
83
|
+
if (!result.returnData) {
|
|
84
|
+
throw new Error('SporeVault get_user_position did not return user data');
|
|
85
|
+
}
|
|
86
|
+
const bytes = decodeReturnData(result.returnData);
|
|
87
|
+
if (bytes.length < 16) {
|
|
88
|
+
throw new Error('SporeVault get_user_position payload was shorter than expected');
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
shares: readU64(bytes, 0),
|
|
92
|
+
estimatedValue: readU64(bytes, 8),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function decodeStrategyInfo(result) {
|
|
96
|
+
ensureReadonlySuccess(result, 'get_strategy_info');
|
|
97
|
+
if (!result.returnData) {
|
|
98
|
+
throw new Error('SporeVault get_strategy_info did not return strategy data');
|
|
99
|
+
}
|
|
100
|
+
const bytes = decodeReturnData(result.returnData);
|
|
101
|
+
if (bytes.length < 24) {
|
|
102
|
+
throw new Error('SporeVault get_strategy_info payload was shorter than expected');
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
strategyType: readU64(bytes, 0),
|
|
106
|
+
allocationPercent: readU64(bytes, 8),
|
|
107
|
+
deployedAmount: readU64(bytes, 16),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
export class SporeVaultClient {
|
|
111
|
+
constructor(connection, programId) {
|
|
112
|
+
this.connection = connection;
|
|
113
|
+
this.resolvedProgram = programId;
|
|
114
|
+
}
|
|
115
|
+
async callReadonly(functionName, args = new Uint8Array()) {
|
|
116
|
+
const programId = await this.getProgramId();
|
|
117
|
+
return this.connection.callReadonlyContract(programId, functionName, args);
|
|
118
|
+
}
|
|
119
|
+
async getProgramId() {
|
|
120
|
+
if (this.resolvedProgram) {
|
|
121
|
+
return this.resolvedProgram;
|
|
122
|
+
}
|
|
123
|
+
for (const symbol of PROGRAM_SYMBOL_CANDIDATES) {
|
|
124
|
+
try {
|
|
125
|
+
const entry = await this.connection.getSymbolRegistry(symbol);
|
|
126
|
+
if (entry?.program) {
|
|
127
|
+
this.resolvedProgram = new PublicKey(entry.program);
|
|
128
|
+
return this.resolvedProgram;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// Try the next known registry alias.
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
throw new Error('Unable to resolve the SporeVault program via getSymbolRegistry("SPOREVAULT")');
|
|
136
|
+
}
|
|
137
|
+
async getVaultStats() {
|
|
138
|
+
return decodeVaultStats(await this.callReadonly('get_vault_stats'));
|
|
139
|
+
}
|
|
140
|
+
async getUserPosition(user) {
|
|
141
|
+
return decodeUserPosition(await this.callReadonly('get_user_position', encodeUserLookupArgs(user)));
|
|
142
|
+
}
|
|
143
|
+
async getStrategyInfo(index) {
|
|
144
|
+
const result = await this.callReadonly('get_strategy_info', encodeIndexArgs(index));
|
|
145
|
+
if ((result.returnCode ?? 0) === 1 || !result.returnData) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
return decodeStrategyInfo(result);
|
|
149
|
+
}
|
|
150
|
+
async getStats() {
|
|
151
|
+
const stats = await this.connection.getSporeVaultStats();
|
|
152
|
+
return {
|
|
153
|
+
totalAssets: stats.total_assets ?? 0,
|
|
154
|
+
totalShares: stats.total_shares ?? 0,
|
|
155
|
+
strategyCount: stats.strategy_count ?? 0,
|
|
156
|
+
totalEarned: stats.total_earned ?? 0,
|
|
157
|
+
feesEarned: stats.fees_earned ?? 0,
|
|
158
|
+
protocolFees: stats.protocol_fees ?? 0,
|
|
159
|
+
paused: Boolean(stats.paused),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
async deposit(depositor, amount) {
|
|
163
|
+
const programId = await this.getProgramId();
|
|
164
|
+
const args = encodeUserAmountArgs(depositor.pubkey(), amount);
|
|
165
|
+
return this.connection.callContract(depositor, programId, 'deposit', args, normalizeUnsignedU64(amount, 'amount'));
|
|
166
|
+
}
|
|
167
|
+
async withdraw(depositor, sharesToBurn) {
|
|
168
|
+
const programId = await this.getProgramId();
|
|
169
|
+
const args = encodeUserAmountArgs(depositor.pubkey(), sharesToBurn);
|
|
170
|
+
return this.connection.callContract(depositor, programId, 'withdraw', args);
|
|
171
|
+
}
|
|
172
|
+
async harvest(caller) {
|
|
173
|
+
const programId = await this.getProgramId();
|
|
174
|
+
return this.connection.callContract(caller, programId, 'harvest');
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Connection } from './connection.js';
|
|
2
|
+
import { Keypair } from './keypair.js';
|
|
3
|
+
import { PublicKey } from './publickey.js';
|
|
4
|
+
export interface ThallLendAccountInfo {
|
|
5
|
+
deposit: bigint;
|
|
6
|
+
borrow: bigint;
|
|
7
|
+
healthFactorBps: bigint;
|
|
8
|
+
}
|
|
9
|
+
export interface ThallLendProtocolStats {
|
|
10
|
+
totalDeposits: bigint;
|
|
11
|
+
totalBorrows: bigint;
|
|
12
|
+
utilizationPct: bigint;
|
|
13
|
+
reserves: bigint;
|
|
14
|
+
}
|
|
15
|
+
export interface ThallLendInterestRate {
|
|
16
|
+
ratePerSlot: bigint;
|
|
17
|
+
utilizationPct: bigint;
|
|
18
|
+
totalAvailable: bigint;
|
|
19
|
+
}
|
|
20
|
+
export interface ThallLendStats {
|
|
21
|
+
totalDeposits: number;
|
|
22
|
+
totalBorrows: number;
|
|
23
|
+
reserves: number;
|
|
24
|
+
depositCount: number;
|
|
25
|
+
borrowCount: number;
|
|
26
|
+
liquidationCount: number;
|
|
27
|
+
paused: boolean;
|
|
28
|
+
}
|
|
29
|
+
export interface LiquidateParams {
|
|
30
|
+
borrower: PublicKey | string;
|
|
31
|
+
repayAmount: number | bigint;
|
|
32
|
+
}
|
|
33
|
+
export declare class ThallLendClient {
|
|
34
|
+
private readonly connection;
|
|
35
|
+
private resolvedProgram?;
|
|
36
|
+
constructor(connection: Connection, programId?: PublicKey);
|
|
37
|
+
private callReadonly;
|
|
38
|
+
getProgramId(): Promise<PublicKey>;
|
|
39
|
+
getAccountInfo(user: PublicKey | string): Promise<ThallLendAccountInfo>;
|
|
40
|
+
getProtocolStats(): Promise<ThallLendProtocolStats>;
|
|
41
|
+
getInterestRate(): Promise<ThallLendInterestRate>;
|
|
42
|
+
getDepositCount(): Promise<bigint>;
|
|
43
|
+
getBorrowCount(): Promise<bigint>;
|
|
44
|
+
getLiquidationCount(): Promise<bigint>;
|
|
45
|
+
getStats(): Promise<ThallLendStats>;
|
|
46
|
+
deposit(depositor: Keypair, amount: number | bigint): Promise<string>;
|
|
47
|
+
withdraw(depositor: Keypair, amount: number | bigint): Promise<string>;
|
|
48
|
+
borrow(borrower: Keypair, amount: number | bigint): Promise<string>;
|
|
49
|
+
repay(borrower: Keypair, amount: number | bigint): Promise<string>;
|
|
50
|
+
liquidate(liquidator: Keypair, params: LiquidateParams): Promise<string>;
|
|
51
|
+
}
|