@vultisig/core-mpc 1.12.1 → 1.14.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 +165 -0
- package/dist/chains/cosmos/qbtc/QBTCHelper.js +4 -3
- package/dist/chains/cosmos/qbtc/QBTCHelper.js.map +1 -1
- package/dist/chains/cosmos/qbtc/QBTCTx.js +1 -1
- package/dist/chains/cosmos/qbtc/QBTCTx.js.map +1 -1
- package/dist/keysign/chainSpecific/resolvers/cosmos/index.d.ts.map +1 -1
- package/dist/keysign/chainSpecific/resolvers/cosmos/index.js +71 -10
- package/dist/keysign/chainSpecific/resolvers/cosmos/index.js.map +1 -1
- package/dist/keysign/chainSpecific/resolvers/sui/index.d.ts.map +1 -1
- package/dist/keysign/chainSpecific/resolvers/sui/index.js +62 -64
- package/dist/keysign/chainSpecific/resolvers/sui/index.js.map +1 -1
- package/dist/keysign/chainSpecific/resolvers/sui/refine.d.ts.map +1 -1
- package/dist/keysign/chainSpecific/resolvers/sui/refine.js +14 -4
- package/dist/keysign/chainSpecific/resolvers/sui/refine.js.map +1 -1
- package/dist/keysign/chainSpecific/resolvers/tron/energyPrice.d.ts +4 -3
- package/dist/keysign/chainSpecific/resolvers/tron/energyPrice.d.ts.map +1 -1
- package/dist/keysign/chainSpecific/resolvers/tron/energyPrice.js +11 -5
- package/dist/keysign/chainSpecific/resolvers/tron/energyPrice.js.map +1 -1
- package/dist/keysign/chainSpecific/resolvers/tron/fee.d.ts +11 -0
- package/dist/keysign/chainSpecific/resolvers/tron/fee.d.ts.map +1 -1
- package/dist/keysign/chainSpecific/resolvers/tron/fee.js +43 -19
- package/dist/keysign/chainSpecific/resolvers/tron/fee.js.map +1 -1
- package/dist/keysign/fee/resolvers/cosmos.d.ts +5 -3
- package/dist/keysign/fee/resolvers/cosmos.d.ts.map +1 -1
- package/dist/keysign/fee/resolvers/cosmos.js +8 -21
- package/dist/keysign/fee/resolvers/cosmos.js.map +1 -1
- package/dist/keysign/fee/resolvers/tron.d.ts.map +1 -1
- package/dist/keysign/fee/resolvers/tron.js +11 -2
- package/dist/keysign/fee/resolvers/tron.js.map +1 -1
- package/dist/keysign/signingInputs/resolvers/cosmos/index.d.ts.map +1 -1
- package/dist/keysign/signingInputs/resolvers/cosmos/index.js +30 -17
- package/dist/keysign/signingInputs/resolvers/cosmos/index.js.map +1 -1
- package/dist/keysign/swap/buildLimitSwapKeysignPayload.d.ts +86 -0
- package/dist/keysign/swap/buildLimitSwapKeysignPayload.d.ts.map +1 -0
- package/dist/keysign/swap/buildLimitSwapKeysignPayload.js +178 -0
- package/dist/keysign/swap/buildLimitSwapKeysignPayload.js.map +1 -0
- package/dist/keysign/swap/getKeysignLimitSwapOrder.d.ts +41 -0
- package/dist/keysign/swap/getKeysignLimitSwapOrder.d.ts.map +1 -0
- package/dist/keysign/swap/getKeysignLimitSwapOrder.js +50 -0
- package/dist/keysign/swap/getKeysignLimitSwapOrder.js.map +1 -0
- package/package.json +14 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildLimitSwapKeysignPayload.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/mpc/keysign/swap/buildLimitSwapKeysignPayload.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAA;AAYnE,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,+CAA+C,CAAA;AAKzE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAiB7C,MAAM,MAAM,iCAAiC,GAAG;IAC9C,QAAQ,EAAE,WAAW,CAAA;IACrB,MAAM,EAAE,WAAW,CAAA;IACnB,yDAAyD;IACzD,MAAM,EAAE,MAAM,CAAA;IACd,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,SAAS,CAAA;IACxB,WAAW,EAAE,SAAS,CAAA;IACtB,OAAO,EAAE,cAAc,CAAA;IACvB,UAAU,EAAE,UAAU,CAAA;IACtB;;;;;;;;;;OAUG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,0FAA0F;IAC1F,GAAG,CAAC,EAAE,MAAM,CAAA;CACb,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,eAAO,MAAM,4BAA4B,GAAU,oIAahD,iCAAiC,yFA8HnC,CAAA"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { Buffer } from 'buffer';
|
|
2
|
+
import { create } from '@bufbuild/protobuf';
|
|
3
|
+
import { fromChainAmountDisplay } from '@vultisig/core-chain/amount/fromChainAmountExact';
|
|
4
|
+
import { Chain } from '@vultisig/core-chain/Chain';
|
|
5
|
+
import { isChainOfKind } from '@vultisig/core-chain/ChainKind';
|
|
6
|
+
import { getThorchainInboundAddress } from '@vultisig/core-chain/chains/cosmos/thor/getThorchainInboundAddress';
|
|
7
|
+
import { getErc20Allowance } from '@vultisig/core-chain/chains/evm/erc20/getErc20Allowance';
|
|
8
|
+
import { chainFeeCoin } from '@vultisig/core-chain/coin/chainFeeCoin';
|
|
9
|
+
import { areEqualCoins } from '@vultisig/core-chain/coin/Coin';
|
|
10
|
+
import { isFeeCoin } from '@vultisig/core-chain/coin/utils/isFeeCoin';
|
|
11
|
+
import { getAdvancedSwapQueueEnabled } from '@vultisig/core-chain/swap/native/limitSwapAvailability';
|
|
12
|
+
import { findLimitSwapInbound, isLimitSwapDestinationHalted, shouldBlockRuneDeposit, } from '@vultisig/core-chain/swap/native/limitSwapInbound';
|
|
13
|
+
import { parseLimitSwapMemo } from '@vultisig/core-chain/swap/native/limitSwapMemo';
|
|
14
|
+
import { getNativeSwapDecimals } from '@vultisig/core-chain/swap/native/utils/getNativeSwapDecimals';
|
|
15
|
+
import { getChainSpecific } from '../chainSpecific/index.js';
|
|
16
|
+
import { refineKeysignUtxo } from '../refine/utxo.js';
|
|
17
|
+
import { getKeysignUtxoInfo } from '../utxo/getKeysignUtxoInfo.js';
|
|
18
|
+
import { toCommCoin } from '../../types/utils/commCoin.js';
|
|
19
|
+
import { Erc20ApprovePayloadSchema } from '../../types/vultisig/keysign/v1/erc20_approve_payload_pb.js';
|
|
20
|
+
import { KeysignPayloadSchema } from '../../types/vultisig/keysign/v1/keysign_message_pb.js';
|
|
21
|
+
import { THORChainSwapPayloadSchema } from '../../types/vultisig/keysign/v1/thorchain_swap_payload_pb.js';
|
|
22
|
+
/**
|
|
23
|
+
* On-chain deadline for the router's `depositWithExpiry` call.
|
|
24
|
+
*
|
|
25
|
+
* This bounds when the *deposit transaction* may execute — a stale, long-
|
|
26
|
+
* unconfirmed tx cannot land later. It is NOT the resting order's lifetime: that
|
|
27
|
+
* is the memo's interval, up to 3 days, evaluated per block by THORChain. Once
|
|
28
|
+
* THORChain observes the deposit (a block or two, far inside this window) the
|
|
29
|
+
* order rests for its full memo TTL regardless of this value.
|
|
30
|
+
*/
|
|
31
|
+
const routerExpiryMs = 15 * 60 * 1000;
|
|
32
|
+
/**
|
|
33
|
+
* Build the `KeysignPayload` for a THORChain limit order.
|
|
34
|
+
*
|
|
35
|
+
* The limit-ness always lives in the memo (`=<` vs the market `=>`); how that
|
|
36
|
+
* memo reaches the chain depends on the source asset:
|
|
37
|
+
*
|
|
38
|
+
* - **Native RUNE** — `MsgDeposit` on THORChain itself. No inbound vault and no
|
|
39
|
+
* real destination, so `toAddress` carries the signer's own address as a
|
|
40
|
+
* placeholder (the Cosmos signer keys off `isDeposit`, not `toAddress`).
|
|
41
|
+
* - **Native gas asset** (BTC/ETH/AVAX/…) — a plain transfer to the Asgard
|
|
42
|
+
* inbound vault with the memo in tx `data` / `OP_RETURN`. No swap payload.
|
|
43
|
+
* - **ERC20** — the router's `depositWithExpiry(vault, asset, amount, memo,
|
|
44
|
+
* expiry)` call, which needs `approve(router, amount)` first. Both ride one
|
|
45
|
+
* ceremony, mirroring the market ERC20 THORChain swap. A token source signed
|
|
46
|
+
* *without* a swap payload would fall through to a plain ERC20 transfer,
|
|
47
|
+
* dropping the memo and stranding the tokens on the router — so it must carry
|
|
48
|
+
* one.
|
|
49
|
+
*
|
|
50
|
+
* The swap payload is deliberately NOT attached to the first two branches, even
|
|
51
|
+
* though it would give co-signers the market path's swap review for free
|
|
52
|
+
* (sdk#1611). It is not display metadata: every signing-input resolver re-routes
|
|
53
|
+
* on its presence — UTXO takes both amount and destination from it, EVM and Tron
|
|
54
|
+
* take the destination from `vaultAddress`, and the Cosmos resolver drops the
|
|
55
|
+
* tx-body memo (`txMemo: swapPayload ? '' : memo`). Attaching one would change
|
|
56
|
+
* signed bytes on a working path to improve a display, and for RUNE it would
|
|
57
|
+
* additionally make `assertNativeSwapReadyForBroadcast` resolve a `THOR` inbound
|
|
58
|
+
* row that THORChain's own inbound list does not contain, failing the broadcast
|
|
59
|
+
* guard outright. Joining devices get the order terms from
|
|
60
|
+
* `getKeysignLimitSwapOrder` instead, which reads the memo that every branch
|
|
61
|
+
* already carries — and which, being the string THORChain executes, cannot
|
|
62
|
+
* disagree with what is signed.
|
|
63
|
+
*
|
|
64
|
+
* Fund-safety gates, all fail-closed:
|
|
65
|
+
* - The `EnableAdvSwapQueue` mimir is re-checked here, at sign time. Placement
|
|
66
|
+
* was already gated on it, but it can flip while the user sits on Verify, and
|
|
67
|
+
* a `=<` order on a network with the queue disabled can execute as an
|
|
68
|
+
* unprotected market swap.
|
|
69
|
+
* - The memo must actually be a limit memo.
|
|
70
|
+
* - RUNE deposits are blocked on THORChain's global trading pause (they bypass
|
|
71
|
+
* the per-chain inbound halt filter entirely), including when the inbound list
|
|
72
|
+
* is unverifiable — and on a halted destination chain: with no swap payload
|
|
73
|
+
* attached, the broadcast guard's destination-leg re-check never runs for
|
|
74
|
+
* them, so sign time is their only destination gate.
|
|
75
|
+
* - External sources must resolve a live, non-halted, non-paused inbound row,
|
|
76
|
+
* and the destination is taken from that same live view rather than a cache.
|
|
77
|
+
*
|
|
78
|
+
* Gas: unlike iOS, no EVM gas-limit override is applied. iOS pins a native-EVM
|
|
79
|
+
* limit deposit to 120000 to match its own market path; here both paths put the
|
|
80
|
+
* memo on `keysignPayload.memo` and neither sets a general swap payload, so both
|
|
81
|
+
* already floor at `deriveEvmGasLimit`'s 600000 data-tx limit. Forcing 120000
|
|
82
|
+
* would make the limit path diverge from the market path and risk under-gassing.
|
|
83
|
+
*/
|
|
84
|
+
export const buildLimitSwapKeysignPayload = async ({ fromCoin, toCoin, amount, memo, vaultId, localPartyId, fromPublicKey, toPublicKey, libType, walletCore, expectedToAmount, now = Date.now(), }) => {
|
|
85
|
+
const order = parseLimitSwapMemo(memo);
|
|
86
|
+
if (expectedToAmount !== undefined && expectedToAmount !== order.limit) {
|
|
87
|
+
throw new Error(`buildLimitSwapKeysignPayload: expectedToAmount ${expectedToAmount} disagrees with the memo's LIM ${order.limit}. ` +
|
|
88
|
+
"It must be the LIM in THORChain's 1e8 fixed point, not rescaled to the target coin's decimals.");
|
|
89
|
+
}
|
|
90
|
+
if (amount <= 0n) {
|
|
91
|
+
throw new Error('buildLimitSwapKeysignPayload: amount must be greater than 0');
|
|
92
|
+
}
|
|
93
|
+
if (!(await getAdvancedSwapQueueEnabled())) {
|
|
94
|
+
throw new Error("THORChain's advanced swap queue is disabled; a limit order placed now could execute as an unprotected market swap");
|
|
95
|
+
}
|
|
96
|
+
const fromCoinHexPublicKey = Buffer.from(fromPublicKey.data()).toString('hex');
|
|
97
|
+
const toCoinHexPublicKey = Buffer.from(toPublicKey.data()).toString('hex');
|
|
98
|
+
const isRuneDeposit = areEqualCoins(fromCoin, chainFeeCoin[Chain.THORChain]);
|
|
99
|
+
const inbounds = await getThorchainInboundAddress();
|
|
100
|
+
const { toAddress, swapPayload, approveSpender } = (() => {
|
|
101
|
+
if (isRuneDeposit) {
|
|
102
|
+
if (shouldBlockRuneDeposit(inbounds)) {
|
|
103
|
+
throw new Error('THORChain has globally paused trading (or its inbound list is unverifiable); refusing to sign a RUNE limit-order deposit');
|
|
104
|
+
}
|
|
105
|
+
if (isLimitSwapDestinationHalted({ inbounds, chain: toCoin.chain })) {
|
|
106
|
+
throw new Error(`THORChain has halted ${toCoin.chain} trading; refusing to sign a RUNE limit-order deposit whose outbound could not leave`);
|
|
107
|
+
}
|
|
108
|
+
return { toAddress: fromCoin.address };
|
|
109
|
+
}
|
|
110
|
+
const inbound = findLimitSwapInbound({ inbounds, chain: fromCoin.chain });
|
|
111
|
+
if (isFeeCoin(fromCoin)) {
|
|
112
|
+
return { toAddress: inbound.address };
|
|
113
|
+
}
|
|
114
|
+
const { router } = inbound;
|
|
115
|
+
if (!router) {
|
|
116
|
+
throw new Error(`buildLimitSwapKeysignPayload: THORChain's ${inbound.chain} inbound has no router contract, so a token deposit cannot be built`);
|
|
117
|
+
}
|
|
118
|
+
return {
|
|
119
|
+
toAddress: router,
|
|
120
|
+
approveSpender: router,
|
|
121
|
+
swapPayload: create(THORChainSwapPayloadSchema, {
|
|
122
|
+
fromAddress: fromCoin.address,
|
|
123
|
+
fromCoin: toCommCoin({ ...fromCoin, hexPublicKey: fromCoinHexPublicKey }),
|
|
124
|
+
toCoin: toCommCoin({ ...toCoin, hexPublicKey: toCoinHexPublicKey }),
|
|
125
|
+
vaultAddress: inbound.address,
|
|
126
|
+
routerAddress: router,
|
|
127
|
+
fromAmount: amount.toString(),
|
|
128
|
+
// Display-only, for the co-signer's "you receive" row. The order's real
|
|
129
|
+
// floor is the LIM inside the memo; these fields are never read here.
|
|
130
|
+
toAmountDecimal: fromChainAmountDisplay(order.limit, getNativeSwapDecimals(toCoin)),
|
|
131
|
+
toAmountLimit: '0',
|
|
132
|
+
streamingInterval: '0',
|
|
133
|
+
streamingQuantity: '0',
|
|
134
|
+
expirationTime: BigInt(Math.floor((now + routerExpiryMs) / 1000)),
|
|
135
|
+
isAffiliate: true,
|
|
136
|
+
}),
|
|
137
|
+
};
|
|
138
|
+
})();
|
|
139
|
+
let keysignPayload = create(KeysignPayloadSchema, {
|
|
140
|
+
coin: toCommCoin({ ...fromCoin, hexPublicKey: fromCoinHexPublicKey }),
|
|
141
|
+
toAmount: amount.toString(),
|
|
142
|
+
vaultLocalPartyId: localPartyId,
|
|
143
|
+
vaultPublicKeyEcdsa: vaultId,
|
|
144
|
+
libType,
|
|
145
|
+
toAddress,
|
|
146
|
+
memo,
|
|
147
|
+
utxoInfo: await getKeysignUtxoInfo(fromCoin),
|
|
148
|
+
...(swapPayload ? { swapPayload: { case: 'thorchainSwapPayload', value: swapPayload } } : {}),
|
|
149
|
+
});
|
|
150
|
+
keysignPayload.blockchainSpecific = await getChainSpecific({
|
|
151
|
+
keysignPayload,
|
|
152
|
+
walletCore,
|
|
153
|
+
isDeposit: isRuneDeposit,
|
|
154
|
+
});
|
|
155
|
+
if (approveSpender && isChainOfKind(fromCoin.chain, 'evm') && fromCoin.id) {
|
|
156
|
+
const allowance = await getErc20Allowance({
|
|
157
|
+
chain: fromCoin.chain,
|
|
158
|
+
id: fromCoin.id,
|
|
159
|
+
address: fromCoin.address,
|
|
160
|
+
spender: approveSpender,
|
|
161
|
+
});
|
|
162
|
+
if (allowance < amount) {
|
|
163
|
+
keysignPayload.erc20ApprovePayload = create(Erc20ApprovePayloadSchema, {
|
|
164
|
+
amount: amount.toString(),
|
|
165
|
+
spender: approveSpender,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (isChainOfKind(fromCoin.chain, 'utxo')) {
|
|
170
|
+
keysignPayload = await refineKeysignUtxo({
|
|
171
|
+
keysignPayload,
|
|
172
|
+
walletCore,
|
|
173
|
+
publicKey: fromPublicKey,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
return keysignPayload;
|
|
177
|
+
};
|
|
178
|
+
//# sourceMappingURL=buildLimitSwapKeysignPayload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buildLimitSwapKeysignPayload.js","sourceRoot":"","sources":["../../../../../../packages/core/mpc/keysign/swap/buildLimitSwapKeysignPayload.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Chain } from '@vultisig/core-chain/Chain';
|
|
2
|
+
import { LimitSwapExpiryHours, ParsedLimitSwapMemo } from '@vultisig/core-chain/swap/native/limitSwapMemo';
|
|
3
|
+
import { KeysignPayload } from '../../types/vultisig/keysign/v1/keysign_message_pb.js';
|
|
4
|
+
export type KeysignLimitSwapOrder = ParsedLimitSwapMemo & {
|
|
5
|
+
/** The buy chain, when the target asset's prefix is one this SDK can route. */
|
|
6
|
+
targetChain?: Chain;
|
|
7
|
+
/**
|
|
8
|
+
* Guaranteed-minimum received, as a decimal string in the target asset's
|
|
9
|
+
* display units. Formatted from the LIM with THORChain's 8 decimals, matching
|
|
10
|
+
* how `toAmountDecimal` is formatted for market swaps.
|
|
11
|
+
*/
|
|
12
|
+
minimumReceivedDecimal: string;
|
|
13
|
+
/** The order's resting lifetime, when the interval maps to one this SDK builds. */
|
|
14
|
+
expiryHours?: LimitSwapExpiryHours;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Decode the limit order a keysign payload is about to place, for review.
|
|
18
|
+
*
|
|
19
|
+
* Returns `undefined` for any payload that is not a limit order, so a caller can
|
|
20
|
+
* branch on it directly.
|
|
21
|
+
*
|
|
22
|
+
* **This is how a *joining* device reviews a limit order.** The builder attaches
|
|
23
|
+
* a `swapPayload` only for ERC20 sources — RUNE and native-gas-asset orders are
|
|
24
|
+
* plain deposits — so a co-signer keying off the swap payload alone sees a
|
|
25
|
+
* generic send to an opaque address with an opaque memo, and cannot tell what is
|
|
26
|
+
* being bought, where it pays out, or at what floor.
|
|
27
|
+
*
|
|
28
|
+
* Deriving the terms from `keysignPayload.memo` fixes that uniformly, because
|
|
29
|
+
* the memo is present on every source branch. It is also the *only* trustworthy
|
|
30
|
+
* source: the memo is the exact string THORChain executes, so terms read from it
|
|
31
|
+
* cannot disagree with the order that gets signed. A display field carried
|
|
32
|
+
* alongside the memo can — it is supplied by the initiating device, and a
|
|
33
|
+
* co-signer has no way to tell a mistaken one from a malicious one.
|
|
34
|
+
*
|
|
35
|
+
* Parsing is fail-closed on a malformed memo, but a caller reviewing an
|
|
36
|
+
* arbitrary payload wants "not a limit order" rather than a throw, so a memo
|
|
37
|
+
* that does not parse is reported as not-a-limit-order. Signing still rejects it:
|
|
38
|
+
* `buildLimitSwapKeysignPayload` parses the same memo and throws.
|
|
39
|
+
*/
|
|
40
|
+
export declare const getKeysignLimitSwapOrder: ({ memo }: Pick<KeysignPayload, "memo">) => KeysignLimitSwapOrder | undefined;
|
|
41
|
+
//# sourceMappingURL=getKeysignLimitSwapOrder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getKeysignLimitSwapOrder.d.ts","sourceRoot":"","sources":["../../../../../../packages/core/mpc/keysign/swap/getKeysignLimitSwapOrder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAA;AAElD,OAAO,EAEL,oBAAoB,EAGpB,mBAAmB,EAEpB,MAAM,gDAAgD,CAAA;AAIvD,OAAO,EAAE,cAAc,EAAE,MAAM,oDAAoD,CAAA;AASnF,MAAM,MAAM,qBAAqB,GAAG,mBAAmB,GAAG;IACxD,+EAA+E;IAC/E,WAAW,CAAC,EAAE,KAAK,CAAA;IACnB;;;;OAIG;IACH,sBAAsB,EAAE,MAAM,CAAA;IAC9B,mFAAmF;IACnF,WAAW,CAAC,EAAE,oBAAoB,CAAA;CACnC,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,wBAAwB,GAAI,UAAU,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,KAAG,qBAAqB,GAAG,SAkBzG,CAAA"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { fromChainAmountDisplay } from '@vultisig/core-chain/amount/fromChainAmountExact';
|
|
2
|
+
import { Chain } from '@vultisig/core-chain/Chain';
|
|
3
|
+
import { chainFeeCoin } from '@vultisig/core-chain/coin/chainFeeCoin';
|
|
4
|
+
import { limitSwapExpiryHours, getLimitSwapIntervalBlocks, limitSwapMemoPrefix, parseLimitSwapMemo, } from '@vultisig/core-chain/swap/native/limitSwapMemo';
|
|
5
|
+
import { thorchainAssetPrefixToChain } from '@vultisig/core-chain/swap/native/thorchainMemoAsset';
|
|
6
|
+
import { attempt } from '@vultisig/lib-utils/attempt';
|
|
7
|
+
/** THORChain's fixed point for pool amounts, which the memo's LIM is expressed in. */
|
|
8
|
+
const thorchainDecimals = chainFeeCoin[Chain.THORChain].decimals;
|
|
9
|
+
const intervalBlocksToExpiryHours = Object.freeze(Object.fromEntries(limitSwapExpiryHours.map(hours => [getLimitSwapIntervalBlocks(hours), hours])));
|
|
10
|
+
/**
|
|
11
|
+
* Decode the limit order a keysign payload is about to place, for review.
|
|
12
|
+
*
|
|
13
|
+
* Returns `undefined` for any payload that is not a limit order, so a caller can
|
|
14
|
+
* branch on it directly.
|
|
15
|
+
*
|
|
16
|
+
* **This is how a *joining* device reviews a limit order.** The builder attaches
|
|
17
|
+
* a `swapPayload` only for ERC20 sources — RUNE and native-gas-asset orders are
|
|
18
|
+
* plain deposits — so a co-signer keying off the swap payload alone sees a
|
|
19
|
+
* generic send to an opaque address with an opaque memo, and cannot tell what is
|
|
20
|
+
* being bought, where it pays out, or at what floor.
|
|
21
|
+
*
|
|
22
|
+
* Deriving the terms from `keysignPayload.memo` fixes that uniformly, because
|
|
23
|
+
* the memo is present on every source branch. It is also the *only* trustworthy
|
|
24
|
+
* source: the memo is the exact string THORChain executes, so terms read from it
|
|
25
|
+
* cannot disagree with the order that gets signed. A display field carried
|
|
26
|
+
* alongside the memo can — it is supplied by the initiating device, and a
|
|
27
|
+
* co-signer has no way to tell a mistaken one from a malicious one.
|
|
28
|
+
*
|
|
29
|
+
* Parsing is fail-closed on a malformed memo, but a caller reviewing an
|
|
30
|
+
* arbitrary payload wants "not a limit order" rather than a throw, so a memo
|
|
31
|
+
* that does not parse is reported as not-a-limit-order. Signing still rejects it:
|
|
32
|
+
* `buildLimitSwapKeysignPayload` parses the same memo and throws.
|
|
33
|
+
*/
|
|
34
|
+
export const getKeysignLimitSwapOrder = ({ memo }) => {
|
|
35
|
+
if (!memo?.startsWith(limitSwapMemoPrefix)) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
const parsed = attempt(() => parseLimitSwapMemo(memo));
|
|
39
|
+
if ('error' in parsed) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
const { data: order } = parsed;
|
|
43
|
+
return {
|
|
44
|
+
...order,
|
|
45
|
+
targetChain: thorchainAssetPrefixToChain[order.targetAsset.split('.')[0].toUpperCase()],
|
|
46
|
+
minimumReceivedDecimal: fromChainAmountDisplay(order.limit, thorchainDecimals),
|
|
47
|
+
expiryHours: intervalBlocksToExpiryHours[order.intervalBlocks],
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=getKeysignLimitSwapOrder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"getKeysignLimitSwapOrder.js","sourceRoot":"","sources":["../../../../../../packages/core/mpc/keysign/swap/getKeysignLimitSwapOrder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,kDAAkD,CAAA;AACzF,OAAO,EAAE,KAAK,EAAE,MAAM,4BAA4B,CAAA;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,wCAAwC,CAAA;AACrE,OAAO,EACL,oBAAoB,EAEpB,0BAA0B,EAC1B,mBAAmB,EAEnB,kBAAkB,GACnB,MAAM,gDAAgD,CAAA;AACvD,OAAO,EAAE,2BAA2B,EAAE,MAAM,qDAAqD,CAAA;AACjG,OAAO,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAA;AAIrD,sFAAsF;AACtF,MAAM,iBAAiB,GAAG,YAAY,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAA;AAEhE,MAAM,2BAA2B,GAA4D,MAAM,CAAC,MAAM,CACxG,MAAM,CAAC,WAAW,CAAC,oBAAoB,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,0BAA0B,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAClG,CAAA;AAeD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,EAAE,IAAI,EAAgC,EAAqC,EAAE;IACpH,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,mBAAmB,CAAC,EAAE,CAAC;QAC3C,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAA;IACtD,IAAI,OAAO,IAAI,MAAM,EAAE,CAAC;QACtB,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,CAAA;IAE9B,OAAO;QACL,GAAG,KAAK;QACR,WAAW,EAAE,2BAA2B,CAAC,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACvF,sBAAsB,EAAE,sBAAsB,CAAC,KAAK,CAAC,KAAK,EAAE,iBAAiB,CAAC;QAC9E,WAAW,EAAE,2BAA2B,CAAC,KAAK,CAAC,cAAc,CAAC;KAC/D,CAAA;AACH,CAAC,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vultisig/core-mpc",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.14.0",
|
|
4
4
|
"description": "MPC, keysign, and vault types shared across Vultisig clients",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -591,11 +591,21 @@
|
|
|
591
591
|
"import": "./dist/keysign/swap/build.js",
|
|
592
592
|
"default": "./dist/keysign/swap/build.js"
|
|
593
593
|
},
|
|
594
|
+
"./keysign/swap/buildLimitSwapKeysignPayload": {
|
|
595
|
+
"types": "./dist/keysign/swap/buildLimitSwapKeysignPayload.d.ts",
|
|
596
|
+
"import": "./dist/keysign/swap/buildLimitSwapKeysignPayload.js",
|
|
597
|
+
"default": "./dist/keysign/swap/buildLimitSwapKeysignPayload.js"
|
|
598
|
+
},
|
|
594
599
|
"./keysign/swap/cowswap/buildCowSwapApprovalSigningInput": {
|
|
595
600
|
"types": "./dist/keysign/swap/cowswap/buildCowSwapApprovalSigningInput.d.ts",
|
|
596
601
|
"import": "./dist/keysign/swap/cowswap/buildCowSwapApprovalSigningInput.js",
|
|
597
602
|
"default": "./dist/keysign/swap/cowswap/buildCowSwapApprovalSigningInput.js"
|
|
598
603
|
},
|
|
604
|
+
"./keysign/swap/getKeysignLimitSwapOrder": {
|
|
605
|
+
"types": "./dist/keysign/swap/getKeysignLimitSwapOrder.d.ts",
|
|
606
|
+
"import": "./dist/keysign/swap/getKeysignLimitSwapOrder.js",
|
|
607
|
+
"default": "./dist/keysign/swap/getKeysignLimitSwapOrder.js"
|
|
608
|
+
},
|
|
599
609
|
"./keysign/swap/getKeysignSwapPayload": {
|
|
600
610
|
"types": "./dist/keysign/swap/getKeysignSwapPayload.d.ts",
|
|
601
611
|
"import": "./dist/keysign/swap/getKeysignSwapPayload.js",
|
|
@@ -1063,13 +1073,13 @@
|
|
|
1063
1073
|
},
|
|
1064
1074
|
"dependencies": {
|
|
1065
1075
|
"7z-wasm": "^1.2.0",
|
|
1066
|
-
"@bufbuild/protobuf": "^2.
|
|
1076
|
+
"@bufbuild/protobuf": "^2.13.0",
|
|
1067
1077
|
"@cosmjs/encoding": "^0.39.0",
|
|
1068
|
-
"@mysten/sui": "^2.
|
|
1078
|
+
"@mysten/sui": "^2.22.1",
|
|
1069
1079
|
"@noble/hashes": "^1.8.0",
|
|
1070
1080
|
"@solana/web3.js": "^1.98.4",
|
|
1071
1081
|
"@trustwallet/wallet-core": "^4.7.0",
|
|
1072
|
-
"@vultisig/core-chain": "2.
|
|
1082
|
+
"@vultisig/core-chain": "2.29.0",
|
|
1073
1083
|
"@vultisig/core-config": "0.9.1",
|
|
1074
1084
|
"@vultisig/lib-dkls": "0.9.0",
|
|
1075
1085
|
"@vultisig/lib-mldsa": "0.9.0",
|