Soulbound Memberships, Transferable Vouchers: Splitting a Token in Two

Why FairWins made membership non-transferable, then built a gift-and-resale market on top of it anyway

Responsible use. FairWins wagers are based on publicly available information and legitimate forecasting. Memberships gate access to that activity; they are not a mechanism for circumventing any law. All participants remain fully subject to applicable laws and compliance requirements, and every membership — however acquired — passes sanctions screening.

The gift you can’t give

A FairWins membership is deliberately boring. You pay in USDC — $2 for Bronze, $8 for Silver, $25 for Gold, $100 for Platinum — and for the next 30 days your wallet can create and accept wagers, up to a limit set by your tier. The membership is soulbound, a term for something permanently bound to a single wallet: it lives at your address, it can’t be transferred, and there’s no market for it. That’s a feature, not a limitation. An access record that can move is an access record that can be stolen, rented out to a sanctioned party, or briefly borrowed to sneak past a compliance check.

Then a product request landed: let me buy a membership for a friend. And its sibling: let me resell the one I bought and don’t want. Both are completely reasonable — gift cards and resale markets are table stakes for any paid product — and both are, on their face, impossible. A record welded to your address has nothing to hand over. And making it movable would destroy the very properties the platform relies on: sanctions screening happens when membership is granted, usage limits are tracked per wallet, and the wager engine trusts that the wallet holding a membership is the one that was screened.

The tempting-but-wrong fix is to bolt a “transfer, and re-screen the new owner” button onto the membership itself — turning a fixed access record into a moving target every other part of the system has to special-case. The fix FairWins shipped is cleaner: don’t make the membership transferable. Make the right to claim one transferable, and keep the two ideas in completely separate contracts.

Two rails, one membership

Start with what “soulbound” actually means here. A FairWins membership isn’t an NFT with transfers switched off — it isn’t a token at all. It’s just a record in the platform’s membership ledger, filed under your wallet address: which tier you have, when it expires, and how much of your usage limit you’ve used this month. There’s no “transfer” button to disable because there’s nothing to hand over — non-transferability simply falls out of how the data is shaped.

It’s worth contrasting this with the popular “soulbound token” — a standard for NFTs permanently locked to a wallet but still visible in it. FairWins didn’t need the token part at all: memberships are read by contracts, not shown off in wallets, so a plain ledger entry is simpler, cheaper, and has no transfer machinery to audit.

Buying a membership directly writes that record: it screens the buyer against sanctions lists, pulls the tier’s price in USDC, marks the tier and its expiry, and resets the usage counters. Thirty days later, it lapses.

The new idea adds a second way in that lands on the exact same record. A membership voucher is a real, freely transferable NFT — think of it as a prepaid gift card for a membership — minted for the tier’s normal price. The whole trick is in what a voucher doesn’t do:

  • It grants no membership while you hold it. No clock is running, no usage limits accrue, your wallet has no access.
  • It never expires. A voucher is a bearer claim you can sit on for a year and still redeem into a fresh, full 30-day membership.
  • It locks in its tier and duration at the moment it’s minted. If the team later reprices or retires that tier, the voucher still delivers exactly what it was bought for.

Because the voucher is inert — it confers nothing until redeemed — it is completely safe to trade. Gifting it is an ordinary transfer. Reselling it works on any standard NFT marketplace. The contract even suggests a small resale royalty back to the treasury (2.5% by default, capped in the code at 5% so it can never be cranked higher). None of that touches compliance, because none of it grants anyone access.

Redemption: where the rails converge

A voucher becomes a membership at one moment: redemption. This is the single control point where everything a direct buyer faces gets applied to the person redeeming.

In plain terms, the redemption does this, in order:

  1. Confirm the person redeeming actually owns the voucher.
  2. Confirm they don’t already have an active membership for that role.
  3. Screen them against the sanctions lists — and if they’re listed, stop everything right here.
  4. Write the membership: grant the exact tier and duration the voucher locked in, reset the usage counters, record which terms they agreed to.
  5. Only then, as the very last step, burn the voucher.

The ordering carries the product’s failure semantics. If the redeemer is sanctioned, or already holds an active membership, the entire call is undone and the voucher is left completely untouched — still owned, still tradable. A legitimate buyer is never punished because some previous holder couldn’t redeem, and because the voucher is destroyed only at the very end, any earlier failure rolls the whole thing back safely.

Notice who gets screened: only the person redeeming, and only at the moment of redemption. Minters and resale buyers are deliberately not screened. That’s a conscious trade-off: a sanctioned party could profit by reselling a voucher they never redeem. What they can never do is turn one into actual platform access, because the screen sits exactly where access is granted. Compliance lives at the point of use, not the point of trade.

After redemption, the two routes are indistinguishable. The wager engine reads the same membership record either way and has no idea how it was obtained — and that’s a hard requirement, verified by running the full test suite against both routes.

The economics are deliberately flat

A voucher costs exactly the tier’s normal price — the same amount the direct route charges — so neither path is cheaper and there’s no buy-here-redeem-there arbitrage to game. The money goes to the treasury the moment the voucher is minted, which works because granting the membership later costs the platform essentially nothing, so there’s no need to hold reserves against outstanding vouchers. There are no primary refunds: buyer’s remorse is resolved by reselling or redeeming. And the voucher’s own artwork and description — generated entirely on-chain — literally reads “utility access token, not an investment.”

Buying a batch of vouchers as gifts and sending them straight to a recipient is handled by a small, separate helper, since the voucher itself only mints one at a time. The helper pulls the exact total, mints the batch, and forwards every one to the recipient in a single transaction. It holds no funds at rest and has no admin or withdrawal path. If it isn’t deployed on a given network, buying one at a time still works.

Privacy: pseudonymity, stated plainly

The voucher route has a quiet second use. Because redemption only checks that you own the voucher — never that you minted it — you can move a voucher to a fresh wallet and redeem it there. The resulting membership keeps no back-reference to who bought it or how it changed hands, so your wagering activity isn’t chained on the public ledger to the wallet that originally paid. A gasless version goes further: since redeeming moves no money, a helper service can submit the transaction for you, so even the wallet paying the network fee needn’t be your trading wallet.

FairWins is careful not to oversell this. Voucher mints, transfers, and burns are all public events — anyone can watch a voucher move. What redeeming from a fresh wallet buys you is pseudonymity, not cryptographic unlinkability, and the interface is required to say exactly that.

Design decisions

Changeable logic, frozen asset. The membership ledger can be upgraded in place — the voucher feature itself arrived as one such upgrade — because screening, terms, and grant logic must be able to evolve. The voucher is the opposite: deliberately not upgradeable, because the rules of a tradable, paid-for bearer asset must not change after someone buys it. The thing people pay for stays fixed; the machinery around it can improve.

The membership is a ledger entry, not a locked NFT. No transfer function to disable, no locked-token standard to implement. The only cost is that it’s invisible in your wallet — which doesn’t matter for something only contracts ever read.

Royalty as a hint, not a cage. Forcing royalties would mean whitelisting marketplaces or running our own, killing open trading and the trade privacy that comes with it. A flat, capped suggestion keeps the utility framing honest, accepts that some marketplaces will ignore it, and lets the platform earn reliably on the first sale.

The general pattern travels well. When you need a token to be both non-transferable (for compliance and integrity) and transferable (for gifting and resale), you don’t need one token that does both badly. You need two artifacts — an inert, tradable claim and a soulbound grant — joined by a single, guarded redemption that burns one and writes the other.

Further reading

  • ERC-721, the non-fungible token standard the voucher is built on: https://eips.ethereum.org/EIPS/eip-721
  • EIP-2981, the NFT royalty standard: https://eips.ethereum.org/EIPS/eip-2981
  • EIP-5192, the minimal soulbound (locked) NFT standard discussed above: https://eips.ethereum.org/EIPS/eip-5192
  • OpenZeppelin Contracts, the audited building blocks behind the token and access logic: https://docs.openzeppelin.com/contracts

One Action, One Role: RBAC and the Operations Control Plane

How FairWins maps every privileged action to exactly one permission — and makes the operator dashboard prove it

The compliance officer who couldn’t reach her own tool

Picture a compliance officer at a wagering platform. Her job is narrow and serious: when an address needs to be blocked from the protocol, she adds it to a block-list, and the reason is recorded permanently on the blockchain. The system was built for exactly this. She holds one specific permission — the one that lets her edit the block-list — and nothing more. She cannot pause the platform, cannot touch the treasury, cannot freeze anyone’s account.

Then she opens the admin panel, and the block-list tab isn’t there.

This was a real gap FairWins found while auditing its own controls. The permission existed on the blockchain, and her account held it. But the admin dashboard had never been taught that this permission existed, so it hid the block-list behind the top-level “full administrator” permission instead. The underlying security was correct — and completely useless in practice. To do her narrow job, she would have needed to be handed the keys to everything, which is precisely the over-reach the narrow permission was designed to prevent.

The lesson generalizes. Access control isn’t only a smart-contract problem. A permission that exists on-chain but not in the operator’s screen creates quiet pressure to over-grant, and over-granting is how “least privilege” — the principle that every account should hold the minimum power it needs — dies in real life. This post walks through both halves of FairWins’ answer: the on-chain discipline of “one action, one role,” and the admin console built to mirror it, screen by screen.

The permission inventory: one paid role, six operator roles

FairWins is a peer-to-peer wager platform. Smart contracts hold each side’s stake in escrow and settle the bet against a trusted outside source of truth. Its permission model is deliberately small: one permission that members buy, and six that operators hold. All of them use a plain, well-audited access-control library from OpenZeppelin, an industry-standard toolkit for smart contracts — nothing exotic, nothing homegrown.

The paid one lets a member create and accept wagers; members buy it as a time-limited membership tier, and it’s the subject of part 2 in this series. The other six are the operator permissions, and each maps to a single, clearly bounded job:

  • Full administrator — protocol wiring, tier pricing, treasury withdrawals, and handing out or revoking the other permissions.
  • Guardian — can pause and un-pause the whole platform in an emergency. Nothing else.
  • Account moderator — can freeze and unfreeze an individual account. Cannot pause the platform.
  • Membership manager — can grant or revoke memberships directly. Cannot touch admin permissions.
  • Sanctions admin — can edit the compliance block-list. This is the compliance officer’s role.
  • Upgrader — can ship new versions of the upgradeable contracts. Cannot grant itself anything.

A handful of narrower, single-purpose permissions exist for individual subsystems — issuing tokens, setting fees, curating the naming registry — but they follow the exact same pattern, each scoped to one job.

One action, one role

The discipline that holds the whole model together is simple to state: every privileged action is guarded by exactly one permission. Not “admin or guardian.” Not a points system where enough small permissions add up to a big one. One gate per door.

Pausing the platform requires the guardian permission, full stop. Freezing an account requires the account-moderator permission, full stop. Changing pricing or protocol wiring requires the full-administrator permission. There’s one instructive exception that actually proves the rule: swapping out a piece of the wager engine’s code is treated as an upgrade — because it changes what code runs — so it requires the upgrader permission rather than the administrator one. The gate matches the true weight of the action.

The upgrader permission is kept separate from the top administrator permission on purpose. It can later be reassigned to a time-locked multi-signature wallet without changing any code, and the ability to perform future upgrades is wired in permanently, so an upgrade can never accidentally strip away the platform’s own ability to be upgraded again.

The negative space matters as much

FairWins keeps a table of what each role explicitly cannot do, and that table earns its keep. A guardian can stop the whole platform but cannot seize an account. A moderator can freeze an account but cannot pause the platform or move money. A membership manager can hand out memberships but cannot revoke anyone’s admin powers. The compliance officer can block an address and do nothing else. Even the full administrator has hard limits baked into the code: it cannot create wagers on someone’s behalf, cannot decide who wins, and cannot move staked funds — there is simply no button for any of those, because no such function was ever written.

When you design a permission, write its “cannot” list first. It tells you whether the role is genuinely narrow or just labeled that way.

The control plane: permissions flow from the contract to the screen

The operator side is a single admin console, reorganized after that audit into clear groups: a control room, incident response, compliance, membership and revenue, protocol config, identity, access control, and infrastructure.

The core rule of the interface mirrors the core rule of the contracts: each screen is shown only to operators who hold the on-chain permission its actions require, and a group of screens appears only if the operator can actually use at least one screen inside it. The dashboard calculates permissions the exact same way the contracts do and checks them against the live blockchain, so what an operator sees is a faithful picture of what they can actually do.

A guardian signing in sees the control room, incident response, and infrastructure — nothing else. The compliance officer from the opening now sees the control room and compliance. Crucially, the dashboard isn’t enforcing security — the contracts do that, and they can’t be fooled by a hidden or shown button. The dashboard’s job is to make the permission set legible, so nobody is ever tempted to over-grant a big role just to make a screen appear.

One subtlety the console gets right: different permissions live on different contracts, so when an operator grants a permission, the request has to be routed to the specific contract that defines it — not blanket-sent to one place and hoped for the best.

Design decisions

Why a plain, boring access-control library? Standard, audited permission checks are understood by every reviewer and every tool in the ecosystem. The “one action, one role” discipline delivers most of what elaborate custom permission systems promise, without introducing a new thing that can break or be attacked. The cost is granularity: withdrawing fees currently requires the full administrator permission, and splitting out a dedicated “treasurer” would take a contract upgrade — a noted future option, not a quick setting.

Why gate the UI on permissions at all, if the contracts already enforce them? Because the failure mode of a permission-blind dashboard isn’t a break-in — it’s privilege creep. The gap that started this story showed that when the interface doesn’t know about a permission, operators get handed a bigger one to compensate. Modeling every permission in the console is what keeps the on-chain least-privilege design honest in day-to-day operations.

What deliberately stays off the console. Two things are excluded by policy, not by accident. Anything touching the most sensitive keys — the ones that authorize upgrades — stays on offline, air-gapped, scripted paths that never touch a web form. And the optional relay service that can sponsor gasless transactions has no remote admin controls at all, on purpose: an internet-facing kill switch would be a brand-new attack surface, and the relay’s worst case is designed to be “refuses to help,” never “loses funds.” The console shows the state of both, read-only, and links to the written procedures.

Not everything needs a permission. Routine housekeeping — sweeping up expired wagers, settling ones whose outcome is already known — is open to anyone by design, so those screens are open to any operator. And some contracts have no admin controls whatsoever: no permission can drain or redirect their funds because no function to do so exists. The strongest access control is the door you never build.

Further reading