fixes and renames

This commit is contained in:
Arihant Bansal
2024-12-22 01:20:29 +05:30
parent f84a618c6c
commit ed689f5efd
31 changed files with 510 additions and 440 deletions

View File

@@ -78,10 +78,10 @@ npm install solana-agent-kit
## Quick Start ## Quick Start
```typescript ```typescript
import { SolanaAgentKit, createSolanaTools } from "solana-agent-kit"; import { SolanaAgent, createSolanaTools } from "solana-agent-kit";
// Initialize with private key and optional RPC URL // Initialize with private key and optional RPC URL
const agent = new SolanaAgentKit( const agent = new SolanaAgent(
"your-wallet-private-key-as-base58", "your-wallet-private-key-as-base58",
"https://api.mainnet-beta.solana.com", "https://api.mainnet-beta.solana.com",
"your-openai-api-key" "your-openai-api-key"

View File

@@ -21,19 +21,19 @@ Create a new TypeScript file in the `src/tools/` directory for your tool (e.g.,
```typescript:src/tools/custom_tool.ts ```typescript:src/tools/custom_tool.ts
import { Tool } from "langchain/tools"; import { Tool } from "langchain/tools";
import { SolanaAgentKit } from "../agent"; import { SolanaAgent } from "../agent";
export class CustomTool extends Tool { export class CustomTool extends Tool {
name = "custom_tool"; name = "custom_tool";
description = "Description of what the custom tool does."; description = "Description of what the custom tool does.";
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
protected async _call(input: string): Promise<string> { protected async _call(input: string): Promise<string> {
try { try {
const result = await this.solanaKit.customFunction(input); const result = await this.solanaAgent.customFunction(input);
return JSON.stringify({ return JSON.stringify({
status: "success", status: "success",
message: "Custom tool executed successfully", message: "Custom tool executed successfully",
@@ -53,7 +53,7 @@ export class CustomTool extends Tool {
### 3. Add Supporting Functions to SolanaAgentKit ### 3. Add Supporting Functions to SolanaAgentKit
```typescript:src/agent/index.ts ```typescript:src/agent/index.ts
export class SolanaAgentKit { export class SolanaAgent {
// ... existing code ... // ... existing code ...
async customFunction(input: string): Promise<string> { async customFunction(input: string): Promise<string> {
@@ -87,9 +87,9 @@ export function createSolanaTools(agent: SolanaAgentKit) {
### 6. Usage Example ### 6. Usage Example
```typescript ```typescript
import { SolanaAgentKit, createSolanaTools } from "solana-agent-kit"; import { SolanaAgent, createSolanaTools } from "solana-agent-kit";
const agent = new SolanaAgentKit( const agent = new SolanaAgent(
"your-wallet-private-key-as-base58", "your-wallet-private-key-as-base58",
"https://api.mainnet-beta.solana.com", "https://api.mainnet-beta.solana.com",
"your-openai-api-key" "your-openai-api-key"
@@ -118,19 +118,19 @@ Here's a complete example of implementing a tool to fetch token prices:
```typescript:src/tools/fetch_token_price.ts ```typescript:src/tools/fetch_token_price.ts
import { Tool } from "langchain/tools"; import { Tool } from "langchain/tools";
import { SolanaAgentKit } from "../agent"; import { SolanaAgent } from "../agent";
export class FetchTokenPriceTool extends Tool { export class FetchTokenPriceTool extends Tool {
name = "fetch_token_price"; name = "fetch_token_price";
description = "Fetches the current price of a specified token."; description = "Fetches the current price of a specified token.";
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
protected async _call(tokenSymbol: string): Promise<string> { protected async _call(tokenSymbol: string): Promise<string> {
try { try {
const price = await this.solanaKit.getTokenPrice(tokenSymbol); const price = await this.solanaAgent.getTokenPrice(tokenSymbol);
return JSON.stringify({ return JSON.stringify({
status: "success", status: "success",
message: `Price fetched successfully for ${tokenSymbol}.`, message: `Price fetched successfully for ${tokenSymbol}.`,
@@ -176,4 +176,4 @@ If you encounter any issues while implementing your custom tool:
- Contact the maintainer - Contact the maintainer
- Check existing tools for implementation examples - Check existing tools for implementation examples
--- ---

View File

@@ -15,10 +15,9 @@
"pnpm": ">=8.0.0" "pnpm": ">=8.0.0"
}, },
"keywords": [], "keywords": [],
"author": "", "author": "sendaifun",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@raydium-io/raydium-sdk-v2": "0.1.95-alpha",
"@bonfida/spl-name-service": "^3.0.7", "@bonfida/spl-name-service": "^3.0.7",
"@coral-xyz/anchor": "0.29", "@coral-xyz/anchor": "0.29",
"@langchain/core": "^0.3.18", "@langchain/core": "^0.3.18",
@@ -34,11 +33,12 @@
"@metaplex-foundation/umi-web3js-adapters": "^0.9.2", "@metaplex-foundation/umi-web3js-adapters": "^0.9.2",
"@orca-so/common-sdk": "0.6.4", "@orca-so/common-sdk": "0.6.4",
"@orca-so/whirlpools-sdk": "^0.13.12", "@orca-so/whirlpools-sdk": "^0.13.12",
"@raydium-io/raydium-sdk-v2": "0.1.95-alpha",
"@solana/spl-token": "^0.4.9", "@solana/spl-token": "^0.4.9",
"@solana/web3.js": "^1.95.4", "@solana/web3.js": "^1.95.4",
"bn.js": "^5.2.1",
"bs58": "^6.0.0", "bs58": "^6.0.0",
"decimal.js": "^10.4.3", "decimal.js": "^10.4.3",
"bn.js": "^5.2.1",
"dotenv": "^16.4.5", "dotenv": "^16.4.5",
"form-data": "^4.0.1", "form-data": "^4.0.1",
"langchain": "^0.3.6", "langchain": "^0.3.6",
@@ -51,4 +51,4 @@
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"typescript": "^5.7.2" "typescript": "^5.7.2"
} }
} }

View File

@@ -26,6 +26,7 @@ import {
sendCompressedAirdrop, sendCompressedAirdrop,
createOrcaSingleSidedWhirlpool, createOrcaSingleSidedWhirlpool,
FEE_TIERS, FEE_TIERS,
fetchPrice,
} from "../tools"; } from "../tools";
import { import {
CollectionDeployment, CollectionDeployment,
@@ -39,14 +40,14 @@ import { BN } from "@coral-xyz/anchor";
/** /**
* Main class for interacting with Solana blockchain * Main class for interacting with Solana blockchain
* Provides a unified interface for token operations, NFT management, and trading * Provides a unified interface for token operations, NFT management, trading and more
* *
* @class SolanaAgentKit * @class SolanaAgent
* @property {Connection} connection - Solana RPC connection * @property {Connection} connection - Solana RPC connection
* @property {Keypair} wallet - Wallet keypair for signing transactions * @property {Keypair} wallet - Wallet keypair for signing transactions
* @property {PublicKey} wallet_address - Public key of the wallet * @property {PublicKey} wallet_address - Public key of the wallet
*/ */
export class SolanaAgentKit { export class SolanaAgent {
public connection: Connection; public connection: Connection;
public wallet: Keypair; public wallet: Keypair;
public wallet_address: PublicKey; public wallet_address: PublicKey;
@@ -145,6 +146,10 @@ export class SolanaAgentKit {
return getTokenDataByTicker(ticker); return getTokenDataByTicker(ticker);
} }
async fetchTokenPrice(mint: string) {
return fetchPrice(new PublicKey(mint));
}
async launchPumpFunToken( async launchPumpFunToken(
tokenName: string, tokenName: string,
tokenTicker: string, tokenTicker: string,

View File

@@ -1,7 +1,7 @@
import { SolanaAgentKit } from './agent'; // Move the SolanaAgentKit class to src/agent.ts import { SolanaAgent } from "./agent";
import { createSolanaTools } from './langchain'; import { createSolanaTools } from "./langchain";
export { SolanaAgentKit, createSolanaTools }; export { SolanaAgent, createSolanaTools };
// Optional: Export types that users might need // Optional: Export types that users might need
export * from './types'; export * from "./types";

View File

@@ -1,9 +1,8 @@
import { PublicKey } from "@solana/web3.js"; import { PublicKey } from "@solana/web3.js";
import Decimal from "decimal.js"; import Decimal from "decimal.js";
import { Tool } from "langchain/tools"; import { Tool } from "langchain/tools";
import { SolanaAgentKit } from "../index"; import { SolanaAgent } from "../index";
import { create_image } from "../tools/create_image"; import { create_image } from "../tools/create_image";
import { fetchPrice } from "../tools/fetch_price";
import { BN } from "@coral-xyz/anchor"; import { BN } from "@coral-xyz/anchor";
import { FEE_TIERS } from "../tools"; import { FEE_TIERS } from "../tools";
import { toJSON } from "../utils/toJSON"; import { toJSON } from "../utils/toJSON";
@@ -18,14 +17,14 @@ export class SolanaBalanceTool extends Tool {
Inputs: Inputs:
tokenAddress: string, eg "So11111111111111111111111111111111111111112" (optional)`; tokenAddress: string, eg "So11111111111111111111111111111111111111112" (optional)`;
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
protected async _call(input: string): Promise<string> { protected async _call(input: string): Promise<string> {
try { try {
const tokenAddress = input ? new PublicKey(input) : undefined; const tokenAddress = input ? new PublicKey(input) : undefined;
const balance = await this.solanaKit.getBalance(tokenAddress); const balance = await this.solanaAgent.getBalance(tokenAddress);
return JSON.stringify({ return JSON.stringify({
status: "success", status: "success",
@@ -51,7 +50,7 @@ export class SolanaTransferTool extends Tool {
amount: number, eg 1 (required) amount: number, eg 1 (required)
mint?: string, eg "So11111111111111111111111111111111111111112" or "SENDdRQtYMWaQrBroBrJ2Q53fgVuq95CV9UPGEvpCxa" (optional)`; mint?: string, eg "So11111111111111111111111111111111111111112" or "SENDdRQtYMWaQrBroBrJ2Q53fgVuq95CV9UPGEvpCxa" (optional)`;
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
@@ -64,10 +63,10 @@ export class SolanaTransferTool extends Tool {
? new PublicKey(parsedInput.mint) ? new PublicKey(parsedInput.mint)
: undefined; : undefined;
const tx = await this.solanaKit.transfer( const tx = await this.solanaAgent.transfer(
recipient, recipient,
parsedInput.amount, parsedInput.amount,
mintAddress mintAddress,
); );
return JSON.stringify({ return JSON.stringify({
@@ -94,12 +93,12 @@ export class SolanaDeployTokenTool extends Tool {
Inputs (input is a JSON string): Inputs (input is a JSON string):
name: string, eg "My Token" (required) name: string, eg "My Token" (required)
uri: string, eg "https://example.com/token.json" (required) uri: string, eg "https://example.com/token.json" (required)
symbol: string, eg "MTK" (required) symbol: string, eg "MTK" (required)
decimals?: number, eg 9 (optional, defaults to 9) decimals?: number, eg 9 (optional, defaults to 9)
initialSupply?: number, eg 1000000 (optional)`; initialSupply?: number, eg 1000000 (optional)`;
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
@@ -107,12 +106,12 @@ export class SolanaDeployTokenTool extends Tool {
try { try {
const parsedInput = JSON.parse(input); const parsedInput = JSON.parse(input);
const result = await this.solanaKit.deployToken( const result = await this.solanaAgent.deployToken(
parsedInput.name, parsedInput.name,
parsedInput.uri, parsedInput.uri,
parsedInput.symbol, parsedInput.symbol,
parsedInput.decimals, parsedInput.decimals,
parsedInput.initialSupply parsedInput.initialSupply,
); );
return JSON.stringify({ return JSON.stringify({
@@ -140,7 +139,7 @@ export class SolanaDeployCollectionTool extends Tool {
uri: string, eg "https://example.com/collection.json" (required) uri: string, eg "https://example.com/collection.json" (required)
royaltyBasisPoints?: number, eg 500 for 5% (optional)`; royaltyBasisPoints?: number, eg 500 for 5% (optional)`;
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
@@ -148,7 +147,7 @@ export class SolanaDeployCollectionTool extends Tool {
try { try {
const parsedInput = JSON.parse(input); const parsedInput = JSON.parse(input);
const result = await this.solanaKit.deployCollection(parsedInput); const result = await this.solanaAgent.deployCollection(parsedInput);
return JSON.stringify({ return JSON.stringify({
status: "success", status: "success",
@@ -174,9 +173,9 @@ export class SolanaMintNFTTool extends Tool {
collectionMint: string, eg "J1S9H3QjnRtBbbuD4HjPV6RpRhwuk4zKbxsnCHuTgh9w" (required) - The address of the collection to mint into collectionMint: string, eg "J1S9H3QjnRtBbbuD4HjPV6RpRhwuk4zKbxsnCHuTgh9w" (required) - The address of the collection to mint into
name: string, eg "My NFT" (required) name: string, eg "My NFT" (required)
uri: string, eg "https://example.com/nft.json" (required) uri: string, eg "https://example.com/nft.json" (required)
recipient?: string, eg "9aUn5swQzUTRanaaTwmszxiv89cvFwUCjEBv1vZCoT1u" (optional) - The wallet to receive the NFT, defaults to agent's wallet which is ${this.solanaKit.wallet_address.toString()}`; recipient?: string, eg "9aUn5swQzUTRanaaTwmszxiv89cvFwUCjEBv1vZCoT1u" (optional) - The wallet to receive the NFT, defaults to agent's wallet which is ${this.solanaAgent.wallet_address.toString()}`;
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
@@ -184,7 +183,7 @@ export class SolanaMintNFTTool extends Tool {
try { try {
const parsedInput = JSON.parse(input); const parsedInput = JSON.parse(input);
const result = await this.solanaKit.mintNFT( const result = await this.solanaAgent.mintNFT(
new PublicKey(parsedInput.collectionMint), new PublicKey(parsedInput.collectionMint),
{ {
name: parsedInput.name, name: parsedInput.name,
@@ -192,7 +191,7 @@ export class SolanaMintNFTTool extends Tool {
}, },
parsedInput.recipient parsedInput.recipient
? new PublicKey(parsedInput.recipient) ? new PublicKey(parsedInput.recipient)
: this.solanaKit.wallet_address : this.solanaAgent.wallet_address,
); );
return JSON.stringify({ return JSON.stringify({
@@ -226,7 +225,7 @@ export class SolanaTradeTool extends Tool {
inputMint?: string, eg "So11111111111111111111111111111111111111112" (optional) inputMint?: string, eg "So11111111111111111111111111111111111111112" (optional)
slippageBps?: number, eg 100 (optional)`; slippageBps?: number, eg 100 (optional)`;
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
@@ -234,13 +233,13 @@ export class SolanaTradeTool extends Tool {
try { try {
const parsedInput = JSON.parse(input); const parsedInput = JSON.parse(input);
const tx = await this.solanaKit.trade( const tx = await this.solanaAgent.trade(
new PublicKey(parsedInput.outputMint), new PublicKey(parsedInput.outputMint),
parsedInput.inputAmount, parsedInput.inputAmount,
parsedInput.inputMint parsedInput.inputMint
? new PublicKey(parsedInput.inputMint) ? new PublicKey(parsedInput.inputMint)
: new PublicKey("So11111111111111111111111111111111111111112"), : new PublicKey("So11111111111111111111111111111111111111112"),
parsedInput.slippageBps parsedInput.slippageBps,
); );
return JSON.stringify({ return JSON.stringify({
@@ -265,18 +264,18 @@ export class SolanaRequestFundsTool extends Tool {
name = "solana_request_funds"; name = "solana_request_funds";
description = "Request SOL from Solana faucet (devnet/testnet only)"; description = "Request SOL from Solana faucet (devnet/testnet only)";
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
protected async _call(_input: string): Promise<string> { protected async _call(_input: string): Promise<string> {
try { try {
await this.solanaKit.requestFaucetFunds(); await this.solanaAgent.requestFaucetFunds();
return JSON.stringify({ return JSON.stringify({
status: "success", status: "success",
message: "Successfully requested faucet funds", message: "Successfully requested faucet funds",
network: this.solanaKit.connection.rpcEndpoint.split("/")[2], network: this.solanaAgent.connection.rpcEndpoint.split("/")[2],
}); });
} catch (error: any) { } catch (error: any) {
return JSON.stringify({ return JSON.stringify({
@@ -297,7 +296,7 @@ export class SolanaRegisterDomainTool extends Tool {
spaceKB: number, eg 1 (optional, default is 1) spaceKB: number, eg 1 (optional, default is 1)
`; `;
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
@@ -318,9 +317,9 @@ export class SolanaRegisterDomainTool extends Tool {
const parsedInput = toJSON(input); const parsedInput = toJSON(input);
this.validateInput(parsedInput); this.validateInput(parsedInput);
const tx = await this.solanaKit.registerDomain( const tx = await this.solanaAgent.registerDomain(
parsedInput.name, parsedInput.name,
parsedInput.spaceKB || 1 parsedInput.spaceKB || 1,
); );
return JSON.stringify({ return JSON.stringify({
@@ -348,14 +347,14 @@ export class SolanaResolveDomainTool extends Tool {
domain: string, eg "pumpfun.sol" or "pumpfun"(required) domain: string, eg "pumpfun.sol" or "pumpfun"(required)
`; `;
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
protected async _call(input: string): Promise<string> { protected async _call(input: string): Promise<string> {
try { try {
const domain = input.trim(); const domain = input.trim();
const publicKey = await this.solanaKit.resolveSolDomain(domain); const publicKey = await this.solanaAgent.resolveSolDomain(domain);
return JSON.stringify({ return JSON.stringify({
status: "success", status: "success",
@@ -380,14 +379,14 @@ export class SolanaGetDomainTool extends Tool {
account: string, eg "4Be9CvxqHW6BYiRAxW9Q3xu1ycTMWaL5z8NX4HR3ha7t" (required) account: string, eg "4Be9CvxqHW6BYiRAxW9Q3xu1ycTMWaL5z8NX4HR3ha7t" (required)
`; `;
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
protected async _call(input: string): Promise<string> { protected async _call(input: string): Promise<string> {
try { try {
const account = new PublicKey(input.trim()); const account = new PublicKey(input.trim());
const domain = await this.solanaKit.getPrimaryDomain(account); const domain = await this.solanaAgent.getPrimaryDomain(account);
return JSON.stringify({ return JSON.stringify({
status: "success", status: "success",
@@ -408,12 +407,12 @@ export class SolanaGetWalletAddressTool extends Tool {
name = "solana_get_wallet_address"; name = "solana_get_wallet_address";
description = `Get the wallet address of the agent`; description = `Get the wallet address of the agent`;
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
async _call(_input: string): Promise<string> { async _call(_input: string): Promise<string> {
return this.solanaKit.wallet_address.toString(); return this.solanaAgent.wallet_address.toString();
} }
} }
@@ -431,7 +430,7 @@ export class SolanaPumpfunTokenLaunchTool extends Tool {
description: string, eg "PumpFun Token is a token on the Solana blockchain", description: string, eg "PumpFun Token is a token on the Solana blockchain",
imageUrl: string, eg "https://i.imgur.com/UFm07Np_d.png`; imageUrl: string, eg "https://i.imgur.com/UFm07Np_d.png`;
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
@@ -465,7 +464,7 @@ export class SolanaPumpfunTokenLaunchTool extends Tool {
this.validateInput(parsedInput); this.validateInput(parsedInput);
// Launch token with validated input // Launch token with validated input
await this.solanaKit.launchPumpFunToken( await this.solanaAgent.launchPumpFunToken(
parsedInput.tokenName, parsedInput.tokenName,
parsedInput.tokenTicker, parsedInput.tokenTicker,
parsedInput.description, parsedInput.description,
@@ -475,7 +474,7 @@ export class SolanaPumpfunTokenLaunchTool extends Tool {
telegram: parsedInput.telegram, telegram: parsedInput.telegram,
website: parsedInput.website, website: parsedInput.website,
initialLiquiditySOL: parsedInput.initialLiquiditySOL, initialLiquiditySOL: parsedInput.initialLiquiditySOL,
} },
); );
return JSON.stringify({ return JSON.stringify({
@@ -499,7 +498,7 @@ export class SolanaCreateImageTool extends Tool {
description = description =
"Create an image using OpenAI's DALL-E. Input should be a string prompt for the image."; "Create an image using OpenAI's DALL-E. Input should be a string prompt for the image.";
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
@@ -512,7 +511,7 @@ export class SolanaCreateImageTool extends Tool {
protected async _call(input: string): Promise<string> { protected async _call(input: string): Promise<string> {
try { try {
this.validateInput(input); this.validateInput(input);
const result = await create_image(this.solanaKit, input.trim()); const result = await create_image(this.solanaAgent, input.trim());
return JSON.stringify({ return JSON.stringify({
status: "success", status: "success",
@@ -536,7 +535,7 @@ export class SolanaLendAssetTool extends Tool {
Inputs (input is a json string): Inputs (input is a json string):
amount: number, eg 1, 0.01 (required)`; amount: number, eg 1, 0.01 (required)`;
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
@@ -544,7 +543,7 @@ export class SolanaLendAssetTool extends Tool {
try { try {
let amount = JSON.parse(input).amount || input; let amount = JSON.parse(input).amount || input;
const tx = await this.solanaKit.lendAssets(amount); const tx = await this.solanaAgent.lendAssets(amount);
return JSON.stringify({ return JSON.stringify({
status: "success", status: "success",
@@ -566,13 +565,13 @@ export class SolanaTPSCalculatorTool extends Tool {
name = "solana_get_tps"; name = "solana_get_tps";
description = "Get the current TPS of the Solana network"; description = "Get the current TPS of the Solana network";
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
async _call(_input: string): Promise<string> { async _call(_input: string): Promise<string> {
try { try {
const tps = await this.solanaKit.getTPS(); const tps = await this.solanaAgent.getTPS();
return `Solana (mainnet-beta) current transactions per second: ${tps}`; return `Solana (mainnet-beta) current transactions per second: ${tps}`;
} catch (error: any) { } catch (error: any) {
return `Error fetching TPS: ${error.message}`; return `Error fetching TPS: ${error.message}`;
@@ -587,7 +586,7 @@ export class SolanaStakeTool extends Tool {
Inputs ( input is a JSON string ): Inputs ( input is a JSON string ):
amount: number, eg 1 or 0.01 (required)`; amount: number, eg 1 or 0.01 (required)`;
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
@@ -595,7 +594,7 @@ export class SolanaStakeTool extends Tool {
try { try {
const parsedInput = JSON.parse(input) || Number(input); const parsedInput = JSON.parse(input) || Number(input);
const tx = await this.solanaKit.stake(parsedInput.amount); const tx = await this.solanaAgent.stake(parsedInput.amount);
return JSON.stringify({ return JSON.stringify({
status: "success", status: "success",
@@ -619,17 +618,17 @@ export class SolanaStakeTool extends Tool {
export class SolanaFetchPriceTool extends Tool { export class SolanaFetchPriceTool extends Tool {
name = "solana_fetch_price"; name = "solana_fetch_price";
description = `Fetch the price of a given token in USDC. description = `Fetch the price of a given token in USDC.
Inputs: Inputs:
- tokenId: string, the mint address of the token, e.g., "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN"`; - tokenId: string, the mint address of the token, e.g., "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN"`;
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
async _call(input: string): Promise<string> { async _call(input: string): Promise<string> {
try { try {
const price = await fetchPrice(this.solanaKit, input.trim()); const price = await this.solanaAgent.fetchTokenPrice(input.trim());
return JSON.stringify({ return JSON.stringify({
status: "success", status: "success",
tokenId: input.trim(), tokenId: input.trim(),
@@ -652,7 +651,7 @@ export class SolanaTokenDataTool extends Tool {
Inputs: mintAddress is required. Inputs: mintAddress is required.
mintAddress: string, eg "So11111111111111111111111111111111111111112" (required)`; mintAddress: string, eg "So11111111111111111111111111111111111111112" (required)`;
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
@@ -660,7 +659,8 @@ export class SolanaTokenDataTool extends Tool {
try { try {
const parsedInput = input.trim(); const parsedInput = input.trim();
const tokenData = await this.solanaKit.getTokenDataByAddress(parsedInput); const tokenData =
await this.solanaAgent.getTokenDataByAddress(parsedInput);
return JSON.stringify({ return JSON.stringify({
status: "success", status: "success",
@@ -683,14 +683,14 @@ export class SolanaTokenDataByTickerTool extends Tool {
Inputs: ticker is required. Inputs: ticker is required.
ticker: string, eg "USDC" (required)`; ticker: string, eg "USDC" (required)`;
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
protected async _call(input: string): Promise<string> { protected async _call(input: string): Promise<string> {
try { try {
const ticker = input.trim(); const ticker = input.trim();
const tokenData = await this.solanaKit.getTokenDataByTicker(ticker); const tokenData = await this.solanaAgent.getTokenDataByTicker(ticker);
return JSON.stringify({ return JSON.stringify({
status: "success", status: "success",
tokenData: tokenData, tokenData: tokenData,
@@ -708,7 +708,7 @@ export class SolanaTokenDataByTickerTool extends Tool {
export class SolanaCompressedAirdropTool extends Tool { export class SolanaCompressedAirdropTool extends Tool {
name = "solana_compressed_airdrop"; name = "solana_compressed_airdrop";
description = `Airdrop SPL tokens with ZK Compression (also called as airdropping tokens) description = `Airdrop SPL tokens with ZK Compression (also called as airdropping tokens)
Inputs (input is a JSON string): Inputs (input is a JSON string):
mintAddress: string, the mint address of the token, e.g., "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN" (required) mintAddress: string, the mint address of the token, e.g., "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN" (required)
amount: number, the amount of tokens to airdrop per recipient, e.g., 42 (required) amount: number, the amount of tokens to airdrop per recipient, e.g., 42 (required)
@@ -717,7 +717,7 @@ export class SolanaCompressedAirdropTool extends Tool {
priorityFeeInLamports: number, the priority fee in lamports. Default is 30_000. (optional) priorityFeeInLamports: number, the priority fee in lamports. Default is 30_000. (optional)
shouldLog: boolean, whether to log progress to stdout. Default is false. (optional)`; shouldLog: boolean, whether to log progress to stdout. Default is false. (optional)`;
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
@@ -725,13 +725,13 @@ export class SolanaCompressedAirdropTool extends Tool {
try { try {
const parsedInput = JSON.parse(input); const parsedInput = JSON.parse(input);
const txs = await this.solanaKit.sendCompressedAirdrop( const txs = await this.solanaAgent.sendCompressedAirdrop(
parsedInput.mintAddress, parsedInput.mintAddress,
parsedInput.amount, parsedInput.amount,
parsedInput.decimals, parsedInput.decimals,
parsedInput.recipients, parsedInput.recipients,
parsedInput.priorityFeeInLamports || 30_000, parsedInput.priorityFeeInLamports || 30_000,
parsedInput.shouldLog || false parsedInput.shouldLog || false,
); );
return JSON.stringify({ return JSON.stringify({
@@ -761,7 +761,7 @@ export class SolanaCreateSingleSidedWhirlpoolTool extends Tool {
- maxPrice: number, eg: 5.0 (required, maximum price at which liquidity is added) - maxPrice: number, eg: 5.0 (required, maximum price at which liquidity is added)
- feeTier: number, eg: 0.30 (required, fee tier for the pool)`; - feeTier: number, eg: 0.30 (required, fee tier for the pool)`;
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
@@ -776,10 +776,12 @@ export class SolanaCreateSingleSidedWhirlpoolTool extends Tool {
const feeTier = inputFormat.feeTier; const feeTier = inputFormat.feeTier;
if (!feeTier || !(feeTier in FEE_TIERS)) { if (!feeTier || !(feeTier in FEE_TIERS)) {
throw new Error(`Invalid feeTier. Available options: ${Object.keys(FEE_TIERS).join(", ")}`); throw new Error(
`Invalid feeTier. Available options: ${Object.keys(FEE_TIERS).join(", ")}`,
);
} }
const txId = await this.solanaKit.createOrcaSingleSidedWhirlpool( const txId = await this.solanaAgent.createOrcaSingleSidedWhirlpool(
depositTokenAmount, depositTokenAmount,
depositTokenMint, depositTokenMint,
otherTokenMint, otherTokenMint,
@@ -803,7 +805,6 @@ export class SolanaCreateSingleSidedWhirlpoolTool extends Tool {
} }
} }
export class SolanaRaydiumCreateAmmV4 extends Tool { export class SolanaRaydiumCreateAmmV4 extends Tool {
name = "raydium_create_ammV4"; name = "raydium_create_ammV4";
description = `Raydium's Legacy AMM that requiers an OpenBook marketID description = `Raydium's Legacy AMM that requiers an OpenBook marketID
@@ -815,15 +816,15 @@ export class SolanaRaydiumCreateAmmV4 extends Tool {
startTime: number(seconds), eg: now number or zero (required) startTime: number(seconds), eg: now number or zero (required)
`; `;
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
async _call(input: string): Promise<string> { async _call(input: string): Promise<string> {
try { try {
let inputFormat = JSON.parse(input) let inputFormat = JSON.parse(input);
const tx = await this.solanaKit.raydiumCreateAmmV4( const tx = await this.solanaAgent.raydiumCreateAmmV4(
new PublicKey(inputFormat.marketId), new PublicKey(inputFormat.marketId),
new BN(inputFormat.baseAmount), new BN(inputFormat.baseAmount),
new BN(inputFormat.quoteAmount), new BN(inputFormat.quoteAmount),
@@ -857,15 +858,15 @@ export class SolanaRaydiumCreateClmm extends Tool {
startTime: number(seconds), eg: now number or zero (required) startTime: number(seconds), eg: now number or zero (required)
`; `;
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
async _call(input: string): Promise<string> { async _call(input: string): Promise<string> {
try { try {
let inputFormat = JSON.parse(input) let inputFormat = JSON.parse(input);
const tx = await this.solanaKit.raydiumCreateClmm( const tx = await this.solanaAgent.raydiumCreateClmm(
new PublicKey(inputFormat.mint1), new PublicKey(inputFormat.mint1),
new PublicKey(inputFormat.mint2), new PublicKey(inputFormat.mint2),
@@ -892,7 +893,7 @@ export class SolanaRaydiumCreateClmm extends Tool {
export class SolanaRaydiumCreateCpmm extends Tool { export class SolanaRaydiumCreateCpmm extends Tool {
name = "raydium_create_cpmm"; name = "raydium_create_cpmm";
description = `Raydium's newest CPMM, does not require marketID, supports Token 2022 standard description = `Raydium's newest CPMM, does not require marketID, supports Token 2022 standard
Inputs (input is a json string): Inputs (input is a json string):
mint1: string (required) mint1: string (required)
@@ -903,15 +904,15 @@ export class SolanaRaydiumCreateCpmm extends Tool {
startTime: number(seconds), eg: now number or zero (required) startTime: number(seconds), eg: now number or zero (required)
`; `;
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
async _call(input: string): Promise<string> { async _call(input: string): Promise<string> {
try { try {
let inputFormat = JSON.parse(input) let inputFormat = JSON.parse(input);
const tx = await this.solanaKit.raydiumCreateCpmm( const tx = await this.solanaAgent.raydiumCreateCpmm(
new PublicKey(inputFormat.mint1), new PublicKey(inputFormat.mint1),
new PublicKey(inputFormat.mint2), new PublicKey(inputFormat.mint2),
@@ -940,7 +941,7 @@ export class SolanaRaydiumCreateCpmm extends Tool {
export class SolanaOpenbookCreateMarket extends Tool { export class SolanaOpenbookCreateMarket extends Tool {
name = "solana_openbook_create_market"; name = "solana_openbook_create_market";
description = `Openbook marketId, required for ammv4 description = `Openbook marketId, required for ammv4
Inputs (input is a json string): Inputs (input is a json string):
baseMint: string (required) baseMint: string (required)
@@ -949,15 +950,15 @@ export class SolanaOpenbookCreateMarket extends Tool {
tickSize: number (required) tickSize: number (required)
`; `;
constructor(private solanaKit: SolanaAgentKit) { constructor(private solanaAgent: SolanaAgent) {
super(); super();
} }
async _call(input: string): Promise<string> { async _call(input: string): Promise<string> {
try { try {
let inputFormat = JSON.parse(input) let inputFormat = JSON.parse(input);
const tx = await this.solanaKit.openbookCreateMarket( const tx = await this.solanaAgent.openbookCreateMarket(
new PublicKey(inputFormat.baseMint), new PublicKey(inputFormat.baseMint),
new PublicKey(inputFormat.quoteMint), new PublicKey(inputFormat.quoteMint),
@@ -980,32 +981,32 @@ export class SolanaOpenbookCreateMarket extends Tool {
} }
} }
export function createSolanaTools(solanaKit: SolanaAgentKit) { export function createSolanaTools(solanaAgent: SolanaAgent) {
return [ return [
new SolanaBalanceTool(solanaKit), new SolanaBalanceTool(solanaAgent),
new SolanaTransferTool(solanaKit), new SolanaTransferTool(solanaAgent),
new SolanaDeployTokenTool(solanaKit), new SolanaDeployTokenTool(solanaAgent),
new SolanaDeployCollectionTool(solanaKit), new SolanaDeployCollectionTool(solanaAgent),
new SolanaMintNFTTool(solanaKit), new SolanaMintNFTTool(solanaAgent),
new SolanaTradeTool(solanaKit), new SolanaTradeTool(solanaAgent),
new SolanaRequestFundsTool(solanaKit), new SolanaRequestFundsTool(solanaAgent),
new SolanaRegisterDomainTool(solanaKit), new SolanaRegisterDomainTool(solanaAgent),
new SolanaGetWalletAddressTool(solanaKit), new SolanaGetWalletAddressTool(solanaAgent),
new SolanaPumpfunTokenLaunchTool(solanaKit), new SolanaPumpfunTokenLaunchTool(solanaAgent),
new SolanaCreateImageTool(solanaKit), new SolanaCreateImageTool(solanaAgent),
new SolanaLendAssetTool(solanaKit), new SolanaLendAssetTool(solanaAgent),
new SolanaTPSCalculatorTool(solanaKit), new SolanaTPSCalculatorTool(solanaAgent),
new SolanaStakeTool(solanaKit), new SolanaStakeTool(solanaAgent),
new SolanaFetchPriceTool(solanaKit), new SolanaFetchPriceTool(solanaAgent),
new SolanaResolveDomainTool(solanaKit), new SolanaResolveDomainTool(solanaAgent),
new SolanaGetDomainTool(solanaKit), new SolanaGetDomainTool(solanaAgent),
new SolanaTokenDataTool(solanaKit), new SolanaTokenDataTool(solanaAgent),
new SolanaTokenDataByTickerTool(solanaKit), new SolanaTokenDataByTickerTool(solanaAgent),
new SolanaCompressedAirdropTool(solanaKit), new SolanaCompressedAirdropTool(solanaAgent),
new SolanaRaydiumCreateAmmV4(solanaKit), new SolanaRaydiumCreateAmmV4(solanaAgent),
new SolanaRaydiumCreateClmm(solanaKit), new SolanaRaydiumCreateClmm(solanaAgent),
new SolanaRaydiumCreateCpmm(solanaKit), new SolanaRaydiumCreateCpmm(solanaAgent),
new SolanaOpenbookCreateMarket(solanaKit), new SolanaOpenbookCreateMarket(solanaAgent),
new SolanaCreateSingleSidedWhirlpoolTool(solanaKit), new SolanaCreateSingleSidedWhirlpoolTool(solanaAgent),
]; ];
} }

View File

@@ -1,16 +1,16 @@
import { SolanaAgentKit } from "../index"; import { SolanaAgent } from "../index";
import OpenAI from "openai"; import OpenAI from "openai";
/** /**
* Generate an image using OpenAI's DALL-E * Generate an image using OpenAI's DALL-E
* @param agent SolanaAgentKit instance * @param agent SolanaAgent instance
* @param prompt Text description of the image to generate * @param prompt Text description of the image to generate
* @param size Image size ('256x256', '512x512', or '1024x1024') (default: '1024x1024') * @param size Image size ('256x256', '512x512', or '1024x1024') (default: '1024x1024')
* @param n Number of images to generate (default: 1) * @param n Number of images to generate (default: 1)
* @returns Object containing the generated image URLs * @returns Object containing the generated image URLs
*/ */
export async function create_image( export async function create_image(
agent: SolanaAgentKit, agent: SolanaAgent,
prompt: string, prompt: string,
size: "256x256" | "512x512" | "1024x1024" = "1024x1024", size: "256x256" | "512x512" | "1024x1024" = "1024x1024",
n: number = 1, n: number = 1,

View File

@@ -1,5 +1,5 @@
import { Keypair, PublicKey, Transaction } from "@solana/web3.js"; import { Keypair, PublicKey, Transaction } from "@solana/web3.js";
import { SolanaAgentKit } from "../agent"; import { SolanaAgent } from "../index";
import { BN, Wallet } from "@coral-xyz/anchor"; import { BN, Wallet } from "@coral-xyz/anchor";
import { Decimal } from "decimal.js"; import { Decimal } from "decimal.js";
import { import {
@@ -40,7 +40,7 @@ import { sendTx } from "../utils/send_tx";
* @remarks * @remarks
* Fee tiers determine the percentage of fees collected on swaps, while tick spacing affects * Fee tiers determine the percentage of fees collected on swaps, while tick spacing affects
* the granularity of price ranges for liquidity positions. * the granularity of price ranges for liquidity positions.
* *
* For more details, refer to: * For more details, refer to:
* - [Whirlpool Fees](https://orca-so.github.io/whirlpools/Architecture%20Overview/Whirlpool%20Fees) * - [Whirlpool Fees](https://orca-so.github.io/whirlpools/Architecture%20Overview/Whirlpool%20Fees)
* - [Whirlpool Parameters](https://orca-so.github.io/whirlpools/Architecture%20Overview/Whirlpool%20Parameters) * - [Whirlpool Parameters](https://orca-so.github.io/whirlpools/Architecture%20Overview/Whirlpool%20Parameters)
@@ -54,17 +54,17 @@ export const FEE_TIERS = {
0.04: 4, 0.04: 4,
0.05: 8, 0.05: 8,
0.16: 16, 0.16: 16,
0.30: 64, 0.3: 64,
0.65: 96, 0.65: 96,
1.00: 128, 1.0: 128,
2.00: 256, 2.0: 256,
} as const; } as const;
/** /**
* # Creates a single-sided Whirlpool. * # Creates a single-sided Whirlpool.
* *
* This function initializes a new Whirlpool (liquidity pool) on Orca and seeds it with liquidity from a single token. * This function initializes a new Whirlpool (liquidity pool) on Orca and seeds it with liquidity from a single token.
* *
* ## Example Usage: * ## Example Usage:
* You created a new token called SHARK, and you want to set the initial price to 0.001 USDC. * You created a new token called SHARK, and you want to set the initial price to 0.001 USDC.
* You set `depositTokenMint` to SHARK's mint address and `otherTokenMint` to USDC's mint address. * You set `depositTokenMint` to SHARK's mint address and `otherTokenMint` to USDC's mint address.
@@ -72,7 +72,7 @@ export const FEE_TIERS = {
* 1. Increase the amount of tokens you deposit * 1. Increase the amount of tokens you deposit
* 2. Set the initial price very low * 2. Set the initial price very low
* 3. Set the maximum price closer to the initial price * 3. Set the maximum price closer to the initial price
* *
* ### Note for experts: * ### Note for experts:
* The Wrhirlpool program initializes the Whirlpool with the in a specific order. This might not be * The Wrhirlpool program initializes the Whirlpool with the in a specific order. This might not be
* the order you expect, so the function checks the order and adjusts the inverts the prices. This means that * the order you expect, so the function checks the order and adjusts the inverts the prices. This means that
@@ -86,13 +86,13 @@ export const FEE_TIERS = {
* @param initialPrice - The initial price of the deposit token in terms of the other token. * @param initialPrice - The initial price of the deposit token in terms of the other token.
* @param maxPrice - The maximum price at which liquidity is added. * @param maxPrice - The maximum price at which liquidity is added.
* @param feeTier - The fee tier percentage for the pool, determining tick spacing and fee collection rates. * @param feeTier - The fee tier percentage for the pool, determining tick spacing and fee collection rates.
* *
* @returns A promise that resolves to a transaction ID (`string`) of the transaction creating the pool. * @returns A promise that resolves to a transaction ID (`string`) of the transaction creating the pool.
* *
* @throws Will throw an error if: * @throws Will throw an error if:
* - Mint accounts for the tokens cannot be fetched. * - Mint accounts for the tokens cannot be fetched.
* - Prices are out of bounds. * - Prices are out of bounds.
* *
* @remarks * @remarks
* This function is designed for single-sided deposits where users only contribute one type of token, * This function is designed for single-sided deposits where users only contribute one type of token,
* and the function manages mint order and necessary calculations. * and the function manages mint order and necessary calculations.
@@ -103,7 +103,7 @@ export const FEE_TIERS = {
* import { PublicKey } from "@solana/web3.js"; * import { PublicKey } from "@solana/web3.js";
* import { BN } from "@coral-xyz/anchor"; * import { BN } from "@coral-xyz/anchor";
* import Decimal from "decimal.js"; * import Decimal from "decimal.js";
* *
* const agent = new SolanaAgentKit(wallet, connection); * const agent = new SolanaAgentKit(wallet, connection);
* const depositAmount = new BN(1_000_000_000_000); // 1 million SHARK if SHARK has 6 decimals * const depositAmount = new BN(1_000_000_000_000); // 1 million SHARK if SHARK has 6 decimals
* const depositTokenMint = new PublicKey("DEPOSTI_TOKEN_ADDRESS"); * const depositTokenMint = new PublicKey("DEPOSTI_TOKEN_ADDRESS");
@@ -111,7 +111,7 @@ export const FEE_TIERS = {
* const initialPrice = new Decimal(0.001); * const initialPrice = new Decimal(0.001);
* const maxPrice = new Decimal(5.0); * const maxPrice = new Decimal(5.0);
* const feeTier = 0.30; * const feeTier = 0.30;
* *
* const txId = await createOrcaSingleSidedWhirlpool( * const txId = await createOrcaSingleSidedWhirlpool(
* agent, * agent,
* depositAmount, * depositAmount,
@@ -125,7 +125,7 @@ export const FEE_TIERS = {
* ``` * ```
*/ */
export async function createOrcaSingleSidedWhirlpool( export async function createOrcaSingleSidedWhirlpool(
agent: SolanaAgentKit, agent: SolanaAgent,
depositTokenAmount: BN, depositTokenAmount: BN,
depositTokenMint: PublicKey, depositTokenMint: PublicKey,
otherTokenMint: PublicKey, otherTokenMint: PublicKey,
@@ -134,13 +134,19 @@ export async function createOrcaSingleSidedWhirlpool(
feeTier: keyof typeof FEE_TIERS, feeTier: keyof typeof FEE_TIERS,
): Promise<string> { ): Promise<string> {
const wallet = new Wallet(agent.wallet); const wallet = new Wallet(agent.wallet);
const ctx = WhirlpoolContext.from(agent.connection, wallet, ORCA_WHIRLPOOL_PROGRAM_ID); const ctx = WhirlpoolContext.from(
agent.connection,
wallet,
ORCA_WHIRLPOOL_PROGRAM_ID,
);
const fetcher = ctx.fetcher; const fetcher = ctx.fetcher;
const correctTokenOrder = PoolUtil.orderMints(otherTokenMint, depositTokenMint).map( const correctTokenOrder = PoolUtil.orderMints(
(addr) => addr.toString(), otherTokenMint,
); depositTokenMint,
const isCorrectMintOrder = correctTokenOrder[0] === depositTokenMint.toString(); ).map((addr) => addr.toString());
const isCorrectMintOrder =
correctTokenOrder[0] === depositTokenMint.toString();
let mintA, mintB; let mintA, mintB;
if (isCorrectMintOrder) { if (isCorrectMintOrder) {
[mintA, mintB] = [depositTokenMint, otherTokenMint]; [mintA, mintB] = [depositTokenMint, otherTokenMint];
@@ -151,10 +157,18 @@ export async function createOrcaSingleSidedWhirlpool(
} }
const mintAAccount = await fetcher.getMintInfo(mintA); const mintAAccount = await fetcher.getMintInfo(mintA);
const mintBAccount = await fetcher.getMintInfo(mintB); const mintBAccount = await fetcher.getMintInfo(mintB);
if (mintAAccount === null || mintBAccount === null) throw Error('Mint account not found'); if (mintAAccount === null || mintBAccount === null)
throw Error("Mint account not found");
const tickSpacing = FEE_TIERS[feeTier]; const tickSpacing = FEE_TIERS[feeTier];
const tickIndex = PriceMath.priceToTickIndex(initialPrice, mintAAccount.decimals, mintBAccount.decimals); const tickIndex = PriceMath.priceToTickIndex(
const initialTick = TickUtil.getInitializableTickIndex(tickIndex, tickSpacing); initialPrice,
mintAAccount.decimals,
mintBAccount.decimals,
);
const initialTick = TickUtil.getInitializableTickIndex(
tickIndex,
tickSpacing,
);
const tokenExtensionCtx: TokenExtensionContextForPool = { const tokenExtensionCtx: TokenExtensionContextForPool = {
...NO_TOKEN_EXTENSION_CONTEXT, ...NO_TOKEN_EXTENSION_CONTEXT,
@@ -196,17 +210,17 @@ export async function createOrcaSingleSidedWhirlpool(
tokenVaultBKeypair, tokenVaultBKeypair,
feeTierKey, feeTierKey,
tickSpacing: tickSpacing, tickSpacing: tickSpacing,
funder: wallet.publicKey funder: wallet.publicKey,
}; };
const initPoolIx = !TokenExtensionUtil.isV2IxRequiredPool(tokenExtensionCtx) const initPoolIx = !TokenExtensionUtil.isV2IxRequiredPool(tokenExtensionCtx)
? WhirlpoolIx.initializePoolIx(ctx.program, baseParamsPool) ? WhirlpoolIx.initializePoolIx(ctx.program, baseParamsPool)
: WhirlpoolIx.initializePoolV2Ix(ctx.program, { : WhirlpoolIx.initializePoolV2Ix(ctx.program, {
...baseParamsPool, ...baseParamsPool,
tokenProgramA: tokenExtensionCtx.tokenMintWithProgramA.tokenProgram, tokenProgramA: tokenExtensionCtx.tokenMintWithProgramA.tokenProgram,
tokenProgramB: tokenExtensionCtx.tokenMintWithProgramB.tokenProgram, tokenProgramB: tokenExtensionCtx.tokenMintWithProgramB.tokenProgram,
tokenBadgeA, tokenBadgeA,
tokenBadgeB, tokenBadgeB,
}); });
const initialTickArrayStartTick = TickUtil.getStartTickIndex( const initialTickArrayStartTick = TickUtil.getStartTickIndex(
initialTick, initialTick,
tickSpacing, tickSpacing,
@@ -235,14 +249,32 @@ export async function createOrcaSingleSidedWhirlpool(
let tickLowerIndex, tickUpperIndex; let tickLowerIndex, tickUpperIndex;
if (isCorrectMintOrder) { if (isCorrectMintOrder) {
tickLowerIndex = initialTick; tickLowerIndex = initialTick;
tickUpperIndex = PriceMath.priceToTickIndex(maxPrice, mintAAccount.decimals, mintBAccount.decimals); tickUpperIndex = PriceMath.priceToTickIndex(
maxPrice,
mintAAccount.decimals,
mintBAccount.decimals,
);
} else { } else {
tickLowerIndex = PriceMath.priceToTickIndex(maxPrice, mintAAccount.decimals, mintBAccount.decimals); tickLowerIndex = PriceMath.priceToTickIndex(
maxPrice,
mintAAccount.decimals,
mintBAccount.decimals,
);
tickUpperIndex = initialTick; tickUpperIndex = initialTick;
} }
const tickLowerInitializableIndex = TickUtil.getInitializableTickIndex(tickLowerIndex, tickSpacing); const tickLowerInitializableIndex = TickUtil.getInitializableTickIndex(
const tickUpperInitializableIndex = TickUtil.getInitializableTickIndex(tickUpperIndex, tickSpacing); tickLowerIndex,
if (!TickUtil.checkTickInBounds(tickLowerInitializableIndex) || !TickUtil.checkTickInBounds(tickUpperInitializableIndex)) throw Error('Prices out of bounds'); tickSpacing,
);
const tickUpperInitializableIndex = TickUtil.getInitializableTickIndex(
tickUpperIndex,
tickSpacing,
);
if (
!TickUtil.checkTickInBounds(tickLowerInitializableIndex) ||
!TickUtil.checkTickInBounds(tickUpperInitializableIndex)
)
throw Error("Prices out of bounds");
const increasLiquidityQuoteParam: IncreaseLiquidityQuoteParam = { const increasLiquidityQuoteParam: IncreaseLiquidityQuoteParam = {
inputTokenAmount: new BN(depositTokenAmount), inputTokenAmount: new BN(depositTokenAmount),
inputTokenMint: depositTokenMint, inputTokenMint: depositTokenMint,
@@ -253,11 +285,11 @@ export async function createOrcaSingleSidedWhirlpool(
tickLowerIndex: tickLowerInitializableIndex, tickLowerIndex: tickLowerInitializableIndex,
tickUpperIndex: tickUpperInitializableIndex, tickUpperIndex: tickUpperInitializableIndex,
tokenExtensionCtx: tokenExtensionCtx, tokenExtensionCtx: tokenExtensionCtx,
slippageTolerance: Percentage.fromFraction(0, 100) slippageTolerance: Percentage.fromFraction(0, 100),
} };
const liquidityInput = increaseLiquidityQuoteByInputTokenWithParams( const liquidityInput = increaseLiquidityQuoteByInputTokenWithParams(
increasLiquidityQuoteParam increasLiquidityQuoteParam,
) );
const { liquidityAmount: liquidity, tokenMaxA, tokenMaxB } = liquidityInput; const { liquidityAmount: liquidity, tokenMaxA, tokenMaxB } = liquidityInput;
const positionMintKeypair = Keypair.generate(); const positionMintKeypair = Keypair.generate();
@@ -285,7 +317,7 @@ export async function createOrcaSingleSidedWhirlpool(
...params, ...params,
positionMint: positionMintPubkey, positionMint: positionMintPubkey,
withTokenMetadataExtension: true, withTokenMetadataExtension: true,
}) });
txBuilder.addInstruction(positionIx); txBuilder.addInstruction(positionIx);
txBuilder.addSigner(positionMintKeypair); txBuilder.addSigner(positionMintKeypair);
@@ -365,35 +397,33 @@ export async function createOrcaSingleSidedWhirlpool(
tickArrayUpper: tickArrayUpperPda.publicKey, tickArrayUpper: tickArrayUpperPda.publicKey,
}; };
const liquidityIx = !TokenExtensionUtil.isV2IxRequiredPool( const liquidityIx = !TokenExtensionUtil.isV2IxRequiredPool(tokenExtensionCtx)
tokenExtensionCtx,
)
? increaseLiquidityIx(ctx.program, baseParamsLiquidity) ? increaseLiquidityIx(ctx.program, baseParamsLiquidity)
: increaseLiquidityV2Ix(ctx.program, { : increaseLiquidityV2Ix(ctx.program, {
...baseParamsLiquidity, ...baseParamsLiquidity,
tokenMintA: mintA, tokenMintA: mintA,
tokenMintB: mintB, tokenMintB: mintB,
tokenProgramA: tokenExtensionCtx.tokenMintWithProgramA.tokenProgram, tokenProgramA: tokenExtensionCtx.tokenMintWithProgramA.tokenProgram,
tokenProgramB: tokenExtensionCtx.tokenMintWithProgramB.tokenProgram, tokenProgramB: tokenExtensionCtx.tokenMintWithProgramB.tokenProgram,
}); });
txBuilder.addInstruction(liquidityIx); txBuilder.addInstruction(liquidityIx);
const txPayload = await txBuilder.build({ const txPayload = await txBuilder.build({
maxSupportedTransactionVersion: "legacy" maxSupportedTransactionVersion: "legacy",
}); });
if (txPayload.transaction instanceof Transaction) { if (txPayload.transaction instanceof Transaction) {
try { try {
const txId = await sendTx( const txId = await sendTx(agent, txPayload.transaction, [
agent, positionMintKeypair,
txPayload.transaction, tokenVaultAKeypair,
[positionMintKeypair, tokenVaultAKeypair, tokenVaultBKeypair], tokenVaultBKeypair,
); ]);
return txId; return txId;
} catch (error) { } catch (error) {
throw new Error(`Failed to create pool: ${JSON.stringify(error)}`); throw new Error(`Failed to create pool: ${JSON.stringify(error)}`);
} }
} else { } else {
throw new Error('Failed to create pool: Transaction not created'); throw new Error("Failed to create pool: Transaction not created");
} }
} }

View File

@@ -1,18 +1,29 @@
import { SolanaAgentKit } from "../index"; import { SolanaAgent } from "../index";
import { generateSigner, keypairIdentity, publicKey } from "@metaplex-foundation/umi"; import {
import { createCollection, mplCore, ruleSet } from "@metaplex-foundation/mpl-core"; generateSigner,
keypairIdentity,
publicKey,
} from "@metaplex-foundation/umi";
import {
createCollection,
mplCore,
ruleSet,
} from "@metaplex-foundation/mpl-core";
import { CollectionOptions, CollectionDeployment } from "../types"; import { CollectionOptions, CollectionDeployment } from "../types";
import { fromWeb3JsKeypair, toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters"; import {
fromWeb3JsKeypair,
toWeb3JsPublicKey,
} from "@metaplex-foundation/umi-web3js-adapters";
import { createUmi } from "@metaplex-foundation/umi-bundle-defaults"; import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";
/** /**
* Deploy a new NFT collection * Deploy a new NFT collection
* @param agent SolanaAgentKit instance * @param agent SolanaAgent instance
* @param options Collection options including name, URI, royalties, and creators * @param options Collection options including name, URI, royalties, and creators
* @returns Object containing collection address and metadata * @returns Object containing collection address and metadata
*/ */
export async function deploy_collection( export async function deploy_collection(
agent: SolanaAgentKit, agent: SolanaAgent,
options: CollectionOptions, options: CollectionOptions,
): Promise<CollectionDeployment> { ): Promise<CollectionDeployment> {
try { try {
@@ -28,11 +39,11 @@ export async function deploy_collection(
address: publicKey(creator.address), address: publicKey(creator.address),
percentage: creator.percentage, percentage: creator.percentage,
})) || [ })) || [
{ {
address: publicKey(agent.wallet_address.toString()), address: publicKey(agent.wallet_address.toString()),
percentage: 100, percentage: 100,
}, },
]; ];
// Create collection // Create collection
const tx = await createCollection(umi, { const tx = await createCollection(umi, {

View File

@@ -1,13 +1,21 @@
import { SolanaAgentKit } from "../index"; import { SolanaAgent } from "../index";
import { PublicKey } from "@solana/web3.js"; import { PublicKey } from "@solana/web3.js";
import { createUmi } from "@metaplex-foundation/umi-bundle-defaults"; import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";
import { generateSigner, keypairIdentity } from "@metaplex-foundation/umi"; import { generateSigner, keypairIdentity } from "@metaplex-foundation/umi";
import { createFungible, mintV1, TokenStandard } from "@metaplex-foundation/mpl-token-metadata"; import {
import { fromWeb3JsKeypair, fromWeb3JsPublicKey, toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters"; createFungible,
mintV1,
TokenStandard,
} from "@metaplex-foundation/mpl-token-metadata";
import {
fromWeb3JsKeypair,
fromWeb3JsPublicKey,
toWeb3JsPublicKey,
} from "@metaplex-foundation/umi-web3js-adapters";
/** /**
* Deploy a new SPL token * Deploy a new SPL token
* @param agent SolanaAgentKit instance * @param agent SolanaAgent instance
* @param name Name of the token * @param name Name of the token
* @param uri URI for the token metadata * @param uri URI for the token metadata
* @param symbol Symbol of the token * @param symbol Symbol of the token
@@ -16,16 +24,16 @@ import { fromWeb3JsKeypair, fromWeb3JsPublicKey, toWeb3JsPublicKey } from "@meta
* @returns Object containing token mint address and initial account (if supply was minted) * @returns Object containing token mint address and initial account (if supply was minted)
*/ */
export async function deploy_token( export async function deploy_token(
agent: SolanaAgentKit, agent: SolanaAgent,
name: string, name: string,
uri: string, uri: string,
symbol: string, symbol: string,
decimals: number = 9, decimals: number = 9,
initialSupply?: number initialSupply?: number,
): Promise<{ mint: PublicKey }> { ): Promise<{ mint: PublicKey }> {
try { try {
// Create UMI instance from agent // Create UMI instance from agent
const umi = createUmi(agent.connection.rpcEndpoint) const umi = createUmi(agent.connection.rpcEndpoint);
umi.use(keypairIdentity(fromWeb3JsKeypair(agent.wallet))); umi.use(keypairIdentity(fromWeb3JsKeypair(agent.wallet)));
// Create new token mint // Create new token mint
@@ -51,11 +59,11 @@ export async function deploy_token(
tokenStandard: TokenStandard.Fungible, tokenStandard: TokenStandard.Fungible,
tokenOwner: fromWeb3JsPublicKey(agent.wallet_address), tokenOwner: fromWeb3JsPublicKey(agent.wallet_address),
amount: initialSupply, amount: initialSupply,
}) }),
); );
} }
builder.sendAndConfirm(umi, { confirm: { commitment: 'finalized' } }); builder.sendAndConfirm(umi, { confirm: { commitment: "finalized" } });
return { return {
mint: toWeb3JsPublicKey(mint.publicKey), mint: toWeb3JsPublicKey(mint.publicKey),

View File

@@ -1,20 +1,13 @@
import { SolanaAgentKit } from "../index"; import { PublicKey } from "@solana/web3.js";
import { Tool } from "langchain/tools";
/** /**
* Fetch the price of a given token in USDC using Jupiter API * Fetch the price of a given token quoted in USDC using Jupiter API
* @param agent SolanaAgentKit instance
* @param tokenId The token mint address * @param tokenId The token mint address
* @returns The price of the token in USDC * @returns The price of the token quoted in USDC
*/ */
export async function fetchPrice( export async function fetchPrice(tokenId: PublicKey): Promise<string> {
agent: SolanaAgentKit,
tokenId: string
): Promise<string> {
try { try {
const response = await fetch( const response = await fetch(`https://api.jup.ag/price/v2?ids=${tokenId}`);
`https://api.jup.ag/price/v2?ids=${tokenId}`
);
if (!response.ok) { if (!response.ok) {
throw new Error(`Failed to fetch price: ${response.statusText}`); throw new Error(`Failed to fetch price: ${response.statusText}`);
@@ -22,7 +15,7 @@ export async function fetchPrice(
const data = await response.json(); const data = await response.json();
const price = data.data[tokenId]?.price; const price = data.data[tokenId.toBase58()]?.price;
if (!price) { if (!price) {
throw new Error("Price data not available for the given token."); throw new Error("Price data not available for the given token.");
@@ -32,4 +25,4 @@ export async function fetchPrice(
} catch (error: any) { } catch (error: any) {
throw new Error(`Price fetch failed: ${error.message}`); throw new Error(`Price fetch failed: ${error.message}`);
} }
} }

View File

@@ -1,14 +1,14 @@
import { LAMPORTS_PER_SOL, PublicKey } from "@solana/web3.js"; import { LAMPORTS_PER_SOL, PublicKey } from "@solana/web3.js";
import { SolanaAgentKit } from "../index"; import { SolanaAgent } from "../index";
/** /**
* Get the balance of SOL or an SPL token for the agent's wallet * Get the balance of SOL or an SPL token for the agent's wallet
* @param agent - SolanaAgentKit instance * @param agent - SolanaAgent instance
* @param token_address - Optional SPL token mint address. If not provided, returns SOL balance * @param token_address - Optional SPL token mint address. If not provided, returns SOL balance
* @returns Promise resolving to the balance as a number (in UI units) or null if account doesn't exist * @returns Promise resolving to the balance as a number (in UI units) or null if account doesn't exist
*/ */
export async function get_balance( export async function get_balance(
agent: SolanaAgentKit, agent: SolanaAgent,
token_address?: PublicKey, token_address?: PublicKey,
): Promise<number | null> { ): Promise<number | null> {
if (!token_address) if (!token_address)

View File

@@ -1,6 +1,6 @@
import { getPrimaryDomain as _getPrimaryDomain } from "@bonfida/spl-name-service"; import { getPrimaryDomain as _getPrimaryDomain } from "@bonfida/spl-name-service";
import { PublicKey } from "@solana/web3.js"; import { PublicKey } from "@solana/web3.js";
import { SolanaAgentKit } from "../index"; import { SolanaAgent } from "../index";
/** /**
* Retrieves the primary .sol domain associated with a given Solana public key. * Retrieves the primary .sol domain associated with a given Solana public key.
@@ -9,29 +9,29 @@ import { SolanaAgentKit } from "../index";
* a specified Solana public key. If the primary domain is stale or an error occurs during * a specified Solana public key. If the primary domain is stale or an error occurs during
* the resolution, it throws an error. * the resolution, it throws an error.
* *
* @param agent SolanaAgentKit instance * @param agent SolanaAgent instance
* @param account The Solana public key for which to retrieve the primary domain * @param account The Solana public key for which to retrieve the primary domain
* @returns A promise that resolves to the primary .sol domain as a string * @returns A promise that resolves to the primary .sol domain as a string
* @throws Error if the domain is stale or if the domain resolution fails * @throws Error if the domain is stale or if the domain resolution fails
*/ */
export async function getPrimaryDomain( export async function getPrimaryDomain(
agent: SolanaAgentKit, agent: SolanaAgent,
account: PublicKey account: PublicKey,
): Promise<string> { ): Promise<string> {
try { try {
const { reverse, stale } = await _getPrimaryDomain( const { reverse, stale } = await _getPrimaryDomain(
agent.connection, agent.connection,
account account,
); );
if (stale) { if (stale) {
throw new Error( throw new Error(
`Primary domain is stale for account: ${account.toBase58()}` `Primary domain is stale for account: ${account.toBase58()}`,
); );
} }
return reverse; return reverse;
} catch (error) { } catch (error) {
throw new Error( throw new Error(
`Failed to get primary domain for account: ${account.toBase58()}` `Failed to get primary domain for account: ${account.toBase58()}`,
); );
} }
} }

View File

@@ -1,4 +1,4 @@
import { SolanaAgentKit } from "../index"; import { SolanaAgent } from "../index";
export async function getTPS(agent: SolanaAgentKit): Promise<number> { export async function getTPS(agent: SolanaAgentKit): Promise<number> {
const perfSamples = await agent.connection.getRecentPerformanceSamples(); const perfSamples = await agent.connection.getRecentPerformanceSamples();

View File

@@ -15,8 +15,8 @@ export * from "./get_token_data";
export * from "./stake_with_jup"; export * from "./stake_with_jup";
export * from "./fetch_price"; export * from "./fetch_price";
export * from "./send_compressed_airdrop"; export * from "./send_compressed_airdrop";
export * from "./create_orca_single_sided_whirlpool";
export * from "./create_orca_single_sided_whirlpool";export * from "./raydium_create_ammV4"; export * from "./raydium_create_ammV4";
export * from "./raydium_create_clmm"; export * from "./raydium_create_clmm";
export * from "./raydium_create_cpmm"; export * from "./raydium_create_cpmm";
export * from "./openbook_create_market"; export * from "./openbook_create_market";

View File

@@ -55,7 +55,7 @@ async function uploadMetadata(
} }
async function createTokenTransaction( async function createTokenTransaction(
agent: SolanaAgentKit, agent: SolanaAgent,
mintKeypair: Keypair, mintKeypair: Keypair,
metadataResponse: any, metadataResponse: any,
options?: PumpFunTokenOptions, options?: PumpFunTokenOptions,
@@ -140,7 +140,7 @@ async function signAndSendTransaction(
/** /**
* Launch a token on Pump.fun * Launch a token on Pump.fun
* @param agent - SolanaAgentKit instance * @param agent - SolanaAgent instance
* @param tokenName - Name of the token * @param tokenName - Name of the token
* @param tokenTicker - Ticker of the token * @param tokenTicker - Ticker of the token
* @param description - Description of the token * @param description - Description of the token
@@ -149,7 +149,7 @@ async function signAndSendTransaction(
* @returns - Signature of the transaction, mint address and metadata URI, if successful, else error * @returns - Signature of the transaction, mint address and metadata URI, if successful, else error
*/ */
export async function launchPumpFunToken( export async function launchPumpFunToken(
agent: SolanaAgentKit, agent: SolanaAgent,
tokenName: string, tokenName: string,
tokenTicker: string, tokenTicker: string,
description: string, description: string,

View File

@@ -1,16 +1,15 @@
import { VersionedTransaction } from "@solana/web3.js"; import { VersionedTransaction } from "@solana/web3.js";
import { LuloAccountDetailsResponse } from "../types"; import { SolanaAgent } from "../index";
import { SolanaAgentKit } from "../agent";
/** /**
* Lend tokens for yields using Lulo * Lend tokens for yields using Lulo
* @param agent SolanaAgentKit instance * @param agent SolanaAgent instance
* @param amount Amount of USDC to lend * @param amount Amount of USDC to lend
* @returns Transaction signature * @returns Transaction signature
*/ */
export async function lendAsset( export async function lendAsset(
agent: SolanaAgentKit, agent: SolanaAgent,
amount: number amount: number,
): Promise<string> { ): Promise<string> {
try { try {
const response = await fetch( const response = await fetch(
@@ -23,14 +22,14 @@ export async function lendAsset(
body: JSON.stringify({ body: JSON.stringify({
account: agent.wallet.publicKey.toBase58(), account: agent.wallet.publicKey.toBase58(),
}), }),
} },
); );
const data = await response.json(); const data = await response.json();
// Deserialize the transaction // Deserialize the transaction
const luloTxn = VersionedTransaction.deserialize( const luloTxn = VersionedTransaction.deserialize(
Buffer.from(data.transaction, "base64") Buffer.from(data.transaction, "base64"),
); );
// Get a recent blockhash and set it // Get a recent blockhash and set it

View File

@@ -1,22 +1,26 @@
import { SolanaAgentKit } from "../index"; import { SolanaAgent } from "../index";
import { generateSigner, keypairIdentity } from '@metaplex-foundation/umi'; import { generateSigner, keypairIdentity } from "@metaplex-foundation/umi";
import { create, mplCore } from '@metaplex-foundation/mpl-core'; import { create, mplCore } from "@metaplex-foundation/mpl-core";
import { fetchCollection } from '@metaplex-foundation/mpl-core'; import { fetchCollection } from "@metaplex-foundation/mpl-core";
import { PublicKey } from "@solana/web3.js"; import { PublicKey } from "@solana/web3.js";
import { fromWeb3JsKeypair, fromWeb3JsPublicKey, toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters"; import {
import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'; fromWeb3JsKeypair,
import { MintCollectionNFTResponse } from '../types'; fromWeb3JsPublicKey,
toWeb3JsPublicKey,
} from "@metaplex-foundation/umi-web3js-adapters";
import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";
import { MintCollectionNFTResponse } from "../types";
/** /**
* Mint a new NFT as part of an existing collection * Mint a new NFT as part of an existing collection
* @param agent SolanaAgentKit instance * @param agent SolanaAgent instance
* @param collectionMint Address of the collection's master NFT * @param collectionMint Address of the collection's master NFT
* @param metadata NFT metadata object * @param metadata NFT metadata object
* @param recipient Optional recipient address (defaults to wallet address) * @param recipient Optional recipient address (defaults to wallet address)
* @returns Object containing NFT mint address and token account * @returns Object containing NFT mint address and token account
*/ */
export async function mintCollectionNFT( export async function mintCollectionNFT(
agent: SolanaAgentKit, agent: SolanaAgent,
collectionMint: PublicKey, collectionMint: PublicKey,
metadata: { metadata: {
name: string; name: string;
@@ -27,7 +31,7 @@ export async function mintCollectionNFT(
share: number; share: number;
}>; }>;
}, },
recipient?: PublicKey recipient?: PublicKey,
): Promise<MintCollectionNFTResponse> { ): Promise<MintCollectionNFTResponse> {
try { try {
// Create UMI instance from agent // Create UMI instance from agent
@@ -49,15 +53,15 @@ export async function mintCollectionNFT(
collection: collection, collection: collection,
name: metadata.name, name: metadata.name,
uri: metadata.uri, uri: metadata.uri,
owner: fromWeb3JsPublicKey(recipient ?? agent.wallet.publicKey) owner: fromWeb3JsPublicKey(recipient ?? agent.wallet.publicKey),
}).sendAndConfirm(umi); }).sendAndConfirm(umi);
return { return {
mint: toWeb3JsPublicKey(assetSigner.publicKey), mint: toWeb3JsPublicKey(assetSigner.publicKey),
// Note: Token account is now handled automatically by the create instruction // Note: Token account is now handled automatically by the create instruction
metadata: toWeb3JsPublicKey(assetSigner.publicKey) metadata: toWeb3JsPublicKey(assetSigner.publicKey),
}; };
} catch (error: any) { } catch (error: any) {
throw new Error(`Collection NFT minting failed: ${error.message}`); throw new Error(`Collection NFT minting failed: ${error.message}`);
} }
} }

View File

@@ -1,33 +1,34 @@
import { OPEN_BOOK_PROGRAM, Raydium, TxVersion } from "@raydium-io/raydium-sdk-v2"; import {
OPEN_BOOK_PROGRAM,
Raydium,
TxVersion,
} from "@raydium-io/raydium-sdk-v2";
import { MintLayout, TOKEN_PROGRAM_ID } from "@solana/spl-token"; import { MintLayout, TOKEN_PROGRAM_ID } from "@solana/spl-token";
import { PublicKey } from "@solana/web3.js"; import { PublicKey } from "@solana/web3.js";
import { SolanaAgentKit } from "../agent"; import { SolanaAgent } from "../index";
export async function openbookCreateMarket( export async function openbookCreateMarket(
agent: SolanaAgentKit, agent: SolanaAgent,
baseMint: PublicKey, baseMint: PublicKey,
quoteMint: PublicKey, quoteMint: PublicKey,
lotSize: number = 1, lotSize: number = 1,
tickSize: number = 0.01, tickSize: number = 0.01,
): Promise<string[]> { ): Promise<string[]> {
const raydium = await Raydium.load({ const raydium = await Raydium.load({
owner: agent.wallet, owner: agent.wallet,
connection: agent.connection, connection: agent.connection,
}) });
const baseMintInfo = await agent.connection.getAccountInfo(baseMint) const baseMintInfo = await agent.connection.getAccountInfo(baseMint);
const quoteMintInfo = await agent.connection.getAccountInfo(quoteMint) const quoteMintInfo = await agent.connection.getAccountInfo(quoteMint);
if ( if (
baseMintInfo?.owner.toString() !== TOKEN_PROGRAM_ID.toBase58() || baseMintInfo?.owner.toString() !== TOKEN_PROGRAM_ID.toBase58() ||
quoteMintInfo?.owner.toString() !== TOKEN_PROGRAM_ID.toBase58() quoteMintInfo?.owner.toString() !== TOKEN_PROGRAM_ID.toBase58()
) { ) {
throw new Error( throw new Error(
'openbook market only support TOKEN_PROGRAM_ID mints, if you want to create pool with token-2022, please create raydium cpmm pool instead' "openbook market only support TOKEN_PROGRAM_ID mints, if you want to create pool with token-2022, please create raydium cpmm pool instead",
) );
} }
const { execute } = await raydium.marketV2.create({ const { execute } = await raydium.marketV2.create({
@@ -44,9 +45,9 @@ export async function openbookCreateMarket(
dexProgramId: OPEN_BOOK_PROGRAM, dexProgramId: OPEN_BOOK_PROGRAM,
txVersion: TxVersion.V0, txVersion: TxVersion.V0,
}) });
const { txIds } = await execute({ sequentially: true, }) const { txIds } = await execute({ sequentially: true });
return txIds return txIds;
} }

View File

@@ -1,42 +1,59 @@
import { AMM_V4, FEE_DESTINATION_ID, MARKET_STATE_LAYOUT_V3, OPEN_BOOK_PROGRAM, Raydium, TxVersion } from "@raydium-io/raydium-sdk-v2"; import {
AMM_V4,
FEE_DESTINATION_ID,
MARKET_STATE_LAYOUT_V3,
OPEN_BOOK_PROGRAM,
Raydium,
TxVersion,
} from "@raydium-io/raydium-sdk-v2";
import { MintLayout, TOKEN_PROGRAM_ID } from "@solana/spl-token"; import { MintLayout, TOKEN_PROGRAM_ID } from "@solana/spl-token";
import { PublicKey } from "@solana/web3.js"; import { PublicKey } from "@solana/web3.js";
import BN from 'bn.js'; import BN from "bn.js";
import { SolanaAgentKit } from "../agent"; import { SolanaAgent } from "../index";
export async function raydiumCreateAmmV4( export async function raydiumCreateAmmV4(
agent: SolanaAgentKit, agent: SolanaAgent,
marketId: PublicKey, marketId: PublicKey,
baseAmount: BN, baseAmount: BN,
quoteAmount: BN, quoteAmount: BN,
startTime: BN, startTime: BN,
): Promise<string> { ): Promise<string> {
const raydium = await Raydium.load({ const raydium = await Raydium.load({
owner: agent.wallet, owner: agent.wallet,
connection: agent.connection, connection: agent.connection,
}) });
const marketBufferInfo = await agent.connection.getAccountInfo(new PublicKey(marketId)) const marketBufferInfo = await agent.connection.getAccountInfo(
const { baseMint, quoteMint } = MARKET_STATE_LAYOUT_V3.decode(marketBufferInfo!.data) new PublicKey(marketId),
);
const { baseMint, quoteMint } = MARKET_STATE_LAYOUT_V3.decode(
marketBufferInfo!.data,
);
const baseMintInfo = await agent.connection.getAccountInfo(baseMint) const baseMintInfo = await agent.connection.getAccountInfo(baseMint);
const quoteMintInfo = await agent.connection.getAccountInfo(quoteMint) const quoteMintInfo = await agent.connection.getAccountInfo(quoteMint);
if ( if (
baseMintInfo?.owner.toString() !== TOKEN_PROGRAM_ID.toBase58() || baseMintInfo?.owner.toString() !== TOKEN_PROGRAM_ID.toBase58() ||
quoteMintInfo?.owner.toString() !== TOKEN_PROGRAM_ID.toBase58() quoteMintInfo?.owner.toString() !== TOKEN_PROGRAM_ID.toBase58()
) { ) {
throw new Error( throw new Error(
'amm pools with openbook market only support TOKEN_PROGRAM_ID mints, if you want to create pool with token-2022, please create cpmm pool instead' "amm pools with openbook market only support TOKEN_PROGRAM_ID mints, if you want to create pool with token-2022, please create cpmm pool instead",
) );
} }
if (baseAmount.mul(quoteAmount).lte(new BN(1).mul(new BN(10 ** MintLayout.decode(baseMintInfo.data).decimals)).pow(new BN(2)))) { if (
throw new Error('initial liquidity too low, try adding more baseAmount/quoteAmount') baseAmount
.mul(quoteAmount)
.lte(
new BN(1)
.mul(new BN(10 ** MintLayout.decode(baseMintInfo.data).decimals))
.pow(new BN(2)),
)
) {
throw new Error(
"initial liquidity too low, try adding more baseAmount/quoteAmount",
);
} }
const { execute } = await raydium.liquidity.createPoolV4({ const { execute } = await raydium.liquidity.createPoolV4({
@@ -63,9 +80,9 @@ export async function raydiumCreateAmmV4(
associatedOnly: false, associatedOnly: false,
txVersion: TxVersion.V0, txVersion: TxVersion.V0,
feeDestinationId: FEE_DESTINATION_ID, feeDestinationId: FEE_DESTINATION_ID,
}) });
const { txId } = await execute({ sendAndConfirm: true }) const { txId } = await execute({ sendAndConfirm: true });
return txId return txId;
} }

View File

@@ -1,55 +1,58 @@
import { CLMM_PROGRAM_ID, Raydium, TxVersion } from '@raydium-io/raydium-sdk-v2' import {
import { MintLayout } from '@solana/spl-token' CLMM_PROGRAM_ID,
import { PublicKey } from '@solana/web3.js' Raydium,
import BN from 'bn.js' TxVersion,
import Decimal from 'decimal.js' } from "@raydium-io/raydium-sdk-v2";
import { SolanaAgentKit } from '../agent' import { MintLayout } from "@solana/spl-token";
import { PublicKey } from "@solana/web3.js";
import BN from "bn.js";
import Decimal from "decimal.js";
import { SolanaAgent } from "../index";
export async function raydiumCreateClmm( export async function raydiumCreateClmm(
agent: SolanaAgentKit, agent: SolanaAgent,
mint1: PublicKey, mint1: PublicKey,
mint2: PublicKey, mint2: PublicKey,
configId: PublicKey, configId: PublicKey,
initialPrice: Decimal, initialPrice: Decimal,
startTime: BN, startTime: BN,
): Promise<string> { ): Promise<string> {
const raydium = await Raydium.load({ const raydium = await Raydium.load({
owner: agent.wallet, owner: agent.wallet,
connection: agent.connection, connection: agent.connection,
}) });
const [mintInfo1, mintInfo2] = await agent.connection.getMultipleAccountsInfo([mint1, mint2]) const [mintInfo1, mintInfo2] = await agent.connection.getMultipleAccountsInfo(
if (mintInfo1 === null || mintInfo2 === null) throw Error('fetch mint info error') [mint1, mint2],
);
if (mintInfo1 === null || mintInfo2 === null)
throw Error("fetch mint info error");
const mintDecodeInfo1 = MintLayout.decode(mintInfo1.data) const mintDecodeInfo1 = MintLayout.decode(mintInfo1.data);
const mintDecodeInfo2 = MintLayout.decode(mintInfo2.data) const mintDecodeInfo2 = MintLayout.decode(mintInfo2.data);
const mintFormatInfo1 = { const mintFormatInfo1 = {
chainId: 101, chainId: 101,
address: mint1.toString(), address: mint1.toString(),
programId: mintInfo1.owner.toString(), programId: mintInfo1.owner.toString(),
logoURI: '', logoURI: "",
symbol: '', symbol: "",
name: '', name: "",
decimals: mintDecodeInfo1.decimals, decimals: mintDecodeInfo1.decimals,
tags: [], tags: [],
extensions: {} extensions: {},
} };
const mintFormatInfo2 = { const mintFormatInfo2 = {
chainId: 101, chainId: 101,
address: mint2.toString(), address: mint2.toString(),
programId: mintInfo2.owner.toString(), programId: mintInfo2.owner.toString(),
logoURI: '', logoURI: "",
symbol: '', symbol: "",
name: '', name: "",
decimals: mintDecodeInfo2.decimals, decimals: mintDecodeInfo2.decimals,
tags: [], tags: [],
extensions: {} extensions: {},
} };
const { execute } = await raydium.clmm.createPool({ const { execute } = await raydium.clmm.createPool({
programId: CLMM_PROGRAM_ID, programId: CLMM_PROGRAM_ID,
@@ -65,9 +68,9 @@ export async function raydiumCreateClmm(
// units: 600000, // units: 600000,
// microLamports: 46591500, // microLamports: 46591500,
// }, // },
}) });
const { txId } = await execute({ sendAndConfirm: true }) const { txId } = await execute({ sendAndConfirm: true });
return txId return txId;
} }

View File

@@ -2,60 +2,58 @@ import {
CREATE_CPMM_POOL_FEE_ACC, CREATE_CPMM_POOL_FEE_ACC,
CREATE_CPMM_POOL_PROGRAM, CREATE_CPMM_POOL_PROGRAM,
Raydium, Raydium,
TxVersion TxVersion,
} from '@raydium-io/raydium-sdk-v2' } from "@raydium-io/raydium-sdk-v2";
import { MintLayout } from '@solana/spl-token' import { MintLayout } from "@solana/spl-token";
import { PublicKey } from '@solana/web3.js' import { PublicKey } from "@solana/web3.js";
import BN from 'bn.js' import BN from "bn.js";
import { SolanaAgentKit } from '../agent' import { SolanaAgent } from "../index";
export async function raydiumCreateCpmm( export async function raydiumCreateCpmm(
agent: SolanaAgentKit, agent: SolanaAgent,
mintA: PublicKey, mintA: PublicKey,
mintB: PublicKey, mintB: PublicKey,
configId: PublicKey, configId: PublicKey,
mintAAmount: BN, mintAAmount: BN,
mintBAmount: BN, mintBAmount: BN,
startTime: BN, startTime: BN,
): Promise<string> { ): Promise<string> {
const raydium = await Raydium.load({ const raydium = await Raydium.load({
owner: agent.wallet, owner: agent.wallet,
connection: agent.connection, connection: agent.connection,
}) });
const [mintInfoA, mintInfoB] = await agent.connection.getMultipleAccountsInfo([mintA, mintB]) const [mintInfoA, mintInfoB] = await agent.connection.getMultipleAccountsInfo(
if (mintInfoA === null || mintInfoB === null) throw Error('fetch mint info error') [mintA, mintB],
);
if (mintInfoA === null || mintInfoB === null)
throw Error("fetch mint info error");
const mintDecodeInfoA = MintLayout.decode(mintInfoA.data) const mintDecodeInfoA = MintLayout.decode(mintInfoA.data);
const mintDecodeInfoB = MintLayout.decode(mintInfoB.data) const mintDecodeInfoB = MintLayout.decode(mintInfoB.data);
const mintFormatInfoA = { const mintFormatInfoA = {
chainId: 101, chainId: 101,
address: mintA.toString(), address: mintA.toString(),
programId: mintInfoA.owner.toString(), programId: mintInfoA.owner.toString(),
logoURI: '', logoURI: "",
symbol: '', symbol: "",
name: '', name: "",
decimals: mintDecodeInfoA.decimals, decimals: mintDecodeInfoA.decimals,
tags: [], tags: [],
extensions: {} extensions: {},
} };
const mintFormatInfoB = { const mintFormatInfoB = {
chainId: 101, chainId: 101,
address: mintB.toString(), address: mintB.toString(),
programId: mintInfoB.owner.toString(), programId: mintInfoB.owner.toString(),
logoURI: '', logoURI: "",
symbol: '', symbol: "",
name: '', name: "",
decimals: mintDecodeInfoB.decimals, decimals: mintDecodeInfoB.decimals,
tags: [], tags: [],
extensions: {} extensions: {},
} };
const { execute, extInfo } = await raydium.cpmm.createPool({ const { execute, extInfo } = await raydium.cpmm.createPool({
programId: CREATE_CPMM_POOL_PROGRAM, programId: CREATE_CPMM_POOL_PROGRAM,
@@ -76,9 +74,9 @@ export async function raydiumCreateCpmm(
// units: 600000, // units: 600000,
// microLamports: 46591500, // microLamports: 46591500,
// }, // },
}) });
const { txId } = await execute({ sendAndConfirm: true }) const { txId } = await execute({ sendAndConfirm: true });
return txId return txId;
} }

View File

@@ -1,18 +1,18 @@
import { registerDomainNameV2 } from "@bonfida/spl-name-service"; import { registerDomainNameV2 } from "@bonfida/spl-name-service";
import { Transaction } from "@solana/web3.js"; import { Transaction } from "@solana/web3.js";
import { SolanaAgentKit } from "../index"; import { SolanaAgent } from "../index";
import { getAssociatedTokenAddressSync } from "@solana/spl-token"; import { getAssociatedTokenAddressSync } from "@solana/spl-token";
import { TOKENS } from "../constants"; import { TOKENS } from "../constants";
/** /**
* Register a .sol domain name using Bonfida Name Service * Register a .sol domain name using Bonfida Name Service
* @param agent SolanaAgentKit instance * @param agent SolanaAgent instance
* @param name Domain name to register (without .sol) * @param name Domain name to register (without .sol)
* @param spaceKB Space allocation in KB (max 10KB) * @param spaceKB Space allocation in KB (max 10KB)
* @returns Transaction signature * @returns Transaction signature
*/ */
export async function registerDomain( export async function registerDomain(
agent: SolanaAgentKit, agent: SolanaAgent,
name: string, name: string,
spaceKB: number = 1, spaceKB: number = 1,
): Promise<string> { ): Promise<string> {

View File

@@ -1,14 +1,14 @@
import { SolanaAgentKit } from "../index"; import { SolanaAgent } from "../index";
import { LAMPORTS_PER_SOL } from "@solana/web3.js"; import { LAMPORTS_PER_SOL } from "@solana/web3.js";
/** /**
* Request SOL from the Solana faucet (devnet/testnet only) * Request SOL from the Solana faucet (devnet/testnet only)
* @param agent - SolanaAgentKit instance * @param agent - SolanaAgent instance
* @returns Transaction signature * @returns Transaction signature
* @throws Error if the request fails or times out * @throws Error if the request fails or times out
*/ */
export async function request_faucet_funds( export async function request_faucet_funds(
agent: SolanaAgentKit, agent: SolanaAgent,
): Promise<string> { ): Promise<string> {
const tx = await agent.connection.requestAirdrop( const tx = await agent.connection.requestAirdrop(
agent.wallet_address, agent.wallet_address,

View File

@@ -1,6 +1,6 @@
import { resolve } from "@bonfida/spl-name-service"; import { resolve } from "@bonfida/spl-name-service";
import { PublicKey } from "@solana/web3.js"; import { PublicKey } from "@solana/web3.js";
import { SolanaAgentKit } from "../index"; import { SolanaAgent } from "../index";
/** /**
* Resolves a .sol domain to a Solana PublicKey. * Resolves a .sol domain to a Solana PublicKey.
@@ -9,14 +9,14 @@ import { SolanaAgentKit } from "../index";
* to the corresponding Solana PublicKey. The domain can be provided with or without * to the corresponding Solana PublicKey. The domain can be provided with or without
* the .sol suffix. * the .sol suffix.
* *
* @param agent SolanaAgentKit instance * @param agent SolanaAgent instance
* @param domain The .sol domain to resolve. This can be provided with or without the .sol TLD suffix * @param domain The .sol domain to resolve. This can be provided with or without the .sol TLD suffix
* @returns A promise that resolves to the corresponding Solana PublicKey * @returns A promise that resolves to the corresponding Solana PublicKey
* @throws Error if the domain resolution fails * @throws Error if the domain resolution fails
*/ */
export async function resolveSolDomain( export async function resolveSolDomain(
agent: SolanaAgentKit, agent: SolanaAgent,
domain: string domain: string,
): Promise<PublicKey> { ): Promise<PublicKey> {
if (!domain || typeof domain !== "string") { if (!domain || typeof domain !== "string") {
throw new Error("Invalid domain. Expected a non-empty string."); throw new Error("Invalid domain. Expected a non-empty string.");

View File

@@ -1,12 +1,11 @@
import { import {
AddressLookupTableAccount, AddressLookupTableAccount,
ComputeBudgetProgram, ComputeBudgetProgram,
Connection,
Keypair, Keypair,
PublicKey, PublicKey,
TransactionInstruction, TransactionInstruction,
} from "@solana/web3.js"; } from "@solana/web3.js";
import { SolanaAgentKit } from "../agent/index.js"; import { SolanaAgent } from "../index";
import { import {
buildAndSignTx, buildAndSignTx,
calculateComputeUnitPrice, calculateComputeUnitPrice,
@@ -33,7 +32,7 @@ const MAX_CONCURRENT_TXS = 30;
*/ */
export const getAirdropCostEstimate = ( export const getAirdropCostEstimate = (
numberOfRecipients: number, numberOfRecipients: number,
priorityFeeInLamports: number priorityFeeInLamports: number,
) => { ) => {
const baseFee = 5000; const baseFee = 5000;
const perRecipientCompressedStateFee = 300; const perRecipientCompressedStateFee = 300;
@@ -57,28 +56,27 @@ export const getAirdropCostEstimate = (
* @param shouldLog Whether to log progress to stdout. Defaults to false. * @param shouldLog Whether to log progress to stdout. Defaults to false.
*/ */
export async function sendCompressedAirdrop( export async function sendCompressedAirdrop(
agent: SolanaAgentKit, agent: SolanaAgent,
mintAddress: PublicKey, mintAddress: PublicKey,
amount: number, amount: number,
decimals: number, decimals: number,
recipients: PublicKey[], recipients: PublicKey[],
priorityFeeInLamports: number, priorityFeeInLamports: number,
shouldLog: boolean = false shouldLog: boolean = false,
): Promise<string[]> { ): Promise<string[]> {
if (recipients.length > MAX_AIRDROP_RECIPIENTS) { if (recipients.length > MAX_AIRDROP_RECIPIENTS) {
throw new Error( throw new Error(
`Max airdrop can be ${MAX_AIRDROP_RECIPIENTS} recipients at a time. For more scale, use open source ZK Compression airdrop tools such as https://github.com/helius-labs/airship.` `Max airdrop can be ${MAX_AIRDROP_RECIPIENTS} recipients at a time. For more scale, use open source ZK Compression airdrop tools such as https://github.com/helius-labs/airship.`,
); );
} }
const url = agent.connection.rpcEndpoint; const url = agent.connection.rpcEndpoint;
if (url.includes("devnet")) { if (url.includes("devnet")) {
throw new Error("Devnet is not supported for airdrop. Please use mainnet."); throw new Error("Devnet is not supported for airdrop. Please use mainnet.");
} }
if (!url.includes("helius")) { if (!url.includes("helius")) {
console.warn( console.warn(
"Warning: Must use RPC with ZK Compression support. Double check with your RPC provider if in doubt." "Warning: Must use RPC with ZK Compression support. Double check with your RPC provider if in doubt.",
); );
} }
@@ -88,11 +86,11 @@ export async function sendCompressedAirdrop(
agent.connection, agent.connection,
agent.wallet, agent.wallet,
mintAddress, mintAddress,
agent.wallet.publicKey agent.wallet.publicKey,
); );
} catch (error) { } catch (error) {
throw new Error( throw new Error(
"Source token account not found and failed to create it. Please add funds to your wallet and try again." "Source token account not found and failed to create it. Please add funds to your wallet and try again.",
); );
} }
@@ -100,7 +98,7 @@ export async function sendCompressedAirdrop(
await createTokenPool( await createTokenPool(
agent.connection as unknown as Rpc, agent.connection as unknown as Rpc,
agent.wallet, agent.wallet,
mintAddress mintAddress,
); );
} catch (error: any) { } catch (error: any) {
if (error.message.includes("already in use")) { if (error.message.includes("already in use")) {
@@ -116,17 +114,17 @@ export async function sendCompressedAirdrop(
mintAddress, mintAddress,
recipients, recipients,
priorityFeeInLamports, priorityFeeInLamports,
shouldLog shouldLog,
); );
} }
async function processAll( async function processAll(
agent: SolanaAgentKit, agent: SolanaAgent,
amount: number, amount: number,
mint: PublicKey, mint: PublicKey,
recipients: PublicKey[], recipients: PublicKey[],
priorityFeeInLamports: number, priorityFeeInLamports: number,
shouldLog: boolean shouldLog: boolean,
): Promise<string[]> { ): Promise<string[]> {
const mintAddress = mint; const mintAddress = mint;
const payer = agent.wallet; const payer = agent.wallet;
@@ -135,13 +133,13 @@ async function processAll(
agent.connection, agent.connection,
agent.wallet, agent.wallet,
mintAddress, mintAddress,
agent.wallet.publicKey agent.wallet.publicKey,
); );
const maxRecipientsPerInstruction = 5; const maxRecipientsPerInstruction = 5;
const maxIxs = 3; // empirically determined (as of 12/15/2024) const maxIxs = 3; // empirically determined (as of 12/15/2024)
const lookupTableAddress = new PublicKey( const lookupTableAddress = new PublicKey(
"9NYFyEqPkyXUhkerbGHXUXkvb4qpzeEdHuGpgbgpH1NJ" "9NYFyEqPkyXUhkerbGHXUXkvb4qpzeEdHuGpgbgpH1NJ",
); );
const lookupTableAccount = ( const lookupTableAccount = (
@@ -164,7 +162,7 @@ async function processAll(
ComputeBudgetProgram.setComputeUnitPrice({ ComputeBudgetProgram.setComputeUnitPrice({
microLamports: calculateComputeUnitPrice( microLamports: calculateComputeUnitPrice(
priorityFeeInLamports, priorityFeeInLamports,
500_000 500_000,
), ),
}), }),
]; ];
@@ -184,13 +182,13 @@ async function processAll(
toAddress: batch, toAddress: batch,
amount: batch.map(() => amount), amount: batch.map(() => amount),
mint: mintAddress, mint: mintAddress,
}) }),
); );
} }
const compressIxs = await Promise.all(compressIxPromises); const compressIxs = await Promise.all(compressIxPromises);
return [...instructions, ...compressIxs]; return [...instructions, ...compressIxs];
}) }),
); );
const url = agent.connection.rpcEndpoint; const url = agent.connection.rpcEndpoint;
@@ -225,12 +223,12 @@ async function processAll(
instructions, instructions,
payer, payer,
lookupTableAccount, lookupTableAccount,
i + idx i + idx,
).then((signature) => { ).then((signature) => {
confirmedCount++; confirmedCount++;
log("\r" + renderProgressBar(confirmedCount, totalBatches)); log("\r" + renderProgressBar(confirmedCount, totalBatches));
return signature; return signature;
}) }),
); );
const batchResults = await Promise.allSettled(batchPromises); const batchResults = await Promise.allSettled(batchPromises);
@@ -250,7 +248,7 @@ async function processAll(
throw new Error( throw new Error(
`Failed to process ${failures.length} batches: ${failures `Failed to process ${failures.length} batches: ${failures
.map((f) => f.error) .map((f) => f.error)
.join(", ")}` .join(", ")}`,
); );
} }
@@ -262,7 +260,7 @@ async function sendTransactionWithRetry(
instructions: TransactionInstruction[], instructions: TransactionInstruction[],
payer: Keypair, payer: Keypair,
lookupTableAccount: AddressLookupTableAccount, lookupTableAccount: AddressLookupTableAccount,
batchIndex: number batchIndex: number,
): Promise<string> { ): Promise<string> {
const MAX_RETRIES = 3; const MAX_RETRIES = 3;
const INITIAL_BACKOFF = 500; // ms const INITIAL_BACKOFF = 500; // ms
@@ -275,7 +273,7 @@ async function sendTransactionWithRetry(
payer, payer,
blockhash, blockhash,
[], [],
[lookupTableAccount] [lookupTableAccount],
); );
const signature = await sendAndConfirmTx(connection, tx); const signature = await sendAndConfirmTx(connection, tx);
@@ -292,7 +290,7 @@ async function sendTransactionWithRetry(
throw new Error( throw new Error(
`Batch ${batchIndex} failed after ${attempt + 1} attempts: ${ `Batch ${batchIndex} failed after ${attempt + 1} attempts: ${
error.message error.message
}` }`,
); );
} }

View File

@@ -1,14 +1,14 @@
import { VersionedTransaction } from "@solana/web3.js"; import { VersionedTransaction } from "@solana/web3.js";
import { SolanaAgentKit } from "../agent"; import { SolanaAgent } from "../index";
/** /**
* Stake SOL with Jup validator * Stake SOL with Jup validator
* @param agent SolanaAgentKit instance * @param agent SolanaAgent instance
* @param amount Amount of SOL to stake * @param amount Amount of SOL to stake
* @returns Transaction signature * @returns Transaction signature
*/ */
export async function stakeWithJup( export async function stakeWithJup(
agent: SolanaAgentKit, agent: SolanaAgent,
amount: number, amount: number,
): Promise<string> { ): Promise<string> {
try { try {

View File

@@ -1,10 +1,14 @@
import { VersionedTransaction, PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js"; import {
import { SolanaAgentKit } from "../index"; VersionedTransaction,
PublicKey,
LAMPORTS_PER_SOL,
} from "@solana/web3.js";
import { SolanaAgent } from "../index";
import { TOKENS, DEFAULT_OPTIONS, JUP_API } from "../constants"; import { TOKENS, DEFAULT_OPTIONS, JUP_API } from "../constants";
/** /**
* Swap tokens using Jupiter Exchange * Swap tokens using Jupiter Exchange
* @param agent SolanaAgentKit instance * @param agent SolanaAgent instance
* @param outputMint Target token mint address * @param outputMint Target token mint address
* @param inputAmount Amount to swap (in token decimals) * @param inputAmount Amount to swap (in token decimals)
* @param inputMint Source token mint address (defaults to USDC) * @param inputMint Source token mint address (defaults to USDC)
@@ -12,7 +16,7 @@ import { TOKENS, DEFAULT_OPTIONS, JUP_API } from "../constants";
* @returns Transaction signature * @returns Transaction signature
*/ */
export async function trade( export async function trade(
agent: SolanaAgentKit, agent: SolanaAgent,
outputMint: PublicKey, outputMint: PublicKey,
inputAmount: number, inputAmount: number,
inputMint: PublicKey = TOKENS.USDC, inputMint: PublicKey = TOKENS.USDC,

View File

@@ -1,29 +1,25 @@
import { SolanaAgentKit } from "../index"; import { SolanaAgent } from "../index";
import { import { PublicKey, SystemProgram, Transaction } from "@solana/web3.js";
PublicKey,
SystemProgram,
Transaction
} from "@solana/web3.js";
import { LAMPORTS_PER_SOL } from "@solana/web3.js"; import { LAMPORTS_PER_SOL } from "@solana/web3.js";
import { import {
getAssociatedTokenAddress, getAssociatedTokenAddress,
createTransferInstruction, createTransferInstruction,
getMint getMint,
} from "@solana/spl-token"; } from "@solana/spl-token";
/** /**
* Transfer SOL or SPL tokens to a recipient * Transfer SOL or SPL tokens to a recipient
* @param agent SolanaAgentKit instance * @param agent SolanaAgent instance
* @param to Recipient's public key * @param to Recipient's public key
* @param amount Amount to transfer * @param amount Amount to transfer
* @param mint Optional mint address for SPL tokens * @param mint Optional mint address for SPL tokens
* @returns Transaction signature * @returns Transaction signature
*/ */
export async function transfer( export async function transfer(
agent: SolanaAgentKit, agent: SolanaAgent,
to: PublicKey, to: PublicKey,
amount: number, amount: number,
mint?: PublicKey mint?: PublicKey,
): Promise<string> { ): Promise<string> {
try { try {
let tx: string; let tx: string;
@@ -34,19 +30,19 @@ export async function transfer(
SystemProgram.transfer({ SystemProgram.transfer({
fromPubkey: agent.wallet_address, fromPubkey: agent.wallet_address,
toPubkey: to, toPubkey: to,
lamports: amount * LAMPORTS_PER_SOL lamports: amount * LAMPORTS_PER_SOL,
}) }),
); );
tx = await agent.connection.sendTransaction( tx = await agent.connection.sendTransaction(transaction, [agent.wallet]);
transaction,
[agent.wallet]
);
} else { } else {
// Transfer SPL token // Transfer SPL token
const fromAta = await getAssociatedTokenAddress(mint, agent.wallet_address); const fromAta = await getAssociatedTokenAddress(
mint,
agent.wallet_address,
);
const toAta = await getAssociatedTokenAddress(mint, to); const toAta = await getAssociatedTokenAddress(mint, to);
// Get mint info to determine decimals // Get mint info to determine decimals
const mintInfo = await getMint(agent.connection, mint); const mintInfo = await getMint(agent.connection, mint);
const adjustedAmount = amount * Math.pow(10, mintInfo.decimals); const adjustedAmount = amount * Math.pow(10, mintInfo.decimals);
@@ -56,18 +52,15 @@ export async function transfer(
fromAta, fromAta,
toAta, toAta,
agent.wallet_address, agent.wallet_address,
adjustedAmount adjustedAmount,
) ),
); );
tx = await agent.connection.sendTransaction( tx = await agent.connection.sendTransaction(transaction, [agent.wallet]);
transaction,
[agent.wallet]
);
} }
return tx; return tx;
} catch (error: any) { } catch (error: any) {
throw new Error(`Transfer failed: ${error.message}`); throw new Error(`Transfer failed: ${error.message}`);
} }
} }

View File

@@ -1,4 +1,4 @@
import { SolanaAgentKit } from "../agent"; import { SolanaAgent } from "../agent";
import { Transaction, Keypair, TransactionInstruction } from "@solana/web3.js"; import { Transaction, Keypair, TransactionInstruction } from "@solana/web3.js";
import { Connection, ComputeBudgetProgram } from "@solana/web3.js"; import { Connection, ComputeBudgetProgram } from "@solana/web3.js";
@@ -41,7 +41,7 @@ export async function getPriorityFees(connection: Connection): Promise<{
const median = const median =
sortedFees.length % 2 === 0 sortedFees.length % 2 === 0
? ((sortedFees[mid - 1] ?? 0) + (sortedFees[mid] ?? 0)) / 2 ? ((sortedFees[mid - 1] ?? 0) + (sortedFees[mid] ?? 0)) / 2
: sortedFees[mid] ?? 0; : (sortedFees[mid] ?? 0);
// Helper to create priority fee IX based on chosen strategy // Helper to create priority fee IX based on chosen strategy
const createPriorityFeeIx = (fee: number) => { const createPriorityFeeIx = (fee: number) => {
@@ -69,14 +69,14 @@ export async function getPriorityFees(connection: Connection): Promise<{
/** /**
* Send a transaction with priority fees * Send a transaction with priority fees
* @param agent - SolanaAgentKit instance * @param agent - SolanaAgent instance
* @param tx - Transaction to send * @param tx - Transaction to send
* @returns Transaction ID * @returns Transaction ID
*/ */
export async function sendTx( export async function sendTx(
agent: SolanaAgentKit, agent: SolanaAgent,
tx: Transaction, tx: Transaction,
otherKeypairs?: Keypair[] otherKeypairs?: Keypair[],
) { ) {
tx.recentBlockhash = (await agent.connection.getLatestBlockhash()).blockhash; tx.recentBlockhash = (await agent.connection.getLatestBlockhash()).blockhash;
tx.feePayer = agent.wallet_address; tx.feePayer = agent.wallet_address;
@@ -90,9 +90,8 @@ export async function sendTx(
await agent.connection.confirmTransaction({ await agent.connection.confirmTransaction({
signature: txid, signature: txid,
blockhash: (await agent.connection.getLatestBlockhash()).blockhash, blockhash: (await agent.connection.getLatestBlockhash()).blockhash,
lastValidBlockHeight: ( lastValidBlockHeight: (await agent.connection.getLatestBlockhash())
await agent.connection.getLatestBlockhash() .lastValidBlockHeight,
).lastValidBlockHeight,
}); });
return txid; return txid;
} }

View File

@@ -1,4 +1,4 @@
import { SolanaAgentKit } from "../src"; import { SolanaAgent } from "../src";
import { createSolanaTools } from "../src/langchain"; import { createSolanaTools } from "../src/langchain";
import { HumanMessage } from "@langchain/core/messages"; import { HumanMessage } from "@langchain/core/messages";
import { MemorySaver } from "@langchain/langgraph"; import { MemorySaver } from "@langchain/langgraph";
@@ -14,7 +14,7 @@ function validateEnvironment(): void {
const missingVars: string[] = []; const missingVars: string[] = [];
const requiredVars = ["OPENAI_API_KEY", "RPC_URL", "SOLANA_PRIVATE_KEY"]; const requiredVars = ["OPENAI_API_KEY", "RPC_URL", "SOLANA_PRIVATE_KEY"];
requiredVars.forEach(varName => { requiredVars.forEach((varName) => {
if (!process.env[varName]) { if (!process.env[varName]) {
missingVars.push(varName); missingVars.push(varName);
} }
@@ -22,7 +22,7 @@ function validateEnvironment(): void {
if (missingVars.length > 0) { if (missingVars.length > 0) {
console.error("Error: Required environment variables are not set"); console.error("Error: Required environment variables are not set");
missingVars.forEach(varName => { missingVars.forEach((varName) => {
console.error(`${varName}=your_${varName.toLowerCase()}_here`); console.error(`${varName}=your_${varName.toLowerCase()}_here`);
}); });
process.exit(1); process.exit(1);
@@ -50,13 +50,13 @@ async function initializeAgent() {
} }
} }
const solanaKit = new SolanaAgentKit( const solanaAgent = new SolanaAgent(
process.env.SOLANA_PRIVATE_KEY!, process.env.SOLANA_PRIVATE_KEY!,
process.env.RPC_URL, process.env.RPC_URL,
process.env.OPENAI_API_KEY! process.env.OPENAI_API_KEY!,
); );
const tools = createSolanaTools(solanaKit); const tools = createSolanaTools(solanaAgent);
const memory = new MemorySaver(); const memory = new MemorySaver();
const config = { configurable: { thread_id: "Solana Agent Kit!" } }; const config = { configurable: { thread_id: "Solana Agent Kit!" } };
@@ -65,13 +65,13 @@ async function initializeAgent() {
tools, tools,
checkpointSaver: memory, checkpointSaver: memory,
messageModifier: ` messageModifier: `
You are a helpful agent that can interact onchain using the Solana Agent Kit. You are You are a helpful agent that can interact onchain using the Solana Agent Kit. You are
empowered to interact onchain using your tools. If you ever need funds, you can request them from the empowered to interact onchain using your tools. If you ever need funds, you can request them from the
faucet. If not, you can provide your wallet details and request funds from the user. If there is a 5XX faucet. If not, you can provide your wallet details and request funds from the user. If there is a 5XX
(internal) HTTP error code, ask the user to try again later. If someone asks you to do something you (internal) HTTP error code, ask the user to try again later. If someone asks you to do something you
can't do with your currently available tools, you must say so, and encourage them to implement it can't do with your currently available tools, you must say so, and encourage them to implement it
themselves using the Solana Agent Kit, recommend they go to https://sendai.fun/kit for more information. Be themselves using the Solana Agent Kit, recommend they go to https://sendai.fun/kit for more information. Be
concise and helpful with your responses. Refrain from restating your tools' descriptions unless it is concise and helpful with your responses. Refrain from restating your tools' descriptions unless it is
explicitly requested. explicitly requested.
`, `,
}); });
@@ -96,7 +96,10 @@ async function runAutonomousMode(agent: any, config: any, interval = 10) {
"Be creative and do something interesting on the blockchain. " + "Be creative and do something interesting on the blockchain. " +
"Choose an action or set of actions and execute it that highlights your abilities."; "Choose an action or set of actions and execute it that highlights your abilities.";
const stream = await agent.stream({ messages: [new HumanMessage(thought)] }, config); const stream = await agent.stream(
{ messages: [new HumanMessage(thought)] },
config,
);
for await (const chunk of stream) { for await (const chunk of stream) {
if ("agent" in chunk) { if ("agent" in chunk) {
@@ -107,7 +110,7 @@ async function runAutonomousMode(agent: any, config: any, interval = 10) {
console.log("-------------------"); console.log("-------------------");
} }
await new Promise(resolve => setTimeout(resolve, interval * 1000)); await new Promise((resolve) => setTimeout(resolve, interval * 1000));
} catch (error) { } catch (error) {
if (error instanceof Error) { if (error instanceof Error) {
console.error("Error:", error.message); console.error("Error:", error.message);
@@ -126,7 +129,7 @@ async function runChatMode(agent: any, config: any) {
}); });
const question = (prompt: string): Promise<string> => const question = (prompt: string): Promise<string> =>
new Promise(resolve => rl.question(prompt, resolve)); new Promise((resolve) => rl.question(prompt, resolve));
try { try {
while (true) { while (true) {
@@ -136,7 +139,10 @@ async function runChatMode(agent: any, config: any) {
break; break;
} }
const stream = await agent.stream({ messages: [new HumanMessage(userInput)] }, config); const stream = await agent.stream(
{ messages: [new HumanMessage(userInput)] },
config,
);
for await (const chunk of stream) { for await (const chunk of stream) {
if ("agent" in chunk) { if ("agent" in chunk) {
@@ -164,7 +170,7 @@ async function chooseMode(): Promise<"chat" | "auto"> {
}); });
const question = (prompt: string): Promise<string> => const question = (prompt: string): Promise<string> =>
new Promise(resolve => rl.question(prompt, resolve)); new Promise((resolve) => rl.question(prompt, resolve));
while (true) { while (true) {
console.log("\nAvailable modes:"); console.log("\nAvailable modes:");
@@ -206,7 +212,7 @@ async function main() {
} }
if (require.main === module) { if (require.main === module) {
main().catch(error => { main().catch((error) => {
console.error("Fatal error:", error); console.error("Fatal error:", error);
process.exit(1); process.exit(1);
}); });