Contents
Zxo in one page
Zxo is a liquidity farming protocol for Uniswap V3 and compatible DEXes. Each Zxo pool launches its own reward token, paired with an established asset such as a stablecoin or a major token, and designed to trade close to 1:1 with it. Users provide liquidity to that pair as a V3 position on a fixed, shared price range, stake the position NFT in the Zxo contract, and earn the pool's reward token while the price, and its 15-minute average, stay within the range.
Every pool is anchored by a core LP: a large, single-sided position of the reward token placed just above parity. As buyers push the price up, the core LP sells reward tokens and accumulates the pair asset. The contract has no function that can withdraw or reduce this position, so the core LP and everything it accumulates is locked permanently.
Rewards are minted on claim at a rate that the contract re-targets to a configured APR on staked value, capped by hard limits set at pool creation. A bonus rewards deposits that bring more of the pair asset than the reward token. Staked positions can always be withdrawn after a one-day lock, with no price condition and no owner approval.
Stake a V3 position, earn trading fees plus minted rewards, withdraw at any time after 24 hours.
Locked core liquidity that no one can pull, and a reward token priced against a real asset from its first block.
No upgrades, no pause, no admin withdrawal path. Every rule in this paper is enforced by immutable code.
Project overview
Zxo lets people farm, harvest, collect and earn on concentrated liquidity. One deployment of the Zxo contract on a chain can host up to 255 pools. Each pool consists of:
- a reward token (a ZxoToken) created by the pool, with the Zxo contract as its only minter;
- a pair token with 6, 8 or 18 decimals, such as USDC, WBTC or an 18-decimal asset;
- a V3 pool for the pair at the 0.01% fee tier;
- a permanently locked core LP and core-stake position owned by the Zxo contract;
- an emission controller that targets an APR on staked value within hard caps.
The protocol consists of five deployed contracts per chain: Zxo (the farm), ZxoTreasury (fee receiver), ZxoV3Deployer (bootstraps prices on other DEXes), ZxoOracle (reads a 15-minute average price from V3 pools), and ZxoVerify (a deployment sanity check). Each pool also deploys its own ZxoToken.
Problem statement
Liquidity that can leave overnight
New tokens usually launch with liquidity the team controls. Holders have to trust that it will stay, and many launches end with that liquidity withdrawn.
Farms that pay for idle capital
Classic MasterChef farms pay rewards on LP tokens whether or not that liquidity is useful. With concentrated liquidity, a position outside the active price earns no fees and does nothing for traders.
Unbounded emissions
Fixed emission schedules pay the same number of tokens whether the farm holds $10,000 or $10 million, which leads to runaway APRs early and dilution later.
Admin keys over user funds
Many farms can pause withdrawals, change reward rules or migrate funds. Users carry that key risk for as long as they stay.
Solution summary
| Problem | Zxo design | Where it is enforced |
|---|---|---|
| Removable launch liquidity | The core LP is minted to the Zxo contract, which has no function to transfer, burn or reduce it. | Zxo.initializePool |
| Rewards for idle liquidity | All user positions share one range around parity. Claims are only allowed while the price is inside that range. | _getStakeTick, _requireFairRatio |
| Price pushed for one transaction | The health gate also requires the 15-minute time-weighted average price to be inside the range, which a same-block swap cannot move. | ZxoOracle.inRange, _poolRatio |
| Deploying with wrong addresses | The Zxo constructor checks every configured address on-chain: code present, DEX wiring, fee tier, HOLDER token, and the treasury, deployer and oracle contracts. | Zxo constructor, ZxoVerify |
| Unbounded emissions | The reward rate re-targets to an APR on staked value, capped by a max APR and a max 30-day amount fixed at pool creation. | _performAutoAdjust |
| Admin control over funds | No upgrades, pause, migration or rescue functions. Unstake has no owner or price gate. | Entire codebase |
| Hidden launch allocations | Tokens used to seed other DEXes must be finalized (remainder burned, seeding closed) before staking can open. | ZxoV3Deployer.finalizeToken |
Architecture
queryData and getUserData, builds stake, harvest and unstake transactions.update-chain-lib.py, compile.py, deploy.py, run.sh.slot0) and price history (observe) for the health check.ChainLib. Each chain has a small library of constants: chain id, position manager, factory, HOLDER token and HOLDER threshold, and the treasury, deployer and oracle addresses. One library is compiled into the contracts for the target chain. Every constructor reverts if deployed on a different chain id, and the deploy script refuses to send a build to a chain that doesn't match.
Checked at deployment. The Zxo constructor reverts, with a readable reason, unless every configured address is a deployed contract, the position manager uses the configured factory, the 0.01% fee tier exists with tick spacing 1, the HOLDER token has 18 decimals and a threshold of at least one token, the treasury, deployer and oracle identify themselves correctly, and the deployer is not already linked to another Zxo. These checks live in the constructor, so they cost nothing at runtime.
No proxies. Contracts are deployed directly and cannot be upgraded. The rules described here are the rules for the life of each deployment.
Core functionality
5.1 Pool launch
The owner launches each pool in six steps. The contracts enforce the order of the last two: the core stake cannot be minted until the reward token is finalized on the deployer, and staking cannot open until the core stake exists. Step 3 gives the new pool room to store 15 minutes of price history, which the health gate needs.
set-observations.py). Anyone can do it; the size can only grow.5.2 Stake
5.3 Harvest
Action 0 claims rewards, 1 collects fees, 2 does both.
5.4 Unstake
to (default: you)No price check and no owner approval. Harvest first: pending rewards are forfeited on unstake.
5.5 Emission re-targeting
Staking and reward math
Every formula below is taken from the contract code. Amounts are integers in the token's smallest unit, divisions round down, and PRECISION = 10¹⁸. Examples use round numbers for readability.
6.1 Price, ticks and ranges
| Position | Range (18/18, Zxo = token0) | Zxo price band | Purpose |
|---|---|---|---|
| User stake | (−300, +300) | 0.9704 – 1.0305 | Every user position uses this exact range |
| Core LP | [11, 300] | 1.0011 – 1.0305 | Single-sided sell wall above parity |
| Core stake | [31, 300] | 1.0031 – 1.0305 | Opens staking, keeps totals non-zero |
| Launch price | tick 10 | 1.0010 | One tick below the core LP |
When Zxo is token1, all ranges mirror around parity (for example the core LP becomes [−300, −11]). The full table for every pair type is in the appendix.
6.2 Health gate
currentTick is read live from the pool's slot0. twapTick is the 15-minute time-weighted average, read by ZxoOracle through the pool's observe. Both bounds are strict.A swap inside one transaction can move the spot tick but not the 15-minute average, so pushing the price into range just to harvest does not work. If the pool does not yet have 15 minutes of history, no average is available and the pool counts as unhealthy.Stake and harvest require a healthy pool. Unstake does not. Rewards accrue whether or not the pool is healthy, and become claimable again when the price returns to the range.6.3 Deposit normalization
totalAmount is recorded as the position's contribution to pool TVL.6.4 Position liquidity
6.5 Stake bonus
local is the reward token in your deposit and paired is the pair token. The bonus rewards deposits that bring more of the pair asset than of the reward token, which happens when the price is above parity.The bonus reaches its 20% cap (25% for HOLDER holders) at a 53.36% paired ratio, about 20 ticks above parity. At the launch tick the organic bonus is about 10–12%.The bonus is virtual reward weight. It adds nothing to your principal and is removed from the totals when you unstake.HOLDER_THRESHOLD and the HOLDER token are set per chain in its ChainLib and fixed at compile time. The threshold is at least one token (for example 369,000 on Arbitrum).6.6 Reward accounting
totalStaked is the sum of all weights in the pool, including the core stake. Your share of emissions at any moment is weight / totalStaked.Rewards are minted when claimed. Nothing is pre-funded, and rewards that are never claimed are never minted.6.7 Emission rate
updatePool call after a 1-hour cooldown from the last change. The default target is 10% (1,000 bps).6.8 Referral split
6.9 Unstake threshold
acceptForfeit lets you exit immediately, for example while the pool is out of range and harvest is blocked.Assumes weight is proportional to value and ignores the core stake's share, fee income and price changes. The contract's actual figures depend on every position in the pool.
Technical design
7.1 Storage
| Variable | Type | Contents |
|---|---|---|
poolInfo | PoolInfo[] | Per pool: accRewardPerShare, totalStaked, totalTokens, token0, token1, rewardToken, rewardRate (uint96), lastRewardTime, lastAutoUpdate (uint40), targetAprBps, refRateBps (uint16), stakes (uint32) |
poolConfig | uint8 → uint256 | Packed: maxNormalAPR (bits 240–255), decimals (232–239), maxBonusEnds (192–231), max30dRate (64–191), corePositionId (0–63) |
stakedPositions_ | uint8 → uint64 → StakedPosition | rewardDebt, liquidity (weight), tokens (TVL share), owner, stakeTime, poolId |
userStakedRefs_ | address → PositionRef[] | Up to 21 (poolId, tokenId) pairs per address |
userReferrer_ | address → address | Referrer, set once on first stake |
referralsPerUser_ | address → uint32 | Count of addresses that chose this referrer |
totalGlobalStakes | uint32 | Active positions across all pools |
7.2 Events
Zxo emits a single event, through one shared internal function, to keep the contract under the 24,576-byte size limit. Indexers filter on action. A harvest with action 2 (ClaimAndCollect) logs a Claim and a CollectFees event.
event ZxoEvent(address indexed user, uint256 data0, uint256 data1, uint256 data2, Action_ action);
| Action | Code | user | data0 | data1 | data2 |
|---|---|---|---|---|---|
| Claim | 0 | staker | poolId | tokenId | rewards claimed (incl. referral share) |
| CollectFees | 1 | staker | poolId | tokenId | amount0 + amount1 |
| Bonus | 3 | staker | poolId | tokenId | bonus weight added |
| Unstake | 4 | staker | poolId | tokenId | pending rewards forfeited |
| AutoAdjust | 5 | caller | poolId | new rewardRate | targetAprBps |
| Stake | 6 | staker | poolId | tokenId | weight (liquidity + bonus) |
| Referrer | 7 | referrer | 0 | 0 | 0 |
| Deployed | 8 | deployer | timestamp | chain id | 0 |
| PoolCreated | 9 | reward token | poolId | max30dRate | maxNormalAPR |
| CoreFeesCollected | 10 | treasury | poolId | amount0 | amount1 |
| ConfigUpdated | 11 | owner | poolId | dataType | value stored (target APR after the cap) |
| PoolInitialized | 12 | reward token | poolId | position tokenId | liquidity |
7.3 Access control, errors and upgradeability
Access control
Zxo and ZxoV3Deployer use OpenZeppelin Ownable2Step. ZxoTreasury uses Ownable. ZxoToken ownership is fixed to Zxo and cannot be transferred or renounced.
Reentrancy
Every state-changing external function is nonReentrant. State is written before external transfers, and unstake makes its NFT transfer the final step.
Errors and upgrades
Runtime failures use a bare revert() to save bytecode; the dapp checks conditions before sending. Deployment checks use readable messages. No proxy, no upgrade path.
User and owner functions
8.1 Functions anyone can call
| Function | What it does | Requirements |
|---|---|---|
stake(poolId, amount0Desired, amount1Desired, slippageBps, referrer) | Mints a V3 position on the pool's stake range from your tokens and stakes it. | Approve Zxo for both tokens. Pool open and healthy. Fewer than 21 positions. Slippage ≤ 5,000 bps. |
harvest(tokenId, poolId, action) | 0 claims rewards, 1 collects trading fees, 2 does both. | You own the position. Pool healthy. |
unstake(tokenId, poolId, acceptForfeit, to) | Returns the position NFT to to (address(0) = you). | You own the position. 24 hours since staking. Pending ≤ 0.000001 or forfeit accepted. to can receive NFTs. |
updatePool(poolId, updateAll) | Accrues rewards and re-targets the rate for one or all pools. | None |
pool.increaseObservationCardinalityNext(n) | A V3 pool function, not Zxo. Enlarges the pool's price history so the 15-minute average is always available. set-observations.py picks a size for the chain. | None. The size can only grow, so it cannot be used to break a pool. |
8.2 Read functions
| Function | Returns |
|---|---|
queryData(tokenId, poolId, 0, 0) | Time of the last rate change |
queryData(tokenId, poolId, 1, 0) | Pending rewards for a position |
queryData(0, poolId, 2, index) | Pool config: 0 maxNormalAPR, 1 decimals, 2 bonus-year end, 3 max30dRate, 4 core LP id, 5 packed value. Other indexes revert. |
queryData(0, poolId, 3, 0) | Current APR in bps |
queryData(0, poolId, 4, 0) | (healthy, sqrtPriceX96, tick); healthy needs the spot tick and the 15-minute average in range. (false, 0, 0) when no price is available |
ZxoOracle.twapTick(pool) / inRange(pool, lower, upper) | The 15-minute average tick (ok = false when history is missing), and whether it sits strictly inside a range |
queryData(tokenId, poolId, 5, 0) | (canUnstakeWithoutForfeit, pending, threshold) |
getUserData(user, dataType) | 0: positions and ids · 1: ids and pool ids · 2: all |
getTargetAprsByIds(poolIds) | Target APR per pool |
poolInfo(i), poolConfig(i), stakedPositions_(pid, id), userReferrer_(a), totalGlobalStakes() | Raw storage getters |
8.3 Owner functions
| Contract | Function | Effect and limits |
|---|---|---|
| Zxo | createPool(...) | New token and pool. maxNormalAPR ≤ 320%. Pair decimals must match (6, 8 or 18). Max 255 pools. |
| Zxo | initializePool(poolId, slippageBps, isCore) | Once each: core LP (price must equal the launch price), then core stake (token must be finalized). |
| Zxo | updatePoolConfig(poolId, 0, v) | Target APR, clamped to the current max. |
| Zxo | updatePoolConfig(poolId, 1, v) | Referral rate between 3% and 30%. |
| Zxo | updatePoolConfig(poolId, 2, v) | Lower the reward rate only. Lasts until the next re-target. Any other type reverts. |
| Zxo | collectFeesFromCoreLP(poolId) | Sends core LP trading fees to the treasury. Liquidity is not touched. |
| ZxoV3Deployer | startV3pool, finalizeToken, burnTokens, setZxoContract | Seed external DEXes until finalized. ZXO_CONTRACT can be set once. |
| ZxoTreasury | sweepToken, sweepAll, scheduleDestination, executeDestinationChange, lockDestinationForever | Sweeps to the destination. Destination changes wait 7 days and can be locked permanently. |
Tokenomics
Each pool has its own reward token, a standard ERC-20 with burn support. The name, symbol and core allocations are set by the owner when the pool is created. Only the Zxo contract can mint, and it only mints in three situations.
Where value flows
- Trading fees (0.01%) on user positions go to the stakers who own them.
- Core LP fees go to the treasury.
- Referral share (3–30% of each claim) goes to the staker's referrer, or the treasury by default.
- Pair tokens bought by the core LP stay locked in it.
- Zxo charges no fee on principal and no deposit or withdrawal fee.
Utility and incentives
- The reward token trades against its pair asset near parity, backed on the upside by the core LP's sell wall.
- Deposits above parity get up to 20% extra reward weight.
- Holders of at least the chain's HOLDER threshold get 25% more bonus. The HOLDER token and threshold are set per chain; the token is only available on public DEXes.
- Referrers earn a share of the rewards of everyone who names them.
Planned core LP sizes range from about $5 million to $15 million per pool, with larger pools possible. At those sizes, moving the price from the launch tick into the core-stake range requires absorbing about 7% of the core LP (roughly $350,000 for a $5 million core).
Governance and owner powers
Zxo has no token voting. Each contract has a single owner address. The team intends to move ownership of Zxo and ZxoV3Deployer to a multisig before mainnet; both use two-step transfers, so a new owner must accept before control moves.
The owner can
- Create pools and their tokens, up to 255
- Initialize each pool's core LP and core stake once
- Set the target APR within the pool's cap, including 0
- Set the referral rate between 3% and 30%
- Temporarily lower a pool's reward rate
- Send core LP trading fees to the treasury
- Seed external DEXes for a new token until it is finalized
- Transfer or renounce ownership
The owner cannot
- Touch staked NFTs, deposits or unclaimed rewards
- Block, delay or pause unstaking
- Withdraw or reduce core LP or core-stake liquidity
- Mint reward tokens outside pool creation and emissions
- Burn anyone's tokens
- Raise a pool's max APR or 30-day cap after creation
- Change ranges, fee tier, DEX or reward token
- Upgrade the contracts or rescue any asset Zxo holds
If ownership is renounced, pools keep running with their last settings. No new pools can be created and core LP fees stay uncollected in the positions.
Treasury. The treasury owner can sweep tokens to a destination address. Changing that destination takes 7 days, and the destination can be locked permanently.
Security
Revision 2.5, AI-assisted, up from 6.6 before remediation.
20 resolved 8 accepted 0 open
Both High and three of four Medium findings fixed. The core-stake weighting is accepted as a design trade-off.
Fork and invariant tests, a testnet launch rehearsal, and an independent human re-audit before mainnet.
11.1 User guarantees
| Guarantee | Why it holds |
|---|---|
| You can always withdraw after 24 hours | Unstake has no price check and no owner gate. Accounting totals are guarded so a drift can never block exits. |
| Only you control your position | Every action checks the stored owner. Zxo never approves anyone to move its NFTs. |
| Your NFT is only released by unstake | Unstake deletes the record before the transfer; no other function moves position NFTs. |
| Core liquidity cannot be pulled | No function transfers, burns or reduces core positions. |
| A failed withdrawal changes nothing | If the NFT transfer fails, the whole unstake reverts and your position stays staked. |
11.2 Threat model
| Threat | Protection | Status |
|---|---|---|
| Reentrancy through tokens, NFTs or recipient contracts | nonReentrant on every mutating entry point; checks-effects-interactions; external call last in unstake | Protected |
| Owner extracting launch tokens through a fake DEX | Finalization burns the remainder and closes seeding; staking cannot open before it. Users verify isFinalized and each seeding DEX. | Mitigated |
| Pool pre-initialized at a wrong price | Core LP init reverts unless the pool price equals the launch price. | Protected |
DEX fork with a different slot0 layout | Price read decodes only the two fields every fork shares; failures report unhealthy. | Protected |
| Flash swap to harvest while the price is out of range | The health gate also requires the 15-minute average tick in range, which one transaction cannot move | Protected |
| Wrong or swapped addresses in a chain library | The Zxo constructor and ZxoVerify check every address on-chain; a bad build cannot deploy | Protected |
| Non-standard tokens such as USDT | SafeERC20 transfers and forceApprove. Fee-on-transfer and rebasing pair tokens are not supported. | Protected |
| Contract stakers that cannot receive NFTs | to parameter on unstake | Protected |
| Flash swap to raise one stake's bonus | The bonus uses the deposit's token mix at the spot price. It is capped at 20% (25% with HOLDER), emissions stay capped and principal is not at risk | Accepted |
| Price history not enlarged on a new pool | Run set-observations.py once per pool. Until then stake and harvest pause; unstake and funds are unaffected | Operational |
| Core stake takes an unclaimed share of emissions | Users earn somewhat below the target APR; gap shrinks as TVL grows | Accepted |
| Owner key compromise | Owner cannot reach user funds; multisig planned; two-step transfers | Operational |
11.3 Accepted risks and design choices
- Spot-based bonus. The health gate uses both the spot price and a 15-minute average. The stake bonus still uses the deposit's token mix at the current price.
- Core-stake dilution. The contract-owned core stake counts toward TVL and reward weight but never claims; its trading fees cannot be collected.
- Bonus curve. The bonus reaches its cap about 20 ticks above parity, by design.
- Deadlines. Mint deadlines are set at execution time; price protection comes from the slippage minimums.
- No rescue path. Dust below the refund threshold and NFTs sent directly to Zxo cannot be recovered. This keeps the owner away from every asset Zxo holds.
- Referrers. Any address can be a referrer. Naming the Zxo contract loses that user's referral share; naming yourself skips the split.
- Holder bonus. The HOLDER token can be bought, used for a stake and sold again. This is intended.
- Forfeiture. Unstaking forfeits pending rewards. Rewards are paid only through harvest, while the pool is healthy.
11.4 Compiler
Contracts are compiled with solc 0.8.36 on the legacy pipeline (via-IR off), optimizer 200 runs, EVM version Shanghai. The three known bugs listed for 0.8.36 require via-IR, recursion, custom errors in require, or deleting an element of a memory bytes array. Zxo uses none of these.
Gas efficiency
| Choice | Saves |
|---|---|
Bare revert() without messages | Bytecode and deployment gas |
| One shared event with an action code, emitted through one internal function | Bytecode; keeps Zxo under the 24,576-byte limit |
| Deployment checks in the constructor | Readable errors with no runtime bytecode cost |
| The oracle is read only when the spot check passes | An external call on unhealthy pools |
| Packed structs (uint96 rate, uint40 times, uint16 settings) and one packed config word per pool | Storage reads and writes |
| Rewards minted on claim | No reward funding transfers or balance bookkeeping |
| Chain addresses as compile-time constants | No storage reads for external addresses |
| Dust below thresholds kept instead of refunded | A token transfer on most stakes |
| Rate re-targeting at most once per hour | Repeated storage writes |
| Bounded loops (21 positions per address, 255 pools) | Predictable worst-case gas |
| uint8 pool ids, unchecked counters where overflow is impossible | Arithmetic overhead |
Interfaces and dependencies
| Dependency | Used for |
|---|---|
| OpenZeppelin Contracts 5.x | ERC20, ERC20Burnable, Ownable, Ownable2Step, ReentrancyGuard, SafeERC20, IERC721, IERC721Receiver, IERC20Metadata |
| Uniswap V3 NonfungiblePositionManager (or fork) | createAndInitializePoolIfNecessary, mint, collect, NFT custody |
| Uniswap V3 Factory (or fork) | getPool for the pair at fee 100 |
| V3 pool | slot0 read by low-level call (price and tick only); observe read by ZxoOracle for the 15-minute average; increaseObservationCardinalityNext at launch |
| HOLDER token | balanceOf for the holder bonus |
| Standards | ERC-20, ERC-721 and the ERC-721 receiver hook |
Project interfaces: IZxo, IZxoToken, IZxoV3Deployer, IZxoOracle, Iv3PosManager, IUniswapV3. Libraries: ZxoLib (ticks, bonus, packing, validation) and one ChainLib per chain.
Deployment
14.1 Networks
| Network | Chain id | DEX | Position manager |
|---|---|---|---|
| PulseChain | 369 | LibertySwap | 0x19b1347900840e7a299F339868881750918FAD91 |
| Ethereum | 1 | Uniswap V3 | 0xC36442b4a4522E871399CD717aBDD847Ab11FE88 |
| Arbitrum | 42161 | Uniswap V3 | 0xC36442b4a4522E871399CD717aBDD847Ab11FE88 |
| Optimism | 10 | Uniswap V3 | 0xC36442b4a4522E871399CD717aBDD847Ab11FE88 |
| Polygon | 137 | Uniswap V3 | 0xC36442b4a4522E871399CD717aBDD847Ab11FE88 |
| Base | 8453 | Uniswap V3 | 0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1 |
| BNB Chain | 56 | PancakeSwap V3 | 0x46A15B0b27311cedF172AB29E4f4766fbE7F4364 |
| Unichain | 130 | Uniswap V3 | 0x943e6e07a7E8E791dAFC44083e54041D743C46E9 |
| Robinhood Chain | 4663 | Uniswap V3 | 0x73991a25C818Bf1f1128dEAaB1492D45638DE0D3 |
| PulseChain Testnet | 943 | Test | configured for testing |
Zxo contract addresses will be published at Zxo.Farm after each deployment is verified. Only use addresses from that source.
14.2 Deployment order
| Step | Action |
|---|---|
| 1 | Deploy ZxoTreasury; set ZXO_TREASURY in the chain library |
| 2 | Deploy ZxoOracle; set ZXO_ORACLE in the chain library |
| 3 | Deploy ZxoVerify(false, true) to check the factory, position manager, HOLDER token and threshold, treasury and oracle on-chain |
| 4 | Deploy ZxoV3Deployer; set ZXO_V3_DEPLOYER in the chain library |
| 5 | Deploy ZxoVerify(true, true) to confirm the deployer as well |
| 6 | Deploy Zxo (its constructor repeats the checks); call ZxoV3Deployer.setZxoContract(Zxo) once |
| 7 | Transfer ownership to the multisig |
| Per pool | After the core LP is initialized, run set-observations.py once so the 15-minute average is available |
14.3 Verification
The build compiles each contract once with solc --standard-json. The deployed bytecode and the explorer's verification input come from that single compile, and the input embeds every source file under its import name, so any explorer can reproduce the bytecode without local files. After deploying, the deploy script compares the on-chain code with the build (immutable values masked) and records the deployment.
Anyone can check a deployment by compiling the published standard-json-input.json with solc 0.8.36 and comparing the runtime bytecode with the chain. This has been confirmed for Zxo: the input compiles with no local files and reproduces the build bytecode.
Legal and compliance
License. The contracts are released under the Business Source License 1.1. Copying, modification and non-production use are permitted. Production use requires a commercial license from the licensor. On 23 September 2029 (or the fourth anniversary of a version's first public release, if earlier), the license converts to GNU GPL v3.0 or later.
No offer. This whitepaper describes software. It is not an offer or solicitation to buy or sell any token, security or financial product, and it is not investment, legal or tax advice.
Risk. Using Zxo involves smart contract risk, DEX and pair-token risk, price risk (including impermanent loss on concentrated positions), and network risk. Rewards and APR figures are variable and not guaranteed. Tokens can lose some or all of their value.
Jurisdiction. Users are responsible for complying with the laws of their jurisdiction, including rules on digital assets and taxes.
Software as is. To the extent permitted by law, the software is provided without warranties of any kind.
Roadmap
Milestones are planned and may change. Dates will be announced at Zxo.Farm.
Phase 1 · Protocol and review Complete
- Core contracts, chain libraries and deployment verifier
- Security review and remediation to Revision 2.5, with no open findings
- 15-minute average price gate (ZxoOracle) and on-chain deployment checks
- Reproducible build, verification and deployment tooling
Phase 2 · Testing In progress
- Contracts deployed on PulseChain Testnet; full launch rehearsal
- Fork tests on every target DEX and invariant tests
- Static analysis (Slither or Aderyn) and an independent human re-audit of the changes
- Dapp trust panel: finalization, seeding DEXes, burned NFTs
Phase 3 · Mainnet launch
- PulseChain mainnet deployment with multisig ownership
- First pools and public verification of all contracts
Phase 4 · Multichain
- Ethereum, Arbitrum, Base, Optimism, Polygon, BNB Chain, Unichain and Robinhood Chain
Phase 5 · Growth
- Additional pools per chain, up to 255 per deployment
- Seeding new V3 DEXes for new pools as they launch
Reference
A. Constants
| Constant | Value | Meaning |
|---|---|---|
LP_FEE_TIER | 100 | 0.01% V3 fee tier |
DEFAULT_TARGET_APR | 1,000 | 10% initial target |
MAX_NORMAL_APR | 32,000 | 320% max per pool (×2 in year one) |
BONUS_APR_ENDS | 365 days | Doubled APR cap window |
RATE_COOLDOWN | 1 hour | Minimum time between rate changes |
MIN_REF_RATE_BPS / MAX_REF_RATE_BPS | 300 / 3,000 | Referral share 3% – 30% |
MAX_BONUS_BPS | 2,000 | 20% bonus cap (25% with HOLDER) |
BONUS_THRESHOLD | 0.5 × 10¹⁸ | Bonus starts above 50% paired |
HOLDER_THRESHOLD | per chain, ≥ 10¹⁸ | HOLDER balance for +25% bonus (ChainLib) |
ZxoOracle.WINDOW | 900 s | 15-minute average for the health gate |
MAX_USER_STAKES | 21 | Positions per address |
MAX_SLIP | 5,000 | 50% max slippage |
MIN_UNSTAKE | 10¹² | Pending above this needs forfeit |
MIN_DUST_18 / _8 / _6 | 10¹² / 10 / 1,000 | Smallest refund sent back |
| Unstake lock | 1 day | From stake time |
PRECISION | 10¹⁸ | Reward accumulator scale |
B. Tick ranges for every pair type
| Pair | Zxo is | Launch tick | Core LP | Core stake | User stake |
|---|---|---|---|---|---|
| 18 / 18 | token0 | 10 | [11, 300] | [31, 300] | (−300, 300) |
| 18 / 18 | token1 | −10 | [−300, −11] | [−300, −31] | (−300, 300) |
| 18 / 6 | token0 | −276,314 | [−276,313, −276,024] | [−276,293, −276,024] | (−276,624, −276,024) |
| 18 / 6 | token1 | 276,314 | [276,024, 276,313] | [276,024, 276,293] | (276,024, 276,624) |
| 18 / 8 | token0 | −230,260 | [−230,259, −229,970] | [−230,239, −229,970] | (−230,570, −229,970) |
| 18 / 8 | token1 | 230,260 | [229,970, 230,259] | [229,970, 230,239] | (229,970, 230,570) |
C. Code references
| Topic | Location |
|---|---|
| Staking | Zxo.stake, ZxoLib._requireStakeValidations |
| Bonus and TVL | ZxoLib._calcBonusAndTotal |
| Rewards | Zxo._updatePool, Zxo._pendingRewards, Zxo._claimRewards |
| Emissions | Zxo._performAutoAdjust, Zxo.updatePoolConfig |
| Health | Zxo._readSlot0, Zxo._poolRatio, ZxoOracle.twapTick, ZxoOracle.inRange, ZxoLib._getStakeTick |
| Deployment checks | Zxo constructor, ZxoVerify |
| Launch | Zxo.createPool, Zxo.initializePool, ZxoLib._getCoreTicks, ZxoV3Deployer.startV3pool, ZxoV3Deployer.finalizeToken |
| Config packing | ZxoLib.packPoolConfig and unpack* |
D. Glossary
- Tick
- A 0.01% price step in a V3 pool. Price = 1.0001^tick.
- Concentrated liquidity
- Liquidity placed only within a chosen price range, earning fees only while the price is in that range.
- Position NFT
- The ERC-721 token a V3 position manager issues for each liquidity position.
- Parity
- One reward token equals one pair token in value.
- Core LP
- The protocol's permanently locked, single-sided reward-token position above parity.
- Core stake
- A contract-owned staked position that opens staking for a pool.
- Healthy
- The pool's current tick and its 15-minute average tick are both strictly inside the user stake range.
- TWAP
- Time-weighted average price. Zxo uses the average tick over the last 15 minutes.
- Observation cardinality
- How many price-history slots a V3 pool keeps. Raised once per pool so the 15-minute average is always available.
- Weight
- A position's liquidity plus any bonus. Rewards are shared by weight.
- accRewardPerShare
- Running total of rewards per unit of weight, scaled by 10¹⁸.
- Basis point (bps)
- 0.01%. 10,000 bps = 100%.
- TVL
- Sum of staked deposits in 18-decimal units, counted 1:1.
- HOLDER token
- A separate token, set per chain, whose holders at or above the chain's threshold get a larger stake bonus.
- Seeding
- Small single-sided positions on other V3 DEXes that set the initial price there.
- Finalization
- Burning the deployer's remaining launch tokens and closing seeding for that token.
- Forfeit
- Giving up pending rewards when unstaking without harvesting first.
- ChainLib
- The per-chain library of addresses compiled into the contracts.