@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,131 @@
|
|
|
1
|
+
import { MoveEnum, MoveStruct } from "../utils/index.mjs";
|
|
2
|
+
import { ObjectBag } from "./deps/sui/object_bag.mjs";
|
|
3
|
+
import { Utxo, UtxoId } from "./utxo.mjs";
|
|
4
|
+
import { Balance } from "./deps/sui/balance.mjs";
|
|
5
|
+
import { bcs } from "@mysten/sui/bcs";
|
|
6
|
+
|
|
7
|
+
//#region src/contracts/hashi/withdrawal_queue.ts
|
|
8
|
+
/**************************************************************
|
|
9
|
+
* THIS FILE IS GENERATED AND SHOULD NOT BE MANUALLY MODIFIED *
|
|
10
|
+
**************************************************************/
|
|
11
|
+
const $moduleName = "@local-pkg/hashi::withdrawal_queue";
|
|
12
|
+
const WithdrawalRequestQueue = new MoveStruct({
|
|
13
|
+
name: `${$moduleName}::WithdrawalRequestQueue`,
|
|
14
|
+
fields: {
|
|
15
|
+
requests: ObjectBag,
|
|
16
|
+
processed: ObjectBag,
|
|
17
|
+
withdrawal_txns: ObjectBag,
|
|
18
|
+
confirmed_txns: ObjectBag
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
const OutputUtxo = new MoveStruct({
|
|
22
|
+
name: `${$moduleName}::OutputUtxo`,
|
|
23
|
+
fields: {
|
|
24
|
+
amount: bcs.u64(),
|
|
25
|
+
bitcoin_address: bcs.vector(bcs.u8())
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
const WithdrawalStatus = new MoveEnum({
|
|
29
|
+
name: `${$moduleName}::WithdrawalStatus`,
|
|
30
|
+
fields: {
|
|
31
|
+
Requested: null,
|
|
32
|
+
Approved: null,
|
|
33
|
+
Processing: null,
|
|
34
|
+
Signed: null,
|
|
35
|
+
Confirmed: null
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
const WithdrawalRequest = new MoveStruct({
|
|
39
|
+
name: `${$moduleName}::WithdrawalRequest`,
|
|
40
|
+
fields: {
|
|
41
|
+
id: bcs.Address,
|
|
42
|
+
sender: bcs.Address,
|
|
43
|
+
btc_amount: bcs.u64(),
|
|
44
|
+
bitcoin_address: bcs.vector(bcs.u8()),
|
|
45
|
+
timestamp_ms: bcs.u64(),
|
|
46
|
+
status: WithdrawalStatus,
|
|
47
|
+
withdrawal_txn_id: bcs.option(bcs.Address),
|
|
48
|
+
sui_tx_digest: bcs.vector(bcs.u8()),
|
|
49
|
+
btc: Balance
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
const WithdrawalTransaction = new MoveStruct({
|
|
53
|
+
name: `${$moduleName}::WithdrawalTransaction`,
|
|
54
|
+
fields: {
|
|
55
|
+
id: bcs.Address,
|
|
56
|
+
txid: bcs.Address,
|
|
57
|
+
request_ids: bcs.vector(bcs.Address),
|
|
58
|
+
inputs: bcs.vector(Utxo),
|
|
59
|
+
withdrawal_outputs: bcs.vector(OutputUtxo),
|
|
60
|
+
change_output: bcs.option(OutputUtxo),
|
|
61
|
+
timestamp_ms: bcs.u64(),
|
|
62
|
+
randomness: bcs.vector(bcs.u8()),
|
|
63
|
+
signatures: bcs.option(bcs.vector(bcs.vector(bcs.u8()))),
|
|
64
|
+
presig_start_index: bcs.u64(),
|
|
65
|
+
epoch: bcs.u64()
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
const CommittedRequestInfo = new MoveStruct({
|
|
69
|
+
name: `${$moduleName}::CommittedRequestInfo`,
|
|
70
|
+
fields: {
|
|
71
|
+
btc_amount: bcs.u64(),
|
|
72
|
+
bitcoin_address: bcs.vector(bcs.u8())
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
const WithdrawalRequestedEvent = new MoveStruct({
|
|
76
|
+
name: `${$moduleName}::WithdrawalRequestedEvent`,
|
|
77
|
+
fields: {
|
|
78
|
+
request_id: bcs.Address,
|
|
79
|
+
btc_amount: bcs.u64(),
|
|
80
|
+
bitcoin_address: bcs.vector(bcs.u8()),
|
|
81
|
+
timestamp_ms: bcs.u64(),
|
|
82
|
+
requester_address: bcs.Address,
|
|
83
|
+
sui_tx_digest: bcs.vector(bcs.u8())
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
const WithdrawalApprovedEvent = new MoveStruct({
|
|
87
|
+
name: `${$moduleName}::WithdrawalApprovedEvent`,
|
|
88
|
+
fields: { request_id: bcs.Address }
|
|
89
|
+
});
|
|
90
|
+
const WithdrawalPickedForProcessingEvent = new MoveStruct({
|
|
91
|
+
name: `${$moduleName}::WithdrawalPickedForProcessingEvent`,
|
|
92
|
+
fields: {
|
|
93
|
+
withdrawal_txn_id: bcs.Address,
|
|
94
|
+
txid: bcs.Address,
|
|
95
|
+
request_ids: bcs.vector(bcs.Address),
|
|
96
|
+
inputs: bcs.vector(Utxo),
|
|
97
|
+
withdrawal_outputs: bcs.vector(OutputUtxo),
|
|
98
|
+
change_output: bcs.option(OutputUtxo),
|
|
99
|
+
timestamp_ms: bcs.u64(),
|
|
100
|
+
randomness: bcs.vector(bcs.u8())
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
const WithdrawalSignedEvent = new MoveStruct({
|
|
104
|
+
name: `${$moduleName}::WithdrawalSignedEvent`,
|
|
105
|
+
fields: {
|
|
106
|
+
withdrawal_txn_id: bcs.Address,
|
|
107
|
+
request_ids: bcs.vector(bcs.Address),
|
|
108
|
+
signatures: bcs.vector(bcs.vector(bcs.u8()))
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
const WithdrawalConfirmedEvent = new MoveStruct({
|
|
112
|
+
name: `${$moduleName}::WithdrawalConfirmedEvent`,
|
|
113
|
+
fields: {
|
|
114
|
+
withdrawal_txn_id: bcs.Address,
|
|
115
|
+
txid: bcs.Address,
|
|
116
|
+
change_utxo_id: bcs.option(UtxoId),
|
|
117
|
+
request_ids: bcs.vector(bcs.Address),
|
|
118
|
+
change_utxo_amount: bcs.option(bcs.u64())
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
const WithdrawalCancelledEvent = new MoveStruct({
|
|
122
|
+
name: `${$moduleName}::WithdrawalCancelledEvent`,
|
|
123
|
+
fields: {
|
|
124
|
+
request_id: bcs.Address,
|
|
125
|
+
requester_address: bcs.Address,
|
|
126
|
+
btc_amount: bcs.u64()
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
//#endregion
|
|
131
|
+
export { OutputUtxo, WithdrawalRequest, WithdrawalRequestQueue, WithdrawalTransaction };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { BcsEnum, BcsStruct } from "@mysten/sui/bcs";
|
|
2
|
+
import { TransactionArgument } from "@mysten/sui/transactions";
|
|
3
|
+
import { ClientWithCoreApi, SuiClientTypes } from "@mysten/sui/client";
|
|
4
|
+
|
|
5
|
+
//#region src/contracts/utils/index.d.ts
|
|
6
|
+
type RawTransactionArgument<T> = T | TransactionArgument;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { RawTransactionArgument };
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { BcsEnum, BcsStruct, TypeTagSerializer, bcs } from "@mysten/sui/bcs";
|
|
2
|
+
import { normalizeSuiAddress } from "@mysten/sui/utils";
|
|
3
|
+
import { isArgument } from "@mysten/sui/transactions";
|
|
4
|
+
|
|
5
|
+
//#region src/contracts/utils/index.ts
|
|
6
|
+
const MOVE_STDLIB_ADDRESS = normalizeSuiAddress("0x1");
|
|
7
|
+
const SUI_FRAMEWORK_ADDRESS = normalizeSuiAddress("0x2");
|
|
8
|
+
function getPureBcsSchema(typeTag) {
|
|
9
|
+
const parsedTag = typeof typeTag === "string" ? TypeTagSerializer.parseFromStr(typeTag) : typeTag;
|
|
10
|
+
if ("u8" in parsedTag) return bcs.U8;
|
|
11
|
+
else if ("u16" in parsedTag) return bcs.U16;
|
|
12
|
+
else if ("u32" in parsedTag) return bcs.U32;
|
|
13
|
+
else if ("u64" in parsedTag) return bcs.U64;
|
|
14
|
+
else if ("u128" in parsedTag) return bcs.U128;
|
|
15
|
+
else if ("u256" in parsedTag) return bcs.U256;
|
|
16
|
+
else if ("address" in parsedTag) return bcs.Address;
|
|
17
|
+
else if ("bool" in parsedTag) return bcs.Bool;
|
|
18
|
+
else if ("vector" in parsedTag) {
|
|
19
|
+
const type = getPureBcsSchema(parsedTag.vector);
|
|
20
|
+
return type ? bcs.vector(type) : null;
|
|
21
|
+
} else if ("struct" in parsedTag) {
|
|
22
|
+
const structTag = parsedTag.struct;
|
|
23
|
+
const pkg = normalizeSuiAddress(structTag.address);
|
|
24
|
+
if (pkg === MOVE_STDLIB_ADDRESS) {
|
|
25
|
+
if ((structTag.module === "ascii" || structTag.module === "string") && structTag.name === "String") return bcs.String;
|
|
26
|
+
if (structTag.module === "option" && structTag.name === "Option") {
|
|
27
|
+
const type = getPureBcsSchema(structTag.typeParams[0]);
|
|
28
|
+
return type ? bcs.option(type) : null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
if (pkg === SUI_FRAMEWORK_ADDRESS && structTag.module === "object" && (structTag.name === "ID" || structTag.name === "UID")) return bcs.Address;
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
function normalizeMoveArguments(args, argTypes, parameterNames) {
|
|
36
|
+
const argLen = Array.isArray(args) ? args.length : Object.keys(args).length;
|
|
37
|
+
if (parameterNames && argLen !== parameterNames.length) throw new Error(`Invalid number of arguments, expected ${parameterNames.length}, got ${argLen}`);
|
|
38
|
+
const normalizedArgs = [];
|
|
39
|
+
let index = 0;
|
|
40
|
+
for (const [i, argType] of argTypes.entries()) {
|
|
41
|
+
if (argType === "0x2::clock::Clock") {
|
|
42
|
+
normalizedArgs.push((tx) => tx.object.clock());
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
if (argType === "0x2::random::Random") {
|
|
46
|
+
normalizedArgs.push((tx) => tx.object.random());
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (argType === "0x2::deny_list::DenyList") {
|
|
50
|
+
normalizedArgs.push((tx) => tx.object.denyList());
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (argType === "0x3::sui_system::SuiSystemState") {
|
|
54
|
+
normalizedArgs.push((tx) => tx.object.system());
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
let arg;
|
|
58
|
+
if (Array.isArray(args)) {
|
|
59
|
+
if (index >= args.length) throw new Error(`Invalid number of arguments, expected at least ${index + 1}, got ${args.length}`);
|
|
60
|
+
arg = args[index];
|
|
61
|
+
} else {
|
|
62
|
+
if (!parameterNames) throw new Error(`Expected arguments to be passed as an array`);
|
|
63
|
+
const name = parameterNames[index];
|
|
64
|
+
arg = args[name];
|
|
65
|
+
if (arg === void 0) throw new Error(`Parameter ${name} is required`);
|
|
66
|
+
}
|
|
67
|
+
index += 1;
|
|
68
|
+
if (typeof arg === "function" || isArgument(arg)) {
|
|
69
|
+
normalizedArgs.push(arg);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
const type = argTypes[i];
|
|
73
|
+
const bcsType = type === null ? null : getPureBcsSchema(type);
|
|
74
|
+
if (bcsType) {
|
|
75
|
+
const bytes = bcsType.serialize(arg);
|
|
76
|
+
normalizedArgs.push((tx) => tx.pure(bytes));
|
|
77
|
+
continue;
|
|
78
|
+
} else if (typeof arg === "string") {
|
|
79
|
+
normalizedArgs.push((tx) => tx.object(arg));
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
throw new Error(`Invalid argument ${stringify(arg)} for type ${type}`);
|
|
83
|
+
}
|
|
84
|
+
return normalizedArgs;
|
|
85
|
+
}
|
|
86
|
+
var MoveStruct = class extends BcsStruct {
|
|
87
|
+
async get({ objectId, ...options }) {
|
|
88
|
+
const [res] = await this.getMany({
|
|
89
|
+
...options,
|
|
90
|
+
objectIds: [objectId]
|
|
91
|
+
});
|
|
92
|
+
return res;
|
|
93
|
+
}
|
|
94
|
+
async getMany({ client, ...options }) {
|
|
95
|
+
return (await client.core.getObjects({
|
|
96
|
+
...options,
|
|
97
|
+
include: {
|
|
98
|
+
...options.include,
|
|
99
|
+
content: true
|
|
100
|
+
}
|
|
101
|
+
})).objects.map((obj) => {
|
|
102
|
+
if (obj instanceof Error) throw obj;
|
|
103
|
+
return {
|
|
104
|
+
...obj,
|
|
105
|
+
json: this.parse(obj.content)
|
|
106
|
+
};
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
var MoveEnum = class extends BcsEnum {};
|
|
111
|
+
function stringify(val) {
|
|
112
|
+
if (typeof val === "object") return JSON.stringify(val, (val$1) => val$1);
|
|
113
|
+
if (typeof val === "bigint") return val.toString();
|
|
114
|
+
return val;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
//#endregion
|
|
118
|
+
export { MoveEnum, MoveStruct, normalizeMoveArguments };
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
//#region src/errors.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Custom error classes thrown by the Hashi SDK. Consumers can `instanceof`-check
|
|
4
|
+
* these to distinguish SDK-structured failures (missing/malformed on-chain data,
|
|
5
|
+
* chain-fetch failures) from generic runtime errors.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Thrown when a governance config entry on-chain is missing, has an unexpected
|
|
9
|
+
* variant, or has a payload that cannot be decoded to the expected type.
|
|
10
|
+
* Carries the offending `key` and `expectedVariant` as structured fields so
|
|
11
|
+
* callers can react programmatically without string-parsing the message.
|
|
12
|
+
*/
|
|
13
|
+
declare class HashiConfigError extends Error {
|
|
14
|
+
readonly key: string;
|
|
15
|
+
readonly expectedVariant: string;
|
|
16
|
+
readonly actualVariant?: string;
|
|
17
|
+
constructor(message: string, details: {
|
|
18
|
+
key: string;
|
|
19
|
+
expectedVariant: string;
|
|
20
|
+
actualVariant?: string;
|
|
21
|
+
}, options?: {
|
|
22
|
+
cause?: unknown;
|
|
23
|
+
});
|
|
24
|
+
static missing(key: string, expectedVariant: string): HashiConfigError;
|
|
25
|
+
static wrongVariant(key: string, expectedVariant: string, actualVariant: string): HashiConfigError;
|
|
26
|
+
static malformedPayload(key: string, expectedVariant: string, detail: string, cause?: unknown): HashiConfigError;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Thrown when fetching the Hashi shared object fails or returns an
|
|
30
|
+
* unexpectedly shaped response. Wraps the underlying Sui-client error via
|
|
31
|
+
* `cause` so callers can still access the network-layer detail.
|
|
32
|
+
*/
|
|
33
|
+
declare class HashiFetchError extends Error {
|
|
34
|
+
readonly hashiObjectId: string;
|
|
35
|
+
constructor(message: string, hashiObjectId: string, options?: {
|
|
36
|
+
cause?: unknown;
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* One amount that failed a client-side minimum check. `vout` is present for
|
|
41
|
+
* deposit-UTXO violations (so callers can map each offender back to a Bitcoin
|
|
42
|
+
* output) and absent for withdrawal violations, where there is a single
|
|
43
|
+
* top-level amount and no output index.
|
|
44
|
+
*/
|
|
45
|
+
interface AmountViolation {
|
|
46
|
+
readonly amount: bigint;
|
|
47
|
+
readonly minimum: bigint;
|
|
48
|
+
readonly vout?: number;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Thrown by `HashiClient.deposit()` and `HashiClient.requestWithdrawal()` when
|
|
52
|
+
* one or more amounts are below the live on-chain minimum. Deposits may carry
|
|
53
|
+
* multiple violations (one per offending UTXO) so callers can fix the whole
|
|
54
|
+
* batch in one round-trip; withdrawals always carry exactly one. Raised after
|
|
55
|
+
* the governance snapshot is fetched but before any PTB is built — mirrors
|
|
56
|
+
* the Move-side `EBelowMinimumDeposit` / `EBelowMinimumWithdrawal` aborts.
|
|
57
|
+
*/
|
|
58
|
+
declare class AmountBelowMinimumError extends Error {
|
|
59
|
+
readonly violations: readonly AmountViolation[];
|
|
60
|
+
constructor(details: {
|
|
61
|
+
violations: readonly AmountViolation[];
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Thrown by user-facing entry points when `paused` is `true` in the governance
|
|
66
|
+
* config snapshot. Mirrors the Move-side `ESystemPaused` abort in
|
|
67
|
+
* `hashi::assert_unpaused` so the SDK can fail early with a typed error
|
|
68
|
+
* instead of a gas-burning on-chain abort.
|
|
69
|
+
*/
|
|
70
|
+
declare class HashiPausedError extends Error {
|
|
71
|
+
readonly operation?: string;
|
|
72
|
+
constructor(details?: {
|
|
73
|
+
operation?: string;
|
|
74
|
+
}, options?: {
|
|
75
|
+
cause?: unknown;
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Thrown by `HashiClient` direct methods when the caller-supplied params
|
|
80
|
+
* don't meet structural preconditions (empty `utxos`, duplicate `vout`,
|
|
81
|
+
* malformed hex). Raised before any chain read so even a paused or
|
|
82
|
+
* unreachable protocol surfaces the client-side bug first.
|
|
83
|
+
*/
|
|
84
|
+
declare class InvalidParamsError extends Error {
|
|
85
|
+
readonly reason: string;
|
|
86
|
+
readonly detail?: string;
|
|
87
|
+
constructor(details: {
|
|
88
|
+
reason: string;
|
|
89
|
+
detail?: string;
|
|
90
|
+
}, options?: {
|
|
91
|
+
cause?: unknown;
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Stable discriminator for `InvalidBitcoinAddressError`. Callers can switch on
|
|
96
|
+
* `code` to surface targeted UX (e.g. "wrong network" → prompt the user to
|
|
97
|
+
* switch, "bad-checksum" → flag a typo) without string-parsing the message.
|
|
98
|
+
*/
|
|
99
|
+
type InvalidBitcoinAddressCode = "malformed" | "bad-checksum" | "wrong-network" | "unsupported-version" | "bad-program-length";
|
|
100
|
+
/**
|
|
101
|
+
* Thrown when a user-supplied Bitcoin address cannot be decoded into a
|
|
102
|
+
* witness program that the Hashi withdrawal path accepts. `code` is a stable
|
|
103
|
+
* discriminator suitable for programmatic handling; `address` echoes the
|
|
104
|
+
* offending input back so callers can display it.
|
|
105
|
+
*/
|
|
106
|
+
declare class InvalidBitcoinAddressError extends Error {
|
|
107
|
+
readonly address: string;
|
|
108
|
+
readonly code: InvalidBitcoinAddressCode;
|
|
109
|
+
constructor(details: {
|
|
110
|
+
address: string;
|
|
111
|
+
code: InvalidBitcoinAddressCode;
|
|
112
|
+
message: string;
|
|
113
|
+
}, options?: {
|
|
114
|
+
cause?: unknown;
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
//#endregion
|
|
118
|
+
export { AmountBelowMinimumError, AmountViolation, HashiConfigError, HashiFetchError, HashiPausedError, InvalidBitcoinAddressCode, InvalidBitcoinAddressError, InvalidParamsError };
|
package/dist/errors.mjs
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
//#region src/errors.ts
|
|
2
|
+
/**
|
|
3
|
+
* Custom error classes thrown by the Hashi SDK. Consumers can `instanceof`-check
|
|
4
|
+
* these to distinguish SDK-structured failures (missing/malformed on-chain data,
|
|
5
|
+
* chain-fetch failures) from generic runtime errors.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Thrown when a governance config entry on-chain is missing, has an unexpected
|
|
9
|
+
* variant, or has a payload that cannot be decoded to the expected type.
|
|
10
|
+
* Carries the offending `key` and `expectedVariant` as structured fields so
|
|
11
|
+
* callers can react programmatically without string-parsing the message.
|
|
12
|
+
*/
|
|
13
|
+
var HashiConfigError = class HashiConfigError extends Error {
|
|
14
|
+
constructor(message, details, options) {
|
|
15
|
+
super(message, options);
|
|
16
|
+
this.name = "HashiConfigError";
|
|
17
|
+
this.key = details.key;
|
|
18
|
+
this.expectedVariant = details.expectedVariant;
|
|
19
|
+
this.actualVariant = details.actualVariant;
|
|
20
|
+
}
|
|
21
|
+
static missing(key, expectedVariant) {
|
|
22
|
+
return new HashiConfigError(`Config key "${key}" not found on-chain.`, {
|
|
23
|
+
key,
|
|
24
|
+
expectedVariant
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
static wrongVariant(key, expectedVariant, actualVariant) {
|
|
28
|
+
return new HashiConfigError(`Config key "${key}" is ${actualVariant}, expected ${expectedVariant}.`, {
|
|
29
|
+
key,
|
|
30
|
+
expectedVariant,
|
|
31
|
+
actualVariant
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
static malformedPayload(key, expectedVariant, detail, cause) {
|
|
35
|
+
return new HashiConfigError(`Config key "${key}" ${expectedVariant} payload is malformed: ${detail}.`, {
|
|
36
|
+
key,
|
|
37
|
+
expectedVariant,
|
|
38
|
+
actualVariant: expectedVariant
|
|
39
|
+
}, { cause });
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Thrown when fetching the Hashi shared object fails or returns an
|
|
44
|
+
* unexpectedly shaped response. Wraps the underlying Sui-client error via
|
|
45
|
+
* `cause` so callers can still access the network-layer detail.
|
|
46
|
+
*/
|
|
47
|
+
var HashiFetchError = class extends Error {
|
|
48
|
+
constructor(message, hashiObjectId, options) {
|
|
49
|
+
super(message, options);
|
|
50
|
+
this.name = "HashiFetchError";
|
|
51
|
+
this.hashiObjectId = hashiObjectId;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Thrown by `HashiClient.deposit()` and `HashiClient.requestWithdrawal()` when
|
|
56
|
+
* one or more amounts are below the live on-chain minimum. Deposits may carry
|
|
57
|
+
* multiple violations (one per offending UTXO) so callers can fix the whole
|
|
58
|
+
* batch in one round-trip; withdrawals always carry exactly one. Raised after
|
|
59
|
+
* the governance snapshot is fetched but before any PTB is built — mirrors
|
|
60
|
+
* the Move-side `EBelowMinimumDeposit` / `EBelowMinimumWithdrawal` aborts.
|
|
61
|
+
*/
|
|
62
|
+
var AmountBelowMinimumError = class extends Error {
|
|
63
|
+
constructor(details) {
|
|
64
|
+
const { violations } = details;
|
|
65
|
+
const head = violations[0];
|
|
66
|
+
let summary;
|
|
67
|
+
if (violations.length === 1) summary = head.vout === void 0 ? `Amount ${head.amount} sats is below the protocol minimum of ${head.minimum} sats.` : `UTXO at vout ${head.vout} has amount ${head.amount} sats, below the protocol minimum of ${head.minimum} sats.`;
|
|
68
|
+
else summary = `${violations.length} UTXOs are below the protocol minimum (${head.minimum} sats): ${violations.map((v) => v.vout === void 0 ? `${v.amount} sats` : `vout ${v.vout} = ${v.amount} sats`).join(", ")}.`;
|
|
69
|
+
super(summary);
|
|
70
|
+
this.name = "AmountBelowMinimumError";
|
|
71
|
+
this.violations = violations;
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Thrown by user-facing entry points when `paused` is `true` in the governance
|
|
76
|
+
* config snapshot. Mirrors the Move-side `ESystemPaused` abort in
|
|
77
|
+
* `hashi::assert_unpaused` so the SDK can fail early with a typed error
|
|
78
|
+
* instead of a gas-burning on-chain abort.
|
|
79
|
+
*/
|
|
80
|
+
var HashiPausedError = class extends Error {
|
|
81
|
+
constructor(details, options) {
|
|
82
|
+
const op = details?.operation;
|
|
83
|
+
super(op ? `Hashi protocol is currently paused; cannot ${op}.` : "Hashi protocol is currently paused.", options);
|
|
84
|
+
this.name = "HashiPausedError";
|
|
85
|
+
this.operation = op;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Thrown by `HashiClient` direct methods when the caller-supplied params
|
|
90
|
+
* don't meet structural preconditions (empty `utxos`, duplicate `vout`,
|
|
91
|
+
* malformed hex). Raised before any chain read so even a paused or
|
|
92
|
+
* unreachable protocol surfaces the client-side bug first.
|
|
93
|
+
*/
|
|
94
|
+
var InvalidParamsError = class extends Error {
|
|
95
|
+
constructor(details, options) {
|
|
96
|
+
super(details.detail ? `${details.reason}: ${details.detail}` : details.reason, options);
|
|
97
|
+
this.name = "InvalidParamsError";
|
|
98
|
+
this.reason = details.reason;
|
|
99
|
+
this.detail = details.detail;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
/**
|
|
103
|
+
* Thrown when a user-supplied Bitcoin address cannot be decoded into a
|
|
104
|
+
* witness program that the Hashi withdrawal path accepts. `code` is a stable
|
|
105
|
+
* discriminator suitable for programmatic handling; `address` echoes the
|
|
106
|
+
* offending input back so callers can display it.
|
|
107
|
+
*/
|
|
108
|
+
var InvalidBitcoinAddressError = class extends Error {
|
|
109
|
+
constructor(details, options) {
|
|
110
|
+
super(details.message, options);
|
|
111
|
+
this.name = "InvalidBitcoinAddressError";
|
|
112
|
+
this.address = details.address;
|
|
113
|
+
this.code = details.code;
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
//#endregion
|
|
118
|
+
export { AmountBelowMinimumError, HashiConfigError, HashiFetchError, HashiPausedError, InvalidBitcoinAddressError, InvalidParamsError };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { BitcoinNetwork, CancelWithdrawalParams, DepositHistoryItem, DepositParams, GovernanceConfig, HashiClientOptions, NetworkConfig, SuiNetwork, TransactionHistoryItem, UtxoId, UtxoOutput, UtxoUsageResult, WithdrawalHistoryItem, WithdrawalParams, WithdrawalStatus } from "./types.mjs";
|
|
2
|
+
import { HashiClient, hashi } from "./client.mjs";
|
|
3
|
+
import { AmountBelowMinimumError, AmountViolation, HashiConfigError, HashiFetchError, HashiPausedError, InvalidBitcoinAddressCode, InvalidBitcoinAddressError, InvalidParamsError } from "./errors.mjs";
|
|
4
|
+
import { arkworksToSec1Compressed, bitcoinAddressToWitnessProgram, deriveChildPubkey, generateDepositAddress, taprootScriptPathAddress } from "./bitcoin.mjs";
|
|
5
|
+
export { AmountBelowMinimumError, type AmountViolation, type BitcoinNetwork, type CancelWithdrawalParams, type DepositHistoryItem, type DepositParams, type GovernanceConfig, HashiClient, type HashiClientOptions, HashiConfigError, HashiFetchError, HashiPausedError, type InvalidBitcoinAddressCode, InvalidBitcoinAddressError, InvalidParamsError, type NetworkConfig, type SuiNetwork, type TransactionHistoryItem, type UtxoId, type UtxoOutput, type UtxoUsageResult, type WithdrawalHistoryItem, type WithdrawalParams, type WithdrawalStatus, arkworksToSec1Compressed, bitcoinAddressToWitnessProgram, deriveChildPubkey, generateDepositAddress, hashi, taprootScriptPathAddress };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { AmountBelowMinimumError, HashiConfigError, HashiFetchError, HashiPausedError, InvalidBitcoinAddressError, InvalidParamsError } from "./errors.mjs";
|
|
2
|
+
import { arkworksToSec1Compressed, bitcoinAddressToWitnessProgram, deriveChildPubkey, generateDepositAddress, taprootScriptPathAddress } from "./bitcoin.mjs";
|
|
3
|
+
import { HashiClient, hashi } from "./client.mjs";
|
|
4
|
+
|
|
5
|
+
export { AmountBelowMinimumError, HashiClient, HashiConfigError, HashiFetchError, HashiPausedError, InvalidBitcoinAddressError, InvalidParamsError, arkworksToSec1Compressed, bitcoinAddressToWitnessProgram, deriveChildPubkey, generateDepositAddress, hashi, taprootScriptPathAddress };
|
package/dist/types.d.mts
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
type BitcoinNetwork = "mainnet" | "testnet" | "signet" | "regtest";
|
|
3
|
+
type SuiNetwork = "devnet" | "testnet" | "mainnet" | "localnet";
|
|
4
|
+
interface NetworkConfig {
|
|
5
|
+
hashiObjectId: string;
|
|
6
|
+
packageId: string;
|
|
7
|
+
bitcoinNetwork: BitcoinNetwork;
|
|
8
|
+
}
|
|
9
|
+
interface HashiClientOptions<Name = "HashiClient"> {
|
|
10
|
+
name?: Name;
|
|
11
|
+
/** Sui network — determines Hashi object IDs and default Bitcoin network. */
|
|
12
|
+
network: SuiNetwork;
|
|
13
|
+
/** Override the auto-resolved Hashi shared object ID (for custom/local deployments). */
|
|
14
|
+
hashiObjectId?: string;
|
|
15
|
+
/** Override the auto-resolved Hashi package ID (for custom/local deployments). */
|
|
16
|
+
packageId?: string;
|
|
17
|
+
/** Override the auto-resolved Bitcoin network for address encoding. */
|
|
18
|
+
bitcoinNetwork?: BitcoinNetwork;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Frozen snapshot of every governance-controlled protocol parameter, returned
|
|
22
|
+
* by `HashiClient.view.all()`. Fields are `readonly` because the snapshot is
|
|
23
|
+
* a point-in-time read from chain — mutating it locally cannot change on-chain
|
|
24
|
+
* state. `depositMinimum` is a Move-side alias of `bitcoinDepositMinimum`;
|
|
25
|
+
* `worstCaseNetworkFee` is derived as `bitcoinWithdrawalMinimum - 546` (the
|
|
26
|
+
* dust relay floor) and is always ≥ 1.
|
|
27
|
+
*/
|
|
28
|
+
interface GovernanceConfig {
|
|
29
|
+
readonly paused: boolean;
|
|
30
|
+
readonly bitcoinChainId: string;
|
|
31
|
+
readonly bitcoinDepositMinimum: bigint;
|
|
32
|
+
readonly bitcoinWithdrawalMinimum: bigint;
|
|
33
|
+
readonly bitcoinConfirmationThreshold: bigint;
|
|
34
|
+
readonly withdrawalCancellationCooldownMs: bigint;
|
|
35
|
+
readonly depositMinimum: bigint;
|
|
36
|
+
readonly worstCaseNetworkFee: bigint;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A single UTXO output within a Bitcoin transaction used to fund a deposit.
|
|
40
|
+
* `vout` is the output index (Bitcoin u32); `amountSats` must be ≥ the
|
|
41
|
+
* on-chain deposit minimum (enforced at deposit time).
|
|
42
|
+
*/
|
|
43
|
+
interface UtxoOutput {
|
|
44
|
+
readonly vout: number;
|
|
45
|
+
readonly amountSats: bigint;
|
|
46
|
+
}
|
|
47
|
+
/** Parameters for `HashiClient.deposit()` — one Bitcoin txid, one or more outputs paying the deposit address. */
|
|
48
|
+
interface DepositParams {
|
|
49
|
+
/**
|
|
50
|
+
* 0x-prefixed 32-byte Bitcoin txid of the funding transaction, in
|
|
51
|
+
* **display byte order** — the form mempool.space, blockstream.info,
|
|
52
|
+
* and `bitcoin-cli` show. The SDK reverses to internal byte order
|
|
53
|
+
* before recording on-chain (see `reverseTxidBytes` in `util.ts`).
|
|
54
|
+
*/
|
|
55
|
+
readonly txid: string;
|
|
56
|
+
/** UTXOs from `txid` that paid the deposit address (one per output to the address). */
|
|
57
|
+
readonly utxos: readonly UtxoOutput[];
|
|
58
|
+
/**
|
|
59
|
+
* Sui address that derived the deposit address and will receive the minted
|
|
60
|
+
* hBTC. Becomes the `derivation_path` of every `Utxo` built in the PTB.
|
|
61
|
+
*/
|
|
62
|
+
readonly recipient: string;
|
|
63
|
+
}
|
|
64
|
+
/** Parameters for `HashiClient.requestWithdrawal()`. */
|
|
65
|
+
interface WithdrawalParams {
|
|
66
|
+
/** Amount in satoshis to withdraw. Must be ≥ the on-chain withdrawal minimum. */
|
|
67
|
+
readonly amountSats: bigint;
|
|
68
|
+
/**
|
|
69
|
+
* Recipient Bitcoin address. Bech32 for P2WPKH (`bc1q…`, `tb1q…`) or
|
|
70
|
+
* bech32m for P2TR (`bc1p…`, `tb1p…`). Decoded client-side into a witness
|
|
71
|
+
* program and must match the client's configured Bitcoin network.
|
|
72
|
+
*/
|
|
73
|
+
readonly bitcoinAddress: string;
|
|
74
|
+
}
|
|
75
|
+
/** Parameters for `HashiClient.cancelWithdrawal()`. */
|
|
76
|
+
interface CancelWithdrawalParams {
|
|
77
|
+
/** 0x-prefixed 32-byte object ID of the pending withdrawal request. */
|
|
78
|
+
readonly requestId: string;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Identifies a single Bitcoin UTXO by its funding transaction and output
|
|
82
|
+
* index. `txid` is in **display byte order** — the form mempool.space,
|
|
83
|
+
* blockstream.info, and `bitcoin-cli` show.
|
|
84
|
+
*/
|
|
85
|
+
interface UtxoId {
|
|
86
|
+
/** 0x-prefixed 32-byte Bitcoin txid in display byte order. */
|
|
87
|
+
readonly txid: string;
|
|
88
|
+
/** Output index within the Bitcoin transaction (u32). */
|
|
89
|
+
readonly vout: number;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Result of checking a single UTXO against the on-chain `UtxoPool` bags.
|
|
93
|
+
* `inActivePool` means the UTXO is live (confirmed deposit, not yet
|
|
94
|
+
* consumed by a withdrawal); `inSpentPool` means it was consumed.
|
|
95
|
+
*/
|
|
96
|
+
interface UtxoUsageResult {
|
|
97
|
+
readonly utxoId: UtxoId;
|
|
98
|
+
readonly inActivePool: boolean;
|
|
99
|
+
readonly inSpentPool: boolean;
|
|
100
|
+
/** Convenience: `inActivePool || inSpentPool`. */
|
|
101
|
+
readonly isUsed: boolean;
|
|
102
|
+
}
|
|
103
|
+
/** Discriminated union of deposit and withdrawal history entries. */
|
|
104
|
+
type TransactionHistoryItem = DepositHistoryItem | WithdrawalHistoryItem;
|
|
105
|
+
interface DepositHistoryItem {
|
|
106
|
+
readonly kind: "deposit";
|
|
107
|
+
readonly requestId: string;
|
|
108
|
+
readonly sender: string;
|
|
109
|
+
readonly timestampMs: bigint;
|
|
110
|
+
readonly suiTxDigest: string;
|
|
111
|
+
readonly amountSats: bigint;
|
|
112
|
+
/** Bitcoin txid of the funding transaction, in display byte order. */
|
|
113
|
+
readonly btcTxid: string;
|
|
114
|
+
/** Output index within the funding transaction. */
|
|
115
|
+
readonly btcVout: number;
|
|
116
|
+
/** `true` once the committee has approved the deposit. */
|
|
117
|
+
readonly approved: boolean;
|
|
118
|
+
readonly approvalTimestampMs: bigint | null;
|
|
119
|
+
}
|
|
120
|
+
type WithdrawalStatus = "Requested" | "Approved" | "Processing" | "Signed" | "Confirmed";
|
|
121
|
+
interface WithdrawalHistoryItem {
|
|
122
|
+
readonly kind: "withdrawal";
|
|
123
|
+
readonly requestId: string;
|
|
124
|
+
readonly sender: string;
|
|
125
|
+
readonly btcAmountSats: bigint;
|
|
126
|
+
/** Raw witness program bytes of the destination Bitcoin address. */
|
|
127
|
+
readonly bitcoinAddress: Uint8Array;
|
|
128
|
+
readonly timestampMs: bigint;
|
|
129
|
+
readonly suiTxDigest: string;
|
|
130
|
+
readonly status: WithdrawalStatus;
|
|
131
|
+
/** Object ID of the linked `WithdrawalTransaction`, if one exists. */
|
|
132
|
+
readonly withdrawalTxnId: string | null;
|
|
133
|
+
/** Bitcoin txid from the `WithdrawalTransaction`, in display byte order. `null` until the committee commits. */
|
|
134
|
+
readonly btcTxid: string | null;
|
|
135
|
+
}
|
|
136
|
+
//#endregion
|
|
137
|
+
export { BitcoinNetwork, CancelWithdrawalParams, DepositHistoryItem, DepositParams, GovernanceConfig, HashiClientOptions, NetworkConfig, SuiNetwork, TransactionHistoryItem, UtxoId, UtxoOutput, UtxoUsageResult, WithdrawalHistoryItem, WithdrawalParams, WithdrawalStatus };
|