Skip to content

Commit

Permalink
feat: add accountsToSign parameter to txs + Meteora example (#90)
Browse files Browse the repository at this point in the history
* Add accountsToSign parameter to txs + Meteora example

* Add changeset

* Regenerate lockfile
  • Loading branch information
0xaguspunk authored Dec 17, 2024
1 parent fe6fea5 commit ab3b1dc
Show file tree
Hide file tree
Showing 13 changed files with 671 additions and 3 deletions.
4 changes: 4 additions & 0 deletions goat.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@
"name": "[Plugin] 🪐 jupiter",
"path": "./typescript/packages/plugins/jupiter"
},
{
"name": "[Plugin] ☄️ meteora",
"path": "./typescript/packages/plugins/meteora"
},
{
"name": "[Plugin] 🪙 spl token",
"path": "./typescript/packages/plugins/spl-token"
Expand Down
6 changes: 6 additions & 0 deletions typescript/.changeset/short-trees-heal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@goat-sdk/plugin-meteora": patch
"@goat-sdk/wallet-solana": patch
---

Add accountsToSign to Solana tx
39 changes: 39 additions & 0 deletions typescript/packages/plugins/meteora/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "@goat-sdk/plugin-meteora",
"version": "0.2.0",
"files": ["dist/**/*", "README.md", "package.json"],
"scripts": {
"build": "tsup",
"clean": "rm -rf dist",
"test": "vitest run --passWithNoTests"
},
"sideEffects": false,
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"dependencies": {
"@coral-xyz/anchor": "0.30.1",
"@goat-sdk/core": "workspace:*",
"@goat-sdk/wallet-solana": "workspace:*",
"@meteora-ag/dlmm": "1.3.4",
"@solana/web3.js": "catalog:",
"bn.js": "5.2.1",
"zod": "catalog:"
},
"peerDependencies": {
"@goat-sdk/core": "workspace:*"
},
"homepage": "https://ohmygoat.dev",
"repository": {
"type": "git",
"url": "git+https://github.com/goat-sdk/goat.git"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/goat-sdk/goat/issues"
},
"keywords": ["ai", "agents", "web3"],
"devDependencies": {
"@types/bn.js": "5.1.6"
}
}
2 changes: 2 additions & 0 deletions typescript/packages/plugins/meteora/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./meteora.plugin";
export * from "./parameters";
12 changes: 12 additions & 0 deletions typescript/packages/plugins/meteora/src/meteora.plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { type Chain, PluginBase } from "@goat-sdk/core";
import { MeteoraService } from "./meteora.service";

export class MeteoraPlugin extends PluginBase {
constructor() {
super("meteora", [new MeteoraService()]);
}

supportsChain = (chain: Chain) => chain.type === "solana";
}

export const meteora = () => new MeteoraPlugin();
61 changes: 61 additions & 0 deletions typescript/packages/plugins/meteora/src/meteora.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Tool } from "@goat-sdk/core";
import { SolanaWalletClient } from "@goat-sdk/wallet-solana";
import DLMM, { StrategyType } from "@meteora-ag/dlmm";
import { Keypair, PublicKey, Transaction } from "@solana/web3.js";
import { BN } from "bn.js";
import { CreateDLMMPositionParameters } from "./parameters";

export class MeteoraService {
@Tool({
description: "Creates a position on the Meteora DEX.",
})
async createDLMMPosition(walletClient: SolanaWalletClient, parameters: CreateDLMMPositionParameters) {
const newPosition = new Keypair();
const user = new PublicKey(walletClient.getAddress());
const dlmmPool = await this.getDLMM(walletClient, parameters.poolAddress);
const activeBin = await this.getActiveBin(dlmmPool);
const activeBinPricePerToken = Number(activeBin.pricePerToken);
const TOKEN_X_DECIMALS = dlmmPool.tokenX.decimal;
const TOKEN_Y_DECIMALS = dlmmPool.tokenY.decimal;
const minBinId = activeBin.binId - 34;
const maxBinId = activeBin.binId + 34;

const totalXAmount = new BN(Number(parameters.amount) * 10 ** TOKEN_X_DECIMALS);

const totalYAmount = new BN(
Math.floor(Number(parameters.amount) * activeBinPricePerToken * 10 ** TOKEN_Y_DECIMALS),
);

const createPositionTx: Transaction = await dlmmPool.initializePositionAndAddLiquidityByStrategy({
positionPubKey: newPosition.publicKey,
user: user,
totalXAmount,
totalYAmount,
strategy: {
maxBinId,
minBinId,
strategyType: StrategyType.SpotBalanced,
},
});

try {
const { hash } = await walletClient.sendTransaction({
instructions: createPositionTx.instructions,
accountsToSign: [newPosition],
});
return hash;
} catch (error) {
throw new Error(`Failed to create position: ${JSON.stringify(error)}`);
}
}

private async getDLMM(walletClient: SolanaWalletClient, poolAddress: string) {
const dlmmPool = await DLMM.create(walletClient.getConnection(), new PublicKey(poolAddress));
return dlmmPool;
}

private async getActiveBin(dlmmPool: DLMM) {
const activeBin = await dlmmPool.getActiveBin();
return activeBin;
}
}
9 changes: 9 additions & 0 deletions typescript/packages/plugins/meteora/src/parameters.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { createToolParameters } from "@goat-sdk/core";
import { z } from "zod";

export class CreateDLMMPositionParameters extends createToolParameters(
z.object({
poolAddress: z.string().describe("The pool address"),
amount: z.string().describe("The amount of tokens to swap in the tokens base unit"),
}),
) {}
6 changes: 6 additions & 0 deletions typescript/packages/plugins/meteora/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"$schema": "https://json.schemastore.org/tsconfig",
"extends": "../../../tsconfig.base.json",
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
6 changes: 6 additions & 0 deletions typescript/packages/plugins/meteora/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { defineConfig } from "tsup";
import { treeShakableConfig } from "../../../tsup.config.base";

export default defineConfig({
...treeShakableConfig,
});
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class SolanaKeypairWalletClient extends SolanaWalletClient {
};
}

async sendTransaction({ instructions, addressLookupTableAddresses = [] }: SolanaTransaction) {
async sendTransaction({ instructions, addressLookupTableAddresses = [], accountsToSign = [] }: SolanaTransaction) {
const latestBlockhash = await this.connection.getLatestBlockhash();
const message = new TransactionMessage({
payerKey: this.#keypair.publicKey,
Expand All @@ -37,7 +37,7 @@ export class SolanaKeypairWalletClient extends SolanaWalletClient {
}).compileToV0Message(await this.getAddressLookupTableAccounts(addressLookupTableAddresses));
const transaction = new VersionedTransaction(message);

transaction.sign([this.#keypair]);
transaction.sign([this.#keypair, ...accountsToSign]);

const hash = await this.connection.sendTransaction(transaction, {
maxRetries: 5,
Expand Down
4 changes: 4 additions & 0 deletions typescript/packages/wallets/solana/src/SolanaWalletClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export abstract class SolanaWalletClient extends WalletClientBase {
} as const;
}

getConnection() {
return this.connection;
}

async balanceOf(address: string) {
const pubkey = new PublicKey(address);
const balance = await this.connection.getBalance(pubkey);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { TransactionInstruction } from "@solana/web3.js";
import type { Keypair, TransactionInstruction } from "@solana/web3.js";

export type SolanaTransaction = SolanaInstructionTransaction;

export type SolanaInstructionTransaction = {
instructions: TransactionInstruction[];
addressLookupTableAddresses?: string[];
accountsToSign?: Keypair[];
};
Loading

0 comments on commit ab3b1dc

Please sign in to comment.