@vultisig/core-chain 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/CHANGELOG.md +18 -0
- package/dist/chains/cosmos/gas.d.ts +9 -0
- package/dist/chains/cosmos/gas.d.ts.map +1 -1
- package/dist/chains/cosmos/gas.js +9 -0
- package/dist/chains/cosmos/gas.js.map +1 -1
- package/dist/chains/cosmos/terraClassicTax.d.ts +15 -0
- package/dist/chains/cosmos/terraClassicTax.d.ts.map +1 -1
- package/dist/chains/cosmos/terraClassicTax.js +14 -0
- package/dist/chains/cosmos/terraClassicTax.js.map +1 -1
- package/dist/chains/solana/kamino/KaminoServiceError.d.ts +63 -0
- package/dist/chains/solana/kamino/KaminoServiceError.d.ts.map +1 -0
- package/dist/chains/solana/kamino/KaminoServiceError.js +29 -0
- package/dist/chains/solana/kamino/KaminoServiceError.js.map +1 -0
- package/dist/chains/solana/kamino/amount.d.ts +141 -0
- package/dist/chains/solana/kamino/amount.d.ts.map +1 -0
- package/dist/chains/solana/kamino/amount.js +156 -0
- package/dist/chains/solana/kamino/amount.js.map +1 -0
- package/dist/chains/solana/kamino/api.d.ts +20 -0
- package/dist/chains/solana/kamino/api.d.ts.map +1 -0
- package/dist/chains/solana/kamino/api.js +59 -0
- package/dist/chains/solana/kamino/api.js.map +1 -0
- package/dist/chains/solana/kamino/baseUnits.d.ts +23 -0
- package/dist/chains/solana/kamino/baseUnits.d.ts.map +1 -0
- package/dist/chains/solana/kamino/baseUnits.js +30 -0
- package/dist/chains/solana/kamino/baseUnits.js.map +1 -0
- package/dist/chains/solana/kamino/config.d.ts +27 -0
- package/dist/chains/solana/kamino/config.d.ts.map +1 -0
- package/dist/chains/solana/kamino/config.js +27 -0
- package/dist/chains/solana/kamino/config.js.map +1 -0
- package/dist/chains/solana/kamino/decimal.d.ts +24 -0
- package/dist/chains/solana/kamino/decimal.d.ts.map +1 -0
- package/dist/chains/solana/kamino/decimal.js +52 -0
- package/dist/chains/solana/kamino/decimal.js.map +1 -0
- package/dist/chains/solana/kamino/models.d.ts +148 -0
- package/dist/chains/solana/kamino/models.d.ts.map +1 -0
- package/dist/chains/solana/kamino/models.js +2 -0
- package/dist/chains/solana/kamino/models.js.map +1 -0
- package/dist/chains/solana/kamino/position.d.ts +79 -0
- package/dist/chains/solana/kamino/position.d.ts.map +1 -0
- package/dist/chains/solana/kamino/position.js +52 -0
- package/dist/chains/solana/kamino/position.js.map +1 -0
- package/dist/chains/solana/kamino/rate.d.ts +57 -0
- package/dist/chains/solana/kamino/rate.d.ts.map +1 -0
- package/dist/chains/solana/kamino/rate.js +88 -0
- package/dist/chains/solana/kamino/rate.js.map +1 -0
- package/dist/chains/solana/kamino/registry.d.ts +70 -0
- package/dist/chains/solana/kamino/registry.d.ts.map +1 -0
- package/dist/chains/solana/kamino/registry.js +67 -0
- package/dist/chains/solana/kamino/registry.js.map +1 -0
- package/dist/chains/solana/kamino/tx/actions.d.ts +22 -0
- package/dist/chains/solana/kamino/tx/actions.d.ts.map +1 -0
- package/dist/chains/solana/kamino/tx/actions.js +85 -0
- package/dist/chains/solana/kamino/tx/actions.js.map +1 -0
- package/dist/chains/solana/kamino/tx/computeBudget.d.ts +66 -0
- package/dist/chains/solana/kamino/tx/computeBudget.d.ts.map +1 -0
- package/dist/chains/solana/kamino/tx/computeBudget.js +73 -0
- package/dist/chains/solana/kamino/tx/computeBudget.js.map +1 -0
- package/dist/chains/solana/kamino/tx/decode.d.ts +121 -0
- package/dist/chains/solana/kamino/tx/decode.d.ts.map +1 -0
- package/dist/chains/solana/kamino/tx/decode.js +458 -0
- package/dist/chains/solana/kamino/tx/decode.js.map +1 -0
- package/dist/chains/solana/kamino/tx/instructions.d.ts +260 -0
- package/dist/chains/solana/kamino/tx/instructions.d.ts.map +1 -0
- package/dist/chains/solana/kamino/tx/instructions.js +256 -0
- package/dist/chains/solana/kamino/tx/instructions.js.map +1 -0
- package/dist/chains/solana/kamino/tx/sequence.d.ts +123 -0
- package/dist/chains/solana/kamino/tx/sequence.d.ts.map +1 -0
- package/dist/chains/solana/kamino/tx/sequence.js +235 -0
- package/dist/chains/solana/kamino/tx/sequence.js.map +1 -0
- package/dist/chains/solana/kamino/tx/validate.d.ts +138 -0
- package/dist/chains/solana/kamino/tx/validate.d.ts.map +1 -0
- package/dist/chains/solana/kamino/tx/validate.js +542 -0
- package/dist/chains/solana/kamino/tx/validate.js.map +1 -0
- package/dist/chains/solana/kamino/tx/wire.d.ts +107 -0
- package/dist/chains/solana/kamino/tx/wire.d.ts.map +1 -0
- package/dist/chains/solana/kamino/tx/wire.js +274 -0
- package/dist/chains/solana/kamino/tx/wire.js.map +1 -0
- package/dist/chains/solana/kamino/vaultInfo.d.ts +17 -0
- package/dist/chains/solana/kamino/vaultInfo.d.ts.map +1 -0
- package/dist/chains/solana/kamino/vaultInfo.js +150 -0
- package/dist/chains/solana/kamino/vaultInfo.js.map +1 -0
- package/dist/swap/utils/getSwapArrivalStatus/index.d.ts +79 -0
- package/dist/swap/utils/getSwapArrivalStatus/index.d.ts.map +1 -0
- package/dist/swap/utils/getSwapArrivalStatus/index.js +374 -0
- package/dist/swap/utils/getSwapArrivalStatus/index.js.map +1 -0
- package/package.json +96 -1
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { VersionedTransaction } from '@solana/web3.js';
|
|
2
|
+
/**
|
|
3
|
+
* Wire-level operations over the v0 transaction Kamino's builder returns:
|
|
4
|
+
* parsing, the two injections (compute budget in front, attribution memo
|
|
5
|
+
* behind), and the blockhash replacement that keeps a built transaction
|
|
6
|
+
* signable after a slow ceremony.
|
|
7
|
+
*
|
|
8
|
+
* Injection appends the program as a new static, read-only, unsigned account
|
|
9
|
+
* key. Static keys are ordered by privilege — writable signers, read-only
|
|
10
|
+
* signers, writable non-signers, read-only non-signers — so the read-only
|
|
11
|
+
* unsigned block is the tail, and appending there plus incrementing
|
|
12
|
+
* `numReadonlyUnsignedAccounts` preserves the ordering without moving any
|
|
13
|
+
* existing key.
|
|
14
|
+
*
|
|
15
|
+
* The one consequence is index drift: indexes at or above the old static
|
|
16
|
+
* count addressed lookup-table-loaded accounts, which now begin one slot
|
|
17
|
+
* later, so each is shifted by one. `programIdIndex` values are untouched — a
|
|
18
|
+
* versioned message may not invoke a program loaded from a lookup table, so
|
|
19
|
+
* every program is already a static key below the insertion point. The
|
|
20
|
+
* indexes *inside* each address-table lookup are positions in the table
|
|
21
|
+
* itself, not in this message, so they are untouched too.
|
|
22
|
+
*/
|
|
23
|
+
/** A failure to parse or edit the wire bytes. */
|
|
24
|
+
export declare class KaminoWireError extends Error {
|
|
25
|
+
constructor(detail: string);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Parses the base64 transaction the build endpoints return. `undefined` for
|
|
29
|
+
* bytes that are not a v0 versioned transaction — a legacy message invoking
|
|
30
|
+
* the kVaults program must be surfaced as unreadable, never edited or
|
|
31
|
+
* described.
|
|
32
|
+
*/
|
|
33
|
+
export declare const parseKaminoWireTransaction: (base64Transaction: string) => VersionedTransaction | undefined;
|
|
34
|
+
/** Serializes back to the base64 form the keysign payload carries. */
|
|
35
|
+
export declare const serializeKaminoWireTransaction: (transaction: VersionedTransaction) => string;
|
|
36
|
+
/**
|
|
37
|
+
* Whether this is the shape the app is willing to sign: one required
|
|
38
|
+
* signature, no read-only signers, every signature slot still an empty
|
|
39
|
+
* placeholder, and — when `feePayer` is given — that account in slot 0.
|
|
40
|
+
*/
|
|
41
|
+
export declare const isUnsignedSingleSigner: (transaction: VersionedTransaction, feePayer?: string) => boolean;
|
|
42
|
+
/** The number of accounts the message addresses, static and lookup-loaded. */
|
|
43
|
+
export declare const totalKaminoAccountCount: (transaction: VersionedTransaction) => number;
|
|
44
|
+
/**
|
|
45
|
+
* Every account the message addresses, in runtime index order: static keys,
|
|
46
|
+
* then every table's writable loads, then every table's read-only loads.
|
|
47
|
+
* `undefined` when a table is missing from `lookupTables` or an index falls
|
|
48
|
+
* outside it — resolution either accounts for every index or fails whole.
|
|
49
|
+
*/
|
|
50
|
+
export declare const resolveKaminoAccountAddresses: ({ transaction, lookupTables, }: {
|
|
51
|
+
transaction: VersionedTransaction;
|
|
52
|
+
/** Table address to that table's full address list. */
|
|
53
|
+
lookupTables: Record<string, string[]>;
|
|
54
|
+
}) => string[] | undefined;
|
|
55
|
+
/**
|
|
56
|
+
* Whether the account at a runtime index may be written to, from the header
|
|
57
|
+
* privileges (static region) or the lookup section it was loaded through.
|
|
58
|
+
*/
|
|
59
|
+
export declare const isKaminoAccountWritable: ({ transaction, index, }: {
|
|
60
|
+
transaction: VersionedTransaction;
|
|
61
|
+
index: number;
|
|
62
|
+
}) => boolean;
|
|
63
|
+
/**
|
|
64
|
+
* The contents of every lookup table the transaction reads from, via RPC.
|
|
65
|
+
*
|
|
66
|
+
* Every failure surfaces as `KaminoWireError` — a missing table, a stalled
|
|
67
|
+
* node past the deadline, or a transport error — so callers see one rejection
|
|
68
|
+
* type from resolution rather than raw RPC errors.
|
|
69
|
+
*/
|
|
70
|
+
export declare const resolveKaminoLookupTables: (transaction: VersionedTransaction) => Promise<Record<string, string[]>>;
|
|
71
|
+
/**
|
|
72
|
+
* Injects the app's ComputeBudget pair in front of the existing instructions.
|
|
73
|
+
*
|
|
74
|
+
* Kamino's builder emits no ComputeBudget instruction, so a transaction that
|
|
75
|
+
* already carries the program — as a key or as an instruction — is not one
|
|
76
|
+
* this app built the fee for, and appending a second pair would double-charge
|
|
77
|
+
* rather than repair it.
|
|
78
|
+
*/
|
|
79
|
+
export declare const injectKaminoComputeBudget: ({ transaction, unitLimit, unitPriceMicroLamports, }: {
|
|
80
|
+
transaction: VersionedTransaction;
|
|
81
|
+
unitLimit: number;
|
|
82
|
+
unitPriceMicroLamports: bigint;
|
|
83
|
+
}) => VersionedTransaction;
|
|
84
|
+
/**
|
|
85
|
+
* Appends the attribution memo behind every existing instruction: one SPL
|
|
86
|
+
* Memo instruction carrying exactly the tag, no accounts.
|
|
87
|
+
*
|
|
88
|
+
* The key and the instruction are two ways for a memo to already be here, and
|
|
89
|
+
* appending a second one would both duplicate the account and leave the
|
|
90
|
+
* transaction carrying two attributions — so either form is a refusal.
|
|
91
|
+
*/
|
|
92
|
+
export declare const injectKaminoAttributionMemo: (transaction: VersionedTransaction) => VersionedTransaction;
|
|
93
|
+
/**
|
|
94
|
+
* The same transaction over a new recent blockhash.
|
|
95
|
+
*
|
|
96
|
+
* The build endpoints embed a blockhash worth ~60–90 seconds, and an MPC
|
|
97
|
+
* ceremony can outlive it — so the blockhash is replaced immediately before
|
|
98
|
+
* keysign rather than trusted to survive the wait. Nothing else in the
|
|
99
|
+
* message changes.
|
|
100
|
+
*/
|
|
101
|
+
export declare const withKaminoRecentBlockhash: ({ transaction, recentBlockhash, }: {
|
|
102
|
+
transaction: VersionedTransaction;
|
|
103
|
+
recentBlockhash: string;
|
|
104
|
+
}) => VersionedTransaction;
|
|
105
|
+
/** Fetches the current blockhash and stamps it onto the transaction. */
|
|
106
|
+
export declare const refreshKaminoRecentBlockhash: (transaction: VersionedTransaction) => Promise<VersionedTransaction>;
|
|
107
|
+
//# sourceMappingURL=wire.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wire.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/chain/chains/solana/kamino/tx/wire.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAK5E;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,iDAAiD;AACjD,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,MAAM,EAAE,MAAM;CAI3B;AAED;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,GAAI,mBAAmB,MAAM,KAAG,oBAAoB,GAAG,SAO7F,CAAA;AAED,sEAAsE;AACtE,eAAO,MAAM,8BAA8B,GAAI,aAAa,oBAAoB,KAAG,MAC1B,CAAA;AAEzD;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GAAI,aAAa,oBAAoB,EAAE,WAAW,MAAM,KAAG,OAO7F,CAAA;AAED,8EAA8E;AAC9E,eAAO,MAAM,uBAAuB,GAAI,aAAa,oBAAoB,KAAG,MAKzE,CAAA;AAEH;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,GAAI,gCAG3C;IACD,WAAW,EAAE,oBAAoB,CAAA;IACjC,uDAAuD;IACvD,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;CACvC,KAAG,MAAM,EAAE,GAAG,SAuBd,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,uBAAuB,GAAI,yBAGrC;IACD,WAAW,EAAE,oBAAoB,CAAA;IACjC,KAAK,EAAE,MAAM,CAAA;CACd,KAAG,OAaH,CAAA;AAUD;;;;;;GAMG;AACH,eAAO,MAAM,yBAAyB,GACpC,aAAa,oBAAoB,KAChC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CA6BlC,CAAA;AAkED;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,GAAI,qDAIvC;IACD,WAAW,EAAE,oBAAoB,CAAA;IACjC,SAAS,EAAE,MAAM,CAAA;IACjB,sBAAsB,EAAE,MAAM,CAAA;CAC/B,KAAG,oBAsBH,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,GAAI,aAAa,oBAAoB,KAAG,oBAiB/E,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,GAAI,mCAGvC;IACD,WAAW,EAAE,oBAAoB,CAAA;IACjC,eAAe,EAAE,MAAM,CAAA;CACxB,KAAG,oBAWH,CAAA;AAED,wEAAwE;AACxE,eAAO,MAAM,4BAA4B,GACvC,aAAa,oBAAoB,KAChC,OAAO,CAAC,oBAAoB,CAG9B,CAAA"}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { MessageV0, PublicKey, VersionedTransaction } from '@solana/web3.js';
|
|
2
|
+
import { getSolanaClient } from '../../client.js';
|
|
3
|
+
import { computeBudgetDiscriminators, kaminoAllowedPrograms, kaminoAttributionMemoTagBytes } from './instructions.js';
|
|
4
|
+
/**
|
|
5
|
+
* Wire-level operations over the v0 transaction Kamino's builder returns:
|
|
6
|
+
* parsing, the two injections (compute budget in front, attribution memo
|
|
7
|
+
* behind), and the blockhash replacement that keeps a built transaction
|
|
8
|
+
* signable after a slow ceremony.
|
|
9
|
+
*
|
|
10
|
+
* Injection appends the program as a new static, read-only, unsigned account
|
|
11
|
+
* key. Static keys are ordered by privilege — writable signers, read-only
|
|
12
|
+
* signers, writable non-signers, read-only non-signers — so the read-only
|
|
13
|
+
* unsigned block is the tail, and appending there plus incrementing
|
|
14
|
+
* `numReadonlyUnsignedAccounts` preserves the ordering without moving any
|
|
15
|
+
* existing key.
|
|
16
|
+
*
|
|
17
|
+
* The one consequence is index drift: indexes at or above the old static
|
|
18
|
+
* count addressed lookup-table-loaded accounts, which now begin one slot
|
|
19
|
+
* later, so each is shifted by one. `programIdIndex` values are untouched — a
|
|
20
|
+
* versioned message may not invoke a program loaded from a lookup table, so
|
|
21
|
+
* every program is already a static key below the insertion point. The
|
|
22
|
+
* indexes *inside* each address-table lookup are positions in the table
|
|
23
|
+
* itself, not in this message, so they are untouched too.
|
|
24
|
+
*/
|
|
25
|
+
/** A failure to parse or edit the wire bytes. */
|
|
26
|
+
export class KaminoWireError extends Error {
|
|
27
|
+
constructor(detail) {
|
|
28
|
+
super(`Kamino wire transaction: ${detail}`);
|
|
29
|
+
this.name = 'KaminoWireError';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Parses the base64 transaction the build endpoints return. `undefined` for
|
|
34
|
+
* bytes that are not a v0 versioned transaction — a legacy message invoking
|
|
35
|
+
* the kVaults program must be surfaced as unreadable, never edited or
|
|
36
|
+
* described.
|
|
37
|
+
*/
|
|
38
|
+
export const parseKaminoWireTransaction = (base64Transaction) => {
|
|
39
|
+
try {
|
|
40
|
+
const transaction = VersionedTransaction.deserialize(Buffer.from(base64Transaction, 'base64'));
|
|
41
|
+
return transaction.version === 0 ? transaction : undefined;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
/** Serializes back to the base64 form the keysign payload carries. */
|
|
48
|
+
export const serializeKaminoWireTransaction = (transaction) => Buffer.from(transaction.serialize()).toString('base64');
|
|
49
|
+
/**
|
|
50
|
+
* Whether this is the shape the app is willing to sign: one required
|
|
51
|
+
* signature, no read-only signers, every signature slot still an empty
|
|
52
|
+
* placeholder, and — when `feePayer` is given — that account in slot 0.
|
|
53
|
+
*/
|
|
54
|
+
export const isUnsignedSingleSigner = (transaction, feePayer) => {
|
|
55
|
+
const { header, staticAccountKeys } = transaction.message;
|
|
56
|
+
if (header.numRequiredSignatures !== 1 || header.numReadonlySignedAccounts !== 0)
|
|
57
|
+
return false;
|
|
58
|
+
if (transaction.signatures.length !== 1)
|
|
59
|
+
return false;
|
|
60
|
+
if (transaction.signatures.some(signature => signature.some(byte => byte !== 0)))
|
|
61
|
+
return false;
|
|
62
|
+
if (feePayer !== undefined && staticAccountKeys[0]?.toBase58() !== feePayer)
|
|
63
|
+
return false;
|
|
64
|
+
return true;
|
|
65
|
+
};
|
|
66
|
+
/** The number of accounts the message addresses, static and lookup-loaded. */
|
|
67
|
+
export const totalKaminoAccountCount = (transaction) => transaction.message.staticAccountKeys.length +
|
|
68
|
+
transaction.message.addressTableLookups.reduce((count, lookup) => count + lookup.writableIndexes.length + lookup.readonlyIndexes.length, 0);
|
|
69
|
+
/**
|
|
70
|
+
* Every account the message addresses, in runtime index order: static keys,
|
|
71
|
+
* then every table's writable loads, then every table's read-only loads.
|
|
72
|
+
* `undefined` when a table is missing from `lookupTables` or an index falls
|
|
73
|
+
* outside it — resolution either accounts for every index or fails whole.
|
|
74
|
+
*/
|
|
75
|
+
export const resolveKaminoAccountAddresses = ({ transaction, lookupTables, }) => {
|
|
76
|
+
const resolved = transaction.message.staticAccountKeys.map(key => key.toBase58());
|
|
77
|
+
const load = (tableAddress, indexes) => {
|
|
78
|
+
const table = lookupTables[tableAddress];
|
|
79
|
+
if (!table)
|
|
80
|
+
return undefined;
|
|
81
|
+
const addresses = [];
|
|
82
|
+
for (const index of indexes) {
|
|
83
|
+
const address = table[index];
|
|
84
|
+
if (address === undefined)
|
|
85
|
+
return undefined;
|
|
86
|
+
addresses.push(address);
|
|
87
|
+
}
|
|
88
|
+
return addresses;
|
|
89
|
+
};
|
|
90
|
+
for (const kind of ['writableIndexes', 'readonlyIndexes']) {
|
|
91
|
+
for (const lookup of transaction.message.addressTableLookups) {
|
|
92
|
+
const addresses = load(lookup.accountKey.toBase58(), lookup[kind]);
|
|
93
|
+
if (!addresses)
|
|
94
|
+
return undefined;
|
|
95
|
+
resolved.push(...addresses);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return resolved;
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Whether the account at a runtime index may be written to, from the header
|
|
102
|
+
* privileges (static region) or the lookup section it was loaded through.
|
|
103
|
+
*/
|
|
104
|
+
export const isKaminoAccountWritable = ({ transaction, index, }) => {
|
|
105
|
+
const { header, staticAccountKeys, addressTableLookups } = transaction.message;
|
|
106
|
+
const staticCount = staticAccountKeys.length;
|
|
107
|
+
if (index < staticCount) {
|
|
108
|
+
const writableSigners = header.numRequiredSignatures - header.numReadonlySignedAccounts;
|
|
109
|
+
if (index < writableSigners)
|
|
110
|
+
return true;
|
|
111
|
+
if (index < header.numRequiredSignatures)
|
|
112
|
+
return false;
|
|
113
|
+
return index < staticCount - header.numReadonlyUnsignedAccounts;
|
|
114
|
+
}
|
|
115
|
+
const writableLoads = addressTableLookups.reduce((count, lookup) => count + lookup.writableIndexes.length, 0);
|
|
116
|
+
return index < staticCount + writableLoads;
|
|
117
|
+
};
|
|
118
|
+
/**
|
|
119
|
+
* Deadline for one lookup-table read. This sits on the signing critical path
|
|
120
|
+
* and the RPC client itself carries no timeout, so an unbounded call would
|
|
121
|
+
* let a stalled node wedge the prepare indefinitely. Matches the HTTP layer's
|
|
122
|
+
* default deadline — comfortably above a healthy read, still bounding a hang.
|
|
123
|
+
*/
|
|
124
|
+
const lookupTableReadTimeoutMs = 20_000;
|
|
125
|
+
/**
|
|
126
|
+
* The contents of every lookup table the transaction reads from, via RPC.
|
|
127
|
+
*
|
|
128
|
+
* Every failure surfaces as `KaminoWireError` — a missing table, a stalled
|
|
129
|
+
* node past the deadline, or a transport error — so callers see one rejection
|
|
130
|
+
* type from resolution rather than raw RPC errors.
|
|
131
|
+
*/
|
|
132
|
+
export const resolveKaminoLookupTables = async (transaction) => {
|
|
133
|
+
const client = getSolanaClient();
|
|
134
|
+
const tables = {};
|
|
135
|
+
for (const lookup of transaction.message.addressTableLookups) {
|
|
136
|
+
const address = lookup.accountKey.toBase58();
|
|
137
|
+
if (tables[address])
|
|
138
|
+
continue;
|
|
139
|
+
let timeoutId;
|
|
140
|
+
const deadline = new Promise((_, reject) => {
|
|
141
|
+
timeoutId = setTimeout(() => reject(new KaminoWireError(`address lookup table ${address} read timed out after ${lookupTableReadTimeoutMs}ms`)), lookupTableReadTimeoutMs);
|
|
142
|
+
});
|
|
143
|
+
try {
|
|
144
|
+
const table = await Promise.race([client.getAddressLookupTable(lookup.accountKey), deadline]);
|
|
145
|
+
if (!table.value)
|
|
146
|
+
throw new KaminoWireError(`address lookup table ${address} not found`);
|
|
147
|
+
tables[address] = table.value.state.addresses.map(key => key.toBase58());
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
if (error instanceof KaminoWireError)
|
|
151
|
+
throw error;
|
|
152
|
+
throw new KaminoWireError(`address lookup table ${address} could not be read: ${extractErrorMessage(error)}`);
|
|
153
|
+
}
|
|
154
|
+
finally {
|
|
155
|
+
clearTimeout(timeoutId);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return tables;
|
|
159
|
+
};
|
|
160
|
+
const extractErrorMessage = (error) => (error instanceof Error ? error.message : String(error));
|
|
161
|
+
/**
|
|
162
|
+
* Appends `programKey` as a new static, read-only, unsigned account key and
|
|
163
|
+
* rebuilds the message with `leading` instructions in front of the existing
|
|
164
|
+
* ones and `trailing` behind them. Both callbacks receive the index the new
|
|
165
|
+
* key lands at. See the module doc for why lookup-region indexes shift.
|
|
166
|
+
*/
|
|
167
|
+
const injectProgram = (transaction, programKey, leading, trailing) => {
|
|
168
|
+
const message = transaction.message;
|
|
169
|
+
const staticCount = message.staticAccountKeys.length;
|
|
170
|
+
const shift = (index) => (index >= staticCount ? index + 1 : index);
|
|
171
|
+
const shifted = message.compiledInstructions.map(instruction => ({
|
|
172
|
+
programIdIndex: instruction.programIdIndex,
|
|
173
|
+
accountKeyIndexes: instruction.accountKeyIndexes.map(shift),
|
|
174
|
+
data: instruction.data,
|
|
175
|
+
}));
|
|
176
|
+
const injected = new MessageV0({
|
|
177
|
+
header: {
|
|
178
|
+
numRequiredSignatures: message.header.numRequiredSignatures,
|
|
179
|
+
numReadonlySignedAccounts: message.header.numReadonlySignedAccounts,
|
|
180
|
+
numReadonlyUnsignedAccounts: message.header.numReadonlyUnsignedAccounts + 1,
|
|
181
|
+
},
|
|
182
|
+
staticAccountKeys: [...message.staticAccountKeys, new PublicKey(programKey)],
|
|
183
|
+
recentBlockhash: message.recentBlockhash,
|
|
184
|
+
compiledInstructions: [...leading(staticCount), ...shifted, ...trailing(staticCount)],
|
|
185
|
+
addressTableLookups: message.addressTableLookups,
|
|
186
|
+
});
|
|
187
|
+
return new VersionedTransaction(injected);
|
|
188
|
+
};
|
|
189
|
+
const invokesProgram = (transaction, programKey) => {
|
|
190
|
+
const keys = transaction.message.staticAccountKeys.map(key => key.toBase58());
|
|
191
|
+
return transaction.message.compiledInstructions.some(instruction => keys[instruction.programIdIndex] === programKey);
|
|
192
|
+
};
|
|
193
|
+
const hasStaticKey = (transaction, key) => transaction.message.staticAccountKeys.some(existing => existing.toBase58() === key);
|
|
194
|
+
const u32LittleEndian = (value) => {
|
|
195
|
+
const bytes = new Uint8Array(4);
|
|
196
|
+
new DataView(bytes.buffer).setUint32(0, value, true);
|
|
197
|
+
return bytes;
|
|
198
|
+
};
|
|
199
|
+
const u64LittleEndian = (value) => {
|
|
200
|
+
const bytes = new Uint8Array(8);
|
|
201
|
+
new DataView(bytes.buffer).setBigUint64(0, value, true);
|
|
202
|
+
return bytes;
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* Injects the app's ComputeBudget pair in front of the existing instructions.
|
|
206
|
+
*
|
|
207
|
+
* Kamino's builder emits no ComputeBudget instruction, so a transaction that
|
|
208
|
+
* already carries the program — as a key or as an instruction — is not one
|
|
209
|
+
* this app built the fee for, and appending a second pair would double-charge
|
|
210
|
+
* rather than repair it.
|
|
211
|
+
*/
|
|
212
|
+
export const injectKaminoComputeBudget = ({ transaction, unitLimit, unitPriceMicroLamports, }) => {
|
|
213
|
+
const program = kaminoAllowedPrograms.computeBudget;
|
|
214
|
+
if (hasStaticKey(transaction, program) || invokesProgram(transaction, program)) {
|
|
215
|
+
throw new KaminoWireError('compute budget already present');
|
|
216
|
+
}
|
|
217
|
+
return injectProgram(transaction, program, programIdIndex => [
|
|
218
|
+
{
|
|
219
|
+
programIdIndex,
|
|
220
|
+
accountKeyIndexes: [],
|
|
221
|
+
data: Uint8Array.from([computeBudgetDiscriminators.setUnitLimit, ...u32LittleEndian(unitLimit)]),
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
programIdIndex,
|
|
225
|
+
accountKeyIndexes: [],
|
|
226
|
+
data: Uint8Array.from([computeBudgetDiscriminators.setUnitPrice, ...u64LittleEndian(unitPriceMicroLamports)]),
|
|
227
|
+
},
|
|
228
|
+
], () => []);
|
|
229
|
+
};
|
|
230
|
+
/**
|
|
231
|
+
* Appends the attribution memo behind every existing instruction: one SPL
|
|
232
|
+
* Memo instruction carrying exactly the tag, no accounts.
|
|
233
|
+
*
|
|
234
|
+
* The key and the instruction are two ways for a memo to already be here, and
|
|
235
|
+
* appending a second one would both duplicate the account and leave the
|
|
236
|
+
* transaction carrying two attributions — so either form is a refusal.
|
|
237
|
+
*/
|
|
238
|
+
export const injectKaminoAttributionMemo = (transaction) => {
|
|
239
|
+
const program = kaminoAllowedPrograms.memo;
|
|
240
|
+
if (hasStaticKey(transaction, program) || invokesProgram(transaction, program)) {
|
|
241
|
+
throw new KaminoWireError('attribution memo already present');
|
|
242
|
+
}
|
|
243
|
+
return injectProgram(transaction, program, () => [], programIdIndex => [
|
|
244
|
+
{
|
|
245
|
+
programIdIndex,
|
|
246
|
+
accountKeyIndexes: [],
|
|
247
|
+
data: kaminoAttributionMemoTagBytes,
|
|
248
|
+
},
|
|
249
|
+
]);
|
|
250
|
+
};
|
|
251
|
+
/**
|
|
252
|
+
* The same transaction over a new recent blockhash.
|
|
253
|
+
*
|
|
254
|
+
* The build endpoints embed a blockhash worth ~60–90 seconds, and an MPC
|
|
255
|
+
* ceremony can outlive it — so the blockhash is replaced immediately before
|
|
256
|
+
* keysign rather than trusted to survive the wait. Nothing else in the
|
|
257
|
+
* message changes.
|
|
258
|
+
*/
|
|
259
|
+
export const withKaminoRecentBlockhash = ({ transaction, recentBlockhash, }) => {
|
|
260
|
+
const message = transaction.message;
|
|
261
|
+
return new VersionedTransaction(new MessageV0({
|
|
262
|
+
header: message.header,
|
|
263
|
+
staticAccountKeys: message.staticAccountKeys,
|
|
264
|
+
recentBlockhash,
|
|
265
|
+
compiledInstructions: message.compiledInstructions,
|
|
266
|
+
addressTableLookups: message.addressTableLookups,
|
|
267
|
+
}));
|
|
268
|
+
};
|
|
269
|
+
/** Fetches the current blockhash and stamps it onto the transaction. */
|
|
270
|
+
export const refreshKaminoRecentBlockhash = async (transaction) => {
|
|
271
|
+
const { blockhash } = await getSolanaClient().getLatestBlockhash();
|
|
272
|
+
return withKaminoRecentBlockhash({ transaction, recentBlockhash: blockhash });
|
|
273
|
+
};
|
|
274
|
+
//# sourceMappingURL=wire.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wire.js","sourceRoot":"","sources":["../../../../../../../../packages/core/chain/chains/solana/kamino/tx/wire.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { KaminoVaultInfo } from './models.js';
|
|
2
|
+
/**
|
|
3
|
+
* Hydrates a curated vault with live state and metrics.
|
|
4
|
+
*
|
|
5
|
+
* Takes an address and resolves the descriptor from the registry itself, so
|
|
6
|
+
* descriptor-shaped data from anywhere else can never decide a vault's
|
|
7
|
+
* identity; an address the registry does not carry is refused. The response's
|
|
8
|
+
* account of the vault is then checked against the pinned identity, every
|
|
9
|
+
* numeric field is parsed under the strict decimal rules (a value that fails
|
|
10
|
+
* throws rather than defaulting to zero), and the published minimums are
|
|
11
|
+
* replaced by the effective ones the program actually accepts.
|
|
12
|
+
*
|
|
13
|
+
* Uncached — client layers own caching (and their caches then hold only
|
|
14
|
+
* hydrations that cleared every check here).
|
|
15
|
+
*/
|
|
16
|
+
export declare const fetchKaminoVaultInfo: (vaultAddress: string) => Promise<KaminoVaultInfo>;
|
|
17
|
+
//# sourceMappingURL=vaultInfo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vaultInfo.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/chain/chains/solana/kamino/vaultInfo.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,eAAe,EAA4B,MAAM,UAAU,CAAA;AA6EpE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,oBAAoB,GAAU,cAAc,MAAM,KAAG,OAAO,CAAC,eAAe,CAmExF,CAAA"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { isValidKaminoRequestAmount, kaminoTokenAmount, kaminoTokenAmountFromBaseUnitString, kaminoTokenAmountFromDecimalString, kaminoTokenToShareAmountRoundedUp, } from './amount.js';
|
|
2
|
+
import { fetchKaminoVaultMetrics, fetchKaminoVaultState } from './api.js';
|
|
3
|
+
import { parseKaminoDisplayDecimal } from './decimal.js';
|
|
4
|
+
import { KaminoServiceError } from './KaminoServiceError.js';
|
|
5
|
+
import { isPositiveKaminoRate, parseKaminoRate } from './rate.js';
|
|
6
|
+
import { getKaminoVaultDescriptor } from './registry.js';
|
|
7
|
+
/**
|
|
8
|
+
* A margin of one part in this many — a tenth of a percent — added to the
|
|
9
|
+
* published minimum deposit.
|
|
10
|
+
*/
|
|
11
|
+
const minimumDepositMarginDivisor = 1000n;
|
|
12
|
+
/**
|
|
13
|
+
* Smallest deposit margin in base units, for a minimum too small for the
|
|
14
|
+
* proportional one to clear a rounding loss. Larger than either measured
|
|
15
|
+
* shortfall (8 and 10 base units on the launch vaults).
|
|
16
|
+
*/
|
|
17
|
+
const minimumDepositMarginFloor = 16n;
|
|
18
|
+
/**
|
|
19
|
+
* Multiple of the published minimum a withdraw's token value must reach.
|
|
20
|
+
*
|
|
21
|
+
* The floor measured on-chain sits just above two; the third is margin,
|
|
22
|
+
* because the rule is a reading of observed behaviour rather than a documented
|
|
23
|
+
* contract.
|
|
24
|
+
*/
|
|
25
|
+
const minimumWithdrawMultiple = 3n;
|
|
26
|
+
/** The Solana default/system address doubles as "no farm attached". */
|
|
27
|
+
const systemProgramAddress = '11111111111111111111111111111111';
|
|
28
|
+
/**
|
|
29
|
+
* The smallest deposit a form may offer.
|
|
30
|
+
*
|
|
31
|
+
* The published `minDepositAmount` is in the right unit — unlike its withdraw
|
|
32
|
+
* counterpart — but the program still refuses a deposit *at* it, with
|
|
33
|
+
* `DepositAmountBelowMinimum`. Measured at one base unit of resolution the
|
|
34
|
+
* shortfall is a handful of base units and moves with the share rate — a
|
|
35
|
+
* rounding loss in the share the deposit mints, not a second threshold. The
|
|
36
|
+
* margin is therefore proportional with an absolute floor, both generous
|
|
37
|
+
* against what was measured and both invisible in money.
|
|
38
|
+
*/
|
|
39
|
+
const effectiveMinimumDeposit = (published) => {
|
|
40
|
+
const proportional = (published.baseUnits + minimumDepositMarginDivisor - 1n) / minimumDepositMarginDivisor;
|
|
41
|
+
const margin = proportional > minimumDepositMarginFloor ? proportional : minimumDepositMarginFloor;
|
|
42
|
+
return kaminoTokenAmount(published.baseUnits + margin, published.decimals);
|
|
43
|
+
};
|
|
44
|
+
const malformed = (field, value) => new KaminoServiceError({ malformedNumber: { field, value } });
|
|
45
|
+
const assertFieldMatchesRegistry = (field, actual, expected) => {
|
|
46
|
+
if (actual !== expected) {
|
|
47
|
+
throw new KaminoServiceError({ vaultMetadataMismatch: { field, expected, actual } });
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Refuses a response whose account of the vault differs from the registry's.
|
|
52
|
+
*
|
|
53
|
+
* The mints, their decimals and the farm are immutable properties of a kVault,
|
|
54
|
+
* so this can never fire on a legitimate change. It exists because those
|
|
55
|
+
* values decide where funds go and how amounts are scaled: taking them from
|
|
56
|
+
* the API would mean validating a transaction the API built against values the
|
|
57
|
+
* same API supplied, and the pair could be made consistent. Checking here
|
|
58
|
+
* means the whole feature works from a vault identity the app already knew.
|
|
59
|
+
*/
|
|
60
|
+
const assertMatchesRegistry = (state, descriptor) => {
|
|
61
|
+
assertFieldMatchesRegistry('tokenMint', state.tokenMint, descriptor.tokenMint);
|
|
62
|
+
assertFieldMatchesRegistry('sharesMint', state.sharesMint, descriptor.sharesMint);
|
|
63
|
+
assertFieldMatchesRegistry('tokenMintDecimals', String(state.tokenMintDecimals), String(descriptor.tokenDecimals));
|
|
64
|
+
assertFieldMatchesRegistry('sharesMintDecimals', String(state.sharesMintDecimals), String(descriptor.sharesDecimals));
|
|
65
|
+
assertFieldMatchesRegistry('vaultFarm', normalizeFarm(state.vaultFarm) ?? '', descriptor.farm ?? '');
|
|
66
|
+
};
|
|
67
|
+
const normalizeFarm = (address) => {
|
|
68
|
+
if (!address || address === systemProgramAddress)
|
|
69
|
+
return undefined;
|
|
70
|
+
return address;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Hydrates a curated vault with live state and metrics.
|
|
74
|
+
*
|
|
75
|
+
* Takes an address and resolves the descriptor from the registry itself, so
|
|
76
|
+
* descriptor-shaped data from anywhere else can never decide a vault's
|
|
77
|
+
* identity; an address the registry does not carry is refused. The response's
|
|
78
|
+
* account of the vault is then checked against the pinned identity, every
|
|
79
|
+
* numeric field is parsed under the strict decimal rules (a value that fails
|
|
80
|
+
* throws rather than defaulting to zero), and the published minimums are
|
|
81
|
+
* replaced by the effective ones the program actually accepts.
|
|
82
|
+
*
|
|
83
|
+
* Uncached — client layers own caching (and their caches then hold only
|
|
84
|
+
* hydrations that cleared every check here).
|
|
85
|
+
*/
|
|
86
|
+
export const fetchKaminoVaultInfo = async (vaultAddress) => {
|
|
87
|
+
const descriptor = getKaminoVaultDescriptor(vaultAddress);
|
|
88
|
+
if (!descriptor) {
|
|
89
|
+
throw new KaminoServiceError({ vaultNotInRegistry: vaultAddress });
|
|
90
|
+
}
|
|
91
|
+
const [{ state }, metrics] = await Promise.all([
|
|
92
|
+
fetchKaminoVaultState(descriptor.address),
|
|
93
|
+
fetchKaminoVaultMetrics(descriptor.address),
|
|
94
|
+
]);
|
|
95
|
+
assertMatchesRegistry(state, descriptor);
|
|
96
|
+
// The published figures are numbers a remote service hands us, and they end
|
|
97
|
+
// up bounding what a user may spend. A non-positive or absurd one is refused
|
|
98
|
+
// rather than propagated into a minimum nothing can satisfy.
|
|
99
|
+
const publishedMinDeposit = kaminoTokenAmountFromBaseUnitString(state.minDepositAmount, descriptor.tokenDecimals);
|
|
100
|
+
if (!publishedMinDeposit)
|
|
101
|
+
throw malformed('minDepositAmount', state.minDepositAmount);
|
|
102
|
+
const minDeposit = effectiveMinimumDeposit(publishedMinDeposit);
|
|
103
|
+
if (!isValidKaminoRequestAmount(publishedMinDeposit) || !isValidKaminoRequestAmount(minDeposit)) {
|
|
104
|
+
throw malformed('minDepositAmount', state.minDepositAmount);
|
|
105
|
+
}
|
|
106
|
+
// TOKEN base units, despite the field naming the unit the withdraw endpoint
|
|
107
|
+
// takes. The two scales differ on the SOL vault (9 vs 6), so reading it
|
|
108
|
+
// wrong is silent there and a ~930× error — see `models.ts`.
|
|
109
|
+
const publishedMinWithdraw = kaminoTokenAmountFromBaseUnitString(state.minWithdrawAmount, descriptor.tokenDecimals);
|
|
110
|
+
if (!publishedMinWithdraw)
|
|
111
|
+
throw malformed('minWithdrawAmount', state.minWithdrawAmount);
|
|
112
|
+
const apy30d = parseKaminoDisplayDecimal(metrics.apy30d);
|
|
113
|
+
if (apy30d === undefined)
|
|
114
|
+
throw malformed('apy30d', metrics.apy30d);
|
|
115
|
+
const tokenPriceUsd = parseKaminoDisplayDecimal(metrics.tokenPrice);
|
|
116
|
+
if (tokenPriceUsd === undefined)
|
|
117
|
+
throw malformed('tokenPrice', metrics.tokenPrice);
|
|
118
|
+
// Parsed exactly rather than as a display decimal: this rate converts a
|
|
119
|
+
// user's token amount into the shares a withdraw burns.
|
|
120
|
+
const tokensPerShare = parseKaminoRate(metrics.tokensPerShare);
|
|
121
|
+
if (!tokensPerShare || !isPositiveKaminoRate(tokensPerShare)) {
|
|
122
|
+
throw malformed('tokensPerShare', metrics.tokensPerShare);
|
|
123
|
+
}
|
|
124
|
+
// The program's own withdraw floor is higher than the published figure, so
|
|
125
|
+
// the form minimum is a multiple of it, converted to the share count a
|
|
126
|
+
// withdraw has to name — rounded up, because a share count worth
|
|
127
|
+
// fractionally less than the minimum is exactly the failure being avoided.
|
|
128
|
+
const minWithdraw = kaminoTokenToShareAmountRoundedUp({
|
|
129
|
+
tokens: kaminoTokenAmount(publishedMinWithdraw.baseUnits * minimumWithdrawMultiple, publishedMinWithdraw.decimals),
|
|
130
|
+
tokensPerShare,
|
|
131
|
+
shareDecimals: descriptor.sharesDecimals,
|
|
132
|
+
});
|
|
133
|
+
if (!minWithdraw || !isValidKaminoRequestAmount(publishedMinWithdraw) || !isValidKaminoRequestAmount(minWithdraw)) {
|
|
134
|
+
throw malformed('minWithdrawAmount', state.minWithdrawAmount);
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
descriptor,
|
|
138
|
+
name: state.name,
|
|
139
|
+
minDeposit,
|
|
140
|
+
minWithdraw,
|
|
141
|
+
lookupTable: state.vaultLookupTable,
|
|
142
|
+
apy30d,
|
|
143
|
+
tokensPerShare,
|
|
144
|
+
tokenPriceUsd,
|
|
145
|
+
// Advisory, so an unreadable value drops the liquidity notice rather than
|
|
146
|
+
// failing the whole hydration and blocking deposits too.
|
|
147
|
+
tokensAvailable: kaminoTokenAmountFromDecimalString(metrics.tokensAvailable, descriptor.tokenDecimals),
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
//# sourceMappingURL=vaultInfo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vaultInfo.js","sourceRoot":"","sources":["../../../../../../../packages/core/chain/chains/solana/kamino/vaultInfo.ts"],"names":[],"mappings":"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"}
|