Developer DocumentationPlaykit Integration
On-chain wallet burns for in-game sinks, with signed backend settlement.
A burn is an on-chain action: the player sends Project Tokens from their own
wallet to a signed sink address — the dead address (0x…dead) by default, but the
backend may sign any destination. Forest records the verified on-chain transfer
with its reason/metadata for tracking. A transfer to 0x…dead is a real,
provable supply burn; any other sink is just the app-chosen destination, not a
supply reduction.
Use it for in-game sinks such as activation fees. Burns are wallet-side and never touch Game Balance.
Authorize before you transfer
The burn settlement requires an unused action authorization, so authorize first — otherwise the player can burn tokens on-chain and then fail Forest authorization/settlement, stranding the burn. The transfer is irreversible; the authorization is not.
The developer backend owns the burn intent — it decides actionId, amount,
burnAddress, reason, and metadata. The client only executes that exact intent
on-chain and relays the resulting txHash back; it does not choose the destination
or purpose.
Your backend defines the intent (actionId, amount, burnAddress, reason,
metadata). The iframe calls
forest.game.action.authorize
with that actionId and a display-amount debit limit — which is also the
player-authorized max burn amount. Do not use 0.
callForest("forest.game.burn", {
amount: "10", // display units
burnAddress, // optional; defaults to 0x…dead
});
// response arrives via the message listener: { txHash, burnAddress }Forest performs the on-chain transfer and returns { txHash, burnAddress }
after receipt confirmation — it does not call any API, and never surfaces a
pending tx hash. Relay txHash and burnAddress to your backend.
Your backend HMAC-signs and submits POST /playables/{projectId}/html/burns with
the same actionId, the base-unit amount, txHash, burnAddress,
reason, metadata, and timestamp. Forest verifies on-chain that exactly
amount Project Tokens moved from a linked wallet to burnAddress, then records
the burn. Game Balance is never touched.
The signed amount is the same economic amount as the client burn — display
units in forest.game.burn (e.g. "10"), base-unit integer string in the
signed body (e.g. "10000000000000000000").
POST /playables/{projectId}/html/burns
Content-Type: application/json
X-Forest-Settlement-Signature: v1=<hex-hmac>await fetch(`${FOREST_API}/playables/${projectId}/html/burns`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Forest-Settlement-Signature": `v1=${hmac}`,
},
body: JSON.stringify({
actionId: "same-action-id",
amount: "10000000000000000000", // base-unit integer string
txHash: "0x…",
burnAddress: "0x000000000000000000000000000000000000dead",
timestamp: Math.floor(Date.now() / 1000),
reason: "activation_fee",
metadata: { item: "gold_miner_rig", level: 3 },
}),
});Request fields:
Prop
Type
Sign the exact request body with your Settlement Signing Secret, identical to the settlement flow. Successful responses return:
{
burnId: "burn-id",
actionId: "same-action-id",
amount: "10",
reason: "activation_fee",
metadata: { item: "gold_miner_rig", level: 3 },
txHash: "0x…",
burnAddress: "0x000000000000000000000000000000000000dead",
replayed: false
}Retrying a burn settlement
Burns are idempotent by actionId. A safe retry MUST replay the same
actionId, the exact same JSON body, and the same signature, and arrive
within the request-freshness window (~5 min). An exact replay returns
replayed: true. After the window, do not retry — confirm the burn settled by
looking up its burnId in forest.game.burns.
Idempotency is keyed on actionId, not txHash: a txHash backs exactly one
burn, and a txHash already recorded as a deposit is a conflict.
Burns do not change Game Balance (the tokens are burned straight from the
wallet) and are not part of forest.game.transactions.
Replay and validation failures return the shared settlement error codes —
SETTLEMENT_TIMESTAMP_STALE, ACTION_ID_REUSED, BURN_TX_HASH_REUSED,
DEBIT_LIMIT_EXCEEDED, INVALID_SETTLEMENT_AMOUNT, and signature errors. See
Errors and Security
for each code's cause and resolution.
forest.game.burns returns the current player's recorded burns (paginated,
newest first); forest.game.burns.summary returns the player's totals grouped by
reason. Both are player-scoped iframe reads (like forest.game.balance).
callForest("forest.game.burns", { page: 1, perPage: 20 });
// response: { data, meta } — data[]: { id, amount, reason, metadata, txHash, burnAddress, createdAt }
callForest("forest.game.burns.summary");
// response: { totalBurned, byReason: [{ reason, amount, count }] }totalBurned here is "verified burn/sink settlements" — not necessarily supply
reduced (only dead/zero destinations reduce supply). Burns sent without a reason
are grouped under reason: null. Amounts are display token amount strings.
Token-level burn history is separate from Game Balance history. The token Burns tab is populated from synced on-chain token transfers to the burn address or zero address, alongside backend buyback burns.