Module 07: Token Creation
Create and manage tokens on the BASIS factory. You set a creator fee of up to 1% on every trade of your token (it is 0 until you set it), and you receive 20% of the base part of loan-side fees on it, for the life of the token. Launching needs no liquidity; the creation fee is the factory's feeAmount() in ETH (0 today).
Prerequisites
- Wallet funded with the pair asset of the main you launch on (→01), client initialized (→02)
- Understand hybrid multiplier and floor mechanics: this is your most important creation choice
- Optional: register an agent identity so your token attribution is on-chain
Next steps after creation
- Set your creator fee and, on a token with holder rewards, the rewards fee (Creator Fee and Rewards Fee)
- Locking team allocations? Use vesting schedules to signal long-term commitment
- Building a prediction market? Use the prediction market flow instead: Predict+ tokens are created differently
- Want to compound your creator advantage? Read Module 12 Strategy 4: Creator's Edge
- Find your token in your portfolio:
getTokensByCreator()(standalone tokens only: a market's token is indexed under its market host)
Why Create a Token
Creating a token makes you a revenue-generating business on the platform, not just a trader.
What you get as creator:
- A creator fee you set, for the life of the token: 0 to 1% of every buy and sell, by any user, paid to your wallet(s) in the same transaction. It is 0 until you set it (
TAXES.setCreatorFee). You also receive 20% of the base part of loan-side fees on loans that use your token as collateral. Nothing is claimed. - No liquidity requirement: the creator deposits nothing to launch. The creation fee is
feeAmount(), 0 today. - What the creator cannot do: the creator cannot mint, has no pool to pull (there is no separate LP) and cannot change the hybrid multiplier (there is no setter for it).
- Presale control: set a presale of
presaleTokenswhole tokens, which can be zero; withfrozenit is whitelist-only. - Holder rewards: an opt-in feature chosen at creation (
rewardsEnabled): you set a rewards fee that is paid to every holder. - Fee control: set the creator fee (0 to 1%) and, on a token with holder rewards, the rewards fee.
- Stacking foundation: your token is collateral-eligible. Buy your own token → borrow the pair asset → deploy elsewhere (see Strategy 4: Creator's Edge).
If your token does $10,000 in daily volume and your creator fee is 0.50%, you earn $50 that day, paid trade by trade. Volume compounds. Fees compound. The creator role is structural.
How It Works
Factory contract → creates the token → sets you as permanent dev → token trades on the DEX immediately.
Two-phase lifecycle:
-
Presale (optional): set by
presaleTokens, a number of whole tokens (presaleTarget = presaleTokens × 1e18). It runs until that many tokens have been bought (presaleSoldreachespresaleTarget). The buy that completes it emitsBonded, setspresaleComplete()totrueand lifts a freeze. There is no pair-asset threshold and no cap; on a reversed token (Meme+, Yolo+, Yolo) the presale may be at most half the launch supply. -
Standard AMM: trading continues on the same curve. The creator fee you set keeps applying.
Pitfall: Presale and AMM buys run the same curve: there is no special presale price. Once the presale is complete it never reopens.
Frozen launch: optionally start trading locked. Only whitelisted wallets can buy (sells are never gated) during the presale; the freeze lifts when the presale completes or when you call disableFreeze(). A frozen token needs a presale (presaleTokens > 0), else frozen needs presale. Useful for presales, controlled early access, or OTC allocation before public launch.
Mains and pair assets. BASIS can run several mains. Each has its own pair asset and its own vault: SWAP.pairCount(), pairList(i) and pairs(main) → (pairAsset, staking, active), or mains[] in /contracts.json. A token is paired with one main for life (token.quoteToken()), and what you pay or receive for it is in that main's pair asset. A paused pair (active false) refuses trades.
Actions
Create Token
Method: client.factory.createTokenWithMetadata(options)
Recommended over a bare factory call. Handles image upload to IPFS via Pinata and metadata registration in one call. Token symbols must be CAPITALISED (a dapp and SDK rule; the contract does not check).
The contract calls behind it:
// 1. the exact creation fee, in ETH
fee = FACTORY.feeWhitelist(me) ? 0 : FACTORY.feeAmount()
// 2. create (payable, msg.value = fee exactly, else "Wrong fee")
FACTORY.createTokenFor(CreateParams{
mainToken, // a main from /contracts.json mains[]
symbol, name,
hybridMultiplier, // 1-100
frozen, // needs presaleTokens > 0
presaleTokens, // whole tokens; 0 = no presale
startLP, // 1000-100000: launch depth; 10000 = standard; not deposited capital
supplyMultiplier, // leave at 1 (the app's value)
rewardsEnabled, // holder rewards: fixed for life
autoVest, autoVestDuration, gradualAutovest
}) { value: fee }
// Meme+: FACTORY.createReversedTokenFor(same struct) { value: fee }
// 3. the token address is in the TokenCreated(tokenAddress, name, symbol, creator) event of the receipt
There is no short form: createTokenFor(params) and createReversedTokenFor(params) take the whole struct, mainToken, supplyMultiplier and rewardsEnabled included. Holder rewards are fixed at creation, for good.
JavaScript:
const { BasisClient } = require("basis-sdk-js");
const client = await BasisClient.create({ privateKey: "0xYourPrivateKey..." });
const result = await client.factory.createTokenWithMetadata({
symbol: "MYTKN",
name: "My Awesome Token",
hybridMultiplier: 50n,
startLP: 10000n, // launch depth: 10000 = the standard depth
presaleTokens: 0n, // whole tokens; 0 = no presale
rewardsEnabled: true, // holder rewards: fixed for life
description: "My awesome DeFi token on Basis",
imageUrl: "https://example.com/my-logo.png",
website: "https://myproject.com",
});
console.log("Token:", result.tokenAddress);
console.log("Image:", result.imageUrl);
console.log("Metadata:", result.metadata.url);
Python:
from basis_sdk import BasisClient
client = BasisClient.create(private_key="0xYourPrivateKey...")
result = client.factory.create_token_with_metadata(
symbol="MYTKN", name="My Awesome Token",
hybrid_multiplier=50, start_lp=10000, # start_lp: launch depth, 10000 = standard
presale_tokens=0, # whole tokens; 0 = no presale
rewards_enabled=True, # holder rewards: fixed for life
description="My awesome DeFi token on Basis",
image_url="https://example.com/my-logo.png",
website="https://myproject.com",
)
print("Token:", result["token_address"])
Returns: { hash, receipt, tokenAddress, imageUrl, metadata }
Parameter Reference
| Parameter | Required | Limits | Description |
|---|---|---|---|
symbol | yes | CAPITALISED only | Token ticker. "MYTKN" not "mytkn". |
name | yes | - | Token full name |
main | no | a live main | The main the token trades against (mainToken). Default: the default main |
hybridMultiplier | yes | 1–100 | Token type and stability. 100 = Stable+, 50–99 = Utility+, 1–49 = Floor+. With reversed: true the same dial makes Meme+ (1–50), Yolo+ (51–99) or Yolo (100). See decision table below. |
startLP | yes | 1,000–100,000 | Launch depth. 10,000 is the standard depth. NOT deposit capital and not a dollar amount: creator pays nothing for it. Higher value = deeper pool = less price movement per trade. |
supplyMultiplier | no | 1 to main.MAX_SUPPLY_MULTIPLIER(), default 1 | Leave at 1 (the app's value) |
rewardsEnabled | no | fixed for life | Turns holder rewards on. Off unless you pass true |
reversed | no | default: false | true creates a reversed token, Meme+, Yolo+ or Yolo (createReversedTokenFor); its presale may be at most half the launch supply |
description | no | - | Platform description text |
imageUrl | no | - | Auto-resized to 512×512 WebP |
website / telegram / twitterx | no | - | Social links: see Module 03 for X/Twitter linking |
frozen | no | default: false | If true, only whitelisted wallets can buy during the presale, until disableFreeze() or until the presale completes. Needs presaleTokens > 0 |
presaleTokens | no | 0 or more, whole tokens | Presale size (presaleTarget = presaleTokens × 1e18). 0 = no presale. It is a token count, not a pair-asset amount. |
autoVest | no | default: false | Every buyer's presale purchases go into a vesting schedule for that buyer instead of the wallet. Nothing vests after the presale completes; with no presale it does nothing |
autoVestDuration | no | 1–990 days when autoVest is true | Vesting period in days |
gradualAutovest | no | - | true = linear unlock over duration. false = cliff unlock at end. |
Fee: the call is payable. msg.value must equal exactly feeWhitelist(you) ? 0 : feeAmount(), in ETH (0 today); any other value reverts Wrong fee. The SDK reads it and sends it.
Requires: SIWE authentication (auto-handled by BasisClient.create: see Module 16).
Understanding startLP
startLP sets the price depth: how much capital is needed to move the price. It does not affect token type or the floor mechanism. Think of it as the zoom level on the price chart.
| startLP | Launch depth |
|---|---|
| 1,000 | The shallowest (the minimum) |
| 2,500 | What Up/Down pool tokens use |
| 10,000 | The standard depth, and what market tokens use |
| 100,000 | The deepest (the maximum) |
Higher is deeper: each trade moves the price less. There is no fixed dollar table. Preview any trade with SWAP.getAmountsOut.
Manage Token (Post-Creation)
Disable Freeze
Opens a frozen token to public trading. Irreversible: once called, freeze cannot be re-enabled. The freeze also lifts by itself when the presale completes.
await client.factory.disableFreeze(tokenAddress);
Whitelist Wallets (frozen tokens)
Add wallets that can buy while the token is frozen. Each wallet gets a cap in tokens: the most it may buy during the frozen presale (18 decimals). It is not a pair-asset amount.
await client.factory.setWhitelistedWallet(
tokenAddress,
["0xWallet1...", "0xWallet2..."],
maxTokensPerWallet, // bigint, token units (18 decimals)
"presale round 1" // label/tag
);
A wallet that is already whitelisted is skipped, so a cap cannot be raised in place. Call removeWhitelist, then add the wallet again with a cap above what it has already bought (the bought counter is kept).
Remove from Whitelist
await client.factory.removeWhitelist(tokenAddress, "0xWallet...");
Claim Holder Rewards
Holder rewards exist only on a token created with rewardsEnabled = true (through createTokenFor or createReversedTokenFor; never on a market token). The token's dev sets a rewards fee; it is shared among all holders in proportion to their balance, follows the tokens when they are transferred, and is paid in the pair asset of the token's main. Read getClaimableRewards(holder) on the token; take it with claimRewards(). With rewards off the read is 0.
const { hash, receipt } = await client.factory.claimRewards(tokenAddress);
Note:
claimRewards()pays the caller's holder rewards only. The creator fee is never claimed: it is paid to the dev wallets on every trade. With nothing owed the call succeeds and pays nothing, so readgetClaimableRewardsfirst. Creator income so far:TAXES.tokenDevEarnings(token, wallet)andTAXES.devTotalEarnings(wallet, pairAsset)(running totals of what was already paid).
Read a Token's Fees
Every trade pays a base fee: 0.60% today, or 0.54% when the wallet has an on-chain referrer. A factory token adds its creator fee (0 to 1%, set by the token's dev; 0 until set) and, only when the token was created with holder rewards, its rewards fee (0 up to maxRewardsFee: 0.5% at multiplier 100, rising to 10% at multiplier 1, always 10% on a reversed token (Meme+, Yolo+, Yolo)). A main pays the base only. A fee-exempt wallet pays nothing. The base rate is capped at 1.5%: read TAXES.baseRate(), do not hardcode it.
Read methods:
// What this wallet pays on this token, in basis points (0 for a fee-exempt wallet)
const rate = await client.taxes.getTaxRate(tokenAddress, userAddress); // TAXES.getTaxRate(token, user)
// The parts: isMain, hybridMultiplier, rewardsEnabled, dev, baseRate, creatorFee, rewardsFee,
// totalRate, maxCreatorFee, maxRewardsFee, reversed
const info = await client.taxes.getTokenTaxInfo(tokenAddress); // TAXES.getTokenTaxInfo(token)
// The platform base rate alone
const base = await client.taxes.getBaseRate(); // TAXES.baseRate()
getAmountsOut() is the quote before fees. Take getTaxRate(token, you) basis points off the input on a buy and off the output on a sell before setting minOut.
Creator Fee and Rewards Fee
await client.taxes.setCreatorFee(tokenAddress, 30n); // TAXES.setCreatorFee(token, bps): 0 to 100
await client.taxes.setRewardsFee(tokenAddress, 25n); // TAXES.setRewardsFee(token, bps): 0 to maxRewardsFee
const info = await client.taxes.getTokenTaxInfo(tokenAddress);
Rules:
- Only the token's dev may call (
only dev); the creator of a market may set the creator fee on its market token. - Not on a main (
not on MAIN). - The creator fee is at most 100 bps; the rewards fee at most
maxRewardsFee(hybridMultiplier, reversed)(over max). - The rewards fee needs a token created with holder rewards (
rewards off). - Each fee can change once per cooldown, 4 hours today; the first setting is free (
fee cooldown). addDevShare(token, wallet, basisPoints)splits your creator income across up to 10 wallets;removeDevShare(token, wallet);getDevWallets(token).
Read Methods
| Method | Returns | Description |
|---|---|---|
client.factory.getTokenState(addr) | { frozen, presaleComplete, presaleSold, presaleTarget, reversed, rewardsEnabled, hybridMultiplier, totalSupply, tokenPrice, pairPrice, main, pairAsset } | Current token state. tokenPrice is in main units (getTokenPrice()), pairPrice in the main's pair asset (getPairPrice()) |
client.factory.getTokensByCreator(creator) | string[] | Standalone tokens the wallet created through the factory. It does not list the tokens of markets you created: the market host is their creator on the factory. See Module 10 for portfolio-wide queries. |
client.factory.isEcosystemToken(addr) | boolean | Is this a valid BASIS token? |
client.factory.getFloorPrice(addr) | string | Floor in the main's pair asset (calculateFloor()); calculateTokenFloor() gives it in main units |
client.factory.getClaimableRewards(addr, investor) | bigint | Claimable holder rewards, in the pair asset (0 when rewards are off) |
client.factory.getFeeAmount() | bigint | Creation fee in wei of ETH (feeAmount()); pair it with feeWhitelist(you) |
client.taxes.getTaxRate(addr, user) | number | Effective tax rate for a user in basis points |
client.taxes.getTokenTaxInfo(addr) | struct | Base rate, creator fee, rewards fee and their caps for a token |
Reading the token type on-chain:
const multiplier = await client.publicClient.readContract({
address: tokenAddress,
abi: [{"inputs":[],"name":"hybridMultiplier","outputs":[{"name":"","type":"uint256"}],"stateMutability":"view","type":"function"}],
functionName: 'hybridMultiplier',
});
const reversed = await client.publicClient.readContract({
address: tokenAddress,
abi: [{"inputs":[],"name":"reversed","outputs":[{"name":"","type":"bool"}],"stateMutability":"view","type":"function"}],
functionName: 'reversed',
});
// reversed true: 1-50 = Meme+, 51-99 = Yolo+, 100n = Yolo.
// reversed false: 100n = Stable+ (or a market token / Up/Down pool token: FACTORY.creatorOf(token) is a market host
// or the Up/Down contract), 50-99 = Utility+, 1-49 = Floor+.
Choosing Your Token Type
The most important decision at creation. Pick the wrong type and your mechanics work against your goals.
| Goal | Token Type | hybridMultiplier | Notes |
|---|---|---|---|
| Maximum value accrual, price only rises (in pair-asset units) | Stable+ | 100 | Up-only mechanics, measured in the main's pair asset. 0.60% base fee like every token; rewards fee capped at 0.5%. Best for utility/service tokens with high transaction frequency. |
| Price discovery + a fast-rising floor | Utility+ | 50–99 (the app offers 50–90) | Real price movement but the floor rises fast. Best for community/brand tokens. |
| Room to run with a rising floor | Floor+ | 1–49 (the app offers 1 and 10–40) | More price action; the floor still rises, slowly toward 1. 1 is the app's default. |
| Memecoin moves, a floor that still grows | Meme+ | 1–50, reversed: true (the app offers 10–50) | Created with createReversedTokenFor. Loans value it at its floor. Rewards fee up to 10%. Presale at most half the launch supply |
| Bigger swings, a slow floor | Yolo+ | 51–99, reversed: true (the app offers 60–90) | As Meme+, with less floor growth |
| A plain memecoin | Yolo | 100, reversed: true | Classic memecoin pricing with a fixed supply; the floor stays at the launch price |
| Prediction market token | Predict+ | 100 (Stable+ subtype) | Created via prediction market flow, not standalone factory. Earns from market volume. |
Every value from 1 to 100 is valid: on a normal token 100 = Stable+, 50 to 99 = Utility+, 1 to 49 = Floor+; on a reversed token 1 to 50 = Meme+, 51 to 99 = Yolo+, 100 = Yolo.
Stable+: The Utility Token
Best when users repeatedly buy to use something, then the agent/service sells back to the pair asset. The ratchet mechanic (slippage stays in the pool, so the price does not fall in its pair asset) rewards high transaction frequency.
Use when: Services, subscriptions, API access, pay-per-use tools.
Avoid when: Speculative tokens: there's no exciting price discovery to attract traders.
Floor+: The Community Token
Best for projects where holders need confidence against dumps. The floor rises on every buy-sell cycle. Whale dumps create dips that look like buying opportunities rather than death spirals.
Use when: Communities, brand tokens, fan tokens, loyalty programs.
Key window: Near launch, the gap between spot and floor is smallest: the highest leverage ratio and the smallest possible drop to the floor. Market your launch to coincide with this window.
Meme+, Yolo+ and Yolo: Memecoins With a Floor
reversed() is true. These trade like classic memecoins, and at setting 100 nothing is minted or burned at all. The price can fall, so "price can only go up" never applies to a Meme+ token. The floor still never goes below the launch price; at 100 it stays at the launch price. A presale may be at most half the launch supply.
Predict+: The Engagement Token
Created as part of a prediction market. The token earns from all market trading volume.
Use when: Monetising domain expertise, running information markets, driving engagement around predictions. See Module 13 for full creator strategy.
Fees
| Action | Fee |
|---|---|
| Token creation | FACTORY.feeAmount() in ETH, sent as the exact msg.value: 0 today; 0 for a fee-whitelisted wallet |
| Trading (ongoing) | base 0.60% (0.54% with a referrer) + your creator fee (0 to 1%) + the rewards fee if the token has holder rewards |
A creator earns two things: the creator fee they set on their token, on every trade and bet, and 20% of the base part of loan-side fees on their token. Both are paid as they happen; there is nothing to claim. tokenDevEarnings(token, wallet) and devTotalEarnings(wallet, pairAsset) are running totals of what was already paid.
Errors
| Error | When | Fix |
|---|---|---|
"invalid multiplier" | hybridMultiplier out of range | Must be 1 to 100 |
"invalid starting LP" | startLP out of range | Must be 1,000–100,000 |
"Wrong fee" | msg.value is not exactly feeWhitelist(you) ? 0 : feeAmount() | Read the fee and send exactly that |
"frozen needs presale" | frozen with presaleTokens = 0 | Set a presale or drop frozen |
"auto-vest duration 1-990 days" | autoVest with a duration out of range | Use 1–990 days |
"invalid supply multiplier" | supplyMultiplier is 0 or above main.MAX_SUPPLY_MULTIPLIER() | Use 1 to that cap |
"unknown pair" / MainNotRegistered() | The main is not an active, registered pair | Pick a main from /contracts.json mains[] |
"presale above half the launch supply" | Presale too large on a reversed token (Meme+, Yolo+, Yolo) | Lower presaleTokens |
"No WL for freeze" | Buying a frozen token without whitelist | Creator must call setWhitelistedWallet() first |
"Overcapped on WL buying" | Whitelisted wallet hit its cap | The cap is in tokens; the creator removes the wallet and whitelists it again with a higher cap |
"only dev" | setCreatorFee / setRewardsFee by someone else | Call from the token's dev wallet |
"over max" | Fee above 100 bps (creator) or maxRewardsFee (rewards) | Lower the fee |
"fee cooldown" | Second change within the cooldown (4 hours today) | Wait for the cooldown |
"rewards off" | setRewardsFee on a token created without holder rewards | Not possible: rewards are fixed at creation |
"not on MAIN" | A fee setter called on a main | Mains pay the base fee only |
For the full alphabetical error index across all modules, see Module 18: SDK Reference.
What Next
After creating a token, the typical creator flow:
- If frozen: Call
setWhitelistedWallet()for presale participants, thendisableFreeze()when ready for public trading (or let the presale complete). - Set your fees:
setCreatorFee(up to 1%) and, with holder rewards on,setRewardsFee. Both start at 0. - Buy during your own presale if you want an early position.
- Monitor state with
getTokenState(): watchpresaleComplete. - Nothing to claim for the creator fee: it is paid on every trade. Holders claim rewards with
claimRewards(). - Stack on top: Creator's Edge strategy: buy your own Floor+/Predict+ token → borrow the pair asset → deploy across Stable+, lending, and predictions. You earn your creator fee on other users' volume on top of your stacked positions. See Module 12 for the full stacking strategies module.
See Also
- Module 04: Trading: Understand how your token will trade and how creator fees route
- Module 05: Lending: Borrow against your own token (Creator's Edge stack)
- Module 08: Predictions: Create a Predict+ market token instead, different flow, similar economics
- Module 09: Vesting: Lock team/investor allocations on a schedule
- Module 11: Token Mechanics: Hybrid multiplier, floor and presale behaviour
- Module 12: Strategy & Stacking: Creator's Edge, stack creator fees on top of borrow/lend/leverage paths
- Module 18: SDK Reference: Full
client.factoryandclient.taxesmethod signatures