Overview
Mavryk (formerly Tezos) uses the Ed25519 elliptic curve — the same as Solana. Addresses are derived with Blake2b-160 hashing and encoded using b58cencode from @mavrykdynamics/taquito-utils. Transactions are built and submitted using MavrykToolkit from @mavrykdynamics/taquito.
| Property | Value |
|---|
| Curve | Ed25519 |
| Root Wallet | Solana |
| Address Format | base58check (mv1...) |
| Hashing | Blake2b-160 (address), Blake2b-256 (signing) |
| Serialization | Micheline / node-forged binary |
| Smallest Unit | Mutez (1 MAV = 10^6 mutez) |
Dependencies
npm install @mavrykdynamics/taquito @mavrykdynamics/taquito-utils @noble/hashes bs58
Derive Address
Hash the Ed25519 public key with Blake2b-160 and encode with b58cencode using the MV1 prefix:
import { blake2b } from "@noble/hashes/blake2";
import { b58cencode, prefix, Prefix } from "@mavrykdynamics/taquito-utils";
import bs58 from "bs58";
function deriveMavrykAddress(solanaAddress: string): string {
const pubkey = bs58.decode(solanaAddress);
const hash = blake2b(pubkey, { dkLen: 20 });
return b58cencode(hash, prefix[Prefix.MV1]);
}
Sign a Message
Sign the raw UTF-8 bytes of the message using the Solana wallet:
import { signMessage } from "@dynamic-labs-sdk/client";
import type { WalletAccount } from "@dynamic-labs-sdk/client";
async function signMavrykMessage(
message: string,
solWallet: WalletAccount,
): Promise<string> {
const messageBytes = new TextEncoder().encode(message);
const result = await signMessage({
walletAccount: solWallet,
message: bytesToHex(messageBytes),
});
return result.signature;
}
Verify a Signature
Verify the Ed25519 signature against the raw message bytes:
import { ed25519 } from "@noble/curves/ed25519";
function verifyMavrykSignature(
message: string,
signature: string,
solanaAddress: string,
): boolean {
const pubkey = bs58.decode(solanaAddress);
const messageBytes = new TextEncoder().encode(message);
const sigBytes = decodeSig(signature);
return ed25519.verify(sigBytes, messageBytes, pubkey);
}
Check Balance
Query the MAV balance via the Mavryk node RPC:
async function getMavrykBalance(address: string): Promise<string> {
const res = await fetch(
`https://rpc.mavryk.network/chains/main/blocks/head/context/contracts/${address}/balance`,
);
if (!res.ok) return "0";
const mutezStr = await res.json();
const mutez = BigInt(mutezStr);
const divisor = BigInt(1_000_000);
const whole = mutez / divisor;
const frac = mutez % divisor;
const fracStr = frac.toString().padStart(6, "0").replace(/0+$/, "");
return fracStr.length === 0 ? whole.toString() : `${whole}.${fracStr}`;
}
Send a Transfer
Use MavrykToolkit with a custom signer that routes signing through Dynamic’s Solana wallet. The toolkit handles forging, watermarking, and injection automatically.
import { blake2b } from "@noble/hashes/blake2";
import { createWaasProvider } from "@dynamic-labs-sdk/client/waas/core";
import {
hex2buf, buf2hex, mergebuf,
b58cencode, prefix, Prefix,
} from "@mavrykdynamics/taquito-utils";
import { MavrykToolkit } from "@mavrykdynamics/taquito";
import bs58 from "bs58";
const MAVRYK_RPC = "https://basenet.rpc.mavryk.network";
class DynamicMavrykSigner {
constructor(
private pubkeyBytes: Uint8Array,
private solWallet: WalletAccount,
private dynamicClient: DynamicClient,
) {}
async publicKey() {
return b58cencode(this.pubkeyBytes, prefix[Prefix.EDPK]);
}
async publicKeyHash() {
return b58cencode(blake2b(this.pubkeyBytes, { dkLen: 20 }), prefix[Prefix.MV1]);
}
async secretKey(): Promise<string> {
throw new Error("Signing routed through Dynamic.");
}
async sign(bytes: string, watermark?: Uint8Array) {
let buf = hex2buf(bytes);
if (watermark) buf = mergebuf(watermark, buf);
// Mavryk requires Blake2b-256 pre-hash before signing
const digest = blake2b(new Uint8Array(buf), { dkLen: 32 });
const provider = createWaasProvider({ sdkClient: this.dynamicClient, chain: "SOL" });
const { signature } = await provider.signMessage({
walletAccount: this.solWallet,
message: bytesToHex(digest),
});
const sigBytes = decodeSig(signature);
return {
bytes,
sig: b58cencode(sigBytes, prefix[Prefix.SIG]),
prefixSig: b58cencode(sigBytes, prefix[Prefix.EDSIG]),
sbytes: bytes + buf2hex(Buffer.from(sigBytes)),
};
}
}
async function sendMavrykTransfer(
to: string,
amount: number,
solWallet: WalletAccount,
dynamicClient: DynamicClient,
): Promise<string> {
const pubkeyBytes = bs58.decode(solWallet.address);
const mavrykAddress = deriveMavrykAddress(solWallet.address);
const signer = new DynamicMavrykSigner(pubkeyBytes, solWallet, dynamicClient);
const toolkit = new MavrykToolkit(MAVRYK_RPC);
toolkit.setSignerProvider(signer);
const op = await toolkit.contract.transfer({ to, amount });
await op.confirmation(1);
return op.hash;
}
MavrykToolkit handles operation forging, watermarking (0x03), and injection. The custom signer applies Blake2b-256 pre-hashing before passing the digest to Dynamic’s signMessage.