Merge branch 'main' into update-orca-single-sided-pools-and-send_transaction

This commit is contained in:
calintje
2024-12-25 14:20:59 +01:00
59 changed files with 2761 additions and 444 deletions

View File

@@ -0,0 +1,81 @@
import { VersionedTransaction } from "@solana/web3.js";
import { PublicKey } from "@solana/web3.js";
import { GibworkCreateTaskReponse, SolanaAgentKit } from "../index";
/**
* Create an new task on Gibwork
* @param agent SolanaAgentKit instance
* @param title Title of the task
* @param content Description of the task
* @param requirements Requirements to complete the task
* @param tags List of tags associated with the task
* @param payer Payer address for the task (default: agent wallet address)
* @param tokenMintAddress Token mint address for payment
* @param tokenAmount Payment amount for the task
* @returns Object containing task creation transaction and generated taskId
*/
export async function create_gibwork_task(
agent: SolanaAgentKit,
title: string,
content: string,
requirements: string,
tags: string[],
tokenMintAddress: PublicKey,
tokenAmount: number,
payer?: PublicKey,
): Promise<GibworkCreateTaskReponse> {
try {
const apiResponse = await fetch(
"https://api2.gib.work/tasks/public/transaction",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
title: title,
content: content,
requirements: requirements,
tags: tags,
payer: payer?.toBase58() || agent.wallet.publicKey.toBase58(),
token: {
mintAddress: tokenMintAddress.toBase58(),
amount: tokenAmount,
},
}),
},
);
const responseData = await apiResponse.json();
if (!responseData.taskId && !responseData.serializedTransaction) {
throw new Error(`${responseData.message}`);
}
const serializedTransaction = Buffer.from(
responseData.serializedTransaction,
"base64",
);
const tx = VersionedTransaction.deserialize(serializedTransaction);
tx.sign([agent.wallet]);
const signature = await agent.connection.sendTransaction(tx, {
preflightCommitment: "confirmed",
maxRetries: 3,
});
const latestBlockhash = await agent.connection.getLatestBlockhash();
await agent.connection.confirmTransaction({
signature,
blockhash: latestBlockhash.blockhash,
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
});
return {
status: "success",
taskId: responseData.taskId,
signature: signature,
};
} catch (err: any) {
throw new Error(`${err.message}`);
}
}

View File

@@ -39,7 +39,7 @@ import { sendTx } from "../utils/send_tx";
* @remarks
* Fee tiers determine the percentage of fees collected on swaps, while tick spacing affects
* the granularity of price ranges for liquidity positions.
*
*
* For more details, refer to:
* - [Whirlpool Fees](https://orca-so.github.io/whirlpools/Architecture%20Overview/Whirlpool%20Fees)
* - [Whirlpool Parameters](https://orca-so.github.io/whirlpools/Architecture%20Overview/Whirlpool%20Parameters)
@@ -53,17 +53,17 @@ export const FEE_TIERS = {
0.04: 4,
0.05: 8,
0.16: 16,
0.30: 64,
0.3: 64,
0.65: 96,
1.00: 128,
2.00: 256,
1.0: 128,
2.0: 256,
} as const;
/**
* # Creates a single-sided Whirlpool.
*
* This function initializes a new Whirlpool (liquidity pool) on Orca and seeds it with liquidity from a single token.
*
*
* ## Example Usage:
* 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.
@@ -71,7 +71,7 @@ export const FEE_TIERS = {
* 1. Increase the amount of tokens you deposit
* 2. Set the initial price very low
* 3. Set the maximum price closer to the initial price
*
*
* ### Note for experts:
* 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
@@ -85,13 +85,13 @@ export const FEE_TIERS = {
* @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 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.
*
*
* @throws Will throw an error if:
* - Mint accounts for the tokens cannot be fetched.
* - Prices are out of bounds.
*
*
* @remarks
* 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.
@@ -102,7 +102,7 @@ export const FEE_TIERS = {
* import { PublicKey } from "@solana/web3.js";
* import { BN } from "@coral-xyz/anchor";
* import Decimal from "decimal.js";
*
*
* const agent = new SolanaAgentKit(wallet, connection);
* const depositAmount = new BN(1_000_000_000_000); // 1 million SHARK if SHARK has 6 decimals
* const depositTokenMint = new PublicKey("DEPOSTI_TOKEN_ADDRESS");
@@ -141,13 +141,19 @@ export async function createOrcaSingleSidedWhirlpool(
throw new Error('Unsupported network');
}
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 correctTokenOrder = PoolUtil.orderMints(otherTokenMint, depositTokenMint).map(
(addr) => addr.toString(),
);
const isCorrectMintOrder = correctTokenOrder[0] === depositTokenMint.toString();
const correctTokenOrder = PoolUtil.orderMints(
otherTokenMint,
depositTokenMint,
).map((addr) => addr.toString());
const isCorrectMintOrder =
correctTokenOrder[0] === depositTokenMint.toString();
let mintA, mintB;
if (isCorrectMintOrder) {
[mintA, mintB] = [depositTokenMint, otherTokenMint];
@@ -158,10 +164,19 @@ export async function createOrcaSingleSidedWhirlpool(
}
const mintAAccount = await fetcher.getMintInfo(mintA);
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 tickIndex = PriceMath.priceToTickIndex(initialPrice, mintAAccount.decimals, mintBAccount.decimals);
const initialTick = TickUtil.getInitializableTickIndex(tickIndex, tickSpacing);
const tickIndex = PriceMath.priceToTickIndex(
initialPrice,
mintAAccount.decimals,
mintBAccount.decimals,
);
const initialTick = TickUtil.getInitializableTickIndex(
tickIndex,
tickSpacing,
);
const tokenExtensionCtx: TokenExtensionContextForPool = {
...NO_TOKEN_EXTENSION_CONTEXT,
@@ -203,17 +218,17 @@ export async function createOrcaSingleSidedWhirlpool(
tokenVaultBKeypair,
feeTierKey,
tickSpacing: tickSpacing,
funder: wallet.publicKey
funder: wallet.publicKey,
};
const initPoolIx = !TokenExtensionUtil.isV2IxRequiredPool(tokenExtensionCtx)
? WhirlpoolIx.initializePoolIx(ctx.program, baseParamsPool)
: WhirlpoolIx.initializePoolV2Ix(ctx.program, {
...baseParamsPool,
tokenProgramA: tokenExtensionCtx.tokenMintWithProgramA.tokenProgram,
tokenProgramB: tokenExtensionCtx.tokenMintWithProgramB.tokenProgram,
tokenBadgeA,
tokenBadgeB,
});
...baseParamsPool,
tokenProgramA: tokenExtensionCtx.tokenMintWithProgramA.tokenProgram,
tokenProgramB: tokenExtensionCtx.tokenMintWithProgramB.tokenProgram,
tokenBadgeA,
tokenBadgeB,
});
const initialTickArrayStartTick = TickUtil.getStartTickIndex(
initialTick,
tickSpacing,
@@ -242,14 +257,33 @@ export async function createOrcaSingleSidedWhirlpool(
let tickLowerIndex, tickUpperIndex;
if (isCorrectMintOrder) {
tickLowerIndex = initialTick;
tickUpperIndex = PriceMath.priceToTickIndex(maxPrice, mintAAccount.decimals, mintBAccount.decimals);
tickUpperIndex = PriceMath.priceToTickIndex(
maxPrice,
mintAAccount.decimals,
mintBAccount.decimals,
);
} else {
tickLowerIndex = PriceMath.priceToTickIndex(maxPrice, mintAAccount.decimals, mintBAccount.decimals);
tickLowerIndex = PriceMath.priceToTickIndex(
maxPrice,
mintAAccount.decimals,
mintBAccount.decimals,
);
tickUpperIndex = initialTick;
}
const tickLowerInitializableIndex = TickUtil.getInitializableTickIndex(tickLowerIndex, tickSpacing);
const tickUpperInitializableIndex = TickUtil.getInitializableTickIndex(tickUpperIndex, tickSpacing);
if (!TickUtil.checkTickInBounds(tickLowerInitializableIndex) || !TickUtil.checkTickInBounds(tickUpperInitializableIndex)) throw Error('Prices out of bounds');
const tickLowerInitializableIndex = TickUtil.getInitializableTickIndex(
tickLowerIndex,
tickSpacing,
);
const tickUpperInitializableIndex = TickUtil.getInitializableTickIndex(
tickUpperIndex,
tickSpacing,
);
if (
!TickUtil.checkTickInBounds(tickLowerInitializableIndex) ||
!TickUtil.checkTickInBounds(tickUpperInitializableIndex)
) {
throw Error("Prices out of bounds");
}
const increasLiquidityQuoteParam: IncreaseLiquidityQuoteParam = {
inputTokenAmount: new BN(depositTokenAmount),
inputTokenMint: depositTokenMint,
@@ -260,11 +294,11 @@ export async function createOrcaSingleSidedWhirlpool(
tickLowerIndex: tickLowerInitializableIndex,
tickUpperIndex: tickUpperInitializableIndex,
tokenExtensionCtx: tokenExtensionCtx,
slippageTolerance: Percentage.fromFraction(0, 100)
}
slippageTolerance: Percentage.fromFraction(0, 100),
};
const liquidityInput = increaseLiquidityQuoteByInputTokenWithParams(
increasLiquidityQuoteParam
)
increasLiquidityQuoteParam,
);
const { liquidityAmount: liquidity, tokenMaxA, tokenMaxB } = liquidityInput;
const positionMintKeypair = Keypair.generate();
@@ -292,7 +326,7 @@ export async function createOrcaSingleSidedWhirlpool(
...params,
positionMint: positionMintPubkey,
withTokenMetadataExtension: true,
})
});
txBuilder.addInstruction(positionIx);
txBuilder.addSigner(positionMintKeypair);
@@ -372,17 +406,15 @@ export async function createOrcaSingleSidedWhirlpool(
tickArrayUpper: tickArrayUpperPda.publicKey,
};
const liquidityIx = !TokenExtensionUtil.isV2IxRequiredPool(
tokenExtensionCtx,
)
const liquidityIx = !TokenExtensionUtil.isV2IxRequiredPool(tokenExtensionCtx)
? increaseLiquidityIx(ctx.program, baseParamsLiquidity)
: increaseLiquidityV2Ix(ctx.program, {
...baseParamsLiquidity,
tokenMintA: mintA,
tokenMintB: mintB,
tokenProgramA: tokenExtensionCtx.tokenMintWithProgramA.tokenProgram,
tokenProgramB: tokenExtensionCtx.tokenMintWithProgramB.tokenProgram,
});
...baseParamsLiquidity,
tokenMintA: mintA,
tokenMintB: mintB,
tokenProgramA: tokenExtensionCtx.tokenMintWithProgramA.tokenProgram,
tokenProgramB: tokenExtensionCtx.tokenMintWithProgramB.tokenProgram,
});
txBuilder.addInstruction(liquidityIx);
const txPayload = await txBuilder.build();

View File

@@ -1,8 +1,19 @@
import { SolanaAgentKit } from "../index";
import { generateSigner, keypairIdentity, publicKey } from "@metaplex-foundation/umi";
import { createCollection, mplCore, ruleSet } from "@metaplex-foundation/mpl-core";
import {
generateSigner,
keypairIdentity,
publicKey,
} from "@metaplex-foundation/umi";
import {
createCollection,
mplCore,
ruleSet,
} from "@metaplex-foundation/mpl-core";
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";
/**
@@ -28,11 +39,11 @@ export async function deploy_collection(
address: publicKey(creator.address),
percentage: creator.percentage,
})) || [
{
address: publicKey(agent.wallet_address.toString()),
percentage: 100,
},
];
{
address: publicKey(agent.wallet_address.toString()),
percentage: 100,
},
];
// Create collection
const tx = await createCollection(umi, {

View File

@@ -2,9 +2,17 @@ import { SolanaAgentKit } from "../index";
import { PublicKey } from "@solana/web3.js";
import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";
import { generateSigner, keypairIdentity } from "@metaplex-foundation/umi";
import { createFungible, mintV1, TokenStandard } from "@metaplex-foundation/mpl-token-metadata";
import { fromWeb3JsKeypair, fromWeb3JsPublicKey, toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters";
import {mplToolbox} from "@metaplex-foundation/mpl-toolbox"
import {
createFungible,
mintV1,
TokenStandard,
} from "@metaplex-foundation/mpl-token-metadata";
import {
fromWeb3JsKeypair,
fromWeb3JsPublicKey,
toWeb3JsPublicKey,
} from "@metaplex-foundation/umi-web3js-adapters";
import { mplToolbox } from "@metaplex-foundation/mpl-toolbox";
/**
* Deploy a new SPL token
@@ -22,11 +30,11 @@ export async function deploy_token(
uri: string,
symbol: string,
decimals: number = 9,
initialSupply?: number
initialSupply?: number,
): Promise<{ mint: PublicKey }> {
try {
// Create UMI instance from agent
const umi = createUmi(agent.connection.rpcEndpoint).use(mplToolbox())
const umi = createUmi(agent.connection.rpcEndpoint).use(mplToolbox());
umi.use(keypairIdentity(fromWeb3JsKeypair(agent.wallet)));
// Create new token mint
@@ -52,11 +60,11 @@ export async function deploy_token(
tokenStandard: TokenStandard.Fungible,
tokenOwner: fromWeb3JsPublicKey(agent.wallet_address),
amount: initialSupply,
})
}),
);
}
builder.sendAndConfirm(umi, { confirm: { commitment: 'finalized' } });
builder.sendAndConfirm(umi, { confirm: { commitment: "finalized" } });
return {
mint: toWeb3JsPublicKey(mint.publicKey),

View File

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

View File

@@ -0,0 +1,19 @@
import { SolanaAgentKit } from "../index";
import { getAllTld } from "@onsol/tldparser";
/**
* Get all active top-level domains (TLDs) in the AllDomains Name Service
* @param agent SolanaAgentKit instance
* @returns Array of active TLD strings
*/
export async function getAllDomainsTLDs(
agent: SolanaAgentKit,
// eslint-disable-next-line @typescript-eslint/ban-types
): Promise<String[]> {
try {
const tlds = await getAllTld(agent.connection);
return tlds.map((tld) => tld.tld);
} catch (error: any) {
throw new Error(`Failed to fetch TLDs: ${error.message}`);
}
}

View File

@@ -0,0 +1,36 @@
import { getAllDomains } from "@bonfida/spl-name-service";
import { SolanaAgentKit } from "../agent";
import { PublicKey } from "@solana/web3.js";
import { getAllDomainsTLDs } from "./get_all_domains_tlds";
/**
* Get all registered domains across all TLDs
* @param agent SolanaAgentKit instance
* @returns Array of all registered domain names with their TLDs
*/
export async function getAllRegisteredAllDomains(
agent: SolanaAgentKit,
): Promise<string[]> {
try {
// First get all TLDs
const tlds = await getAllDomainsTLDs(agent);
const allDomains: string[] = [];
// For each TLD, fetch all registered domains
for (const tld of tlds) {
const domains = await getAllDomains(
agent.connection,
new PublicKey("namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX"),
);
// Add domains with TLD suffix
domains.forEach((domain) => {
allDomains.push(`${domain}.${tld}`);
});
}
return allDomains;
} catch (error: any) {
throw new Error(`Failed to fetch all registered domains: ${error.message}`);
}
}

View File

@@ -11,11 +11,12 @@ export async function get_balance(
agent: SolanaAgentKit,
token_address?: PublicKey,
): Promise<number> {
if (!token_address)
if (!token_address) {
return (
(await agent.connection.getBalance(agent.wallet_address)) /
LAMPORTS_PER_SOL
);
}
const token_account =
await agent.connection.getTokenAccountBalance(token_address);

View File

@@ -0,0 +1,21 @@
import { getFavoriteDomain as _getFavoriteDomain } from "@bonfida/spl-name-service";
import { PublicKey } from "@solana/web3.js";
/**
* Get the user's main/favorite domain for a SolanaAgentKit instance
* @param agent SolanaAgentKit instance
* @param owner Owner's public key
* @returns Promise resolving to the main domain name or null if not found
*/
export async function getMainAllDomainsDomain(
agent: any,
owner: PublicKey,
): Promise<string | null> {
let mainDomain = null;
try {
mainDomain = await _getFavoriteDomain(agent.connection, owner);
return mainDomain.stale ? null : mainDomain.reverse;
} catch (error: any) {
return null;
}
}

View File

@@ -0,0 +1,23 @@
import { SolanaAgentKit } from "../agent";
import { PublicKey } from "@solana/web3.js";
import { TldParser } from "@onsol/tldparser";
/**
* Get all domains owned domains for a specific TLD for the agent's wallet
* @param agent SolanaAgentKit instance
* @param owner - PublicKey of the owner
* @returns Promise resolving to an array of owned domains or an empty array if none are found
*/
export async function getOwnedAllDomains(
agent: SolanaAgentKit,
owner: PublicKey,
): Promise<string[]> {
try {
const domains = await new TldParser(
agent.connection,
).getParsedAllUserDomains(owner);
return domains.map((domain) => domain.domain);
} catch (error: any) {
throw new Error(`Failed to fetch owned domains: ${error.message}`);
}
}

View File

@@ -0,0 +1,21 @@
import { TldParser } from "@onsol/tldparser";
import { SolanaAgentKit } from "../agent";
/**
* Get all domains owned by an address for a specific TLD
* @param agent SolanaAgentKit instance
* @param tld Top-level domain (e.g., "sol")
* @returns Promise resolving to an array of owned domain names for the specified TLD or an empty array if none are found
*/
export async function getOwnedDomainsForTLD(
agent: SolanaAgentKit,
tld: string,
): Promise<string[]> {
try {
const domains = await new TldParser(
agent.connection,
).getParsedAllUserDomainsFromTld(agent.wallet_address, tld);
return domains.map((domain) => domain.domain);
} catch (error: any) {
throw new Error(`Failed to fetch domains for TLD: ${error.message}`);
}
}

View File

@@ -16,22 +16,22 @@ import { SolanaAgentKit } from "../index";
*/
export async function getPrimaryDomain(
agent: SolanaAgentKit,
account: PublicKey
account: PublicKey,
): Promise<string> {
try {
const { reverse, stale } = await _getPrimaryDomain(
agent.connection,
account
account,
);
if (stale) {
throw new Error(
`Primary domain is stale for account: ${account.toBase58()}`
`Primary domain is stale for account: ${account.toBase58()}`,
);
}
return reverse;
} catch (error) {
throw new Error(
`Failed to get primary domain for account: ${account.toBase58()}`
`Failed to get primary domain for account: ${account.toBase58()}`,
);
}
}

View File

@@ -27,11 +27,11 @@ export async function getTokenDataByAddress(
}
export async function getTokenAddressFromTicker(
ticker: string
ticker: string,
): Promise<string | null> {
try {
const response = await fetch(
`https://api.dexscreener.com/latest/dex/search?q=${ticker}`
`https://api.dexscreener.com/latest/dex/search?q=${ticker}`,
);
const data = await response.json();
@@ -46,7 +46,7 @@ export async function getTokenAddressFromTicker(
solanaPairs = solanaPairs.filter(
(pair: any) =>
pair.baseToken.symbol.toLowerCase() === ticker.toLowerCase()
pair.baseToken.symbol.toLowerCase() === ticker.toLowerCase(),
);
// Return the address of the highest FDV Solana pair
@@ -58,11 +58,11 @@ export async function getTokenAddressFromTicker(
}
export async function getTokenDataByTicker(
ticker: string
ticker: string,
): Promise<JupiterTokenData | undefined> {
const address = await getTokenAddressFromTicker(ticker);
if (!address) {
throw new Error(`Token address not found for ticker: ${ticker}`);
}
return getTokenDataByAddress(new PublicKey(address));
}
}

View File

@@ -16,8 +16,24 @@ export * from "./stake_with_jup";
export * from "./fetch_price";
export * from "./send_compressed_airdrop";
export * from "./create_orca_single_sided_whirlpool";
export * from "./get_all_domains_tlds";
export * from "./get_all_registered_all_domains";
export * from "./get_owned_domains_for_tld";
export * from "./get_main_all_domains_domain";
export * from "./get_owned_all_domains";
export * from "./resolve_domain";
export * from "./get_all_domains_tlds";
export * from "./get_all_registered_all_domains";
export * from "./get_owned_domains_for_tld";
export * from "./get_main_all_domains_domain";
export * from "./get_owned_all_domains";
export * from "./resolve_domain";
export * from "./raydium_create_ammV4";
export * from "./raydium_create_clmm";
export * from "./raydium_create_cpmm";
export * from "./openbook_create_market";
export * from "./pyth_fetch_price";
export * from "./pyth_fetch_price";
export * from "./create_gibwork_task";

View File

@@ -21,9 +21,15 @@ async function uploadMetadata(
formData.append("showName", "true");
if (options?.twitter) formData.append("twitter", options.twitter);
if (options?.telegram) formData.append("telegram", options.telegram);
if (options?.website) formData.append("website", options.website);
if (options?.twitter) {
formData.append("twitter", options.twitter);
}
if (options?.telegram) {
formData.append("telegram", options.telegram);
}
if (options?.website) {
formData.append("website", options.website);
}
const imageResponse = await fetch(imageUrl);
const imageBlob = await imageResponse.blob();

View File

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

View File

@@ -1,11 +1,15 @@
import { SolanaAgentKit } from "../index";
import { generateSigner, keypairIdentity } from '@metaplex-foundation/umi';
import { create, mplCore } from '@metaplex-foundation/mpl-core';
import { fetchCollection } from '@metaplex-foundation/mpl-core';
import { generateSigner, keypairIdentity } from "@metaplex-foundation/umi";
import { create, mplCore } from "@metaplex-foundation/mpl-core";
import { fetchCollection } from "@metaplex-foundation/mpl-core";
import { PublicKey } from "@solana/web3.js";
import { fromWeb3JsKeypair, fromWeb3JsPublicKey, toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters";
import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';
import { MintCollectionNFTResponse } from '../types';
import {
fromWeb3JsKeypair,
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
@@ -27,7 +31,7 @@ export async function mintCollectionNFT(
share: number;
}>;
},
recipient?: PublicKey
recipient?: PublicKey,
): Promise<MintCollectionNFTResponse> {
try {
// Create UMI instance from agent
@@ -49,15 +53,15 @@ export async function mintCollectionNFT(
collection: collection,
name: metadata.name,
uri: metadata.uri,
owner: fromWeb3JsPublicKey(recipient ?? agent.wallet.publicKey)
owner: fromWeb3JsPublicKey(recipient ?? agent.wallet.publicKey),
}).sendAndConfirm(umi);
return {
mint: toWeb3JsPublicKey(assetSigner.publicKey),
// Note: Token account is now handled automatically by the create instruction
metadata: toWeb3JsPublicKey(assetSigner.publicKey)
metadata: toWeb3JsPublicKey(assetSigner.publicKey),
};
} catch (error: any) {
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 { PublicKey } from "@solana/web3.js";
import { SolanaAgentKit } from "../agent";
import { SolanaAgentKit } from "../index";
export async function openbookCreateMarket(
agent: SolanaAgentKit,
baseMint: PublicKey,
quoteMint: PublicKey,
lotSize: number = 1,
tickSize: number = 0.01,
): Promise<string[]> {
const raydium = await Raydium.load({
owner: agent.wallet,
connection: agent.connection,
})
});
const baseMintInfo = await agent.connection.getAccountInfo(baseMint)
const quoteMintInfo = await agent.connection.getAccountInfo(quoteMint)
const baseMintInfo = await agent.connection.getAccountInfo(baseMint);
const quoteMintInfo = await agent.connection.getAccountInfo(quoteMint);
if (
baseMintInfo?.owner.toString() !== TOKEN_PROGRAM_ID.toBase58() ||
quoteMintInfo?.owner.toString() !== TOKEN_PROGRAM_ID.toBase58()
) {
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({
@@ -44,9 +45,9 @@ export async function openbookCreateMarket(
dexProgramId: OPEN_BOOK_PROGRAM,
txVersion: TxVersion.V0,
})
});
const { txIds } = await execute({ sequentially: true, })
const { txIds } = await execute({ sequentially: true });
return txIds
return txIds;
}

View File

@@ -1,6 +1,3 @@
import { PublicKey } from "@solana/web3.js";
import { SolanaAgentKit } from "../index";
import { Tool } from "langchain/tools";
import { PriceServiceConnection } from "@pythnetwork/price-service-client";
import BN from "bn.js";
@@ -9,40 +6,35 @@ import BN from "bn.js";
* @param agent SolanaAgentKit instance
* @param priceFeedID Price feed ID
* @returns Latest price value from feed
*
*
* You can find priceFeedIDs here: https://www.pyth.network/developers/price-feed-ids#stable
*/
export async function pythFetchPrice(
agent: SolanaAgentKit,
priceFeedID: string
) {
// get Hermes service URL from https://docs.pyth.network/price-feeds/api-instances-and-providers/hermes
export async function pythFetchPrice(priceFeedID: string): Promise<string> {
// get Hermes service URL from https://docs.pyth.network/price-feeds/api-instances-and-providers/hermes
const stableHermesServiceUrl: string = "https://hermes.pyth.network";
const connection = new PriceServiceConnection(stableHermesServiceUrl);
const feeds = [priceFeedID];
try {
const currentPrice = await connection.getLatestPriceFeeds(feeds);
if (currentPrice === undefined) {
throw new Error("Price data not available for the given token.");
}
if (currentPrice.length === 0) {
throw new Error("Price data not available for the given token.");
}
// get price and exponent from price feed
let price = new BN(currentPrice[0].getPriceUnchecked().price);
let exponent = new BN(currentPrice[0].getPriceUnchecked().expo);
const price = new BN(currentPrice[0].getPriceUnchecked().price);
const exponent = new BN(currentPrice[0].getPriceUnchecked().expo);
// convert to scaled price
let scaledPrice = price.div(new BN(10).pow(exponent));
const scaledPrice = price.div(new BN(10).pow(exponent));
return scaledPrice.toString();
} catch (error: any) {
throw new Error(`Fetching price from Pyth failed: ${error.message}`);
}
}

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 { PublicKey } from "@solana/web3.js";
import BN from 'bn.js';
import { SolanaAgentKit } from "../agent";
import BN from "bn.js";
import { SolanaAgentKit } from "../index";
export async function raydiumCreateAmmV4(
agent: SolanaAgentKit,
marketId: PublicKey,
baseAmount: BN,
quoteAmount: BN,
startTime: BN,
): Promise<string> {
const raydium = await Raydium.load({
owner: agent.wallet,
connection: agent.connection,
})
});
const marketBufferInfo = await agent.connection.getAccountInfo(new PublicKey(marketId))
const { baseMint, quoteMint } = MARKET_STATE_LAYOUT_V3.decode(marketBufferInfo!.data)
const marketBufferInfo = await agent.connection.getAccountInfo(
new PublicKey(marketId),
);
const { baseMint, quoteMint } = MARKET_STATE_LAYOUT_V3.decode(
marketBufferInfo!.data,
);
const baseMintInfo = await agent.connection.getAccountInfo(baseMint)
const quoteMintInfo = await agent.connection.getAccountInfo(quoteMint)
const baseMintInfo = await agent.connection.getAccountInfo(baseMint);
const quoteMintInfo = await agent.connection.getAccountInfo(quoteMint);
if (
baseMintInfo?.owner.toString() !== TOKEN_PROGRAM_ID.toBase58() ||
quoteMintInfo?.owner.toString() !== TOKEN_PROGRAM_ID.toBase58()
) {
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)))) {
throw new Error('initial liquidity too low, try adding more baseAmount/quoteAmount')
if (
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({
@@ -63,9 +80,9 @@ export async function raydiumCreateAmmV4(
associatedOnly: false,
txVersion: TxVersion.V0,
feeDestinationId: FEE_DESTINATION_ID,
})
});
const { txId } = await execute({ sendAndConfirm: true })
const { txId } = await execute({ sendAndConfirm: true });
return txId
}
return txId;
}

View File

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

View File

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

View File

@@ -0,0 +1,30 @@
import { TldParser } from "@onsol/tldparser";
import { SolanaAgentKit } from "../index";
import { PublicKey } from "@solana/web3.js";
/**
* Resolve all domains for a given agent and domain
* @param agent SolanaAgentKit instance
* @param domain Domain name to resolve
* @returns Promise resolving to the domain or undefined if not found
*/
export async function resolveAllDomains(
agent: SolanaAgentKit,
domain: string,
): Promise<PublicKey | undefined> {
try {
const tld = await new TldParser(agent.connection).getOwnerFromDomainTld(
domain,
);
return tld;
} catch (error: any) {
if (
error.message.includes(
"Cannot read properties of undefined (reading 'owner')",
)
) {
return undefined;
}
throw new Error(`Domain resolution failed: ${error.message}`);
}
}

View File

@@ -16,7 +16,7 @@ import { SolanaAgentKit } from "../index";
*/
export async function resolveSolDomain(
agent: SolanaAgentKit,
domain: string
domain: string,
): Promise<PublicKey> {
if (!domain || typeof domain !== "string") {
throw new Error("Invalid domain. Expected a non-empty string.");

View File

@@ -1,12 +1,11 @@
import {
AddressLookupTableAccount,
ComputeBudgetProgram,
Connection,
Keypair,
PublicKey,
TransactionInstruction,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../agent/index.js";
import { SolanaAgentKit } from "../index";
import {
buildAndSignTx,
calculateComputeUnitPrice,
@@ -19,7 +18,7 @@ import {
CompressedTokenProgram,
createTokenPool,
} from "@lightprotocol/compressed-token";
import { Account, getOrCreateAssociatedTokenAccount } from "@solana/spl-token";
import { getOrCreateAssociatedTokenAccount } from "@solana/spl-token";
// arbitrary
const MAX_AIRDROP_RECIPIENTS = 1000;
@@ -33,7 +32,7 @@ const MAX_CONCURRENT_TXS = 30;
*/
export const getAirdropCostEstimate = (
numberOfRecipients: number,
priorityFeeInLamports: number
priorityFeeInLamports: number,
) => {
const baseFee = 5000;
const perRecipientCompressedStateFee = 300;
@@ -63,36 +62,34 @@ export async function sendCompressedAirdrop(
decimals: number,
recipients: PublicKey[],
priorityFeeInLamports: number,
shouldLog: boolean = false
shouldLog: boolean = false,
): Promise<string[]> {
if (recipients.length > MAX_AIRDROP_RECIPIENTS) {
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;
if (url.includes("devnet")) {
throw new Error("Devnet is not supported for airdrop. Please use mainnet.");
}
if (!url.includes("helius")) {
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.",
);
}
let sourceTokenAccount: Account;
try {
sourceTokenAccount = await getOrCreateAssociatedTokenAccount(
await getOrCreateAssociatedTokenAccount(
agent.connection,
agent.wallet,
mintAddress,
agent.wallet.publicKey
agent.wallet.publicKey,
);
} catch (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 +97,7 @@ export async function sendCompressedAirdrop(
await createTokenPool(
agent.connection as unknown as Rpc,
agent.wallet,
mintAddress
mintAddress,
);
} catch (error: any) {
if (error.message.includes("already in use")) {
@@ -116,7 +113,7 @@ export async function sendCompressedAirdrop(
mintAddress,
recipients,
priorityFeeInLamports,
shouldLog
shouldLog,
);
}
@@ -126,7 +123,7 @@ async function processAll(
mint: PublicKey,
recipients: PublicKey[],
priorityFeeInLamports: number,
shouldLog: boolean
shouldLog: boolean,
): Promise<string[]> {
const mintAddress = mint;
const payer = agent.wallet;
@@ -135,13 +132,13 @@ async function processAll(
agent.connection,
agent.wallet,
mintAddress,
agent.wallet.publicKey
agent.wallet.publicKey,
);
const maxRecipientsPerInstruction = 5;
const maxIxs = 3; // empirically determined (as of 12/15/2024)
const lookupTableAddress = new PublicKey(
"9NYFyEqPkyXUhkerbGHXUXkvb4qpzeEdHuGpgbgpH1NJ"
"9NYFyEqPkyXUhkerbGHXUXkvb4qpzeEdHuGpgbgpH1NJ",
);
const lookupTableAccount = (
@@ -164,7 +161,7 @@ async function processAll(
ComputeBudgetProgram.setComputeUnitPrice({
microLamports: calculateComputeUnitPrice(
priorityFeeInLamports,
500_000
500_000,
),
}),
];
@@ -184,13 +181,13 @@ async function processAll(
toAddress: batch,
amount: batch.map(() => amount),
mint: mintAddress,
})
}),
);
}
const compressIxs = await Promise.all(compressIxPromises);
return [...instructions, ...compressIxs];
})
}),
);
const url = agent.connection.rpcEndpoint;
@@ -225,12 +222,12 @@ async function processAll(
instructions,
payer,
lookupTableAccount,
i + idx
i + idx,
).then((signature) => {
confirmedCount++;
log("\r" + renderProgressBar(confirmedCount, totalBatches));
return signature;
})
}),
);
const batchResults = await Promise.allSettled(batchPromises);
@@ -250,7 +247,7 @@ async function processAll(
throw new Error(
`Failed to process ${failures.length} batches: ${failures
.map((f) => f.error)
.join(", ")}`
.join(", ")}`,
);
}
@@ -262,7 +259,7 @@ async function sendTransactionWithRetry(
instructions: TransactionInstruction[],
payer: Keypair,
lookupTableAccount: AddressLookupTableAccount,
batchIndex: number
batchIndex: number,
): Promise<string> {
const MAX_RETRIES = 3;
const INITIAL_BACKOFF = 500; // ms
@@ -275,7 +272,7 @@ async function sendTransactionWithRetry(
payer,
blockhash,
[],
[lookupTableAccount]
[lookupTableAccount],
);
const signature = await sendAndConfirmTx(connection, tx);
@@ -292,7 +289,7 @@ async function sendTransactionWithRetry(
throw new Error(
`Batch ${batchIndex} failed after ${attempt + 1} attempts: ${
error.message
}`
}`,
);
}

View File

@@ -1,5 +1,5 @@
import { VersionedTransaction } from "@solana/web3.js";
import { SolanaAgentKit } from "../agent";
import { SolanaAgentKit } from "../index";
/**
* Stake SOL with Jup validator

View File

@@ -1,4 +1,8 @@
import { VersionedTransaction, PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js";
import {
VersionedTransaction,
PublicKey,
LAMPORTS_PER_SOL,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../index";
import { TOKENS, DEFAULT_OPTIONS, JUP_API } from "../constants";

View File

@@ -1,14 +1,10 @@
import { SolanaAgentKit } from "../index";
import {
PublicKey,
SystemProgram,
Transaction
} from "@solana/web3.js";
import { PublicKey, SystemProgram, Transaction } from "@solana/web3.js";
import { LAMPORTS_PER_SOL } from "@solana/web3.js";
import {
getAssociatedTokenAddress,
import {
getAssociatedTokenAddress,
createTransferInstruction,
getMint
getMint,
} from "@solana/spl-token";
/**
@@ -23,7 +19,7 @@ export async function transfer(
agent: SolanaAgentKit,
to: PublicKey,
amount: number,
mint?: PublicKey
mint?: PublicKey,
): Promise<string> {
try {
let tx: string;
@@ -34,19 +30,19 @@ export async function transfer(
SystemProgram.transfer({
fromPubkey: agent.wallet_address,
toPubkey: to,
lamports: amount * LAMPORTS_PER_SOL
})
lamports: amount * LAMPORTS_PER_SOL,
}),
);
tx = await agent.connection.sendTransaction(
transaction,
[agent.wallet]
);
tx = await agent.connection.sendTransaction(transaction, [agent.wallet]);
} else {
// 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);
// Get mint info to determine decimals
const mintInfo = await getMint(agent.connection, mint);
const adjustedAmount = amount * Math.pow(10, mintInfo.decimals);
@@ -56,18 +52,15 @@ export async function transfer(
fromAta,
toAta,
agent.wallet_address,
adjustedAmount
)
adjustedAmount,
),
);
tx = await agent.connection.sendTransaction(
transaction,
[agent.wallet]
);
tx = await agent.connection.sendTransaction(transaction, [agent.wallet]);
}
return tx;
} catch (error: any) {
throw new Error(`Transfer failed: ${error.message}`);
}
}
}