Skip to main content

Mint an SVG NFT (ART)

Mint an SVG generated by the network as an on-chain ART NFT (Fork V3, active on mainnet).

Flow

1. Submit TASK_SUBMIT type="nft_svg" with prompt
2. Wait for TASK_REWARD (best miner produces SVG)
3. Fetch SVG from /api/chain/task/:id → bestResponse.content
4. Build ART_MINT tx with deterministic tokenId
5. Broadcast
6. NFT appears at /api/nft/:tokenId

Full code

import {
OmbraClient, Wallet,
buildTaskSubmitTx, buildArtMintTx,
computeArtTokenId, computeArtSeedHash,
} from "@ombrachain/sdk";
import { randomBytes } from "crypto";

const client = new OmbraClient({ endpoint: "https://api.ombra-net.com" });
const wallet = Wallet.fromMnemonic("...");

async function mintArt(prompt: string) {
// 1. Submit NFT SVG task
const taskId = randomBytes(16).toString("hex");
let acct = await client.chain.getAccount(wallet.address);
const submitTx = buildTaskSubmitTx(
wallet.address, taskId, "nft_svg", prompt,
500_000n, // 0.5 OMBRA — fair for quality SVG
acct.nonce, wallet.privateKey,
);
await client.chain.submitTx(submitTx);
console.log("task submitted:", taskId);

// 2. Poll for rewarded
for (let i = 0; i < 90; i++) {
await new Promise((r) => setTimeout(r, 2_000));
const task = await client.chain.getTask(taskId);
if (task.status === "rewarded") {
// 3. Get best SVG content
const bestResponse = task.responses.find((r: any) => r.minerId === task.bestMinerId);
const svg = bestResponse.content;
const minerId = task.bestMinerId;
const model = bestResponse.model;
const blockHash = bestResponse.blockHash;

// 4. Build ART_MINT
const tokenId = computeArtTokenId(taskId, svg);
const seedHash = computeArtSeedHash(prompt, minerId, blockHash);
acct = await client.chain.getAccount(wallet.address);

const mintTx = buildArtMintTx(wallet.address, {
tokenId,
svg,
owner: wallet.address,
taskId,
minerId,
seedHash,
model,
}, acct.nonce, wallet.privateKey);

await client.chain.submitTx(mintTx);
console.log("minted NFT:", tokenId);
console.log("explorer:", `https://ombra-net.com/nft/${tokenId}`);
return { tokenId, svg };
}
}
throw new Error("task did not complete in 3 minutes");
}

await mintArt("Minimal red triangle with crimson glow on dark background");

SVG sanitizer rules

The chain rejects unsafe SVG. Your SVG must:

  • Start with <svg and end with </svg>
  • Have xmlns="http://www.w3.org/2000/svg"
  • NOT contain: <script>, <foreignObject>, <iframe>, <image>, <link>, <style> with @import
  • NOT use: on* attributes (onclick, onload), javascript: URLs, external href
  • Be ≤ 16384 bytes total

The network's nft_svg task prompt wrapper enforces these rules on the miner side. If you build a custom SVG, sanitize before mint.

Browser via extension

const result = await window.ombra.request({
method: "ombra_mintArt",
params: {
svg: "<svg xmlns=...>...</svg>",
taskId,
minerId,
model,
},
}) as { tokenId: string; txHash: string };

Extension computes tokenId deterministically and shows the SVG preview before approval.

Verify the mint

# Metadata
curl https://api.ombra-net.com/api/nft/YOUR_TOKEN_ID

# Raw SVG
curl https://api.ombra-net.com/api/nft/YOUR_TOKEN_ID/svg

Or open https://ombra-net.com/nft/YOUR_TOKEN_ID in the explorer.

Transfer / burn

import { buildArtTransferTx, buildArtBurnTx } from "@ombrachain/sdk";

// Transfer
const xferTx = buildArtTransferTx(wallet.address, tokenId, recipientAddr, nonce, privKey);
await client.chain.submitTx(xferTx);

// Burn
const burnTx = buildArtBurnTx(wallet.address, tokenId, nonce, privKey);
await client.chain.submitTx(burnTx);