Skip to content

Commit ab3b1dc

Browse files
authored
feat: add accountsToSign parameter to txs + Meteora example (#90)
* Add accountsToSign parameter to txs + Meteora example * Add changeset * Regenerate lockfile
1 parent fe6fea5 commit ab3b1dc

File tree

13 files changed

+671
-3
lines changed

13 files changed

+671
-3
lines changed

goat.code-workspace

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@
5050
"name": "[Plugin] 🪐 jupiter",
5151
"path": "./typescript/packages/plugins/jupiter"
5252
},
53+
{
54+
"name": "[Plugin] ☄️ meteora",
55+
"path": "./typescript/packages/plugins/meteora"
56+
},
5357
{
5458
"name": "[Plugin] 🪙 spl token",
5559
"path": "./typescript/packages/plugins/spl-token"
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@goat-sdk/plugin-meteora": patch
3+
"@goat-sdk/wallet-solana": patch
4+
---
5+
6+
Add accountsToSign to Solana tx
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"name": "@goat-sdk/plugin-meteora",
3+
"version": "0.2.0",
4+
"files": ["dist/**/*", "README.md", "package.json"],
5+
"scripts": {
6+
"build": "tsup",
7+
"clean": "rm -rf dist",
8+
"test": "vitest run --passWithNoTests"
9+
},
10+
"sideEffects": false,
11+
"main": "./dist/index.js",
12+
"module": "./dist/index.mjs",
13+
"types": "./dist/index.d.ts",
14+
"dependencies": {
15+
"@coral-xyz/anchor": "0.30.1",
16+
"@goat-sdk/core": "workspace:*",
17+
"@goat-sdk/wallet-solana": "workspace:*",
18+
"@meteora-ag/dlmm": "1.3.4",
19+
"@solana/web3.js": "catalog:",
20+
"bn.js": "5.2.1",
21+
"zod": "catalog:"
22+
},
23+
"peerDependencies": {
24+
"@goat-sdk/core": "workspace:*"
25+
},
26+
"homepage": "https://ohmygoat.dev",
27+
"repository": {
28+
"type": "git",
29+
"url": "git+https://github.com/goat-sdk/goat.git"
30+
},
31+
"license": "MIT",
32+
"bugs": {
33+
"url": "https://github.com/goat-sdk/goat/issues"
34+
},
35+
"keywords": ["ai", "agents", "web3"],
36+
"devDependencies": {
37+
"@types/bn.js": "5.1.6"
38+
}
39+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from "./meteora.plugin";
2+
export * from "./parameters";
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { type Chain, PluginBase } from "@goat-sdk/core";
2+
import { MeteoraService } from "./meteora.service";
3+
4+
export class MeteoraPlugin extends PluginBase {
5+
constructor() {
6+
super("meteora", [new MeteoraService()]);
7+
}
8+
9+
supportsChain = (chain: Chain) => chain.type === "solana";
10+
}
11+
12+
export const meteora = () => new MeteoraPlugin();
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { Tool } from "@goat-sdk/core";
2+
import { SolanaWalletClient } from "@goat-sdk/wallet-solana";
3+
import DLMM, { StrategyType } from "@meteora-ag/dlmm";
4+
import { Keypair, PublicKey, Transaction } from "@solana/web3.js";
5+
import { BN } from "bn.js";
6+
import { CreateDLMMPositionParameters } from "./parameters";
7+
8+
export class MeteoraService {
9+
@Tool({
10+
description: "Creates a position on the Meteora DEX.",
11+
})
12+
async createDLMMPosition(walletClient: SolanaWalletClient, parameters: CreateDLMMPositionParameters) {
13+
const newPosition = new Keypair();
14+
const user = new PublicKey(walletClient.getAddress());
15+
const dlmmPool = await this.getDLMM(walletClient, parameters.poolAddress);
16+
const activeBin = await this.getActiveBin(dlmmPool);
17+
const activeBinPricePerToken = Number(activeBin.pricePerToken);
18+
const TOKEN_X_DECIMALS = dlmmPool.tokenX.decimal;
19+
const TOKEN_Y_DECIMALS = dlmmPool.tokenY.decimal;
20+
const minBinId = activeBin.binId - 34;
21+
const maxBinId = activeBin.binId + 34;
22+
23+
const totalXAmount = new BN(Number(parameters.amount) * 10 ** TOKEN_X_DECIMALS);
24+
25+
const totalYAmount = new BN(
26+
Math.floor(Number(parameters.amount) * activeBinPricePerToken * 10 ** TOKEN_Y_DECIMALS),
27+
);
28+
29+
const createPositionTx: Transaction = await dlmmPool.initializePositionAndAddLiquidityByStrategy({
30+
positionPubKey: newPosition.publicKey,
31+
user: user,
32+
totalXAmount,
33+
totalYAmount,
34+
strategy: {
35+
maxBinId,
36+
minBinId,
37+
strategyType: StrategyType.SpotBalanced,
38+
},
39+
});
40+
41+
try {
42+
const { hash } = await walletClient.sendTransaction({
43+
instructions: createPositionTx.instructions,
44+
accountsToSign: [newPosition],
45+
});
46+
return hash;
47+
} catch (error) {
48+
throw new Error(`Failed to create position: ${JSON.stringify(error)}`);
49+
}
50+
}
51+
52+
private async getDLMM(walletClient: SolanaWalletClient, poolAddress: string) {
53+
const dlmmPool = await DLMM.create(walletClient.getConnection(), new PublicKey(poolAddress));
54+
return dlmmPool;
55+
}
56+
57+
private async getActiveBin(dlmmPool: DLMM) {
58+
const activeBin = await dlmmPool.getActiveBin();
59+
return activeBin;
60+
}
61+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { createToolParameters } from "@goat-sdk/core";
2+
import { z } from "zod";
3+
4+
export class CreateDLMMPositionParameters extends createToolParameters(
5+
z.object({
6+
poolAddress: z.string().describe("The pool address"),
7+
amount: z.string().describe("The amount of tokens to swap in the tokens base unit"),
8+
}),
9+
) {}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"$schema": "https://json.schemastore.org/tsconfig",
3+
"extends": "../../../tsconfig.base.json",
4+
"include": ["src/**/*"],
5+
"exclude": ["node_modules", "dist"]
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { defineConfig } from "tsup";
2+
import { treeShakableConfig } from "../../../tsup.config.base";
3+
4+
export default defineConfig({
5+
...treeShakableConfig,
6+
});

typescript/packages/wallets/solana/src/SolanaKeypairWalletClient.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export class SolanaKeypairWalletClient extends SolanaWalletClient {
2828
};
2929
}
3030

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

40-
transaction.sign([this.#keypair]);
40+
transaction.sign([this.#keypair, ...accountsToSign]);
4141

4242
const hash = await this.connection.sendTransaction(transaction, {
4343
maxRetries: 5,

typescript/packages/wallets/solana/src/SolanaWalletClient.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ export abstract class SolanaWalletClient extends WalletClientBase {
2121
} as const;
2222
}
2323

24+
getConnection() {
25+
return this.connection;
26+
}
27+
2428
async balanceOf(address: string) {
2529
const pubkey = new PublicKey(address);
2630
const balance = await this.connection.getBalance(pubkey);
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import type { TransactionInstruction } from "@solana/web3.js";
1+
import type { Keypair, TransactionInstruction } from "@solana/web3.js";
22

33
export type SolanaTransaction = SolanaInstructionTransaction;
44

55
export type SolanaInstructionTransaction = {
66
instructions: TransactionInstruction[];
77
addressLookupTableAddresses?: string[];
8+
accountsToSign?: Keypair[];
89
};

0 commit comments

Comments
 (0)