@mysten-incubation/hashi 0.0.1
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 +1 -0
- package/dist/bitcoin.d.mts +140 -0
- package/dist/bitcoin.mjs +272 -0
- package/dist/client.d.mts +318 -0
- package/dist/client.mjs +522 -0
- package/dist/constants.mjs +60 -0
- package/dist/contracts/hashi/bitcoin_state.mjs +28 -0
- package/dist/contracts/hashi/committee.mjs +40 -0
- package/dist/contracts/hashi/committee_set.mjs +34 -0
- package/dist/contracts/hashi/config.mjs +28 -0
- package/dist/contracts/hashi/config_value.mjs +21 -0
- package/dist/contracts/hashi/deposit.mjs +67 -0
- package/dist/contracts/hashi/deposit_queue.mjs +33 -0
- package/dist/contracts/hashi/deps/sui/bag.mjs +42 -0
- package/dist/contracts/hashi/deps/sui/balance.mjs +20 -0
- package/dist/contracts/hashi/deps/sui/group_ops.mjs +16 -0
- package/dist/contracts/hashi/deps/sui/object_bag.mjs +25 -0
- package/dist/contracts/hashi/deps/sui/package.mjs +26 -0
- package/dist/contracts/hashi/deps/sui/table.mjs +37 -0
- package/dist/contracts/hashi/deps/sui/vec_map.mjs +36 -0
- package/dist/contracts/hashi/deps/sui/vec_set.mjs +25 -0
- package/dist/contracts/hashi/hashi.mjs +29 -0
- package/dist/contracts/hashi/proposals.mjs +18 -0
- package/dist/contracts/hashi/treasury.mjs +28 -0
- package/dist/contracts/hashi/utxo.mjs +56 -0
- package/dist/contracts/hashi/utxo_pool.mjs +35 -0
- package/dist/contracts/hashi/withdraw.mjs +91 -0
- package/dist/contracts/hashi/withdrawal_queue.mjs +131 -0
- package/dist/contracts/utils/index.d.mts +8 -0
- package/dist/contracts/utils/index.mjs +118 -0
- package/dist/errors.d.mts +118 -0
- package/dist/errors.mjs +118 -0
- package/dist/index.d.mts +5 -0
- package/dist/index.mjs +5 -0
- package/dist/types.d.mts +137 -0
- package/dist/util.mjs +50 -0
- package/package.json +64 -0
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import { RawTransactionArgument } from "./contracts/utils/index.mjs";
|
|
2
|
+
import { BitcoinNetwork, CancelWithdrawalParams, DepositParams, GovernanceConfig, HashiClientOptions, SuiNetwork, TransactionHistoryItem, UtxoId, UtxoUsageResult, WithdrawalParams } from "./types.mjs";
|
|
3
|
+
import * as _mysten_sui_transactions0 from "@mysten/sui/transactions";
|
|
4
|
+
import { Transaction } from "@mysten/sui/transactions";
|
|
5
|
+
import { ClientWithCoreApi, SuiClientTypes } from "@mysten/sui/client";
|
|
6
|
+
import { Signer } from "@mysten/sui/cryptography";
|
|
7
|
+
|
|
8
|
+
//#region src/client.d.ts
|
|
9
|
+
declare function hashi<const Name = "hashi">({
|
|
10
|
+
name,
|
|
11
|
+
...options
|
|
12
|
+
}: HashiClientOptions<Name>): {
|
|
13
|
+
name: Name;
|
|
14
|
+
register: (client: ClientWithCoreApi) => HashiClient;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* User-facing SDK client for the Hashi protocol. Constructed via the
|
|
18
|
+
* `hashi({...})` factory and attached to any Sui client via `$extend`:
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* const client = new SuiGrpcClient({ ... }).$extend(hashi({ network: "devnet" }));
|
|
22
|
+
* const result = await client.hashi.deposit({ signer, ... });
|
|
23
|
+
* ```
|
|
24
|
+
*
|
|
25
|
+
* **Direct methods (`deposit`, `withdraw`) sign and execute transactions** on
|
|
26
|
+
* behalf of the caller — pass a `Signer` and receive the execution result.
|
|
27
|
+
* For composable flows (bundling into a larger PTB, sponsored transactions,
|
|
28
|
+
* dry-run/simulation), use the `tx.*` builders instead; they return unsigned
|
|
29
|
+
* `Transaction` objects and leave signing to the caller.
|
|
30
|
+
*/
|
|
31
|
+
declare class HashiClient {
|
|
32
|
+
#private;
|
|
33
|
+
constructor({
|
|
34
|
+
client,
|
|
35
|
+
network,
|
|
36
|
+
hashiObjectId,
|
|
37
|
+
packageId,
|
|
38
|
+
bitcoinNetwork
|
|
39
|
+
}: {
|
|
40
|
+
client: ClientWithCoreApi;
|
|
41
|
+
network: SuiNetwork;
|
|
42
|
+
hashiObjectId?: string;
|
|
43
|
+
packageId?: string;
|
|
44
|
+
bitcoinNetwork?: BitcoinNetwork;
|
|
45
|
+
});
|
|
46
|
+
/**
|
|
47
|
+
* Generates a unique Bitcoin P2TR deposit address for a Sui address.
|
|
48
|
+
*
|
|
49
|
+
* Fetches the MPC committee public key from on-chain, derives a child key
|
|
50
|
+
* using the Sui address, and builds a taproot script-path address.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* const btcAddress = await client.hashi.generateDepositAddress({
|
|
55
|
+
* suiAddress: signer.toSuiAddress(),
|
|
56
|
+
* });
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
generateDepositAddress({
|
|
60
|
+
suiAddress,
|
|
61
|
+
bitcoinNetwork
|
|
62
|
+
}: {
|
|
63
|
+
/** The Sui address to generate a deposit address for (hex string with 0x prefix). */suiAddress: string; /** Override the default Bitcoin network for this call. */
|
|
64
|
+
bitcoinNetwork?: BitcoinNetwork;
|
|
65
|
+
}): Promise<string>;
|
|
66
|
+
/**
|
|
67
|
+
* Submit one or more Bitcoin deposits for committee confirmation, batched
|
|
68
|
+
* into a single Sui PTB. Signs with `signer` and submits, returning the
|
|
69
|
+
* execution result (`$kind: "Transaction" | "FailedTransaction"`). The
|
|
70
|
+
* result includes `effects` and `events` so callers can confirm
|
|
71
|
+
* `DepositRequestedEvent` without an extra round-trip.
|
|
72
|
+
*
|
|
73
|
+
* The method runs three preflight stages before signing:
|
|
74
|
+
*
|
|
75
|
+
* 1. **Structural validation** — `txid` and `recipient` must be
|
|
76
|
+
* 0x-prefixed 32-byte hex; `utxos` must be non-empty; every `vout`
|
|
77
|
+
* must be a non-negative u32 and unique within the call. Violations
|
|
78
|
+
* throw `InvalidParamsError` without any chain read.
|
|
79
|
+
* 2. **Pause check** — reads the governance snapshot via `view.all()`
|
|
80
|
+
* and throws `HashiPausedError` if `paused` is `true`. Mirrors the
|
|
81
|
+
* Move-side `hashi::assert_unpaused`.
|
|
82
|
+
* 3. **Minimum check** — every UTXO must have `amountSats ≥
|
|
83
|
+
* snap.bitcoinDepositMinimum`. All offenders are collected into one
|
|
84
|
+
* `AmountBelowMinimumError`, so callers can fix the batch in one
|
|
85
|
+
* round-trip. Mirrors `EBelowMinimumDeposit` in `deposit::deposit`.
|
|
86
|
+
*
|
|
87
|
+
* Both chain-reading checks (2, 3) read from the same `view.all()`
|
|
88
|
+
* snapshot, so validation is internally consistent. Chain state can still
|
|
89
|
+
* drift between the snapshot and execution — the Move side re-asserts
|
|
90
|
+
* both invariants, so a genuine race simply aborts the tx.
|
|
91
|
+
*
|
|
92
|
+
* For composable flows (sponsored tx, dry-run, or bundling into a larger
|
|
93
|
+
* PTB), use `tx.deposit(params)` instead — it returns the unsigned
|
|
94
|
+
* `Transaction` and leaves signing to the caller.
|
|
95
|
+
*/
|
|
96
|
+
deposit({
|
|
97
|
+
signer,
|
|
98
|
+
...params
|
|
99
|
+
}: DepositParams & {
|
|
100
|
+
/** Signs and pays for the resulting transaction. The signer's address becomes the tx sender. */signer: Signer;
|
|
101
|
+
}): Promise<SuiClientTypes.TransactionResult<{
|
|
102
|
+
effects: true;
|
|
103
|
+
events: true;
|
|
104
|
+
}>>;
|
|
105
|
+
/**
|
|
106
|
+
* Submit a BTC withdrawal request for committee processing. Burns `hBTC`
|
|
107
|
+
* from the signer's balance and enqueues a request for the committee to
|
|
108
|
+
* send `amountSats` to `bitcoinAddress` on the Bitcoin network. Signs
|
|
109
|
+
* with `signer` and submits, returning the execution result including
|
|
110
|
+
* `effects` and `events` (`WithdrawalRequestedEvent`).
|
|
111
|
+
*
|
|
112
|
+
* The method runs three preflight stages before signing:
|
|
113
|
+
*
|
|
114
|
+
* 1. **Address decoding** — `bitcoinAddress` is decoded as bech32 (v0
|
|
115
|
+
* P2WPKH, 20 bytes) or bech32m (v1 P2TR, 32 bytes) via
|
|
116
|
+
* `bitcoinAddressToWitnessProgram`. The HRP must match the client's
|
|
117
|
+
* configured Bitcoin network. Violations throw
|
|
118
|
+
* `InvalidBitcoinAddressError` with a structured `code` so callers
|
|
119
|
+
* can distinguish a typo from a wrong-network mistake.
|
|
120
|
+
* 2. **Pause check** — reads the governance snapshot via `view.all()`
|
|
121
|
+
* and throws `HashiPausedError` if `paused` is `true`. Mirrors the
|
|
122
|
+
* Move-side `hashi::assert_unpaused` in `request_withdrawal`.
|
|
123
|
+
* 3. **Minimum check** — `amountSats` must be ≥
|
|
124
|
+
* `snap.bitcoinWithdrawalMinimum`. Below-minimum throws
|
|
125
|
+
* `AmountBelowMinimumError` with a single violation. Mirrors
|
|
126
|
+
* `EBelowMinimumWithdrawal` in `withdraw::request_withdrawal`.
|
|
127
|
+
*
|
|
128
|
+
* For composable flows (sponsored tx, dry-run, or bundling into a
|
|
129
|
+
* larger PTB), use `tx.requestWithdrawal(options)` instead — it returns
|
|
130
|
+
* the unsigned `Transaction` and leaves signing to the caller.
|
|
131
|
+
*/
|
|
132
|
+
requestWithdrawal({
|
|
133
|
+
signer,
|
|
134
|
+
...params
|
|
135
|
+
}: WithdrawalParams & {
|
|
136
|
+
/** Signs and pays for the resulting transaction. The signer's address becomes the tx sender. */signer: Signer;
|
|
137
|
+
}): Promise<SuiClientTypes.TransactionResult<{
|
|
138
|
+
effects: true;
|
|
139
|
+
events: true;
|
|
140
|
+
}>>;
|
|
141
|
+
/**
|
|
142
|
+
* Cancel a pending withdrawal request and return the locked BTC to the
|
|
143
|
+
* signer. Signs with `signer` and submits, returning the execution result.
|
|
144
|
+
*
|
|
145
|
+
* The only client-side precondition is that `requestId` is 0x-prefixed
|
|
146
|
+
* 32-byte hex. All other constraints — ownership (only the original
|
|
147
|
+
* requester can cancel), state window (cancellable only while `Requested`
|
|
148
|
+
* or `Approved`, not after committee commitment), and the on-chain
|
|
149
|
+
* `withdrawal_cancellation_cooldown_ms` — are enforced by the Move side
|
|
150
|
+
* and left to abort at execution. Pre-fetching them would cost an extra
|
|
151
|
+
* round-trip and still race chain state.
|
|
152
|
+
*
|
|
153
|
+
* Unlike `requestWithdrawal`, no pause check is performed: the Move
|
|
154
|
+
* `cancel_withdrawal` function has no `assert_unpaused`, so users can
|
|
155
|
+
* always unwind a pending request even when the system is paused.
|
|
156
|
+
*
|
|
157
|
+
* For composable flows, use `tx.cancelWithdrawal({ requestId, recipient })`.
|
|
158
|
+
*/
|
|
159
|
+
cancelWithdrawal({
|
|
160
|
+
signer,
|
|
161
|
+
requestId
|
|
162
|
+
}: CancelWithdrawalParams & {
|
|
163
|
+
/** Signs and pays for the resulting transaction. The signer's address becomes the tx sender and the recipient of the returned BTC. */signer: Signer;
|
|
164
|
+
}): Promise<SuiClientTypes.TransactionResult<{
|
|
165
|
+
effects: true;
|
|
166
|
+
events: true;
|
|
167
|
+
}>>;
|
|
168
|
+
tx: {
|
|
169
|
+
/**
|
|
170
|
+
* Build a transaction that submits one or more Bitcoin deposits for
|
|
171
|
+
* committee confirmation, batched into a single Sui PTB.
|
|
172
|
+
*
|
|
173
|
+
* A single Bitcoin funding tx can pay the same deposit address on
|
|
174
|
+
* multiple outputs (e.g. change + donation, or a coinjoin). Rather
|
|
175
|
+
* than forcing the user to submit one Sui tx per output, this method
|
|
176
|
+
* accepts every qualifying output and emits a dedicated Move-call
|
|
177
|
+
* triple per UTXO:
|
|
178
|
+
*
|
|
179
|
+
* utxo::utxo_id(txid, vout_i) → UtxoId
|
|
180
|
+
* utxo::utxo(utxoId, amount_i, derivationPath = recipient) → Utxo
|
|
181
|
+
* deposit::deposit(hashi, utxo)
|
|
182
|
+
*
|
|
183
|
+
* The triples are emitted in `params.utxos` order, so N UTXOs yield
|
|
184
|
+
* exactly `3 * N` PTB commands.
|
|
185
|
+
*
|
|
186
|
+
* Because all triples live in one PTB, execution is atomic: either
|
|
187
|
+
* every deposit is recorded, or none are (any abort — wrong minimum,
|
|
188
|
+
* replayed UTXO, paused system — reverts the whole transaction).
|
|
189
|
+
*
|
|
190
|
+
* All UTXOs share the `txid` because `DepositParams` has a single
|
|
191
|
+
* top-level `txid` field, and are credited to the same `recipient`.
|
|
192
|
+
*
|
|
193
|
+
* The `txid` is provided in **display byte order** (the form
|
|
194
|
+
* mempool.space and `bitcoin-cli` show); this method reverses it
|
|
195
|
+
* to internal byte order before recording on-chain via
|
|
196
|
+
* `reverseTxidBytes`. The committee verifier reads `Utxo.txid`
|
|
197
|
+
* as a `bitcoin::Txid`, which expects internal byte order — so
|
|
198
|
+
* recording display-order bytes leaves the committee searching
|
|
199
|
+
* for a phantom (byte-reversed) tx and the deposit never confirms.
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* ```ts
|
|
203
|
+
* const tx = client.hashi.tx.deposit({
|
|
204
|
+
* txid: `0x${btcTxid}`,
|
|
205
|
+
* utxos: [
|
|
206
|
+
* { vout: 0, amountSats: 100_000n },
|
|
207
|
+
* { vout: 2, amountSats: 50_000n },
|
|
208
|
+
* ],
|
|
209
|
+
* recipient: signer.toSuiAddress(),
|
|
210
|
+
* });
|
|
211
|
+
* await client.signAndExecuteTransaction({ signer, transaction: tx });
|
|
212
|
+
* ```
|
|
213
|
+
*/
|
|
214
|
+
deposit: (params: DepositParams) => Transaction;
|
|
215
|
+
/**
|
|
216
|
+
* Build a transaction that submits a BTC withdrawal request. Sources the
|
|
217
|
+
* BTC via `coinWithBalance`, unwraps it into a `Balance<BTC>`, and passes
|
|
218
|
+
* it to `withdraw::request_withdrawal` along with the target Bitcoin
|
|
219
|
+
* output address.
|
|
220
|
+
*/
|
|
221
|
+
requestWithdrawal: (options: {
|
|
222
|
+
/** Amount in sats to withdraw. Must be ≥ the on-chain withdrawal minimum. */amount: bigint;
|
|
223
|
+
/**
|
|
224
|
+
* Target Bitcoin address as raw witness program bytes — 20 bytes for
|
|
225
|
+
* P2WPKH, 32 bytes for P2TR. Callers decode their own bech32(m)
|
|
226
|
+
* strings for now; a string-input overload may land in a follow-up.
|
|
227
|
+
*/
|
|
228
|
+
bitcoinAddress: Uint8Array;
|
|
229
|
+
}) => Transaction;
|
|
230
|
+
/**
|
|
231
|
+
* Build a transaction that cancels a pending withdrawal request and
|
|
232
|
+
* returns the locked BTC to the user. Consumes the `Balance<BTC>`
|
|
233
|
+
* hot-potato returned by `withdraw::cancel_withdrawal` by wrapping it
|
|
234
|
+
* into a `Coin<BTC>` and transferring to `recipient`.
|
|
235
|
+
*/
|
|
236
|
+
cancelWithdrawal: (options: {
|
|
237
|
+
/** The withdrawal request ID to cancel. */requestId: string;
|
|
238
|
+
/**
|
|
239
|
+
* Sui address that will receive the returned `Coin<BTC>`. Required
|
|
240
|
+
* because the unsigned `Transaction` does not know its sender at
|
|
241
|
+
* build time — the caller must pass their own address explicitly.
|
|
242
|
+
*/
|
|
243
|
+
recipient: string;
|
|
244
|
+
}) => Transaction;
|
|
245
|
+
};
|
|
246
|
+
call: {
|
|
247
|
+
deposit: (options: {
|
|
248
|
+
utxo: RawTransactionArgument<string>;
|
|
249
|
+
}) => (tx: Transaction) => _mysten_sui_transactions0.TransactionResult;
|
|
250
|
+
requestWithdrawal: (options: {
|
|
251
|
+
btc: RawTransactionArgument<string>;
|
|
252
|
+
bitcoinAddress: RawTransactionArgument<number[]>;
|
|
253
|
+
}) => (tx: Transaction) => _mysten_sui_transactions0.TransactionResult;
|
|
254
|
+
/**
|
|
255
|
+
* Cancel a pending withdrawal request. Returns a `Balance<BTC>` hot potato
|
|
256
|
+
* that must be consumed in the same PTB (e.g. wrapped into a Coin and
|
|
257
|
+
* transferred back to the sender).
|
|
258
|
+
*/
|
|
259
|
+
cancelWithdrawal: (options: {
|
|
260
|
+
requestId: RawTransactionArgument<string>;
|
|
261
|
+
}) => (tx: Transaction) => _mysten_sui_transactions0.TransactionResult;
|
|
262
|
+
};
|
|
263
|
+
view: {
|
|
264
|
+
/**
|
|
265
|
+
* Fetches the MPC committee's threshold public key from on-chain.
|
|
266
|
+
*
|
|
267
|
+
* This is the 33-byte compressed secp256k1 key stored in `CommitteeSet.mpc_public_key`.
|
|
268
|
+
* It is set after the committee completes DKG and is updated at epoch boundaries.
|
|
269
|
+
*
|
|
270
|
+
* @returns 33-byte compressed secp256k1 public key
|
|
271
|
+
* @throws If the MPC key is not yet available (DKG not completed)
|
|
272
|
+
*/
|
|
273
|
+
mpcPublicKey: () => Promise<Uint8Array>;
|
|
274
|
+
/**
|
|
275
|
+
* Fetches all governance values in a single round-trip and returns a
|
|
276
|
+
* consistent snapshot. Prefer this over individual methods when you
|
|
277
|
+
* need 2+ values — it avoids redundant `Hashi.get` calls and gives
|
|
278
|
+
* you all fields from the same on-chain state.
|
|
279
|
+
*/
|
|
280
|
+
all: () => Promise<GovernanceConfig>;
|
|
281
|
+
paused: () => Promise<boolean>; /** Floored to `DUST_RELAY_MIN_VALUE` if the on-chain value is lower. */
|
|
282
|
+
bitcoinDepositMinimum: () => Promise<bigint>; /** Floored to `DUST_RELAY_MIN_VALUE + 1` so `worstCaseNetworkFee` is always ≥ 1. */
|
|
283
|
+
bitcoinWithdrawalMinimum: () => Promise<bigint>;
|
|
284
|
+
bitcoinConfirmationThreshold: () => Promise<bigint>;
|
|
285
|
+
withdrawalCancellationCooldownMs: () => Promise<bigint>;
|
|
286
|
+
bitcoinChainId: () => Promise<string>; /** Alias of `bitcoinDepositMinimum`. */
|
|
287
|
+
depositMinimum: () => Promise<bigint>;
|
|
288
|
+
/**
|
|
289
|
+
* Worst-case Bitcoin miner fee (sats) deducted from a withdrawal.
|
|
290
|
+
* Derived as `bitcoinWithdrawalMinimum - DUST_RELAY_MIN_VALUE`; always ≥ 1.
|
|
291
|
+
*/
|
|
292
|
+
worstCaseNetworkFee: () => Promise<bigint>;
|
|
293
|
+
/**
|
|
294
|
+
* Check whether one or more Bitcoin UTXOs already exist in the
|
|
295
|
+
* on-chain `UtxoPool` — either as active (confirmed deposit, not yet
|
|
296
|
+
* consumed) or spent (consumed by a withdrawal). Callers use this to
|
|
297
|
+
* detect already-used outputs before submitting a deposit.
|
|
298
|
+
*
|
|
299
|
+
* `txid` in each `UtxoId` is **display byte order**; the method
|
|
300
|
+
* reverses to internal byte order before encoding the on-chain key.
|
|
301
|
+
*/
|
|
302
|
+
findUsedUtxos: (utxos: readonly UtxoId[]) => Promise<UtxoUsageResult[]>;
|
|
303
|
+
/**
|
|
304
|
+
* Fetch the unified transaction history (deposits + withdrawals) for
|
|
305
|
+
* a Sui address. Reads from `BitcoinState.user_requests` — a Table
|
|
306
|
+
* that maps each user address to a Bag of request IDs — then batch-
|
|
307
|
+
* fetches the request objects and dispatches by Move type.
|
|
308
|
+
*
|
|
309
|
+
* Deposit items include `btcTxid` / `btcVout` extracted from the
|
|
310
|
+
* UTXO stored on the `DepositRequest`. Withdrawal items include the
|
|
311
|
+
* BTC txid from the linked `WithdrawalTransaction` once the committee
|
|
312
|
+
* has committed one.
|
|
313
|
+
*/
|
|
314
|
+
transactionHistory: (suiAddress: string) => Promise<TransactionHistoryItem[]>;
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
//#endregion
|
|
318
|
+
export { HashiClient, hashi };
|