Skip to main content

Core contractsCreatorShareOFT

CreatorShareOFT

The tradable LayerZero share token that holders buy and sell, which charges the buy fee and emits lottery entries.

CreatorShareOFT is the ShareOFT () that holders trade on the Base book after the auction settles. It is a LayerZero V2 omnichain fungible token: the wrapper mints it on Base, and LayerZero moves it across the mesh.

At a glance​

RoleTradable share; takes the buy fee and emits lottery entries on qualifying buys
DeployedPer creator: hub on Base, plus a remote ShareOFT on each mesh chain
Standard(s)ERC-20, LayerZero V2 OFT
Key relationshipsCreatorOVaultWrapper, CreatorOVault, Gauge controller, LotteryManager, Registry4626

How it works​

Vault claim vs ShareOFT​

The vault claim () is accounting: a claim on what the vault holds. Holders don't trade it on DEXs. ShareOFT () is what they buy and sell. Deposit does not mint ShareOFT; wrapping does.

A deposit through the wrapper still reads as about 1 creator coin to 1 , but the wrap itself is never 1:1. The wrapper locks 1000 per 1 because vault accounting and the DEX use different units. See Token units.

redeems for the creator coin on Base. After a wrap, the wrapper holds the and the holder holds . The wrapper's deposit() does both steps in one call.

Mint backing​

Every hub mint checks backing with _assertMintBacking: the wrapper's balance must be at least local totalSupply × VAULT_SHARE_NORMALIZATION (1000), or the mint reverts UnbackedShareMint. The check is skipped when the wrapper or vault is unset. LayerZero's _credit on bridge receipt is not this mint.

Qualifying buys​

A qualifying DEX buy of ShareOFT pays a transfer fee of 6.9% by default (buyFeeBps = 690). A buy is a transfer from a SwapOnly address to a non-SwapOnly address.

  1. The fee goes to the per-creator gauge.
  2. If the hub gauge's receiveFees reverts, the fee waits in ShareOFT pendingFees until anyone calls flushPendingFeesToGauge.
  3. The same transfer may emit a lottery entry for the recipient.

The fee split and lottery odds are on How it works.

DEX aggregators (1inch, Paraswap, LlamaSwap, CoW Swap, Uniswap Universal Router) are SwapOnly. The recipient gets the lottery entry, not the aggregator. Same-type hops and NoFees / Unknown addresses pay no trade fee.

On Base (the hub), entries go to LotteryManager in the same transaction. On remote chains, buys queue via lotteryQueue.queueFromOft, and the buyer later calls lotteryQueue.submitPendingLotteryEntry and pays the LayerZero fee.

Bridging is not a trade

LayerZero send moves ShareOFT across the mesh with no buy fee and no lottery entry. Remote chains hold ShareOFT only, with no local vault redeem. See Solana share bridge.

Key functions​

Transfers and bridging​

FunctionWhat it doesWho can call
send(sendParam, fee, refundAddress)Bridge ShareOFT over LayerZero (inherited from OFT)Holder
quoteSend(sendParam, payInLzToken)Quote the LayerZero fee for sendAnyone
flushPendingFeesToGauge()Retry sending pendingFees to the gaugeAnyone, hub only

Configuration​

FunctionWhat it doesWho can call
setAddressType(addr, opType)Mark an address Unknown, SwapOnly, or NoFeesOwner
setBuyFee(feeBps)Change the buy fee (default 690, max 1000 / MAX_FEE_BPS)Owner
setFeesEnabled(enabled) / setLotteryEnabled(enabled)Toggle fees or lottery entriesOwner
setGaugeController(controller)Set the fee recipient gaugeOwner
setLotteryResolver(resolver, allowed)Allowlist an ILotteryBeneficiary resolverOwner
setWrapper(wrapper)Set the minting wrapperOwner
setPeer(eid, peer) / setHubConfig(isHub, hubEid, hubGaugeReceiver) / setHubLotteryPeer(hubEid, hubLotteryPeer)LayerZero mesh wiringOwner, or remoteProtocolWireAuthority off Base

Lottery queue (remote chains)​

lotteryQueue is a ShareOFTRemoteLotteryQueue created in the constructor. Pending-entry calls live on the queue, not on ShareOFT.

FunctionWhat it doesWho can call
queueFromOft(buyer, amount, coverageBalance, venueUsd)Record a pending entryThe ShareOFT
quotePendingLotteryEntry(entryId)Quote the LayerZero fee to submit an entryAnyone
submitPendingLotteryEntry(entryId)Send the entry to the hub before it expiresThe buyer only (NotPendingLotteryEntryOwner)
quoteLottery(payload, options) / sendLottery(...)ShareOFT-side LayerZero send for entriessendLottery is queue only (OnlyLotteryQueue)

Gotchas​

  • The wrap is 1000 per 1 . Never treat and as 1:1.
  • Wraps, deposits, and bridge receipts do not pay the buy fee and do not enter the lottery.
  • Sell-side fees need an enabled tax hook aligned to the same tradeFeeCollector. Don't assume sell fees are on without checking that hook onchain.
  • setBuyFee is mutable. Read buyFeeBps onchain instead of hardcoding 6.9%.
  • Remote pending entries expire after 7 days (PENDING_LOTTERY_ENTRY_TTL), and each buyer can hold at most 16 (MAX_PENDING_LOTTERY_ENTRIES_PER_BUYER); extra buys queue no entry.
  • lotteryQueue is immutable and has no setter. The queue stores shareOft as immutable too.
  • setLotteryResolver only affects allowlisted ILotteryBeneficiary contracts. Unknown contracts and EOAs keep the recipient as the beneficiary.
  • setHubLotteryPeer requires setHubConfig first, with a non-zero hubGaugeReceiver.
  • flushPendingFeesToGauge reverts NotHub off Base and NothingToFlush when empty. Remote pending fees bridge through spokeModule.flushFees, not this call.
  • setWrapper(address(0)) reverts WrapperRequiredWhileSupplyExists while supply remains, since that would disable mint-backing checks.

Emergency​

To disable the lottery on an affected remote ShareOFT, run from the repo root:

forge script script/EmergencyDisableRemoteLottery.s.sol:EmergencyDisableRemoteLottery \
--rpc-url $RPC_URL --broadcast -vvvv

Required env: PRIVATE_KEY (ShareOFT owner) and SHARE_OFT (the remote ShareOFT address).

Source​