@miden-sdk/para 0.0.0-bootstrap → 0.16.0-rc.6
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 +45 -6
- package/dist/cjs/index.d.ts +4 -0
- package/dist/cjs/index.js +27 -0
- package/dist/cjs/midenClient.d.ts +19 -0
- package/dist/cjs/midenClient.js +131 -0
- package/dist/cjs/modalClient.d.ts +11 -0
- package/dist/cjs/modalClient.js +253 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/types.d.ts +34 -0
- package/dist/cjs/types.js +16 -0
- package/dist/cjs/utils.d.ts +33 -0
- package/dist/cjs/utils.js +141 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +9 -0
- package/dist/esm/midenClient.d.ts +19 -0
- package/dist/esm/midenClient.js +109 -0
- package/dist/esm/modalClient.d.ts +11 -0
- package/dist/esm/modalClient.js +233 -0
- package/dist/esm/package.json +4 -0
- package/dist/esm/types.d.ts +34 -0
- package/dist/esm/types.js +0 -0
- package/dist/esm/utils.d.ts +33 -0
- package/dist/esm/utils.js +111 -0
- package/package.json +65 -7
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var utils_exports = {};
|
|
30
|
+
__export(utils_exports, {
|
|
31
|
+
accountSeedFromStr: () => accountSeedFromStr,
|
|
32
|
+
evmPkToCommitment: () => evmPkToCommitment,
|
|
33
|
+
fromHexSig: () => fromHexSig,
|
|
34
|
+
getUncompressedPublicKeyFromWallet: () => getUncompressedPublicKeyFromWallet,
|
|
35
|
+
hexToBytes: () => import_utils.hexToBytes,
|
|
36
|
+
txSummaryToJson: () => txSummaryToJson
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(utils_exports);
|
|
39
|
+
var import_utils = require("@noble/hashes/utils.js");
|
|
40
|
+
const fromHexSig = (hexString) => {
|
|
41
|
+
if (hexString.length % 2 !== 0) {
|
|
42
|
+
throw new Error("Invalid string len");
|
|
43
|
+
}
|
|
44
|
+
const sigBytes = (0, import_utils.hexToBytes)(hexString);
|
|
45
|
+
const serialized = new Uint8Array(sigBytes.length + 2);
|
|
46
|
+
serialized[0] = 1;
|
|
47
|
+
serialized.set(sigBytes, 1);
|
|
48
|
+
return serialized;
|
|
49
|
+
};
|
|
50
|
+
const accountSeedFromStr = (str) => {
|
|
51
|
+
if (!str) return;
|
|
52
|
+
const buffer = new Uint8Array(32);
|
|
53
|
+
const bytes = (0, import_utils.utf8ToBytes)(str);
|
|
54
|
+
buffer.set(bytes.slice(0, 32));
|
|
55
|
+
return buffer;
|
|
56
|
+
};
|
|
57
|
+
const evmPkToCommitment = async (uncompressedPublicKey) => {
|
|
58
|
+
const { Felt, Poseidon2, FeltArray } = await import("@miden-sdk/miden-sdk");
|
|
59
|
+
const withoutPrefix = uncompressedPublicKey.slice(4);
|
|
60
|
+
const x = withoutPrefix.slice(0, 64);
|
|
61
|
+
const y = withoutPrefix.slice(64);
|
|
62
|
+
const felts = [...coordToLimbs(x), ...coordToLimbs(y)].map(
|
|
63
|
+
(limb) => new Felt(BigInt(limb))
|
|
64
|
+
);
|
|
65
|
+
return Poseidon2.hashElements(new FeltArray(felts));
|
|
66
|
+
};
|
|
67
|
+
const coordToLimbs = (hex) => {
|
|
68
|
+
const bytes = (0, import_utils.hexToBytes)(hex);
|
|
69
|
+
if (bytes.length !== 32) {
|
|
70
|
+
throw new Error(`Expected a 32-byte coordinate, got ${bytes.length}`);
|
|
71
|
+
}
|
|
72
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
73
|
+
const limbs = [];
|
|
74
|
+
for (let i = 7; i >= 0; i--) {
|
|
75
|
+
limbs.push(view.getUint32(i * 4, false));
|
|
76
|
+
}
|
|
77
|
+
return limbs;
|
|
78
|
+
};
|
|
79
|
+
const getUncompressedPublicKeyFromWallet = async (para, wallet) => {
|
|
80
|
+
if (wallet.publicKey) {
|
|
81
|
+
return wallet.publicKey;
|
|
82
|
+
}
|
|
83
|
+
const { token } = await para.issueJwt();
|
|
84
|
+
const payload = JSON.parse(window.atob(token.split(".")[1]));
|
|
85
|
+
const wallets = payload.data?.connectedWallets;
|
|
86
|
+
if (!wallets) {
|
|
87
|
+
throw new Error("Got invalid jwt token");
|
|
88
|
+
}
|
|
89
|
+
const w = wallets.find((connected) => connected.id === wallet.id);
|
|
90
|
+
if (!w) {
|
|
91
|
+
throw new Error("Wallet Not Found in jwt data");
|
|
92
|
+
}
|
|
93
|
+
if (!w.publicKey) {
|
|
94
|
+
throw new Error("Wallet in jwt data has no public key");
|
|
95
|
+
}
|
|
96
|
+
return w.publicKey;
|
|
97
|
+
};
|
|
98
|
+
const txSummaryToJson = (txSummary) => {
|
|
99
|
+
const inputNotes = txSummary.inputNotes().notes().map((inputNote) => ({
|
|
100
|
+
id: inputNote.id().toString(),
|
|
101
|
+
assets: inputNote.note().assets().fungibleAssets().map((asset) => {
|
|
102
|
+
return {
|
|
103
|
+
assetId: asset.faucetId().toString(),
|
|
104
|
+
amount: asset.amount().toString()
|
|
105
|
+
};
|
|
106
|
+
}),
|
|
107
|
+
sender: inputNote.note().metadata().sender().toString()
|
|
108
|
+
}));
|
|
109
|
+
const outputNotes = txSummary.outputNotes().notes().map((outputNote) => {
|
|
110
|
+
const assets = outputNote.assets();
|
|
111
|
+
if (!assets) {
|
|
112
|
+
throw new Error(
|
|
113
|
+
`Output note ${outputNote.id().toString()} carries no asset data; refusing to summarize a transaction whose assets are unknown`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
id: outputNote.id().toString(),
|
|
118
|
+
assets: assets.fungibleAssets().map((asset) => {
|
|
119
|
+
return {
|
|
120
|
+
assetId: asset.faucetId().toString(),
|
|
121
|
+
amount: asset.amount().toString()
|
|
122
|
+
};
|
|
123
|
+
}),
|
|
124
|
+
noteType: noteTypeToString(outputNote.metadata().noteType())
|
|
125
|
+
};
|
|
126
|
+
});
|
|
127
|
+
return {
|
|
128
|
+
inputNotes,
|
|
129
|
+
outputNotes
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
function noteTypeToString(noteType) {
|
|
133
|
+
switch (noteType) {
|
|
134
|
+
case 1:
|
|
135
|
+
return "public";
|
|
136
|
+
case 0:
|
|
137
|
+
return "private";
|
|
138
|
+
default:
|
|
139
|
+
return "UNKNOWN";
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export * from "./midenClient.js";
|
|
2
|
+
export { evmPkToCommitment, getUncompressedPublicKeyFromWallet, } from "./utils.js";
|
|
3
|
+
export type { MidenAccountOpts, Opts, MidenAccountStorageMode, TxSummaryJson, } from "./types.js";
|
|
4
|
+
export type { CustomSignConfirmStep } from "./midenClient.js";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ParaWeb, Wallet } from "@getpara/web-sdk";
|
|
2
|
+
import type { Opts, TxSummaryJson } from "./types.js";
|
|
3
|
+
import type { MidenClient } from "@miden-sdk/miden-sdk";
|
|
4
|
+
export type CustomSignConfirmStep = (txSummaryJson: TxSummaryJson) => Promise<unknown>;
|
|
5
|
+
/**
|
|
6
|
+
* Creates a signing callback that routes Miden signing requests through Para.
|
|
7
|
+
* Prompts the user with a modal before delegating the keccak-hashed message to Para's signer,
|
|
8
|
+
* and optionally runs a custom confirmation step in between.
|
|
9
|
+
*/
|
|
10
|
+
export declare const signCb: (para: ParaWeb, wallet: Wallet, showSigningModal: boolean, customSignConfirmStep?: CustomSignConfirmStep) => (_: Uint8Array, signingInputs: Uint8Array) => Promise<Uint8Array<ArrayBuffer>>;
|
|
11
|
+
/**
|
|
12
|
+
* Builds a MidenClient wired to Para wallets and ensures an account exists for the user.
|
|
13
|
+
* Filters to EVM wallets, prompts for selection, creates the client, and
|
|
14
|
+
* hydrates or creates the corresponding Miden account before returning the client + account id.
|
|
15
|
+
*/
|
|
16
|
+
export declare function createParaMidenClient(para: ParaWeb, wallets: Wallet[], opts: Opts, showSigningModal?: boolean, customSignConfirmStep?: CustomSignConfirmStep): Promise<{
|
|
17
|
+
client: MidenClient;
|
|
18
|
+
accountId: string;
|
|
19
|
+
}>;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import {
|
|
2
|
+
hexStringToBase64
|
|
3
|
+
} from "@getpara/web-sdk";
|
|
4
|
+
import { keccak_256 as keccak256 } from "@noble/hashes/sha3.js";
|
|
5
|
+
import {
|
|
6
|
+
accountSeedFromStr,
|
|
7
|
+
evmPkToCommitment,
|
|
8
|
+
fromHexSig,
|
|
9
|
+
getUncompressedPublicKeyFromWallet,
|
|
10
|
+
txSummaryToJson
|
|
11
|
+
} from "./utils.js";
|
|
12
|
+
import { bytesToHex, hexToBytes } from "@noble/hashes/utils.js";
|
|
13
|
+
import { accountSelectionModal, signingModal } from "./modalClient.js";
|
|
14
|
+
const signCb = (para, wallet, showSigningModal, customSignConfirmStep) => {
|
|
15
|
+
return async (_, signingInputs) => {
|
|
16
|
+
const { SigningInputs } = await import("@miden-sdk/miden-sdk");
|
|
17
|
+
const inputs = SigningInputs.deserialize(signingInputs);
|
|
18
|
+
let commitment = inputs.toCommitment().toHex().slice(2);
|
|
19
|
+
const hashed = bytesToHex(keccak256(hexToBytes(commitment)));
|
|
20
|
+
const txSummaryJson = txSummaryToJson(inputs.transactionSummaryPayload());
|
|
21
|
+
if (showSigningModal) {
|
|
22
|
+
const confirmed = await signingModal(txSummaryJson);
|
|
23
|
+
if (!confirmed) {
|
|
24
|
+
throw new Error("User cancelled signing");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (customSignConfirmStep) {
|
|
28
|
+
await customSignConfirmStep(txSummaryJson);
|
|
29
|
+
}
|
|
30
|
+
console.time("Para Signing Time");
|
|
31
|
+
const res = await para.signMessage({
|
|
32
|
+
walletId: wallet.id,
|
|
33
|
+
messageBase64: hexStringToBase64(hashed)
|
|
34
|
+
});
|
|
35
|
+
console.timeEnd("Para Signing Time");
|
|
36
|
+
const signature = res.signature;
|
|
37
|
+
const sig = fromHexSig(signature);
|
|
38
|
+
return sig;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
async function createAccount(client, publicKey, opts) {
|
|
42
|
+
const { AccountBuilder, AccountComponent, AccountStorageMode } = await import("@miden-sdk/miden-sdk");
|
|
43
|
+
await client.sync();
|
|
44
|
+
let pkc = await evmPkToCommitment(publicKey);
|
|
45
|
+
const accountBuilder = new AccountBuilder(
|
|
46
|
+
accountSeedFromStr(opts.accountSeed) ?? new Uint8Array(32).fill(0)
|
|
47
|
+
);
|
|
48
|
+
const accountStorageMode = opts.storageMode === "private" ? AccountStorageMode.private() : AccountStorageMode.public();
|
|
49
|
+
const account = accountBuilder.withAuthComponent(
|
|
50
|
+
AccountComponent.createAuthComponentFromCommitment(pkc, 1)
|
|
51
|
+
).storageMode(accountStorageMode).withBasicWalletComponent().build().account;
|
|
52
|
+
if (opts.storageMode !== "private") {
|
|
53
|
+
try {
|
|
54
|
+
await client.accounts.import(account);
|
|
55
|
+
} catch {
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const existing = await client.accounts.get(account.id());
|
|
59
|
+
if (!existing) {
|
|
60
|
+
await client.accounts.insert({ account });
|
|
61
|
+
}
|
|
62
|
+
await client.sync();
|
|
63
|
+
return account.id().toString();
|
|
64
|
+
}
|
|
65
|
+
async function createParaMidenClient(para, wallets, opts, showSigningModal = true, customSignConfirmStep) {
|
|
66
|
+
const evmWallets = wallets.filter((wallet2) => wallet2.type === "EVM");
|
|
67
|
+
if (!evmWallets?.length) {
|
|
68
|
+
throw new Error("No EVM wallets provided");
|
|
69
|
+
}
|
|
70
|
+
const accountKeys = await Promise.all(
|
|
71
|
+
evmWallets.map((w) => getUncompressedPublicKeyFromWallet(para, w))
|
|
72
|
+
);
|
|
73
|
+
const selectedIndex = await accountSelectionModal(accountKeys);
|
|
74
|
+
const wallet = evmWallets[selectedIndex] ?? evmWallets[0];
|
|
75
|
+
const publicKey = accountKeys[selectedIndex] ?? accountKeys[0];
|
|
76
|
+
const { MidenClient } = await import("@miden-sdk/miden-sdk");
|
|
77
|
+
if (opts.storageMode === "private" && !opts.accountSeed) {
|
|
78
|
+
throw new Error("accountSeed is required when using private storage mode");
|
|
79
|
+
}
|
|
80
|
+
const signCallback = signCb(
|
|
81
|
+
para,
|
|
82
|
+
wallet,
|
|
83
|
+
showSigningModal,
|
|
84
|
+
customSignConfirmStep
|
|
85
|
+
);
|
|
86
|
+
const noteTransportUrl = opts.noteTransportUrl || opts.nodeTransportUrl || "https://transport.miden.io";
|
|
87
|
+
const client = await MidenClient.create({
|
|
88
|
+
rpcUrl: opts.endpoint,
|
|
89
|
+
noteTransportUrl,
|
|
90
|
+
seed: accountSeedFromStr(opts.seed),
|
|
91
|
+
keystore: {
|
|
92
|
+
getKey: async () => void 0,
|
|
93
|
+
insertKey: async () => {
|
|
94
|
+
},
|
|
95
|
+
sign: signCallback
|
|
96
|
+
},
|
|
97
|
+
autoSync: true
|
|
98
|
+
});
|
|
99
|
+
const accountId = await createAccount(
|
|
100
|
+
client,
|
|
101
|
+
publicKey,
|
|
102
|
+
opts
|
|
103
|
+
);
|
|
104
|
+
return { client, accountId };
|
|
105
|
+
}
|
|
106
|
+
export {
|
|
107
|
+
createParaMidenClient,
|
|
108
|
+
signCb
|
|
109
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { TxSummaryJson } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Shows a lightweight confirmation modal for signing a hashed transaction.
|
|
4
|
+
* Resolves to true when the user confirms, false when cancelled; no-op on the server.
|
|
5
|
+
*/
|
|
6
|
+
export declare const signingModal: (txSummaryJson: TxSummaryJson) => Promise<boolean>;
|
|
7
|
+
/**
|
|
8
|
+
* Shows a selectable list of account commitments and resolves with the chosen index.
|
|
9
|
+
* Returns 0 immediately when only one account is provided or when running server-side.
|
|
10
|
+
*/
|
|
11
|
+
export declare const accountSelectionModal: (accounts: string[]) => Promise<number>;
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
const createModalShell = (titleText) => {
|
|
2
|
+
const existing = document.getElementById("para-signing-modal");
|
|
3
|
+
if (existing) existing.remove();
|
|
4
|
+
const overlay = document.createElement("div");
|
|
5
|
+
overlay.id = "para-signing-modal";
|
|
6
|
+
Object.assign(overlay.style, {
|
|
7
|
+
position: "fixed",
|
|
8
|
+
inset: "0",
|
|
9
|
+
background: "rgba(0, 0, 0, 0.6)",
|
|
10
|
+
display: "flex",
|
|
11
|
+
alignItems: "center",
|
|
12
|
+
justifyContent: "center",
|
|
13
|
+
zIndex: "9999",
|
|
14
|
+
padding: "16px",
|
|
15
|
+
boxSizing: "border-box",
|
|
16
|
+
color: "#0f172a",
|
|
17
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif'
|
|
18
|
+
});
|
|
19
|
+
const modal = document.createElement("div");
|
|
20
|
+
Object.assign(modal.style, {
|
|
21
|
+
background: "#f8fafc",
|
|
22
|
+
borderRadius: "12px",
|
|
23
|
+
padding: "20px",
|
|
24
|
+
maxWidth: "420px",
|
|
25
|
+
width: "100%",
|
|
26
|
+
boxShadow: "0 12px 40px rgba(15, 23, 42, 0.25)"
|
|
27
|
+
});
|
|
28
|
+
const title = document.createElement("div");
|
|
29
|
+
title.textContent = titleText;
|
|
30
|
+
Object.assign(title.style, {
|
|
31
|
+
fontSize: "16px",
|
|
32
|
+
fontWeight: "600",
|
|
33
|
+
marginBottom: "12px"
|
|
34
|
+
});
|
|
35
|
+
modal.append(title);
|
|
36
|
+
overlay.append(modal);
|
|
37
|
+
return { overlay, modal };
|
|
38
|
+
};
|
|
39
|
+
const createActionsRow = () => {
|
|
40
|
+
const actions = document.createElement("div");
|
|
41
|
+
Object.assign(actions.style, {
|
|
42
|
+
display: "flex",
|
|
43
|
+
gap: "10px",
|
|
44
|
+
justifyContent: "flex-end"
|
|
45
|
+
});
|
|
46
|
+
return actions;
|
|
47
|
+
};
|
|
48
|
+
const createPrimaryButton = (label) => {
|
|
49
|
+
const button = document.createElement("button");
|
|
50
|
+
button.textContent = label;
|
|
51
|
+
Object.assign(button.style, {
|
|
52
|
+
padding: "10px 16px",
|
|
53
|
+
borderRadius: "8px",
|
|
54
|
+
border: "none",
|
|
55
|
+
background: "#0ea5e9",
|
|
56
|
+
color: "#fff",
|
|
57
|
+
cursor: "pointer",
|
|
58
|
+
fontWeight: "600"
|
|
59
|
+
});
|
|
60
|
+
return button;
|
|
61
|
+
};
|
|
62
|
+
const createSecondaryButton = (label) => {
|
|
63
|
+
const button = document.createElement("button");
|
|
64
|
+
button.textContent = label;
|
|
65
|
+
Object.assign(button.style, {
|
|
66
|
+
padding: "10px 16px",
|
|
67
|
+
borderRadius: "8px",
|
|
68
|
+
border: "1px solid #cbd5e1",
|
|
69
|
+
background: "#fff",
|
|
70
|
+
color: "#0f172a",
|
|
71
|
+
cursor: "pointer",
|
|
72
|
+
fontWeight: "600"
|
|
73
|
+
});
|
|
74
|
+
return button;
|
|
75
|
+
};
|
|
76
|
+
const signingModal = (txSummaryJson) => {
|
|
77
|
+
if (typeof document === "undefined") return Promise.resolve(true);
|
|
78
|
+
return new Promise((resolve) => {
|
|
79
|
+
const { overlay, modal } = createModalShell(
|
|
80
|
+
"Do you want to sign this transaction?"
|
|
81
|
+
);
|
|
82
|
+
const txDetails = document.createElement("div");
|
|
83
|
+
Object.assign(txDetails.style, {
|
|
84
|
+
fontSize: "13px",
|
|
85
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
86
|
+
background: "#e2e8f0",
|
|
87
|
+
padding: "10px",
|
|
88
|
+
borderRadius: "8px",
|
|
89
|
+
marginBottom: "16px",
|
|
90
|
+
color: "#0f172a",
|
|
91
|
+
maxHeight: "300px",
|
|
92
|
+
overflowY: "auto"
|
|
93
|
+
});
|
|
94
|
+
const inputSection = document.createElement("div");
|
|
95
|
+
inputSection.style.marginBottom = "12px";
|
|
96
|
+
const inputTitle = document.createElement("div");
|
|
97
|
+
inputTitle.textContent = "Input Notes:";
|
|
98
|
+
inputTitle.style.fontWeight = "600";
|
|
99
|
+
inputTitle.style.marginBottom = "4px";
|
|
100
|
+
inputSection.append(inputTitle);
|
|
101
|
+
if (txSummaryJson.inputNotes.length === 0) {
|
|
102
|
+
const noInputs = document.createElement("div");
|
|
103
|
+
noInputs.textContent = "None";
|
|
104
|
+
noInputs.style.color = "#64748b";
|
|
105
|
+
inputSection.append(noInputs);
|
|
106
|
+
} else {
|
|
107
|
+
txSummaryJson.inputNotes.forEach((note, idx) => {
|
|
108
|
+
const noteDiv = document.createElement("div");
|
|
109
|
+
noteDiv.style.marginBottom = "8px";
|
|
110
|
+
noteDiv.style.paddingLeft = "8px";
|
|
111
|
+
noteDiv.innerHTML = `
|
|
112
|
+
<div><strong>Note ${idx + 1}:</strong> ${note.id}</div>
|
|
113
|
+
<div style="padding-left: 8px; color: #475569;">
|
|
114
|
+
Sender: ${note.sender}<br/>
|
|
115
|
+
Assets: ${note.assets.map((a) => `${a.amount} of ${a.assetId}`).join(", ") || "None"}
|
|
116
|
+
</div>
|
|
117
|
+
`;
|
|
118
|
+
inputSection.append(noteDiv);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
const outputSection = document.createElement("div");
|
|
122
|
+
const outputTitle = document.createElement("div");
|
|
123
|
+
outputTitle.textContent = "Output Notes:";
|
|
124
|
+
outputTitle.style.fontWeight = "600";
|
|
125
|
+
outputTitle.style.marginBottom = "4px";
|
|
126
|
+
outputSection.append(outputTitle);
|
|
127
|
+
if (txSummaryJson.outputNotes.length === 0) {
|
|
128
|
+
const noOutputs = document.createElement("div");
|
|
129
|
+
noOutputs.textContent = "None";
|
|
130
|
+
noOutputs.style.color = "#64748b";
|
|
131
|
+
outputSection.append(noOutputs);
|
|
132
|
+
} else {
|
|
133
|
+
txSummaryJson.outputNotes.forEach((note, idx) => {
|
|
134
|
+
const noteDiv = document.createElement("div");
|
|
135
|
+
noteDiv.style.marginBottom = "8px";
|
|
136
|
+
noteDiv.style.paddingLeft = "8px";
|
|
137
|
+
noteDiv.innerHTML = `
|
|
138
|
+
<div><strong>Note ${idx + 1}:</strong> ${note.id}</div>
|
|
139
|
+
<div style="padding-left: 8px; color: #475569;">
|
|
140
|
+
Type: ${note.noteType}<br/>
|
|
141
|
+
Assets: ${note.assets.map((a) => `${a.amount} of ${a.assetId}`).join(", ") || "None"}
|
|
142
|
+
</div>
|
|
143
|
+
`;
|
|
144
|
+
outputSection.append(noteDiv);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
txDetails.append(inputSection, outputSection);
|
|
148
|
+
const actions = createActionsRow();
|
|
149
|
+
const yesBtn = createPrimaryButton("Yes");
|
|
150
|
+
const noBtn = createSecondaryButton("No");
|
|
151
|
+
const cleanup = () => {
|
|
152
|
+
overlay.remove();
|
|
153
|
+
};
|
|
154
|
+
yesBtn.onclick = () => {
|
|
155
|
+
cleanup();
|
|
156
|
+
resolve(true);
|
|
157
|
+
};
|
|
158
|
+
noBtn.onclick = () => {
|
|
159
|
+
cleanup();
|
|
160
|
+
resolve(false);
|
|
161
|
+
};
|
|
162
|
+
actions.append(noBtn, yesBtn);
|
|
163
|
+
modal.append(txDetails, actions);
|
|
164
|
+
document.body.append(overlay);
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
const accountSelectionModal = (accounts) => {
|
|
168
|
+
if (accounts.length === 1 || typeof document === "undefined") {
|
|
169
|
+
return Promise.resolve(0);
|
|
170
|
+
}
|
|
171
|
+
return new Promise((resolve) => {
|
|
172
|
+
const { overlay, modal } = createModalShell("Choose an account to use");
|
|
173
|
+
const list = document.createElement("div");
|
|
174
|
+
Object.assign(list.style, {
|
|
175
|
+
display: "flex",
|
|
176
|
+
flexDirection: "column",
|
|
177
|
+
gap: "8px",
|
|
178
|
+
marginBottom: "16px"
|
|
179
|
+
});
|
|
180
|
+
let selectedIndex = 0;
|
|
181
|
+
const items = [];
|
|
182
|
+
const highlightSelection = () => {
|
|
183
|
+
items.forEach((item, idx) => {
|
|
184
|
+
item.style.borderColor = idx === selectedIndex ? "#0ea5e9" : "#cbd5e1";
|
|
185
|
+
item.style.background = idx === selectedIndex ? "#e0f2fe" : "#e2e8f0";
|
|
186
|
+
});
|
|
187
|
+
};
|
|
188
|
+
const okBtn = createPrimaryButton("Ok");
|
|
189
|
+
okBtn.disabled = !accounts.length;
|
|
190
|
+
accounts.forEach((account, index) => {
|
|
191
|
+
const item = document.createElement("button");
|
|
192
|
+
item.type = "button";
|
|
193
|
+
item.textContent = account;
|
|
194
|
+
Object.assign(item.style, {
|
|
195
|
+
padding: "10px",
|
|
196
|
+
borderRadius: "8px",
|
|
197
|
+
border: "1px solid #cbd5e1",
|
|
198
|
+
background: "#e2e8f0",
|
|
199
|
+
fontSize: "13px",
|
|
200
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
|
|
201
|
+
color: "#0f172a",
|
|
202
|
+
wordBreak: "break-all",
|
|
203
|
+
textAlign: "left",
|
|
204
|
+
cursor: "pointer"
|
|
205
|
+
});
|
|
206
|
+
item.onclick = () => {
|
|
207
|
+
selectedIndex = index;
|
|
208
|
+
highlightSelection();
|
|
209
|
+
okBtn.disabled = false;
|
|
210
|
+
};
|
|
211
|
+
items.push(item);
|
|
212
|
+
list.append(item);
|
|
213
|
+
});
|
|
214
|
+
if (items.length) {
|
|
215
|
+
highlightSelection();
|
|
216
|
+
}
|
|
217
|
+
const actions = createActionsRow();
|
|
218
|
+
const cleanup = () => {
|
|
219
|
+
overlay.remove();
|
|
220
|
+
};
|
|
221
|
+
okBtn.onclick = () => {
|
|
222
|
+
cleanup();
|
|
223
|
+
resolve(selectedIndex);
|
|
224
|
+
};
|
|
225
|
+
actions.append(okBtn);
|
|
226
|
+
modal.append(list, actions);
|
|
227
|
+
document.body.append(overlay);
|
|
228
|
+
});
|
|
229
|
+
};
|
|
230
|
+
export {
|
|
231
|
+
accountSelectionModal,
|
|
232
|
+
signingModal
|
|
233
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** @public */
|
|
2
|
+
export interface MidenClientOpts {
|
|
3
|
+
endpoint?: string;
|
|
4
|
+
noteTransportUrl?: string;
|
|
5
|
+
/**
|
|
6
|
+
* @deprecated Use noteTransportUrl instead
|
|
7
|
+
*/
|
|
8
|
+
nodeTransportUrl?: string;
|
|
9
|
+
seed?: string;
|
|
10
|
+
}
|
|
11
|
+
export type MidenAccountStorageMode = "public" | "private";
|
|
12
|
+
export interface MidenAccountOpts {
|
|
13
|
+
accountSeed?: string;
|
|
14
|
+
storageMode: MidenAccountStorageMode;
|
|
15
|
+
}
|
|
16
|
+
export type Opts = MidenClientOpts & MidenAccountOpts;
|
|
17
|
+
interface NoteIdAndAsset {
|
|
18
|
+
id: string;
|
|
19
|
+
assets: {
|
|
20
|
+
assetId: string;
|
|
21
|
+
amount: string;
|
|
22
|
+
}[];
|
|
23
|
+
}
|
|
24
|
+
interface InputNoteSender {
|
|
25
|
+
sender: string;
|
|
26
|
+
}
|
|
27
|
+
interface OutputNoteType {
|
|
28
|
+
noteType: string;
|
|
29
|
+
}
|
|
30
|
+
export interface TxSummaryJson {
|
|
31
|
+
inputNotes: (NoteIdAndAsset & InputNoteSender)[];
|
|
32
|
+
outputNotes: (NoteIdAndAsset & OutputNoteType)[];
|
|
33
|
+
}
|
|
34
|
+
export {};
|
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import ParaWeb, { Wallet } from "@getpara/web-sdk";
|
|
2
|
+
import type { TransactionSummary } from "@miden-sdk/miden-sdk";
|
|
3
|
+
import { hexToBytes } from "@noble/hashes/utils.js";
|
|
4
|
+
import { TxSummaryJson } from "./types.js";
|
|
5
|
+
/** @public */
|
|
6
|
+
export { hexToBytes };
|
|
7
|
+
/**
|
|
8
|
+
* Converts a Para hex signature into the serialized format expected by Miden.
|
|
9
|
+
* Prefixes the auth scheme byte and appends the extra padding byte required by current crypto libs.
|
|
10
|
+
*/
|
|
11
|
+
export declare const fromHexSig: (hexString: string) => Uint8Array<ArrayBuffer>;
|
|
12
|
+
/**
|
|
13
|
+
* Derives a 32-byte seed buffer from a UTF-8 string, truncating when longer than 32 bytes.
|
|
14
|
+
*/
|
|
15
|
+
export declare const accountSeedFromStr: (str?: string) => Uint8Array<ArrayBuffer> | undefined;
|
|
16
|
+
/**
|
|
17
|
+
* Converts an uncompressed EVM public key into a Miden commitment.
|
|
18
|
+
* Assumes input format `0x04${x}${y}` where x and y are 64-char hex strings.
|
|
19
|
+
*
|
|
20
|
+
* The protocol commits to the affine point as Poseidon2 over 16 field elements:
|
|
21
|
+
* the x coordinate then the y coordinate, each as eight 32-bit limbs in
|
|
22
|
+
* little-endian limb order. 0.15 hashed a different preimage — nine felts
|
|
23
|
+
* packed from the 33-byte compressed SEC1 encoding — so a commitment computed
|
|
24
|
+
* by an older release will not match one computed here.
|
|
25
|
+
*/
|
|
26
|
+
export declare const evmPkToCommitment: (uncompressedPublicKey: string) => Promise<import("@miden-sdk/miden-sdk").Word>;
|
|
27
|
+
/**
|
|
28
|
+
* Retrieves the uncompressed public key for a Para wallet, falling back to JWT data when absent.
|
|
29
|
+
* Throws rather than returning undefined: callers derive the account commitment from this value,
|
|
30
|
+
* so an absent key is an error, not a state to propagate.
|
|
31
|
+
*/
|
|
32
|
+
export declare const getUncompressedPublicKeyFromWallet: (para: ParaWeb, wallet: Wallet) => Promise<string>;
|
|
33
|
+
export declare const txSummaryToJson: (txSummary: TransactionSummary) => TxSummaryJson;
|