@utxopia/sdk 0.1.0-alpha.2 → 0.1.0-alpha.3
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/README.md +214 -108
- package/{packages/sdk/dist → dist}/client.d.ts +25 -1
- package/{packages/sdk/dist → dist}/client.js +36 -1
- package/{packages/sdk/dist → dist}/crypto-ed25519.d.ts +14 -0
- package/{packages/sdk/dist → dist}/crypto-ed25519.js +14 -0
- package/{packages/sdk/dist → dist}/index.d.ts +3 -3
- package/{packages/sdk/dist → dist}/index.js +3 -3
- package/{packages/sdk/dist → dist}/instructions.d.ts +28 -11
- package/{packages/sdk/dist → dist}/instructions.js +45 -15
- package/{packages/sdk/dist → dist}/stealth.d.ts +104 -9
- package/{packages/sdk/dist → dist}/stealth.js +146 -14
- package/{packages/sdk/dist → dist}/taproot.d.ts +39 -2
- package/{packages/sdk/dist → dist}/taproot.js +54 -2
- package/package.json +86 -63
- package/src/announcement-client.ts +457 -0
- package/src/auditor-ciphertext.ts +181 -0
- package/src/auditor.ts +409 -0
- package/src/bitcoin/ika.ts +103 -0
- package/src/bitcoin/index.ts +5 -0
- package/src/bound-params.ts +322 -0
- package/src/chadbuffer.ts +603 -0
- package/src/circomlibjs.d.ts +51 -0
- package/src/claim-link.ts +53 -0
- package/src/client.ts +638 -0
- package/src/commitment-tree.ts +736 -0
- package/src/config.ts +772 -0
- package/src/core/esplora.ts +332 -0
- package/src/core/mempool.ts +159 -0
- package/src/crypto-babyjub.ts +385 -0
- package/src/crypto-ed25519.ts +297 -0
- package/src/crypto.ts +199 -0
- package/src/event-client.ts +231 -0
- package/src/events.ts +384 -0
- package/src/explorer.ts +300 -0
- package/src/index.ts +902 -0
- package/src/instructions.ts +2820 -0
- package/src/keys.ts +1228 -0
- package/src/logger.ts +41 -0
- package/src/magicblock.ts +278 -0
- package/src/merkle.ts +197 -0
- package/src/note.ts +754 -0
- package/src/pda.ts +516 -0
- package/src/pool-state.ts +176 -0
- package/src/poseidon.ts +175 -0
- package/src/prover/index.ts +19 -0
- package/src/prover/mobile.ts +303 -0
- package/src/prover/web.ts +771 -0
- package/src/psbt.ts +333 -0
- package/src/selective-disclosure.ts +284 -0
- package/src/sender-memo.ts +343 -0
- package/src/snarkjs.d.ts +19 -0
- package/src/sns-resolver.ts +333 -0
- package/src/solana/connection.ts +189 -0
- package/src/solana/priority-fee.ts +201 -0
- package/src/spend-doc.ts +163 -0
- package/src/stealth.ts +1477 -0
- package/src/taproot.ts +707 -0
- package/src/token-registry.ts +207 -0
- package/src/utils/encoding.ts +33 -0
- package/src/vk-registry.ts +295 -0
- package/LICENSE +0 -21
- package/packages/btc-client/src/esplora-client.ts +0 -153
- package/packages/btc-client/src/index.ts +0 -3
- package/packages/btc-client/src/op-return.ts +0 -93
- package/packages/btc-client/src/types.ts +0 -112
- package/packages/sdk/README.md +0 -277
- /package/{packages/sdk/dist → dist}/announcement-client.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/announcement-client.js +0 -0
- /package/{packages/sdk/dist → dist}/auditor-ciphertext.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/auditor-ciphertext.js +0 -0
- /package/{packages/sdk/dist → dist}/auditor.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/auditor.js +0 -0
- /package/{packages/sdk/dist → dist}/bitcoin/ika.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/bitcoin/ika.js +0 -0
- /package/{packages/sdk/dist → dist}/bitcoin/index.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/bitcoin/index.js +0 -0
- /package/{packages/sdk/dist → dist}/bound-params.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/bound-params.js +0 -0
- /package/{packages/sdk/dist → dist}/chadbuffer.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/chadbuffer.js +0 -0
- /package/{packages/sdk/dist → dist}/claim-link.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/claim-link.js +0 -0
- /package/{packages/sdk/dist → dist}/commitment-tree.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/commitment-tree.js +0 -0
- /package/{packages/sdk/dist → dist}/config.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/config.js +0 -0
- /package/{packages/sdk/dist → dist}/core/esplora.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/core/esplora.js +0 -0
- /package/{packages/sdk/dist → dist}/core/mempool.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/core/mempool.js +0 -0
- /package/{packages/sdk/dist → dist}/crypto-babyjub.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/crypto-babyjub.js +0 -0
- /package/{packages/sdk/dist → dist}/crypto.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/crypto.js +0 -0
- /package/{packages/sdk/dist → dist}/event-client.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/event-client.js +0 -0
- /package/{packages/sdk/dist → dist}/events.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/events.js +0 -0
- /package/{packages/sdk/dist → dist}/explorer.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/explorer.js +0 -0
- /package/{packages/sdk/dist → dist}/keys.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/keys.js +0 -0
- /package/{packages/sdk/dist → dist}/logger.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/logger.js +0 -0
- /package/{packages/sdk/dist → dist}/magicblock.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/magicblock.js +0 -0
- /package/{packages/sdk/dist → dist}/merkle.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/merkle.js +0 -0
- /package/{packages/sdk/dist → dist}/note.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/note.js +0 -0
- /package/{packages/sdk/dist → dist}/pda.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/pda.js +0 -0
- /package/{packages/sdk/dist → dist}/pool-state.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/pool-state.js +0 -0
- /package/{packages/sdk/dist → dist}/poseidon.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/poseidon.js +0 -0
- /package/{packages/sdk/dist → dist}/prover/index.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/prover/index.js +0 -0
- /package/{packages/sdk/dist → dist}/prover/mobile.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/prover/mobile.js +0 -0
- /package/{packages/sdk/dist → dist}/prover/web.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/prover/web.js +0 -0
- /package/{packages/sdk/dist → dist}/psbt.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/psbt.js +0 -0
- /package/{packages/sdk/dist → dist}/selective-disclosure.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/selective-disclosure.js +0 -0
- /package/{packages/sdk/dist → dist}/sender-memo.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/sender-memo.js +0 -0
- /package/{packages/sdk/dist → dist}/sns-resolver.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/sns-resolver.js +0 -0
- /package/{packages/sdk/dist → dist}/solana/connection.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/solana/connection.js +0 -0
- /package/{packages/sdk/dist → dist}/solana/priority-fee.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/solana/priority-fee.js +0 -0
- /package/{packages/sdk/dist → dist}/spend-doc.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/spend-doc.js +0 -0
- /package/{packages/sdk/dist → dist}/token-registry.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/token-registry.js +0 -0
- /package/{packages/sdk/dist → dist}/utils/encoding.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/utils/encoding.js +0 -0
- /package/{packages/sdk/dist → dist}/vk-registry.d.ts +0 -0
- /package/{packages/sdk/dist → dist}/vk-registry.js +0 -0
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
BitcoinAddressInfo,
|
|
3
|
-
BitcoinClient,
|
|
4
|
-
BitcoinDepositCandidate,
|
|
5
|
-
BitcoinMerkleProof,
|
|
6
|
-
BitcoinNetwork,
|
|
7
|
-
BitcoinOutspend,
|
|
8
|
-
BitcoinTransaction,
|
|
9
|
-
BitcoinTxStatus,
|
|
10
|
-
BitcoinUtxo,
|
|
11
|
-
} from "./types";
|
|
12
|
-
import { extractUtxopiaDepositOpReturn } from "./op-return";
|
|
13
|
-
|
|
14
|
-
const NETWORK_URLS: Record<BitcoinNetwork, string> = {
|
|
15
|
-
mainnet: "https://mempool.space/api",
|
|
16
|
-
testnet: "https://mempool.space/testnet/api",
|
|
17
|
-
testnet4: "https://mempool.space/testnet4/api",
|
|
18
|
-
signet: "https://mempool.space/signet/api",
|
|
19
|
-
regtest: "http://localhost:2140",
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
export interface EsploraClientOptions {
|
|
23
|
-
network?: BitcoinNetwork;
|
|
24
|
-
baseUrl?: string;
|
|
25
|
-
fetchImpl?: typeof fetch;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export class EsploraBitcoinClient implements BitcoinClient {
|
|
29
|
-
readonly network: BitcoinNetwork;
|
|
30
|
-
private readonly baseUrl: string;
|
|
31
|
-
private readonly fetchImpl: typeof fetch;
|
|
32
|
-
|
|
33
|
-
constructor(options: EsploraClientOptions = {}) {
|
|
34
|
-
this.network = options.network ?? "testnet";
|
|
35
|
-
this.baseUrl = (options.baseUrl ?? NETWORK_URLS[this.network]).replace(/\/$/, "");
|
|
36
|
-
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async getAddress(address: string): Promise<BitcoinAddressInfo> {
|
|
40
|
-
return this.getJson(`/address/${address}`);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async getAddressTxs(address: string, lastSeenTxid?: string): Promise<BitcoinTransaction[]> {
|
|
44
|
-
return this.getJson(lastSeenTxid ? `/address/${address}/txs/chain/${lastSeenTxid}` : `/address/${address}/txs`);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async getAddressTxsMempool(address: string): Promise<BitcoinTransaction[]> {
|
|
48
|
-
return this.getJson(`/address/${address}/txs/mempool`);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async getAddressUtxos(address: string): Promise<BitcoinUtxo[]> {
|
|
52
|
-
return this.getJson(`/address/${address}/utxo`);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
async getTransaction(txid: string): Promise<BitcoinTransaction> {
|
|
56
|
-
return this.getJson(`/tx/${txid}`);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async getTxStatus(txid: string): Promise<BitcoinTxStatus> {
|
|
60
|
-
return this.getJson(`/tx/${txid}/status`);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async getTxHex(txid: string): Promise<string> {
|
|
64
|
-
return this.getText(`/tx/${txid}/hex`);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async getTxMerkleProof(txid: string): Promise<BitcoinMerkleProof> {
|
|
68
|
-
return this.getJson(`/tx/${txid}/merkle-proof`);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
async getTxOutspend(txid: string, vout: number): Promise<BitcoinOutspend> {
|
|
72
|
-
return this.getJson(`/tx/${txid}/outspend/${vout}`);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
async getBlockHeight(): Promise<number> {
|
|
76
|
-
return Number.parseInt(await this.getText("/blocks/tip/height"), 10);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
async getBlockHash(height: number): Promise<string> {
|
|
80
|
-
return this.getText(`/block-height/${height}`);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
async getBlockHeader(hash: string): Promise<string> {
|
|
84
|
-
return this.getText(`/block/${hash}/header`);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
async broadcastTx(txHex: string): Promise<string> {
|
|
88
|
-
const res = await this.fetchImpl(`${this.baseUrl}/tx`, {
|
|
89
|
-
method: "POST",
|
|
90
|
-
headers: { "content-type": "text/plain" },
|
|
91
|
-
body: txHex,
|
|
92
|
-
});
|
|
93
|
-
if (!res.ok) {
|
|
94
|
-
throw new Error(`Esplora broadcast failed: ${res.status} ${res.statusText}: ${await res.text()}`);
|
|
95
|
-
}
|
|
96
|
-
return res.text();
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
async getConfirmations(txid: string): Promise<number> {
|
|
100
|
-
const status = await this.getTxStatus(txid);
|
|
101
|
-
if (!status.confirmed || status.block_height === undefined) {
|
|
102
|
-
return 0;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const tipHeight = await this.getBlockHeight();
|
|
106
|
-
return tipHeight - status.block_height + 1;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
async findDepositCandidates(address: string, minConfirmations = 1): Promise<BitcoinDepositCandidate[]> {
|
|
110
|
-
const [utxos, tipHeight, txs] = await Promise.all([
|
|
111
|
-
this.getAddressUtxos(address),
|
|
112
|
-
this.getBlockHeight(),
|
|
113
|
-
this.getAddressTxs(address),
|
|
114
|
-
]);
|
|
115
|
-
const txById = new Map(txs.map((tx) => [tx.txid, tx]));
|
|
116
|
-
|
|
117
|
-
return utxos
|
|
118
|
-
.map((utxo) => ({
|
|
119
|
-
address,
|
|
120
|
-
txid: utxo.txid,
|
|
121
|
-
vout: utxo.vout,
|
|
122
|
-
value: utxo.value,
|
|
123
|
-
confirmations: confirmationsForStatus(utxo.status, tipHeight),
|
|
124
|
-
status: utxo.status,
|
|
125
|
-
opReturn: extractUtxopiaDepositOpReturn(txById.get(utxo.txid)),
|
|
126
|
-
}))
|
|
127
|
-
.filter((candidate) => candidate.confirmations >= minConfirmations);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
private async getJson<T>(endpoint: string): Promise<T> {
|
|
131
|
-
const res = await this.fetchImpl(`${this.baseUrl}${endpoint}`);
|
|
132
|
-
if (!res.ok) {
|
|
133
|
-
throw new Error(`Esplora API failed: ${res.status} ${res.statusText}`);
|
|
134
|
-
}
|
|
135
|
-
return res.json() as Promise<T>;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
private async getText(endpoint: string): Promise<string> {
|
|
139
|
-
const res = await this.fetchImpl(`${this.baseUrl}${endpoint}`);
|
|
140
|
-
if (!res.ok) {
|
|
141
|
-
throw new Error(`Esplora API failed: ${res.status} ${res.statusText}`);
|
|
142
|
-
}
|
|
143
|
-
return res.text();
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
function confirmationsForStatus(status: BitcoinTxStatus, tipHeight: number): number {
|
|
148
|
-
if (!status.confirmed || status.block_height === undefined) {
|
|
149
|
-
return 0;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return tipHeight - status.block_height + 1;
|
|
153
|
-
}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import type { BitcoinTransaction, UtxopiaDepositOpReturn } from "./types";
|
|
2
|
-
|
|
3
|
-
export const UTXOPIA_DEPOSIT_OP_RETURN_SIZE = 73;
|
|
4
|
-
export const UTXOPIA_DEPOSIT_OP_RETURN_VERSION = 1;
|
|
5
|
-
export const DESTINATION_CHAIN_SOLANA = 1;
|
|
6
|
-
export const BITCOIN_NETWORK_MAINNET = 0;
|
|
7
|
-
export const BITCOIN_NETWORK_TESTNET4 = 2;
|
|
8
|
-
export const BITCOIN_NETWORK_REGTEST = 3;
|
|
9
|
-
|
|
10
|
-
export function extractUtxopiaDepositOpReturn(
|
|
11
|
-
tx: BitcoinTransaction | undefined,
|
|
12
|
-
): UtxopiaDepositOpReturn | undefined {
|
|
13
|
-
if (!tx) {
|
|
14
|
-
return undefined;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
for (const output of tx.vout) {
|
|
18
|
-
const payload = parseOpReturnPayload(output.scriptpubkey);
|
|
19
|
-
if (payload?.length !== UTXOPIA_DEPOSIT_OP_RETURN_SIZE) {
|
|
20
|
-
continue;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const header = decodeDepositHeader(payload[0]);
|
|
24
|
-
if (!header) {
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const ephemeralPubkey = payload.slice(9, 41);
|
|
29
|
-
const notePublicKey = payload.slice(41, 73);
|
|
30
|
-
return {
|
|
31
|
-
...header,
|
|
32
|
-
poolTag: payload.slice(1, 9),
|
|
33
|
-
ephemeralPubkey,
|
|
34
|
-
notePublicKey,
|
|
35
|
-
rawPayload: payload,
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
return undefined;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function decodeDepositHeader(header: number): {
|
|
43
|
-
version: number;
|
|
44
|
-
destinationChain: number;
|
|
45
|
-
bitcoinNetwork: number;
|
|
46
|
-
} | undefined {
|
|
47
|
-
const version = header >> 6;
|
|
48
|
-
const destinationChain = (header >> 4) & 0x03;
|
|
49
|
-
const bitcoinNetwork = header & 0x0f;
|
|
50
|
-
if (version !== UTXOPIA_DEPOSIT_OP_RETURN_VERSION) return undefined;
|
|
51
|
-
if (destinationChain !== DESTINATION_CHAIN_SOLANA) return undefined;
|
|
52
|
-
if (
|
|
53
|
-
bitcoinNetwork !== BITCOIN_NETWORK_MAINNET
|
|
54
|
-
&& bitcoinNetwork !== BITCOIN_NETWORK_TESTNET4
|
|
55
|
-
&& bitcoinNetwork !== BITCOIN_NETWORK_REGTEST
|
|
56
|
-
) {
|
|
57
|
-
return undefined;
|
|
58
|
-
}
|
|
59
|
-
return { version, destinationChain, bitcoinNetwork };
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function parseOpReturnPayload(scriptPubkeyHex: string): Uint8Array | undefined {
|
|
63
|
-
const script = hexToBytes(scriptPubkeyHex);
|
|
64
|
-
if (script.length < 2 || script[0] !== 0x6a) {
|
|
65
|
-
return undefined;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const pushOpcode = script[1];
|
|
69
|
-
if (pushOpcode > 0 && pushOpcode <= 75) {
|
|
70
|
-
const end = 2 + pushOpcode;
|
|
71
|
-
return end === script.length ? script.slice(2, end) : undefined;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (pushOpcode === 0x4c && script.length >= 3) {
|
|
75
|
-
const len = script[2];
|
|
76
|
-
const end = 3 + len;
|
|
77
|
-
return end === script.length ? script.slice(3, end) : undefined;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return undefined;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export function hexToBytes(hex: string): Uint8Array {
|
|
84
|
-
if (hex.length % 2 !== 0) {
|
|
85
|
-
throw new Error("Invalid hex string length");
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const bytes = new Uint8Array(hex.length / 2);
|
|
89
|
-
for (let i = 0; i < bytes.length; i += 1) {
|
|
90
|
-
bytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
91
|
-
}
|
|
92
|
-
return bytes;
|
|
93
|
-
}
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
export type BitcoinNetwork = "mainnet" | "testnet" | "testnet4" | "signet" | "regtest";
|
|
2
|
-
|
|
3
|
-
export interface BitcoinTxStatus {
|
|
4
|
-
confirmed: boolean;
|
|
5
|
-
block_height?: number;
|
|
6
|
-
block_hash?: string;
|
|
7
|
-
block_time?: number;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface BitcoinTxInput {
|
|
11
|
-
txid: string;
|
|
12
|
-
vout: number;
|
|
13
|
-
prevout: BitcoinTxOutput | null;
|
|
14
|
-
scriptsig: string;
|
|
15
|
-
scriptsig_asm: string;
|
|
16
|
-
witness?: string[];
|
|
17
|
-
is_coinbase: boolean;
|
|
18
|
-
sequence: number;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface BitcoinTxOutput {
|
|
22
|
-
scriptpubkey: string;
|
|
23
|
-
scriptpubkey_asm: string;
|
|
24
|
-
scriptpubkey_type: string;
|
|
25
|
-
scriptpubkey_address?: string;
|
|
26
|
-
value: number;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface BitcoinTransaction {
|
|
30
|
-
txid: string;
|
|
31
|
-
version: number;
|
|
32
|
-
locktime: number;
|
|
33
|
-
vin: BitcoinTxInput[];
|
|
34
|
-
vout: BitcoinTxOutput[];
|
|
35
|
-
size: number;
|
|
36
|
-
weight: number;
|
|
37
|
-
fee: number;
|
|
38
|
-
status: BitcoinTxStatus;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface BitcoinAddressInfo {
|
|
42
|
-
address: string;
|
|
43
|
-
chain_stats: {
|
|
44
|
-
funded_txo_count: number;
|
|
45
|
-
funded_txo_sum: number;
|
|
46
|
-
spent_txo_count: number;
|
|
47
|
-
spent_txo_sum: number;
|
|
48
|
-
tx_count: number;
|
|
49
|
-
};
|
|
50
|
-
mempool_stats: {
|
|
51
|
-
funded_txo_count: number;
|
|
52
|
-
funded_txo_sum: number;
|
|
53
|
-
spent_txo_count: number;
|
|
54
|
-
spent_txo_sum: number;
|
|
55
|
-
tx_count: number;
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export interface BitcoinUtxo {
|
|
60
|
-
txid: string;
|
|
61
|
-
vout: number;
|
|
62
|
-
status: BitcoinTxStatus;
|
|
63
|
-
value: number;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export interface BitcoinMerkleProof {
|
|
67
|
-
block_height: number;
|
|
68
|
-
merkle: string[];
|
|
69
|
-
pos: number;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export interface BitcoinOutspend {
|
|
73
|
-
spent: boolean;
|
|
74
|
-
txid?: string;
|
|
75
|
-
vin?: number;
|
|
76
|
-
status?: BitcoinTxStatus;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export interface BitcoinDepositCandidate {
|
|
80
|
-
address: string;
|
|
81
|
-
txid: string;
|
|
82
|
-
vout: number;
|
|
83
|
-
value: number;
|
|
84
|
-
confirmations: number;
|
|
85
|
-
status: BitcoinTxStatus;
|
|
86
|
-
opReturn?: UtxopiaDepositOpReturn;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export interface UtxopiaDepositOpReturn {
|
|
90
|
-
version: number;
|
|
91
|
-
destinationChain: number;
|
|
92
|
-
bitcoinNetwork: number;
|
|
93
|
-
poolTag: Uint8Array;
|
|
94
|
-
ephemeralPubkey: Uint8Array;
|
|
95
|
-
notePublicKey: Uint8Array;
|
|
96
|
-
rawPayload: Uint8Array;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export interface BitcoinClient {
|
|
100
|
-
getAddress(address: string): Promise<BitcoinAddressInfo>;
|
|
101
|
-
getAddressTxs(address: string, lastSeenTxid?: string): Promise<BitcoinTransaction[]>;
|
|
102
|
-
getAddressUtxos(address: string): Promise<BitcoinUtxo[]>;
|
|
103
|
-
getTransaction(txid: string): Promise<BitcoinTransaction>;
|
|
104
|
-
getTxStatus(txid: string): Promise<BitcoinTxStatus>;
|
|
105
|
-
getTxHex(txid: string): Promise<string>;
|
|
106
|
-
getTxMerkleProof(txid: string): Promise<BitcoinMerkleProof>;
|
|
107
|
-
getTxOutspend(txid: string, vout: number): Promise<BitcoinOutspend>;
|
|
108
|
-
getBlockHeight(): Promise<number>;
|
|
109
|
-
getBlockHash(height: number): Promise<string>;
|
|
110
|
-
getBlockHeader(hash: string): Promise<string>;
|
|
111
|
-
broadcastTx(txHex: string): Promise<string>;
|
|
112
|
-
}
|
package/packages/sdk/README.md
DELETED
|
@@ -1,277 +0,0 @@
|
|
|
1
|
-
# @utxopia/sdk
|
|
2
|
-
|
|
3
|
-
TypeScript SDK for interacting with the UTXOpia protocol - a privacy-preserving Bitcoin-to-Solana bridge using Zero-Knowledge Proofs.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
bun add @utxopia/sdk
|
|
9
|
-
# or
|
|
10
|
-
npm install @utxopia/sdk
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
## Quick Start
|
|
14
|
-
|
|
15
|
-
```typescript
|
|
16
|
-
import {
|
|
17
|
-
deriveKeysFromWallet,
|
|
18
|
-
createStealthDeposit,
|
|
19
|
-
scanAnnouncements,
|
|
20
|
-
resolveSnsName,
|
|
21
|
-
} from '@utxopia/sdk';
|
|
22
|
-
|
|
23
|
-
// 1. Derive keys from wallet
|
|
24
|
-
const keys = await deriveKeysFromWallet(walletAdapter);
|
|
25
|
-
|
|
26
|
-
// 2. Look up recipient by .utxopia.sol name
|
|
27
|
-
const recipient = await resolveSnsName(connection, 'alice');
|
|
28
|
-
|
|
29
|
-
// 3. Create stealth deposit
|
|
30
|
-
const deposit = await createStealthDeposit(recipient, 100000n);
|
|
31
|
-
|
|
32
|
-
// 4. Scan for incoming deposits
|
|
33
|
-
const notes = await scanAnnouncements(keys, announcements);
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
## Core Features
|
|
37
|
-
|
|
38
|
-
### Key Derivation
|
|
39
|
-
|
|
40
|
-
Derive spending and viewing keys from a Solana wallet signature (RAILGUN-style):
|
|
41
|
-
|
|
42
|
-
```typescript
|
|
43
|
-
import { deriveKeysFromWallet, type UTXOpiaKeys } from '@utxopia/sdk';
|
|
44
|
-
|
|
45
|
-
const keys: UTXOpiaKeys = await deriveKeysFromWallet(walletAdapter);
|
|
46
|
-
// keys.spendingPubKey - for receiving funds
|
|
47
|
-
// keys.viewingPubKey - for scanning deposits
|
|
48
|
-
// keys.spendingPrivKey - for claiming (keep secret!)
|
|
49
|
-
// keys.viewingPrivKey - for scanning (can delegate)
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
### Stealth Addresses (EIP-5564/DKSAP Pattern)
|
|
53
|
-
|
|
54
|
-
Create private deposits that only the recipient can detect and claim:
|
|
55
|
-
|
|
56
|
-
```typescript
|
|
57
|
-
import {
|
|
58
|
-
createStealthDeposit,
|
|
59
|
-
scanAnnouncements,
|
|
60
|
-
prepareClaimInputs,
|
|
61
|
-
} from '@utxopia/sdk';
|
|
62
|
-
|
|
63
|
-
// Sender: Create stealth deposit
|
|
64
|
-
const deposit = await createStealthDeposit(recipientMeta, amountSats);
|
|
65
|
-
// deposit.ephemeralPub - publish on-chain
|
|
66
|
-
// deposit.commitment - add to Merkle tree
|
|
67
|
-
// deposit.amountSats - verified BTC amount
|
|
68
|
-
|
|
69
|
-
// Recipient: Scan for deposits
|
|
70
|
-
const notes = await scanAnnouncements(keys, onChainAnnouncements);
|
|
71
|
-
|
|
72
|
-
// Recipient: Prepare claim inputs for ZK proof
|
|
73
|
-
const claimInputs = await prepareClaimInputs(keys, note, merkleProof);
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### Note Generation
|
|
77
|
-
|
|
78
|
-
Create and manage shielded notes:
|
|
79
|
-
|
|
80
|
-
```typescript
|
|
81
|
-
import {
|
|
82
|
-
generateNote,
|
|
83
|
-
deriveNote,
|
|
84
|
-
createClaimLink,
|
|
85
|
-
parseClaimLink,
|
|
86
|
-
} from '@utxopia/sdk';
|
|
87
|
-
|
|
88
|
-
// Generate random note
|
|
89
|
-
const note = generateNote(100000n);
|
|
90
|
-
|
|
91
|
-
// Derive deterministic note from seed
|
|
92
|
-
const note = deriveNote('my-secret-phrase', 0, 100000n);
|
|
93
|
-
|
|
94
|
-
// Create shareable claim link
|
|
95
|
-
const link = createClaimLink(note);
|
|
96
|
-
|
|
97
|
-
// Parse claim link
|
|
98
|
-
const parsed = parseClaimLink(link);
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### Taproot Address Derivation
|
|
102
|
-
|
|
103
|
-
Generate BTC deposit addresses:
|
|
104
|
-
|
|
105
|
-
```typescript
|
|
106
|
-
import { deriveTaprootAddress, verifyTaprootAddress } from '@utxopia/sdk';
|
|
107
|
-
|
|
108
|
-
// Use the configured FROST/Ika custody public key; there is no safe default.
|
|
109
|
-
const custodyInternalKey = getConfiguredCustodyInternalKey();
|
|
110
|
-
const { address } = deriveTaprootAddress(
|
|
111
|
-
commitment,
|
|
112
|
-
'testnet',
|
|
113
|
-
custodyInternalKey,
|
|
114
|
-
);
|
|
115
|
-
|
|
116
|
-
// Verify address matches commitment
|
|
117
|
-
const isValid = verifyTaprootAddress(address, commitment, custodyInternalKey);
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### Merkle Proofs
|
|
121
|
-
|
|
122
|
-
Work with the on-chain commitment tree:
|
|
123
|
-
|
|
124
|
-
```typescript
|
|
125
|
-
import {
|
|
126
|
-
createMerkleProof,
|
|
127
|
-
proofToNoirFormat,
|
|
128
|
-
TREE_DEPTH,
|
|
129
|
-
} from '@utxopia/sdk';
|
|
130
|
-
|
|
131
|
-
const proof = createMerkleProof(leaves, leafIndex);
|
|
132
|
-
const noirProof = proofToNoirFormat(proof);
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
## API Reference
|
|
136
|
-
|
|
137
|
-
### Stealth Module
|
|
138
|
-
|
|
139
|
-
| Function | Description |
|
|
140
|
-
|----------|-------------|
|
|
141
|
-
| `createStealthDeposit(recipient, amount)` | Create stealth deposit for recipient |
|
|
142
|
-
| `scanAnnouncements(keys, announcements)` | Scan for deposits using viewing key |
|
|
143
|
-
| `prepareClaimInputs(keys, note, proof)` | Prepare inputs for ZK claim proof |
|
|
144
|
-
| `scanUnifiedNotes(keys, announcements)` | Scan announcement events for owned notes |
|
|
145
|
-
| `resolveSnsName(conn, name)` | Look up .utxopia.sol name to stealth address |
|
|
146
|
-
|
|
147
|
-
### Key Derivation Module
|
|
148
|
-
|
|
149
|
-
| Function | Description |
|
|
150
|
-
|----------|-------------|
|
|
151
|
-
| `deriveKeysFromWallet(wallet)` | Derive keys from wallet signature |
|
|
152
|
-
| `deriveKeysFromSignature(sig)` | Derive keys from raw signature |
|
|
153
|
-
| `deriveKeysFromSeed(seed)` | Derive keys from seed bytes |
|
|
154
|
-
| `createStealthMetaAddress(keys)` | Create stealth meta-address |
|
|
155
|
-
| `createDelegatedViewKey(keys, perms, expiry)` | Create delegated view key |
|
|
156
|
-
|
|
157
|
-
### Constants
|
|
158
|
-
|
|
159
|
-
```typescript
|
|
160
|
-
// Program IDs
|
|
161
|
-
UTXOPIA_PROGRAM_ID // Main UTXOpia program (devnet)
|
|
162
|
-
CHADBUFFER_PROGRAM_ID // ChadBuffer for SPV proofs
|
|
163
|
-
|
|
164
|
-
// Merkle Tree
|
|
165
|
-
TREE_DEPTH // 20
|
|
166
|
-
MAX_LEAVES // 2^20
|
|
167
|
-
ZERO_VALUE // Empty leaf value
|
|
168
|
-
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
## Types
|
|
172
|
-
|
|
173
|
-
### UTXOpiaKeys
|
|
174
|
-
|
|
175
|
-
```typescript
|
|
176
|
-
interface UTXOpiaKeys {
|
|
177
|
-
spendingPubKey: BabyJubPoint;
|
|
178
|
-
spendingPrivKey: bigint;
|
|
179
|
-
viewingPubKey: Uint8Array;
|
|
180
|
-
viewingPrivKey: Uint8Array;
|
|
181
|
-
nullifyingKey: bigint;
|
|
182
|
-
}
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
### StealthDeposit
|
|
186
|
-
|
|
187
|
-
```typescript
|
|
188
|
-
interface StealthDeposit {
|
|
189
|
-
ephemeralPub: Uint8Array; // 32 bytes (Ed25519)
|
|
190
|
-
amountSats: bigint;
|
|
191
|
-
commitment: Uint8Array; // 32 bytes
|
|
192
|
-
createdAt: number;
|
|
193
|
-
}
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
### ScannedNote
|
|
197
|
-
|
|
198
|
-
```typescript
|
|
199
|
-
interface ScannedNote {
|
|
200
|
-
amount: bigint;
|
|
201
|
-
ephemeralPub: Uint8Array;
|
|
202
|
-
stealthPub: BabyJubPoint;
|
|
203
|
-
leafIndex: number;
|
|
204
|
-
commitment: Uint8Array;
|
|
205
|
-
}
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
### ConnectionAdapter
|
|
209
|
-
|
|
210
|
-
```typescript
|
|
211
|
-
interface ConnectionAdapter {
|
|
212
|
-
getAccountInfo: (
|
|
213
|
-
pubkey: { toBytes(): Uint8Array }
|
|
214
|
-
) => Promise<{ data: Uint8Array } | null>;
|
|
215
|
-
}
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
## Security Considerations
|
|
219
|
-
|
|
220
|
-
1. **Never expose spending private key** - Only needed for claiming
|
|
221
|
-
2. **Viewing key can be delegated** - For balance monitoring without spend capability
|
|
222
|
-
3. **Nullifiers prevent double-spending** - Derived from spending key + leaf index
|
|
223
|
-
4. **Commitments hide amounts** - Poseidon hash of NPK, token, and amount
|
|
224
|
-
|
|
225
|
-
## Development
|
|
226
|
-
|
|
227
|
-
```bash
|
|
228
|
-
# Install dependencies
|
|
229
|
-
bun install
|
|
230
|
-
|
|
231
|
-
# Build
|
|
232
|
-
bun run build
|
|
233
|
-
|
|
234
|
-
# Run tests
|
|
235
|
-
bun test
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
## Verify it yourself: rebuild the tree from chain
|
|
239
|
-
|
|
240
|
-
Spending a note needs a Merkle proof, and a Merkle proof needs the whole leaf
|
|
241
|
-
set — which normally comes from our indexer. If the leaves weren't recoverable
|
|
242
|
-
from the chain itself, "you can exit without the operator" would be a promise
|
|
243
|
-
rather than a property.
|
|
244
|
-
|
|
245
|
-
This script rebuilds a pool's entire leaf set from Solana transaction logs and
|
|
246
|
-
checks the result against the root the program is verifying against. It talks to
|
|
247
|
-
a public RPC endpoint and nothing else — no backend, no indexer, no API key:
|
|
248
|
-
|
|
249
|
-
```bash
|
|
250
|
-
TREE=<commitment-tree-pda> bun run scripts/rebuild-tree-from-chain.ts
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
```
|
|
254
|
-
on-chain: 123 leaves, root 2994f7d670d12cd8dcbd89af708ff55f9e877bbb387aac4cf09e688a615650ce
|
|
255
|
-
134/134 scanned, 123 leaves
|
|
256
|
-
recovered 123/123 leaves from logs
|
|
257
|
-
rebuilt: 123 leaves, root 2994f7d670d12cd8dcbd89af708ff55f9e877bbb387aac4cf09e688a615650ce
|
|
258
|
-
|
|
259
|
-
MATCH — the leaf set is recoverable from chain alone
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
It exits non-zero on a mismatch or on missing leaves, so it works as a check in
|
|
263
|
-
CI as well as by hand.
|
|
264
|
-
|
|
265
|
-
| Env | Default | |
|
|
266
|
-
|---|---|---|
|
|
267
|
-
| `RPC` | devnet | Any Solana RPC endpoint |
|
|
268
|
-
| `TREE` | `DEVNET_CONFIG.commitmentTreePda` | Commitment tree PDA to rebuild |
|
|
269
|
-
| `EPOCH_SIG` | — | Tree's `INITIALIZE` signature. Optional; set it to skip a dead epoch if the PDA was closed and recreated |
|
|
270
|
-
| `PACE_MS` | `120` | Delay between requests. Raise it if your endpoint throttles you |
|
|
271
|
-
|
|
272
|
-
Public endpoints rate-limit this scan aggressively — the script paces itself and
|
|
273
|
-
backs off, so expect it to take a minute or two rather than to fail.
|
|
274
|
-
|
|
275
|
-
## License
|
|
276
|
-
|
|
277
|
-
MIT
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|