@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,59 @@
|
|
|
1
|
+
import { HttpResponseError } from '@vultisig/lib-utils/fetch/HttpResponseError';
|
|
2
|
+
import { queryUrl } from '@vultisig/lib-utils/query/queryUrl';
|
|
3
|
+
import { kaminoConfig } from './config.js';
|
|
4
|
+
import { KaminoServiceError } from './KaminoServiceError.js';
|
|
5
|
+
/**
|
|
6
|
+
* Read-side REST client for Kamino Earn vaults, against
|
|
7
|
+
* `https://api.kamino.finance` (public, unauthenticated).
|
|
8
|
+
*
|
|
9
|
+
* Kamino's structured error body is converted into `KaminoServiceError` so
|
|
10
|
+
* callers can branch on `reason.api.code` (`KVAULT_NOT_FOUND`, …) instead of
|
|
11
|
+
* matching message text. Retries are the caller's concern — the client layers
|
|
12
|
+
* (react-query, CLI) already own retry policy, and `KaminoServiceError.isRetryable`
|
|
13
|
+
* tells them whether one is worth it.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Timeout for the position read, which gates withdraws. Getting out of a
|
|
17
|
+
* position is the operation whose failure a user cannot route around, so it
|
|
18
|
+
* keeps a long limit: cutting the wait converts "slow" into "cannot withdraw"
|
|
19
|
+
* rather than into a faster answer. Everything else uses `queryUrl`'s 20s
|
|
20
|
+
* default, which no healthy Kamino call approaches (reads measure ~100ms).
|
|
21
|
+
*/
|
|
22
|
+
const positionsReadTimeoutMs = 60_000;
|
|
23
|
+
const isKaminoErrorBody = (body) => typeof body === 'object' &&
|
|
24
|
+
body !== null &&
|
|
25
|
+
typeof body.statusCode === 'number' &&
|
|
26
|
+
typeof body.message === 'string';
|
|
27
|
+
const kaminoGet = async (path, timeoutMs) => {
|
|
28
|
+
try {
|
|
29
|
+
return await queryUrl(`${kaminoConfig.apiBaseUrl}${path}`, timeoutMs === undefined ? undefined : { timeoutMs });
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
if (error instanceof HttpResponseError && isKaminoErrorBody(error.body)) {
|
|
33
|
+
throw new KaminoServiceError({
|
|
34
|
+
api: { status: error.status, code: error.body.code, message: error.body.message },
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
// Path parameters are encoded because nothing in this module enforces that
|
|
41
|
+
// they are pubkeys: a value containing `/`, `?` or `#` would otherwise change
|
|
42
|
+
// which endpoint is called. A base58 string passes through unchanged.
|
|
43
|
+
const segment = encodeURIComponent;
|
|
44
|
+
/** Fetches a vault's live state (`GET /kvaults/vaults/{address}`). */
|
|
45
|
+
export const fetchKaminoVaultState = (address) => kaminoGet(`/kvaults/vaults/${segment(address)}`);
|
|
46
|
+
/** Fetches a vault's metrics (`GET /kvaults/vaults/{address}/metrics`). */
|
|
47
|
+
export const fetchKaminoVaultMetrics = (address) => kaminoGet(`/kvaults/vaults/${segment(address)}/metrics`);
|
|
48
|
+
/**
|
|
49
|
+
* Fetches every kVault position the owner holds
|
|
50
|
+
* (`GET /kvaults/users/{owner}/positions`). Uncached and generously timed —
|
|
51
|
+
* this read gates withdraws.
|
|
52
|
+
*/
|
|
53
|
+
export const fetchKaminoUserPositions = (owner) => kaminoGet(`/kvaults/users/${segment(owner)}/positions`, positionsReadTimeoutMs);
|
|
54
|
+
/**
|
|
55
|
+
* Fetches lifetime PnL for one position
|
|
56
|
+
* (`GET /kvaults/users/{owner}/vaults/{vault}/pnl`).
|
|
57
|
+
*/
|
|
58
|
+
export const fetchKaminoPnl = ({ owner, vault }) => kaminoGet(`/kvaults/users/${segment(owner)}/vaults/${segment(vault)}/pnl`);
|
|
59
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../../../../../../packages/core/chain/chains/solana/kamino/api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,6CAA6C,CAAA;AAC/E,OAAO,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAA;AAE7D,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAQzD;;;;;;;;;GASG;AAEH;;;;;;GAMG;AACH,MAAM,sBAAsB,GAAG,MAAM,CAAA;AAarC,MAAM,iBAAiB,GAAG,CAAC,IAAa,EAA+B,EAAE,CACvE,OAAO,IAAI,KAAK,QAAQ;IACxB,IAAI,KAAK,IAAI;IACb,OAAQ,IAA4B,CAAC,UAAU,KAAK,QAAQ;IAC5D,OAAQ,IAA4B,CAAC,OAAO,KAAK,QAAQ,CAAA;AAE3D,MAAM,SAAS,GAAG,KAAK,EAAK,IAAY,EAAE,SAAkB,EAAc,EAAE;IAC1E,IAAI,CAAC;QACH,OAAO,MAAM,QAAQ,CAAI,GAAG,YAAY,CAAC,UAAU,GAAG,IAAI,EAAE,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAA;IACpH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,iBAAiB,IAAI,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACxE,MAAM,IAAI,kBAAkB,CAAC;gBAC3B,GAAG,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE;aAClF,CAAC,CAAA;QACJ,CAAC;QACD,MAAM,KAAK,CAAA;IACb,CAAC;AACH,CAAC,CAAA;AAED,2EAA2E;AAC3E,8EAA8E;AAC9E,sEAAsE;AACtE,MAAM,OAAO,GAAG,kBAAkB,CAAA;AAElC,sEAAsE;AACtE,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,OAAe,EAAqC,EAAE,CAC1F,SAAS,CAAC,mBAAmB,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;AAElD,2EAA2E;AAC3E,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,OAAe,EAAuC,EAAE,CAC9F,SAAS,CAAC,mBAAmB,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;AAE1D;;;;GAIG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,KAAa,EAAyC,EAAE,CAC/F,SAAS,CAAC,kBAAkB,OAAO,CAAC,KAAK,CAAC,YAAY,EAAE,sBAAsB,CAAC,CAAA;AAEjF;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,EAAE,KAAK,EAAE,KAAK,EAAoC,EAA8B,EAAE,CAC/G,SAAS,CAAC,kBAAkB,OAAO,CAAC,KAAK,CAAC,WAAW,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rendering and bounds shared by the Kamino amount types.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Solana instruction arguments are `u64`; anything above that cannot be
|
|
6
|
+
* expressed on-chain regardless of what the API would accept.
|
|
7
|
+
*/
|
|
8
|
+
export declare const kaminoMaxBaseUnits: bigint;
|
|
9
|
+
/**
|
|
10
|
+
* Widest decimal scale we are willing to handle. SPL mints are a `u8`, but
|
|
11
|
+
* nothing legitimate approaches this, and an absurd scale would make the
|
|
12
|
+
* `10^decimals` factors in the conversions unbounded.
|
|
13
|
+
*/
|
|
14
|
+
export declare const kaminoMaxDecimals = 18;
|
|
15
|
+
/**
|
|
16
|
+
* Renders base units as a plain human-units decimal string: no grouping, no
|
|
17
|
+
* trailing zeros, no exponent. This is the form Kamino's request bodies take.
|
|
18
|
+
*/
|
|
19
|
+
export declare const renderKaminoBaseUnits: ({ baseUnits, decimals }: {
|
|
20
|
+
baseUnits: bigint;
|
|
21
|
+
decimals: number;
|
|
22
|
+
}) => string;
|
|
23
|
+
//# sourceMappingURL=baseUnits.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"baseUnits.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/chain/chains/solana/kamino/baseUnits.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,eAAO,MAAM,kBAAkB,QAAiB,CAAA;AAEhD;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,KAAK,CAAA;AAEnC;;;GAGG;AACH,eAAO,MAAM,qBAAqB,GAAI,yBAAyB;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,KAAG,MAWxG,CAAA"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rendering and bounds shared by the Kamino amount types.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Solana instruction arguments are `u64`; anything above that cannot be
|
|
6
|
+
* expressed on-chain regardless of what the API would accept.
|
|
7
|
+
*/
|
|
8
|
+
export const kaminoMaxBaseUnits = 2n ** 64n - 1n;
|
|
9
|
+
/**
|
|
10
|
+
* Widest decimal scale we are willing to handle. SPL mints are a `u8`, but
|
|
11
|
+
* nothing legitimate approaches this, and an absurd scale would make the
|
|
12
|
+
* `10^decimals` factors in the conversions unbounded.
|
|
13
|
+
*/
|
|
14
|
+
export const kaminoMaxDecimals = 18;
|
|
15
|
+
/**
|
|
16
|
+
* Renders base units as a plain human-units decimal string: no grouping, no
|
|
17
|
+
* trailing zeros, no exponent. This is the form Kamino's request bodies take.
|
|
18
|
+
*/
|
|
19
|
+
export const renderKaminoBaseUnits = ({ baseUnits, decimals }) => {
|
|
20
|
+
const isNegative = baseUnits < 0n;
|
|
21
|
+
const digits = (isNegative ? -baseUnits : baseUnits).toString();
|
|
22
|
+
if (decimals <= 0)
|
|
23
|
+
return isNegative ? `-${digits}` : digits;
|
|
24
|
+
const padded = digits.padStart(decimals + 1, '0');
|
|
25
|
+
const whole = padded.slice(0, padded.length - decimals);
|
|
26
|
+
const fraction = padded.slice(padded.length - decimals).replace(/0+$/, '');
|
|
27
|
+
const magnitude = fraction ? `${whole}.${fraction}` : whole;
|
|
28
|
+
return isNegative ? `-${magnitude}` : magnitude;
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=baseUnits.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"baseUnits.js","sourceRoot":"","sources":["../../../../../../../packages/core/chain/chains/solana/kamino/baseUnits.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,IAAI,GAAG,GAAG,EAAE,CAAA;AAEhD;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAA;AAEnC;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,EAAE,SAAS,EAAE,QAAQ,EAA2C,EAAU,EAAE;IAChH,MAAM,UAAU,GAAG,SAAS,GAAG,EAAE,CAAA;IACjC,MAAM,MAAM,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,QAAQ,EAAE,CAAA;IAC/D,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,UAAU,CAAC,CAAC,CAAC,IAAI,MAAM,EAAE,CAAC,CAAC,CAAC,MAAM,CAAA;IAE5D,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,GAAG,CAAC,EAAE,GAAG,CAAC,CAAA;IACjD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAA;IACvD,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;IAE1E,MAAM,SAAS,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,QAAQ,EAAE,CAAC,CAAC,CAAC,KAAK,CAAA;IAC3D,OAAO,UAAU,CAAC,CAAC,CAAC,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;AACjD,CAAC,CAAA"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constants for Kamino Earn vaults (kVaults) on Solana.
|
|
3
|
+
*
|
|
4
|
+
* The REST API is public and unauthenticated — the action endpoints build an
|
|
5
|
+
* unsigned transaction from the caller's wallet address alone, which is why
|
|
6
|
+
* every built transaction must be validated on-device before it is signed.
|
|
7
|
+
*/
|
|
8
|
+
export declare const kaminoConfig: {
|
|
9
|
+
/** Kamino's public REST API. No key, no auth header. */
|
|
10
|
+
readonly apiBaseUrl: "https://api.kamino.finance";
|
|
11
|
+
/** The kVaults program every deposit and withdraw invokes. */
|
|
12
|
+
readonly programId: "KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd";
|
|
13
|
+
/**
|
|
14
|
+
* Kamino's farms program. Every launch vault has a farm attached, so a
|
|
15
|
+
* deposit ends with `initializeUser` + `stake` against this program and the
|
|
16
|
+
* shares never land in the user's wallet.
|
|
17
|
+
*/
|
|
18
|
+
readonly farmsProgramId: "FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr";
|
|
19
|
+
/**
|
|
20
|
+
* Wrapped SOL. The SOL vault's underlying token is this mint, not native
|
|
21
|
+
* SOL, which is why its deposits carry a wrap prefix.
|
|
22
|
+
*/
|
|
23
|
+
readonly wrappedSolMint: "So11111111111111111111111111111111111111112";
|
|
24
|
+
/** Circle's USDC — the underlying token of both dollar vaults. */
|
|
25
|
+
readonly usdcMint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/chain/chains/solana/kamino/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,YAAY;IACvB,wDAAwD;;IAGxD,8DAA8D;;IAG9D;;;;OAIG;;IAGH;;;OAGG;;IAGH,kEAAkE;;CAE1D,CAAA"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Constants for Kamino Earn vaults (kVaults) on Solana.
|
|
3
|
+
*
|
|
4
|
+
* The REST API is public and unauthenticated — the action endpoints build an
|
|
5
|
+
* unsigned transaction from the caller's wallet address alone, which is why
|
|
6
|
+
* every built transaction must be validated on-device before it is signed.
|
|
7
|
+
*/
|
|
8
|
+
export const kaminoConfig = {
|
|
9
|
+
/** Kamino's public REST API. No key, no auth header. */
|
|
10
|
+
apiBaseUrl: 'https://api.kamino.finance',
|
|
11
|
+
/** The kVaults program every deposit and withdraw invokes. */
|
|
12
|
+
programId: 'KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd',
|
|
13
|
+
/**
|
|
14
|
+
* Kamino's farms program. Every launch vault has a farm attached, so a
|
|
15
|
+
* deposit ends with `initializeUser` + `stake` against this program and the
|
|
16
|
+
* shares never land in the user's wallet.
|
|
17
|
+
*/
|
|
18
|
+
farmsProgramId: 'FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr',
|
|
19
|
+
/**
|
|
20
|
+
* Wrapped SOL. The SOL vault's underlying token is this mint, not native
|
|
21
|
+
* SOL, which is why its deposits carry a wrap prefix.
|
|
22
|
+
*/
|
|
23
|
+
wrappedSolMint: 'So11111111111111111111111111111111111111112',
|
|
24
|
+
/** Circle's USDC — the underlying token of both dollar vaults. */
|
|
25
|
+
usdcMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../../../../../packages/core/chain/chains/solana/kamino/config.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,wDAAwD;IACxD,UAAU,EAAE,4BAA4B;IAExC,8DAA8D;IAC9D,SAAS,EAAE,6CAA6C;IAExD;;;;OAIG;IACH,cAAc,EAAE,8CAA8C;IAE9D;;;OAGG;IACH,cAAc,EAAE,6CAA6C;IAE7D,kEAAkE;IAClE,QAAQ,EAAE,8CAA8C;CAChD,CAAA"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strict-format parsing for Kamino's numeric JSON, which arrives as decimal
|
|
3
|
+
* strings (`"0.03994268764493801732"`, `"1.0536041812651029025"`).
|
|
4
|
+
*
|
|
5
|
+
* The format is validated rather than coerced: grouping separators, exponents
|
|
6
|
+
* and whitespace are rejected instead of being silently mis-read. What this
|
|
7
|
+
* does not guarantee is exactness — a JS `number` holds ~16 significant digits
|
|
8
|
+
* and Kamino publishes longer values. That is acceptable for the display
|
|
9
|
+
* values `parseKaminoDisplayDecimal` is used on (APY, PnL, prices). Anything
|
|
10
|
+
* that sizes a transaction must use `KaminoRate`, which is exact.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Whether `raw` is a plain decimal string: optional leading `-`, ASCII digits,
|
|
14
|
+
* at most one `.`, at least one digit. Everything else — grouping separators,
|
|
15
|
+
* exponents, whitespace, non-ASCII digits — is rejected.
|
|
16
|
+
*/
|
|
17
|
+
export declare const isPlainKaminoDecimal: (raw: string) => boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Parses a display-only value (APY, price, PnL) after strict format
|
|
20
|
+
* validation, or `undefined` when the format is not a plain decimal. Never use
|
|
21
|
+
* the result to size a transaction — it round-trips through a binary double.
|
|
22
|
+
*/
|
|
23
|
+
export declare const parseKaminoDisplayDecimal: (raw: string) => number | undefined;
|
|
24
|
+
//# sourceMappingURL=decimal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decimal.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/chain/chains/solana/kamino/decimal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,KAAK,MAAM,KAAG,OAmBlD,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,GAAI,KAAK,MAAM,KAAG,MAAM,GAAG,SAGhE,CAAA"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strict-format parsing for Kamino's numeric JSON, which arrives as decimal
|
|
3
|
+
* strings (`"0.03994268764493801732"`, `"1.0536041812651029025"`).
|
|
4
|
+
*
|
|
5
|
+
* The format is validated rather than coerced: grouping separators, exponents
|
|
6
|
+
* and whitespace are rejected instead of being silently mis-read. What this
|
|
7
|
+
* does not guarantee is exactness — a JS `number` holds ~16 significant digits
|
|
8
|
+
* and Kamino publishes longer values. That is acceptable for the display
|
|
9
|
+
* values `parseKaminoDisplayDecimal` is used on (APY, PnL, prices). Anything
|
|
10
|
+
* that sizes a transaction must use `KaminoRate`, which is exact.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Whether `raw` is a plain decimal string: optional leading `-`, ASCII digits,
|
|
14
|
+
* at most one `.`, at least one digit. Everything else — grouping separators,
|
|
15
|
+
* exponents, whitespace, non-ASCII digits — is rejected.
|
|
16
|
+
*/
|
|
17
|
+
export const isPlainKaminoDecimal = (raw) => {
|
|
18
|
+
if (!raw)
|
|
19
|
+
return false;
|
|
20
|
+
let hasDigit = false;
|
|
21
|
+
let hasSeparator = false;
|
|
22
|
+
for (let index = 0; index < raw.length; index++) {
|
|
23
|
+
const character = raw[index];
|
|
24
|
+
if (character === '-') {
|
|
25
|
+
if (index !== 0)
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
else if (character === '.') {
|
|
29
|
+
if (hasSeparator)
|
|
30
|
+
return false;
|
|
31
|
+
hasSeparator = true;
|
|
32
|
+
}
|
|
33
|
+
else if (character >= '0' && character <= '9') {
|
|
34
|
+
hasDigit = true;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return hasDigit;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Parses a display-only value (APY, price, PnL) after strict format
|
|
44
|
+
* validation, or `undefined` when the format is not a plain decimal. Never use
|
|
45
|
+
* the result to size a transaction — it round-trips through a binary double.
|
|
46
|
+
*/
|
|
47
|
+
export const parseKaminoDisplayDecimal = (raw) => {
|
|
48
|
+
if (!isPlainKaminoDecimal(raw))
|
|
49
|
+
return undefined;
|
|
50
|
+
return Number(raw);
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=decimal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decimal.js","sourceRoot":"","sources":["../../../../../../../packages/core/chain/chains/solana/kamino/decimal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,GAAW,EAAW,EAAE;IAC3D,IAAI,CAAC,GAAG;QAAE,OAAO,KAAK,CAAA;IAEtB,IAAI,QAAQ,GAAG,KAAK,CAAA;IACpB,IAAI,YAAY,GAAG,KAAK,CAAA;IACxB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QAChD,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,CAAA;QAC5B,IAAI,SAAS,KAAK,GAAG,EAAE,CAAC;YACtB,IAAI,KAAK,KAAK,CAAC;gBAAE,OAAO,KAAK,CAAA;QAC/B,CAAC;aAAM,IAAI,SAAS,KAAK,GAAG,EAAE,CAAC;YAC7B,IAAI,YAAY;gBAAE,OAAO,KAAK,CAAA;YAC9B,YAAY,GAAG,IAAI,CAAA;QACrB,CAAC;aAAM,IAAI,SAAS,IAAI,GAAG,IAAI,SAAS,IAAI,GAAG,EAAE,CAAC;YAChD,QAAQ,GAAG,IAAI,CAAA;QACjB,CAAC;aAAM,CAAC;YACN,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC;IACD,OAAO,QAAQ,CAAA;AACjB,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,GAAW,EAAsB,EAAE;IAC3E,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC;QAAE,OAAO,SAAS,CAAA;IAChD,OAAO,MAAM,CAAC,GAAG,CAAC,CAAA;AACpB,CAAC,CAAA"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { KaminoShareAmount, KaminoTokenAmount } from './amount.js';
|
|
2
|
+
import { KaminoRate } from './rate.js';
|
|
3
|
+
import { KaminoVaultDescriptor } from './registry.js';
|
|
4
|
+
/**
|
|
5
|
+
* `GET /kvaults/vaults/{address}`. Every numeric field arrives as a string;
|
|
6
|
+
* nothing here may be consumed without going through the typed amounts.
|
|
7
|
+
*/
|
|
8
|
+
export type KaminoVaultStateResponse = {
|
|
9
|
+
address: string;
|
|
10
|
+
programId: string;
|
|
11
|
+
state: {
|
|
12
|
+
name: string;
|
|
13
|
+
tokenMint: string;
|
|
14
|
+
tokenMintDecimals: number;
|
|
15
|
+
sharesMint: string;
|
|
16
|
+
/**
|
|
17
|
+
* Share decimals are independent of token decimals — across the live vault
|
|
18
|
+
* set the pairs include (6,6), (6,9), (8,8) and (9,9). Never assume they
|
|
19
|
+
* match.
|
|
20
|
+
*/
|
|
21
|
+
sharesMintDecimals: number;
|
|
22
|
+
/**
|
|
23
|
+
* Base units of the **underlying token**. Correctly denominated, unlike
|
|
24
|
+
* `minWithdrawAmount` — but still not a figure the program accepts: a
|
|
25
|
+
* deposit at exactly this amount is refused. Only the effective-minimum
|
|
26
|
+
* derivation in `fetchKaminoVaultInfo` may read it.
|
|
27
|
+
*/
|
|
28
|
+
minDepositAmount: string;
|
|
29
|
+
/**
|
|
30
|
+
* Base units of the **underlying token**, despite naming the unit the
|
|
31
|
+
* withdraw endpoint takes. Reading it as a share count is a silent error on
|
|
32
|
+
* a dollar vault (the rate is near 1) and a ~930× one on the SOL vault. It
|
|
33
|
+
* is also not the floor the program enforces — see the effective-minimum
|
|
34
|
+
* derivation in `fetchKaminoVaultInfo`, which is what forms should use.
|
|
35
|
+
* Nothing but that derivation may read this field.
|
|
36
|
+
*/
|
|
37
|
+
minWithdrawAmount: string;
|
|
38
|
+
/** Address lookup table the built transactions reference. */
|
|
39
|
+
vaultLookupTable: string;
|
|
40
|
+
/**
|
|
41
|
+
* When set, deposits auto-stake the shares into this farm and they never
|
|
42
|
+
* appear in the user's associated token account.
|
|
43
|
+
*/
|
|
44
|
+
vaultFarm: string;
|
|
45
|
+
performanceFeeBps: number;
|
|
46
|
+
managementFeeBps: number;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* `GET /kvaults/vaults/{address}/metrics`. Every numeric field is a decimal
|
|
51
|
+
* string; APYs are fractions (`"0.0391"` = 3.91%), not percentages.
|
|
52
|
+
*/
|
|
53
|
+
export type KaminoVaultMetricsResponse = {
|
|
54
|
+
apy30d: string;
|
|
55
|
+
/** Underlying tokens per share — the rate for valuing a position. */
|
|
56
|
+
tokensPerShare: string;
|
|
57
|
+
/** USD per share. NOT interchangeable with `tokensPerShare`. */
|
|
58
|
+
sharePrice: string;
|
|
59
|
+
tokenPrice: string;
|
|
60
|
+
tokensAvailable: string;
|
|
61
|
+
tokensInvested: string;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* One element of `GET /kvaults/users/{owner}/positions`. Shares only — the
|
|
65
|
+
* endpoint returns no token or fiat value.
|
|
66
|
+
*/
|
|
67
|
+
export type KaminoUserPositionResponse = {
|
|
68
|
+
vaultAddress: string;
|
|
69
|
+
/**
|
|
70
|
+
* Shares held inside the vault's farm. For a farmed vault this is where a
|
|
71
|
+
* deposit lands, so it is normally the whole position.
|
|
72
|
+
*/
|
|
73
|
+
stakedShares: string;
|
|
74
|
+
/** Shares sitting in the user's associated token account. */
|
|
75
|
+
unstakedShares: string;
|
|
76
|
+
totalShares: string;
|
|
77
|
+
};
|
|
78
|
+
/** `GET /kvaults/users/{owner}/vaults/{vault}/pnl`. */
|
|
79
|
+
export type KaminoPnlResponse = {
|
|
80
|
+
totalCostBasis: KaminoPnlAmounts;
|
|
81
|
+
totalPnl: KaminoPnlAmounts;
|
|
82
|
+
};
|
|
83
|
+
/** One PnL figure expressed in the three denominations the API reports. */
|
|
84
|
+
export type KaminoPnlAmounts = {
|
|
85
|
+
token: string;
|
|
86
|
+
sol: string;
|
|
87
|
+
usd: string;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* A launch vault after its registry entry has been merged with live API state
|
|
91
|
+
* and metrics. This is what the feature layer consumes; the raw responses stay
|
|
92
|
+
* at the service boundary. The vault's identity (mints, decimals, farm) is
|
|
93
|
+
* read from `descriptor` — the registry's record — never from the response: a
|
|
94
|
+
* transaction built by the API cannot be validated against values the same API
|
|
95
|
+
* supplied.
|
|
96
|
+
*/
|
|
97
|
+
export type KaminoVaultInfo = {
|
|
98
|
+
descriptor: KaminoVaultDescriptor;
|
|
99
|
+
/** On-chain vault name (`"Steakhouse USDC"`). */
|
|
100
|
+
name: string;
|
|
101
|
+
/**
|
|
102
|
+
* The smallest deposit a form may offer.
|
|
103
|
+
*
|
|
104
|
+
* DERIVED: the published `minDepositAmount` plus a margin, because the
|
|
105
|
+
* program refuses a deposit at exactly the published figure. See
|
|
106
|
+
* `fetchKaminoVaultInfo`.
|
|
107
|
+
*/
|
|
108
|
+
minDeposit: KaminoTokenAmount;
|
|
109
|
+
/**
|
|
110
|
+
* The smallest withdraw a form may offer, in SHARE base units.
|
|
111
|
+
*
|
|
112
|
+
* DERIVED, not `state.minWithdrawAmount` read at face value: that field is
|
|
113
|
+
* denominated in the underlying token and the program's own floor is higher
|
|
114
|
+
* than it. Because the derivation goes through `tokensPerShare`, this figure
|
|
115
|
+
* moves with the rate, and it is hydrated in the same response.
|
|
116
|
+
*/
|
|
117
|
+
minWithdraw: KaminoShareAmount;
|
|
118
|
+
/**
|
|
119
|
+
* The address lookup table the built transactions reference.
|
|
120
|
+
*
|
|
121
|
+
* Live rather than pinned: unlike the mints and the farm, Kamino can
|
|
122
|
+
* legitimately repoint a vault at a new table. A substituted table renames
|
|
123
|
+
* an account into a mismatch rather than out of one, so no transaction check
|
|
124
|
+
* rests on it.
|
|
125
|
+
*/
|
|
126
|
+
lookupTable: string;
|
|
127
|
+
/** 30-day APY as a fraction (0.0391 = 3.91%). Display-only. */
|
|
128
|
+
apy30d: number;
|
|
129
|
+
/**
|
|
130
|
+
* Underlying tokens per share, kept exact: this rate sizes a withdraw, and a
|
|
131
|
+
* rounding error in it can over-request shares.
|
|
132
|
+
*/
|
|
133
|
+
tokensPerShare: KaminoRate;
|
|
134
|
+
/** USD price of the underlying token. Display-only. */
|
|
135
|
+
tokenPriceUsd: number;
|
|
136
|
+
/**
|
|
137
|
+
* The underlying balance the vault currently holds liquid, rather than
|
|
138
|
+
* invested in lending reserves. `undefined` when the metrics response did
|
|
139
|
+
* not carry a readable value.
|
|
140
|
+
*
|
|
141
|
+
* Advisory only — it says what a withdraw is likely to be able to settle
|
|
142
|
+
* immediately, and nothing sizes a transaction from it. The measured ratio
|
|
143
|
+
* is well under 1% of the launch vaults, so a withdraw above this buffer is
|
|
144
|
+
* the ordinary case, not an exception.
|
|
145
|
+
*/
|
|
146
|
+
tokensAvailable?: KaminoTokenAmount;
|
|
147
|
+
};
|
|
148
|
+
//# sourceMappingURL=models.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/chain/chains/solana/kamino/models.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AACnC,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAElD;;;GAGG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAA;QACZ,SAAS,EAAE,MAAM,CAAA;QACjB,iBAAiB,EAAE,MAAM,CAAA;QACzB,UAAU,EAAE,MAAM,CAAA;QAClB;;;;WAIG;QACH,kBAAkB,EAAE,MAAM,CAAA;QAC1B;;;;;WAKG;QACH,gBAAgB,EAAE,MAAM,CAAA;QACxB;;;;;;;WAOG;QACH,iBAAiB,EAAE,MAAM,CAAA;QACzB,6DAA6D;QAC7D,gBAAgB,EAAE,MAAM,CAAA;QACxB;;;WAGG;QACH,SAAS,EAAE,MAAM,CAAA;QACjB,iBAAiB,EAAE,MAAM,CAAA;QACzB,gBAAgB,EAAE,MAAM,CAAA;KACzB,CAAA;CACF,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,0BAA0B,GAAG;IACvC,MAAM,EAAE,MAAM,CAAA;IACd,qEAAqE;IACrE,cAAc,EAAE,MAAM,CAAA;IACtB,gEAAgE;IAChE,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;IAClB,eAAe,EAAE,MAAM,CAAA;IACvB,cAAc,EAAE,MAAM,CAAA;CACvB,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,0BAA0B,GAAG;IACvC,YAAY,EAAE,MAAM,CAAA;IACpB;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB,6DAA6D;IAC7D,cAAc,EAAE,MAAM,CAAA;IACtB,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,uDAAuD;AACvD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,cAAc,EAAE,gBAAgB,CAAA;IAChC,QAAQ,EAAE,gBAAgB,CAAA;CAC3B,CAAA;AAED,2EAA2E;AAC3E,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;IACX,GAAG,EAAE,MAAM,CAAA;CACZ,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,UAAU,EAAE,qBAAqB,CAAA;IACjC,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAA;IACZ;;;;;;OAMG;IACH,UAAU,EAAE,iBAAiB,CAAA;IAC7B;;;;;;;OAOG;IACH,WAAW,EAAE,iBAAiB,CAAA;IAC9B;;;;;;;OAOG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAA;IACd;;;OAGG;IACH,cAAc,EAAE,UAAU,CAAA;IAC1B,uDAAuD;IACvD,aAAa,EAAE,MAAM,CAAA;IACrB;;;;;;;;;OASG;IACH,eAAe,CAAC,EAAE,iBAAiB,CAAA;CACpC,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.js","sourceRoot":"","sources":["../../../../../../../packages/core/chain/chains/solana/kamino/models.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { KaminoShareAmount } from './amount.js';
|
|
2
|
+
import { KaminoUserPositionResponse } from './models.js';
|
|
3
|
+
/**
|
|
4
|
+
* The user's share balance in one vault, split the way `/positions` reports
|
|
5
|
+
* it.
|
|
6
|
+
*
|
|
7
|
+
* Shares, never tokens: the withdraw endpoint takes shares and the balance a
|
|
8
|
+
* full withdraw must send is a share count. The token figure a form is
|
|
9
|
+
* denominated in is a projection of these at the current rate, and it is
|
|
10
|
+
* derived — never the other way round.
|
|
11
|
+
*/
|
|
12
|
+
export type KaminoSharePosition = {
|
|
13
|
+
staked: KaminoShareAmount;
|
|
14
|
+
unstaked: KaminoShareAmount;
|
|
15
|
+
total: KaminoShareAmount;
|
|
16
|
+
/**
|
|
17
|
+
* The largest share amount `POST /ktx/kvault/withdraw` will size as a share
|
|
18
|
+
* count rather than rewriting to `u64::MAX` — its *withdraw everything*
|
|
19
|
+
* sentinel.
|
|
20
|
+
*
|
|
21
|
+
* The sentinel fires at greater-than-**or-equal-to** the spendable balance,
|
|
22
|
+
* not merely above it. That was measured, not assumed, on wallets whose
|
|
23
|
+
* balance is an exact `u64` in a token account, so there is no rounding
|
|
24
|
+
* anywhere in the comparison. So "the whole balance" is not a request this
|
|
25
|
+
* API accepts, and the maximum has to be the largest amount strictly
|
|
26
|
+
* beneath it.
|
|
27
|
+
*
|
|
28
|
+
* Truncating the reported string is necessary and NOT sufficient. It gives a
|
|
29
|
+
* value strictly below the balance only when there were digits past the
|
|
30
|
+
* mint's scale to throw away; when the balance is exactly representable the
|
|
31
|
+
* truncation IS the balance, and asking for it is asking for everything. So
|
|
32
|
+
* the exact case gives up one base unit — `10^-6` of a share, worth about a
|
|
33
|
+
* ten-thousandth of a cent on the launch vaults — and the inexact case does
|
|
34
|
+
* not have to.
|
|
35
|
+
*/
|
|
36
|
+
spendable: KaminoShareAmount;
|
|
37
|
+
/**
|
|
38
|
+
* Whether the two reported parts add up to the reported total.
|
|
39
|
+
*
|
|
40
|
+
* A response whose total exceeds its parts has shares it has not accounted
|
|
41
|
+
* for, and a withdraw flow has to know exactly how the position is split:
|
|
42
|
+
* the unstaked half is what decides how many shares the farm is asked to
|
|
43
|
+
* release, and a request built against a wrong split is one the API will
|
|
44
|
+
* answer with a different transaction from the one being checked for.
|
|
45
|
+
*
|
|
46
|
+
* Compared at the API's OWN precision, never at the share mint's scale.
|
|
47
|
+
* Truncating three strings to six decimals and adding two of them can miss
|
|
48
|
+
* the third by a base unit with nothing wrong — `0.9445485 + 0.9595935`
|
|
49
|
+
* truncates to `944548 + 959593`, one short of `1904142` — and refusing a
|
|
50
|
+
* real position over a last decimal place is not a guard, it is a bug.
|
|
51
|
+
*/
|
|
52
|
+
accountsForItsTotal: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Whether the three reported numbers can all be true at once. A response
|
|
55
|
+
* whose parts sum past its total says the user holds more shares than they
|
|
56
|
+
* hold — figures that cannot be spent as a balance, and spending a balance
|
|
57
|
+
* that is too large is precisely the mistake the API turns into `u64::MAX`.
|
|
58
|
+
* The sum is compared at the API's own precision, like
|
|
59
|
+
* `accountsForItsTotal`: each part can sit below the total while the pair
|
|
60
|
+
* still exceeds it, and a truncated comparison could mask a real excess.
|
|
61
|
+
*/
|
|
62
|
+
isPlausible: boolean;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Parses one `/positions` element at the vault's pinned share scale.
|
|
66
|
+
* `undefined` when any of the three values is not a plain decimal — a value
|
|
67
|
+
* that is present and unreadable is a failed read, not a zero balance.
|
|
68
|
+
*
|
|
69
|
+
* Every value is TRUNCATED to the share mint's own decimals on the way in.
|
|
70
|
+
* The endpoint reports up to 14 decimal places for a 6-decimal mint, and the
|
|
71
|
+
* extra digits are not a balance anyone can spend: handing the reported
|
|
72
|
+
* string straight back as an amount asks for more than the position holds,
|
|
73
|
+
* which the API rewrites to its withdraw-everything sentinel.
|
|
74
|
+
*/
|
|
75
|
+
export declare const parseKaminoSharePosition: ({ position, shareDecimals, }: {
|
|
76
|
+
position: KaminoUserPositionResponse;
|
|
77
|
+
shareDecimals: number;
|
|
78
|
+
}) => KaminoSharePosition | undefined;
|
|
79
|
+
//# sourceMappingURL=position.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"position.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/chain/chains/solana/kamino/position.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAA0E,MAAM,UAAU,CAAA;AACpH,OAAO,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAA;AAGrD;;;;;;;;GAQG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,iBAAiB,CAAA;IACzB,QAAQ,EAAE,iBAAiB,CAAA;IAC3B,KAAK,EAAE,iBAAiB,CAAA;IACxB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,SAAS,EAAE,iBAAiB,CAAA;IAC5B;;;;;;;;;;;;;;OAcG;IACH,mBAAmB,EAAE,OAAO,CAAA;IAC5B;;;;;;;;OAQG;IACH,WAAW,EAAE,OAAO,CAAA;CACrB,CAAA;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,GAAI,8BAGtC;IACD,QAAQ,EAAE,0BAA0B,CAAA;IACpC,aAAa,EAAE,MAAM,CAAA;CACtB,KAAG,mBAAmB,GAAG,SAqBzB,CAAA"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { kaminoShareAmount, kaminoShareAmountFromDecimalString, scaleKaminoRate } from './amount.js';
|
|
2
|
+
import { compareKaminoRates, kaminoRateEquals, parseKaminoRate, sumKaminoRates } from './rate.js';
|
|
3
|
+
/**
|
|
4
|
+
* Parses one `/positions` element at the vault's pinned share scale.
|
|
5
|
+
* `undefined` when any of the three values is not a plain decimal — a value
|
|
6
|
+
* that is present and unreadable is a failed read, not a zero balance.
|
|
7
|
+
*
|
|
8
|
+
* Every value is TRUNCATED to the share mint's own decimals on the way in.
|
|
9
|
+
* The endpoint reports up to 14 decimal places for a 6-decimal mint, and the
|
|
10
|
+
* extra digits are not a balance anyone can spend: handing the reported
|
|
11
|
+
* string straight back as an amount asks for more than the position holds,
|
|
12
|
+
* which the API rewrites to its withdraw-everything sentinel.
|
|
13
|
+
*/
|
|
14
|
+
export const parseKaminoSharePosition = ({ position, shareDecimals, }) => {
|
|
15
|
+
const staked = kaminoShareAmountFromDecimalString(position.stakedShares, shareDecimals);
|
|
16
|
+
const unstaked = kaminoShareAmountFromDecimalString(position.unstakedShares, shareDecimals);
|
|
17
|
+
const total = kaminoShareAmountFromDecimalString(position.totalShares, shareDecimals);
|
|
18
|
+
const spendable = spendableShares(position.totalShares, shareDecimals);
|
|
19
|
+
if (!staked || !unstaked || !total || !spendable)
|
|
20
|
+
return undefined;
|
|
21
|
+
const reportedSum = sumKaminoRates(position.stakedShares, position.unstakedShares);
|
|
22
|
+
const reportedTotal = parseKaminoRate(position.totalShares);
|
|
23
|
+
return {
|
|
24
|
+
staked,
|
|
25
|
+
unstaked,
|
|
26
|
+
total,
|
|
27
|
+
spendable,
|
|
28
|
+
accountsForItsTotal: reportedSum ? kaminoRateEquals(reportedSum, position.totalShares) : false,
|
|
29
|
+
// The amounts above parsed, so the parts are non-negative; what remains to
|
|
30
|
+
// check is that together they do not exceed the total.
|
|
31
|
+
isPlausible: reportedSum !== undefined && reportedTotal !== undefined && compareKaminoRates(reportedSum, reportedTotal) <= 0,
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* The spendable rule: truncate to the mint's scale, and give up one base unit
|
|
36
|
+
* only when the truncation was exact (see `KaminoSharePosition.spendable`).
|
|
37
|
+
* Refusing the sentinel is the point of the whole withdraw guard, so it is
|
|
38
|
+
* never accepted to make a 100% withdraw work — the dust is the price of the
|
|
39
|
+
* sentinel being indistinguishable, in the bytes, from an amount the user
|
|
40
|
+
* never asked for.
|
|
41
|
+
*/
|
|
42
|
+
const spendableShares = (totalShares, shareDecimals) => {
|
|
43
|
+
const rate = parseKaminoRate(totalShares);
|
|
44
|
+
if (!rate)
|
|
45
|
+
return undefined;
|
|
46
|
+
const scaled = scaleKaminoRate({ rate, toDecimals: shareDecimals });
|
|
47
|
+
if (!scaled)
|
|
48
|
+
return undefined;
|
|
49
|
+
const baseUnits = scaled.isExact ? scaled.baseUnits - 1n : scaled.baseUnits;
|
|
50
|
+
return kaminoShareAmount(baseUnits < 0n ? 0n : baseUnits, shareDecimals);
|
|
51
|
+
};
|
|
52
|
+
//# sourceMappingURL=position.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"position.js","sourceRoot":"","sources":["../../../../../../../packages/core/chain/chains/solana/kamino/position.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,iBAAiB,EAAE,kCAAkC,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAEpH,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AAgE9F;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,EACvC,QAAQ,EACR,aAAa,GAId,EAAmC,EAAE;IACpC,MAAM,MAAM,GAAG,kCAAkC,CAAC,QAAQ,CAAC,YAAY,EAAE,aAAa,CAAC,CAAA;IACvF,MAAM,QAAQ,GAAG,kCAAkC,CAAC,QAAQ,CAAC,cAAc,EAAE,aAAa,CAAC,CAAA;IAC3F,MAAM,KAAK,GAAG,kCAAkC,CAAC,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,CAAA;IACrF,MAAM,SAAS,GAAG,eAAe,CAAC,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,CAAA;IACtE,IAAI,CAAC,MAAM,IAAI,CAAC,QAAQ,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS;QAAE,OAAO,SAAS,CAAA;IAElE,MAAM,WAAW,GAAG,cAAc,CAAC,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAA;IAClF,MAAM,aAAa,GAAG,eAAe,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAA;IAE3D,OAAO;QACL,MAAM;QACN,QAAQ;QACR,KAAK;QACL,SAAS;QACT,mBAAmB,EAAE,WAAW,CAAC,CAAC,CAAC,gBAAgB,CAAC,WAAW,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,KAAK;QAC9F,2EAA2E;QAC3E,uDAAuD;QACvD,WAAW,EACT,WAAW,KAAK,SAAS,IAAI,aAAa,KAAK,SAAS,IAAI,kBAAkB,CAAC,WAAW,EAAE,aAAa,CAAC,IAAI,CAAC;KAClH,CAAA;AACH,CAAC,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,eAAe,GAAG,CAAC,WAAmB,EAAE,aAAqB,EAAiC,EAAE;IACpG,MAAM,IAAI,GAAG,eAAe,CAAC,WAAW,CAAC,CAAA;IACzC,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAA;IAC3B,MAAM,MAAM,GAAG,eAAe,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,aAAa,EAAE,CAAC,CAAA;IACnE,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAA;IAE7B,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAA;IAC3E,OAAO,iBAAiB,CAAC,SAAS,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,aAAa,CAAC,CAAA;AAC1E,CAAC,CAAA"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An exact decimal value from the API, held as `numerator / 10^scale`.
|
|
3
|
+
*
|
|
4
|
+
* This exists because floating-point (and any fixed-precision decimal)
|
|
5
|
+
* arithmetic rounds. A share rate drives how many shares a withdraw burns, and
|
|
6
|
+
* the API does not validate amounts — it rewrites an over-sized withdraw to
|
|
7
|
+
* `u64::MAX`, meaning *withdraw everything*. A division that rounded up at the
|
|
8
|
+
* far end of the mantissa could therefore turn a partial withdraw into a full
|
|
9
|
+
* exit. Integer arithmetic removes the question.
|
|
10
|
+
*/
|
|
11
|
+
export type KaminoRate = {
|
|
12
|
+
numerator: bigint;
|
|
13
|
+
/** Denominator exponent: the value is `numerator / 10^scale`. */
|
|
14
|
+
scale: number;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Parses Kamino's decimal-string form exactly, with no intermediate binary or
|
|
18
|
+
* fixed-precision representation. `undefined` when the string is not a plain
|
|
19
|
+
* decimal.
|
|
20
|
+
*/
|
|
21
|
+
export declare const parseKaminoRate: (raw: string) => KaminoRate | undefined;
|
|
22
|
+
/** Whether the rate names a value strictly above zero. */
|
|
23
|
+
export declare const isPositiveKaminoRate: (rate: KaminoRate) => boolean;
|
|
24
|
+
/**
|
|
25
|
+
* This value's numerator at `targetScale` decimal places, or `undefined` when
|
|
26
|
+
* the target is too coarse to hold it without loss.
|
|
27
|
+
*/
|
|
28
|
+
export declare const kaminoRateNumeratorAtScale: (rate: KaminoRate, targetScale: number) => bigint | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* The exact sum of two API decimal strings, or `undefined` when either is not
|
|
31
|
+
* one.
|
|
32
|
+
*
|
|
33
|
+
* Exists so two reported figures can be added at the precision they were
|
|
34
|
+
* reported at, rather than after each has been truncated to a mint's scale.
|
|
35
|
+
* See `parseKaminoSharePosition` for why that distinction is the difference
|
|
36
|
+
* between a guard and a false refusal.
|
|
37
|
+
*/
|
|
38
|
+
export declare const sumKaminoRates: (lhs: string, rhs: string) => KaminoRate | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Whether `raw` names exactly the same number as `rate`. `false` when it is
|
|
41
|
+
* not a plain decimal at all — an unreadable figure is never equal to a
|
|
42
|
+
* readable one.
|
|
43
|
+
*/
|
|
44
|
+
export declare const kaminoRateEquals: (rate: KaminoRate, raw: string) => boolean;
|
|
45
|
+
/**
|
|
46
|
+
* Orders two exact rates: negative when `lhs < rhs`, zero when equal,
|
|
47
|
+
* positive when `lhs > rhs`. Both values are brought to a common scale first,
|
|
48
|
+
* so the comparison happens at full reported precision rather than after a
|
|
49
|
+
* truncation that could mask a one-base-unit difference.
|
|
50
|
+
*/
|
|
51
|
+
export declare const compareKaminoRates: (lhs: KaminoRate, rhs: KaminoRate) => number;
|
|
52
|
+
/**
|
|
53
|
+
* Renders the rate back to its plain decimal-string form (no trailing zeros,
|
|
54
|
+
* no exponent).
|
|
55
|
+
*/
|
|
56
|
+
export declare const renderKaminoRate: (rate: KaminoRate) => string;
|
|
57
|
+
//# sourceMappingURL=rate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate.d.ts","sourceRoot":"","sources":["../../../../../../../packages/core/chain/chains/solana/kamino/rate.ts"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,SAAS,EAAE,MAAM,CAAA;IACjB,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,GAAI,KAAK,MAAM,KAAG,UAAU,GAAG,SAgB1D,CAAA;AAED,0DAA0D;AAC1D,eAAO,MAAM,oBAAoB,GAAI,MAAM,UAAU,KAAG,OAA8B,CAAA;AAEtF;;;GAGG;AACH,eAAO,MAAM,0BAA0B,GAAI,MAAM,UAAU,EAAE,aAAa,MAAM,KAAG,MAAM,GAAG,SAG3F,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GAAI,KAAK,MAAM,EAAE,KAAK,MAAM,KAAG,UAAU,GAAG,SAWtE,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,GAAI,MAAM,UAAU,EAAE,KAAK,MAAM,KAAG,OAQhE,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,KAAK,UAAU,EAAE,KAAK,UAAU,KAAG,MAMrE,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,gBAAgB,GAAI,MAAM,UAAU,KAAG,MACwB,CAAA"}
|