@utxopia/sdk 0.1.0-alpha.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/LICENSE +21 -0
- package/README.md +171 -0
- package/package.json +108 -0
- package/packages/btc-client/src/esplora-client.ts +153 -0
- package/packages/btc-client/src/index.ts +3 -0
- package/packages/btc-client/src/op-return.ts +93 -0
- package/packages/btc-client/src/types.ts +112 -0
- package/packages/sdk/README.md +277 -0
- package/packages/sdk/dist/announcement-client.d.ts +64 -0
- package/packages/sdk/dist/announcement-client.js +337 -0
- package/packages/sdk/dist/auditor-ciphertext.d.ts +72 -0
- package/packages/sdk/dist/auditor-ciphertext.js +135 -0
- package/packages/sdk/dist/auditor.d.ts +115 -0
- package/packages/sdk/dist/auditor.js +292 -0
- package/packages/sdk/dist/bitcoin/ika.d.ts +47 -0
- package/packages/sdk/dist/bitcoin/ika.js +74 -0
- package/packages/sdk/dist/bitcoin/index.d.ts +2 -0
- package/packages/sdk/dist/bitcoin/index.js +1 -0
- package/packages/sdk/dist/bound-params.d.ts +96 -0
- package/packages/sdk/dist/bound-params.js +249 -0
- package/packages/sdk/dist/chadbuffer.d.ts +124 -0
- package/packages/sdk/dist/chadbuffer.js +405 -0
- package/packages/sdk/dist/claim-link.d.ts +30 -0
- package/packages/sdk/dist/claim-link.js +50 -0
- package/packages/sdk/dist/client.d.ts +233 -0
- package/packages/sdk/dist/client.js +416 -0
- package/packages/sdk/dist/commitment-tree.d.ts +268 -0
- package/packages/sdk/dist/commitment-tree.js +564 -0
- package/packages/sdk/dist/config.d.ts +177 -0
- package/packages/sdk/dist/config.js +426 -0
- package/packages/sdk/dist/core/esplora.d.ts +110 -0
- package/packages/sdk/dist/core/esplora.js +187 -0
- package/packages/sdk/dist/core/mempool.d.ts +60 -0
- package/packages/sdk/dist/core/mempool.js +107 -0
- package/packages/sdk/dist/crypto-babyjub.d.ts +105 -0
- package/packages/sdk/dist/crypto-babyjub.js +312 -0
- package/packages/sdk/dist/crypto-ed25519.d.ts +116 -0
- package/packages/sdk/dist/crypto-ed25519.js +235 -0
- package/packages/sdk/dist/crypto.d.ts +59 -0
- package/packages/sdk/dist/crypto.js +140 -0
- package/packages/sdk/dist/event-client.d.ts +64 -0
- package/packages/sdk/dist/event-client.js +165 -0
- package/packages/sdk/dist/events.d.ts +127 -0
- package/packages/sdk/dist/events.js +305 -0
- package/packages/sdk/dist/explorer.d.ts +86 -0
- package/packages/sdk/dist/explorer.js +197 -0
- package/packages/sdk/dist/index.d.ts +61 -0
- package/packages/sdk/dist/index.js +216 -0
- package/packages/sdk/dist/instructions.d.ts +1125 -0
- package/packages/sdk/dist/instructions.js +1760 -0
- package/packages/sdk/dist/keys.d.ts +467 -0
- package/packages/sdk/dist/keys.js +799 -0
- package/packages/sdk/dist/logger.d.ts +10 -0
- package/packages/sdk/dist/logger.js +40 -0
- package/packages/sdk/dist/magicblock.d.ts +90 -0
- package/packages/sdk/dist/magicblock.js +164 -0
- package/packages/sdk/dist/merkle.d.ts +82 -0
- package/packages/sdk/dist/merkle.js +141 -0
- package/packages/sdk/dist/note.d.ts +348 -0
- package/packages/sdk/dist/note.js +483 -0
- package/packages/sdk/dist/pda.d.ts +160 -0
- package/packages/sdk/dist/pda.js +384 -0
- package/packages/sdk/dist/pool-state.d.ts +100 -0
- package/packages/sdk/dist/pool-state.js +126 -0
- package/packages/sdk/dist/poseidon.d.ts +53 -0
- package/packages/sdk/dist/poseidon.js +136 -0
- package/packages/sdk/dist/prover/index.d.ts +10 -0
- package/packages/sdk/dist/prover/index.js +10 -0
- package/packages/sdk/dist/prover/mobile.d.ts +45 -0
- package/packages/sdk/dist/prover/mobile.js +227 -0
- package/packages/sdk/dist/prover/web.d.ts +136 -0
- package/packages/sdk/dist/prover/web.js +597 -0
- package/packages/sdk/dist/psbt.d.ts +73 -0
- package/packages/sdk/dist/psbt.js +202 -0
- package/packages/sdk/dist/selective-disclosure.d.ts +181 -0
- package/packages/sdk/dist/selective-disclosure.js +172 -0
- package/packages/sdk/dist/sender-memo.d.ts +149 -0
- package/packages/sdk/dist/sender-memo.js +250 -0
- package/packages/sdk/dist/sns-resolver.d.ts +121 -0
- package/packages/sdk/dist/sns-resolver.js +229 -0
- package/packages/sdk/dist/solana/connection.d.ts +77 -0
- package/packages/sdk/dist/solana/connection.js +133 -0
- package/packages/sdk/dist/solana/priority-fee.d.ts +78 -0
- package/packages/sdk/dist/solana/priority-fee.js +137 -0
- package/packages/sdk/dist/stealth.d.ts +429 -0
- package/packages/sdk/dist/stealth.js +703 -0
- package/packages/sdk/dist/taproot.d.ts +172 -0
- package/packages/sdk/dist/taproot.js +494 -0
- package/packages/sdk/dist/token-registry.d.ts +96 -0
- package/packages/sdk/dist/token-registry.js +122 -0
- package/packages/sdk/dist/utils/encoding.d.ts +19 -0
- package/packages/sdk/dist/utils/encoding.js +29 -0
- package/packages/sdk/dist/vk-registry.d.ts +100 -0
- package/packages/sdk/dist/vk-registry.js +212 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conditional debug logger for UTXOPIA SDK.
|
|
3
|
+
*
|
|
4
|
+
* Set `UTXOPIA_DEBUG=1` (Node.js) or `localStorage.utxopiaDebug = "1"` (browser)
|
|
5
|
+
* to enable debug output. All logs are suppressed by default.
|
|
6
|
+
*/
|
|
7
|
+
export declare function debug(tag: string, ...args: unknown[]): void;
|
|
8
|
+
export declare function warn(tag: string, ...args: unknown[]): void;
|
|
9
|
+
/** Force-enable or disable debug logging at runtime */
|
|
10
|
+
export declare function setDebug(enabled: boolean): void;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conditional debug logger for UTXOPIA SDK.
|
|
3
|
+
*
|
|
4
|
+
* Set `UTXOPIA_DEBUG=1` (Node.js) or `localStorage.utxopiaDebug = "1"` (browser)
|
|
5
|
+
* to enable debug output. All logs are suppressed by default.
|
|
6
|
+
*/
|
|
7
|
+
let _enabled = null;
|
|
8
|
+
function isEnabled() {
|
|
9
|
+
if (_enabled !== null)
|
|
10
|
+
return _enabled;
|
|
11
|
+
try {
|
|
12
|
+
// Node.js
|
|
13
|
+
if (typeof process !== "undefined" && process.env?.UTXOPIA_DEBUG === "1") {
|
|
14
|
+
_enabled = true;
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
catch { /* not Node */ }
|
|
19
|
+
try {
|
|
20
|
+
// Browser
|
|
21
|
+
if (typeof localStorage !== "undefined" && localStorage.getItem("utxopiaDebug") === "1") {
|
|
22
|
+
_enabled = true;
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
catch { /* no localStorage */ }
|
|
27
|
+
_enabled = false;
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
export function debug(tag, ...args) {
|
|
31
|
+
if (isEnabled())
|
|
32
|
+
console.log(`[utxo:${tag}]`, ...args);
|
|
33
|
+
}
|
|
34
|
+
export function warn(tag, ...args) {
|
|
35
|
+
console.warn(`[utxo:${tag}]`, ...args);
|
|
36
|
+
}
|
|
37
|
+
/** Force-enable or disable debug logging at runtime */
|
|
38
|
+
export function setDebug(enabled) {
|
|
39
|
+
_enabled = enabled;
|
|
40
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { type Address } from "@solana/kit";
|
|
2
|
+
import { Connection as MagicBlockConnection } from "@magicblock-labs/ephemeral-rollups-kit";
|
|
3
|
+
import { type NetworkConfig } from "./config";
|
|
4
|
+
export type MagicBlockExecutionMode = "solana" | "er" | "per";
|
|
5
|
+
export type MagicBlockPolicyMode = "disabled" | "per";
|
|
6
|
+
export type MagicBlockValidatorRegion = "asia" | "eu" | "us" | "tee" | "local";
|
|
7
|
+
export type PrivacyDomainKind = "public" | "institution";
|
|
8
|
+
export interface MagicBlockEndpointConfig {
|
|
9
|
+
/** Magic Router HTTP endpoint used for delegated-account-aware routing. */
|
|
10
|
+
routerUrl?: string;
|
|
11
|
+
/** Magic Router WebSocket endpoint used for confirmations/subscriptions. */
|
|
12
|
+
routerWsUrl?: string;
|
|
13
|
+
/** MagicBlock ER endpoint used for low-latency public shielded transfers. */
|
|
14
|
+
erUrl?: string;
|
|
15
|
+
/** MagicBlock PER endpoint used for private institution policy execution. */
|
|
16
|
+
perUrl?: string;
|
|
17
|
+
/** Validator region selected for delegation. Use "tee" for PER. */
|
|
18
|
+
validatorRegion?: MagicBlockValidatorRegion;
|
|
19
|
+
}
|
|
20
|
+
export interface PrivacyDomainConfig {
|
|
21
|
+
/** Stable domain identifier. Bind this into future circuit bound params before multi-domain launch. */
|
|
22
|
+
domainId: string;
|
|
23
|
+
/** Human-readable label for UI and logs. */
|
|
24
|
+
label: string;
|
|
25
|
+
/** Product/compliance category for this privacy domain. */
|
|
26
|
+
kind: PrivacyDomainKind;
|
|
27
|
+
/** Execution lane selected for this domain. */
|
|
28
|
+
executionMode: MagicBlockExecutionMode;
|
|
29
|
+
/** Optional PER policy coprocessor. Asset execution remains Solana. */
|
|
30
|
+
policyMode: MagicBlockPolicyMode;
|
|
31
|
+
/** UTXOpia program instance for this domain. */
|
|
32
|
+
programId: Address;
|
|
33
|
+
/** Pool state PDA for this domain. */
|
|
34
|
+
poolStatePda: Address;
|
|
35
|
+
/** Active commitment tree PDA for this domain. */
|
|
36
|
+
commitmentTreePda: Address;
|
|
37
|
+
/** Optional auditor signer for permissioned domains. */
|
|
38
|
+
auditor?: Address;
|
|
39
|
+
/** Optional auditor viewing public key, encoded by the caller. */
|
|
40
|
+
auditorViewingPubkey?: string;
|
|
41
|
+
/** Optional MagicBlock endpoints for ER/PER routing. */
|
|
42
|
+
magicblock?: MagicBlockEndpointConfig;
|
|
43
|
+
}
|
|
44
|
+
export interface BuildPrivacyDomainOptions {
|
|
45
|
+
domainId?: string;
|
|
46
|
+
label?: string;
|
|
47
|
+
kind?: PrivacyDomainKind;
|
|
48
|
+
executionMode?: MagicBlockExecutionMode;
|
|
49
|
+
policyMode?: MagicBlockPolicyMode;
|
|
50
|
+
auditor?: Address;
|
|
51
|
+
auditorViewingPubkey?: string;
|
|
52
|
+
magicblock?: MagicBlockEndpointConfig;
|
|
53
|
+
}
|
|
54
|
+
export declare const MAGICBLOCK_DELEGATION_PROGRAM_ID: Address;
|
|
55
|
+
export declare const MAGICBLOCK_PERMISSION_PROGRAM_ID: Address;
|
|
56
|
+
export declare const MAGICBLOCK_MAGIC_PROGRAM_ID: Address;
|
|
57
|
+
export declare const MAGICBLOCK_MAGIC_CONTEXT_ID: Address;
|
|
58
|
+
export declare const MAGICBLOCK_EPHEMERAL_VAULT_ID: Address;
|
|
59
|
+
export declare const MAGICBLOCK_DEVNET_ROUTER_URL = "https://devnet-router.magicblock.app";
|
|
60
|
+
export declare const MAGICBLOCK_DEVNET_ROUTER_WS_URL = "wss://devnet-router.magicblock.app";
|
|
61
|
+
export declare const MAGICBLOCK_VALIDATOR_IDENTITIES: Record<MagicBlockValidatorRegion, Address>;
|
|
62
|
+
export declare const MAGICBLOCK_PER_MEMBER_FLAGS: {
|
|
63
|
+
readonly authority: number;
|
|
64
|
+
readonly txLogs: number;
|
|
65
|
+
readonly txBalances: number;
|
|
66
|
+
readonly txMessage: number;
|
|
67
|
+
readonly accountSignatures: number;
|
|
68
|
+
};
|
|
69
|
+
export declare const MAGICBLOCK_MAX_PER_MEMBERS = 8;
|
|
70
|
+
export type MagicBlockPerMemberFlagName = keyof typeof MAGICBLOCK_PER_MEMBER_FLAGS;
|
|
71
|
+
export declare function buildMagicBlockPerMemberFlags(flags: MagicBlockPerMemberFlagName[]): number;
|
|
72
|
+
export declare function getMagicBlockValidatorIdentity(region: MagicBlockValidatorRegion): Address;
|
|
73
|
+
export declare function deriveMagicBlockDelegationRecordPDA(delegatedAccount: Address): Promise<[Address, number]>;
|
|
74
|
+
export declare function deriveMagicBlockDelegationMetadataPDA(delegatedAccount: Address): Promise<[Address, number]>;
|
|
75
|
+
export declare function deriveMagicBlockDelegateBufferPDA(delegatedAccount: Address, ownerProgram: Address): Promise<[Address, number]>;
|
|
76
|
+
export declare function deriveMagicBlockUndelegateBufferPDA(delegatedAccount: Address): Promise<[Address, number]>;
|
|
77
|
+
export declare function deriveMagicBlockCommitStatePDA(delegatedAccount: Address): Promise<[Address, number]>;
|
|
78
|
+
export declare function deriveMagicBlockCommitRecordPDA(delegatedAccount: Address): Promise<[Address, number]>;
|
|
79
|
+
export declare function deriveMagicBlockPermissionPDA(permissionedAccount: Address): Promise<[Address, number]>;
|
|
80
|
+
export declare function buildDefaultPrivacyDomain(config: NetworkConfig, options?: BuildPrivacyDomainOptions): PrivacyDomainConfig;
|
|
81
|
+
export declare function requiresMagicBlockEndpoint(mode: MagicBlockExecutionMode): boolean;
|
|
82
|
+
export declare function getMagicBlockEndpoint(domain: PrivacyDomainConfig, mode?: MagicBlockExecutionMode): string | undefined;
|
|
83
|
+
export declare function assertMagicBlockRouteReady(domain: PrivacyDomainConfig): void;
|
|
84
|
+
export declare function assertDomainExecutionPolicy(domain: PrivacyDomainConfig): void;
|
|
85
|
+
/**
|
|
86
|
+
* Create the delegated-account-aware connection used for public ER traffic.
|
|
87
|
+
* PER uses the authenticated server relay because the kit connection does not
|
|
88
|
+
* expose request headers for the signed-challenge bearer token.
|
|
89
|
+
*/
|
|
90
|
+
export declare function createMagicBlockRouterConnection(domain: PrivacyDomainConfig): Promise<MagicBlockConnection>;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { getAddressEncoder, getProgramDerivedAddress } from "@solana/kit";
|
|
2
|
+
import { Connection as MagicBlockConnection } from "@magicblock-labs/ephemeral-rollups-kit";
|
|
3
|
+
import { address } from "./config";
|
|
4
|
+
export const MAGICBLOCK_DELEGATION_PROGRAM_ID = address("DELeGGvXpWV2fqJUhqcF5ZSYMS4JTLjteaAMARRSaeSh");
|
|
5
|
+
export const MAGICBLOCK_PERMISSION_PROGRAM_ID = address("ACLseoPoyC3cBqoUtkbjZ4aDrkurZW86v19pXz2XQnp1");
|
|
6
|
+
export const MAGICBLOCK_MAGIC_PROGRAM_ID = address("Magic11111111111111111111111111111111111111");
|
|
7
|
+
export const MAGICBLOCK_MAGIC_CONTEXT_ID = address("MagicContext1111111111111111111111111111111");
|
|
8
|
+
export const MAGICBLOCK_EPHEMERAL_VAULT_ID = address("MagicVau1t999999999999999999999999999999999");
|
|
9
|
+
export const MAGICBLOCK_DEVNET_ROUTER_URL = "https://devnet-router.magicblock.app";
|
|
10
|
+
export const MAGICBLOCK_DEVNET_ROUTER_WS_URL = "wss://devnet-router.magicblock.app";
|
|
11
|
+
export const MAGICBLOCK_VALIDATOR_IDENTITIES = {
|
|
12
|
+
asia: address("MAS1Dt9qreoRMQ14YQuhg8UTZMMzDdKhmkZMECCzk57"),
|
|
13
|
+
eu: address("MEUGGrYPxKk17hCr7wpT6s8dtNokZj5U2L57vjYMS8e"),
|
|
14
|
+
us: address("MUS3hc9TCw4cGC12vHNoYcCGzJG1txjgQLZWVoeNHNd"),
|
|
15
|
+
tee: address("MTEWGuqxUpYZGFJQcp8tLN7x5v9BSeoFHYWQQ3n3xzo"),
|
|
16
|
+
local: address("mAGicPQYBMvcYveUZA5F5UNNwyHvfYh5xkLS2Fr1mev"),
|
|
17
|
+
};
|
|
18
|
+
export const MAGICBLOCK_PER_MEMBER_FLAGS = {
|
|
19
|
+
authority: 1 << 0,
|
|
20
|
+
txLogs: 1 << 1,
|
|
21
|
+
txBalances: 1 << 2,
|
|
22
|
+
txMessage: 1 << 3,
|
|
23
|
+
accountSignatures: 1 << 4,
|
|
24
|
+
};
|
|
25
|
+
export const MAGICBLOCK_MAX_PER_MEMBERS = 8;
|
|
26
|
+
export function buildMagicBlockPerMemberFlags(flags) {
|
|
27
|
+
return flags.reduce((acc, flag) => acc | MAGICBLOCK_PER_MEMBER_FLAGS[flag], 0);
|
|
28
|
+
}
|
|
29
|
+
export function getMagicBlockValidatorIdentity(region) {
|
|
30
|
+
return MAGICBLOCK_VALIDATOR_IDENTITIES[region];
|
|
31
|
+
}
|
|
32
|
+
function magicBlockAddressBytes(value) {
|
|
33
|
+
return new Uint8Array(getAddressEncoder().encode(value));
|
|
34
|
+
}
|
|
35
|
+
export async function deriveMagicBlockDelegationRecordPDA(delegatedAccount) {
|
|
36
|
+
const result = await getProgramDerivedAddress({
|
|
37
|
+
programAddress: MAGICBLOCK_DELEGATION_PROGRAM_ID,
|
|
38
|
+
seeds: [new TextEncoder().encode("delegation"), magicBlockAddressBytes(delegatedAccount)],
|
|
39
|
+
});
|
|
40
|
+
return [result[0], result[1]];
|
|
41
|
+
}
|
|
42
|
+
export async function deriveMagicBlockDelegationMetadataPDA(delegatedAccount) {
|
|
43
|
+
const result = await getProgramDerivedAddress({
|
|
44
|
+
programAddress: MAGICBLOCK_DELEGATION_PROGRAM_ID,
|
|
45
|
+
seeds: [
|
|
46
|
+
new TextEncoder().encode("delegation-metadata"),
|
|
47
|
+
magicBlockAddressBytes(delegatedAccount),
|
|
48
|
+
],
|
|
49
|
+
});
|
|
50
|
+
return [result[0], result[1]];
|
|
51
|
+
}
|
|
52
|
+
export async function deriveMagicBlockDelegateBufferPDA(delegatedAccount, ownerProgram) {
|
|
53
|
+
const result = await getProgramDerivedAddress({
|
|
54
|
+
programAddress: ownerProgram,
|
|
55
|
+
seeds: [new TextEncoder().encode("buffer"), magicBlockAddressBytes(delegatedAccount)],
|
|
56
|
+
});
|
|
57
|
+
return [result[0], result[1]];
|
|
58
|
+
}
|
|
59
|
+
export async function deriveMagicBlockUndelegateBufferPDA(delegatedAccount) {
|
|
60
|
+
const result = await getProgramDerivedAddress({
|
|
61
|
+
programAddress: MAGICBLOCK_DELEGATION_PROGRAM_ID,
|
|
62
|
+
seeds: [new TextEncoder().encode("undelegate-buffer"), magicBlockAddressBytes(delegatedAccount)],
|
|
63
|
+
});
|
|
64
|
+
return [result[0], result[1]];
|
|
65
|
+
}
|
|
66
|
+
export async function deriveMagicBlockCommitStatePDA(delegatedAccount) {
|
|
67
|
+
const result = await getProgramDerivedAddress({
|
|
68
|
+
programAddress: MAGICBLOCK_DELEGATION_PROGRAM_ID,
|
|
69
|
+
seeds: [new TextEncoder().encode("state-diff"), magicBlockAddressBytes(delegatedAccount)],
|
|
70
|
+
});
|
|
71
|
+
return [result[0], result[1]];
|
|
72
|
+
}
|
|
73
|
+
export async function deriveMagicBlockCommitRecordPDA(delegatedAccount) {
|
|
74
|
+
const result = await getProgramDerivedAddress({
|
|
75
|
+
programAddress: MAGICBLOCK_DELEGATION_PROGRAM_ID,
|
|
76
|
+
seeds: [
|
|
77
|
+
new TextEncoder().encode("commit-state-record"),
|
|
78
|
+
magicBlockAddressBytes(delegatedAccount),
|
|
79
|
+
],
|
|
80
|
+
});
|
|
81
|
+
return [result[0], result[1]];
|
|
82
|
+
}
|
|
83
|
+
export async function deriveMagicBlockPermissionPDA(permissionedAccount) {
|
|
84
|
+
const result = await getProgramDerivedAddress({
|
|
85
|
+
programAddress: MAGICBLOCK_PERMISSION_PROGRAM_ID,
|
|
86
|
+
seeds: [
|
|
87
|
+
new TextEncoder().encode("permission:"),
|
|
88
|
+
magicBlockAddressBytes(permissionedAccount),
|
|
89
|
+
],
|
|
90
|
+
});
|
|
91
|
+
return [result[0], result[1]];
|
|
92
|
+
}
|
|
93
|
+
export function buildDefaultPrivacyDomain(config, options = {}) {
|
|
94
|
+
return {
|
|
95
|
+
domainId: options.domainId ?? "public",
|
|
96
|
+
label: options.label ?? "Public Pool",
|
|
97
|
+
kind: options.kind ?? "public",
|
|
98
|
+
executionMode: options.executionMode ?? "solana",
|
|
99
|
+
policyMode: options.policyMode ?? "disabled",
|
|
100
|
+
programId: config.utxopiaProgramId,
|
|
101
|
+
poolStatePda: config.poolStatePda,
|
|
102
|
+
commitmentTreePda: config.commitmentTreePda,
|
|
103
|
+
auditor: options.auditor,
|
|
104
|
+
auditorViewingPubkey: options.auditorViewingPubkey,
|
|
105
|
+
magicblock: {
|
|
106
|
+
routerUrl: MAGICBLOCK_DEVNET_ROUTER_URL,
|
|
107
|
+
routerWsUrl: MAGICBLOCK_DEVNET_ROUTER_WS_URL,
|
|
108
|
+
validatorRegion: options.policyMode === "per" ? "tee" : "asia",
|
|
109
|
+
...options.magicblock,
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
export function requiresMagicBlockEndpoint(mode) {
|
|
114
|
+
return mode === "er" || mode === "per";
|
|
115
|
+
}
|
|
116
|
+
export function getMagicBlockEndpoint(domain, mode = domain.executionMode) {
|
|
117
|
+
if (mode === "er") {
|
|
118
|
+
return domain.magicblock?.erUrl;
|
|
119
|
+
}
|
|
120
|
+
if (mode === "per") {
|
|
121
|
+
return domain.magicblock?.perUrl;
|
|
122
|
+
}
|
|
123
|
+
return undefined;
|
|
124
|
+
}
|
|
125
|
+
export function assertMagicBlockRouteReady(domain) {
|
|
126
|
+
const mode = domain.executionMode;
|
|
127
|
+
if (!requiresMagicBlockEndpoint(mode)) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const endpoint = getMagicBlockEndpoint(domain, mode);
|
|
131
|
+
if (!endpoint) {
|
|
132
|
+
throw new Error(`Privacy domain "${domain.domainId}" uses ${mode.toUpperCase()} but no MagicBlock ${mode.toUpperCase()} endpoint is configured`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
export function assertDomainExecutionPolicy(domain) {
|
|
136
|
+
if (domain.executionMode !== "solana") {
|
|
137
|
+
throw new Error("UTXOpia asset execution must remain on Solana");
|
|
138
|
+
}
|
|
139
|
+
if (domain.kind === "public" && domain.policyMode === "per") {
|
|
140
|
+
throw new Error("Public permissionless domains must not require PER policy");
|
|
141
|
+
}
|
|
142
|
+
if (domain.policyMode === "per" && domain.magicblock?.validatorRegion !== "tee") {
|
|
143
|
+
throw new Error("PER policy domains must use the TEE validator region");
|
|
144
|
+
}
|
|
145
|
+
if (domain.policyMode === "per" && !domain.magicblock?.perUrl) {
|
|
146
|
+
throw new Error(`Privacy domain "${domain.domainId}" requires a PER policy endpoint`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Create the delegated-account-aware connection used for public ER traffic.
|
|
151
|
+
* PER uses the authenticated server relay because the kit connection does not
|
|
152
|
+
* expose request headers for the signed-challenge bearer token.
|
|
153
|
+
*/
|
|
154
|
+
export async function createMagicBlockRouterConnection(domain) {
|
|
155
|
+
assertDomainExecutionPolicy(domain);
|
|
156
|
+
if (domain.executionMode !== "er") {
|
|
157
|
+
throw new Error("MagicBlock kit router connections are supported only for public ER domains");
|
|
158
|
+
}
|
|
159
|
+
const routerUrl = domain.magicblock?.routerUrl;
|
|
160
|
+
if (!routerUrl) {
|
|
161
|
+
throw new Error("MagicBlock router URL is required for ER execution");
|
|
162
|
+
}
|
|
163
|
+
return MagicBlockConnection.create(routerUrl, domain.magicblock?.routerWsUrl);
|
|
164
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merkle tree utilities for UTXOpia
|
|
3
|
+
*
|
|
4
|
+
* Provides structures and helpers for Merkle proofs.
|
|
5
|
+
* Actual tree operations use Poseidon hashing which is computed
|
|
6
|
+
* on-chain or via circom circuits.
|
|
7
|
+
*
|
|
8
|
+
* Note: This module does NOT compute Poseidon hashes in JavaScript.
|
|
9
|
+
* The on-chain program maintains the Merkle tree. This SDK provides
|
|
10
|
+
* proof structures for interaction with the program.
|
|
11
|
+
*/
|
|
12
|
+
export declare const TREE_DEPTH = 16;
|
|
13
|
+
export declare const ROOT_HISTORY_SIZE = 30;
|
|
14
|
+
export declare const MAX_LEAVES: number;
|
|
15
|
+
export declare const ZERO_VALUE: Uint8Array<ArrayBufferLike>;
|
|
16
|
+
/**
|
|
17
|
+
* Merkle proof structure
|
|
18
|
+
*/
|
|
19
|
+
export interface MerkleProof {
|
|
20
|
+
pathElements: Uint8Array[];
|
|
21
|
+
pathIndices: number[];
|
|
22
|
+
leafIndex: number;
|
|
23
|
+
root: Uint8Array;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Create a Merkle proof from on-chain data
|
|
27
|
+
*
|
|
28
|
+
* @param pathElements - Sibling hashes as 32-byte arrays
|
|
29
|
+
* @param pathIndices - Direction at each level (0=left, 1=right)
|
|
30
|
+
* @param leafIndex - Index of the leaf in the tree
|
|
31
|
+
* @param root - Current Merkle root
|
|
32
|
+
*/
|
|
33
|
+
export declare function createMerkleProof(pathElements: Uint8Array[], pathIndices: number[], leafIndex: number, root: Uint8Array): MerkleProof;
|
|
34
|
+
/**
|
|
35
|
+
* Create a Merkle proof from bigint values
|
|
36
|
+
*/
|
|
37
|
+
export declare function createMerkleProofFromBigints(pathElements: bigint[], pathIndices: number[], leafIndex: number, root: bigint): MerkleProof;
|
|
38
|
+
/**
|
|
39
|
+
* Convert Merkle proof to format expected by circom circuits
|
|
40
|
+
*/
|
|
41
|
+
export declare function proofToCircomFormat(proof: MerkleProof): {
|
|
42
|
+
merkle_path: string[];
|
|
43
|
+
path_indices: string[];
|
|
44
|
+
merkle_root: string;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Convert Merkle proof to format expected by on-chain program
|
|
48
|
+
*/
|
|
49
|
+
export declare function proofToOnChainFormat(proof: MerkleProof): {
|
|
50
|
+
siblings: number[][];
|
|
51
|
+
path: boolean[];
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Compute leaf index from path indices
|
|
55
|
+
*/
|
|
56
|
+
export declare function pathIndicesToLeafIndex(pathIndices: number[]): number;
|
|
57
|
+
/**
|
|
58
|
+
* Compute path indices from leaf index
|
|
59
|
+
*/
|
|
60
|
+
export declare function leafIndexToPathIndices(leafIndex: number, depth?: number): number[];
|
|
61
|
+
/**
|
|
62
|
+
* Create an empty/placeholder Merkle proof
|
|
63
|
+
* Used when constructing proofs before on-chain data is available
|
|
64
|
+
*/
|
|
65
|
+
export declare function createEmptyMerkleProof(): MerkleProof;
|
|
66
|
+
/**
|
|
67
|
+
* Parse a merkle proof response (hex strings) into BigInt format for circuit input.
|
|
68
|
+
* Accepts the response from /api/merkle/proof or /api/tree/proof.
|
|
69
|
+
*/
|
|
70
|
+
export declare function parseMerkleProofResponse(response: {
|
|
71
|
+
root: string;
|
|
72
|
+
siblings: string[];
|
|
73
|
+
indices: number[];
|
|
74
|
+
}): {
|
|
75
|
+
root: bigint;
|
|
76
|
+
pathElements: bigint[];
|
|
77
|
+
pathIndices: number[];
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Validate Merkle proof structure
|
|
81
|
+
*/
|
|
82
|
+
export declare function validateMerkleProofStructure(proof: MerkleProof): boolean;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merkle tree utilities for UTXOpia
|
|
3
|
+
*
|
|
4
|
+
* Provides structures and helpers for Merkle proofs.
|
|
5
|
+
* Actual tree operations use Poseidon hashing which is computed
|
|
6
|
+
* on-chain or via circom circuits.
|
|
7
|
+
*
|
|
8
|
+
* Note: This module does NOT compute Poseidon hashes in JavaScript.
|
|
9
|
+
* The on-chain program maintains the Merkle tree. This SDK provides
|
|
10
|
+
* proof structures for interaction with the program.
|
|
11
|
+
*/
|
|
12
|
+
import { bigintToBytes } from "./crypto";
|
|
13
|
+
import { toHex } from "./utils/encoding";
|
|
14
|
+
// Tree configuration - matches on-chain constants (depth 16 = 65,536 leaves)
|
|
15
|
+
export const TREE_DEPTH = 16;
|
|
16
|
+
export const ROOT_HISTORY_SIZE = 30;
|
|
17
|
+
export const MAX_LEAVES = 1 << TREE_DEPTH; // 65,536
|
|
18
|
+
// Zero value for empty nodes (matches on-chain)
|
|
19
|
+
export const ZERO_VALUE = bigintToBytes(0x2fe54c60d3ada40e0000000000000000000000000000000000000000n);
|
|
20
|
+
/**
|
|
21
|
+
* Create a Merkle proof from on-chain data
|
|
22
|
+
*
|
|
23
|
+
* @param pathElements - Sibling hashes as 32-byte arrays
|
|
24
|
+
* @param pathIndices - Direction at each level (0=left, 1=right)
|
|
25
|
+
* @param leafIndex - Index of the leaf in the tree
|
|
26
|
+
* @param root - Current Merkle root
|
|
27
|
+
*/
|
|
28
|
+
export function createMerkleProof(pathElements, pathIndices, leafIndex, root) {
|
|
29
|
+
if (pathElements.length !== TREE_DEPTH) {
|
|
30
|
+
throw new Error(`Expected ${TREE_DEPTH} path elements, got ${pathElements.length}`);
|
|
31
|
+
}
|
|
32
|
+
if (pathIndices.length !== TREE_DEPTH) {
|
|
33
|
+
throw new Error(`Expected ${TREE_DEPTH} path indices, got ${pathIndices.length}`);
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
pathElements: pathElements.map((el) => new Uint8Array(el)),
|
|
37
|
+
pathIndices: [...pathIndices],
|
|
38
|
+
leafIndex,
|
|
39
|
+
root: new Uint8Array(root),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Create a Merkle proof from bigint values
|
|
44
|
+
*/
|
|
45
|
+
export function createMerkleProofFromBigints(pathElements, pathIndices, leafIndex, root) {
|
|
46
|
+
return createMerkleProof(pathElements.map(bigintToBytes), pathIndices, leafIndex, bigintToBytes(root));
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Convert Merkle proof to format expected by circom circuits
|
|
50
|
+
*/
|
|
51
|
+
export function proofToCircomFormat(proof) {
|
|
52
|
+
return {
|
|
53
|
+
merkle_path: proof.pathElements.map((el) => "0x" + toHex(el)),
|
|
54
|
+
path_indices: proof.pathIndices.map((i) => i.toString()),
|
|
55
|
+
merkle_root: "0x" + toHex(proof.root),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Convert Merkle proof to format expected by on-chain program
|
|
60
|
+
*/
|
|
61
|
+
export function proofToOnChainFormat(proof) {
|
|
62
|
+
return {
|
|
63
|
+
siblings: proof.pathElements.map((el) => Array.from(el)),
|
|
64
|
+
path: proof.pathIndices.map((i) => i === 1),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Compute leaf index from path indices
|
|
69
|
+
*/
|
|
70
|
+
export function pathIndicesToLeafIndex(pathIndices) {
|
|
71
|
+
let index = 0;
|
|
72
|
+
for (let i = 0; i < pathIndices.length; i++) {
|
|
73
|
+
if (pathIndices[i] === 1) {
|
|
74
|
+
index |= 1 << i;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return index;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Compute path indices from leaf index
|
|
81
|
+
*/
|
|
82
|
+
export function leafIndexToPathIndices(leafIndex, depth = TREE_DEPTH) {
|
|
83
|
+
const indices = [];
|
|
84
|
+
let idx = leafIndex;
|
|
85
|
+
for (let i = 0; i < depth; i++) {
|
|
86
|
+
indices.push(idx & 1);
|
|
87
|
+
idx >>= 1;
|
|
88
|
+
}
|
|
89
|
+
return indices;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Create an empty/placeholder Merkle proof
|
|
93
|
+
* Used when constructing proofs before on-chain data is available
|
|
94
|
+
*/
|
|
95
|
+
export function createEmptyMerkleProof() {
|
|
96
|
+
const pathElements = [];
|
|
97
|
+
const pathIndices = [];
|
|
98
|
+
for (let i = 0; i < TREE_DEPTH; i++) {
|
|
99
|
+
pathElements.push(new Uint8Array(ZERO_VALUE));
|
|
100
|
+
pathIndices.push(0);
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
pathElements,
|
|
104
|
+
pathIndices,
|
|
105
|
+
leafIndex: 0,
|
|
106
|
+
root: new Uint8Array(ZERO_VALUE),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Parse a merkle proof response (hex strings) into BigInt format for circuit input.
|
|
111
|
+
* Accepts the response from /api/merkle/proof or /api/tree/proof.
|
|
112
|
+
*/
|
|
113
|
+
export function parseMerkleProofResponse(response) {
|
|
114
|
+
return {
|
|
115
|
+
root: BigInt("0x" + response.root),
|
|
116
|
+
pathElements: response.siblings.map((s) => BigInt("0x" + s)),
|
|
117
|
+
pathIndices: response.indices,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Validate Merkle proof structure
|
|
122
|
+
*/
|
|
123
|
+
export function validateMerkleProofStructure(proof) {
|
|
124
|
+
if (proof.pathElements.length !== TREE_DEPTH)
|
|
125
|
+
return false;
|
|
126
|
+
if (proof.pathIndices.length !== TREE_DEPTH)
|
|
127
|
+
return false;
|
|
128
|
+
if (proof.leafIndex < 0 || proof.leafIndex >= MAX_LEAVES)
|
|
129
|
+
return false;
|
|
130
|
+
if (proof.root.length !== 32)
|
|
131
|
+
return false;
|
|
132
|
+
for (const el of proof.pathElements) {
|
|
133
|
+
if (el.length !== 32)
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
for (const idx of proof.pathIndices) {
|
|
137
|
+
if (idx !== 0 && idx !== 1)
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
return true;
|
|
141
|
+
}
|