Stake MOTO
Lock MOTO to earn a boosted share of protocol fees.
MotoStaking locks MOTO for one of four durations at a boosted weight and
pays multi-token revenue share from the Collector (the staking bucket of
the protocol fee. The split starts at 2 : 2 : 2 : 1 across staking,
treasury, Rakeback, and buyback and burn, which is 0.20%, 0.20%, 0.20% and
0.10% of each swap at launch).
Deployment status
MotoStaking is deployed on Ethereum at /config.addresses.motoStaking (see
the deployment status table). Staking, appending, unstaking and every read on this page work
against it. The deployed implementation has no reward tokens registered and
its feeRouter() is a 0x…dEaD placeholder, so rewardTokensLength() reads 0
and claimAsMoto reverts. Both arrive with a contract upgrade.
Lock options
durationOption | Duration | Boost |
|---|---|---|
| 0 | 90 days | 1x |
| 1 | 180 days | 2x |
| 2 | 365 days | 4x |
| 3 | 730 days | 8x |
Read the ladder live from durations(i) and boostBps(i). A position's
weight is amount * (BPS + boostBps) / BPS with BPS = 10000, so the
multiplier is 1 + boostBps / 10000: the four tiers read 0, 10000,
30000 and 70000 for 1x, 2x, 4x and 8x. Boost is snapshotted when the position
opens. Rewards arrive in the quote assets fees were collected in, not as MOTO
emissions.
The essentials
stake(amount, durationOption)opens a position at the chosen tier.appendStake(amount)adds to an existing position; the remaining lock is extended by a weighted average (old money keeps its remaining clock, new money carries a full lock). The lock never shortens, and a dust append never re-locks a matured position.- Principal vests linearly over the lock;
requestUnstakemoves vested principal into a 30-day linear drip you claim from as it arrives. - Reward claims have no cooldown.
Vault metadata is served at GET /stake/vaults. Per-user positions and
pending rewards are chain-direct reads. There is no per-user stake
endpoint on the API.
Reading a position
// One position per wallet. Returns a 7-field tuple.
const [amount, original, durationOption, start, boostBps, duration, availableFloor] =
await publicClient.readContract({
address: motoStaking,
abi: parseAbi([
"function positions(address) view returns (uint256 amount, uint256 original, uint8 durationOption, uint64 start, uint256 boostBps, uint256 duration, uint256 availableFloor)",
]),
functionName: "positions",
args: [user],
});
// Accrued reward for one reward token.
const owed = await publicClient.readContract({
address: motoStaking,
abi: parseAbi(["function pendingReward(address account, address token) view returns (uint256)"]),
functionName: "pendingReward",
args: [user, weth],
});duration is the lock length snapshotted at stake time, and it is what
vesting is computed against, not whatever the tier ladder says today. A later
change to the ladder therefore cannot retroactively re-vest an open position.
boostBps is snapshotted for the same reason.
There is no getPositions and no pendingClaim on this contract. If you find
either in older material, it is wrong.
Write surface
stake(uint256 amount, uint8 durationOption)- approve MOTO toMotoStakingfirst. One position per wallet: a secondstakerevertsPositionExists(), useappendStakeappendStake(uint256 amount)- one argument; the tier is inherited from the open positionrequestUnstake(uint256 amount)claimUnstaked()drains every matured ticket;claimUnstakedFrom(uint256 maxTickets)bounds the walk when a wallet has manyclaim(address token), the no-argclaim()that settles every reward token at once, andclaimAsMoto(address token, uint256 minMotoOut, address[] path, uint256 deadline)(note the trailingdeadline).pathmust runtokento MOTO, the swap goes through theFeeRouterand pays the protocol fee, andminMotoOutis checked against the MOTO that actually reached you (BelowMinMotoOut)
Operators: purgeRewardToken takes (address token, uint256 maxResidual),
with maxResidual capped at MAX_PURGE_RESIDUAL. In the current source,
reward tokens are an owner-curated list (addRewardToken), and
notifyReward reverts RewardTokenNotApproved(token) for anything else.
addRewardToken is not on the deployed implementation yet. It arrives with a
contract upgrade.
Full function/event surface: reference/contracts-full-inventory.