@ember-finance/sdk 2.37.0 → 2.39.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/api/apis/vaults-api.d.ts +0 -68
- package/dist/src/api/apis/vaults-api.js +1 -143
- package/dist/src/api/models/index.d.ts +0 -2
- package/dist/src/api/models/index.js +0 -2
- package/dist/src/common/constants.d.ts +1 -1
- package/dist/src/ember-vaults.d.ts +76 -4
- package/dist/src/ember-vaults.js +127 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/solana-vaults/index.d.ts +3 -0
- package/dist/src/solana-vaults/index.js +3 -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 +1 -0
- package/dist/src/solana-vaults/on-chain-calls/index.js +1 -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/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 +54 -0
- package/dist/src/solana-vaults/utils/pdas.js +93 -0
- package/dist/src/sui-vaults/on-chain-calls/vault.d.ts +0 -35
- package/dist/src/sui-vaults/on-chain-calls/vault.js +0 -25
- package/package.json +10 -4
- package/dist/src/api/models/strategy.d.ts +0 -60
- package/dist/src/api/models/strategy.js +0 -14
- package/dist/src/api/models/update-vault-strategies.d.ts +0 -61
- package/dist/src/api/models/update-vault-strategies.js +0 -14
- package/dist/src/sui-vaults/interfaces/bcs.d.ts +0 -26
- package/dist/src/sui-vaults/interfaces/bcs.js +0 -17
package/dist/src/ember-vaults.js
CHANGED
|
@@ -14,6 +14,8 @@ 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";
|
|
17
19
|
import { environmentConfig } from "./common/constants.js";
|
|
18
20
|
/**
|
|
19
21
|
* EmberVaults - Unified interface for both SUI and EVM chains
|
|
@@ -54,10 +56,29 @@ export class EmberVaults {
|
|
|
54
56
|
if (options.chainType === "sui") {
|
|
55
57
|
this._initSui(options);
|
|
56
58
|
}
|
|
59
|
+
else if (options.chainType === "solana") {
|
|
60
|
+
this._initSolana(options);
|
|
61
|
+
}
|
|
57
62
|
else {
|
|
58
63
|
this._initEvm(options);
|
|
59
64
|
}
|
|
60
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Solana setup.
|
|
68
|
+
*
|
|
69
|
+
* The deployment is optional here as it is on the other chains: pass one
|
|
70
|
+
* to work offline, or call `init()` to fetch it from the API. Without
|
|
71
|
+
* either, the reader has nothing to resolve addresses against.
|
|
72
|
+
*/
|
|
73
|
+
_initSolana(options) {
|
|
74
|
+
this._solanaConnection = options.connection;
|
|
75
|
+
this._walletAddress = options.walletAddress;
|
|
76
|
+
if (options.deployment) {
|
|
77
|
+
this._solanaDeployment = options.deployment;
|
|
78
|
+
this._solanaParser = new SolanaDeploymentParser(options.deployment);
|
|
79
|
+
this._solanaVaultReader = new SolanaVaultReader(options.connection, this._solanaParser);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
61
82
|
_initSui(options) {
|
|
62
83
|
this._suiNetwork = options.network;
|
|
63
84
|
this._suiClient = options.suiClient;
|
|
@@ -97,6 +118,23 @@ export class EmberVaults {
|
|
|
97
118
|
throw new Error(`Deployment data not found for chain ${this._chainIdentifier}`);
|
|
98
119
|
}
|
|
99
120
|
}
|
|
121
|
+
else if (this.chainType === "solana") {
|
|
122
|
+
// One Solana deployment per environment, so there is nothing to
|
|
123
|
+
// disambiguate by chain identifier - it is "solana" on every
|
|
124
|
+
// cluster, and which cluster follows from the environment.
|
|
125
|
+
// Compared as a string: the API spec vendored under src/api/v2 was
|
|
126
|
+
// copied before bfvs added "solana" to VaultsDeploymentConfig's
|
|
127
|
+
// chainType enum, so the generated union does not list it yet. The
|
|
128
|
+
// wire value is correct and staging already serves it; this reads
|
|
129
|
+
// as written rather than as typed until the client is regenerated.
|
|
130
|
+
const deploymentData = deployment.data.find(d => d.chainType === "solana");
|
|
131
|
+
if (deploymentData) {
|
|
132
|
+
this.updateDeployment(deploymentData.config);
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
throw new Error("Deployment data not found for chain solana");
|
|
136
|
+
}
|
|
137
|
+
}
|
|
100
138
|
else {
|
|
101
139
|
const deploymentData = deployment.data.find(d => d.chainType === "evm" && d.chain === this._chainIdentifier);
|
|
102
140
|
if (deploymentData) {
|
|
@@ -112,6 +150,21 @@ export class EmberVaults {
|
|
|
112
150
|
* @param newDeployment The new deployment configuration (ISuiDeployment or EvmProtocolInfo)
|
|
113
151
|
*/
|
|
114
152
|
updateDeployment(newDeployment) {
|
|
153
|
+
if (this.chainType === "solana") {
|
|
154
|
+
const solanaDeployment = newDeployment;
|
|
155
|
+
this._solanaDeployment = solanaDeployment;
|
|
156
|
+
// The parser holds the program id that every address derives from,
|
|
157
|
+
// so it is rebuilt rather than mutated - a deployment pointing at a
|
|
158
|
+
// different program is a different parser.
|
|
159
|
+
this._solanaParser = new SolanaDeploymentParser(solanaDeployment);
|
|
160
|
+
if (this._solanaVaultReader) {
|
|
161
|
+
this._solanaVaultReader.updateDeployment(this._solanaParser);
|
|
162
|
+
}
|
|
163
|
+
else if (this._solanaConnection) {
|
|
164
|
+
this._solanaVaultReader = new SolanaVaultReader(this._solanaConnection, this._solanaParser);
|
|
165
|
+
}
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
115
168
|
if (this.chainType === "sui") {
|
|
116
169
|
const suiDeployment = newDeployment;
|
|
117
170
|
this._suiDeployment = suiDeployment;
|
|
@@ -161,28 +214,78 @@ export class EmberVaults {
|
|
|
161
214
|
get walletAddress() {
|
|
162
215
|
return this._walletAddress;
|
|
163
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* Read-only vault access. Sui has no reader, so this is EVM or Solana.
|
|
219
|
+
*/
|
|
220
|
+
get reader() {
|
|
221
|
+
if (this.chainType === "solana") {
|
|
222
|
+
if (!this._solanaVaultReader)
|
|
223
|
+
throw new Error("No Solana deployment loaded. Pass one, or call init().");
|
|
224
|
+
return this._solanaVaultReader;
|
|
225
|
+
}
|
|
226
|
+
if (this.chainType === "sui")
|
|
227
|
+
throw new Error("reader is not available for SUI chain");
|
|
228
|
+
return this._evmVaultReader;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Solana's write surfaces are not built yet. Thrown rather than returning
|
|
232
|
+
* undefined so the failure names what is missing instead of surfacing as
|
|
233
|
+
* "cannot read property of undefined" several frames later.
|
|
234
|
+
*/
|
|
235
|
+
notOnSolana(surface) {
|
|
236
|
+
throw new Error(`${surface} is not available for Solana yet. ` +
|
|
237
|
+
`The Solana module currently exposes reads only: use .reader, .parser or .deployment.`);
|
|
238
|
+
}
|
|
239
|
+
// ============================================
|
|
240
|
+
// Solana Getters
|
|
241
|
+
// ============================================
|
|
242
|
+
get connection() {
|
|
243
|
+
if (this.chainType !== "solana")
|
|
244
|
+
throw new Error("connection is only available for Solana chain");
|
|
245
|
+
return this._solanaConnection;
|
|
246
|
+
}
|
|
164
247
|
get signer() {
|
|
165
248
|
return this.chainType === "sui" ? this._suiSigner : this._evmSigner;
|
|
166
249
|
}
|
|
167
250
|
get parser() {
|
|
251
|
+
if (this.chainType === "solana") {
|
|
252
|
+
if (!this._solanaParser)
|
|
253
|
+
throw new Error("No Solana deployment loaded. Pass one, or call init().");
|
|
254
|
+
return this._solanaParser;
|
|
255
|
+
}
|
|
168
256
|
return this.chainType === "sui" ? this._suiParser : this._evmParser;
|
|
169
257
|
}
|
|
170
258
|
get txBuilder() {
|
|
259
|
+
if (this.chainType === "solana")
|
|
260
|
+
this.notOnSolana("txBuilder");
|
|
171
261
|
return this.chainType === "sui" ? this._suiTxBuilder : this._evmTxBuilder;
|
|
172
262
|
}
|
|
173
263
|
get admin() {
|
|
264
|
+
if (this.chainType === "solana")
|
|
265
|
+
this.notOnSolana("admin");
|
|
174
266
|
return this.chainType === "sui" ? this._suiAdmin : this._evmAdmin;
|
|
175
267
|
}
|
|
176
268
|
get operator() {
|
|
269
|
+
if (this.chainType === "solana")
|
|
270
|
+
this.notOnSolana("operator");
|
|
177
271
|
return this.chainType === "sui" ? this._suiOperator : this._evmOperator;
|
|
178
272
|
}
|
|
179
273
|
get vaultAdmin() {
|
|
274
|
+
if (this.chainType === "solana")
|
|
275
|
+
this.notOnSolana("vaultAdmin");
|
|
180
276
|
return this.chainType === "sui" ? this._suiVaultAdmin : this._evmVaultAdmin;
|
|
181
277
|
}
|
|
182
278
|
get user() {
|
|
279
|
+
if (this.chainType === "solana")
|
|
280
|
+
this.notOnSolana("user");
|
|
183
281
|
return this.chainType === "sui" ? this._suiUser : this._evmUser;
|
|
184
282
|
}
|
|
185
283
|
get deployment() {
|
|
284
|
+
if (this.chainType === "solana") {
|
|
285
|
+
if (!this._solanaDeployment)
|
|
286
|
+
throw new Error("No Solana deployment loaded. Pass one, or call init().");
|
|
287
|
+
return this._solanaDeployment;
|
|
288
|
+
}
|
|
186
289
|
return this.chainType === "sui" ? this._suiDeployment : this._evmDeployment;
|
|
187
290
|
}
|
|
188
291
|
// ============================================
|
|
@@ -197,6 +300,12 @@ export class EmberVaults {
|
|
|
197
300
|
/**
|
|
198
301
|
* Check if this is an EVM instance
|
|
199
302
|
*/
|
|
303
|
+
/**
|
|
304
|
+
* Check if this is a Solana instance
|
|
305
|
+
*/
|
|
306
|
+
isSolana() {
|
|
307
|
+
return this.chainType === "solana";
|
|
308
|
+
}
|
|
200
309
|
isEvm() {
|
|
201
310
|
return this.chainType === "evm";
|
|
202
311
|
}
|
|
@@ -242,4 +351,22 @@ export class EmberVaults {
|
|
|
242
351
|
reader: this._evmVaultReader
|
|
243
352
|
};
|
|
244
353
|
}
|
|
354
|
+
/**
|
|
355
|
+
* Get Solana-specific instance (throws if not Solana)
|
|
356
|
+
*/
|
|
357
|
+
asSolana() {
|
|
358
|
+
if (!this.isSolana())
|
|
359
|
+
throw new Error("This EmberVaults instance is not configured for Solana");
|
|
360
|
+
if (!this._solanaParser || !this._solanaVaultReader || !this._solanaDeployment) {
|
|
361
|
+
throw new Error("No Solana deployment loaded. Pass one, or call init().");
|
|
362
|
+
}
|
|
363
|
+
return {
|
|
364
|
+
chainType: "solana",
|
|
365
|
+
connection: this._solanaConnection,
|
|
366
|
+
parser: this._solanaParser,
|
|
367
|
+
walletAddress: this._walletAddress,
|
|
368
|
+
reader: this._solanaVaultReader,
|
|
369
|
+
deployment: this._solanaDeployment
|
|
370
|
+
};
|
|
371
|
+
}
|
|
245
372
|
}
|
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.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./vault-reader.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./vault-reader.js";
|