import { AccountsResource } from "./resources/accounts.js";
import { TransactionsResource } from "./resources/transactions.js";
import { NotesResource } from "./resources/notes.js";
import { TagsResource } from "./resources/tags.js";
import { SettingsResource } from "./resources/settings.js";
import { CompilerResource } from "./resources/compiler.js";
import { KeystoreResource } from "./resources/keystore.js";
import { hashSeed } from "./utils.js";
export class MidenClient {
static _WasmWebClient = null;
static _MockWasmWebClient = null;
static _getWasmOrThrow = null;
#inner;
#getWasm;
#terminated = false;
#defaultProver = null;
#isMock = false;
constructor(inner, getWasm, defaultProver) {
this.#inner = inner;
this.#getWasm = getWasm;
this.#defaultProver = defaultProver ?? null;
this.accounts = new AccountsResource(inner, getWasm, this);
this.transactions = new TransactionsResource(inner, getWasm, this);
this.notes = new NotesResource(inner, getWasm, this);
this.tags = new TagsResource(inner, getWasm, this);
this.settings = new SettingsResource(inner, getWasm, this);
this.compile = new CompilerResource(inner, getWasm, this);
this.keystore = new KeystoreResource(inner, this);
}
static async create(options) {
if (!options?.rpcUrl) {
return MidenClient.createTestnet(options);
}
const getWasm = MidenClient._getWasmOrThrow;
const WebClientClass = MidenClient._WasmWebClient;
if (!WebClientClass || !getWasm) {
throw new Error(
"MidenClient not initialized. Import from the SDK package entry point."
);
}
const seed = options?.seed ? await hashSeed(options.seed) : undefined;
const rpcUrl = resolveRpcUrl(options?.rpcUrl);
const noteTransportUrl = resolveNoteTransportUrl(options?.noteTransportUrl);
let inner;
if (options?.keystore) {
inner = await WebClientClass.createClientWithExternalKeystore(
rpcUrl,
noteTransportUrl,
seed,
options?.storeName,
options.keystore.getKey,
options.keystore.insertKey,
options.keystore.sign
);
} else {
inner = await WebClientClass.createClient(
rpcUrl,
noteTransportUrl,
seed,
options?.storeName
);
}
let defaultProver = null;
if (options?.proverUrl) {
const wasm = await getWasm();
defaultProver = resolveProver(options.proverUrl, wasm);
}
const client = new MidenClient(inner, getWasm, defaultProver);
if (options?.autoSync) {
await client.sync();
}
return client;
}
static async createTestnet(options) {
return MidenClient.create({
rpcUrl: "testnet",
proverUrl: "testnet",
noteTransportUrl: "testnet",
autoSync: true,
...options,
});
}
static async createDevnet(options) {
return MidenClient.create({
rpcUrl: "devnet",
proverUrl: "devnet",
noteTransportUrl: "devnet",
autoSync: true,
...options,
});
}
static async createMock(options) {
const getWasm = MidenClient._getWasmOrThrow;
const MockWebClientClass = MidenClient._MockWasmWebClient;
if (!MockWebClientClass || !getWasm) {
throw new Error(
"MidenClient not initialized. Import from the SDK package entry point."
);
}
const seed = options?.seed ? await hashSeed(options.seed) : undefined;
const inner = await MockWebClientClass.createClient(
options?.serializedMockChain,
options?.serializedNoteTransport,
seed
);
const client = new MidenClient(inner, getWasm, null);
client.#isMock = true;
return client;
}
get defaultProver() {
return this.#defaultProver;
}
async sync(opts) {
this.assertNotTerminated();
return await this.#inner.syncStateWithTimeout(opts?.timeout ?? 0);
}
async getSyncHeight() {
this.assertNotTerminated();
return await this.#inner.getSyncHeight();
}
terminate() {
this.#terminated = true;
this.#inner.terminate?.();
}
[Symbol.dispose]() {
this.terminate();
}
async [Symbol.asyncDispose]() {
this.terminate();
}
storeIdentifier() {
this.assertNotTerminated();
return this.#inner.storeIdentifier();
}
proveBlock() {
this.assertNotTerminated();
this.#assertMock("proveBlock");
return this.#inner.proveBlock();
}
usesMockChain() {
return this.#isMock;
}
serializeMockChain() {
this.assertNotTerminated();
this.#assertMock("serializeMockChain");
return this.#inner.serializeMockChain();
}
serializeMockNoteTransportNode() {
this.assertNotTerminated();
this.#assertMock("serializeMockNoteTransportNode");
return this.#inner.serializeMockNoteTransportNode();
}
assertNotTerminated() {
if (this.#terminated) {
throw new Error("Client terminated");
}
}
#assertMock(method) {
if (!this.#isMock) {
throw new Error(`${method}() is only available on mock clients`);
}
}
}
const RPC_URLS = {
testnet: "https://rpc.testnet.miden.io",
devnet: "https://rpc.devnet.miden.io",
localhost: "http://localhost:57291",
local: "http://localhost:57291",
};
function resolveRpcUrl(rpcUrl) {
if (!rpcUrl) return undefined;
return RPC_URLS[rpcUrl.trim().toLowerCase()] ?? rpcUrl;
}
const PROVER_URLS = {
devnet: "https://tx-prover.devnet.miden.io",
testnet: "https://tx-prover.testnet.miden.io",
};
const NOTE_TRANSPORT_URLS = {
testnet: "https://transport.miden.io",
devnet: "https://transport.devnet.miden.io",
};
function resolveNoteTransportUrl(noteTransportUrl) {
if (!noteTransportUrl) return undefined;
return (
NOTE_TRANSPORT_URLS[noteTransportUrl.trim().toLowerCase()] ??
noteTransportUrl
);
}
function resolveProver(proverUrl, wasm) {
const normalized = proverUrl.trim().toLowerCase();
if (normalized === "local") {
return wasm.TransactionProver.newLocalProver();
}
const remoteUrl = PROVER_URLS[normalized] ?? proverUrl;
return wasm.TransactionProver.newRemoteProver(remoteUrl, undefined);
}