Stage 2: Creator Royaltieshook read verified
- 0 · Roadmap
- 1 · Receipts & warranty
- 2 · Creator royalties
- 3 · B2B invoices
- 4 · Payments bridge
- 5 · Simulator
- 6 · Devnet: repeat it
- ✓ · Trust chain
- ★ · Benefits & use

For years, NFT royalties depended on whether a marketplace felt like paying them. WNS moved that rule into the token: a transfer hook runs on every transfer and enforces the creator's share. This model shows how to see which program guards an NFT, and how to move the NFT so that guard actually runs.
The task
Make sure a creator receives their share on every resale, whether the NFT is sold on a big marketplace, a small one, or passed directly between wallets.
The model
- An NFT mint carries the Token-2022 Transfer Hook extension, which names a hook program. For WNS assets that program is WNS royalty distribution:
diste3nXmK7ddDTs1zb6uday6j4etCa9RChD8fJ1xay. - On every transfer, Token-2022 calls the hook. If the hook's conditions are not met, the whole transaction is rolled back.
- The hook needs extra accounts. The official helper
createTransferCheckedWithTransferHookInstructionreads them from the chain, so wallets and marketplaces do not have to guess.
| Before (classic NFTs) | With a WNS transfer hook | |
|---|---|---|
| Who enforces royalties | Each marketplace, if it wants to | The token itself, on every transfer |
| Wallet-to-wallet sale | Royalty easily skipped | Hook still runs |
| Integration | Custom per marketplace | One standard flow for everyone |
The code
File: lab/code/src/02-royalty-transfer.ts · Apache-2.0
// Model 2: creator royalties. Inspect which program enforces royalties on an NFT,
// then transfer it so that the transfer hook (e.g. WNS royalty distribution) runs.
// License: Apache-2.0. Example code, not audited. Devnet first.
import { PublicKey, Transaction, sendAndConfirmTransaction } from '@solana/web3.js';
import {
TOKEN_2022_PROGRAM_ID, createAssociatedTokenAccountIdempotentInstruction,
createTransferCheckedWithTransferHookInstruction, getAssociatedTokenAddressSync, getMint, getTransferHook,
} from '@solana/spl-token';
import { WNS_ROYALTY_PROGRAM_ID, detectTokenProgram, getConnection, loadKeypair } from './common.js';
/** Read the transfer-hook program attached to a mint. */
export async function inspectRoyaltyHook(mintAddress: string) {
const connection = getConnection();
const mint = new PublicKey(mintAddress);
const program = await detectTokenProgram(connection, mint);
if (!program.equals(TOKEN_2022_PROGRAM_ID)) return { mint: mintAddress, hook: null, note: 'Classic SPL token: no transfer hooks' };
const info = await getMint(connection, mint, 'confirmed', TOKEN_2022_PROGRAM_ID);
const hook = getTransferHook(info);
const hookProgram = hook?.programId ?? null;
return {
mint: mintAddress,
hook: hookProgram?.toBase58() ?? null,
isWnsRoyalty: hookProgram ? hookProgram.equals(WNS_ROYALTY_PROGRAM_ID) : false,
};
}
/** Transfer one NFT. The helper resolves the hook's extra accounts on-chain, so royalties logic runs. */
export async function transferWithRoyalties(mintAddress: string, to: string) {
const connection = getConnection();
const owner = loadKeypair();
const mint = new PublicKey(mintAddress);
const recipient = new PublicKey(to);
const source = getAssociatedTokenAddressSync(mint, owner.publicKey, false, TOKEN_2022_PROGRAM_ID);
const destination = getAssociatedTokenAddressSync(mint, recipient, false, TOKEN_2022_PROGRAM_ID);
const transferIx = await createTransferCheckedWithTransferHookInstruction(
connection, source, mint, destination, owner.publicKey, 1n, 0, [], 'confirmed', TOKEN_2022_PROGRAM_ID,
);
const tx = new Transaction().add(
createAssociatedTokenAccountIdempotentInstruction(owner.publicKey, destination, recipient, mint, TOKEN_2022_PROGRAM_ID),
transferIx,
);
// If the hook rejects the transfer (for example, royalties not approved), the whole transaction rolls back.
return sendAndConfirmTransaction(connection, tx, [owner]);
}
// Example: npx tsx src/02-royalty-transfer.ts <MINT_ADDRESS>
if (process.argv[2]) inspectRoyaltyHook(process.argv[2]).then(r => console.log(r)).catch(e => { console.error(e); process.exit(1); });
What was verified
In LiteSVM: an NFT mint is created with a transfer hook pointing to the WNS royalty program, and the inspection logic reads that program back from the mint correctly. The transfer function is type-checked against the official @solana/spl-token API; running it end to end needs the real WNS programs, which is the job of the devnet stage on the roadmap.
Where it is useful
- Digital art, music and video NFTs.
- Game items whose designers earn from the secondary market.
- Licensed brand collectibles, where the license fee follows every sale.
Limits and risks
- The hook sees the number of tokens moved, not the price. A sale settled in a separate transaction ("a gift for 0 SOL") can dodge a percentage royalty. WNS handles this with approval logic; read the program before relying on it.
- Every hook call costs compute units. Aggregators may need to request more.
- The WNS audit is documented as not formally completed. Treat it accordingly.
Wen in pictures





Where Wen fits in this model
This is Wen's own invention at work: wen_royalty_distribution is one of the two programs of the Wen Program Library. A creator community can choose to quote prices or pay tips in WEN, while the royalty rule itself stays in the token. Read the Wen Program Library review for the full picture.
The Wen thread: one contract, everywhere
Every model in the Lab is free and open, and every one carries the same signature: the $WEN token contract. Copy it, repeat it in your own docs, and always verify it against an explorer before use.
WENWENvqqNya429ubCdR81ZmD69brwQaaBYY6p3LCpk
Open use policy
- Free and open. All Lab code is released under the Apache License 2.0, the same license as the Wen Program Library. Use it, change it, ship it, commercially or not; keep the license notice.
- Open tools only. Built on public, documented tools: Solana SPL Token-2022 (Token Extensions), the token layout of the Wen New Standard (WNS), Solana Pay, and LiteSVM for local testing.
- The rule: always verify your own model. Run the tests, start on devnet, read the official documentation, review every account and amount, and get an independent audit before any mainnet use. A model that is not checked is not a model, it is a guess.
- Not audited, not advice. These are educational examples. They are not financial, legal or tax advice, and not a licensed payment or banking product. Payments, stablecoins and acquiring are regulated activities in most countries.
- Security. Never paste a private key into a website. Do not install the npm package
@wen-community/wen-new-standard: npm removed it as malicious. Build from official sources.
Spoken by Wen, for the community. More in our editorial and data policy.
Wen Open Lab · roadmap · all code · Wen Program Library on GitHub