@vultisig/core-chain 1.1.0 → 1.2.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 +42 -0
- package/dist/amount/toChainAmount.d.ts +6 -1
- package/dist/amount/toChainAmount.d.ts.map +1 -1
- package/dist/amount/toChainAmount.js +88 -0
- package/dist/amount/toChainAmount.js.map +1 -1
- package/dist/chains/cosmos/qbtc/claim/BtcAddressType.d.ts +7 -0
- package/dist/chains/cosmos/qbtc/claim/BtcAddressType.d.ts.map +1 -0
- package/dist/chains/cosmos/qbtc/claim/BtcAddressType.js +9 -0
- package/dist/chains/cosmos/qbtc/claim/BtcAddressType.js.map +1 -0
- package/dist/chains/cosmos/qbtc/claim/ClaimableUtxo.d.ts +8 -0
- package/dist/chains/cosmos/qbtc/claim/ClaimableUtxo.d.ts.map +1 -0
- package/dist/chains/cosmos/qbtc/claim/ClaimableUtxo.js +2 -0
- package/dist/chains/cosmos/qbtc/claim/ClaimableUtxo.js.map +1 -0
- package/dist/chains/cosmos/qbtc/claim/broadcastClaimTx.d.ts +25 -0
- package/dist/chains/cosmos/qbtc/claim/broadcastClaimTx.d.ts.map +1 -0
- package/dist/chains/cosmos/qbtc/claim/broadcastClaimTx.js +43 -0
- package/dist/chains/cosmos/qbtc/claim/broadcastClaimTx.js.map +1 -0
- package/dist/chains/cosmos/qbtc/claim/buildClaimTx.d.ts +26 -0
- package/dist/chains/cosmos/qbtc/claim/buildClaimTx.d.ts.map +1 -0
- package/dist/chains/cosmos/qbtc/claim/buildClaimTx.js +57 -0
- package/dist/chains/cosmos/qbtc/claim/buildClaimTx.js.map +1 -0
- package/dist/chains/cosmos/qbtc/claim/computeClaimHashes.d.ts +49 -0
- package/dist/chains/cosmos/qbtc/claim/computeClaimHashes.d.ts.map +1 -0
- package/dist/chains/cosmos/qbtc/claim/computeClaimHashes.js +85 -0
- package/dist/chains/cosmos/qbtc/claim/computeClaimHashes.js.map +1 -0
- package/dist/chains/cosmos/qbtc/claim/detectBtcAddressType.d.ts +14 -0
- package/dist/chains/cosmos/qbtc/claim/detectBtcAddressType.d.ts.map +1 -0
- package/dist/chains/cosmos/qbtc/claim/detectBtcAddressType.js +31 -0
- package/dist/chains/cosmos/qbtc/claim/detectBtcAddressType.js.map +1 -0
- package/dist/chains/cosmos/qbtc/claim/getClaimWithProofDisabled.d.ts +3 -0
- package/dist/chains/cosmos/qbtc/claim/getClaimWithProofDisabled.d.ts.map +1 -0
- package/dist/chains/cosmos/qbtc/claim/getClaimWithProofDisabled.js +13 -0
- package/dist/chains/cosmos/qbtc/claim/getClaimWithProofDisabled.js.map +1 -0
- package/dist/chains/cosmos/qbtc/claim/getClaimableUtxos.d.ts +14 -0
- package/dist/chains/cosmos/qbtc/claim/getClaimableUtxos.d.ts.map +1 -0
- package/dist/chains/cosmos/qbtc/claim/getClaimableUtxos.js +22 -0
- package/dist/chains/cosmos/qbtc/claim/getClaimableUtxos.js.map +1 -0
- package/dist/chains/cosmos/qbtc/claim/proofService.d.ts +50 -0
- package/dist/chains/cosmos/qbtc/claim/proofService.d.ts.map +1 -0
- package/dist/chains/cosmos/qbtc/claim/proofService.js +71 -0
- package/dist/chains/cosmos/qbtc/claim/proofService.js.map +1 -0
- package/dist/chains/cosmos/thor/lp/halts.d.ts +56 -0
- package/dist/chains/cosmos/thor/lp/halts.d.ts.map +1 -0
- package/dist/chains/cosmos/thor/lp/halts.js +95 -0
- package/dist/chains/cosmos/thor/lp/halts.js.map +1 -0
- package/dist/chains/cosmos/thor/lp/index.d.ts +34 -11
- package/dist/chains/cosmos/thor/lp/index.d.ts.map +1 -1
- package/dist/chains/cosmos/thor/lp/index.js +9 -15
- package/dist/chains/cosmos/thor/lp/index.js.map +1 -1
- package/dist/chains/cosmos/thor/lp/lockup.d.ts +47 -0
- package/dist/chains/cosmos/thor/lp/lockup.d.ts.map +1 -0
- package/dist/chains/cosmos/thor/lp/lockup.js +56 -0
- package/dist/chains/cosmos/thor/lp/lockup.js.map +1 -0
- package/dist/chains/cosmos/thor/lp/lpChainMap.d.ts +25 -0
- package/dist/chains/cosmos/thor/lp/lpChainMap.d.ts.map +1 -0
- package/dist/chains/cosmos/thor/lp/lpChainMap.js +30 -0
- package/dist/chains/cosmos/thor/lp/lpChainMap.js.map +1 -0
- package/dist/chains/cosmos/thor/lp/math.d.ts +129 -0
- package/dist/chains/cosmos/thor/lp/math.d.ts.map +1 -0
- package/dist/chains/cosmos/thor/lp/math.js +227 -0
- package/dist/chains/cosmos/thor/lp/math.js.map +1 -0
- package/dist/chains/cosmos/thor/lp/memberPool.d.ts +4 -0
- package/dist/chains/cosmos/thor/lp/memberPool.d.ts.map +1 -0
- package/dist/chains/cosmos/thor/lp/memberPool.js +24 -0
- package/dist/chains/cosmos/thor/lp/memberPool.js.map +1 -0
- package/dist/chains/cosmos/thor/lp/memo.d.ts +38 -17
- package/dist/chains/cosmos/thor/lp/memo.d.ts.map +1 -1
- package/dist/chains/cosmos/thor/lp/memo.js +40 -16
- package/dist/chains/cosmos/thor/lp/memo.js.map +1 -1
- package/dist/chains/cosmos/thor/lp/pairing.d.ts +30 -0
- package/dist/chains/cosmos/thor/lp/pairing.d.ts.map +1 -0
- package/dist/chains/cosmos/thor/lp/pairing.js +44 -0
- package/dist/chains/cosmos/thor/lp/pairing.js.map +1 -0
- package/dist/chains/cosmos/thor/lp/payload.d.ts +30 -10
- package/dist/chains/cosmos/thor/lp/payload.d.ts.map +1 -1
- package/dist/chains/cosmos/thor/lp/payload.js +30 -18
- package/dist/chains/cosmos/thor/lp/payload.js.map +1 -1
- package/dist/chains/cosmos/thor/lp/position.d.ts +15 -22
- package/dist/chains/cosmos/thor/lp/position.d.ts.map +1 -1
- package/dist/chains/cosmos/thor/lp/position.js +82 -35
- package/dist/chains/cosmos/thor/lp/position.js.map +1 -1
- package/dist/chains/cosmos/thor/lp/positions.d.ts +15 -0
- package/dist/chains/cosmos/thor/lp/positions.d.ts.map +1 -0
- package/dist/chains/cosmos/thor/lp/positions.js +47 -0
- package/dist/chains/cosmos/thor/lp/positions.js.map +1 -0
- package/dist/chains/cosmos/thor/lp/types.d.ts +45 -0
- package/dist/chains/cosmos/thor/lp/types.d.ts.map +1 -0
- package/dist/chains/cosmos/thor/lp/types.js +2 -0
- package/dist/chains/cosmos/thor/lp/types.js.map +1 -0
- package/dist/chains/cosmos/thor/lp/validation.d.ts +32 -7
- package/dist/chains/cosmos/thor/lp/validation.d.ts.map +1 -1
- package/dist/chains/cosmos/thor/lp/validation.js +76 -11
- package/dist/chains/cosmos/thor/lp/validation.js.map +1 -1
- package/dist/chains/utxo/tx/buildSignBitcoinFromPsbt.d.ts +21 -0
- package/dist/chains/utxo/tx/buildSignBitcoinFromPsbt.d.ts.map +1 -0
- package/dist/chains/utxo/tx/buildSignBitcoinFromPsbt.js +182 -0
- package/dist/chains/utxo/tx/buildSignBitcoinFromPsbt.js.map +1 -0
- package/package.json +90 -5
- package/dist/chains/cosmos/thor/lp/affiliate.d.ts +0 -16
- package/dist/chains/cosmos/thor/lp/affiliate.d.ts.map +0 -1
- package/dist/chains/cosmos/thor/lp/affiliate.js +0 -16
- package/dist/chains/cosmos/thor/lp/affiliate.js.map +0 -1
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal pool state required for LP math. All values are base-unit
|
|
3
|
+
* strings at THORChain's fixed 1e8 precision — these are the shapes
|
|
4
|
+
* returned by thornode `/thorchain/pool/{asset}` (`balance_asset`,
|
|
5
|
+
* `balance_rune`, `pool_units`).
|
|
6
|
+
*/
|
|
7
|
+
export type PoolState = {
|
|
8
|
+
assetDepth: string;
|
|
9
|
+
runeDepth: string;
|
|
10
|
+
poolUnits: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Calculate the liquidity units earned for a deposit.
|
|
14
|
+
*
|
|
15
|
+
* Formula: `units = P * (R*a + r*A) / (2 * R * A)`
|
|
16
|
+
*
|
|
17
|
+
* P = current pool units
|
|
18
|
+
* r = RUNE deposited (base units)
|
|
19
|
+
* a = asset deposited (base units)
|
|
20
|
+
* R = current pool RUNE depth
|
|
21
|
+
* A = current pool asset depth
|
|
22
|
+
*
|
|
23
|
+
* Source: docs.thorchain.org continuous-liquidity-pools.md and the
|
|
24
|
+
* THORChain dev handbook. Implemented from the canonical formula, not
|
|
25
|
+
* copied from any third-party codebase. The on-chain handler multiplies
|
|
26
|
+
* by a slip-adjustment term (`1 - |rA - aR| / (rA + aR)`) before minting
|
|
27
|
+
* the final units; this helper intentionally omits that adjustment so UIs
|
|
28
|
+
* can show a quick estimate. For asymmetric adds the simplified number
|
|
29
|
+
* can diverge a few percent from mainnet — note that in UX copy if you
|
|
30
|
+
* display it directly.
|
|
31
|
+
*
|
|
32
|
+
* For an asymmetric deposit either `r` or `a` is zero — the formula still
|
|
33
|
+
* holds (the internal 50/50 rebalancing happens on-chain and is reflected
|
|
34
|
+
* in the returned units via the pool's existing depth ratio).
|
|
35
|
+
*
|
|
36
|
+
* All inputs / outputs are in 1e8 base units. Returns a non-negative
|
|
37
|
+
* BigInt-safe integer string.
|
|
38
|
+
*/
|
|
39
|
+
export declare const getLiquidityUnits: ({ pool, assetAmountBaseUnit, runeAmountBaseUnit, }: {
|
|
40
|
+
pool: PoolState;
|
|
41
|
+
assetAmountBaseUnit: string;
|
|
42
|
+
runeAmountBaseUnit: string;
|
|
43
|
+
}) => string;
|
|
44
|
+
/**
|
|
45
|
+
* Calculate the user's fractional share of a pool AFTER a deposit settles.
|
|
46
|
+
*
|
|
47
|
+
* Returns only the decimal share (`units / (poolUnits + units)`). The
|
|
48
|
+
* rune/asset base-unit shares are NOT computed here because the correct
|
|
49
|
+
* values depend on the post-deposit pool depths, which this helper does
|
|
50
|
+
* not take as inputs. For those values, use `estimateLpAdd` which has
|
|
51
|
+
* the full pool state and deposit amounts.
|
|
52
|
+
*
|
|
53
|
+
* For display only — the on-chain accounting uses the units directly.
|
|
54
|
+
*/
|
|
55
|
+
export declare const getPoolShare: ({ pool, liquidityUnits, }: {
|
|
56
|
+
pool: PoolState;
|
|
57
|
+
liquidityUnits: string;
|
|
58
|
+
}) => {
|
|
59
|
+
poolShareDecimal: string;
|
|
60
|
+
};
|
|
61
|
+
export type SlippageResult = {
|
|
62
|
+
/**
|
|
63
|
+
* Slippage as a decimal string, e.g. `"0.0032"` = 0.32%. Always
|
|
64
|
+
* non-negative. Zero for symmetric deposits (balanced r/a against R/A).
|
|
65
|
+
*/
|
|
66
|
+
decimalPercent: string;
|
|
67
|
+
/**
|
|
68
|
+
* Slippage expressed in RUNE base units, for display. For asym-asset
|
|
69
|
+
* deposits this is the RUNE-equivalent of the lost value.
|
|
70
|
+
*/
|
|
71
|
+
slippageInRuneBaseUnit: string;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Calculate slippage for an LP add.
|
|
75
|
+
*
|
|
76
|
+
* Formula: `slip = |R*a - A*r| / (A*r + R*A)`
|
|
77
|
+
*
|
|
78
|
+
* This is the asym-rebalancing slip cost: when only one side is deposited,
|
|
79
|
+
* THORChain internally performs a 50/50 swap to balance the pool, and
|
|
80
|
+
* that swap incurs a slip cost proportional to how imbalanced the input
|
|
81
|
+
* is against the existing depth.
|
|
82
|
+
*
|
|
83
|
+
* For symmetric deposits (r/a ratio matches R/A), the numerator is zero
|
|
84
|
+
* and the slippage is exactly zero.
|
|
85
|
+
*
|
|
86
|
+
* Source: derived from the THORChain asymmetric-deposit-as-swap
|
|
87
|
+
* documentation. Cross-checked against the formula used by multiple
|
|
88
|
+
* independent implementations (iOS / extension don't compute this in
|
|
89
|
+
* their UI — they leave it to the chain).
|
|
90
|
+
*/
|
|
91
|
+
export declare const getLpAddSlippage: ({ pool, assetAmountBaseUnit, runeAmountBaseUnit, }: {
|
|
92
|
+
pool: PoolState;
|
|
93
|
+
assetAmountBaseUnit: string;
|
|
94
|
+
runeAmountBaseUnit: string;
|
|
95
|
+
}) => SlippageResult;
|
|
96
|
+
export type EstimateLpAddResult = {
|
|
97
|
+
liquidityUnits: string;
|
|
98
|
+
poolShareDecimal: string;
|
|
99
|
+
/**
|
|
100
|
+
* Estimated rune-denominated value of the user's pool share, using
|
|
101
|
+
* post-deposit (pre-internal-swap) pool depths. This is the amount
|
|
102
|
+
* they could expect to reclaim on a full withdraw if nothing else
|
|
103
|
+
* changed.
|
|
104
|
+
*/
|
|
105
|
+
runeShareBaseUnit: string;
|
|
106
|
+
/** Estimated asset-denominated value of the user's pool share. */
|
|
107
|
+
assetShareBaseUnit: string;
|
|
108
|
+
slippageDecimal: string;
|
|
109
|
+
slippageRuneBaseUnit: string;
|
|
110
|
+
};
|
|
111
|
+
/**
|
|
112
|
+
* One-shot estimator that chains pool-state fetch + the three math
|
|
113
|
+
* helpers. Returns everything a UI needs to surface a quote before the
|
|
114
|
+
* user signs.
|
|
115
|
+
*
|
|
116
|
+
* Fetches from thornode `/thorchain/pool/{asset}`. Injectable
|
|
117
|
+
* `fetchImpl` for tests.
|
|
118
|
+
*/
|
|
119
|
+
export declare const estimateLpAdd: ({ pool, assetAmountBaseUnit, runeAmountBaseUnit, thornodeBaseUrl, }: {
|
|
120
|
+
pool: string;
|
|
121
|
+
assetAmountBaseUnit: string;
|
|
122
|
+
runeAmountBaseUnit: string;
|
|
123
|
+
/**
|
|
124
|
+
* Override for the thornode base URL. Defaults to the shared
|
|
125
|
+
* `cosmosRpcUrl[Chain.THORChain]` used by the rest of the lp module.
|
|
126
|
+
*/
|
|
127
|
+
thornodeBaseUrl?: string;
|
|
128
|
+
}) => Promise<EstimateLpAddResult>;
|
|
129
|
+
//# sourceMappingURL=math.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"math.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/chain/chains/cosmos/thor/lp/math.ts"],"names":[],"mappings":"AAMA;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAyBD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,iBAAiB,GAAI,oDAI/B;IACD,IAAI,EAAE,SAAS,CAAA;IACf,mBAAmB,EAAE,MAAM,CAAA;IAC3B,kBAAkB,EAAE,MAAM,CAAA;CAC3B,KAAG,MAkBH,CAAA;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,YAAY,GAAI,2BAG1B;IACD,IAAI,EAAE,SAAS,CAAA;IACf,cAAc,EAAE,MAAM,CAAA;CACvB,KAAG;IACF,gBAAgB,EAAE,MAAM,CAAA;CAsBzB,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B;;;OAGG;IACH,cAAc,EAAE,MAAM,CAAA;IACtB;;;OAGG;IACH,sBAAsB,EAAE,MAAM,CAAA;CAC/B,CAAA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,gBAAgB,GAAI,oDAI9B;IACD,IAAI,EAAE,SAAS,CAAA;IACf,mBAAmB,EAAE,MAAM,CAAA;IAC3B,kBAAkB,EAAE,MAAM,CAAA;CAC3B,KAAG,cAqDH,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,cAAc,EAAE,MAAM,CAAA;IACtB,gBAAgB,EAAE,MAAM,CAAA;IACxB;;;;;OAKG;IACH,iBAAiB,EAAE,MAAM,CAAA;IACzB,kEAAkE;IAClE,kBAAkB,EAAE,MAAM,CAAA;IAC1B,eAAe,EAAE,MAAM,CAAA;IACvB,oBAAoB,EAAE,MAAM,CAAA;CAC7B,CAAA;AASD;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,GAAU,qEAKjC;IACD,IAAI,EAAE,MAAM,CAAA;IACZ,mBAAmB,EAAE,MAAM,CAAA;IAC3B,kBAAkB,EAAE,MAAM,CAAA;IAC1B;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB,KAAG,OAAO,CAAC,mBAAmB,CAyE9B,CAAA"}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { Chain } from '@vultisig/core-chain/Chain';
|
|
2
|
+
import { cosmosRpcUrl } from '@vultisig/core-chain/chains/cosmos/cosmosRpcUrl';
|
|
3
|
+
import { queryUrl } from '@vultisig/lib-utils/query/queryUrl';
|
|
4
|
+
import { assertValidPoolId } from './pools.js';
|
|
5
|
+
/**
|
|
6
|
+
* Assert a numeric input is a non-negative base-unit integer string.
|
|
7
|
+
*
|
|
8
|
+
* Public math helpers consume untrusted strings (LLM tool args, API
|
|
9
|
+
* responses). Raw `BigInt('abc')` throws `SyntaxError: Cannot convert
|
|
10
|
+
* abc to a BigInt`, which is an unhelpful error for callers and hides
|
|
11
|
+
* the field name. This validator produces stable SDK-level errors
|
|
12
|
+
* instead.
|
|
13
|
+
*/
|
|
14
|
+
const assertBaseUnitString = (value, fieldName) => {
|
|
15
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
16
|
+
throw new Error(`${fieldName} must be a non-empty base-unit string, got ${typeof value === 'string' ? JSON.stringify(value) : typeof value}`);
|
|
17
|
+
}
|
|
18
|
+
if (!/^\d+$/.test(value)) {
|
|
19
|
+
throw new Error(`${fieldName} must be a non-negative integer base-unit string, got ${JSON.stringify(value)}`);
|
|
20
|
+
}
|
|
21
|
+
return BigInt(value);
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Calculate the liquidity units earned for a deposit.
|
|
25
|
+
*
|
|
26
|
+
* Formula: `units = P * (R*a + r*A) / (2 * R * A)`
|
|
27
|
+
*
|
|
28
|
+
* P = current pool units
|
|
29
|
+
* r = RUNE deposited (base units)
|
|
30
|
+
* a = asset deposited (base units)
|
|
31
|
+
* R = current pool RUNE depth
|
|
32
|
+
* A = current pool asset depth
|
|
33
|
+
*
|
|
34
|
+
* Source: docs.thorchain.org continuous-liquidity-pools.md and the
|
|
35
|
+
* THORChain dev handbook. Implemented from the canonical formula, not
|
|
36
|
+
* copied from any third-party codebase. The on-chain handler multiplies
|
|
37
|
+
* by a slip-adjustment term (`1 - |rA - aR| / (rA + aR)`) before minting
|
|
38
|
+
* the final units; this helper intentionally omits that adjustment so UIs
|
|
39
|
+
* can show a quick estimate. For asymmetric adds the simplified number
|
|
40
|
+
* can diverge a few percent from mainnet — note that in UX copy if you
|
|
41
|
+
* display it directly.
|
|
42
|
+
*
|
|
43
|
+
* For an asymmetric deposit either `r` or `a` is zero — the formula still
|
|
44
|
+
* holds (the internal 50/50 rebalancing happens on-chain and is reflected
|
|
45
|
+
* in the returned units via the pool's existing depth ratio).
|
|
46
|
+
*
|
|
47
|
+
* All inputs / outputs are in 1e8 base units. Returns a non-negative
|
|
48
|
+
* BigInt-safe integer string.
|
|
49
|
+
*/
|
|
50
|
+
export const getLiquidityUnits = ({ pool, assetAmountBaseUnit, runeAmountBaseUnit, }) => {
|
|
51
|
+
const P = assertBaseUnitString(pool.poolUnits, 'pool.poolUnits');
|
|
52
|
+
const R = assertBaseUnitString(pool.runeDepth, 'pool.runeDepth');
|
|
53
|
+
const A = assertBaseUnitString(pool.assetDepth, 'pool.assetDepth');
|
|
54
|
+
const r = assertBaseUnitString(runeAmountBaseUnit, 'runeAmountBaseUnit');
|
|
55
|
+
const a = assertBaseUnitString(assetAmountBaseUnit, 'assetAmountBaseUnit');
|
|
56
|
+
if (R === 0n || A === 0n || P === 0n) {
|
|
57
|
+
// Empty / just-initialized pool — the first-deposit case is handled
|
|
58
|
+
// differently on-chain (the depositor mints the full initial unit
|
|
59
|
+
// supply). We can't model that here without knowing the genesis
|
|
60
|
+
// unit scale, so return 0 and let the caller decide what to do.
|
|
61
|
+
return '0';
|
|
62
|
+
}
|
|
63
|
+
const numerator = P * (R * a + r * A);
|
|
64
|
+
const denominator = 2n * R * A;
|
|
65
|
+
return (numerator / denominator).toString();
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Calculate the user's fractional share of a pool AFTER a deposit settles.
|
|
69
|
+
*
|
|
70
|
+
* Returns only the decimal share (`units / (poolUnits + units)`). The
|
|
71
|
+
* rune/asset base-unit shares are NOT computed here because the correct
|
|
72
|
+
* values depend on the post-deposit pool depths, which this helper does
|
|
73
|
+
* not take as inputs. For those values, use `estimateLpAdd` which has
|
|
74
|
+
* the full pool state and deposit amounts.
|
|
75
|
+
*
|
|
76
|
+
* For display only — the on-chain accounting uses the units directly.
|
|
77
|
+
*/
|
|
78
|
+
export const getPoolShare = ({ pool, liquidityUnits, }) => {
|
|
79
|
+
const P = assertBaseUnitString(pool.poolUnits, 'pool.poolUnits');
|
|
80
|
+
const L = assertBaseUnitString(liquidityUnits, 'liquidityUnits');
|
|
81
|
+
if (P === 0n || L === 0n) {
|
|
82
|
+
return { poolShareDecimal: '0' };
|
|
83
|
+
}
|
|
84
|
+
const totalAfter = P + L;
|
|
85
|
+
// Decimal share with 18-digit precision as a string (no floats).
|
|
86
|
+
// We multiply by 1e18, divide, then format as "0.xxx".
|
|
87
|
+
const SCALE = 10n ** 18n;
|
|
88
|
+
const scaled = (L * SCALE) / totalAfter;
|
|
89
|
+
const decimal = scaled.toString().padStart(19, '0'); // at least 18 fractional digits
|
|
90
|
+
const intPart = decimal.slice(0, -18) || '0';
|
|
91
|
+
const fracPart = decimal.slice(-18).replace(/0+$/, '');
|
|
92
|
+
return {
|
|
93
|
+
poolShareDecimal: fracPart.length > 0 ? `${intPart}.${fracPart}` : intPart,
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* Calculate slippage for an LP add.
|
|
98
|
+
*
|
|
99
|
+
* Formula: `slip = |R*a - A*r| / (A*r + R*A)`
|
|
100
|
+
*
|
|
101
|
+
* This is the asym-rebalancing slip cost: when only one side is deposited,
|
|
102
|
+
* THORChain internally performs a 50/50 swap to balance the pool, and
|
|
103
|
+
* that swap incurs a slip cost proportional to how imbalanced the input
|
|
104
|
+
* is against the existing depth.
|
|
105
|
+
*
|
|
106
|
+
* For symmetric deposits (r/a ratio matches R/A), the numerator is zero
|
|
107
|
+
* and the slippage is exactly zero.
|
|
108
|
+
*
|
|
109
|
+
* Source: derived from the THORChain asymmetric-deposit-as-swap
|
|
110
|
+
* documentation. Cross-checked against the formula used by multiple
|
|
111
|
+
* independent implementations (iOS / extension don't compute this in
|
|
112
|
+
* their UI — they leave it to the chain).
|
|
113
|
+
*/
|
|
114
|
+
export const getLpAddSlippage = ({ pool, assetAmountBaseUnit, runeAmountBaseUnit, }) => {
|
|
115
|
+
const R = assertBaseUnitString(pool.runeDepth, 'pool.runeDepth');
|
|
116
|
+
const A = assertBaseUnitString(pool.assetDepth, 'pool.assetDepth');
|
|
117
|
+
const r = assertBaseUnitString(runeAmountBaseUnit, 'runeAmountBaseUnit');
|
|
118
|
+
const a = assertBaseUnitString(assetAmountBaseUnit, 'assetAmountBaseUnit');
|
|
119
|
+
if (R === 0n || A === 0n) {
|
|
120
|
+
return { decimalPercent: '0', slippageInRuneBaseUnit: '0' };
|
|
121
|
+
}
|
|
122
|
+
// |R*a - A*r| / (A*r + R*A)
|
|
123
|
+
const ra = R * a;
|
|
124
|
+
const ar = A * r;
|
|
125
|
+
const numerator = ra > ar ? ra - ar : ar - ra;
|
|
126
|
+
const denominator = A * r + R * A;
|
|
127
|
+
if (denominator === 0n) {
|
|
128
|
+
return { decimalPercent: '0', slippageInRuneBaseUnit: '0' };
|
|
129
|
+
}
|
|
130
|
+
// Express as decimal with 18-digit precision
|
|
131
|
+
const SCALE = 10n ** 18n;
|
|
132
|
+
const scaled = (numerator * SCALE) / denominator;
|
|
133
|
+
const decimal = scaled.toString().padStart(19, '0');
|
|
134
|
+
const intPart = decimal.slice(0, -18) || '0';
|
|
135
|
+
const fracPart = decimal.slice(-18).replace(/0+$/, '');
|
|
136
|
+
const decimalPercent = fracPart.length > 0 ? `${intPart}.${fracPart}` : intPart;
|
|
137
|
+
// Convert to rune-equivalent for display. The slip applies only to the
|
|
138
|
+
// imbalanced portion of the deposit — the part that has to be swapped
|
|
139
|
+
// internally to balance the pool. We compute the imbalance in rune
|
|
140
|
+
// terms: |R*a - A*r| / (2*A), then apply the slip fraction to it.
|
|
141
|
+
//
|
|
142
|
+
// For pure asym RUNE (a=0): imbalance = R*0 - A*r divided by 2*A, abs
|
|
143
|
+
// → r/2 (half the deposit gets swapped) → slippageInRune ≈ (r/2) * slip
|
|
144
|
+
// For pure asym asset (r=0): imbalance = (R*a) / (2*A), the rune-value
|
|
145
|
+
// of half the deposit → slippageInRune ≈ (a*R / (2*A)) * slip
|
|
146
|
+
// For balanced deposit: imbalance = 0 → slippageInRune = 0
|
|
147
|
+
//
|
|
148
|
+
// This is a heuristic display value, not an exact chain computation.
|
|
149
|
+
// It under/overstates real on-chain slip by a constant factor in some
|
|
150
|
+
// regimes but is directionally correct and never overstates by orders
|
|
151
|
+
// of magnitude the way "slip * total deposit value" did.
|
|
152
|
+
const imbalanceNumerator = ra > ar ? ra - ar : ar - ra;
|
|
153
|
+
const imbalanceInRune = A === 0n ? 0n : imbalanceNumerator / (2n * A);
|
|
154
|
+
const slippageInRune = (imbalanceInRune * scaled) / SCALE;
|
|
155
|
+
return {
|
|
156
|
+
decimalPercent,
|
|
157
|
+
slippageInRuneBaseUnit: slippageInRune.toString(),
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
/**
|
|
161
|
+
* One-shot estimator that chains pool-state fetch + the three math
|
|
162
|
+
* helpers. Returns everything a UI needs to surface a quote before the
|
|
163
|
+
* user signs.
|
|
164
|
+
*
|
|
165
|
+
* Fetches from thornode `/thorchain/pool/{asset}`. Injectable
|
|
166
|
+
* `fetchImpl` for tests.
|
|
167
|
+
*/
|
|
168
|
+
export const estimateLpAdd = async ({ pool, assetAmountBaseUnit, runeAmountBaseUnit, thornodeBaseUrl, }) => {
|
|
169
|
+
assertValidPoolId(pool);
|
|
170
|
+
const base = thornodeBaseUrl ?? cosmosRpcUrl[Chain.THORChain];
|
|
171
|
+
const url = `${base}/thorchain/pool/${encodeURIComponent(pool)}`;
|
|
172
|
+
const raw = await queryUrl(url);
|
|
173
|
+
if (!raw ||
|
|
174
|
+
typeof raw !== 'object' ||
|
|
175
|
+
typeof raw.balance_asset !== 'string' ||
|
|
176
|
+
typeof raw.balance_rune !== 'string') {
|
|
177
|
+
throw new Error(`estimateLpAdd: pool ${pool} response from ${url} missing balance fields`);
|
|
178
|
+
}
|
|
179
|
+
const poolUnitsRaw = raw.pool_units ?? raw.LP_units;
|
|
180
|
+
if (typeof poolUnitsRaw !== 'string' || poolUnitsRaw.length === 0) {
|
|
181
|
+
throw new Error(`estimateLpAdd: pool ${pool} response from ${url} missing pool_units / LP_units`);
|
|
182
|
+
}
|
|
183
|
+
const poolState = {
|
|
184
|
+
assetDepth: raw.balance_asset,
|
|
185
|
+
runeDepth: raw.balance_rune,
|
|
186
|
+
poolUnits: poolUnitsRaw,
|
|
187
|
+
};
|
|
188
|
+
const liquidityUnits = getLiquidityUnits({
|
|
189
|
+
pool: poolState,
|
|
190
|
+
assetAmountBaseUnit,
|
|
191
|
+
runeAmountBaseUnit,
|
|
192
|
+
});
|
|
193
|
+
const share = getPoolShare({
|
|
194
|
+
pool: poolState,
|
|
195
|
+
liquidityUnits,
|
|
196
|
+
});
|
|
197
|
+
const slip = getLpAddSlippage({
|
|
198
|
+
pool: poolState,
|
|
199
|
+
assetAmountBaseUnit,
|
|
200
|
+
runeAmountBaseUnit,
|
|
201
|
+
});
|
|
202
|
+
// Compute post-deposit base-unit shares from the caller's deposit
|
|
203
|
+
// amounts. This is the correct frame for display ("you'll own X RUNE +
|
|
204
|
+
// Y asset's worth"). Uses post-deposit depths R+r and A+a, which is
|
|
205
|
+
// the pre-internal-swap state — close enough for display purposes and
|
|
206
|
+
// doesn't require simulating the chain's internal rebalancing swap.
|
|
207
|
+
const R = BigInt(poolState.runeDepth);
|
|
208
|
+
const A = BigInt(poolState.assetDepth);
|
|
209
|
+
const P = BigInt(poolState.poolUnits);
|
|
210
|
+
const r = BigInt(runeAmountBaseUnit);
|
|
211
|
+
const a = BigInt(assetAmountBaseUnit);
|
|
212
|
+
const L = BigInt(liquidityUnits);
|
|
213
|
+
const totalAfter = P + L;
|
|
214
|
+
const runeDepthAfter = R + r;
|
|
215
|
+
const assetDepthAfter = A + a;
|
|
216
|
+
const runeShareBaseUnit = totalAfter === 0n ? '0' : ((runeDepthAfter * L) / totalAfter).toString();
|
|
217
|
+
const assetShareBaseUnit = totalAfter === 0n ? '0' : ((assetDepthAfter * L) / totalAfter).toString();
|
|
218
|
+
return {
|
|
219
|
+
liquidityUnits,
|
|
220
|
+
poolShareDecimal: share.poolShareDecimal,
|
|
221
|
+
runeShareBaseUnit,
|
|
222
|
+
assetShareBaseUnit,
|
|
223
|
+
slippageDecimal: slip.decimalPercent,
|
|
224
|
+
slippageRuneBaseUnit: slip.slippageInRuneBaseUnit,
|
|
225
|
+
};
|
|
226
|
+
};
|
|
227
|
+
//# sourceMappingURL=math.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"math.js","sourceRoot":"","sources":["../../../../../../../../packages/core/chain/chains/cosmos/thor/lp/math.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RawMemberPool, ThorchainLpPosition } from './types.js';
|
|
2
|
+
export declare const isNonZeroBaseUnit: (value: string | undefined) => boolean;
|
|
3
|
+
export declare const normalizeMemberPool: (raw: RawMemberPool) => ThorchainLpPosition;
|
|
4
|
+
//# sourceMappingURL=memberPool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memberPool.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/chain/chains/cosmos/thor/lp/memberPool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAEjE,eAAO,MAAM,iBAAiB,GAAI,OAAO,MAAM,GAAG,SAAS,KAAG,OAO7D,CAAA;AAED,eAAO,MAAM,mBAAmB,GAC9B,KAAK,aAAa,KACjB,mBAaD,CAAA"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export const isNonZeroBaseUnit = (value) => {
|
|
2
|
+
if (!value)
|
|
3
|
+
return false;
|
|
4
|
+
try {
|
|
5
|
+
return BigInt(value) > 0n;
|
|
6
|
+
}
|
|
7
|
+
catch {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
export const normalizeMemberPool = (raw) => ({
|
|
12
|
+
pool: raw.pool ?? '',
|
|
13
|
+
liquidityUnits: raw.liquidityUnits ?? '0',
|
|
14
|
+
runeAdded: raw.runeAdded ?? '0',
|
|
15
|
+
assetAdded: raw.assetAdded ?? '0',
|
|
16
|
+
runePending: raw.runePending ?? '0',
|
|
17
|
+
assetPending: raw.assetPending ?? '0',
|
|
18
|
+
runeAddress: raw.runeAddress ?? '',
|
|
19
|
+
assetAddress: raw.assetAddress ?? '',
|
|
20
|
+
dateLastAdded: raw.dateLastAdded ?? '0',
|
|
21
|
+
lastAddHeight: '',
|
|
22
|
+
isPending: isNonZeroBaseUnit(raw.runePending) || isNonZeroBaseUnit(raw.assetPending),
|
|
23
|
+
});
|
|
24
|
+
//# sourceMappingURL=memberPool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memberPool.js","sourceRoot":"","sources":["../../../../../../../../packages/core/chain/chains/cosmos/thor/lp/memberPool.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,KAAyB,EAAW,EAAE;IACtE,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAA;IACxB,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CAAA;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAA;IACd,CAAC;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,GAAkB,EACG,EAAE,CAAC,CAAC;IACzB,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,EAAE;IACpB,cAAc,EAAE,GAAG,CAAC,cAAc,IAAI,GAAG;IACzC,SAAS,EAAE,GAAG,CAAC,SAAS,IAAI,GAAG;IAC/B,UAAU,EAAE,GAAG,CAAC,UAAU,IAAI,GAAG;IACjC,WAAW,EAAE,GAAG,CAAC,WAAW,IAAI,GAAG;IACnC,YAAY,EAAE,GAAG,CAAC,YAAY,IAAI,GAAG;IACrC,WAAW,EAAE,GAAG,CAAC,WAAW,IAAI,EAAE;IAClC,YAAY,EAAE,GAAG,CAAC,YAAY,IAAI,EAAE;IACpC,aAAa,EAAE,GAAG,CAAC,aAAa,IAAI,GAAG;IACvC,aAAa,EAAE,EAAE;IACjB,SAAS,EACP,iBAAiB,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,iBAAiB,CAAC,GAAG,CAAC,YAAY,CAAC;CAC5E,CAAC,CAAA"}
|
|
@@ -2,40 +2,61 @@ export type AddLpMemoInput = {
|
|
|
2
2
|
/** Canonical pool id, e.g. `BTC.BTC` or `ETH.USDC-0X...`. */
|
|
3
3
|
pool: string;
|
|
4
4
|
/**
|
|
5
|
-
* Paired L1 address.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Paired L1 address. When set, THORChain registers the deposit with a
|
|
6
|
+
* counterpart address on the other side of the pool. The paired address
|
|
7
|
+
* is the vault's address on the OTHER chain relative to the side being
|
|
8
|
+
* deposited:
|
|
9
|
+
*
|
|
10
|
+
* - For a RUNE-side add (depositing RUNE on THORChain), this is the
|
|
11
|
+
* vault's L1 address on the pool's asset chain (e.g., BTC.BTC → the
|
|
12
|
+
* vault's BTC address).
|
|
13
|
+
* - For an asset-side add (depositing L1 asset), this is the vault's
|
|
14
|
+
* THORChain address (`thor1...`).
|
|
15
|
+
*
|
|
16
|
+
* Matches the default behavior of vultisig-ios and vultisig-windows
|
|
17
|
+
* (the extension), which always auto-populate the paired address from
|
|
18
|
+
* the vault. Leave omitted for a pure asymmetric deposit with no
|
|
19
|
+
* paired-address registration.
|
|
8
20
|
*/
|
|
9
21
|
pairedAddress?: string;
|
|
10
|
-
/** Affiliate THORName. Defaults to `VULTISIG_AFFILIATE_NAME`. */
|
|
11
|
-
affiliate?: string;
|
|
12
|
-
/** Affiliate fee in basis points. Defaults to `VULTISIG_AFFILIATE_LP_BPS`. */
|
|
13
|
-
affiliateBps?: number;
|
|
14
22
|
};
|
|
15
23
|
/**
|
|
16
24
|
* Build a THORChain liquidity-pool add memo.
|
|
17
25
|
*
|
|
18
|
-
* Format: `+:POOL
|
|
26
|
+
* Format: `+:POOL` (pure asym, no paired address) or
|
|
27
|
+
* `+:POOL:PAIRED_ADDR` (when paired address is provided)
|
|
19
28
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
29
|
+
* Matches vultisig-ios `AddLPMemoData.memo` and
|
|
30
|
+
* vultisig-windows `memoGenerator` `add_thor_lp` output exactly — no
|
|
31
|
+
* affiliate suffix. The THORChain memo spec allows an affiliate via
|
|
32
|
+
* `+:POOL::AFFILIATE:BPS` but neither Vultisig native client ships it;
|
|
33
|
+
* we match the native behavior for wire-level consistency.
|
|
24
34
|
*/
|
|
25
|
-
export declare const addLpMemo: (
|
|
35
|
+
export declare const addLpMemo: ({ pool, pairedAddress, }: AddLpMemoInput) => string;
|
|
26
36
|
export type RemoveLpMemoInput = {
|
|
27
37
|
pool: string;
|
|
28
38
|
/** Withdraw fraction in basis points: 1..10000 (10000 = 100%). */
|
|
29
39
|
basisPoints: number;
|
|
40
|
+
/**
|
|
41
|
+
* Optional asymmetric-withdraw target. When set, THORChain sends the
|
|
42
|
+
* withdrawn value out to this side only (e.g., `withdrawToAsset: "BTC"`
|
|
43
|
+
* forces all output to BTC). When omitted, the protocol returns both
|
|
44
|
+
* sides proportionally for symmetric positions, or to the same side for
|
|
45
|
+
* asymmetric positions.
|
|
46
|
+
*
|
|
47
|
+
* Only the short asset ticker is used on the wire (e.g., `BTC`, not
|
|
48
|
+
* `BTC.BTC`). Pass the pool's ASSET section, not the full pool id.
|
|
49
|
+
*/
|
|
50
|
+
withdrawToAsset?: string;
|
|
30
51
|
};
|
|
31
52
|
/**
|
|
32
53
|
* Build a THORChain liquidity-pool remove memo.
|
|
33
54
|
*
|
|
34
|
-
* Format: `-:POOL:BPS`
|
|
55
|
+
* Format: `-:POOL:BPS` or `-:POOL:BPS:ASSET` when asym-withdraw target set.
|
|
35
56
|
*
|
|
36
57
|
* Withdraws do not include an affiliate suffix per the THORChain memo spec.
|
|
37
|
-
* THORChain enforces a 1
|
|
38
|
-
*
|
|
58
|
+
* THORChain enforces a ~1 hour window (`LIQUIDITYLOCKUPBLOCKS`, currently
|
|
59
|
+
* 600 on mainnet) after the most recent add before broadcasts process cleanly.
|
|
39
60
|
*/
|
|
40
|
-
export declare const removeLpMemo: ({ pool, basisPoints, }: RemoveLpMemoInput) => string;
|
|
61
|
+
export declare const removeLpMemo: ({ pool, basisPoints, withdrawToAsset, }: RemoveLpMemoInput) => string;
|
|
41
62
|
//# sourceMappingURL=memo.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memo.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/chain/chains/cosmos/thor/lp/memo.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"memo.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/chain/chains/cosmos/thor/lp/memo.ts"],"names":[],"mappings":"AA4BA,MAAM,MAAM,cAAc,GAAG;IAC3B,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAA;IACZ;;;;;;;;;;;;;;;;OAgBG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,SAAS,GAAI,0BAGvB,cAAc,KAAG,MAOnB,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,kEAAkE;IAClE,WAAW,EAAE,MAAM,CAAA;IACnB;;;;;;;;;OASG;IACH,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GAAI,yCAI1B,iBAAiB,KAAG,MAgBtB,CAAA"}
|
|
@@ -1,38 +1,62 @@
|
|
|
1
|
-
import { VULTISIG_AFFILIATE_LP_BPS, VULTISIG_AFFILIATE_NAME, } from './affiliate.js';
|
|
2
1
|
import { assertValidPoolId } from './pools.js';
|
|
2
|
+
/**
|
|
3
|
+
* Reject memo-segment values that would smuggle additional `:`-separated
|
|
4
|
+
* fields into the memo (e.g. a pairedAddress like `bc1q:ss:60` that tries
|
|
5
|
+
* to inject an affiliate). Also rejects whitespace since THORChain memos
|
|
6
|
+
* are tokenized on `:` and trimmed/rejected when they contain internal
|
|
7
|
+
* whitespace.
|
|
8
|
+
*/
|
|
9
|
+
const assertMemoSegmentSafe = (value, fieldName) => {
|
|
10
|
+
if (typeof value !== 'string') {
|
|
11
|
+
throw new Error(`${fieldName} must be a string, got ${typeof value}`);
|
|
12
|
+
}
|
|
13
|
+
if (value.includes(':')) {
|
|
14
|
+
throw new Error(`${fieldName} must not contain \`:\` (would inject extra memo segments), got ${JSON.stringify(value)}`);
|
|
15
|
+
}
|
|
16
|
+
if (/\s/.test(value)) {
|
|
17
|
+
throw new Error(`${fieldName} must not contain whitespace, got ${JSON.stringify(value)}`);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
3
20
|
/**
|
|
4
21
|
* Build a THORChain liquidity-pool add memo.
|
|
5
22
|
*
|
|
6
|
-
* Format: `+:POOL
|
|
23
|
+
* Format: `+:POOL` (pure asym, no paired address) or
|
|
24
|
+
* `+:POOL:PAIRED_ADDR` (when paired address is provided)
|
|
7
25
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
26
|
+
* Matches vultisig-ios `AddLPMemoData.memo` and
|
|
27
|
+
* vultisig-windows `memoGenerator` `add_thor_lp` output exactly — no
|
|
28
|
+
* affiliate suffix. The THORChain memo spec allows an affiliate via
|
|
29
|
+
* `+:POOL::AFFILIATE:BPS` but neither Vultisig native client ships it;
|
|
30
|
+
* we match the native behavior for wire-level consistency.
|
|
12
31
|
*/
|
|
13
|
-
export const addLpMemo = (
|
|
14
|
-
assertValidPoolId(
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
32
|
+
export const addLpMemo = ({ pool, pairedAddress, }) => {
|
|
33
|
+
assertValidPoolId(pool);
|
|
34
|
+
if (pairedAddress && pairedAddress.length > 0) {
|
|
35
|
+
assertMemoSegmentSafe(pairedAddress, 'pairedAddress');
|
|
36
|
+
return `+:${pool}:${pairedAddress}`;
|
|
37
|
+
}
|
|
38
|
+
return `+:${pool}`;
|
|
19
39
|
};
|
|
20
40
|
/**
|
|
21
41
|
* Build a THORChain liquidity-pool remove memo.
|
|
22
42
|
*
|
|
23
|
-
* Format: `-:POOL:BPS`
|
|
43
|
+
* Format: `-:POOL:BPS` or `-:POOL:BPS:ASSET` when asym-withdraw target set.
|
|
24
44
|
*
|
|
25
45
|
* Withdraws do not include an affiliate suffix per the THORChain memo spec.
|
|
26
|
-
* THORChain enforces a 1
|
|
27
|
-
*
|
|
46
|
+
* THORChain enforces a ~1 hour window (`LIQUIDITYLOCKUPBLOCKS`, currently
|
|
47
|
+
* 600 on mainnet) after the most recent add before broadcasts process cleanly.
|
|
28
48
|
*/
|
|
29
|
-
export const removeLpMemo = ({ pool, basisPoints, }) => {
|
|
49
|
+
export const removeLpMemo = ({ pool, basisPoints, withdrawToAsset, }) => {
|
|
30
50
|
assertValidPoolId(pool);
|
|
31
51
|
if (!Number.isInteger(basisPoints) ||
|
|
32
52
|
basisPoints < 1 ||
|
|
33
53
|
basisPoints > 10000) {
|
|
34
54
|
throw new Error(`removeLpMemo: basisPoints must be an integer in [1, 10000], got ${basisPoints}`);
|
|
35
55
|
}
|
|
56
|
+
if (withdrawToAsset && withdrawToAsset.length > 0) {
|
|
57
|
+
assertMemoSegmentSafe(withdrawToAsset, 'withdrawToAsset');
|
|
58
|
+
return `-:${pool}:${basisPoints}:${withdrawToAsset}`;
|
|
59
|
+
}
|
|
36
60
|
return `-:${pool}:${basisPoints}`;
|
|
37
61
|
};
|
|
38
62
|
//# sourceMappingURL=memo.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memo.js","sourceRoot":"","sources":["../../../../../../../../packages/core/chain/chains/cosmos/thor/lp/memo.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"memo.js","sourceRoot":"","sources":["../../../../../../../../packages/core/chain/chains/cosmos/thor/lp/memo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAE3C;;;;;;GAMG;AACH,MAAM,qBAAqB,GAAG,CAC5B,KAAa,EACb,SAAiB,EACX,EAAE;IACR,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,GAAG,SAAS,0BAA0B,OAAO,KAAK,EAAE,CAAC,CAAA;IACvE,CAAC;IACD,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,GAAG,SAAS,mEAAmE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CACvG,CAAA;IACH,CAAC;IACD,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CACb,GAAG,SAAS,qCAAqC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CACzE,CAAA;IACH,CAAC;AACH,CAAC,CAAA;AAyBD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,EACxB,IAAI,EACJ,aAAa,GACE,EAAU,EAAE;IAC3B,iBAAiB,CAAC,IAAI,CAAC,CAAA;IACvB,IAAI,aAAa,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9C,qBAAqB,CAAC,aAAa,EAAE,eAAe,CAAC,CAAA;QACrD,OAAO,KAAK,IAAI,IAAI,aAAa,EAAE,CAAA;IACrC,CAAC;IACD,OAAO,KAAK,IAAI,EAAE,CAAA;AACpB,CAAC,CAAA;AAmBD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,EAC3B,IAAI,EACJ,WAAW,EACX,eAAe,GACG,EAAU,EAAE;IAC9B,iBAAiB,CAAC,IAAI,CAAC,CAAA;IACvB,IACE,CAAC,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC;QAC9B,WAAW,GAAG,CAAC;QACf,WAAW,GAAG,KAAK,EACnB,CAAC;QACD,MAAM,IAAI,KAAK,CACb,mEAAmE,WAAW,EAAE,CACjF,CAAA;IACH,CAAC;IACD,IAAI,eAAe,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAClD,qBAAqB,CAAC,eAAe,EAAE,iBAAiB,CAAC,CAAA;QACzD,OAAO,KAAK,IAAI,IAAI,WAAW,IAAI,eAAe,EAAE,CAAA;IACtD,CAAC;IACD,OAAO,KAAK,IAAI,IAAI,WAAW,EAAE,CAAA;AACnC,CAAC,CAAA"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Chain } from '@vultisig/core-chain/Chain';
|
|
2
|
+
export type VaultAddressMap = Partial<Record<Chain, string>>;
|
|
3
|
+
export type LpSide = 'rune' | 'asset';
|
|
4
|
+
/**
|
|
5
|
+
* Resolve the paired-address for an LP add based on which side of the
|
|
6
|
+
* pool the caller is depositing.
|
|
7
|
+
*
|
|
8
|
+
* - `side: 'rune'` (depositing RUNE on THORChain): returns the vault's L1
|
|
9
|
+
* address on the pool's ASSET chain. E.g. `BTC.BTC` → vault's BTC
|
|
10
|
+
* address.
|
|
11
|
+
* - `side: 'asset'` (depositing L1 asset): returns the vault's THORChain
|
|
12
|
+
* address (`thor1...`).
|
|
13
|
+
*
|
|
14
|
+
* Matches vultisig-ios `FunctionCallAddThorLP.prefillPairedAddressForPool`
|
|
15
|
+
* and vultisig-windows (the extension) `ThorLpSpecific.tsx`
|
|
16
|
+
* behavior exactly — both always auto-populate the paired address when the
|
|
17
|
+
* vault has the required address, producing a symmetric-pending memo.
|
|
18
|
+
*
|
|
19
|
+
* Returns `undefined` when the vault map does not contain the required
|
|
20
|
+
* address. The caller decides whether to:
|
|
21
|
+
* - fall back to a pure asymmetric deposit (`+:POOL` with no paired
|
|
22
|
+
* address), or
|
|
23
|
+
* - surface an error ("add the other chain to your vault first").
|
|
24
|
+
*/
|
|
25
|
+
export declare const resolvePairedAddressForLpAdd: ({ pool, side, vaultAddresses, }: {
|
|
26
|
+
pool: string;
|
|
27
|
+
side: LpSide;
|
|
28
|
+
vaultAddresses: VaultAddressMap;
|
|
29
|
+
}) => string | undefined;
|
|
30
|
+
//# sourceMappingURL=pairing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pairing.d.ts","sourceRoot":"","sources":["../../../../../../../../packages/core/chain/chains/cosmos/thor/lp/pairing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAA;AAKlD,MAAM,MAAM,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAA;AAE5D,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;AAErC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,4BAA4B,GAAI,iCAI1C;IACD,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,cAAc,EAAE,eAAe,CAAA;CAChC,KAAG,MAAM,GAAG,SAmBZ,CAAA"}
|