import { blake2b } from "@noble/hashes/blake2";
import bs58 from "bs58";
const MAVRYK_RPC = "https://atlasnet.rpc.mavryk.network";
// Ed25519 public key prefix for Mavryk (edpk)
const PREFIX_EDPK = new Uint8Array([13, 15, 37, 217]);
async function sendMavrykTransfer(
to: string,
amountMvk: number,
fromAddress: string,
solanaAddress: string,
): Promise<string> {
const wallet = dynamicClient.wallets.userWallets.find(w => w.chain === "SOL")!;
const pubkeyBytes = bs58.decode(solanaAddress);
const encodedPubKey = mavrykB58cencode(pubkeyBytes, PREFIX_EDPK);
const microMvk = Math.round(amountMvk * 1_000_000).toString();
const [blockHead, counterStr, managerKey] = await Promise.all([
fetch(`${MAVRYK_RPC}/chains/main/blocks/head/header`).then(r => r.json()),
fetch(`${MAVRYK_RPC}/chains/main/blocks/head/context/contracts/${fromAddress}/counter`).then(r => r.json()),
fetch(`${MAVRYK_RPC}/chains/main/blocks/head/context/contracts/${fromAddress}/manager_key`).then(r => r.json()).catch(() => null),
]);
const branch = blockHead.hash;
let counter = parseInt(counterStr) + 1;
const isRevealed = managerKey !== null && managerKey !== "";
const contents: object[] = [];
if (!isRevealed) {
contents.push({ kind: "reveal", source: fromAddress, fee: "1000",
counter: counter.toString(), gas_limit: "1000", storage_limit: "0",
public_key: encodedPubKey });
counter++;
}
contents.push({ kind: "transaction", source: fromAddress, fee: "1000",
counter: counter.toString(), gas_limit: "10300", storage_limit: "0",
amount: microMvk, destination: to });
const forgeRes = await fetch(`${MAVRYK_RPC}/chains/main/blocks/head/helpers/forge/operations`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ branch, contents }),
});
const forgedHex: string = await forgeRes.json();
// Mavryk operation signing: blake2b-256( 0x03 || forged_bytes )
const forgedBytes = hexToBytes(forgedHex);
const watermarked = concatBytes(new Uint8Array([0x03]), forgedBytes);
const digest = blake2b(watermarked, { dkLen: 32 }) as Uint8Array;
const { signedMessage } = await dynamicClient.wallets.signMessage({
wallet,
message: bytesToHex(digest),
});
const sigBytes = decodeSig(signedMessage);
const injectRes = await fetch(`${MAVRYK_RPC}/injection/operation`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: `"${forgedHex}${bytesToHex(sigBytes)}"`,
});
if (!injectRes.ok) throw new Error(`Injection failed: ${injectRes.status}`);
return injectRes.json();
}