@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.
Files changed (32) hide show
  1. package/dist/src/common/constants.d.ts +1 -1
  2. package/dist/src/ember-vaults.d.ts +85 -5
  3. package/dist/src/ember-vaults.js +144 -0
  4. package/dist/src/index.d.ts +1 -0
  5. package/dist/src/index.js +1 -0
  6. package/dist/src/solana-vaults/index.d.ts +4 -0
  7. package/dist/src/solana-vaults/index.js +4 -0
  8. package/dist/src/solana-vaults/interfaces/index.d.ts +260 -0
  9. package/dist/src/solana-vaults/interfaces/index.js +37 -0
  10. package/dist/src/solana-vaults/on-chain-calls/index.d.ts +3 -0
  11. package/dist/src/solana-vaults/on-chain-calls/index.js +3 -0
  12. package/dist/src/solana-vaults/on-chain-calls/tx-builder.d.ts +62 -0
  13. package/dist/src/solana-vaults/on-chain-calls/tx-builder.js +190 -0
  14. package/dist/src/solana-vaults/on-chain-calls/user.d.ts +150 -0
  15. package/dist/src/solana-vaults/on-chain-calls/user.js +288 -0
  16. package/dist/src/solana-vaults/on-chain-calls/vault-reader.d.ts +190 -0
  17. package/dist/src/solana-vaults/on-chain-calls/vault-reader.js +432 -0
  18. package/dist/src/solana-vaults/signers/index.d.ts +1 -0
  19. package/dist/src/solana-vaults/signers/index.js +1 -0
  20. package/dist/src/solana-vaults/signers/solana-signer.d.ts +44 -0
  21. package/dist/src/solana-vaults/signers/solana-signer.js +61 -0
  22. package/dist/src/solana-vaults/utils/borsh.d.ts +65 -0
  23. package/dist/src/solana-vaults/utils/borsh.js +237 -0
  24. package/dist/src/solana-vaults/utils/deployment-parser.d.ts +94 -0
  25. package/dist/src/solana-vaults/utils/deployment-parser.js +174 -0
  26. package/dist/src/solana-vaults/utils/index.d.ts +3 -0
  27. package/dist/src/solana-vaults/utils/index.js +3 -0
  28. package/dist/src/solana-vaults/utils/math.d.ts +77 -0
  29. package/dist/src/solana-vaults/utils/math.js +111 -0
  30. package/dist/src/solana-vaults/utils/pdas.d.ts +63 -0
  31. package/dist/src/solana-vaults/utils/pdas.js +114 -0
  32. package/package.json +10 -2
@@ -0,0 +1,3 @@
1
+ export * from "./tx-builder.js";
2
+ export * from "./user.js";
3
+ export * from "./vault-reader.js";
@@ -0,0 +1,3 @@
1
+ export * from "./tx-builder.js";
2
+ export * from "./user.js";
3
+ export * from "./vault-reader.js";
@@ -0,0 +1,62 @@
1
+ import { PublicKey, TransactionInstruction } from "@solana/web3.js";
2
+ /** Everything an instruction needs that cannot be derived from the share mint. */
3
+ export interface ISolanaVaultContext {
4
+ programId: PublicKey;
5
+ shareMint: PublicKey;
6
+ collateralMint: PublicKey;
7
+ /** The owning program of each mint; they need not be the same one. */
8
+ shareProgram: PublicKey;
9
+ collateralProgram: PublicKey;
10
+ }
11
+ /**
12
+ * Builds the program's user instructions. Pure: it derives addresses and
13
+ * encodes data, and never touches the network or signs.
14
+ *
15
+ * Kept separate from SolanaUserCalls so a caller can batch several of these
16
+ * into one transaction, add their own instructions around them, or hand the
17
+ * result to a different sender — the same split the EVM side has.
18
+ */
19
+ export declare class SolanaTxBuilder {
20
+ /**
21
+ * Creates an associated token account only if it is missing.
22
+ *
23
+ * `tokenProgram` is a parameter rather than a constant: an ATA's address
24
+ * derives from it, so a Token-2022 mint resolves somewhere else entirely
25
+ * and passing the SPL Token default would create an account at an address
26
+ * the program will not accept.
27
+ */
28
+ static createAtaIdempotent(payer: PublicKey, owner: PublicKey, mint: PublicKey, tokenProgram?: PublicKey): TransactionInstruction;
29
+ /**
30
+ * `deposit_asset`: moves collateral in and mints shares at the live rate.
31
+ *
32
+ * `minShares` is the slippage floor and is enforced on chain. It matters
33
+ * only if the rate manager moves the rate between quoting and landing.
34
+ */
35
+ static depositAsset(ctx: ISolanaVaultContext, depositor: PublicKey, amount: bigint, minShares: bigint): TransactionInstruction;
36
+ /**
37
+ * `mint_shares`: the same movement expressed the other way round — an exact
38
+ * share count out, with `maxAmount` capping the collateral it may consume.
39
+ */
40
+ static mintShares(ctx: ISolanaVaultContext, depositor: PublicKey, shares: bigint, maxAmount: bigint): TransactionInstruction;
41
+ /**
42
+ * `redeem_shares`: burns nothing yet — it escrows the shares into the
43
+ * vault's pending account and appends a request to the withdrawal queue.
44
+ * The operator settles it later, so this returns no collateral.
45
+ *
46
+ * `requestIndex` identifies the request account and must be the owner's
47
+ * next index; SolanaUserCalls reads it so callers do not have to.
48
+ */
49
+ static redeemShares(ctx: ISolanaVaultContext, owner: PublicKey, shares: bigint, requestNonce: bigint, receiver: PublicKey): TransactionInstruction;
50
+ /**
51
+ * `cancel_pending_withdrawal_request`: withdraws the request from the
52
+ * queue, identified by its `sequenceNumber`.
53
+ *
54
+ * Only four accounts, none of them token accounts. The escrowed shares are
55
+ * not moved here — the request is marked cancelled on the owner's
56
+ * VaultAccount and the operator returns the shares when it next settles —
57
+ * so neither the share mint, the pending-shares escrow nor the request's
58
+ * own account appear. Matches CancelWithdrawalRequest in withdraw.rs: note
59
+ * that `vault` and `owner` are both read-only.
60
+ */
61
+ static cancelPendingWithdrawalRequest(ctx: ISolanaVaultContext, owner: PublicKey, sequenceNumber: bigint): TransactionInstruction;
62
+ }
@@ -0,0 +1,190 @@
1
+ import { SystemProgram, TransactionInstruction } from "@solana/web3.js";
2
+ import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, associatedTokenAddress, deriveProtocolConfig, deriveVaultAccount, deriveVaultPdas, deriveWithdrawalRequest, eventCpiAccounts, instructionDiscriminator, u128le, u64le } from "../utils/pdas.js";
3
+ /**
4
+ * Builds the program's user instructions. Pure: it derives addresses and
5
+ * encodes data, and never touches the network or signs.
6
+ *
7
+ * Kept separate from SolanaUserCalls so a caller can batch several of these
8
+ * into one transaction, add their own instructions around them, or hand the
9
+ * result to a different sender — the same split the EVM side has.
10
+ */
11
+ export class SolanaTxBuilder {
12
+ /**
13
+ * Creates an associated token account only if it is missing.
14
+ *
15
+ * `tokenProgram` is a parameter rather than a constant: an ATA's address
16
+ * derives from it, so a Token-2022 mint resolves somewhere else entirely
17
+ * and passing the SPL Token default would create an account at an address
18
+ * the program will not accept.
19
+ */
20
+ static createAtaIdempotent(payer, owner, mint, tokenProgram = TOKEN_PROGRAM_ID) {
21
+ return new TransactionInstruction({
22
+ programId: ASSOCIATED_TOKEN_PROGRAM_ID,
23
+ keys: [
24
+ { pubkey: payer, isSigner: true, isWritable: true },
25
+ {
26
+ pubkey: associatedTokenAddress(owner, mint, tokenProgram),
27
+ isSigner: false,
28
+ isWritable: true
29
+ },
30
+ { pubkey: owner, isSigner: false, isWritable: false },
31
+ { pubkey: mint, isSigner: false, isWritable: false },
32
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
33
+ { pubkey: tokenProgram, isSigner: false, isWritable: false }
34
+ ],
35
+ // 1 = CreateIdempotent. Unlike 0 (Create), this succeeds when the
36
+ // account already exists, so a repeat deposit needs no pre-check.
37
+ data: Buffer.from([1])
38
+ });
39
+ }
40
+ /**
41
+ * `deposit_asset`: moves collateral in and mints shares at the live rate.
42
+ *
43
+ * `minShares` is the slippage floor and is enforced on chain. It matters
44
+ * only if the rate manager moves the rate between quoting and landing.
45
+ */
46
+ static depositAsset(ctx, depositor, amount, minShares) {
47
+ const pdas = deriveVaultPdas(ctx.programId, ctx.shareMint);
48
+ return new TransactionInstruction({
49
+ programId: ctx.programId,
50
+ keys: [
51
+ {
52
+ pubkey: deriveProtocolConfig(ctx.programId),
53
+ isSigner: false,
54
+ isWritable: false
55
+ },
56
+ { pubkey: pdas.vault, isSigner: false, isWritable: true },
57
+ { pubkey: ctx.collateralMint, isSigner: false, isWritable: false },
58
+ { pubkey: ctx.shareMint, isSigner: false, isWritable: true },
59
+ { pubkey: pdas.vaultCollateral, isSigner: false, isWritable: true },
60
+ { pubkey: depositor, isSigner: true, isWritable: true },
61
+ {
62
+ pubkey: deriveVaultAccount(ctx.programId, pdas.vault, depositor),
63
+ isSigner: false,
64
+ isWritable: true
65
+ },
66
+ {
67
+ pubkey: associatedTokenAddress(depositor, ctx.collateralMint, ctx.collateralProgram),
68
+ isSigner: false,
69
+ isWritable: true
70
+ },
71
+ {
72
+ pubkey: associatedTokenAddress(depositor, ctx.shareMint, ctx.shareProgram),
73
+ isSigner: false,
74
+ isWritable: true
75
+ },
76
+ { pubkey: ctx.collateralProgram, isSigner: false, isWritable: false },
77
+ { pubkey: ctx.shareProgram, isSigner: false, isWritable: false },
78
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
79
+ ...eventCpiAccounts(ctx.programId)
80
+ ],
81
+ data: Buffer.concat([
82
+ instructionDiscriminator("deposit_asset"),
83
+ u64le(amount),
84
+ u64le(minShares)
85
+ ])
86
+ });
87
+ }
88
+ /**
89
+ * `mint_shares`: the same movement expressed the other way round — an exact
90
+ * share count out, with `maxAmount` capping the collateral it may consume.
91
+ */
92
+ static mintShares(ctx, depositor, shares, maxAmount) {
93
+ const deposit = SolanaTxBuilder.depositAsset(ctx, depositor, shares, maxAmount);
94
+ return new TransactionInstruction({
95
+ programId: deposit.programId,
96
+ keys: deposit.keys,
97
+ data: Buffer.concat([
98
+ instructionDiscriminator("mint_shares"),
99
+ u64le(shares),
100
+ u64le(maxAmount)
101
+ ])
102
+ });
103
+ }
104
+ /**
105
+ * `redeem_shares`: burns nothing yet — it escrows the shares into the
106
+ * vault's pending account and appends a request to the withdrawal queue.
107
+ * The operator settles it later, so this returns no collateral.
108
+ *
109
+ * `requestIndex` identifies the request account and must be the owner's
110
+ * next index; SolanaUserCalls reads it so callers do not have to.
111
+ */
112
+ static redeemShares(ctx, owner, shares, requestNonce, receiver) {
113
+ const pdas = deriveVaultPdas(ctx.programId, ctx.shareMint);
114
+ const vaultAccount = deriveVaultAccount(ctx.programId, pdas.vault, owner);
115
+ return new TransactionInstruction({
116
+ programId: ctx.programId,
117
+ keys: [
118
+ {
119
+ pubkey: deriveProtocolConfig(ctx.programId),
120
+ isSigner: false,
121
+ isWritable: false
122
+ },
123
+ { pubkey: pdas.vault, isSigner: false, isWritable: true },
124
+ { pubkey: ctx.shareMint, isSigner: false, isWritable: false },
125
+ { pubkey: pdas.vaultPendingShares, isSigner: false, isWritable: true },
126
+ { pubkey: pdas.withdrawalQueue, isSigner: false, isWritable: true },
127
+ { pubkey: vaultAccount, isSigner: false, isWritable: true },
128
+ { pubkey: owner, isSigner: true, isWritable: true },
129
+ {
130
+ pubkey: deriveWithdrawalRequest(ctx.programId, pdas.vault, owner, requestNonce),
131
+ isSigner: false,
132
+ isWritable: true
133
+ },
134
+ {
135
+ pubkey: associatedTokenAddress(owner, ctx.shareMint, ctx.shareProgram),
136
+ isSigner: false,
137
+ isWritable: true
138
+ },
139
+ { pubkey: ctx.shareProgram, isSigner: false, isWritable: false },
140
+ { pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
141
+ ...eventCpiAccounts(ctx.programId)
142
+ ],
143
+ // `receiver` is an argument, not an account: the collateral is paid
144
+ // out later by the operator, so there is nothing to write to now.
145
+ data: Buffer.concat([
146
+ instructionDiscriminator("redeem_shares"),
147
+ u64le(shares),
148
+ receiver.toBuffer()
149
+ ])
150
+ });
151
+ }
152
+ /**
153
+ * `cancel_pending_withdrawal_request`: withdraws the request from the
154
+ * queue, identified by its `sequenceNumber`.
155
+ *
156
+ * Only four accounts, none of them token accounts. The escrowed shares are
157
+ * not moved here — the request is marked cancelled on the owner's
158
+ * VaultAccount and the operator returns the shares when it next settles —
159
+ * so neither the share mint, the pending-shares escrow nor the request's
160
+ * own account appear. Matches CancelWithdrawalRequest in withdraw.rs: note
161
+ * that `vault` and `owner` are both read-only.
162
+ */
163
+ static cancelPendingWithdrawalRequest(ctx, owner, sequenceNumber) {
164
+ const pdas = deriveVaultPdas(ctx.programId, ctx.shareMint);
165
+ return new TransactionInstruction({
166
+ programId: ctx.programId,
167
+ keys: [
168
+ {
169
+ pubkey: deriveProtocolConfig(ctx.programId),
170
+ isSigner: false,
171
+ isWritable: false
172
+ },
173
+ { pubkey: pdas.vault, isSigner: false, isWritable: false },
174
+ {
175
+ pubkey: deriveVaultAccount(ctx.programId, pdas.vault, owner),
176
+ isSigner: false,
177
+ isWritable: true
178
+ },
179
+ { pubkey: owner, isSigner: true, isWritable: false },
180
+ ...eventCpiAccounts(ctx.programId)
181
+ ],
182
+ // u128, not u64: a queue sequence number, not the request nonce
183
+ // that seeds the request PDA.
184
+ data: Buffer.concat([
185
+ instructionDiscriminator("cancel_pending_withdrawal_request"),
186
+ u128le(sequenceNumber)
187
+ ])
188
+ });
189
+ }
190
+ }
@@ -0,0 +1,150 @@
1
+ import { type Connection, PublicKey, type TransactionInstruction } from "@solana/web3.js";
2
+ import { type ISolanaWithdrawalRequest } from "../interfaces/index.js";
3
+ import { type SolanaSigner } from "../signers/solana-signer.js";
4
+ import { SolanaDeploymentParser } from "../utils/deployment-parser.js";
5
+ /** What a write returns. */
6
+ export interface ISolanaTxResponse {
7
+ /** The transaction signature, base58. Also its explorer id. */
8
+ signature: string;
9
+ /** Compute units the simulation reported, when one ran. */
10
+ unitsConsumed?: number;
11
+ }
12
+ export interface ISolanaWriteOptions {
13
+ /**
14
+ * Simulate before sending. On by default: a failed simulation costs one
15
+ * round trip and reports the program's error, where a failed send costs a
16
+ * fee and reports a bare "custom program error".
17
+ */
18
+ simulate?: boolean;
19
+ /** Extra instructions to run before the vault call, e.g. a compute budget. */
20
+ preInstructions?: TransactionInstruction[];
21
+ /** Overrides the signer supplied to the constructor, for this call only. */
22
+ signer?: SolanaSigner;
23
+ }
24
+ /**
25
+ * Redemptions, and only redemptions, take a receiver.
26
+ *
27
+ * `deposit_asset` and `mint_shares` have no receiver argument and mint into the
28
+ * depositor's own associated token account, so a `receiver` on those options
29
+ * would be accepted and silently ignored. It is kept off their type rather than
30
+ * rejected at runtime, so the mistake does not compile.
31
+ */
32
+ export interface ISolanaRedeemOptions extends ISolanaWriteOptions {
33
+ /** Who receives the collateral at settlement. Defaults to the signer. */
34
+ receiver?: PublicKey | string;
35
+ }
36
+ /**
37
+ * Amounts accept the shapes the Sui and EVM surfaces accept, not just bigint.
38
+ *
39
+ * `EmberVaults.user` is a union of the three chains' call classes, so a shared
40
+ * call site resolves each parameter to the INTERSECTION of their types. Sui's
41
+ * `NumStr` is `number | string` and EVM's is `string | number | bigint`;
42
+ * intersected with a bigint-only Solana that is `never`, so before this type
43
+ * existed `ember.user.redeemShares(vault, x)` failed to compile for every x —
44
+ * `100`, `"100"` and `100n` alike.
45
+ *
46
+ * `100n` through `ember.user` is still rejected, because Sui's `NumStr` has no
47
+ * bigint. That is pre-existing and unrelated to Solana; a bigint works on a
48
+ * narrowed instance (`asSolana().user`, or a `SolanaUserCalls` directly).
49
+ */
50
+ export type SolanaAmount = bigint | number | string;
51
+ /**
52
+ * The user-facing writes: deposit, mint, redeem and cancel.
53
+ *
54
+ * Each method reads only what it needs to build a correct instruction — the
55
+ * mints' token programs and, for a redeem, the owner's request nonce — then
56
+ * signs and sends. The instruction building itself lives in SolanaTxBuilder,
57
+ * so a caller who wants to batch or route differently can use that directly.
58
+ */
59
+ export declare class SolanaUserCalls {
60
+ private readonly connection;
61
+ /**
62
+ * Optional so one instance can serve a page that has not connected a
63
+ * wallet yet; every write then takes its own signer.
64
+ */
65
+ private readonly signer?;
66
+ private parser;
67
+ private readonly reader;
68
+ constructor(connection: Connection, parser: SolanaDeploymentParser,
69
+ /**
70
+ * Optional so one instance can serve a page that has not connected a
71
+ * wallet yet; every write then takes its own signer.
72
+ */
73
+ signer?: SolanaSigner);
74
+ /** Swap in a newer deployment without rebuilding the caller. */
75
+ updateDeployment(parser: SolanaDeploymentParser): void;
76
+ private key;
77
+ private resolveSigner;
78
+ /**
79
+ * The mints and their owning token programs.
80
+ *
81
+ * The programs come from the deployment manifest when it records them and
82
+ * from the mint accounts otherwise. They are not assumed equal: a vault's
83
+ * collateral and share mints may sit under SPL Token and Token-2022
84
+ * respectively, and the ATA addresses differ between the two.
85
+ */
86
+ private vaultContext;
87
+ private tokenProgram;
88
+ /**
89
+ * Signs, sends and confirms. Simulation runs first unless turned off, and
90
+ * its error is surfaced in place of the send's: the program's own message
91
+ * is in the simulation logs, while a failed send gives only a code.
92
+ */
93
+ private send;
94
+ /**
95
+ * Deposits collateral and mints shares at the vault's live rate.
96
+ *
97
+ * `minShares` is the on-chain slippage floor; it bites only if the rate
98
+ * manager moves the rate between quoting and landing. Quote it with
99
+ * `SolanaVaultReader.previewDeposit`.
100
+ *
101
+ * The depositor's share account is created here when missing, so a first
102
+ * deposit is still a single transaction.
103
+ */
104
+ depositAsset(shareMint: PublicKey | string, amount: SolanaAmount, minShares: SolanaAmount, options?: ISolanaWriteOptions): Promise<ISolanaTxResponse>;
105
+ /**
106
+ * Mints an exact share count, spending at most `maxAmount` of collateral.
107
+ * The mirror of depositAsset, for a caller who wants a round share figure.
108
+ */
109
+ mintShares(shareMint: PublicKey | string, shares: SolanaAmount, maxAmount: SolanaAmount, options?: ISolanaWriteOptions): Promise<ISolanaTxResponse>;
110
+ /**
111
+ * The idempotent ATA instruction, but only when the account is actually
112
+ * missing: including it unconditionally costs a few hundred compute units
113
+ * on every repeat deposit for nothing.
114
+ */
115
+ private ensureShareAccount;
116
+ /**
117
+ * Queues a withdrawal. This does NOT return collateral: the shares are
118
+ * escrowed and a request is appended to the vault's queue, which the
119
+ * operator settles later. Watch it with `getPendingWithdrawals`.
120
+ *
121
+ * The request's account is seeded by the owner's next nonce, which is read
122
+ * here — a caller passing a stale one would collide with their own earlier
123
+ * request.
124
+ */
125
+ redeemShares(shareMint: PublicKey | string, shares: SolanaAmount, options?: ISolanaRedeemOptions): Promise<ISolanaTxResponse>;
126
+ /**
127
+ * Cancels a queued withdrawal by its queue `sequenceNumber` — not the
128
+ * request nonce that seeds the request account. Take it from
129
+ * `getPendingWithdrawals`.
130
+ */
131
+ cancelPendingWithdrawalRequest(shareMint: PublicKey | string, sequenceNumber: SolanaAmount, options?: ISolanaWriteOptions): Promise<ISolanaTxResponse>;
132
+ /**
133
+ * An owner's live queued withdrawals, each carrying the `sequenceNumber`
134
+ * that `cancelPendingWithdrawalRequest` takes.
135
+ *
136
+ * CANCELLED REQUESTS ARE EXCLUDED, and that filter is not cosmetic.
137
+ * `cancel_pending_withdrawal_request` does not remove the entry: it pushes
138
+ * the sequence number onto the account's `cancel_withdraw_request` list and
139
+ * leaves the request in place, for the operator to skip when it settles
140
+ * (see withdraw.rs). Returning the raw list would therefore show a
141
+ * withdrawal the user already cancelled as still pending, indefinitely.
142
+ */
143
+ getPendingWithdrawals(shareMint: PublicKey | string, owner?: PublicKey | string): Promise<ISolanaWithdrawalRequest[]>;
144
+ /**
145
+ * The requests this owner has cancelled that the operator has not yet
146
+ * settled. They still occupy a slot on the account until it does.
147
+ */
148
+ getCancelledWithdrawals(shareMint: PublicKey | string, owner?: PublicKey | string): Promise<ISolanaWithdrawalRequest[]>;
149
+ private accountState;
150
+ }