Contracts
Addresses
BSC mainnet (chainId 56), deployed at block 130926361. The contracts that have an owner (the factory, the platform lists and both splitter factories) are owned by the Samefirst DAO multisig; the deployer holds no rights:
Components
| Contract | Role |
|---|---|
SamefirstFactory | Generic factory. Registers game templates (registered by the multisig, effective immediately; immutable once registered, can be disabled) and identity sources (also effective immediately, can be disabled), and creates each round with createLaunch(templateId, params[, salt]) as an EIP-1167 clone of a template (CREATE2): with a salt, the round's address is known in advance from predictLaunch(templateId, creator, salt); salts are per creator, so nobody else can take it. Holds the settings every template shares: fee (1%–5%), DAO address, raise range (at least 0.1 BNB), how long a full round waits for its launch (abandonAfter, 1 hour by default, 30 minutes–7 days), pause for new rounds, the launch signer for non-BNB pools (launchSigner, set by the multisig; clearing it stops every non-BNB launch, and this one setting applies to existing rounds too), and the issuer identity source (which decides who counts as the same issuer; Agent ID by default). Settings only affect rounds created afterwards. |
SamefirstLaunch (Classic template) | One round: subscribe, exit, launch, distribute, claim, refund. Creating costs nothing (the token's address is chosen at launch; nothing is locked). Shares are counted per identity: the gate settles the identity on the first subscription (an Agent ID, or the address itself), one share per identity, and the first subscribing address is the round's payee for that identity. For the first 10 minutes after the round fills only the issuer's launch address (launcher) can launch, then anyone can; up to 0.01 BNB of gas for the launch address can be prepaid at creation. Checked at creation: each tax at most 10% (Flap's limit), a 1%–5% slippage cap on non-BNB pools, the pool token must be a contract. Funds can only go to Flap, the swapper (at a non-BNB-pool launch), subscribers or the DAO. |
SamefirstBinding | Lets each Agent ID bind one ordinary wallet: the ID signs an EIP-712 authorization (no gas), the wallet submits it. One wallet belongs to one ID; 3-day cooldowns on both the ID side and the wallet side; either side can unbind at any time. No admin. |
FlapAdapter | Read-only helper: checks the issuer's Flap parameters and builds the token-creation call; for non-BNB pools checks the launch plan (checkPlan: signed for this round and this sender by the factory's launch signer, unexpired and valid for at most 10 minutes, a path of genuine PancakeSwap pools from WBNB to the pool token) and, for a custom pool token, sets an independent floor from Flap's signed price (flapFloor, BNB via Chainlink BNB/USD, at least 85%). Vaults: any vault factory usable on Flap's VaultPortal can be chosen, unless it has no code, is on the platform's VAULT_BAN list, is disabled by Flap, or doesn't support the pool; the multisig can also switch to allowing official vaults only. |
| Identity sources | Implement ISamefirstIdentity (identify(payer, config) returns the identity, checkConfig(config) validates the config), registered in the factory by number: 0 AgentIdIdentity (the Agent ID itself or the wallet bound to it; also the issuer identity), 1 BabtIdentity (addresses holding a BABT), 2 HoldingIdentity (config is a token and a minimum balance, checked at subscription). |
SamefirstSplitterFactory | Factory for tax splitter contracts. Each round's splitter address is derived with CREATE2 from the round's address plus the DAO share, so it can be set as Flap's tax recipient when the round is created and the contract deployed later; tax received before deployment all belongs to it once deployed. Anyone can deploy it after launch (deploy(launch, share), which checks the round was registered by the factory and the token matches). The DAO share is the issuer's choice (on the site, subscribers 1–80%, so the DAO keeps at least 20%); an address that names less than 20% for the DAO is deployed with 20%, so the tax still flows. Owned by the multisig, which can only change the default share the site uses for new rounds (DAO 50% by default, never below 20%); existing splitter contracts are unaffected. |
SamefirstSplitter | One token's tax recipient. Any BNB or token it receives is divided by split(asset) according to the share: the DAO's part goes straight to this round's DAO, and the subscribers' part is recorded as "claimable per allocated token"; subscribers claim it themselves from their subscribing address with claim(assets) (a contract address that can't receive an asset can use claimTo(assets, recipient) to have it sent elsewhere). Claimable = (allocated tokens × cumulative claimable per token − already settled) × holding factor; holding factor = amount held at claim time (including tokens still undelivered in the round contract) ÷ amount allocated, at most 1, and it can only go down, never up; the withheld part goes to the DAO. No admin; receiving BNB does nothing (with or without calldata), so tax payments can never fail; payments to the DAO are capped at 60,000 gas, and one the DAO can't take stays owed and is retried later. Known limit: the holding factor reads the balance only at claim time, so someone who sold can restore it by borrowing or buying back tokens just before claiming (this only costs the DAO the withheld part, never another subscriber). |
SamefirstXSplitterFactory | Factory for X-account splitters. The address is derived with CREATE2 from the round's address + the X account (keccak256("x:" + numeric id)) + the account's share (at most 80%) + the claim window (1–90 days, 7 by default), so it can be Flap's tax recipient when the round is created. Anyone can deploy it after launch; the claim window starts at deployment. A share above 80% or a window out of bounds (only by going around the site) is brought within the bounds at deployment. Owned by the multisig, which can replace or clear the platform's claim key (setClaimer; cleared, nobody can bind) and change the window the site offers new rounds; money in existing splitters is unaffected. |
SamefirstXSplitter | One token's tax recipient when the tax goes to an X account. Anyone can split(asset): the account's share is held, the DAO's goes straight to the round's DAO. After the account's owner signs in with X, the platform's claim key calls bind(wallet) (the first wallet within the window; a bound one can be changed), and every later split pays that wallet; if the window closes unbound, the held and all later account shares go to the DAO (Forfeited). BNB that can't be paid stays owed for the wallet's own withdraw; BNB transfers are capped at 100,000 gas. No admin; receiving BNB does nothing, so tax payments never fail. |
SamefirstSwapper | At a non-BNB-pool launch, swaps the round's BNB along the launch plan's path (PancakeSwap V2 / V3, up to 3 hops) and hands the pool token back to the round, counting what actually arrived (transfer taxes included). No admin, holds nothing; reverts unless the whole amount is swapped. |
Entry lists
| Contract | Role |
|---|---|
SamefirstLists | General platform lists: any number of lists, each with its own rule on whether the operator may add and remove. A few to start: SUBSCRIBER_BLOCK (platform blacklist: the operator can only add; removal takes the multisig), NAME_BAN (banned token names: the operator can add and remove) and VAULT_BAN (banned vault factories: the operator can add and remove). Also holds parameters: MAX_UNBLOCKS (how often an issuer may take the same person off their round blacklist, default 1, 0–10) and Flap's name-check module. Every change carries an optional public reason and an event. Owned by the multisig. |
SamefirstGate | Each round's gate. At creation: the issuer isn't on the platform blacklist, the name and symbol aren't in NAME_BAN, Flap won't refuse the name (asked on-chain from Flap's anti-scam module); it records the round's verification methods (up to 5 identity sources with their configs, whether there's a whitelist, whether it's open to every address), its first whitelist batches (Merkle roots, up to 100), the round blacklist (up to 500 at creation) and MAX_UNBLOCKS at that moment. At subscription: platform blacklist and round blacklist (for both the paying address and the Agent ID it acts for), then it tries the identity sources, the whitelist proof (the Agent ID leaf first, then the address itself; the proof leads to any batch the round holds) and open-to-all in order; the first that passes settles the identity for good. The issuer (the creating address, or a wallet bound to the issuer's Agent ID) manages the lists while preparing and open: addWhitelist adds a batch, removeWhitelist only while preparing, openToAll opens the round to every address for good; blockHere adds to the blacklist, unblockHere is free while preparing and at most MAX_UNBLOCKS times per person once open. |
SamefirstLaunch (Classic template), entry-list part | Besides the above: the params carry the lists (abi.encode(SamefirstGate.Lists): identity sources and configs, whitelist or not, open or not, opening time, first batches, round blacklist); an exclusive round may prepare up to 7 days (no subscriptions before openAt), and the issuer can endPreparing(false) to open early or endPreparing(true) to cancel (stage Cancelled); subscriptions call SamefirstGate.check first; exclusive rounds subscribe with subscribe(bytes32[] proof). Exit, claim and refund ignore the lists, so blocking can never trap anyone's money. |
The lists only affect who may subscribe and who may create, never people already in. Public page: samefirst.com/lists.
Round functions
| Function | Caller | Does |
|---|---|---|
subscribe() payable | Addresses that meet the round's verification methods | Subscribe or top up; the filling subscription only takes up to the target and returns the rest |
exit() | Subscribers | Exit before full, refunded minus the fee |
launch(salts, plan) | The launch address (within 10 minutes of filling), then anyone | Launch once full. plan: empty for a BNB pool; for a non-BNB pool the launch plan the platform's launch signer signed for this round (swap path, least amount out, deadline, and Flap's signed price for a custom pool token). salts are candidate salts for the token address (Flap requires it to end in 7777 / 8888); the first one still free is used in the same transaction, nothing locked in advance (empty list = the one stored in the parameters). If someone copies every candidate from the pending transaction and uses them first, the launch reverts entirely and is retried with new ones; the keeper sends 4 candidates, privately when a private endpoint is configured |
distribute(n) | Anyone | Push tokens and unused BNB in batches |
claim() | Subscribers | Claim your own share |
abandon() | Anyone | Full refunds if not launched when the wait after filling ends (terms().abandonAfter, 1 hour by default) |
refund() / refundBatch(n) | Subscribers / anyone | Claim / push refunds in batches |
withdraw() | Anyone | Withdraw BNB that couldn't be pushed and was recorded instead |
withdrawTokens() | Subscribers | Withdraw tokens that couldn't be transferred at distribution and were recorded instead |
sweep() / sweepToken(t) | Anyone | After full settlement, send dust and stray tokens to the DAO |
Views: idOf(addr) (the ID this address acts for in the round), payeeOf(id) (the ID's payee in the round), creatorId(), stage(), raised(), subscriberCount(), subscribed(addr), exitQuote(addr), previewFor(addr), owedWei(addr), owedTokens(addr), totalOwed(), totalOwedTokens(), feeCharged(), spent(), tokensBought(), token(), cfg(), terms().
Events
Created(creator, creatorId, kind, quoteToken, target, feeBps, salt) · Subscribed(who, id, handle, amount, total, returned) · Exited(who, amount, fee, refunded, pushed) · Filled(raised, subscribers) · FeePaid(to, amount, atLaunch, pushed) · Launched(token, fee, spent, tokensBought, subscribers) · Abandoned() · Distributed(who, tokens, refundWei, pushed) · Refunded(who, amount, pushed) · Withdrawn(who, amount) · TokensOwed(who, amount) · TokensWithdrawn(who, amount) · DistributionDone() · Swept(to, bnb, tokens)
Factory: LaunchCreated(launch, creator, templateId) · TemplateProposed / TemplateActivated / TemplateCancelled / TemplateDisabled · BindingProposed / BindingActivated / BindingCancelled · LaunchSignerSet(signer) · PolicyUpdated() · Paused(paused)
Binding contract: Bound(id, wallet, handle) · Unbound(id, wallet, by) · SignaturesRevoked(id, nextNonce)
External contracts (BSC)
| Contract | Address |
|---|---|
| Agent ID HandleRegistry | 0x60B9262456dE011F7d15a54CdD8c48bAaa8B022F |
| Flap Portal | 0xe2cE6ab80874Fa9Fa2aAE65D277Dd6B8e65C9De0 |
| Flap VaultPortal | 0x90497450f2a706f1951b5bdda52B4E5d16f34C06 |
| Flap Split Vault Factory | 0xfab75Dc774cB9B38b91749B8833360B46a52345F |
Safety
- No admin withdrawal, no upgrades; the factory owner can only change policy for new rounds.
- A failed push never blocks others: BNB or tokens that can't be sent are recorded and the owner withdraws them.
- The launch fee is capped so that fee + BNB spent never exceeds the raise; any raise amount settles.
- Internal security review completed in October 2026: Slither and Aderyn static analysis, a function-by-function manual review, an independent second reviewer, about 2.3 million randomized calls of Foundry and Medusa fuzzing and invariant testing, and 41 tests (including real Flap on a BSC mainnet fork). All 8 issues found are fixed; the report is
audit/REPORT.mdin the contracts repository. A third-party audit is planned before launch.