# #5 - BUIDL an Auto-compounding Bot on Saber

**Authors:** [@wei\_sol\_](https://twitter.com/wei_sol_), [@ironaddicteddog](https://twitter.com/ironaddicteddog)

***\[Updated at 2022.3.31]***

> **See the example repo** [**here**](https://github.com/DappioWonderland/auto-compounding-bot)

## TL; DR

* Build SDKs only using @solana/web3.js
* Learn how to interact with Solana
* Builld an auto-compounding bot with SDK

## Introduction

Solana : Solana is a fast, low cost, decentralized blockchain with thousands of projects spanning DeFi, NFTs, Web3 and more.

Saber : Saber is a Curve-like AMM provider on Solana, support all kinds of stablecoin pair from various bridges

### Solana 101

Account : Everything on Solana is an account \* Accounts can only be owned by programs \* Every Account is like a file in a computer \* Accounts are used to store state \* Only the account owner may debit an account and adjust its data \* All accounts to be written to or read must be passed into `Insructions` \* Developers should use the data field to save data inside accounts [![](https://hackmd.io/_uploads/HkuU8iQx9.png)](https://paulx.dev/blog/2021/01/14/programming-on-solana-an-introduction) >Image from <https://paulx.dev/blog/2021/01/14/programming-on-solana-an-introduction/>

![](https://hackmd.io/_uploads/HyG_Ee_Gq.jpg)

> Image from <https://explorer.solana.com/address/6ZRCB7AAqGre6c72PRz3MHLC73VMYvJ8bi9KHf1HFpNk>

Program : `Program` is just an account with excutable enable \* Solana programs are stateless \* Designed be upgradable (`BPF loader 2`)

![](https://hackmd.io/_uploads/HkZ2Hl_Mc.png)

> Image from <https://explorer.solana.com/address/TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA>

Program Account : An Account owned by a `Program` other than `System program` \* Data struct are very different from program to program

![](https://hackmd.io/_uploads/BytC5xuG9.png)

> Image from <https://explorer.solana.com/address/EzkjmZFzWccf2DRQ6uafahvfZh29ntwAmej8nDQ5tY1C>

Keyair : A byte array that contain `Public key` and `Private key` \* first 32 bytes are use as `Private key` \* last 32 bytes are generated by `Private key` and use as `Public key`

> \[26,151,18,191,115,212,220,144,52,66,74,133,251,235,69,161,254,121,70,227,171,227,17,170,154,227,32,151,40,125,37,158,0,94,253,210,209,242,208,122,162,84,158,36,211,63,243,252,104,36,58,243,120,134,127,132,193,186,63,50,0,230,93,200] 12T1dsupQBqwgQYsXWqhhmzQgreRtvkP95W8rW3Pk23R

Address : A `Public key` encoded in Base58

Rent : Pay for space to store data on Solana \* If `Rent` is paid for over 2 years, the account become `rent-exempt`

![](https://hackmd.io/_uploads/Hk9S9g_Gq.png)

> Image from <https://explorer.solana.com/address/8UviNr47S8eL6J3WfDxMRa3hvLta1VDJwNWqsDgtN3Cv>

RPC : The endpoint to read/write from/to Solana \* Free RPCs can be slow or congested when there is high demand.

PDA (Program Derived Address) : A account without a `Private key` that only can be signed by a `Program` \* PDA is generated by hashing a seed with the Program address

Instruction : The one and only way to interact with `Programs` \* All accounts used when processing should be write into the `Instruction`

![](https://hackmd.io/_uploads/B1j0deuM9.png)

> Raw instruction from <https://explorer.solana.com/tx/v2s26c5vBzrc7rTEyEsCreBDHaVd41iJ8F4j1gLFAy1QA5q8tqJex2Y2h3xsDmJpU1R9wAyRqziyzNB85cgxFjh>

![](https://hackmd.io/_uploads/H1QddedM5.png)

> Serialized instruction <https://explorer.solana.com/tx/37oPunF5tLw6DEqQUk4gMHeb6n1wdCAPWBF7e291znWwPXW7gCvPh9Srv5m5UMoxZoHSMR4BcQau1RnmU5yQeWKj>

Tx (Transaction) : A message send to `RPC` that contains one or more `Instructions`, Signatures and a Fee Payer \* Every Tx have a size limit of 1232 bytes \* A Tx can be signed by differents accounts \* Fee are determined by the amount of Signer at the moment

![](https://hackmd.io/_uploads/SkBRYldz5.png)

> Image from <https://explorer.solana.com/tx/v2s26c5vBzrc7rTEyEsCreBDHaVd41iJ8F4j1gLFAy1QA5q8tqJex2Y2h3xsDmJpU1R9wAyRqziyzNB85cgxFjh>

SPL ([Solana Program Library](https://github.com/solana-labs/solana-program-library)) : A example library organized by Solana lab \* SPL is not a token protocol on Solana, spl-token is.

ATA (Associated Token Account) : A Token Account which is a `PDA` created by [Associated Token Program](https://github.com/solana-labs/solana-program-library/tree/master/associated-token-account/) \* There is only one ATA with every wallet and a token mint

![](https://hackmd.io/_uploads/S1e-3OBgc.png)

> Image from [白上フブキ.eth](https://t.me/fakefubuki)

### Solana system model

![](https://hackmd.io/_uploads/HJ7vkYHl5.png)

## Overview

* Learn Solana basic from building a SDK
* Build a bot that collect, sell, reinvest the yield from LP farming

## Architecture

![](https://hackmd.io/_uploads/HkEhbHHgq.png)

### File Structure

```
├── 📂 raydium
│   │
│   ├── 📄 ids.ts
│   │
│   ├── 📄 index.ts
│   │
│   ├── 📄 infos.ts
│   │
│   ├── 📄 instructions.ts
│   │
│   ├── 📄 layouts.ts
│   │
│   └── 📄 transactions.ts
│
│
├── 📂 saber
│   │
│   ├── 📄 ids.ts
│   │
│   ├── 📄 index.ts
│   │
│   ├── 📄 infos.ts
│   │
│   ├── 📄 instructions.ts
│   │
│   ├── 📄 layouts.ts
│   │
│   └── 📄 transactions.ts
│
│
│── 📄 index.ts
│
│── 📄 utils.ts
│
│── 📄 package.json
│
│── 📄 tsconfig.json
│
└── ...

```

## Setup

### Install Rust and Solana

```bash
$ sh -c "$(curl -sSfL https://release.solana.com/v1.9.8/install)"
...
```

> See <https://hackmd.io/@ironaddicteddog/solana-starter-kit#Install-Rust-and-Solana-Cli> for more details.

### Recover your Wallet (Using Phantom)

![](https://hackmd.io/_uploads/H1IY6ASg5.png)

FIrst, click **Show Secret Reconvery Phrase** and **copy your recovery phrase at this point**.

Next, let's recover the wallet locally:

```bash=
$ solana-keygen recover 'prompt:?key=0/0' -o ~/.config/solana/solmeet-keypair-1.json
```

There should be a prompt asking for entering the recovery phrase in yout terminal. **Paste your recovery phrase at this point**.

* Set keypair

```bash=
$ solana config set --keypair ~/.config/solana/solmeet-keypair-1.json
```

### Config to `solana-mf`

```bash=
$ solana config set --url https://rpc-mainnet-fork.dappio.xyz
$ solana config set --ws wss://rpc-mainnet-fork.dappio.xyz/ws
$ solana config set --commitment processed
$ solana airdrop 1
```

### Scaffold

```bash=
$ mkdir solmeet-5-bot
$ cd solmeet-5-bot
$ tsc --init
```

```bash=
$ touch {index.ts,utils.ts}
$ mkdir saber && touch saber/{index.ts,ids.ts,layouts.ts,infos.ts,instructions.ts,transactions.ts}
$ mkdir raydium && touch raydium/{index.ts,ids.ts,layouts.ts,infos.ts,instructions.ts,transactions.ts}
```

#### Add `package.json`

```json=
{
  "name": "solmeet-5-bot",
  "version": "1.0.0",
  "description": "",
  "main": "./index.ts",
  "scripts": {
    "start": "ts-node ./index.ts"
  },
  "dependencies": {
    "@project-serum/borsh": "^0.2.5",
    "@project-serum/serum": "^0.13.61",
    "@solana/buffer-layout": "^4.0.0",
    "@solana/spl-token": "^0.2.0",
    "@solana/web3.js": "^1.35.0",
    "bignumber.js": "^9.0.1",
    "buffer-layout": "^1.2.2",
    "js-sha256": "^0.9.0"
  },
  "devDependencies": {
    "@project-serum/borsh": "^0.2.5",
    "@solana/web3.js": "^1.35.0",
    "@types/express": "^4.17.13",
    "@types/node": "^17.0.18",
    "buffer-layout": "^1.2.2",
    "ts-node": "^10.5.0",
    "typescript": "^4.5.5"
  }
}
```

#### Install Dependencies

```
$ yarn
```

## Part 1: Implement Common Modules

![](https://hackmd.io/_uploads/H1IXlGLgc.png)

In this part, we will implement the common modules for the bot:

* `ids.ts`
* `layouts.ts`
* `utils.ts`

### `ids.ts`

In Solana, **execution (programs) and states are decoupled**. As a result, we have to be very clear on the scope of the the programs and states:

#### `saber/ids.ts`

```typescript=
import { PublicKey } from "@solana/web3.js";

export const SBR_MINT = new PublicKey("Saber2gLauYim4Mvftnrasomsv6NvAuncvMEZwcLpD1");
export const USDC_UST_POOL = new PublicKey("KwnjUuZhTMTSGAaavkLEmSyfobY16JNH4poL9oeeEvE");
export const ADMIN_KEY = new PublicKey("H9XuKqszWYirDmXDQ12TZXGtxqUYYn4oi7FKzAm7RHGc");
export const SWAP_PROGRAM_ID = new PublicKey("SSwpkEEcbUqx4vtoEByFjSkhKdCT862DNVb52nZg1UZ");
export const SABER_WRAP_PROGRAM_ID = new PublicKey("DecZY86MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB");
export const SABER_QUARRY_REWARDER = new PublicKey("rXhAofQCT7NN9TUqigyEAUzV1uLL4boeD8CRkNBSkYk");
export const QURARRY_MINE_PROGRAM_ID = new PublicKey("QMNeHCGYnLVDn1icRAfQZpjPLBNkfGbSKRB83G5d8KB");
export const SABER_MINT_WRAPPER = new PublicKey("EVVDA3ZiAjTizemLGXNUN3gb6cffQFEYkFjFZokPmUPz");
export const QURARRY_MINT_WRAPPER = new PublicKey("QMWoBmAyJLAsA1Lh9ugMTw2gciTihncciphzdNzdZYV");
export const SABER_FARM_MINTER = new PublicKey("GEoTC3gN12qHDniaDD7Zxvd5xtcZyEKkTPy42B44s82y");
export const IOU_TOKEN_MINT = new PublicKey("iouQcQBAiEXe6cKLS85zmZxUqaCqBdeHFpqKoSz615u");
export const CLAIM_FEE_TOKEN_ACCOUNT = new PublicKey("4Snkea6wv3K6qzDTdyJiF2VTiLPmCoyHJCzAdkdTStBK");
export const SABER_TOKEN_MINT = new PublicKey("Saber2gLauYim4Mvftnrasomsv6NvAuncvMEZwcLpD1");
export const MINTER_PROGRAM_ID = new PublicKey("RDM23yr8pr1kEAmhnFpaabPny6C9UVcEcok3Py5v86X");
export const DEPRECATED_POOLS = [
  new PublicKey("LeekqF2NMKiFNtYD6qXJHZaHx4hUdj4UiPu4t8sz7uK"),
  new PublicKey("2jQoGQRixdcfuRPt9Zui7pk6ivnrQv79mf8h13Tyoa9K"),
  new PublicKey("SPaiZAYyJBQHaSjtxFBKtLtQiCuG328r1mTfmvvydR5"),
  new PublicKey("HoNG9Z4jsA1qtkZhDRYBc67LF2cbusZahjyxXtXdKZgR"),
  new PublicKey("4Fss9Dy3vAUBuQ4SyEZz4vcLxeQqoFLZjdXhEUr3wqz3")
]
```

#### `raydium/ids.ts`

```typescript=
import { PublicKey } from "@solana/web3.js";

export const SBR_AMM_ID = new PublicKey("5cmAS6Mj4pG2Vp9hhyu3kpK9yvC7P6ejh9HiobpTE6Jc")
export const LIQUIDITY_POOL_PROGRAM_ID_V3 = new PublicKey('27haf8L6oxUeXrHrgEgsexjSY5hbVUWEmvv9Nyxg8vQv')
export const LIQUIDITY_POOL_PROGRAM_ID_V4 = new PublicKey('675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8')
export const STAKE_PROGRAM_ID = new PublicKey('EhhTKczWMGQt46ynNeRX1WfeagwwJd7ufHvCDjRxjo5Q')
export const STAKE_PROGRAM_ID_V5 = new PublicKey('9KEPoZmtHUrBbhWN1v1KWLMkkvwY6WLtAVUCPRtRjP4z')
export const AMM_AUTHORITY = new PublicKey("5Q544fKrFoe6tsEbD7S8EmxGTJYAKtTVhAW5Q5pge4j1")
```

### `layouts.ts`

`layouts` play an important role in both reading and writing on-chain data.

* **Reading**: It indicates how the stored bytes is arranged and what their types are.
* **Writing**: It indicates how the instruction data should be assembled to call a certain program.

#### `saber/layouts.ts`

```typescript=
import { publicKey, struct, u64, u128, u8, u16, i64, bool } from "@project-serum/borsh";

export const FARM_LAYOUT = struct([
  publicKey("rewarderKey"),
  publicKey("tokenMintKey"),
  u8("bump"),
  u16("index"),
  u8("tokenMintDecimals"),
  i64("famineTs"),
  i64("lastUpdateTs"),
  u128("rewardsPerTokenStored"),
  u64("annualRewardsRate"),
  u64("rewardsShare"),
  u64("totalTokensDeposited"),
  u64("numMiners"),
]);

export const MINER_LAYOUT = struct([
  publicKey("farmKey"),
  publicKey("owner"),
  u8("bump"),
  publicKey("vault"),
  u64("rewardsEarned"),
  u128("rewardsPerTokenPaid"),
  u64("balance"),
  u64("index"),
]);

export const SWAPINFO_LAYOUT = struct([
  bool("isInitialized"),
  bool("isPaused"),
  u8("nonce"),
  u64("initialAmpFactor"),
  u64("targetAmpFactor"),
  i64("startRampTs"),
  i64("stopRampTs"),
  i64("futureAdminDeadline"),
  publicKey("futureAdminKey"),
  publicKey("adminKey"),
  publicKey("tokenAccountA"),
  publicKey("tokenAccountB"),
  publicKey("poolMint"),
  publicKey("mintA"),
  publicKey("mintB"),
  publicKey("adminFeeAccountA"),
  publicKey("adminFeeAccountB"),
]);

export const WRAPINFO_LAYOUT = struct([
  u8("decimal"),
  u64("multiplyer"),
  publicKey("underlyingWrappedTokenMint"),
  publicKey("underlyingTokenAccount"),
  publicKey("wrappedTokenMint"),
]);

export const DEPOSIT_LAYPOUT = struct([
  u8('instruction'),
  u64('AtokenAmount'),
  u64('BtokenAmount'),
  u64('minimalRecieve'),
]);

export const WITHDRAW_LAYOUT = struct([
  u8('instruction'),
  u64('LPtokenAmount'),
  u64('minimalRecieve'),
]);

export const WRAP_LAYOUT = struct([
  u64('amount'),
]);

export const UNWRAP_LAYOUT = struct([
  u64('amount'),
]);

export const DEPOSIT_TO_FARM_LAYOUT = struct([
  u64('amount'),
]);

export const CREATE_MINER_LAYOUT = struct([
  u64('amount'),
]);

export const WITHDRAW_FROM_FARM_LAYOUT = struct([
  u64('amount'),
]);
```

#### `raydium/layouts.ts`

```typescript=
import { publicKey, struct, u8, u64, u128 } from "@project-serum/borsh";

export const SWAP_LAYOUT = struct([
  u8('instruction'),
  u64('amountIn'),
  u64('minAmountOut')
]);

export const ADD_LIQUIDITY_LAYOUT = struct([
  u8('instruction'),
  u64('maxCoinAmount'),
  u64('maxPcAmount'),
  u64('fixedFromCoin')
]);

export const REMOVE_LIQUIDITY_LAYOUT = struct([
  u8('instruction'),
  u64('amount')
]);

export const AMM_INFO_LAYOUT_V4 = struct([
  u64("status"),
  u64("nonce"),
  u64("orderNum"),
  u64("depth"),
  u64("coinDecimals"),
  u64("pcDecimals"),
  u64("state"),
  u64("resetFlag"),
  u64("minSize"),
  u64("volMaxCutRatio"),
  u64("amountWaveRatio"),
  u64("coinLotSize"),
  u64("pcLotSize"),
  u64("minPriceMultiplier"),
  u64("maxPriceMultiplier"),
  u64("systemDecimalsValue"),
  // Fees
  u64("minSeparateNumerator"),
  u64("minSeparateDenominator"),
  u64("tradeFeeNumerator"),
  u64("tradeFeeDenominator"),
  u64("pnlNumerator"),
  u64("pnlDenominator"),
  u64("swapFeeNumerator"),
  u64("swapFeeDenominator"),
  // OutPutData
  u64("needTakePnlCoin"),
  u64("needTakePnlPc"),
  u64("totalPnlPc"),
  u64("totalPnlCoin"),
  u128("poolTotalDepositPc"),
  u128("poolTotalDepositCoin"),
  u128("swapCoinInAmount"),
  u128("swapPcOutAmount"),
  u64("swapCoin2PcFee"),
  u128("swapPcInAmount"),
  u128("swapCoinOutAmount"),
  u64("swapPc2CoinFee"),
  publicKey("poolCoinTokenAccount"),
  publicKey("poolPcTokenAccount"),
  publicKey("coinMintAddress"),
  publicKey("pcMintAddress"),
  publicKey("lpMintAddress"),
  publicKey("ammOpenOrders"),
  publicKey("serumMarket"),
  publicKey("serumProgramId"),
  publicKey("ammTargetOrders"),
  publicKey("poolWithdrawQueue"),
  publicKey("poolTempLpTokenAccount"),
  publicKey("ammOwner"),
  publicKey("pnlOwner"),
]);
```

### `utils.ts`

Copy [this code snippet](https://raw.githubusercontent.com/DappioWonderland/auto-compounding-bot/main/utils.ts) to `utils.ts`.

#### `getAnchorInsByIdl`

Calculate **Anchor Identifier**:

```typescript=
// Example

function getAnchorInsByIdl(name: string): Buffer {
  const SIGHASH_GLOBAL_NAMESPACE = "global";
  const preimage = `${SIGHASH_GLOBAL_NAMESPACE}:${name}`;
  const hash = sha256.sha256.digest(preimage)
  const data = Buffer.from(hash).slice(0, 8)
  return data;
}
```

## Part 2: Implement "Read" Modules

![](https://hackmd.io/_uploads/HyymgM8x5.png)

* **`DataSizeFilter` and `MemCmpFilter`**

```typescript=
// Example

const adminIdMemcmp: MemcmpFilter = {
  memcmp: {
    offset: 8,
    bytes: rewarderKey.toString(),
  }
};

const sizeFilter: DataSizeFilter = {
  dataSize: 140
}
const filters = [adminIdMemcmp, sizeFilter];
const config: GetProgramAccountsConfig = { filters };
const allFarmAccount = await connection.getProgramAccounts(QURARRY_MINE_PROGRAM_ID, config);
```

### `infos.tx`

Copy [this code snippet](https://raw.githubusercontent.com/DappioWonderland/auto-compounding-bot/main/saber/infos.ts) to `saber/infos.ts` and [this code snippet](https://raw.githubusercontent.com/DappioWonderland/auto-compounding-bot/main/raydium/infos.ts) to `raydium/infos.ts`.

### `index.ts`

Copy [this code snippet](https://raw.githubusercontent.com/DappioWonderland/auto-compounding-bot/main/saber/index.ts) to `saber/index.ts` and [this code snippet](https://raw.githubusercontent.com/DappioWonderland/auto-compounding-bot/main/raydium/index.ts) to `raydium/index.ts`.

## Part 3: Implement "Write" Modules

![](https://hackmd.io/_uploads/S1mmxMIg9.png)

* Need to add Anchor identifier manually
* One single Solana tx includes multiple ixs
  * Solana ix = Ethereum tx
  * Solana tx = Ethereum multicall

### `instructions.ts`

Copy [this code snippet](https://raw.githubusercontent.com/DappioWonderland/auto-compounding-bot/main/saber/instructions.ts) to `saber/instructions.ts` and [this code snippet](https://raw.githubusercontent.com/DappioWonderland/auto-compounding-bot/main/raydium/instructions.ts) to `raydium/instructions.ts`.

### `transactions.ts`

Copy [this code snippet](https://raw.githubusercontent.com/DappioWonderland/auto-compounding-bot/main/saber/transactions.ts) to `saber/transactions.ts` and [this code snippet](https://raw.githubusercontent.com/DappioWonderland/auto-compounding-bot/main/raydium/transactions.ts) to `raydium/transactions.ts`.

## Part 4: Implement the Auto-compounding Bot

![](https://hackmd.io/_uploads/rksXxGLgq.png)

* Load Keypair
* Claim All mining rewards
* Swap all SBR to USDC
* Add all USDC swapped out to USDC-UST pool
* Deposit all LP to farming

### `index.ts`

```typescript=
import os from "os";
import fs from "fs";
import BN from "bn.js";
import { Connection, Keypair } from "@solana/web3.js";
import * as raydium from "./raydium";
import { SBR_AMM_ID } from "./raydium/ids";
import * as saber from "./saber";
import { SBR_MINT, USDC_UST_POOL } from "./saber/ids";
import * as utils from "./utils";

// Load keypair
const keyPairPath = `${os.homedir()}/.config/solana/solmeet-keypair-1.json`;
const privateKeyUint8Array = JSON.parse(fs.readFileSync(keyPairPath, "utf-8"));
const privateKey = Uint8Array.from(privateKeyUint8Array);
const wallet = Keypair.fromSecretKey(privateKey);

async function main() {
  const conn = new Connection("https://rpc-mainnet-fork.dappio.xyz", { wsEndpoint: "wss://rpc-mainnet-fork.dappio.xyz/ws", commitment: "processed", });
  // const connection = new Connection("https://solana-api.tt-prod.net", { commitment: "processed", });
  console.log("Fetching all Saber pools...");
  const swaps = await saber.getAllSwaps(conn);
  console.log("Fetching all Saber miners...");
  const miners = await saber.getAllMiners(conn, wallet.publicKey);
  console.log("Fetching Saber AMM pool on Raydium...");
  const sbrAmm = (await raydium.getAmmPool(SBR_AMM_ID, conn));

  // Claim All mining rewards
  console.log("Claiming all mining rewards...")
  for (const miner of miners) {
    for (const swap of swaps) {
      if (miner.farmKey.toString() === swap.farmingInfo?.infoPubkey.toString()) {
        if (miner.balance.toNumber() > 0) {
          // Create claimRewardTx
          const claimRewardTx = await saber.claimRewardTx(swap.farmingInfo as saber.FarmInfo, wallet.publicKey, conn)
          // Send Tx
          const result = await utils.signAndSendAll(claimRewardTx, conn, wallet)
          console.log(miner.getUnclaimedRewards(swap), "SBR reward claimed. Tx:", result);
        }
      }
    }
  }

  let tokenAccounts = await utils.getAllTokenAccount(wallet.publicKey, conn);
  let swapOutAmount = new BN(0);

  // Swap all SBR to USDC
  console.log("Swapping all SBR to USDC...")
  for (const token of tokenAccounts) {
    if (token.mint === SBR_MINT && token.amount.cmpn(0)) {
      swapOutAmount = await (await sbrAmm.calculateSwapOutAmount("coin", token.amount, conn)).divn(0.98);
      if (!swapOutAmount.cmpn(1)) {
        break;
      }
      const swapIx = await raydium.swap(sbrAmm, token.mint, sbrAmm.pcMintAddress, wallet.publicKey, token.amount, new BN(0), conn);
      const result = await utils.signAndSendAll(swapIx, conn, wallet);
      console.log(token.amount.toNumber() / 1000000, "SBR swapped. Tx:", result);
    }
  }

  // Add all USDC swapped out to USDC-UST pool
  console.log("Adding all USDC swapped out to USDC-UST pool...")
  for (const swap of swaps) {
    if (swap.infoPublicKey === USDC_UST_POOL) {
      const addLP = await saber.createDepositTx(swap, new BN(0), swapOutAmount, new BN(0), wallet.publicKey, conn)
      const result = await utils.signAndSendAll(addLP, conn, wallet)
      console.log("LP reinvested. Tx:", result);
    }
  }

  // Deposit all LP to farming
  console.log("Depositing all LP to farming...")
  tokenAccounts = await utils.getAllTokenAccount(wallet.publicKey, conn)
  for (const swap of swaps) {
    for (const token of tokenAccounts) {
      if (token.mint.toString() === swap.poolMint.toString() && token.amount.cmpn(0)) {
        // Create farmIx
        const farmIx = await saber.depositToFarm(swap.farmingInfo as saber.FarmInfo, wallet.publicKey, token.amount, conn)
        // Send Tx
        const result = await utils.signAndSendAll(farmIx, conn, wallet)
        console.log("Farm deposited. Tx:", result);
      }
    }
  }
}

async function run() {
  try {
    main();
  }
  catch (e) {
    console.error(e);
  }
}

run();
```

Finally, let's run the bot:

```
$ yarn start
Fetching all Saber pools...
Fetching all Saber miners...
Fetching Saber AMM pool on Raydium...
Claiming all mining rewards...
0.008659 SBR reward claimed. Tx: 5xCibLpPokio4YHTwVZ35VM8fG8cww8Ris52E1D1qr2F8VSgdJsrHoBJGRDE77p61kXU3UwTMYKtEP3VB2fj1tFM
0 SBR reward claimed. Tx: 2mwEVHKwQwG384JM9ys9iAoKjRnZVswyQRC4hip44nHPP2uJvkA1YUwLfrDn9bk84GxUpPyZjpjzkxZ2ENmfQPb4
Swapping all SBR to USDC...
Adding all USDC swapped out to USDC-UST pool...
Depositing all LP to farming...
✨  Done in 121.22s.
```

## References

* <https://github.com/DappioWonderland/auto-compounding-bot>
* <https://hackmd.io/@ironaddicteddog/solana-starter-kit>
* <https://hackmd.io/@ironaddicteddog/solana-anchor-escrow>
