@ember-finance/sdk 2.38.0 → 2.40.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/dist/src/common/constants.d.ts +1 -1
- package/dist/src/ember-vaults.d.ts +85 -5
- package/dist/src/ember-vaults.js +144 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/solana-vaults/index.d.ts +4 -0
- package/dist/src/solana-vaults/index.js +4 -0
- package/dist/src/solana-vaults/interfaces/index.d.ts +260 -0
- package/dist/src/solana-vaults/interfaces/index.js +37 -0
- package/dist/src/solana-vaults/on-chain-calls/index.d.ts +3 -0
- package/dist/src/solana-vaults/on-chain-calls/index.js +3 -0
- package/dist/src/solana-vaults/on-chain-calls/tx-builder.d.ts +62 -0
- package/dist/src/solana-vaults/on-chain-calls/tx-builder.js +190 -0
- package/dist/src/solana-vaults/on-chain-calls/user.d.ts +150 -0
- package/dist/src/solana-vaults/on-chain-calls/user.js +288 -0
- package/dist/src/solana-vaults/on-chain-calls/vault-reader.d.ts +190 -0
- package/dist/src/solana-vaults/on-chain-calls/vault-reader.js +432 -0
- package/dist/src/solana-vaults/signers/index.d.ts +1 -0
- package/dist/src/solana-vaults/signers/index.js +1 -0
- package/dist/src/solana-vaults/signers/solana-signer.d.ts +44 -0
- package/dist/src/solana-vaults/signers/solana-signer.js +61 -0
- package/dist/src/solana-vaults/utils/borsh.d.ts +65 -0
- package/dist/src/solana-vaults/utils/borsh.js +237 -0
- package/dist/src/solana-vaults/utils/deployment-parser.d.ts +94 -0
- package/dist/src/solana-vaults/utils/deployment-parser.js +174 -0
- package/dist/src/solana-vaults/utils/index.d.ts +3 -0
- package/dist/src/solana-vaults/utils/index.js +3 -0
- package/dist/src/solana-vaults/utils/math.d.ts +77 -0
- package/dist/src/solana-vaults/utils/math.js +111 -0
- package/dist/src/solana-vaults/utils/pdas.d.ts +63 -0
- package/dist/src/solana-vaults/utils/pdas.js +114 -0
- package/package.json +10 -2
|
@@ -18,6 +18,12 @@ import { EVMDeploymentParser } from "./evm-vaults/utils/deployment-parser.js";
|
|
|
18
18
|
import { EVMVaultReader } from "./evm-vaults/on-chain-calls/vault-reader.js";
|
|
19
19
|
import { AccountsApi, VaultsApi } from "./api/index.js";
|
|
20
20
|
import { AccountsApi as AccountsV2Api, VaultsApi as VaultsV2Api } from "./api/v2/index.js";
|
|
21
|
+
import type { Connection as SolanaConnection } from "@solana/web3.js";
|
|
22
|
+
import type { ISolanaDeployment } from "./solana-vaults/interfaces/index.js";
|
|
23
|
+
import { SolanaDeploymentParser } from "./solana-vaults/utils/deployment-parser.js";
|
|
24
|
+
import { SolanaVaultReader } from "./solana-vaults/on-chain-calls/vault-reader.js";
|
|
25
|
+
import { SolanaUserCalls } from "./solana-vaults/on-chain-calls/user.js";
|
|
26
|
+
import type { SolanaSigner } from "./solana-vaults/signers/solana-signer.js";
|
|
21
27
|
import { ChainType, PlatformEnv } from "./common/constants.js";
|
|
22
28
|
/**
|
|
23
29
|
* SUI-specific initialization options
|
|
@@ -46,7 +52,48 @@ export interface EvmInitOptions {
|
|
|
46
52
|
environment?: PlatformEnv;
|
|
47
53
|
chainIdentifier: string;
|
|
48
54
|
}
|
|
49
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Solana-specific initialization options.
|
|
57
|
+
*
|
|
58
|
+
* `connection` rather than a provider: web3.js has no provider abstraction,
|
|
59
|
+
* and every read goes through it.
|
|
60
|
+
*
|
|
61
|
+
* `signer` takes either shape - a raw Keypair for scripts and ops tooling, or
|
|
62
|
+
* a connected browser wallet - and is optional so a page can read before
|
|
63
|
+
* anyone connects. A write with no signer anywhere throws.
|
|
64
|
+
*/
|
|
65
|
+
export interface SolanaInitOptions {
|
|
66
|
+
chainType: "solana";
|
|
67
|
+
connection: SolanaConnection;
|
|
68
|
+
signer?: SolanaSigner;
|
|
69
|
+
/**
|
|
70
|
+
* Unused on Solana and accepted only so the constructor can read it
|
|
71
|
+
* uniformly. The chain identifier is "solana" on every cluster; which
|
|
72
|
+
* cluster an environment talks to follows from the deployment it serves.
|
|
73
|
+
*/
|
|
74
|
+
chainIdentifier?: string;
|
|
75
|
+
walletAddress?: string;
|
|
76
|
+
deployment?: ISolanaDeployment;
|
|
77
|
+
basePath?: string;
|
|
78
|
+
environment?: PlatformEnv;
|
|
79
|
+
}
|
|
80
|
+
export type EmberVaultsInitOptions = SuiInitOptions | EvmInitOptions | SolanaInitOptions;
|
|
81
|
+
/**
|
|
82
|
+
* Solana-specific EmberVaults instance type.
|
|
83
|
+
*
|
|
84
|
+
* Narrower than its Sui and EVM counterparts: reads plus the user writes.
|
|
85
|
+
* The admin, operator, vaultAdmin and txBuilder surfaces follow, and the
|
|
86
|
+
* getters for them throw rather than returning undefined until they do.
|
|
87
|
+
*/
|
|
88
|
+
export interface SolanaEmberVaults {
|
|
89
|
+
chainType: "solana";
|
|
90
|
+
connection: SolanaConnection;
|
|
91
|
+
parser: SolanaDeploymentParser;
|
|
92
|
+
walletAddress?: string;
|
|
93
|
+
reader: SolanaVaultReader;
|
|
94
|
+
user: SolanaUserCalls;
|
|
95
|
+
deployment: ISolanaDeployment;
|
|
96
|
+
}
|
|
50
97
|
/**
|
|
51
98
|
* SUI-specific EmberVaults instance type
|
|
52
99
|
*/
|
|
@@ -133,9 +180,23 @@ export declare class EmberVaults {
|
|
|
133
180
|
private _evmUser?;
|
|
134
181
|
private _evmDeployment?;
|
|
135
182
|
private _evmVaultReader?;
|
|
183
|
+
private _solanaConnection?;
|
|
184
|
+
private _solanaParser?;
|
|
185
|
+
private _solanaDeployment?;
|
|
186
|
+
private _solanaVaultReader?;
|
|
187
|
+
private _solanaUser?;
|
|
188
|
+
private _solanaSigner?;
|
|
136
189
|
private _walletAddress?;
|
|
137
190
|
private _chainIdentifier?;
|
|
138
191
|
constructor(options: EmberVaultsInitOptions);
|
|
192
|
+
/**
|
|
193
|
+
* Solana setup.
|
|
194
|
+
*
|
|
195
|
+
* The deployment is optional here as it is on the other chains: pass one
|
|
196
|
+
* to work offline, or call `init()` to fetch it from the API. Without
|
|
197
|
+
* either, the reader has nothing to resolve addresses against.
|
|
198
|
+
*/
|
|
199
|
+
private _initSolana;
|
|
139
200
|
private _initSui;
|
|
140
201
|
private _initEvm;
|
|
141
202
|
/**
|
|
@@ -146,19 +207,30 @@ export declare class EmberVaults {
|
|
|
146
207
|
* Updates the deployment configuration
|
|
147
208
|
* @param newDeployment The new deployment configuration (ISuiDeployment or EvmProtocolInfo)
|
|
148
209
|
*/
|
|
149
|
-
updateDeployment(newDeployment: ISuiDeployment | EvmProtocolInfo): void;
|
|
210
|
+
updateDeployment(newDeployment: ISuiDeployment | EvmProtocolInfo | ISolanaDeployment): void;
|
|
150
211
|
get suiClient(): SuiClient;
|
|
151
212
|
get network(): string;
|
|
152
213
|
get provider(): EvmProvider | undefined;
|
|
153
214
|
get walletAddress(): string | undefined;
|
|
215
|
+
/**
|
|
216
|
+
* Read-only vault access. Sui has no reader, so this is EVM or Solana.
|
|
217
|
+
*/
|
|
218
|
+
get reader(): EVMVaultReader | SolanaVaultReader;
|
|
219
|
+
/**
|
|
220
|
+
* Solana's write surfaces are not built yet. Thrown rather than returning
|
|
221
|
+
* undefined so the failure names what is missing instead of surfacing as
|
|
222
|
+
* "cannot read property of undefined" several frames later.
|
|
223
|
+
*/
|
|
224
|
+
private notOnSolana;
|
|
225
|
+
get connection(): SolanaConnection;
|
|
154
226
|
get signer(): SuiSigner | EvmSigner | undefined;
|
|
155
|
-
get parser(): SuiDeploymentParser | EVMDeploymentParser;
|
|
227
|
+
get parser(): SuiDeploymentParser | EVMDeploymentParser | SolanaDeploymentParser;
|
|
156
228
|
get txBuilder(): SuiTxBuilder | EVMTxBuilder;
|
|
157
229
|
get admin(): SuiAdminCalls | EVMAdminCalls;
|
|
158
230
|
get operator(): SuiOperatorCalls | EVMOperatorCalls;
|
|
159
231
|
get vaultAdmin(): SuiVaultAdminCalls | EVMVaultAdminCalls;
|
|
160
|
-
get user(): SuiUserCalls | EVMUserCalls;
|
|
161
|
-
get deployment(): ISuiDeployment | EvmProtocolInfo;
|
|
232
|
+
get user(): SuiUserCalls | EVMUserCalls | SolanaUserCalls;
|
|
233
|
+
get deployment(): ISuiDeployment | EvmProtocolInfo | ISolanaDeployment;
|
|
162
234
|
/**
|
|
163
235
|
* Check if this is a SUI instance
|
|
164
236
|
*/
|
|
@@ -166,6 +238,10 @@ export declare class EmberVaults {
|
|
|
166
238
|
/**
|
|
167
239
|
* Check if this is an EVM instance
|
|
168
240
|
*/
|
|
241
|
+
/**
|
|
242
|
+
* Check if this is a Solana instance
|
|
243
|
+
*/
|
|
244
|
+
isSolana(): this is EmberVaults & SolanaEmberVaults;
|
|
169
245
|
isEvm(): this is EmberVaults & EvmEmberVaults;
|
|
170
246
|
/**
|
|
171
247
|
* Get SUI-specific instance (throws if not SUI)
|
|
@@ -175,4 +251,8 @@ export declare class EmberVaults {
|
|
|
175
251
|
* Get EVM-specific instance (throws if not EVM)
|
|
176
252
|
*/
|
|
177
253
|
asEvm(): EvmEmberVaults;
|
|
254
|
+
/**
|
|
255
|
+
* Get Solana-specific instance (throws if not Solana)
|
|
256
|
+
*/
|
|
257
|
+
asSolana(): SolanaEmberVaults;
|
|
178
258
|
}
|
package/dist/src/ember-vaults.js
CHANGED
|
@@ -14,6 +14,9 @@ import { EVMVaultReader } from "./evm-vaults/on-chain-calls/vault-reader.js";
|
|
|
14
14
|
// API imports
|
|
15
15
|
import { AccountsApi, Configuration, VaultsApi } from "./api/index.js";
|
|
16
16
|
import { AccountsApi as AccountsV2Api, Configuration as ConfigurationV2, VaultsApi as VaultsV2Api } from "./api/v2/index.js";
|
|
17
|
+
import { SolanaDeploymentParser } from "./solana-vaults/utils/deployment-parser.js";
|
|
18
|
+
import { SolanaVaultReader } from "./solana-vaults/on-chain-calls/vault-reader.js";
|
|
19
|
+
import { SolanaUserCalls } from "./solana-vaults/on-chain-calls/user.js";
|
|
17
20
|
import { environmentConfig } from "./common/constants.js";
|
|
18
21
|
/**
|
|
19
22
|
* EmberVaults - Unified interface for both SUI and EVM chains
|
|
@@ -54,10 +57,31 @@ export class EmberVaults {
|
|
|
54
57
|
if (options.chainType === "sui") {
|
|
55
58
|
this._initSui(options);
|
|
56
59
|
}
|
|
60
|
+
else if (options.chainType === "solana") {
|
|
61
|
+
this._initSolana(options);
|
|
62
|
+
}
|
|
57
63
|
else {
|
|
58
64
|
this._initEvm(options);
|
|
59
65
|
}
|
|
60
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Solana setup.
|
|
69
|
+
*
|
|
70
|
+
* The deployment is optional here as it is on the other chains: pass one
|
|
71
|
+
* to work offline, or call `init()` to fetch it from the API. Without
|
|
72
|
+
* either, the reader has nothing to resolve addresses against.
|
|
73
|
+
*/
|
|
74
|
+
_initSolana(options) {
|
|
75
|
+
this._solanaConnection = options.connection;
|
|
76
|
+
this._walletAddress = options.walletAddress;
|
|
77
|
+
this._solanaSigner = options.signer;
|
|
78
|
+
if (options.deployment) {
|
|
79
|
+
this._solanaDeployment = options.deployment;
|
|
80
|
+
this._solanaParser = new SolanaDeploymentParser(options.deployment);
|
|
81
|
+
this._solanaVaultReader = new SolanaVaultReader(options.connection, this._solanaParser);
|
|
82
|
+
this._solanaUser = new SolanaUserCalls(options.connection, this._solanaParser, options.signer);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
61
85
|
_initSui(options) {
|
|
62
86
|
this._suiNetwork = options.network;
|
|
63
87
|
this._suiClient = options.suiClient;
|
|
@@ -97,6 +121,23 @@ export class EmberVaults {
|
|
|
97
121
|
throw new Error(`Deployment data not found for chain ${this._chainIdentifier}`);
|
|
98
122
|
}
|
|
99
123
|
}
|
|
124
|
+
else if (this.chainType === "solana") {
|
|
125
|
+
// One Solana deployment per environment, so there is nothing to
|
|
126
|
+
// disambiguate by chain identifier - it is "solana" on every
|
|
127
|
+
// cluster, and which cluster follows from the environment.
|
|
128
|
+
// Compared as a string: the API spec vendored under src/api/v2 was
|
|
129
|
+
// copied before bfvs added "solana" to VaultsDeploymentConfig's
|
|
130
|
+
// chainType enum, so the generated union does not list it yet. The
|
|
131
|
+
// wire value is correct and staging already serves it; this reads
|
|
132
|
+
// as written rather than as typed until the client is regenerated.
|
|
133
|
+
const deploymentData = deployment.data.find(d => d.chainType === "solana");
|
|
134
|
+
if (deploymentData) {
|
|
135
|
+
this.updateDeployment(deploymentData.config);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
throw new Error("Deployment data not found for chain solana");
|
|
139
|
+
}
|
|
140
|
+
}
|
|
100
141
|
else {
|
|
101
142
|
const deploymentData = deployment.data.find(d => d.chainType === "evm" && d.chain === this._chainIdentifier);
|
|
102
143
|
if (deploymentData) {
|
|
@@ -112,6 +153,27 @@ export class EmberVaults {
|
|
|
112
153
|
* @param newDeployment The new deployment configuration (ISuiDeployment or EvmProtocolInfo)
|
|
113
154
|
*/
|
|
114
155
|
updateDeployment(newDeployment) {
|
|
156
|
+
if (this.chainType === "solana") {
|
|
157
|
+
const solanaDeployment = newDeployment;
|
|
158
|
+
this._solanaDeployment = solanaDeployment;
|
|
159
|
+
// The parser holds the program id that every address derives from,
|
|
160
|
+
// so it is rebuilt rather than mutated - a deployment pointing at a
|
|
161
|
+
// different program is a different parser.
|
|
162
|
+
this._solanaParser = new SolanaDeploymentParser(solanaDeployment);
|
|
163
|
+
if (this._solanaUser) {
|
|
164
|
+
this._solanaUser.updateDeployment(this._solanaParser);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
this._solanaUser = new SolanaUserCalls(this._solanaConnection, this._solanaParser, this._solanaSigner);
|
|
168
|
+
}
|
|
169
|
+
if (this._solanaVaultReader) {
|
|
170
|
+
this._solanaVaultReader.updateDeployment(this._solanaParser);
|
|
171
|
+
}
|
|
172
|
+
else if (this._solanaConnection) {
|
|
173
|
+
this._solanaVaultReader = new SolanaVaultReader(this._solanaConnection, this._solanaParser);
|
|
174
|
+
}
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
115
177
|
if (this.chainType === "sui") {
|
|
116
178
|
const suiDeployment = newDeployment;
|
|
117
179
|
this._suiDeployment = suiDeployment;
|
|
@@ -161,28 +223,82 @@ export class EmberVaults {
|
|
|
161
223
|
get walletAddress() {
|
|
162
224
|
return this._walletAddress;
|
|
163
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* Read-only vault access. Sui has no reader, so this is EVM or Solana.
|
|
228
|
+
*/
|
|
229
|
+
get reader() {
|
|
230
|
+
if (this.chainType === "solana") {
|
|
231
|
+
if (!this._solanaVaultReader)
|
|
232
|
+
throw new Error("No Solana deployment loaded. Pass one, or call init().");
|
|
233
|
+
return this._solanaVaultReader;
|
|
234
|
+
}
|
|
235
|
+
if (this.chainType === "sui")
|
|
236
|
+
throw new Error("reader is not available for SUI chain");
|
|
237
|
+
return this._evmVaultReader;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Solana's write surfaces are not built yet. Thrown rather than returning
|
|
241
|
+
* undefined so the failure names what is missing instead of surfacing as
|
|
242
|
+
* "cannot read property of undefined" several frames later.
|
|
243
|
+
*/
|
|
244
|
+
notOnSolana(surface) {
|
|
245
|
+
throw new Error(`${surface} is not available for Solana yet. ` +
|
|
246
|
+
`The Solana module exposes reads and the user writes: ` +
|
|
247
|
+
`use .reader, .user, .parser or .deployment.`);
|
|
248
|
+
}
|
|
249
|
+
// ============================================
|
|
250
|
+
// Solana Getters
|
|
251
|
+
// ============================================
|
|
252
|
+
get connection() {
|
|
253
|
+
if (this.chainType !== "solana")
|
|
254
|
+
throw new Error("connection is only available for Solana chain");
|
|
255
|
+
return this._solanaConnection;
|
|
256
|
+
}
|
|
164
257
|
get signer() {
|
|
165
258
|
return this.chainType === "sui" ? this._suiSigner : this._evmSigner;
|
|
166
259
|
}
|
|
167
260
|
get parser() {
|
|
261
|
+
if (this.chainType === "solana") {
|
|
262
|
+
if (!this._solanaParser)
|
|
263
|
+
throw new Error("No Solana deployment loaded. Pass one, or call init().");
|
|
264
|
+
return this._solanaParser;
|
|
265
|
+
}
|
|
168
266
|
return this.chainType === "sui" ? this._suiParser : this._evmParser;
|
|
169
267
|
}
|
|
170
268
|
get txBuilder() {
|
|
269
|
+
if (this.chainType === "solana")
|
|
270
|
+
this.notOnSolana("txBuilder");
|
|
171
271
|
return this.chainType === "sui" ? this._suiTxBuilder : this._evmTxBuilder;
|
|
172
272
|
}
|
|
173
273
|
get admin() {
|
|
274
|
+
if (this.chainType === "solana")
|
|
275
|
+
this.notOnSolana("admin");
|
|
174
276
|
return this.chainType === "sui" ? this._suiAdmin : this._evmAdmin;
|
|
175
277
|
}
|
|
176
278
|
get operator() {
|
|
279
|
+
if (this.chainType === "solana")
|
|
280
|
+
this.notOnSolana("operator");
|
|
177
281
|
return this.chainType === "sui" ? this._suiOperator : this._evmOperator;
|
|
178
282
|
}
|
|
179
283
|
get vaultAdmin() {
|
|
284
|
+
if (this.chainType === "solana")
|
|
285
|
+
this.notOnSolana("vaultAdmin");
|
|
180
286
|
return this.chainType === "sui" ? this._suiVaultAdmin : this._evmVaultAdmin;
|
|
181
287
|
}
|
|
182
288
|
get user() {
|
|
289
|
+
if (this.chainType === "solana") {
|
|
290
|
+
if (!this._solanaUser)
|
|
291
|
+
throw new Error("No Solana deployment loaded. Pass one, or call init().");
|
|
292
|
+
return this._solanaUser;
|
|
293
|
+
}
|
|
183
294
|
return this.chainType === "sui" ? this._suiUser : this._evmUser;
|
|
184
295
|
}
|
|
185
296
|
get deployment() {
|
|
297
|
+
if (this.chainType === "solana") {
|
|
298
|
+
if (!this._solanaDeployment)
|
|
299
|
+
throw new Error("No Solana deployment loaded. Pass one, or call init().");
|
|
300
|
+
return this._solanaDeployment;
|
|
301
|
+
}
|
|
186
302
|
return this.chainType === "sui" ? this._suiDeployment : this._evmDeployment;
|
|
187
303
|
}
|
|
188
304
|
// ============================================
|
|
@@ -197,6 +313,12 @@ export class EmberVaults {
|
|
|
197
313
|
/**
|
|
198
314
|
* Check if this is an EVM instance
|
|
199
315
|
*/
|
|
316
|
+
/**
|
|
317
|
+
* Check if this is a Solana instance
|
|
318
|
+
*/
|
|
319
|
+
isSolana() {
|
|
320
|
+
return this.chainType === "solana";
|
|
321
|
+
}
|
|
200
322
|
isEvm() {
|
|
201
323
|
return this.chainType === "evm";
|
|
202
324
|
}
|
|
@@ -242,4 +364,26 @@ export class EmberVaults {
|
|
|
242
364
|
reader: this._evmVaultReader
|
|
243
365
|
};
|
|
244
366
|
}
|
|
367
|
+
/**
|
|
368
|
+
* Get Solana-specific instance (throws if not Solana)
|
|
369
|
+
*/
|
|
370
|
+
asSolana() {
|
|
371
|
+
if (!this.isSolana())
|
|
372
|
+
throw new Error("This EmberVaults instance is not configured for Solana");
|
|
373
|
+
if (!this._solanaParser || !this._solanaVaultReader || !this._solanaDeployment) {
|
|
374
|
+
throw new Error("No Solana deployment loaded. Pass one, or call init().");
|
|
375
|
+
}
|
|
376
|
+
if (!this._solanaUser) {
|
|
377
|
+
throw new Error("No Solana deployment loaded. Pass one, or call init().");
|
|
378
|
+
}
|
|
379
|
+
return {
|
|
380
|
+
chainType: "solana",
|
|
381
|
+
connection: this._solanaConnection,
|
|
382
|
+
parser: this._solanaParser,
|
|
383
|
+
walletAddress: this._walletAddress,
|
|
384
|
+
reader: this._solanaVaultReader,
|
|
385
|
+
user: this._solanaUser,
|
|
386
|
+
deployment: this._solanaDeployment
|
|
387
|
+
};
|
|
388
|
+
}
|
|
245
389
|
}
|
package/dist/src/index.d.ts
CHANGED
package/dist/src/index.js
CHANGED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import type { PublicKey } from "@solana/web3.js";
|
|
2
|
+
/**
|
|
3
|
+
* Solana vault interfaces.
|
|
4
|
+
*
|
|
5
|
+
* The on-chain program is `ember_vaults`, the Anchor port of the Move
|
|
6
|
+
* `vault` module. Three things differ from Sui and EVM and shape everything
|
|
7
|
+
* here:
|
|
8
|
+
*
|
|
9
|
+
* - **Every account a transaction touches must be named up front.** There is
|
|
10
|
+
* no equivalent of Sui reaching through a shared object or EVM reading
|
|
11
|
+
* storage mid-call, so the builders below take addresses the other chains
|
|
12
|
+
* derive internally.
|
|
13
|
+
* - **The share mint is the vault's identity.** The vault PDA is
|
|
14
|
+
* `["vault", share_mint]`, so one mint is one vault and every other address
|
|
15
|
+
* follows from it.
|
|
16
|
+
* - **Withdrawals are asynchronous.** Redeeming queues a request and escrows
|
|
17
|
+
* the shares; the operator settles the queue in FIFO order afterwards.
|
|
18
|
+
*/
|
|
19
|
+
/** 100% in the program's fixed point. Rates and percentages both use it. */
|
|
20
|
+
export declare const RATE_ONE = 1000000000n;
|
|
21
|
+
/** `PERCENT_DECIMALS` in the program: 1% is 1e7. */
|
|
22
|
+
export declare const PERCENT_DECIMALS = 7;
|
|
23
|
+
/** Seeds for every PDA the program owns, verbatim from `constants.rs`. */
|
|
24
|
+
export declare const SOLANA_SEEDS: {
|
|
25
|
+
readonly protocolConfig: "protocol_config";
|
|
26
|
+
readonly vault: "vault";
|
|
27
|
+
readonly vaultCollateral: "vault_collateral";
|
|
28
|
+
readonly vaultPendingShares: "vault_pending_shares";
|
|
29
|
+
readonly withdrawalQueue: "withdrawal_queue";
|
|
30
|
+
readonly account: "account";
|
|
31
|
+
readonly withdrawalRequest: "withdrawal_request";
|
|
32
|
+
readonly parkingLot: "parking_lot";
|
|
33
|
+
readonly parkingLotEscrow: "parking_lot_escrow";
|
|
34
|
+
readonly depositRequest: "deposit_request";
|
|
35
|
+
};
|
|
36
|
+
/** A cluster the deployment manifest can describe. */
|
|
37
|
+
export type SolanaCluster = "localnet" | "devnet" | "testnet" | "mainnet";
|
|
38
|
+
/**
|
|
39
|
+
* The deployed program, as `deployments/<cluster>.json` records it in
|
|
40
|
+
* ember-contracts. Addresses are base58 strings rather than `PublicKey`s so
|
|
41
|
+
* the manifest round-trips through JSON and the API unchanged.
|
|
42
|
+
*/
|
|
43
|
+
export interface ISolanaProgramInfo {
|
|
44
|
+
/** The only field the API guarantees: everything else is a PDA of it. */
|
|
45
|
+
programId: string;
|
|
46
|
+
programDataAddress?: string;
|
|
47
|
+
upgradeAuthority?: string | null;
|
|
48
|
+
/** Manifest only. */
|
|
49
|
+
programSize?: number;
|
|
50
|
+
deployedAt?: string;
|
|
51
|
+
/** Where an indexer starts its backfill. EVM's deploymentBlockNumber. */
|
|
52
|
+
deploySlot?: number;
|
|
53
|
+
/** Manifest only. */
|
|
54
|
+
gitCommit?: string;
|
|
55
|
+
}
|
|
56
|
+
/** The protocol-wide config PDA and the values it holds. */
|
|
57
|
+
export interface ISolanaProtocolConfig {
|
|
58
|
+
address: string;
|
|
59
|
+
version: string;
|
|
60
|
+
/**
|
|
61
|
+
* The fields below are manifest only. All of them change without a
|
|
62
|
+
* redeployment - `transfer_protocol_admin`, `set_guardian`,
|
|
63
|
+
* `update_platform_fee_recipient` - so a config file is the wrong place to
|
|
64
|
+
* trust them. Read them from the chain with {@link decodeProtocolConfig}.
|
|
65
|
+
*/
|
|
66
|
+
admin?: string;
|
|
67
|
+
guardian?: string | null;
|
|
68
|
+
platformFeeRecipient?: string;
|
|
69
|
+
initializedAt?: string;
|
|
70
|
+
slot?: number;
|
|
71
|
+
}
|
|
72
|
+
/** A collateral mint the protocol knows about, keyed by symbol. */
|
|
73
|
+
export interface ISolanaTokenInfo {
|
|
74
|
+
mint: string;
|
|
75
|
+
symbol: string;
|
|
76
|
+
name: string;
|
|
77
|
+
decimals: number;
|
|
78
|
+
/** SPL Token or Token-2022; the two mints of a vault may differ. */
|
|
79
|
+
tokenProgram: string;
|
|
80
|
+
/** Manifest only: live state, readable from the mint. */
|
|
81
|
+
mintAuthority?: string | null;
|
|
82
|
+
freezeAuthority?: string | null;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* A vault, keyed in the manifest by its share mint.
|
|
86
|
+
*
|
|
87
|
+
* The four PDAs are recorded rather than re-derived so ops tooling and
|
|
88
|
+
* indexers agree with the SDK byte for byte, but every one of them is a pure
|
|
89
|
+
* function of `shareMint` — see {@link deriveVaultPdas}.
|
|
90
|
+
*/
|
|
91
|
+
export interface ISolanaVaultInfo {
|
|
92
|
+
name: string;
|
|
93
|
+
vault: string;
|
|
94
|
+
collateralMint: string;
|
|
95
|
+
shareMint: string;
|
|
96
|
+
/** Null when the vault was created with `--no-metadata`. */
|
|
97
|
+
shareName: string | null;
|
|
98
|
+
shareSymbol: string | null;
|
|
99
|
+
/**
|
|
100
|
+
* The Metaplex Token Metadata PDA holding those two. Manifest only: it is
|
|
101
|
+
* a PDA of the share mint, so {@link metadataAddress} derives it.
|
|
102
|
+
*/
|
|
103
|
+
shareMetadata?: string | null;
|
|
104
|
+
admin: string;
|
|
105
|
+
operator: string;
|
|
106
|
+
/** Unset until the admin calls `update_vault_rate_manager`. */
|
|
107
|
+
rateManager: string | null;
|
|
108
|
+
subAccounts: string[];
|
|
109
|
+
/**
|
|
110
|
+
* Manifest only. All three are PDAs of the share mint, so
|
|
111
|
+
* {@link deriveVaultPdas} produces them; the API does not serve a second
|
|
112
|
+
* source of truth that could disagree with the chain.
|
|
113
|
+
*/
|
|
114
|
+
vaultCollateral?: string;
|
|
115
|
+
vaultPendingShares?: string;
|
|
116
|
+
withdrawalQueue?: string;
|
|
117
|
+
/** Raw u64 strings, at the collateral mint's decimals. */
|
|
118
|
+
feePercentage: string;
|
|
119
|
+
maxRateChangePerUpdate: string;
|
|
120
|
+
maxTvl: string;
|
|
121
|
+
minWithdrawalShares: string;
|
|
122
|
+
/** Manifest only: readable from the vault account. */
|
|
123
|
+
rateUpdateInterval?: string;
|
|
124
|
+
}
|
|
125
|
+
/** The whole Solana deployment, as the manifest and the API carry it. */
|
|
126
|
+
export interface ISolanaDeployment {
|
|
127
|
+
/**
|
|
128
|
+
* Which cluster this deployment lives on. Optional because a manifest read
|
|
129
|
+
* from disk may predate the field, not because the API omits it - the
|
|
130
|
+
* chain identifier is "solana" everywhere, so this is the only thing that
|
|
131
|
+
* distinguishes devnet from mainnet.
|
|
132
|
+
*/
|
|
133
|
+
cluster?: SolanaCluster;
|
|
134
|
+
/** Manifest only: the endpoint the deploy ran through, not a property of
|
|
135
|
+
* the deployment, so the API does not serve it. */
|
|
136
|
+
rpcUrl?: string;
|
|
137
|
+
program: ISolanaProgramInfo;
|
|
138
|
+
protocolConfig?: ISolanaProtocolConfig;
|
|
139
|
+
/** Keyed by symbol. */
|
|
140
|
+
tokens?: Record<string, ISolanaTokenInfo>;
|
|
141
|
+
/** Keyed by share mint. */
|
|
142
|
+
vaults?: Record<string, ISolanaVaultInfo>;
|
|
143
|
+
}
|
|
144
|
+
/** Alias matching the EVM and Sui naming, for the unified entry point. */
|
|
145
|
+
export type SolanaProtocolInfo = ISolanaDeployment;
|
|
146
|
+
/** The PDAs that hang off a vault, all derived from its share mint. */
|
|
147
|
+
export interface ISolanaVaultPdas {
|
|
148
|
+
vault: PublicKey;
|
|
149
|
+
vaultCollateral: PublicKey;
|
|
150
|
+
vaultPendingShares: PublicKey;
|
|
151
|
+
withdrawalQueue: PublicKey;
|
|
152
|
+
}
|
|
153
|
+
/** `state::Rate`. */
|
|
154
|
+
export interface ISolanaRate {
|
|
155
|
+
/** Shares per unit of collateral, 1e9 fixed point. Inverse to share value. */
|
|
156
|
+
value: bigint;
|
|
157
|
+
maxRateChangePerUpdate: bigint;
|
|
158
|
+
rateUpdateInterval: bigint;
|
|
159
|
+
lastUpdatedAt: bigint;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* `state::WithdrawalFee`, the tail of `Vault`.
|
|
163
|
+
*
|
|
164
|
+
* Two fees that stack: a permanent one charged on every redemption, and a
|
|
165
|
+
* time-based one charged only while the holder is inside
|
|
166
|
+
* `timeBasedFeeThreshold` of their last deposit. Both are 1e9 fixed point,
|
|
167
|
+
* so 1% is 1e7.
|
|
168
|
+
*/
|
|
169
|
+
export interface ISolanaWithdrawalFee {
|
|
170
|
+
permanentFeePercentage: bigint;
|
|
171
|
+
timeBasedFeePercentage: bigint;
|
|
172
|
+
/** Milliseconds since the holder's last deposit. */
|
|
173
|
+
timeBasedFeeThreshold: bigint;
|
|
174
|
+
/** Addresses that pay neither fee. */
|
|
175
|
+
feeExempt: PublicKey[];
|
|
176
|
+
}
|
|
177
|
+
/** `state::PlatformFee`. */
|
|
178
|
+
export interface ISolanaPlatformFee {
|
|
179
|
+
accrued: bigint;
|
|
180
|
+
lastChargedAt: bigint;
|
|
181
|
+
}
|
|
182
|
+
/** `state::Vault`, decoded. */
|
|
183
|
+
export interface ISolanaVaultState {
|
|
184
|
+
name: string;
|
|
185
|
+
admin: PublicKey;
|
|
186
|
+
operator: PublicKey;
|
|
187
|
+
rateManager: PublicKey;
|
|
188
|
+
collateralMint: PublicKey;
|
|
189
|
+
shareMint: PublicKey;
|
|
190
|
+
blacklisted: PublicKey[];
|
|
191
|
+
paused: boolean;
|
|
192
|
+
pausedDeposits: boolean;
|
|
193
|
+
pausedWithdrawals: boolean;
|
|
194
|
+
pausedPrivileged: boolean;
|
|
195
|
+
subAccounts: PublicKey[];
|
|
196
|
+
rate: ISolanaRate;
|
|
197
|
+
feePercentage: bigint;
|
|
198
|
+
/** Mirrors the custody token account; the two must always agree. */
|
|
199
|
+
balance: bigint;
|
|
200
|
+
pendingSharesToBurn: bigint;
|
|
201
|
+
fee: ISolanaPlatformFee;
|
|
202
|
+
minWithdrawalShares: bigint;
|
|
203
|
+
maxTvl: bigint;
|
|
204
|
+
sequenceNumber: bigint;
|
|
205
|
+
/** `PublicKey.default` when no lot is bound. */
|
|
206
|
+
parkingLot: PublicKey;
|
|
207
|
+
depositUserList: PublicKey[];
|
|
208
|
+
withdrawalUserList: PublicKey[];
|
|
209
|
+
withdrawalFee: ISolanaWithdrawalFee;
|
|
210
|
+
}
|
|
211
|
+
/** `state::WithdrawalRequest`, the entry stored in the queue. */
|
|
212
|
+
export interface ISolanaWithdrawalRequest {
|
|
213
|
+
owner: PublicKey;
|
|
214
|
+
receiver: PublicKey;
|
|
215
|
+
shares: bigint;
|
|
216
|
+
estimatedWithdrawAmount: bigint;
|
|
217
|
+
timestamp: bigint;
|
|
218
|
+
sequenceNumber: bigint;
|
|
219
|
+
}
|
|
220
|
+
/** `state::VaultAccount`: one per (vault, owner). */
|
|
221
|
+
export interface ISolanaVaultAccount {
|
|
222
|
+
/** When this owner last deposited, for the time-based withdrawal fee. */
|
|
223
|
+
lastDepositTs: bigint;
|
|
224
|
+
totalPendingWithdrawalShares: bigint;
|
|
225
|
+
pendingWithdrawalRequests: ISolanaWithdrawalRequest[];
|
|
226
|
+
cancelWithdrawRequest: bigint[];
|
|
227
|
+
/** Seeds the owner's next request PDA; only ever increases. */
|
|
228
|
+
nextRequestNonce: bigint;
|
|
229
|
+
}
|
|
230
|
+
/** `state::WithdrawalRequestAccount`, one account per queued request. */
|
|
231
|
+
export interface ISolanaWithdrawalRequestAccount {
|
|
232
|
+
address: PublicKey;
|
|
233
|
+
vault: PublicKey;
|
|
234
|
+
nonce: bigint;
|
|
235
|
+
queueIndex: bigint;
|
|
236
|
+
request: ISolanaWithdrawalRequest;
|
|
237
|
+
}
|
|
238
|
+
/** `state::WithdrawalQueue`: head and tail only, the entries live elsewhere. */
|
|
239
|
+
export interface ISolanaWithdrawalQueue {
|
|
240
|
+
vault: PublicKey;
|
|
241
|
+
head: bigint;
|
|
242
|
+
tail: bigint;
|
|
243
|
+
}
|
|
244
|
+
/** `state::ProtocolConfig`, decoded. */
|
|
245
|
+
export interface ISolanaProtocolConfigState {
|
|
246
|
+
admin: PublicKey;
|
|
247
|
+
/** Set by `transfer_protocol_admin`; `accept_protocol_admin` completes it. */
|
|
248
|
+
pendingAdmin: PublicKey;
|
|
249
|
+
guardian: PublicKey;
|
|
250
|
+
version: bigint;
|
|
251
|
+
/** The guardian's kill switch. Admin operations keep working. */
|
|
252
|
+
pauseNonAdminOperations: boolean;
|
|
253
|
+
platformFeeRecipient: PublicKey;
|
|
254
|
+
minRate: bigint;
|
|
255
|
+
maxRate: bigint;
|
|
256
|
+
defaultRate: bigint;
|
|
257
|
+
minRateInterval: bigint;
|
|
258
|
+
maxRateInterval: bigint;
|
|
259
|
+
maxFeePercentage: bigint;
|
|
260
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solana vault interfaces.
|
|
3
|
+
*
|
|
4
|
+
* The on-chain program is `ember_vaults`, the Anchor port of the Move
|
|
5
|
+
* `vault` module. Three things differ from Sui and EVM and shape everything
|
|
6
|
+
* here:
|
|
7
|
+
*
|
|
8
|
+
* - **Every account a transaction touches must be named up front.** There is
|
|
9
|
+
* no equivalent of Sui reaching through a shared object or EVM reading
|
|
10
|
+
* storage mid-call, so the builders below take addresses the other chains
|
|
11
|
+
* derive internally.
|
|
12
|
+
* - **The share mint is the vault's identity.** The vault PDA is
|
|
13
|
+
* `["vault", share_mint]`, so one mint is one vault and every other address
|
|
14
|
+
* follows from it.
|
|
15
|
+
* - **Withdrawals are asynchronous.** Redeeming queues a request and escrows
|
|
16
|
+
* the shares; the operator settles the queue in FIFO order afterwards.
|
|
17
|
+
*/
|
|
18
|
+
/** 100% in the program's fixed point. Rates and percentages both use it. */
|
|
19
|
+
export const RATE_ONE = 1000000000n;
|
|
20
|
+
/** `PERCENT_DECIMALS` in the program: 1% is 1e7. */
|
|
21
|
+
export const PERCENT_DECIMALS = 7;
|
|
22
|
+
/** Seeds for every PDA the program owns, verbatim from `constants.rs`. */
|
|
23
|
+
export const SOLANA_SEEDS = {
|
|
24
|
+
protocolConfig: "protocol_config",
|
|
25
|
+
vault: "vault",
|
|
26
|
+
vaultCollateral: "vault_collateral",
|
|
27
|
+
vaultPendingShares: "vault_pending_shares",
|
|
28
|
+
withdrawalQueue: "withdrawal_queue",
|
|
29
|
+
account: "account",
|
|
30
|
+
withdrawalRequest: "withdrawal_request",
|
|
31
|
+
parkingLot: "parking_lot",
|
|
32
|
+
parkingLotEscrow: "parking_lot_escrow",
|
|
33
|
+
depositRequest: "deposit_request"
|
|
34
|
+
};
|
|
35
|
+
// Call options and response types ship with the builders that take them, in
|
|
36
|
+
// the phase that adds writes. Declaring them here first would be a public
|
|
37
|
+
// surface nothing implements.
|