Build a hook, a companion, or both as a pair
Write a custom hook with your own AI coding agent: one that runs on every trade, one that puts the creator fees to work, or both as a pair. Deploy it yourself on the open standard, or submit its source to the Workshop: Bordrless checks it, builds it and deploys it, and you earn half the creator fee of every launch someone else makes with it.
Two routes#
- Your own
- The standard is open: write a custom hook against the
bordrless-hookcrate, deploy it yourself, make it immutable, and put it on a mint of your own, a pool of your own, or a launch on Bordrless from a config you make (prepared for the mint withtemplateHook.prepare, made withbuildCreateConfig; the SDK’s README walks the whole terminal flow). A companion the same way, once the companion upgrade of 2026-10-12 evening is live: deploy the script, make it immutable, and launch with it through the SDK or the API. Nobody approves it. The one rule, the programs’: a hook or a script that runs here is immutable, behind the public hook timelock (3 days or more), or upgradeable only by Bordrless. On bordrless.app such a program is not read by Bordrless: the token shows it as a custom hook written by its creator, with who can upgrade it and no reading, and it is not in the Workshop. A launch whose creator fee goes to a program of your own, not the companion program, is allowed by the chain and hidden on the site. Write a hook and build on Bordrless cover it. - The Workshop
- This walkthrough. You submit the source, never a deploy. Bordrless checks it, approves it and deploys it under its own upgrade key; you pay what the deploy costs. It is listed, launchers pick it on the launch page, the site shows Bordrless’s reading of it (what it does, the most it takes, what to watch) wherever a launcher or a buyer meets it, and you earn half the creator fee of every launch someone else makes with it.
What you can build#
A custom hook on the trades
Today- Runs
- On every transfer of the token. A buy is a transfer out of the pool, a sell one into it.
- Can
- Take a cut, keep state in each holding, refuse a transfer.
- Limits
- It never gets anyone’s signature and calls no other program: the token program applies its answer, never more than the amount.
A custom hook on the creator fee
Today- Runs
- On a timer, every 10 s to 1 day, on the companion program, on chain: it holds the launch’s creator fees and calls your code.
- Can
- Spend the pot the creator fee fills: market making, buybacks, games.
- Limits
- The companion program holds the funds: your code spends the pot and nothing else, leaves the dev’s tokens alone, and never gets anyone’s signature but the creator address’s.
Both, as a pair
Today- Runs
- The hook on every transfer, the companion on the timer. One item in the Workshop, one approval, one config.
- Can
- Rules whose payout depends on what happened at transfer time: a last-buyer game, pay who held through a period, reward who is down, a streak.
- Limits
- The hook records, the companion reads and pays; neither reaches the other’s code. The config requires both, so a launch gets both or neither.
The Workshop takes custom hooks on the trades, on the creator fee, and both as a pair. It takes no game drawn at random: a draw needs an oracle the companion pays for, so that is the built-in Jackpot’s on Bordrless’s own lottery hook.
Two layers, then. A hook on the trades is your program inside the trade: the token program calls it before and after every transfer, mint and burn and applies its answer, so it takes cuts, refuses, and writes what it wants into each holding; it never holds money. A hook on the creator fee is your program too, a script, but it never runs inside a trade: Bordrless’s companion program holds the launch’s creator fee in a pot and calls your script on a timer with the launch’s accounts (the mint, the launch, the companion, the pool, your state, then whatever the sender passes); your script reads whatever it likes, its hook’s data in the holdings, the pool, its own state, and answers by acting as the creator address, and Bordrless’s program executes that inside hard bounds: a trade on the launch pool, a buyback, a burn, never more than the pot, never the dev’s tokens, never anyone’s lamports. Payouts to holders are the next action it will execute. Bordrless’s program is never upgraded for a builder’s program, only for a new kind of action.
A pair: a hook and its companion as one#
Some rules run inside the trade, some on the creator fee, and some need both: the payout depends on what happened at transfer time, and only a hook is there when it happens. A pair is built as two programs and shipped as one thing. They talk one way, hook to companion:
- The hook writes
- Per holder, the 64 bytes of hook data in each holding, answered from
before_transferand written by the token program with the balances. Per mint, its own state at["state", mint]. The aggregate goes in the state (a total, a last buyer, the period), the per-holder facts in the hook data (what they held when the period began, when they last sent). - The companion reads
- In its step it asks for the hook’s state and the holdings it pays, verifies each against its hook data, pays from the pot in bridged SOL, and records what it paid in its own state so nothing is paid twice. It can never write hook data; the hook can never pay.
- No randomness
- A step has no payer for an oracle, and a slot or a clock a trader can time is not random. A pair pays by what the hook recorded: the last buyer, the longest holder, who is down. A drawn-at-random game is the built-in Jackpot.
- One address
- The config made from a pair names the hook and requires the companion. A launch with it gets both or neither, and the launch page locks the hook picker to it.
The template and its rules#
A hook is one Anchor program on Bordrless’s template. You write its Rust files under src/; the Cargo.toml and the crates are the builder’s, so a hook adds no crate of its own. To build and test it on your machine, use the builder’s Cargo.toml with its two crates from the public programs repository:
# Bordrless's hook template, for a build on your machine: the builder's Cargo.toml, with
# its two crates from the public programs repository. The builder uses its own copy and lock.
[workspace]
members = ["."]
[package]
name = "my-hook"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
crate-type = ["cdylib", "lib"]
name = "my_hook"
[features]
default = []
cpi = ["no-entrypoint"]
no-entrypoint = []
no-log-ix-name = []
idl-build = ["anchor-lang/idl-build", "bordrless-hook/idl-build", "bordrless-game/idl-build"]
anchor-debug = []
custom-heap = []
custom-panic = []
[dependencies]
anchor-lang = "=1.2.0"
bordrless-hook = { git = "https://github.com/BordrlessDex/bordrless-programs" }
bordrless-game = { git = "https://github.com/BordrlessDex/bordrless-programs" }
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(target_os, values("solana"))'] }
[profile.release]
overflow-checks = true
lto = "fat"
codegen-units = 1
[profile.release.build-override]
opt-level = 3
incremental = false
codegen-units = 1The rules, checked on your files before anything else:
- Its files
- Rust files under
src/with lower-case names: at most 8 files and 120 kB in all.src/lib.rsis the program. - No calls
- It calls no other program. It acts only through its answers to the token program.
- No SOL
- It moves no SOL, and closes or resizes no account.
- Plain Rust
- No unsafe code, no inline assembly, no macros of its own, and nothing read from the build machine (
include_*!,env!,option_env!,#[path]). - Its id
- One
declare_id!and one#[program]module. The builder writes the program id Bordrless reserves for it. prepare- The standard one: no arguments; accounts payer, mint, state at
["state", mint], registry, system program. The launch sends it for each new mint. - Its registry
- The extra accounts every callback gets: only the hook’s own and the mint’s, at most 4, written once by prepare.
A custom hook on the creator fee builds on a template of its own, since it trades on the DEX and so can’t keep the rule against calling other programs. Its brief holds that template’s Cargo.toml.
1. Give your agent the brief#
Open an empty folder in Claude Code, Cursor or any coding agent and paste the brief for what you are building. It holds the template, every rule the checks hold it to and how to test it, and it tells your agent never to deploy anything: on this route, Bordrless deploys.
You are helping me write a token hook for Bordrless and submit its source to the Bordrless
Workshop. I write and test the code on my machine; Bordrless checks it, builds it and deploys it.
Never deploy anything to mainnet. Read every rule below before writing code.
BORDRLESS: a token standard of its own on Solana, not SPL and not Token-2022, with its own DEX and
launchpad. Its token program calls a mint's hook before and after every transfer, mint and burn,
and applies what the hook answers. Trades are transfers: a buy is a transfer out of the pool's
vault, a sell is one into it. So a hook can take a cut of trades, keep state in each holding and
refuse a transfer.
PROGRAM IDS (the same on mainnet, devnet and localnet):
token 2XoEWp8cF3kRXg74eVwPAyTFhVCAztn3V88komxAvr22
DEX GyzKSnnEu2uN5bBRecE4XYY2enbfR2D2MtxnbJPGy7hk
launchpad 1jcBymHxBjniZDhNPy51Vgm5Nz7pLUdxa9UBHc4TavC
bridged SOL, every launch's quote: A49oVhX22ExMwTEtFC6Y8nhBdZ4LJDGhdXLDn4c2f59i
THE TEMPLATE: a Workshop hook is one Anchor 1.2 program on Bordrless's template. I send only
the Rust files under src/ (src/lib.rs is the program; lower-case names;
at most 8 files and 120 kB in all); the Cargo.toml, its lock and the crates are the
builder's, so add no crate. On my machine, build with this Cargo.toml (the builder's, with its
two crates from the public programs repo):
# Bordrless's hook template, for a build on your machine: the builder's Cargo.toml, with
# its two crates from the public programs repository. The builder uses its own copy and lock.
[workspace]
members = ["."]
[package]
name = "my-hook"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
crate-type = ["cdylib", "lib"]
name = "my_hook"
[features]
default = []
cpi = ["no-entrypoint"]
no-entrypoint = []
no-log-ix-name = []
idl-build = ["anchor-lang/idl-build", "bordrless-hook/idl-build", "bordrless-game/idl-build"]
anchor-debug = []
custom-heap = []
custom-panic = []
[dependencies]
anchor-lang = "=1.2.0"
bordrless-hook = { git = "https://github.com/BordrlessDex/bordrless-programs" }
bordrless-game = { git = "https://github.com/BordrlessDex/bordrless-programs" }
[lints.rust]
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(target_os, values("solana"))'] }
[profile.release]
overflow-checks = true
lto = "fat"
codegen-units = 1
[profile.release.build-override]
opt-level = 3
incremental = false
codegen-units = 1
Read crates/bordrless-hook/src/lib.rs in https://github.com/BordrlessDex/bordrless-programs before
guessing an API (not its authority module: the builder's copy of the crate has none). There,
programs/tax_hook shows cuts. The Workshop takes no game hooks (a jackpot or a streak): their
pots need the companion's own game checks. For TypeScript tests:
npm install @bordrless/sdk @bordrless/shared
(the SDK's source: https://github.com/BordrlessDex/bordrless-sdk).
CALLBACKS: before_transfer, after_transfer, before_mint, after_mint, before_burn, after_burn. Each
is an instruction named after it taking TokenHookArgs (amount, source_owner, destination_owner,
the balances, the supply, both holdings' hook data). Accounts: five fixed ones (hook signer, mint,
source, destination, authority), then the extras the registry lists. Every callback first runs
one check_call(&ctx, &args): the hook signer signs and is PDA(["hook-authority", hook], token
program) (hook_signer in the crate), and args.mint is the mint the hook's state was prepared for.
A before_* callback returns, in its return data:
pub struct HookReturn {
pub deltas: Vec<Delta>, // up to 3 cuts { amount: u64, account: u8 }
pub burn: u64, // pool hooks only: 0
pub lp_fee_bps: Option<u16>, // pool hooks only: None
pub source_hook_data: Option<[u8; 64]>, // the holding's 64 bytes of state
pub destination_hook_data: Option<[u8; 64]>,
}
Each cut goes to a writable holding of the mint the registry lists (account = its index in the
callback's accounts); together they never exceed the amount. An error refuses the transfer. A hook
never gets a user's signature and never moves tokens itself: the token program applies its answer.
BUYS AND SELLS: the launch account PDA(["launch", mint], launchpad) names its pool (Launch.pool):
a buy is a transfer whose source_owner is that pool, a sell one whose destination_owner is.
FLAGS (TOKEN_HOOK_FLAGS, a u16 the launch config carries):
BEFORE_TRANSFER=1 AFTER_TRANSFER=2 BEFORE_MINT=4 AFTER_MINT=8 BEFORE_BURN=16 AFTER_BURN=32
TRANSFER_RETURNS_DELTA=64: before_transfer may answer cuts
WRITES_HOOK_DATA=128: before_* callbacks may answer the 64 bytes
Bordrless reads them from the source, never by hand: a callback is on when its body does more than
check_call(&ctx, &args)?; Ok(HookReturn::default()) (an after_* one: check_call(&ctx, &args)); cuts
when before_transfer builds a Delta; hook data when the code sets source_hook_data or
destination_hook_data. An answer its flags don't allow is refused whole (UnsupportedHookReturn).
A cut on every transfer is BEFORE_TRANSFER | TRANSFER_RETURNS_DELTA = 65.
THE RULES (checked on the source before anything else; one broken stops it):
- Calls no other program: no invoke or invoke_signed by any name, no CpiContext, no cpi module.
Anchor's init is fine. A hook acts only through its answers to the token program.
- Moves no SOL and closes or resizes no account (read a balance with .lamports() only).
- No unsafe, no asm!, no macro_rules!, macro or cfg_attr, no extern, no_mangle or link; nothing
read from the build machine (include_*!, env!, option_env!, #[path]); no std::fs, net, process,
os, env or thread.
- Exactly one declare_id!: the builder writes the id Bordrless reserves. On my machine, use the
address of target/deploy/my_hook-keypair.json (solana address -k <file>). One #[program] module.
- The standard prepare: an Anchor instruction named prepare, no arguments, accounts
payer (signer, writable)
mint (read-only; it may not exist yet)
state = PDA(["state", mint], hook) (writable; seeds = [b"state", mint.key().as_ref()])
registry = PDA(["bordrless-hook-accounts", mint], hook) (writable)
system program
It creates the hook's state for the mint and writes its registry (write_registry in the
crate). The launch sends it for each new mint itself; anyone may send it, once per mint.
- The registry, written by prepare and never again, lists at most 4 accounts: the hook's own
PDAs, the mint's holdings (token program) and its launch (launchpad); as fixed keys only the
system program, a sysvar or the token program. None may depend on who sends or receives (no
SourceOwner or DestinationOwner seed): a launch passes one slice of accounts for every transfer.
THE SECURITY REVIEW, by AI: two independent runs, the worse kept. A critical or high finding stops
it, and that source stays stopped until it changes. It reads the code with comments and strings
blanked out, and calls these deception (high):
- any way for one party to change how the callbacks behave after launch: an instruction besides
prepare and the callbacks that writes state, behaviour keyed to a wallet or an account someone
controls, reading the instructions sysvar;
- a hard-coded address the hook's plain purpose doesn't explain;
- any comment or string addressed to a reviewer, an AI or an auditor.
It also checks the arithmetic (checked math; never panic on what a trader controls), callbacks
anyone could make fail for everyone, and accounts sized far beyond what they store.
THE TRADING TEST, on Bordrless's builder: test wallets buy, sell and send over a simulated year.
It fails a hook that refuses an ordinary buy, sell or send, takes more than 15% of a transfer,
or whose flags and code disagree.
TEST before submitting, on my machine. Build with cargo build-sbf --tools-version v1.57 --arch v3
(the Solana CLI, Agave 4.3). Then test prepare, a config naming the hook, a launch, buys, sells,
sends, every cut and every refusal, in LiteSVM or solana-test-validator, against the live
programs copied from mainnet (solana program dump -u mainnet-beta <PROGRAM_ID> <file>.so) and the
accounts they read, cloned from mainnet. Load the hook upgradeable only by Bordrless's key, as on
mainnet: CS1NRyXNCPxEUP4CRoa26cHQSeSJCxXh5SPijwFhDW6W. The launch program refuses a config whose
hook anyone else can upgrade (HookUpgradeable).
DO NOT deploy it to mainnet: Bordrless deploys it.
SUBMIT: https://bordrless.app/workshop/submit
I sign in with the wallet that will earn from it (it signs a message; nothing moves), choose
Hook, add the files under src/, a name and a few words. The checks above run on their own, with a
reading by AI of what it does and the most it takes; then Bordrless approves it or says why not.
A failed check says why: I fix the files and send the fix from its Workshop page, and the checks
run again. A wallet may submit 6 times a day.
Once it is approved, I pay the quoted deploy cost (the program's rent, network fees and
Bordrless's fee) and Bordrless deploys it with its own upgrade key. I never hold that key, so
nobody can change a launched token's hook behind its holders' backs; a new version is a new
submission. Then I make its launch config from its Workshop page, with a creator fee of
0.5%, 1% or 2%. Launchers pick it under Custom config on the launch page, and I earn 50% of the
creator fee of every launch someone else makes with it, fixed on chain.
KNOW: a launch with a custom hook has no kit rules (holder rewards, max wallet, the locks): it is
the mint's only hook, so build what it needs into it. Bordrless takes 25% of what a launch's
rules collect on each swap, the hook's cuts included. The checks are a review by AI and a test,
not an audit: never call the hook audited, safe or guaranteed.
READ:
https://bordrless.app/sdk/build this walkthrough
https://bordrless.app/sdk/hooks the hook protocol, in full
https://bordrless.app/docs/protocol2. Test it on your machine#
Your agent writes the code and its tests. Run them against the live programs copied from mainnet before you submit. Don’t deploy it: Bordrless does, once it is approved.
# Build it, with the Solana CLI (Agave 4.3) and the Cargo.toml above
cargo build-sbf --tools-version v1.57 --arch v3
# Its id on your machine, for declare_id! (Bordrless's builder writes the real one)
solana address -k target/deploy/my_hook-keypair.json
# The live programs your tests load, copied from mainnet
solana program dump -u mainnet-beta 2XoEWp8cF3kRXg74eVwPAyTFhVCAztn3V88komxAvr22 token.so
solana program dump -u mainnet-beta GyzKSnnEu2uN5bBRecE4XYY2enbfR2D2MtxnbJPGy7hk dex.so
solana program dump -u mainnet-beta 1jcBymHxBjniZDhNPy51Vgm5Nz7pLUdxa9UBHc4TavC launchpad.so
# A local validator with them, and your hook upgradeable only by Bordrless's key, as it
# will be on mainnet (add -u mainnet-beta --clone <address> for each account they read)
solana-test-validator --reset \
--bpf-program 2XoEWp8cF3kRXg74eVwPAyTFhVCAztn3V88komxAvr22 token.so \
--bpf-program GyzKSnnEu2uN5bBRecE4XYY2enbfR2D2MtxnbJPGy7hk dex.so \
--bpf-program 1jcBymHxBjniZDhNPy51Vgm5Nz7pLUdxa9UBHc4TavC launchpad.so \
--upgradeable-program <YOUR_PROGRAM_ID> target/deploy/my_hook.so \
CS1NRyXNCPxEUP4CRoa26cHQSeSJCxXh5SPijwFhDW6W
# Then your tests, there or in LiteSVM: prepare, a config naming your hook, a launch,
# buys, sells and sends, every cut and every refusal
3. Submit it to the Workshop#
On the Workshop, sign in with your wallet (it signs a message), say what it runs on (the trades, the creator fee, or both), add your files under src/ (a pair: the hook’s and the companion’s), and give it a name and a few words. Then you pay the submission fee on its page: a fixed amount of SOL to Bordrless’s treasury, shown before you submit. It pays for the review, the build and the trading test, and is not refunded. Until Bordrless approves it, only you see it, signed in. A custom hook on the trades then goes through these, on Bordrless’s machines:
- The template’s rules. A scan of the code for everything the rules above forbid.
- A security review by AI. Two independent runs look for anything malicious or broken, and the worse is kept. A serious finding stops it.
- The build. On Bordrless’s builder, in a sandbox with no network: your files in the template.
- The trading test. Test wallets buy, sell and send over a simulated year. A hook that refuses an ordinary trade, or takes more than 15% of one, fails.
- A reading by AI. What it does, the most it takes and what to watch for, in plain words on its page.
- Approval. A clean pass is approved on the spot. One with warnings is read by Bordrless, which approves it or says why not.
Its page in the Workshop shows each check as it runs. One that fails says why: fix your files and send the fix from that page, and the checks run again. A wallet may submit 6 times a day. One on the creator fee goes through the same checks, without the trading test. A pair goes through both sets, with one reading, and is approved as one, bound to both builds.
4. Approval and deploy#
Once it is approved, you pay what deploying it costs. Its page quotes the amount, part by part, before your wallet signs:
- Rent
- What Solana keeps in the program’s account for as long as it exists, with room for a fifth more code. Most of the cost.
- Network fees
- The transactions that write the program and deploy it.
- Bordrless’s fee
- For the deploy. It goes to Bordrless’s revenue wallet.
A hook of about 150 kB comes to about 1 SOL, nearly all of it rent. A pair is two programs: one quote with both parts, one payment, both deployed together.
Then Bordrless deploys it as a program of its own, with Bordrless’s upgrade key. You never hold that key: a hook runs on every trade of every token launched with it, so whoever held it could change those tokens’ rules after people bought. This way nobody can change a launched token’s hook behind its holders’ backs. A new version of your hook is a new submission.
5. Its launch config and your earnings#
Once it is deployed, make its launch config from its page in the Workshop: a config on your hook with no kit rules, and a creator fee you choose. It is listed in the Workshop, and launchers pick it under Custom config on the launch page. A pair’s config also requires its companion: a launch with it gets both or neither.
- Creator fee
- 0.5%, 1% or 2% of every trade. You choose it once, and the config keeps it.
- You earn
- 50% of that fee on every launch someone else makes with your config, fixed on chain. You claim it as SOL from your portfolio.
- On the creator fee
- The same 50%, paid by the companion program. Launchers pick it among the launch page’s hooks; a pair’s comes with its config.
What Bordrless checks#
- A review by AI and a test, not an audit. They catch a lot, not everything. Read a hook’s page and its code before you launch with it.
- What holds whatever the code says. The token program never lets a hook take more than the amount or move anyone’s tokens itself, and only Bordrless’s key can upgrade a Workshop hook.
- A custom hook on the creator fee keeps the companion program’s limits whatever its code does: the companion program checks every step and refuses one that spends more than the pot, touches the dev’s tokens or takes lamports.
- What the site shows of your program. A Workshop hook or companion comes with Bordrless’s reading of it, in Bordrless’s words, on the launch page and the token page: what it does, the most it takes, what to watch. One you deployed yourself shows as a custom hook written by its creator, with who can upgrade it, and no reading: not read by Bordrless.
More on each: write a hook, hooks on the creator fee, with the SDK and how a companion works.