# @meteora-ag/m3m3

Latest version **2.0.0** (published 2026-11-10) · ISC license · 0 weekly downloads

## Install

```sh
npm install @meteora-ag/m3m3
pnpm add @meteora-ag/m3m3
yarn add @meteora-ag/m3m3
bun add @meteora-ag/m3m3
```

## Health

**Score 70/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; recently updated; high maintenance score; high quality score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 2.0.0 |
| Published | 2026-11-10 |
| First published | 2024-12-04 |
| Weekly downloads | 0 |
| License | ISC |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 6 |
| Unpacked size | 2.6 MB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Maintainers | siong, lunarwillow, dannweeeee, senddageeand4get, bangyro, dicksonp |

## Links

- npm: https://www.npmjs.com/package/@meteora-ag/m3m3
- npm.io page: https://npm.io/package/@meteora-ag/m3m3

## Dependencies (6)

- [bn.js](https://npm.io/package/bn.js.md) ^5.2.1
- [decimal.js](https://npm.io/package/decimal.js.md) ^10.4.3
- [@solana/web3.js](https://npm.io/package/@solana/web3.js.md) ^1.98.0
- [@coral-xyz/borsh](https://npm.io/package/@coral-xyz/borsh.md) ^0.30.1
- [@coral-xyz/anchor](https://npm.io/package/@coral-xyz/anchor.md) 0.29.0
- [@solana/spl-token](https://npm.io/package/@solana/spl-token.md) ^0.4.8

## Recent versions

- 2.0.0 (latest) — 2026-11-10
- 1.0.6-rc.42 (next) — 2025-03-17
- 1.0.10 — 2025-05-19
- 1.0.9 — 2025-04-15
- 1.0.8 — 2025-03-20
- 1.0.7 — 2025-03-17
- 1.0.6-rc.41 — 2025-03-17
- 1.0.6-rc.40 — 2025-03-17
- 1.0.6-rc.39 — 2025-03-17
- 1.0.6-rc.38 — 2025-03-17
- 1.0.6-rc.37 — 2025-03-17
- 1.0.6-rc.36 — 2025-03-17
- 1.0.6-rc.35 — 2025-03-17
- 1.0.6-rc.34 — 2025-03-16
- 1.0.6-rc.32 — 2025-03-16
- … 37 more at https://npm.io/package/@meteora-ag/m3m3/versions

## README

# Meteora M3M3 SDK

<p align="center">
<img align="center" src="https://app.meteora.ag/icons/logo.svg" width="180" height="180" />
</p>
<br>

## Getting started

NPM: https://www.npmjs.com/package/@meteora-ag/m3m3

SDK: https://github.com/MeteoraAg/stake-for-fee-sdk

Discord: https://discord.com/channels/841152225564950528/864859354335412224

<hr>

## Install

1. Install deps

```
npm i @meteora-ag/m3m3 @coral-xyz/anchor @solana/web3.js @solana/spl-token @solana/spl-token-registry
```

2. Initialize StakeForFee instance

```ts
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 network
const mainnetConnection = new Connection("https://api.mainnet-beta.solana.com");
const mockWallet = new Wallet(new Keypair());
const provider = new AnchorProvider(mainnetConnection, mockWallet, {
  commitment: "confirmed",
});
// Alternatively, to use Solana Wallet Adapter

const poolAddress = new PublicKey(
  "G2MRSjNjCbFUmMf32Z1aXYhy6pc1ihLyYg6orKedyjJG"
);
const m3m3 = await StakeForFee.create(connection, poolAddress);
```

3. To interact with the StakeForFee

> **Deprecated**
>
> - `claimFee` and `unstake`. Use `claimFeeAndWithdrawFull` instead.
> - `createFeeVault`, `createFeeVaultWithParams`, `createFeeVaultInstructions`, `initializeStakeEscrow`, `getOrCreateStakeEscrowInstruction`, `stake`, `cancelUnstake` and `MIN_LOCK_ESCROW_CLAIM_FEE_DURATION`.

> **Warning**
>
> - Stop creating stake-for-fee vaults.
> - Do not create a DAMM lock escrow owned by a stake-for-fee vault PDA, or lock LP into one, e.g. with `AmmImpl.lockLiquidity` and the fee vault as owner.

- Full exit (deprecation)

Full exit for stakers after the program deprecation. Withdraw every ongoing unstake request, then claim all pending fees and withdraw the stake in one transaction with `claimFeeAndWithdrawFull`, then close the empty stake escrow to get the rent back. Once every staker has exited, anyone can close the vault.

```ts
// 1. Withdraw every ongoing unstake request. They no longer wait for the unstake lock duration.
// Once the stake escrow is closed, only unstake requests with zero amount are left, close them with `closeZeroUnstake` instead.
// Skip the ones created before program 0.1.1, they have no owner.
const { stakeEscrow: openStakeEscrow } =
  await feeFarm.getUserStakeAndClaimBalance(mockWallet.publicKey);
const unstakeList = await StakeForFee.getUnstakeByUser(
  connection,
  mockWallet.publicKey,
  feeFarm.feeVaultKey
);
for (const { publicKey: unstakeKey, account } of unstakeList) {
  if (openStakeEscrow) {
    const withdrawTx = await feeFarm.withdraw(unstakeKey, mockWallet.publicKey);
    await provider.sendAndConfirm(withdrawTx);
  } else if (!account.owner.equals(PublicKey.default)) {
    const closeZeroUnstakeTx = await StakeForFee.closeZeroUnstake(
      connection,
      unstakeKey,
      mockWallet.publicKey
    );
    await provider.sendAndConfirm(closeZeroUnstakeTx);
  }
}

// 2. Claim all pending fees, then withdraw the whole stake amount.
// Skip it when there is no stake amount and no pending fees.
await feeFarm.refreshStates();
const { stakeEscrow, unclaimFee } = await feeFarm.getUserStakeAndClaimBalance(
  mockWallet.publicKey
);
if (
  stakeEscrow &&
  (!stakeEscrow.stakeAmount.isZero() ||
    !unclaimFee.feeA.isZero() ||
    !unclaimFee.feeB.isZero())
) {
  const claimFeeAndWithdrawFullTx = await feeFarm.claimFeeAndWithdrawFull(
    mockWallet.publicKey
  );
  await provider.sendAndConfirm(claimFeeAndWithdrawFullTx);
}

// 3. Close the empty stake escrow, the rent goes back to the owner.
if (stakeEscrow) {
  const closeStakeEscrowTx = await feeFarm.closeStakeEscrow(
    mockWallet.publicKey
  );
  await provider.sendAndConfirm(closeStakeEscrowTx);
}

// 4. Close the vault, the top staker list and the full balance list, the rent goes to the vault creator. Anyone can close it.
// Precondition: all stake escrows of the vault are closed, and the vault has no stake amount, ongoing unstake amount or effective stake amount.
// The token vaults are not closed, so the locked fees stay permanently locked.
await feeFarm.refreshStates();
const { metrics, topStakerInfo } = feeFarm.accountStates.feeVault;
if (
  metrics.totalStakeEscrowCount.isZero() &&
  metrics.totalStakedAmount.isZero() &&
  metrics.ongoingTotalPartialUnstakeAmount.isZero() &&
  topStakerInfo.effectiveStakeAmount.isZero()
) {
  const closeVaultTx = await feeFarm.closeVault(mockWallet.publicKey); // Fee payer
  await provider.sendAndConfirm(closeVaultTx);
}
```

- Stake (deprecated)

```ts
const stakeAmount = new BN(
  1_000 * 10 ** feeFarm.accountStates.tokenAMint.decimals
); // 1,000 stake token (make sure you have enough balance in your wallet)
const stakeTx = await feeFarm.stake(stakeAmount, mockWallet.publicKey);
const stakeTxHash = await provider.sendAndConfirm(stakeTx); // Transaction hash
```

- Get stake balance and claimable balance

```ts
await feeFarm.refreshStates(); // make sure to refresh states to get the latest data
const userEscrow = await feeFarm.getUserStakeAndClaimBalance(
  mockWallet.publicKey
);
const stakeBalance =
  userStakeEscrow.stakeEscrow.stakeAmount.toNumber() /
  10 ** feeFarm.accountStates.tokenAMint.decimals;
const claimableFeeA = fromLamports(
  userStakeEscrow.unclaimFee.feeA || 0,
  feeFarm.accountStates.tokenAMint.decimals
);
const claimableFeeB = fromLamports(
  userStakeEscrow.unclaimFee.feeB || 0,
  feeFarm.accountStates.tokenBMint.decimals
);
```

- Claim fee and withdraw full

```ts
// Claims all pending stake token and quote token fees, then withdraws the whole stake amount without an unstake request.
// The stake token fee is transferred to the owner instead of being restaked.
// A stake escrow without stake amount can still claim its pending fees.
const claimFeeAndWithdrawFullTx = await feeVault.claimFeeAndWithdrawFull(
  mockWallet.publicKey
);
const claimFeeAndWithdrawFullTxHash = await provider.sendAndConfirm(
  claimFeeAndWithdrawFullTx
); // Transaction hash
```

- Claim Fee (deprecated, use `claimFeeAndWithdrawFull`)

```ts
const claimFeeTx = await feeVault.claimFee(
  mockWallet.publicKey,
  new BN(U64_MAX)
); // second param is max amount, so usually we just put max number BN.js can support
const claimfeeTxHash = await provider.sendAndConfirm(claimFeeTx); // Transaction hash
```

- Unstake (deprecated, use `claimFeeAndWithdrawFull`)

```ts
const unstakeKeyPair = new Keypair();
const unstakeTx = await feeVault.unstake(
  userEscrow.stakeEscrow.stakeAmount,
  unstakeKeyPair.publicKey,
  mockWallet.publicKey
);
unstakeTx.partialSign(unstakeKeyPair); // Make sure to partial sign unstakeKeypair
const unstakeTxHash = await provider.sendAndConfirm(unstakeTx); // Transaction hash
```

- Get unstake period (Seconds) (deprecated)

```ts
const unstakePeriodInSeconds =
  feeFarm.accountStates.feeVault.configuration.unstakeLockDuration.toNumber();
```

- Cancel unstake (deprecated)

```ts
const cancelUnstakeTx = await feeFarm.cancelUnstake(
  unstakeKeyPair.publicKey,
  mockWallet.publicKey
);
const cancelUnstakeTxHash = await provider.sendAndConfirm(cancelUnstakeTx);
```

- Withdraw

```ts
const withdrawTx = await feeFarm.withdraw(
  unstakeKeyPair.publicKey,
  mockWallet.publicKey
);
const withdrawTxHash = await provider.sendAndConfirm(withdrawTx);
```

- Close stake escrow

```ts
// Permissionless. Closes a stake escrow without stake amount, pending fees and ongoing unstake amount, the rent goes back to the owner.
// Empty it with `withdraw` and `claimFeeAndWithdrawFull` first.
const closeStakeEscrowTx = await feeFarm.closeStakeEscrow(
  stakeEscrowOwner, // Receives the rent
  mockWallet.publicKey // Fee payer, defaults to the owner
);
const closeStakeEscrowTxHash = await provider.sendAndConfirm(
  closeStakeEscrowTx
);
```

- Close zero unstake

```ts
// The owner closes an unstake request with zero amount, the rent goes back to the owner.
// Use it when the stake escrow is already closed, else `withdraw` closes the request too.
const unstakeList = await StakeForFee.getUnstakeByUser(
  connection,
  mockWallet.publicKey,
  feeVaultKey
);
for (const { publicKey: unstakeKey, account } of unstakeList) {
  if (!account.unstakeAmount.isZero()) continue;
  const closeZeroUnstakeTx = await StakeForFee.closeZeroUnstake(
    connection,
    unstakeKey,
    mockWallet.publicKey
  );
  await provider.sendAndConfirm(closeZeroUnstakeTx);
}
```

- Close vault

```ts
// Permissionless. Closes the fee vault, the top staker list and the full balance list, the rent goes to the vault creator.
// Precondition: all stake escrows of the vault are closed, and the vault has no stake amount, ongoing unstake amount or effective stake amount.
// The token vaults are not closed, so the locked fees stay permanently locked.
const closeVaultTx = await feeFarm.closeVault(mockWallet.publicKey); // Fee payer
const closeVaultTxHash = await provider.sendAndConfirm(closeVaultTx);
```

---
_Source: https://npm.io/package/@meteora-ag/m3m3 · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
