@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.
Files changed (102) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/dist/amount/toChainAmount.d.ts +6 -1
  3. package/dist/amount/toChainAmount.d.ts.map +1 -1
  4. package/dist/amount/toChainAmount.js +88 -0
  5. package/dist/amount/toChainAmount.js.map +1 -1
  6. package/dist/chains/cosmos/qbtc/claim/BtcAddressType.d.ts +7 -0
  7. package/dist/chains/cosmos/qbtc/claim/BtcAddressType.d.ts.map +1 -0
  8. package/dist/chains/cosmos/qbtc/claim/BtcAddressType.js +9 -0
  9. package/dist/chains/cosmos/qbtc/claim/BtcAddressType.js.map +1 -0
  10. package/dist/chains/cosmos/qbtc/claim/ClaimableUtxo.d.ts +8 -0
  11. package/dist/chains/cosmos/qbtc/claim/ClaimableUtxo.d.ts.map +1 -0
  12. package/dist/chains/cosmos/qbtc/claim/ClaimableUtxo.js +2 -0
  13. package/dist/chains/cosmos/qbtc/claim/ClaimableUtxo.js.map +1 -0
  14. package/dist/chains/cosmos/qbtc/claim/broadcastClaimTx.d.ts +25 -0
  15. package/dist/chains/cosmos/qbtc/claim/broadcastClaimTx.d.ts.map +1 -0
  16. package/dist/chains/cosmos/qbtc/claim/broadcastClaimTx.js +43 -0
  17. package/dist/chains/cosmos/qbtc/claim/broadcastClaimTx.js.map +1 -0
  18. package/dist/chains/cosmos/qbtc/claim/buildClaimTx.d.ts +26 -0
  19. package/dist/chains/cosmos/qbtc/claim/buildClaimTx.d.ts.map +1 -0
  20. package/dist/chains/cosmos/qbtc/claim/buildClaimTx.js +57 -0
  21. package/dist/chains/cosmos/qbtc/claim/buildClaimTx.js.map +1 -0
  22. package/dist/chains/cosmos/qbtc/claim/computeClaimHashes.d.ts +49 -0
  23. package/dist/chains/cosmos/qbtc/claim/computeClaimHashes.d.ts.map +1 -0
  24. package/dist/chains/cosmos/qbtc/claim/computeClaimHashes.js +85 -0
  25. package/dist/chains/cosmos/qbtc/claim/computeClaimHashes.js.map +1 -0
  26. package/dist/chains/cosmos/qbtc/claim/detectBtcAddressType.d.ts +14 -0
  27. package/dist/chains/cosmos/qbtc/claim/detectBtcAddressType.d.ts.map +1 -0
  28. package/dist/chains/cosmos/qbtc/claim/detectBtcAddressType.js +31 -0
  29. package/dist/chains/cosmos/qbtc/claim/detectBtcAddressType.js.map +1 -0
  30. package/dist/chains/cosmos/qbtc/claim/getClaimWithProofDisabled.d.ts +3 -0
  31. package/dist/chains/cosmos/qbtc/claim/getClaimWithProofDisabled.d.ts.map +1 -0
  32. package/dist/chains/cosmos/qbtc/claim/getClaimWithProofDisabled.js +13 -0
  33. package/dist/chains/cosmos/qbtc/claim/getClaimWithProofDisabled.js.map +1 -0
  34. package/dist/chains/cosmos/qbtc/claim/getClaimableUtxos.d.ts +14 -0
  35. package/dist/chains/cosmos/qbtc/claim/getClaimableUtxos.d.ts.map +1 -0
  36. package/dist/chains/cosmos/qbtc/claim/getClaimableUtxos.js +22 -0
  37. package/dist/chains/cosmos/qbtc/claim/getClaimableUtxos.js.map +1 -0
  38. package/dist/chains/cosmos/qbtc/claim/proofService.d.ts +50 -0
  39. package/dist/chains/cosmos/qbtc/claim/proofService.d.ts.map +1 -0
  40. package/dist/chains/cosmos/qbtc/claim/proofService.js +71 -0
  41. package/dist/chains/cosmos/qbtc/claim/proofService.js.map +1 -0
  42. package/dist/chains/cosmos/thor/lp/halts.d.ts +56 -0
  43. package/dist/chains/cosmos/thor/lp/halts.d.ts.map +1 -0
  44. package/dist/chains/cosmos/thor/lp/halts.js +95 -0
  45. package/dist/chains/cosmos/thor/lp/halts.js.map +1 -0
  46. package/dist/chains/cosmos/thor/lp/index.d.ts +34 -11
  47. package/dist/chains/cosmos/thor/lp/index.d.ts.map +1 -1
  48. package/dist/chains/cosmos/thor/lp/index.js +9 -15
  49. package/dist/chains/cosmos/thor/lp/index.js.map +1 -1
  50. package/dist/chains/cosmos/thor/lp/lockup.d.ts +47 -0
  51. package/dist/chains/cosmos/thor/lp/lockup.d.ts.map +1 -0
  52. package/dist/chains/cosmos/thor/lp/lockup.js +56 -0
  53. package/dist/chains/cosmos/thor/lp/lockup.js.map +1 -0
  54. package/dist/chains/cosmos/thor/lp/lpChainMap.d.ts +25 -0
  55. package/dist/chains/cosmos/thor/lp/lpChainMap.d.ts.map +1 -0
  56. package/dist/chains/cosmos/thor/lp/lpChainMap.js +30 -0
  57. package/dist/chains/cosmos/thor/lp/lpChainMap.js.map +1 -0
  58. package/dist/chains/cosmos/thor/lp/math.d.ts +129 -0
  59. package/dist/chains/cosmos/thor/lp/math.d.ts.map +1 -0
  60. package/dist/chains/cosmos/thor/lp/math.js +227 -0
  61. package/dist/chains/cosmos/thor/lp/math.js.map +1 -0
  62. package/dist/chains/cosmos/thor/lp/memberPool.d.ts +4 -0
  63. package/dist/chains/cosmos/thor/lp/memberPool.d.ts.map +1 -0
  64. package/dist/chains/cosmos/thor/lp/memberPool.js +24 -0
  65. package/dist/chains/cosmos/thor/lp/memberPool.js.map +1 -0
  66. package/dist/chains/cosmos/thor/lp/memo.d.ts +38 -17
  67. package/dist/chains/cosmos/thor/lp/memo.d.ts.map +1 -1
  68. package/dist/chains/cosmos/thor/lp/memo.js +40 -16
  69. package/dist/chains/cosmos/thor/lp/memo.js.map +1 -1
  70. package/dist/chains/cosmos/thor/lp/pairing.d.ts +30 -0
  71. package/dist/chains/cosmos/thor/lp/pairing.d.ts.map +1 -0
  72. package/dist/chains/cosmos/thor/lp/pairing.js +44 -0
  73. package/dist/chains/cosmos/thor/lp/pairing.js.map +1 -0
  74. package/dist/chains/cosmos/thor/lp/payload.d.ts +30 -10
  75. package/dist/chains/cosmos/thor/lp/payload.d.ts.map +1 -1
  76. package/dist/chains/cosmos/thor/lp/payload.js +30 -18
  77. package/dist/chains/cosmos/thor/lp/payload.js.map +1 -1
  78. package/dist/chains/cosmos/thor/lp/position.d.ts +15 -22
  79. package/dist/chains/cosmos/thor/lp/position.d.ts.map +1 -1
  80. package/dist/chains/cosmos/thor/lp/position.js +82 -35
  81. package/dist/chains/cosmos/thor/lp/position.js.map +1 -1
  82. package/dist/chains/cosmos/thor/lp/positions.d.ts +15 -0
  83. package/dist/chains/cosmos/thor/lp/positions.d.ts.map +1 -0
  84. package/dist/chains/cosmos/thor/lp/positions.js +47 -0
  85. package/dist/chains/cosmos/thor/lp/positions.js.map +1 -0
  86. package/dist/chains/cosmos/thor/lp/types.d.ts +45 -0
  87. package/dist/chains/cosmos/thor/lp/types.d.ts.map +1 -0
  88. package/dist/chains/cosmos/thor/lp/types.js +2 -0
  89. package/dist/chains/cosmos/thor/lp/types.js.map +1 -0
  90. package/dist/chains/cosmos/thor/lp/validation.d.ts +32 -7
  91. package/dist/chains/cosmos/thor/lp/validation.d.ts.map +1 -1
  92. package/dist/chains/cosmos/thor/lp/validation.js +76 -11
  93. package/dist/chains/cosmos/thor/lp/validation.js.map +1 -1
  94. package/dist/chains/utxo/tx/buildSignBitcoinFromPsbt.d.ts +21 -0
  95. package/dist/chains/utxo/tx/buildSignBitcoinFromPsbt.d.ts.map +1 -0
  96. package/dist/chains/utxo/tx/buildSignBitcoinFromPsbt.js +182 -0
  97. package/dist/chains/utxo/tx/buildSignBitcoinFromPsbt.js.map +1 -0
  98. package/package.json +90 -5
  99. package/dist/chains/cosmos/thor/lp/affiliate.d.ts +0 -16
  100. package/dist/chains/cosmos/thor/lp/affiliate.d.ts.map +0 -1
  101. package/dist/chains/cosmos/thor/lp/affiliate.js +0 -16
  102. 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. Omit for asymmetric adds (the v1 path). When set,
6
- * THORChain will hold the deposit pending and pair it with a matching
7
- * deposit from the other side, producing a symmetric position.
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:PAIRED_ADDR:AFFILIATE:BPS`
26
+ * Format: `+:POOL` (pure asym, no paired address) or
27
+ * `+:POOL:PAIRED_ADDR` (when paired address is provided)
19
28
  *
20
- * For an asymmetric RUNE-side add with the default Vultisig affiliate at
21
- * 0 bps the memo looks like `+:BTC.BTC::vi:0`. The empty paired-address slot
22
- * is intentional that is what tells THORChain to register the position as
23
- * asymmetric.
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: (input: AddLpMemoInput) => string;
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-hour lockup after the most recent add — broadcasting
38
- * a withdraw inside that window will fail at the chain level.
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":"AAMA,MAAM,MAAM,cAAc,GAAG;IAC3B,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,8EAA8E;IAC9E,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,SAAS,GAAI,OAAO,cAAc,KAAG,MAMjD,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,kEAAkE;IAClE,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,GAAI,wBAG1B,iBAAiB,KAAG,MAYtB,CAAA"}
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:PAIRED_ADDR:AFFILIATE:BPS`
23
+ * Format: `+:POOL` (pure asym, no paired address) or
24
+ * `+:POOL:PAIRED_ADDR` (when paired address is provided)
7
25
  *
8
- * For an asymmetric RUNE-side add with the default Vultisig affiliate at
9
- * 0 bps the memo looks like `+:BTC.BTC::vi:0`. The empty paired-address slot
10
- * is intentional that is what tells THORChain to register the position as
11
- * asymmetric.
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 = (input) => {
14
- assertValidPoolId(input.pool);
15
- const affiliate = input.affiliate ?? VULTISIG_AFFILIATE_NAME;
16
- const bps = input.affiliateBps ?? VULTISIG_AFFILIATE_LP_BPS;
17
- const paired = input.pairedAddress ?? '';
18
- return `+:${input.pool}:${paired}:${affiliate}:${bps}`;
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-hour lockup after the most recent add — broadcasting
27
- * a withdraw inside that window will fail at the chain level.
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,EACL,yBAAyB,EACzB,uBAAuB,GACxB,MAAM,aAAa,CAAA;AACpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAiB3C;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,KAAqB,EAAU,EAAE;IACzD,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAC7B,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,uBAAuB,CAAA;IAC5D,MAAM,GAAG,GAAG,KAAK,CAAC,YAAY,IAAI,yBAAyB,CAAA;IAC3D,MAAM,MAAM,GAAG,KAAK,CAAC,aAAa,IAAI,EAAE,CAAA;IACxC,OAAO,KAAK,KAAK,CAAC,IAAI,IAAI,MAAM,IAAI,SAAS,IAAI,GAAG,EAAE,CAAA;AACxD,CAAC,CAAA;AAQD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,EAC3B,IAAI,EACJ,WAAW,GACO,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,OAAO,KAAK,IAAI,IAAI,WAAW,EAAE,CAAA;AACnC,CAAC,CAAA"}
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"}