mirror of
https://github.com/d0zingcat/solana-agent-kit.git
synced 2026-05-22 15:10:43 +00:00
Merge branch 'main' into update-orca-single-sided-pools-and-send_transaction
This commit is contained in:
@@ -25,12 +25,21 @@ import {
|
||||
stakeWithJup,
|
||||
sendCompressedAirdrop,
|
||||
createOrcaSingleSidedWhirlpool,
|
||||
FEE_TIERS,
|
||||
fetchPrice,
|
||||
pythFetchPrice,
|
||||
FEE_TIERS,
|
||||
getAllDomainsTLDs,
|
||||
getAllRegisteredAllDomains,
|
||||
getOwnedDomainsForTLD,
|
||||
getMainAllDomainsDomain,
|
||||
getOwnedAllDomains,
|
||||
resolveAllDomains,
|
||||
create_gibwork_task,
|
||||
} from "../tools";
|
||||
import {
|
||||
CollectionDeployment,
|
||||
CollectionOptions,
|
||||
GibworkCreateTaskReponse,
|
||||
JupiterTokenData,
|
||||
MintCollectionNFTResponse,
|
||||
PumpfunLaunchResponse,
|
||||
@@ -40,7 +49,7 @@ import { BN } from "@coral-xyz/anchor";
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @property {Connection} connection - Solana RPC connection
|
||||
@@ -146,6 +155,10 @@ export class SolanaAgentKit {
|
||||
return getTokenDataByTicker(ticker);
|
||||
}
|
||||
|
||||
async fetchTokenPrice(mint: string) {
|
||||
return fetchPrice(new PublicKey(mint));
|
||||
}
|
||||
|
||||
async launchPumpFunToken(
|
||||
tokenName: string,
|
||||
tokenTicker: string,
|
||||
@@ -193,7 +206,7 @@ export class SolanaAgentKit {
|
||||
initialPrice: Decimal,
|
||||
maxPrice: Decimal,
|
||||
feeTier: keyof typeof FEE_TIERS,
|
||||
): Promise<string> {
|
||||
) {
|
||||
return createOrcaSingleSidedWhirlpool(
|
||||
this,
|
||||
depositTokenAmount,
|
||||
@@ -205,6 +218,31 @@ export class SolanaAgentKit {
|
||||
);
|
||||
}
|
||||
|
||||
async resolveAllDomains(domain: string): Promise<PublicKey | undefined> {
|
||||
return resolveAllDomains(this, domain);
|
||||
}
|
||||
|
||||
async getOwnedAllDomains(owner: PublicKey): Promise<string[]> {
|
||||
return getOwnedAllDomains(this, owner);
|
||||
}
|
||||
|
||||
async getOwnedDomainsForTLD(tld: string): Promise<string[]> {
|
||||
return getOwnedDomainsForTLD(this, tld);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
async getAllDomainsTLDs(): Promise<String[]> {
|
||||
return getAllDomainsTLDs(this);
|
||||
}
|
||||
|
||||
async getAllRegisteredAllDomains(): Promise<string[]> {
|
||||
return getAllRegisteredAllDomains(this);
|
||||
}
|
||||
|
||||
async getMainAllDomainsDomain(owner: PublicKey): Promise<string | null> {
|
||||
return getMainAllDomainsDomain(this, owner);
|
||||
}
|
||||
|
||||
async raydiumCreateAmmV4(
|
||||
marketId: PublicKey,
|
||||
baseAmount: BN,
|
||||
@@ -254,6 +292,7 @@ export class SolanaAgentKit {
|
||||
configId,
|
||||
mintAAmount,
|
||||
mintBAmount,
|
||||
|
||||
startTime,
|
||||
);
|
||||
}
|
||||
@@ -271,10 +310,31 @@ export class SolanaAgentKit {
|
||||
|
||||
lotSize,
|
||||
tickSize,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
async pythFetchPrice(priceFeedID: string) {
|
||||
return pythFetchPrice(this, priceFeedID);
|
||||
async pythFetchPrice(priceFeedID: string): Promise<string> {
|
||||
return pythFetchPrice(priceFeedID);
|
||||
}
|
||||
|
||||
async createGibworkTask(
|
||||
title: string,
|
||||
content: string,
|
||||
requirements: string,
|
||||
tags: string[],
|
||||
tokenMintAddress: string,
|
||||
tokenAmount: number,
|
||||
payer?: string,
|
||||
): Promise<GibworkCreateTaskReponse> {
|
||||
return create_gibwork_task(
|
||||
this,
|
||||
title,
|
||||
content,
|
||||
requirements,
|
||||
tags,
|
||||
new PublicKey(tokenMintAddress),
|
||||
tokenAmount,
|
||||
payer ? new PublicKey(payer) : undefined,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { SolanaAgentKit } from './agent'; // Move the SolanaAgentKit class to src/agent.ts
|
||||
import { createSolanaTools } from './langchain';
|
||||
import { SolanaAgentKit } from "./agent";
|
||||
import { createSolanaTools } from "./langchain";
|
||||
|
||||
export { SolanaAgentKit, createSolanaTools };
|
||||
|
||||
// Optional: Export types that users might need
|
||||
export * from './types';
|
||||
export * from "./types";
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import Decimal from "decimal.js";
|
||||
import { Tool } from "langchain/tools";
|
||||
import { PythFetchPriceResponse, SolanaAgentKit } from "../index";
|
||||
import {
|
||||
GibworkCreateTaskReponse,
|
||||
PythFetchPriceResponse,
|
||||
SolanaAgentKit,
|
||||
} from "../index";
|
||||
import { create_image } from "../tools/create_image";
|
||||
import { fetchPrice } from "../tools/fetch_price";
|
||||
import { BN } from "@coral-xyz/anchor";
|
||||
import { FEE_TIERS } from "../tools";
|
||||
import { toJSON } from "../utils/toJSON";
|
||||
@@ -67,7 +70,7 @@ export class SolanaTransferTool extends Tool {
|
||||
const tx = await this.solanaKit.transfer(
|
||||
recipient,
|
||||
parsedInput.amount,
|
||||
mintAddress
|
||||
mintAddress,
|
||||
);
|
||||
|
||||
return JSON.stringify({
|
||||
@@ -94,7 +97,7 @@ export class SolanaDeployTokenTool extends Tool {
|
||||
|
||||
Inputs (input is a JSON string):
|
||||
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)
|
||||
decimals?: number, eg 9 (optional, defaults to 9)
|
||||
initialSupply?: number, eg 1000000 (optional)`;
|
||||
@@ -112,7 +115,7 @@ export class SolanaDeployTokenTool extends Tool {
|
||||
parsedInput.uri,
|
||||
parsedInput.symbol,
|
||||
parsedInput.decimals,
|
||||
parsedInput.initialSupply
|
||||
parsedInput.initialSupply,
|
||||
);
|
||||
|
||||
return JSON.stringify({
|
||||
@@ -192,7 +195,7 @@ export class SolanaMintNFTTool extends Tool {
|
||||
},
|
||||
parsedInput.recipient
|
||||
? new PublicKey(parsedInput.recipient)
|
||||
: this.solanaKit.wallet_address
|
||||
: this.solanaKit.wallet_address,
|
||||
);
|
||||
|
||||
return JSON.stringify({
|
||||
@@ -240,7 +243,7 @@ export class SolanaTradeTool extends Tool {
|
||||
parsedInput.inputMint
|
||||
? new PublicKey(parsedInput.inputMint)
|
||||
: new PublicKey("So11111111111111111111111111111111111111112"),
|
||||
parsedInput.slippageBps
|
||||
parsedInput.slippageBps,
|
||||
);
|
||||
|
||||
return JSON.stringify({
|
||||
@@ -320,7 +323,7 @@ export class SolanaRegisterDomainTool extends Tool {
|
||||
|
||||
const tx = await this.solanaKit.registerDomain(
|
||||
parsedInput.name,
|
||||
parsedInput.spaceKB || 1
|
||||
parsedInput.spaceKB || 1,
|
||||
);
|
||||
|
||||
return JSON.stringify({
|
||||
@@ -342,10 +345,12 @@ export class SolanaRegisterDomainTool extends Tool {
|
||||
|
||||
export class SolanaResolveDomainTool extends Tool {
|
||||
name = "solana_resolve_domain";
|
||||
description = `Resolve a .sol domain to a Solana PublicKey.
|
||||
description = `Resolve ONLY .sol domain names to a Solana PublicKey.
|
||||
This tool is exclusively for .sol domains.
|
||||
DO NOT use this for other domain types like .blink, .bonk, etc.
|
||||
|
||||
Inputs:
|
||||
domain: string, eg "pumpfun.sol" or "pumpfun"(required)
|
||||
domain: string, eg "pumpfun.sol" (required)
|
||||
`;
|
||||
|
||||
constructor(private solanaKit: SolanaAgentKit) {
|
||||
@@ -460,7 +465,7 @@ export class SolanaPumpfunTokenLaunchTool extends Tool {
|
||||
try {
|
||||
// Parse and normalize input
|
||||
input = input.trim();
|
||||
let parsedInput = JSON.parse(input);
|
||||
const parsedInput = JSON.parse(input);
|
||||
|
||||
this.validateInput(parsedInput);
|
||||
|
||||
@@ -475,7 +480,7 @@ export class SolanaPumpfunTokenLaunchTool extends Tool {
|
||||
telegram: parsedInput.telegram,
|
||||
website: parsedInput.website,
|
||||
initialLiquiditySOL: parsedInput.initialLiquiditySOL,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return JSON.stringify({
|
||||
@@ -542,7 +547,7 @@ export class SolanaLendAssetTool extends Tool {
|
||||
|
||||
async _call(input: string): Promise<string> {
|
||||
try {
|
||||
let amount = JSON.parse(input).amount || input;
|
||||
const amount = JSON.parse(input).amount || input;
|
||||
|
||||
const tx = await this.solanaKit.lendAssets(amount);
|
||||
|
||||
@@ -619,7 +624,7 @@ export class SolanaStakeTool extends Tool {
|
||||
export class SolanaFetchPriceTool extends Tool {
|
||||
name = "solana_fetch_price";
|
||||
description = `Fetch the price of a given token in USDC.
|
||||
|
||||
|
||||
Inputs:
|
||||
- tokenId: string, the mint address of the token, e.g., "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN"`;
|
||||
|
||||
@@ -629,7 +634,7 @@ export class SolanaFetchPriceTool extends Tool {
|
||||
|
||||
async _call(input: string): Promise<string> {
|
||||
try {
|
||||
const price = await fetchPrice(this.solanaKit, input.trim());
|
||||
const price = await this.solanaKit.fetchTokenPrice(input.trim());
|
||||
return JSON.stringify({
|
||||
status: "success",
|
||||
tokenId: input.trim(),
|
||||
@@ -708,7 +713,7 @@ export class SolanaTokenDataByTickerTool extends Tool {
|
||||
export class SolanaCompressedAirdropTool extends Tool {
|
||||
name = "solana_compressed_airdrop";
|
||||
description = `Airdrop SPL tokens with ZK Compression (also called as airdropping tokens)
|
||||
|
||||
|
||||
Inputs (input is a JSON string):
|
||||
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)
|
||||
@@ -731,7 +736,7 @@ export class SolanaCompressedAirdropTool extends Tool {
|
||||
parsedInput.decimals,
|
||||
parsedInput.recipients,
|
||||
parsedInput.priorityFeeInLamports || 30_000,
|
||||
parsedInput.shouldLog || false
|
||||
parsedInput.shouldLog || false,
|
||||
);
|
||||
|
||||
return JSON.stringify({
|
||||
@@ -776,7 +781,11 @@ export class SolanaCreateSingleSidedWhirlpoolTool extends Tool {
|
||||
const feeTier = inputFormat.feeTier;
|
||||
|
||||
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(
|
||||
@@ -820,7 +829,7 @@ export class SolanaRaydiumCreateAmmV4 extends Tool {
|
||||
|
||||
async _call(input: string): Promise<string> {
|
||||
try {
|
||||
let inputFormat = JSON.parse(input)
|
||||
const inputFormat = JSON.parse(input);
|
||||
|
||||
const tx = await this.solanaKit.raydiumCreateAmmV4(
|
||||
new PublicKey(inputFormat.marketId),
|
||||
@@ -862,7 +871,7 @@ export class SolanaRaydiumCreateClmm extends Tool {
|
||||
|
||||
async _call(input: string): Promise<string> {
|
||||
try {
|
||||
let inputFormat = JSON.parse(input)
|
||||
const inputFormat = JSON.parse(input);
|
||||
|
||||
const tx = await this.solanaKit.raydiumCreateClmm(
|
||||
new PublicKey(inputFormat.mint1),
|
||||
@@ -891,7 +900,7 @@ export class SolanaRaydiumCreateClmm extends Tool {
|
||||
|
||||
export class SolanaRaydiumCreateCpmm extends Tool {
|
||||
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):
|
||||
mint1: string (required)
|
||||
@@ -908,7 +917,7 @@ export class SolanaRaydiumCreateCpmm extends Tool {
|
||||
|
||||
async _call(input: string): Promise<string> {
|
||||
try {
|
||||
let inputFormat = JSON.parse(input)
|
||||
const inputFormat = JSON.parse(input);
|
||||
|
||||
const tx = await this.solanaKit.raydiumCreateCpmm(
|
||||
new PublicKey(inputFormat.mint1),
|
||||
@@ -939,7 +948,7 @@ export class SolanaRaydiumCreateCpmm extends Tool {
|
||||
|
||||
export class SolanaOpenbookCreateMarket extends Tool {
|
||||
name = "solana_openbook_create_market";
|
||||
description = `Openbook marketId, required for ammv4
|
||||
description = `Openbook marketId, required for ammv4
|
||||
|
||||
Inputs (input is a json string):
|
||||
baseMint: string (required)
|
||||
@@ -954,7 +963,7 @@ export class SolanaOpenbookCreateMarket extends Tool {
|
||||
|
||||
async _call(input: string): Promise<string> {
|
||||
try {
|
||||
let inputFormat = JSON.parse(input)
|
||||
const inputFormat = JSON.parse(input);
|
||||
|
||||
const tx = await this.solanaKit.openbookCreateMarket(
|
||||
new PublicKey(inputFormat.baseMint),
|
||||
@@ -993,14 +1002,14 @@ export class SolanaPythFetchPrice extends Tool {
|
||||
async _call(input: string): Promise<string> {
|
||||
try {
|
||||
const price = await this.solanaKit.pythFetchPrice(input);
|
||||
let response: PythFetchPriceResponse = {
|
||||
const response: PythFetchPriceResponse = {
|
||||
status: "success",
|
||||
priceFeedID: input,
|
||||
price: price,
|
||||
};
|
||||
return JSON.stringify(response);
|
||||
} catch (error: any) {
|
||||
let response: PythFetchPriceResponse = {
|
||||
const response: PythFetchPriceResponse = {
|
||||
status: "error",
|
||||
priceFeedID: input,
|
||||
message: error.message,
|
||||
@@ -1011,6 +1020,215 @@ export class SolanaPythFetchPrice extends Tool {
|
||||
}
|
||||
}
|
||||
|
||||
export class SolanaResolveAllDomainsTool extends Tool {
|
||||
name = "solana_resolve_all_domains";
|
||||
description = `Resolve domain names to a public key for ALL domain types EXCEPT .sol domains.
|
||||
Use this for domains like .blink, .bonk, etc.
|
||||
DO NOT use this for .sol domains (use solana_resolve_domain instead).
|
||||
|
||||
Input:
|
||||
domain: string, eg "mydomain.blink" or "mydomain.bonk" (required)`;
|
||||
|
||||
constructor(private solanaKit: SolanaAgentKit) {
|
||||
super();
|
||||
}
|
||||
|
||||
async _call(input: string): Promise<string> {
|
||||
try {
|
||||
const owner = await this.solanaKit.resolveAllDomains(input);
|
||||
|
||||
if (!owner) {
|
||||
return JSON.stringify({
|
||||
status: "error",
|
||||
message: "Domain not found",
|
||||
code: "DOMAIN_NOT_FOUND",
|
||||
});
|
||||
}
|
||||
|
||||
return JSON.stringify({
|
||||
status: "success",
|
||||
message: "Domain resolved successfully",
|
||||
owner: owner?.toString(),
|
||||
});
|
||||
} catch (error: any) {
|
||||
return JSON.stringify({
|
||||
status: "error",
|
||||
message: error.message,
|
||||
code: error.code || "DOMAIN_RESOLUTION_ERROR",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class SolanaGetOwnedDomains extends Tool {
|
||||
name = "solana_get_owned_domains";
|
||||
description = `Get all domains owned by a specific wallet address.
|
||||
|
||||
Inputs:
|
||||
owner: string, eg "4Be9CvxqHW6BYiRAxW9Q3xu1ycTMWaL5z8NX4HR3ha7t" (required)`;
|
||||
|
||||
constructor(private solanaKit: SolanaAgentKit) {
|
||||
super();
|
||||
}
|
||||
|
||||
async _call(input: string): Promise<string> {
|
||||
try {
|
||||
const ownerPubkey = new PublicKey(input.trim());
|
||||
const domains = await this.solanaKit.getOwnedAllDomains(ownerPubkey);
|
||||
|
||||
return JSON.stringify({
|
||||
status: "success",
|
||||
message: "Owned domains fetched successfully",
|
||||
domains: domains,
|
||||
});
|
||||
} catch (error: any) {
|
||||
return JSON.stringify({
|
||||
status: "error",
|
||||
message: error.message,
|
||||
code: error.code || "FETCH_OWNED_DOMAINS_ERROR",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class SolanaGetOwnedTldDomains extends Tool {
|
||||
name = "solana_get_owned_tld_domains";
|
||||
description = `Get all domains owned by the agent's wallet for a specific TLD.
|
||||
|
||||
Inputs:
|
||||
tld: string, eg "bonk" (required)`;
|
||||
|
||||
constructor(private solanaKit: SolanaAgentKit) {
|
||||
super();
|
||||
}
|
||||
|
||||
async _call(input: string): Promise<string> {
|
||||
try {
|
||||
const domains = await this.solanaKit.getOwnedDomainsForTLD(input);
|
||||
|
||||
return JSON.stringify({
|
||||
status: "success",
|
||||
message: "TLD domains fetched successfully",
|
||||
domains: domains,
|
||||
});
|
||||
} catch (error: any) {
|
||||
return JSON.stringify({
|
||||
status: "error",
|
||||
message: error.message,
|
||||
code: error.code || "FETCH_TLD_DOMAINS_ERROR",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class SolanaGetAllTlds extends Tool {
|
||||
name = "solana_get_all_tlds";
|
||||
description = `Get all active top-level domains (TLDs) in the AllDomains Name Service`;
|
||||
|
||||
constructor(private solanaKit: SolanaAgentKit) {
|
||||
super();
|
||||
}
|
||||
|
||||
async _call(): Promise<string> {
|
||||
try {
|
||||
const tlds = await this.solanaKit.getAllDomainsTLDs();
|
||||
|
||||
return JSON.stringify({
|
||||
status: "success",
|
||||
message: "TLDs fetched successfully",
|
||||
tlds: tlds,
|
||||
});
|
||||
} catch (error: any) {
|
||||
return JSON.stringify({
|
||||
status: "error",
|
||||
message: error.message,
|
||||
code: error.code || "FETCH_TLDS_ERROR",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class SolanaGetMainDomain extends Tool {
|
||||
name = "solana_get_main_domain";
|
||||
description = `Get the main/favorite domain for a given wallet address.
|
||||
|
||||
Inputs:
|
||||
owner: string, eg "4Be9CvxqHW6BYiRAxW9Q3xu1ycTMWaL5z8NX4HR3ha7t" (required)`;
|
||||
|
||||
constructor(private solanaKit: SolanaAgentKit) {
|
||||
super();
|
||||
}
|
||||
|
||||
async _call(input: string): Promise<string> {
|
||||
try {
|
||||
const ownerPubkey = new PublicKey(input.trim());
|
||||
const mainDomain =
|
||||
await this.solanaKit.getMainAllDomainsDomain(ownerPubkey);
|
||||
|
||||
return JSON.stringify({
|
||||
status: "success",
|
||||
message: "Main domain fetched successfully",
|
||||
domain: mainDomain,
|
||||
});
|
||||
} catch (error: any) {
|
||||
return JSON.stringify({
|
||||
status: "error",
|
||||
message: error.message,
|
||||
code: error.code || "FETCH_MAIN_DOMAIN_ERROR",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class SolanaCreateGibworkTask extends Tool {
|
||||
name = "create_gibwork_task";
|
||||
description = `Create a task on Gibwork.
|
||||
|
||||
Inputs (input is a JSON string):
|
||||
title: string, title of the task (required)
|
||||
content: string, description of the task (required)
|
||||
requirements: string, requirements to complete the task (required)
|
||||
tags: string[], list of tags associated with the task (required)
|
||||
payer: string, payer address (optional, defaults to agent wallet)
|
||||
tokenMintAddress: string, the mint address of the token, e.g., "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN" (required)
|
||||
amount: number, payment amount (required)
|
||||
`;
|
||||
|
||||
constructor(private solanaSdk: SolanaAgentKit) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async _call(input: string): Promise<string> {
|
||||
try {
|
||||
const parsedInput = JSON.parse(input);
|
||||
|
||||
const taskData = await this.solanaSdk.createGibworkTask(
|
||||
parsedInput.title,
|
||||
parsedInput.content,
|
||||
parsedInput.requirements,
|
||||
parsedInput.tags,
|
||||
parsedInput.tokenMintAddress,
|
||||
parsedInput.amount,
|
||||
parsedInput.payer,
|
||||
);
|
||||
|
||||
const response: GibworkCreateTaskReponse = {
|
||||
status: "success",
|
||||
taskId: taskData.taskId,
|
||||
signature: taskData.signature,
|
||||
};
|
||||
|
||||
return JSON.stringify(response);
|
||||
} catch (err: any) {
|
||||
return JSON.stringify({
|
||||
status: "error",
|
||||
message: err.message,
|
||||
code: err.code || "CREATE_TASK_ERROR",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function createSolanaTools(solanaKit: SolanaAgentKit) {
|
||||
return [
|
||||
new SolanaBalanceTool(solanaKit),
|
||||
@@ -1028,7 +1246,6 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) {
|
||||
new SolanaTPSCalculatorTool(solanaKit),
|
||||
new SolanaStakeTool(solanaKit),
|
||||
new SolanaFetchPriceTool(solanaKit),
|
||||
new SolanaResolveDomainTool(solanaKit),
|
||||
new SolanaGetDomainTool(solanaKit),
|
||||
new SolanaTokenDataTool(solanaKit),
|
||||
new SolanaTokenDataByTickerTool(solanaKit),
|
||||
@@ -1039,6 +1256,12 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) {
|
||||
new SolanaOpenbookCreateMarket(solanaKit),
|
||||
new SolanaCreateSingleSidedWhirlpoolTool(solanaKit),
|
||||
new SolanaPythFetchPrice(solanaKit),
|
||||
new SolanaResolveDomainTool(solanaKit),
|
||||
new SolanaGetOwnedDomains(solanaKit),
|
||||
new SolanaGetOwnedTldDomains(solanaKit),
|
||||
new SolanaGetAllTlds(solanaKit),
|
||||
new SolanaGetMainDomain(solanaKit),
|
||||
new SolanaResolveAllDomainsTool(solanaKit),
|
||||
new SolanaCreateGibworkTask(solanaKit),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
81
src/tools/create_gibwork_task.ts
Normal file
81
src/tools/create_gibwork_task.ts
Normal 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}`);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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, {
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
19
src/tools/get_all_domains_tlds.ts
Normal file
19
src/tools/get_all_domains_tlds.ts
Normal 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}`);
|
||||
}
|
||||
}
|
||||
36
src/tools/get_all_registered_all_domains.ts
Normal file
36
src/tools/get_all_registered_all_domains.ts
Normal 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}`);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
21
src/tools/get_main_all_domains_domain.ts
Normal file
21
src/tools/get_main_all_domains_domain.ts
Normal 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;
|
||||
}
|
||||
}
|
||||
23
src/tools/get_owned_all_domains.ts
Normal file
23
src/tools/get_owned_all_domains.ts
Normal 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}`);
|
||||
}
|
||||
}
|
||||
21
src/tools/get_owned_domains_for_tld.ts
Normal file
21
src/tools/get_owned_domains_for_tld.ts
Normal 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}`);
|
||||
}
|
||||
}
|
||||
@@ -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()}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
30
src/tools/resolve_domain.ts
Normal file
30
src/tools/resolve_domain.ts
Normal 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}`);
|
||||
}
|
||||
}
|
||||
@@ -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.");
|
||||
|
||||
@@ -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
|
||||
}`
|
||||
}`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { VersionedTransaction } from "@solana/web3.js";
|
||||
import { SolanaAgentKit } from "../agent";
|
||||
import { SolanaAgentKit } from "../index";
|
||||
|
||||
/**
|
||||
* Stake SOL with Jup validator
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,3 +85,9 @@ export interface PythFetchPriceResponse {
|
||||
message?: string;
|
||||
code?: string;
|
||||
}
|
||||
|
||||
export interface GibworkCreateTaskReponse {
|
||||
status: "success" | "error";
|
||||
taskId?: string | undefined;
|
||||
signature?: string | undefined;
|
||||
}
|
||||
|
||||
@@ -4,4 +4,4 @@ import bs58 from "bs58";
|
||||
export const keypair = Keypair.generate();
|
||||
|
||||
console.log(keypair.publicKey.toString());
|
||||
console.log(bs58.encode(keypair.secretKey));
|
||||
console.log(bs58.encode(keypair.secretKey));
|
||||
|
||||
Reference in New Issue
Block a user