@silvana-one/mina-utils
Version:
Silvana Mina Utils
222 lines • 9.47 kB
JavaScript
import { Field, PublicKey, Mina, UInt64 } from "o1js";
import { fieldToBase64, fieldFromBase64 } from "../utils/base64-field.js";
export function createTransactionPayloads(tx) {
const transaction = tx.toJSON();
const txJSON = JSON.parse(transaction);
const signedData = JSON.stringify({ zkappCommand: txJSON });
const proverPayload = serializeTransaction(tx);
const fee = tx.transaction.feePayer.body.fee.toJSON();
const sender = tx.transaction.feePayer.body.publicKey.toBase58();
const nonce = Number(tx.transaction.feePayer.body.nonce.toBigint());
const memo = tx.transaction.memo;
const minaSignerPayload = {
zkappCommand: txJSON,
feePayer: {
feePayer: sender,
fee,
nonce,
memo,
},
};
const walletPayload = {
transaction,
nonce,
onlySign: true,
feePayer: {
fee,
memo,
},
};
return {
sender,
nonce,
memo,
fee,
walletPayload,
minaSignerPayload,
proverPayload,
signedData,
transaction,
};
}
export function transactionParams(params) {
const { proverPayload, signedData } = params;
const signedJson = JSON.parse(signedData);
const { sender, tx } = JSON.parse(proverPayload);
const transaction = Mina.Transaction.fromJSON(JSON.parse(tx));
const memo = transaction.transaction.memo;
return {
fee: UInt64.from(signedJson.zkappCommand.feePayer.body.fee),
sender: PublicKey.fromBase58(sender),
nonce: Number(signedJson.zkappCommand.feePayer.body.nonce),
memo,
};
}
export function parseTransactionPayloads(params) {
const { txNew } = params;
const proverPayload = "payloads" in params ? params.payloads.proverPayload : params.proverPayload;
const signedData = "payloads" in params ? params.payloads.signedData : params.signedData;
const signedJson = JSON.parse(signedData);
const { tx, blindingValues, length, forestJSONs } = JSON.parse(proverPayload);
const transaction = Mina.Transaction.fromJSON(JSON.parse(tx));
const forests = forestJSONs.map((f) => JSON.parse(f));
if (length !== txNew.transaction.accountUpdates.length) {
throw new Error(`New Transaction length mismatch: ${length} !== ${txNew.transaction.accountUpdates.length}`);
}
if (length !== transaction.transaction.accountUpdates.length) {
throw new Error(`Serialized Transaction length mismatch: ${length} !== ${transaction.transaction.accountUpdates.length}`);
}
for (let i = 0; i < length; i++) {
transaction.transaction.accountUpdates[i].lazyAuthorization =
txNew.transaction.accountUpdates[i].lazyAuthorization;
if (blindingValues[i] !== "") {
if (transaction.transaction.accountUpdates[i].lazyAuthorization ===
undefined ||
transaction.transaction.accountUpdates[i].lazyAuthorization
.blindingValue === undefined) {
throw new Error(`Lazy authorization blinding value is undefined for item ${i}`);
}
transaction.transaction.accountUpdates[i].lazyAuthorization.blindingValue = Field.fromJSON(blindingValues[i]);
}
if (forests[i].length > 0) {
if (transaction.transaction.accountUpdates[i].lazyAuthorization ===
undefined ||
transaction.transaction.accountUpdates[i].lazyAuthorization
.args === undefined) {
throw new Error(`Lazy authorization args is undefined for item ${i}`);
}
deserializeLazyAuthorization(transaction.transaction.accountUpdates[i].lazyAuthorization
.args, forests[i]);
if (forests[i].restoredItems !== forests[i].length) {
throw new Error(`Forest ${i} not fully restored`);
}
}
}
transaction.transaction.feePayer.authorization =
signedJson.zkappCommand.feePayer.authorization;
transaction.transaction.feePayer.body.fee = UInt64.from(signedJson.zkappCommand.feePayer.body.fee);
for (let i = 0; i < length; i++) {
const signature = signedJson.zkappCommand.accountUpdates[i].authorization.signature;
if (signature !== undefined && signature !== null) {
transaction.transaction.accountUpdates[i].authorization.signature =
signedJson.zkappCommand.accountUpdates[i].authorization.signature;
}
}
return transaction;
}
export function serializeTransaction(tx) {
const length = tx.transaction.accountUpdates.length;
let i;
const blindingValues = [];
const forests = [];
for (i = 0; i < length; i++) {
const la = tx.transaction.accountUpdates[i].lazyAuthorization;
if (la !== undefined &&
la.blindingValue !== undefined &&
la.kind === "lazy-proof")
blindingValues.push(la.blindingValue.toJSON());
else
blindingValues.push("");
const forest = { length: 0, items: [] };
serializeLazyAuthorization(tx.transaction.accountUpdates[i].lazyAuthorization?.args, forest);
forests.push(forest);
}
const serializedTransaction = JSON.stringify({
tx: tx.toJSON(),
blindingValues,
forestJSONs: forests.map((f) => JSON.stringify(f)),
length,
fee: tx.transaction.feePayer.body.fee.toJSON(),
sender: tx.transaction.feePayer.body.publicKey.toBase58(),
nonce: tx.transaction.feePayer.body.nonce.toBigint().toString(),
}, null, 2);
return serializedTransaction;
}
function serializeLazyAuthorization(lazyAuthorization, serialized) {
if (lazyAuthorization?.hash !== undefined && lazyAuthorization.hash.toJSON) {
serialized.items.push({
h: fieldToBase64(lazyAuthorization.hash),
});
}
if (lazyAuthorization?.previousHash !== undefined &&
lazyAuthorization.previousHash.toJSON) {
serialized.items.push({
p: fieldToBase64(lazyAuthorization.previousHash),
});
}
if (lazyAuthorization?.callData !== undefined &&
lazyAuthorization.callData.toJSON) {
serialized.items.push({
c: fieldToBase64(lazyAuthorization.callData),
});
}
if (lazyAuthorization?.id !== undefined) {
serialized.items.push({
i: lazyAuthorization.id,
});
}
if (Array.isArray(lazyAuthorization)) {
for (const item of lazyAuthorization) {
serializeLazyAuthorization(item, serialized);
}
}
if (typeof lazyAuthorization === "object") {
for (const key in lazyAuthorization) {
serializeLazyAuthorization(lazyAuthorization[key], serialized);
}
}
serialized.length = serialized.items.length;
}
function deserializeLazyAuthorization(lazyAuthorization, serialized) {
if (serialized.restoredItems === undefined)
serialized.restoredItems = 0;
if (lazyAuthorization?.hash !== undefined && lazyAuthorization.hash.toJSON) {
if (serialized.restoredItems >= serialized.length)
throw new Error("Restored more items than expected");
const hash = serialized.items[serialized.restoredItems].h;
if (hash === undefined)
throw new Error(`Hash is undefined for item ${serialized.restoredItems}`);
lazyAuthorization.hash = fieldFromBase64(hash);
serialized.restoredItems++;
}
if (lazyAuthorization?.previousHash !== undefined &&
lazyAuthorization.previousHash.toJSON) {
if (serialized.restoredItems >= serialized.length)
throw new Error("Restored more items than expected");
const previousHash = serialized.items[serialized.restoredItems].p;
if (previousHash === undefined)
throw new Error(`Previous hash is undefined for item ${serialized.restoredItems}`);
lazyAuthorization.previousHash = fieldFromBase64(previousHash);
serialized.restoredItems++;
}
if (lazyAuthorization?.callData !== undefined &&
lazyAuthorization.callData.toJSON) {
if (serialized.restoredItems >= serialized.length)
throw new Error("Restored more items than expected");
const callData = serialized.items[serialized.restoredItems].c;
if (callData === undefined)
throw new Error(`Call data is undefined for item ${serialized.restoredItems}`);
lazyAuthorization.callData = fieldFromBase64(callData);
serialized.restoredItems++;
}
if (lazyAuthorization?.id !== undefined) {
if (serialized.restoredItems >= serialized.length)
throw new Error("Restored more items than expected");
const id = serialized.items[serialized.restoredItems].i;
if (id === undefined)
throw new Error(`Id is undefined for item ${serialized.restoredItems}`);
lazyAuthorization.id = id;
serialized.restoredItems++;
}
if (Array.isArray(lazyAuthorization)) {
for (const item of lazyAuthorization) {
deserializeLazyAuthorization(item, serialized);
}
}
if (typeof lazyAuthorization === "object") {
for (const key in lazyAuthorization) {
deserializeLazyAuthorization(lazyAuthorization[key], serialized);
}
}
}
//# sourceMappingURL=transaction.js.map