M3M3 is a where top memecoin stakers compete to earn fee rewards from permanently-locked liquidity in the memecoin pool, transforming memecoins from a race to dump to a race to stake.
As a highly-configurable mechanism that can be used by launchpads, creators, and holders, we believe M3M3 has the potential to greatly incentivize both staking and trading, and in turn bring more sustainable revenue and dynamics to memecoins.
Getting Started
SDK:
NPM:
Program ID: FEESngU3neckdwib9X3KWqdL7Mjmqk9XNp3uh5JbP4KP
PDA seeds:
API
Mainnet API endpoint:
Devnet API endpoint:
API endpoint to get vault info from pool address:
1. Install Dependencies
2. Initialize StakeForFee Instance
3. To interact with StakeForFee
Stake
Get stake balance and claimable balance
Claim Fee
Unstake
Get unstake period (Seconds)
Cancel unstake
Withdraw
How to create a Memecoin Pool with M3M3
Code Example
This code example includes the steps to:
Mint a token
Create dynamic vault and pool
Create M3M3 fee vault
Lock user's LP to M3M3 fee vault
Note: Difference between locking liquidity via M3M3 vs directly locking liquidity in the Memecoin Pool
User lock and M3M3 lock use the same lock mechanism on a Dynamic AMM / Memecoin Pool. But users and M3M3 vaults have their own personal lock escrow account for the pool.
When a user permanently locks liquidity directly on the Memecoin Pool page, the user is locking the LP token to their OWN personal escrow account. Therefore, fees from locked liquidity go to the user's wallet.
When a user locks via the M3M3 creation process, the user is locking the LP to the M3M3 escrow account. Therefore, fees from locked liquidity go to the M3M3 vault, which then distributes fees to the top stakers.
npm i @meteora-ag/m3m3 @coral-xyz/anchor @solana/web3.js @solana/spl-token @solana/spl-token-registry
import StakeForFee from"@meteora-ag/m3m3";import { PublicKey } from"@solana/web3.js";import { Wallet, AnchorProvider } from"@project-serum/anchor";// Connection, Wallet, and AnchorProvider to interact with the networkconstmainnetConnection=newConnection("https://api.mainnet-beta.solana.com");constmockWallet=newWallet(newKeypair());constprovider=newAnchorProvider(mainnetConnection, mockWallet, { commitment:"confirmed",});// Alternatively, to use Solana Wallet AdapterconstpoolAddress=newPublicKey("G2MRSjNjCbFUmMf32Z1aXYhy6pc1ihLyYg6orKedyjJG");constm3m3=awaitStakeForFee.create(connection, poolAddress);
conststakeAmount=newBN(1_000*10**feeFarm.accountStates.tokenAMint.decimals); // 1,000 stake token (make sure you have enough balance in your wallet)conststakeTx=awaitfeeFarm.stake(stakeAmount,mockWallet.publicKey);conststakeTxHash=awaitprovider.sendAndConfirm(stakeTx); // Transaction hash
awaitfeeFarm.refreshStates(); // make sure to refresh states to get the latest dataconstuserEscrow=awaitfeeFarm.getUserStakeAndClaimBalance(mockWallet.publicKey);conststakeBalance=userStakeEscrow.stakeEscrow.stakeAmount.toNumber() /10**feeFarm.accountStates.tokenAMint.decimals;constclaimableFeeA=fromLamports(userStakeEscrow.unclaimFee.feeA ||0,feeFarm.accountStates.tokenAMint.decimals);constclaimableFeeB=fromLamports(userStakeEscrow.unclaimFee.feeB ||0,feeFarm.accountStates.tokenBMint.decimals);
constclaimFeeTx=awaitfeeVault.claimFee(mockWallet.publicKey,newBN(U64_MAX)); // second param is max amount, so usually we just put max number BN.js can supportconstclaimfeeTxHash=awaitprovider.sendAndConfirm(claimFeeTx); // Transaction hash
constunstakeKeyPair=newKeypair();constunstakeTx=awaitfeeVault.unstake(userEscrow.stakeEscrow.stakeAmount,unstakeKeyPair.publicKey,mockWallet.publicKey);unstakeTx.partialSign(unstakeKeyPair); // Make sure to partial sign unstakeKeypairconstunstakeTxHash=awaitprovider.sendAndConfirm(unstakeTx); // Transaction hash