mirror of
https://github.com/d0zingcat/solana-agent-kit.git
synced 2026-05-18 07:36:46 +00:00
merge and docs
This commit is contained in:
@@ -2,7 +2,6 @@ import { Action } from "../types/action";
|
||||
import { SolanaAgentKit } from "../agent";
|
||||
import { z } from "zod";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { BN } from "@coral-xyz/anchor";
|
||||
import { Decimal } from "decimal.js";
|
||||
import { orcaCreateSingleSidedLiquidityPool } from "../tools";
|
||||
|
||||
|
||||
29
src/actions/getWalletAddress.ts
Normal file
29
src/actions/getWalletAddress.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { z } from "zod";
|
||||
import { SolanaAgentKit } from "..";
|
||||
import { get_wallet_address } from "../tools";
|
||||
import { Action } from "../types/action";
|
||||
|
||||
const getWalletAddressAction: Action = {
|
||||
name: "GET_WALLET_ADDRESS",
|
||||
similes: ["wallet address", "address", "wallet"],
|
||||
description: "Get wallet address of the agent",
|
||||
examples: [
|
||||
[
|
||||
{
|
||||
input: {},
|
||||
output: {
|
||||
status: "success",
|
||||
address: "0x1234567890abcdef",
|
||||
},
|
||||
explanation: "The agent's wallet address is 0x1234567890abcdef",
|
||||
},
|
||||
],
|
||||
],
|
||||
schema: z.object({}),
|
||||
handler: async (agent: SolanaAgentKit) => ({
|
||||
status: "success",
|
||||
address: get_wallet_address(agent),
|
||||
}),
|
||||
};
|
||||
|
||||
export default getWalletAddressAction;
|
||||
@@ -10,6 +10,7 @@ import getTokenDataAction from "./getTokenData";
|
||||
import getTPSAction from "./getTPS";
|
||||
import fetchPriceAction from "./fetchPrice";
|
||||
import stakeWithJupAction from "./stakeWithJup";
|
||||
import stakeWithSolayerAction from "./stakeWithSolayer";
|
||||
import registerDomainAction from "./registerDomain";
|
||||
import lendAssetAction from "./lendAsset";
|
||||
import createGibworkTaskAction from "./createGibworkTask";
|
||||
@@ -26,8 +27,10 @@ import raydiumCreateCpmmAction from "./raydiumCreateCpmm";
|
||||
import raydiumCreateAmmV4Action from "./raydiumCreateAmmV4";
|
||||
import createOrcaSingleSidedWhirlpoolAction from "./createOrcaSingleSidedWhirlpool";
|
||||
import launchPumpfunTokenAction from "./launchPumpfunToken";
|
||||
import getWalletAddressAction from "./getWalletAddress";
|
||||
|
||||
export const ACTIONS = {
|
||||
WALLET_ADDRESS_ACTION: getWalletAddressAction,
|
||||
DEPLOY_TOKEN_ACTION: deployTokenAction,
|
||||
BALANCE_ACTION: balanceAction,
|
||||
TRANSFER_ACTION: transferAction,
|
||||
@@ -40,6 +43,7 @@ export const ACTIONS = {
|
||||
GET_TPS_ACTION: getTPSAction,
|
||||
FETCH_PRICE_ACTION: fetchPriceAction,
|
||||
STAKE_WITH_JUP_ACTION: stakeWithJupAction,
|
||||
STAKE_WITH_SOLAYER_ACTION: stakeWithSolayerAction,
|
||||
REGISTER_DOMAIN_ACTION: registerDomainAction,
|
||||
LEND_ASSET_ACTION: lendAssetAction,
|
||||
CREATE_GIBWORK_TASK_ACTION: createGibworkTaskAction,
|
||||
|
||||
60
src/actions/stakeWithSolayer.ts
Normal file
60
src/actions/stakeWithSolayer.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { Action } from "../types/action";
|
||||
import { SolanaAgentKit } from "../agent";
|
||||
import { z } from "zod";
|
||||
import { stakeWithSolayer } from "../tools";
|
||||
|
||||
const stakeWithSolayerAction: Action = {
|
||||
name: "STAKE_WITH_SOLAYER",
|
||||
similes: [
|
||||
"stake sol",
|
||||
"solayer sol",
|
||||
"ssol",
|
||||
"stake with solayer",
|
||||
"solayer restaking",
|
||||
"solayer staking",
|
||||
"stake with sol",
|
||||
"liquid staking solayer",
|
||||
"get solayer sol",
|
||||
"solayer sol restaking",
|
||||
"solayer sol staking",
|
||||
],
|
||||
description:
|
||||
"Stake native SOL with Solayer's restaking protocol to receive Solayer SOL (sSOL)",
|
||||
examples: [
|
||||
[
|
||||
{
|
||||
input: {
|
||||
amount: 1.0,
|
||||
},
|
||||
output: {
|
||||
status: "success",
|
||||
signature: "3FgHn9...",
|
||||
message: "Successfully staked 1.0 SOL for Solayer SOL (sSOL)",
|
||||
},
|
||||
explanation: "Stake 1.0 SOL to receive Solayer SOL (sSOL)",
|
||||
},
|
||||
],
|
||||
],
|
||||
schema: z.object({
|
||||
amount: z.number().positive().describe("Amount of SOL to stake"),
|
||||
}),
|
||||
handler: async (agent: SolanaAgentKit, input: Record<string, any>) => {
|
||||
try {
|
||||
const amount = input.amount as number;
|
||||
|
||||
const res = await stakeWithSolayer(agent, amount);
|
||||
return {
|
||||
status: "success",
|
||||
res,
|
||||
message: `Successfully staked ${amount} SOL for Solayer SOL (sSOL)`,
|
||||
};
|
||||
} catch (error: any) {
|
||||
return {
|
||||
status: "error",
|
||||
message: `Solayer staking failed: ${error.message}`,
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default stakeWithSolayerAction;
|
||||
@@ -27,10 +27,15 @@ import {
|
||||
batchOrder,
|
||||
cancelAllOrders,
|
||||
withdrawAll,
|
||||
closePerpTradeShort,
|
||||
closePerpTradeLong,
|
||||
openPerpTradeShort,
|
||||
openPerpTradeLong,
|
||||
transfer,
|
||||
getTokenDataByAddress,
|
||||
getTokenDataByTicker,
|
||||
stakeWithJup,
|
||||
stakeWithSolayer,
|
||||
sendCompressedAirdrop,
|
||||
orcaCreateSingleSidedLiquidityPool,
|
||||
orcaCreateCLMM,
|
||||
@@ -209,6 +214,42 @@ export class SolanaAgentKit {
|
||||
return withdrawAll(this, marketId);
|
||||
}
|
||||
|
||||
async openPerpTradeLong(
|
||||
args: Omit<Parameters<typeof openPerpTradeLong>[0], "agent">,
|
||||
): Promise<string> {
|
||||
return openPerpTradeLong({
|
||||
agent: this,
|
||||
...args,
|
||||
});
|
||||
}
|
||||
|
||||
async openPerpTradeShort(
|
||||
args: Omit<Parameters<typeof openPerpTradeShort>[0], "agent">,
|
||||
): Promise<string> {
|
||||
return openPerpTradeShort({
|
||||
agent: this,
|
||||
...args,
|
||||
});
|
||||
}
|
||||
|
||||
async closePerpTradeShort(
|
||||
args: Omit<Parameters<typeof closePerpTradeShort>[0], "agent">,
|
||||
): Promise<string> {
|
||||
return closePerpTradeShort({
|
||||
agent: this,
|
||||
...args,
|
||||
});
|
||||
}
|
||||
|
||||
async closePerpTradeLong(
|
||||
args: Omit<Parameters<typeof closePerpTradeLong>[0], "agent">,
|
||||
): Promise<string> {
|
||||
return closePerpTradeLong({
|
||||
agent: this,
|
||||
...args,
|
||||
});
|
||||
}
|
||||
|
||||
async lendAssets(amount: number): Promise<string> {
|
||||
return lendAsset(this, amount);
|
||||
}
|
||||
@@ -254,6 +295,10 @@ export class SolanaAgentKit {
|
||||
return stakeWithJup(this, amount);
|
||||
}
|
||||
|
||||
async restake(amount: number): Promise<string> {
|
||||
return stakeWithSolayer(this, amount);
|
||||
}
|
||||
|
||||
async sendCompressedAirdrop(
|
||||
mintAddress: string,
|
||||
amount: number,
|
||||
|
||||
@@ -18,11 +18,13 @@ export const TOKENS = {
|
||||
* Default configuration options
|
||||
* @property {number} SLIPPAGE_BPS - Default slippage tolerance in basis points (300 = 3%)
|
||||
* @property {number} TOKEN_DECIMALS - Default number of decimals for new tokens
|
||||
* @property {number} LEVERAGE_BPS - Default leverage for trading PERP
|
||||
*/
|
||||
export const DEFAULT_OPTIONS = {
|
||||
SLIPPAGE_BPS: 300,
|
||||
TOKEN_DECIMALS: 9,
|
||||
RERERRAL_FEE: 200,
|
||||
LEVERAGE_BPS: 50000, // 10000 = x1, 50000 = x5, 100000 = x10, 1000000 = x100
|
||||
} as const;
|
||||
|
||||
/**
|
||||
|
||||
20671
src/idls/adrena.ts
Normal file
20671
src/idls/adrena.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,8 @@
|
||||
import { SolanaAgentKit } from "./agent";
|
||||
import { createSolanaTools } from "./langchain";
|
||||
import { createSolanaTools as createVercelAITools } from "./vercel-ai";
|
||||
|
||||
export { SolanaAgentKit, createSolanaTools };
|
||||
export { SolanaAgentKit, createSolanaTools, createVercelAITools };
|
||||
|
||||
// Optional: Export types that users might need
|
||||
export * from "./types";
|
||||
|
||||
@@ -261,6 +261,114 @@ export class SolanaMintNFTTool extends Tool {
|
||||
}
|
||||
}
|
||||
|
||||
export class SolanaPerpCloseTradeTool extends Tool {
|
||||
name = "solana_close_perp_trade";
|
||||
description = `This tool can be used to close perpetuals trade ( It uses Adrena Protocol ).
|
||||
|
||||
Inputs ( input is a JSON string ):
|
||||
tradeMint: string, eg "J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn", "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263" etc. (optional)
|
||||
price?: number, eg 100 (optional)
|
||||
side: string, eg: "long" or "short"`;
|
||||
|
||||
constructor(private solanaKit: SolanaAgentKit) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async _call(input: string): Promise<string> {
|
||||
try {
|
||||
const parsedInput = JSON.parse(input);
|
||||
|
||||
const tx =
|
||||
parsedInput.side === "long"
|
||||
? await this.solanaKit.closePerpTradeLong({
|
||||
price: parsedInput.price,
|
||||
tradeMint: new PublicKey(parsedInput.tradeMint),
|
||||
})
|
||||
: await this.solanaKit.closePerpTradeShort({
|
||||
price: parsedInput.price,
|
||||
tradeMint: new PublicKey(parsedInput.tradeMint),
|
||||
});
|
||||
|
||||
return JSON.stringify({
|
||||
status: "success",
|
||||
message: "Perpetual trade closed successfully",
|
||||
transaction: tx,
|
||||
price: parsedInput.price,
|
||||
tradeMint: new PublicKey(parsedInput.tradeMint),
|
||||
side: parsedInput.side,
|
||||
});
|
||||
} catch (error: any) {
|
||||
return JSON.stringify({
|
||||
status: "error",
|
||||
message: error.message,
|
||||
code: error.code || "UNKNOWN_ERROR",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class SolanaPerpOpenTradeTool extends Tool {
|
||||
name = "solana_open_perp_trade";
|
||||
description = `This tool can be used to open perpetuals trade ( It uses Adrena Protocol ).
|
||||
|
||||
Inputs ( input is a JSON string ):
|
||||
collateralAmount: number, eg 1 or 0.01 (required)
|
||||
collateralMint: string, eg "J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn" or "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v" etc. (optional)
|
||||
tradeMint: string, eg "J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn", "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263" etc. (optional)
|
||||
leverage: number, eg 50000 = x5, 100000 = x10, 1000000 = x100 (optional)
|
||||
price?: number, eg 100 (optional)
|
||||
slippage?: number, eg 0.3 (optional)
|
||||
side: string, eg: "long" or "short"`;
|
||||
|
||||
constructor(private solanaKit: SolanaAgentKit) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async _call(input: string): Promise<string> {
|
||||
try {
|
||||
const parsedInput = JSON.parse(input);
|
||||
|
||||
const tx =
|
||||
parsedInput.side === "long"
|
||||
? await this.solanaKit.openPerpTradeLong({
|
||||
price: parsedInput.price,
|
||||
collateralAmount: parsedInput.collateralAmount,
|
||||
collateralMint: new PublicKey(parsedInput.collateralMint),
|
||||
leverage: parsedInput.leverage,
|
||||
tradeMint: new PublicKey(parsedInput.tradeMint),
|
||||
slippage: parsedInput.slippage,
|
||||
})
|
||||
: await this.solanaKit.openPerpTradeLong({
|
||||
price: parsedInput.price,
|
||||
collateralAmount: parsedInput.collateralAmount,
|
||||
collateralMint: new PublicKey(parsedInput.collateralMint),
|
||||
leverage: parsedInput.leverage,
|
||||
tradeMint: new PublicKey(parsedInput.tradeMint),
|
||||
slippage: parsedInput.slippage,
|
||||
});
|
||||
|
||||
return JSON.stringify({
|
||||
status: "success",
|
||||
message: "Perpetual trade opened successfully",
|
||||
transaction: tx,
|
||||
price: parsedInput.price,
|
||||
collateralAmount: parsedInput.collateralAmount,
|
||||
collateralMint: new PublicKey(parsedInput.collateralMint),
|
||||
leverage: parsedInput.leverage,
|
||||
tradeMint: new PublicKey(parsedInput.tradeMint),
|
||||
slippage: parsedInput.slippage,
|
||||
side: parsedInput.side,
|
||||
});
|
||||
} catch (error: any) {
|
||||
return JSON.stringify({
|
||||
status: "error",
|
||||
message: error.message,
|
||||
code: error.code || "UNKNOWN_ERROR",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class SolanaTradeTool extends Tool {
|
||||
name = "solana_trade";
|
||||
description = `This tool can be used to swap tokens to another token ( It uses Jupiter Exchange ).
|
||||
@@ -862,6 +970,39 @@ export class SolanaStakeTool extends Tool {
|
||||
}
|
||||
}
|
||||
|
||||
export class SolanaRestakeTool extends Tool {
|
||||
name = "solana_restake";
|
||||
description = `This tool can be used to restake your SOL on Solayer to receive Solayer SOL (sSOL) as a Liquid Staking Token (LST).
|
||||
|
||||
Inputs:
|
||||
amount: number, eg 1 or 0.01 (required)`;
|
||||
|
||||
constructor(private solanaKit: SolanaAgentKit) {
|
||||
super();
|
||||
}
|
||||
|
||||
protected async _call(input: string): Promise<string> {
|
||||
try {
|
||||
const parsedInput = JSON.parse(input) || Number(input);
|
||||
|
||||
const tx = await this.solanaKit.restake(parsedInput.amount);
|
||||
|
||||
return JSON.stringify({
|
||||
status: "success",
|
||||
message: "Staked successfully",
|
||||
transaction: tx,
|
||||
amount: parsedInput.amount,
|
||||
});
|
||||
} catch (error: any) {
|
||||
return JSON.stringify({
|
||||
status: "error",
|
||||
message: error.message,
|
||||
code: error.code || "UNKNOWN_ERROR",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tool to fetch the price of a token in USDC
|
||||
*/
|
||||
@@ -1002,7 +1143,7 @@ export class SolanaClosePosition extends Tool {
|
||||
name = "orca_close_position";
|
||||
description = `Closes an existing liquidity position in an Orca Whirlpool. This function fetches the position
|
||||
details using the provided mint address and closes the position with a 1% slippage.
|
||||
|
||||
|
||||
Inputs (JSON string):
|
||||
- positionMintAddress: string, the address of the position mint that represents the liquidity position.`;
|
||||
|
||||
@@ -1089,9 +1230,9 @@ export class SolanaOrcaCreateCLMM extends Tool {
|
||||
|
||||
export class SolanaOrcaCreateSingleSideLiquidityPool extends Tool {
|
||||
name = "orca_create_single_sided_liquidity_pool";
|
||||
description = `Create a single-sided liquidity pool on Orca, the most efficient and capital-optimized CLMM platform on Solana.
|
||||
description = `Create a single-sided liquidity pool on Orca, the most efficient and capital-optimized CLMM platform on Solana.
|
||||
|
||||
This function initializes a single-sided liquidity pool, ideal for community driven project, fair launches, and fundraising. Minimize price impact by setting a narrow price range.
|
||||
This function initializes a single-sided liquidity pool, ideal for community driven project, fair launches, and fundraising. Minimize price impact by setting a narrow price range.
|
||||
|
||||
Inputs (JSON string):
|
||||
- depositTokenAmount: number, in units of the deposit token including decimals, e.g., 1000000000 (required).
|
||||
@@ -2001,6 +2142,7 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) {
|
||||
new SolanaLendAssetTool(solanaKit),
|
||||
new SolanaTPSCalculatorTool(solanaKit),
|
||||
new SolanaStakeTool(solanaKit),
|
||||
new SolanaRestakeTool(solanaKit),
|
||||
new SolanaFetchPriceTool(solanaKit),
|
||||
new SolanaGetDomainTool(solanaKit),
|
||||
new SolanaTokenDataTool(solanaKit),
|
||||
@@ -2035,5 +2177,7 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) {
|
||||
new SolanaCancelNFTListingTool(solanaKit),
|
||||
new SolanaFetchTokenReportSummaryTool(solanaKit),
|
||||
new SolanaFetchTokenDetailedReportTool(solanaKit),
|
||||
new SolanaPerpOpenTradeTool(solanaKit),
|
||||
new SolanaPerpCloseTradeTool(solanaKit),
|
||||
];
|
||||
}
|
||||
|
||||
506
src/tools/adrena_perp_trading.ts
Normal file
506
src/tools/adrena_perp_trading.ts
Normal file
@@ -0,0 +1,506 @@
|
||||
import {
|
||||
PublicKey,
|
||||
SystemProgram,
|
||||
TransactionInstruction,
|
||||
} from "@solana/web3.js";
|
||||
import { SolanaAgentKit } from "../index";
|
||||
import { TOKENS, DEFAULT_OPTIONS } from "../constants";
|
||||
import { TOKEN_PROGRAM_ID } from "@solana/spl-token";
|
||||
import { BN } from "@coral-xyz/anchor";
|
||||
|
||||
import AdrenaClient from "../utils/AdrenaClient";
|
||||
import { sendTx } from "../utils/send_tx";
|
||||
|
||||
const PRICE_DECIMALS = 10;
|
||||
const ADRENA_PROGRAM_ID = new PublicKey(
|
||||
"13gDzEXCdocbj8iAiqrScGo47NiSuYENGsRqi3SEAwet",
|
||||
);
|
||||
|
||||
// i.e percentage = -2 (for -2%)
|
||||
// i.e percentage = 5 (for 5%)
|
||||
function applySlippage(nb: BN, percentage: number): BN {
|
||||
const negative = percentage < 0 ? true : false;
|
||||
|
||||
// Do x10_000 so percentage can be up to 4 decimals
|
||||
const percentageBN = new BN(
|
||||
(negative ? percentage * -1 : percentage) * 10_000,
|
||||
);
|
||||
|
||||
const delta = nb.mul(percentageBN).divRound(new BN(10_000 * 100));
|
||||
|
||||
return negative ? nb.sub(delta) : nb.add(delta);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close short trade on Adrena
|
||||
* @returns Transaction signature
|
||||
*/
|
||||
export async function closePerpTradeShort({
|
||||
agent,
|
||||
price,
|
||||
tradeMint,
|
||||
}: {
|
||||
agent: SolanaAgentKit;
|
||||
price: number;
|
||||
tradeMint: PublicKey;
|
||||
}) {
|
||||
const client = await AdrenaClient.load(agent);
|
||||
|
||||
const owner = agent.wallet.publicKey;
|
||||
|
||||
const custody = client.getCustodyByMint(tradeMint);
|
||||
const collateralCustody = client.getCustodyByMint(TOKENS.USDC);
|
||||
|
||||
const stakingRewardTokenCustodyAccount = client.getCustodyByMint(
|
||||
AdrenaClient.stakingRewardTokenMint,
|
||||
);
|
||||
|
||||
const stakingRewardTokenCustodyTokenAccount =
|
||||
AdrenaClient.findCustodyTokenAccountAddress(
|
||||
AdrenaClient.stakingRewardTokenMint,
|
||||
);
|
||||
|
||||
const position = AdrenaClient.findPositionAddress(
|
||||
owner,
|
||||
custody.pubkey,
|
||||
"long",
|
||||
);
|
||||
|
||||
const userProfilePda = AdrenaClient.getUserProfilePda(owner);
|
||||
|
||||
const userProfile =
|
||||
await client.program.account.userProfile.fetchNullable(userProfilePda);
|
||||
|
||||
const receivingAccount = AdrenaClient.findATAAddressSync(
|
||||
owner,
|
||||
collateralCustody.mint,
|
||||
);
|
||||
|
||||
const preInstructions: TransactionInstruction[] = [];
|
||||
|
||||
const collateralCustodyOracle = collateralCustody.oracle;
|
||||
const collateralCustodyTokenAccount =
|
||||
AdrenaClient.findCustodyTokenAccountAddress(collateralCustody.mint);
|
||||
|
||||
if (
|
||||
!(await AdrenaClient.isAccountInitialized(
|
||||
agent.connection,
|
||||
receivingAccount,
|
||||
))
|
||||
) {
|
||||
preInstructions.push(
|
||||
AdrenaClient.createATAInstruction({
|
||||
ataAddress: receivingAccount,
|
||||
mint: collateralCustody.mint,
|
||||
owner,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const instruction = await client.program.methods
|
||||
.closePositionShort({
|
||||
price: new BN(price * 10 ** PRICE_DECIMALS),
|
||||
})
|
||||
.accountsStrict({
|
||||
owner,
|
||||
receivingAccount,
|
||||
transferAuthority: AdrenaClient.transferAuthority,
|
||||
pool: AdrenaClient.mainPool,
|
||||
position: position,
|
||||
custody: custody.pubkey,
|
||||
custodyTradeOracle: custody.tradeOracle,
|
||||
tokenProgram: TOKEN_PROGRAM_ID,
|
||||
lmStaking: AdrenaClient.lmStaking,
|
||||
lpStaking: AdrenaClient.lpStaking,
|
||||
cortex: AdrenaClient.cortex,
|
||||
stakingRewardTokenCustody: stakingRewardTokenCustodyAccount.pubkey,
|
||||
stakingRewardTokenCustodyOracle: stakingRewardTokenCustodyAccount.oracle,
|
||||
stakingRewardTokenCustodyTokenAccount,
|
||||
lmStakingRewardTokenVault: AdrenaClient.lmStakingRewardTokenVault,
|
||||
lpStakingRewardTokenVault: AdrenaClient.lpStakingRewardTokenVault,
|
||||
lpTokenMint: AdrenaClient.lpTokenMint,
|
||||
protocolFeeRecipient: client.cortex.protocolFeeRecipient,
|
||||
adrenaProgram: AdrenaClient.programId,
|
||||
userProfile: userProfile ? userProfilePda : null,
|
||||
caller: owner,
|
||||
collateralCustody: collateralCustody.pubkey,
|
||||
collateralCustodyOracle,
|
||||
collateralCustodyTokenAccount,
|
||||
})
|
||||
.instruction();
|
||||
|
||||
return sendTx(agent, [...preInstructions, instruction]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close long trade on Adrena
|
||||
* @returns Transaction signature
|
||||
*/
|
||||
export async function closePerpTradeLong({
|
||||
agent,
|
||||
price,
|
||||
tradeMint,
|
||||
}: {
|
||||
agent: SolanaAgentKit;
|
||||
price: number;
|
||||
tradeMint: PublicKey;
|
||||
}) {
|
||||
const client = await AdrenaClient.load(agent);
|
||||
|
||||
const owner = agent.wallet.publicKey;
|
||||
|
||||
const custody = client.getCustodyByMint(tradeMint);
|
||||
|
||||
const custodyTokenAccount =
|
||||
AdrenaClient.findCustodyTokenAccountAddress(tradeMint);
|
||||
|
||||
const stakingRewardTokenCustodyAccount = client.getCustodyByMint(
|
||||
AdrenaClient.stakingRewardTokenMint,
|
||||
);
|
||||
|
||||
const stakingRewardTokenCustodyTokenAccount =
|
||||
AdrenaClient.findCustodyTokenAccountAddress(
|
||||
AdrenaClient.stakingRewardTokenMint,
|
||||
);
|
||||
|
||||
const position = AdrenaClient.findPositionAddress(
|
||||
owner,
|
||||
custody.pubkey,
|
||||
"long",
|
||||
);
|
||||
|
||||
const userProfilePda = AdrenaClient.getUserProfilePda(owner);
|
||||
|
||||
const userProfile =
|
||||
await client.program.account.userProfile.fetchNullable(userProfilePda);
|
||||
|
||||
const receivingAccount = AdrenaClient.findATAAddressSync(owner, custody.mint);
|
||||
|
||||
const preInstructions: TransactionInstruction[] = [];
|
||||
|
||||
if (
|
||||
!(await AdrenaClient.isAccountInitialized(
|
||||
agent.connection,
|
||||
receivingAccount,
|
||||
))
|
||||
) {
|
||||
preInstructions.push(
|
||||
AdrenaClient.createATAInstruction({
|
||||
ataAddress: receivingAccount,
|
||||
mint: custody.mint,
|
||||
owner,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const instruction = await client.program.methods
|
||||
.closePositionLong({
|
||||
price: new BN(price * 10 ** PRICE_DECIMALS),
|
||||
})
|
||||
.accountsStrict({
|
||||
owner,
|
||||
receivingAccount,
|
||||
transferAuthority: AdrenaClient.transferAuthority,
|
||||
pool: AdrenaClient.mainPool,
|
||||
position: position,
|
||||
custody: custody.pubkey,
|
||||
custodyTokenAccount,
|
||||
custodyOracle: custody.oracle,
|
||||
custodyTradeOracle: custody.tradeOracle,
|
||||
tokenProgram: TOKEN_PROGRAM_ID,
|
||||
lmStaking: AdrenaClient.lmStaking,
|
||||
lpStaking: AdrenaClient.lpStaking,
|
||||
cortex: AdrenaClient.cortex,
|
||||
stakingRewardTokenCustody: stakingRewardTokenCustodyAccount.pubkey,
|
||||
stakingRewardTokenCustodyOracle: stakingRewardTokenCustodyAccount.oracle,
|
||||
stakingRewardTokenCustodyTokenAccount,
|
||||
lmStakingRewardTokenVault: AdrenaClient.lmStakingRewardTokenVault,
|
||||
lpStakingRewardTokenVault: AdrenaClient.lpStakingRewardTokenVault,
|
||||
lpTokenMint: AdrenaClient.lpTokenMint,
|
||||
protocolFeeRecipient: client.cortex.protocolFeeRecipient,
|
||||
adrenaProgram: AdrenaClient.programId,
|
||||
userProfile: userProfile ? userProfilePda : null,
|
||||
caller: owner,
|
||||
})
|
||||
.instruction();
|
||||
|
||||
return sendTx(agent, [...preInstructions, instruction]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open long trade on Adrena
|
||||
*
|
||||
* Note: provide the same token as collateralMint and as tradeMint to avoid swap
|
||||
* @returns Transaction signature
|
||||
*/
|
||||
export async function openPerpTradeLong({
|
||||
agent,
|
||||
price,
|
||||
collateralAmount,
|
||||
collateralMint = TOKENS.jitoSOL,
|
||||
leverage = DEFAULT_OPTIONS.LEVERAGE_BPS,
|
||||
tradeMint = TOKENS.jitoSOL,
|
||||
slippage = 0.3,
|
||||
}: {
|
||||
agent: SolanaAgentKit;
|
||||
price: number;
|
||||
collateralAmount: number;
|
||||
collateralMint?: PublicKey;
|
||||
leverage?: number;
|
||||
tradeMint?: PublicKey;
|
||||
slippage?: number;
|
||||
}): Promise<string> {
|
||||
const client = await AdrenaClient.load(agent);
|
||||
|
||||
const owner = agent.wallet.publicKey;
|
||||
|
||||
const collateralAccount = AdrenaClient.findATAAddressSync(owner, tradeMint);
|
||||
const fundingAccount = AdrenaClient.findATAAddressSync(owner, collateralMint);
|
||||
|
||||
const receivingCustody = AdrenaClient.findCustodyAddress(collateralMint);
|
||||
const receivingCustodyOracle = client.getCustodyByMint(collateralMint).oracle;
|
||||
const receivingCustodyTokenAccount =
|
||||
AdrenaClient.findCustodyTokenAccountAddress(collateralMint);
|
||||
|
||||
// Principal custody is the custody of the targeted token
|
||||
// i.e open a 1 ETH long position, principal custody is ETH
|
||||
const principalCustody = AdrenaClient.findCustodyAddress(tradeMint);
|
||||
const principalCustodyAccount = client.getCustodyByMint(tradeMint);
|
||||
const principalCustodyOracle = principalCustodyAccount.oracle;
|
||||
const principalCustodyTradeOracle = principalCustodyAccount.tradeOracle;
|
||||
const principalCustodyTokenAccount =
|
||||
AdrenaClient.findCustodyTokenAccountAddress(tradeMint);
|
||||
|
||||
const stakingRewardTokenCustodyAccount = client.getCustodyByMint(
|
||||
AdrenaClient.stakingRewardTokenMint,
|
||||
);
|
||||
|
||||
const stakingRewardTokenCustodyTokenAccount =
|
||||
AdrenaClient.findCustodyTokenAccountAddress(
|
||||
AdrenaClient.stakingRewardTokenMint,
|
||||
);
|
||||
|
||||
const position = AdrenaClient.findPositionAddress(
|
||||
owner,
|
||||
principalCustody,
|
||||
"long",
|
||||
);
|
||||
|
||||
const userProfilePda = AdrenaClient.getUserProfilePda(owner);
|
||||
|
||||
const userProfile =
|
||||
await client.program.account.userProfile.fetchNullable(userProfilePda);
|
||||
|
||||
const priceWithSlippage = applySlippage(
|
||||
new BN(price * 10 ** PRICE_DECIMALS),
|
||||
slippage,
|
||||
);
|
||||
|
||||
const scaledCollateralAmount = new BN(
|
||||
collateralAmount *
|
||||
Math.pow(10, client.getCustodyByMint(collateralMint).decimals),
|
||||
);
|
||||
|
||||
const preInstructions: TransactionInstruction[] = [];
|
||||
|
||||
if (
|
||||
!(await AdrenaClient.isAccountInitialized(
|
||||
agent.connection,
|
||||
collateralAccount,
|
||||
))
|
||||
) {
|
||||
preInstructions.push(
|
||||
AdrenaClient.createATAInstruction({
|
||||
ataAddress: collateralAccount,
|
||||
mint: tradeMint,
|
||||
owner,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const instruction = await client.program.methods
|
||||
.openOrIncreasePositionWithSwapLong({
|
||||
price: priceWithSlippage,
|
||||
collateral: scaledCollateralAmount,
|
||||
leverage,
|
||||
referrer: null,
|
||||
})
|
||||
.accountsStrict({
|
||||
owner,
|
||||
payer: owner,
|
||||
fundingAccount,
|
||||
collateralAccount,
|
||||
receivingCustody,
|
||||
receivingCustodyOracle,
|
||||
receivingCustodyTokenAccount,
|
||||
principalCustody,
|
||||
principalCustodyOracle,
|
||||
principalCustodyTradeOracle,
|
||||
principalCustodyTokenAccount,
|
||||
transferAuthority: AdrenaClient.transferAuthority,
|
||||
cortex: AdrenaClient.cortex,
|
||||
lmStaking: AdrenaClient.lmStaking,
|
||||
lpStaking: AdrenaClient.lpStaking,
|
||||
pool: AdrenaClient.mainPool,
|
||||
position,
|
||||
stakingRewardTokenCustody: stakingRewardTokenCustodyAccount.pubkey,
|
||||
stakingRewardTokenCustodyOracle: stakingRewardTokenCustodyAccount.oracle,
|
||||
stakingRewardTokenCustodyTokenAccount,
|
||||
lmStakingRewardTokenVault: AdrenaClient.lmStakingRewardTokenVault,
|
||||
lpStakingRewardTokenVault: AdrenaClient.lpStakingRewardTokenVault,
|
||||
lpTokenMint: AdrenaClient.lpTokenMint,
|
||||
userProfile: userProfile ? userProfilePda : null,
|
||||
protocolFeeRecipient: client.cortex.protocolFeeRecipient,
|
||||
systemProgram: SystemProgram.programId,
|
||||
tokenProgram: TOKEN_PROGRAM_ID,
|
||||
adrenaProgram: ADRENA_PROGRAM_ID,
|
||||
})
|
||||
.instruction();
|
||||
|
||||
return sendTx(agent, [...preInstructions, instruction]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open short trade on Adrena
|
||||
*
|
||||
* Note: provide USDC as collateralMint to avoid swap
|
||||
* @returns Transaction signature
|
||||
*/
|
||||
export async function openPerpTradeShort({
|
||||
agent,
|
||||
price,
|
||||
collateralAmount,
|
||||
collateralMint = TOKENS.USDC,
|
||||
leverage = DEFAULT_OPTIONS.LEVERAGE_BPS,
|
||||
tradeMint = TOKENS.jitoSOL,
|
||||
slippage = 0.3,
|
||||
}: {
|
||||
agent: SolanaAgentKit;
|
||||
price: number;
|
||||
collateralAmount: number;
|
||||
collateralMint?: PublicKey;
|
||||
leverage?: number;
|
||||
tradeMint?: PublicKey;
|
||||
slippage?: number;
|
||||
}): Promise<string> {
|
||||
const client = await AdrenaClient.load(agent);
|
||||
|
||||
const owner = agent.wallet.publicKey;
|
||||
|
||||
const collateralAccount = AdrenaClient.findATAAddressSync(owner, tradeMint);
|
||||
const fundingAccount = AdrenaClient.findATAAddressSync(owner, collateralMint);
|
||||
|
||||
const receivingCustody = AdrenaClient.findCustodyAddress(collateralMint);
|
||||
const receivingCustodyOracle = client.getCustodyByMint(collateralMint).oracle;
|
||||
const receivingCustodyTokenAccount =
|
||||
AdrenaClient.findCustodyTokenAccountAddress(collateralMint);
|
||||
|
||||
// Principal custody is the custody of the targeted token
|
||||
// i.e open a 1 BTC short position, principal custody is BTC
|
||||
const principalCustody = AdrenaClient.findCustodyAddress(tradeMint);
|
||||
const principalCustodyAccount = client.getCustodyByMint(tradeMint);
|
||||
const principalCustodyTradeOracle = principalCustodyAccount.tradeOracle;
|
||||
const principalCustodyTokenAccount =
|
||||
AdrenaClient.findCustodyTokenAccountAddress(tradeMint);
|
||||
|
||||
const usdcAta = AdrenaClient.findATAAddressSync(owner, TOKENS.USDC);
|
||||
|
||||
const preInstructions: TransactionInstruction[] = [];
|
||||
|
||||
if (!(await AdrenaClient.isAccountInitialized(agent.connection, usdcAta))) {
|
||||
preInstructions.push(
|
||||
AdrenaClient.createATAInstruction({
|
||||
ataAddress: usdcAta,
|
||||
mint: TOKENS.USDC,
|
||||
owner,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// Custody used to provide collateral when opening the position
|
||||
// Should be a stable token, by default, use USDC
|
||||
const instructionCollateralMint = TOKENS.USDC;
|
||||
|
||||
const collateralCustody = AdrenaClient.findCustodyAddress(
|
||||
instructionCollateralMint,
|
||||
);
|
||||
const collateralCustodyOracle = client.getCustodyByMint(
|
||||
instructionCollateralMint,
|
||||
).oracle;
|
||||
|
||||
const collateralCustodyTokenAccount =
|
||||
AdrenaClient.findCustodyTokenAccountAddress(instructionCollateralMint);
|
||||
|
||||
const stakingRewardTokenCustodyAccount = client.getCustodyByMint(
|
||||
AdrenaClient.stakingRewardTokenMint,
|
||||
);
|
||||
|
||||
const stakingRewardTokenCustodyTokenAccount =
|
||||
AdrenaClient.findCustodyTokenAccountAddress(
|
||||
AdrenaClient.stakingRewardTokenMint,
|
||||
);
|
||||
|
||||
const position = AdrenaClient.findPositionAddress(
|
||||
owner,
|
||||
principalCustody,
|
||||
"long",
|
||||
);
|
||||
|
||||
const userProfilePda = AdrenaClient.getUserProfilePda(owner);
|
||||
|
||||
const userProfile =
|
||||
await client.program.account.userProfile.fetchNullable(userProfilePda);
|
||||
|
||||
const priceWithSlippage = applySlippage(
|
||||
new BN(price * 10 ** PRICE_DECIMALS),
|
||||
slippage,
|
||||
);
|
||||
|
||||
const scaledCollateralAmount = new BN(
|
||||
collateralAmount *
|
||||
Math.pow(10, client.getCustodyByMint(collateralMint).decimals),
|
||||
);
|
||||
|
||||
const instruction = await client.program.methods
|
||||
.openOrIncreasePositionWithSwapShort({
|
||||
price: priceWithSlippage,
|
||||
collateral: scaledCollateralAmount,
|
||||
leverage,
|
||||
referrer: null,
|
||||
})
|
||||
.accountsStrict({
|
||||
owner,
|
||||
payer: owner,
|
||||
fundingAccount,
|
||||
collateralAccount,
|
||||
receivingCustody,
|
||||
receivingCustodyOracle,
|
||||
receivingCustodyTokenAccount,
|
||||
principalCustody,
|
||||
principalCustodyTradeOracle,
|
||||
principalCustodyTokenAccount,
|
||||
collateralCustody,
|
||||
collateralCustodyOracle,
|
||||
collateralCustodyTokenAccount,
|
||||
transferAuthority: AdrenaClient.transferAuthority,
|
||||
cortex: AdrenaClient.cortex,
|
||||
lmStaking: AdrenaClient.lmStaking,
|
||||
lpStaking: AdrenaClient.lpStaking,
|
||||
pool: AdrenaClient.mainPool,
|
||||
position,
|
||||
stakingRewardTokenCustody: stakingRewardTokenCustodyAccount.pubkey,
|
||||
stakingRewardTokenCustodyOracle: stakingRewardTokenCustodyAccount.oracle,
|
||||
stakingRewardTokenCustodyTokenAccount,
|
||||
lmStakingRewardTokenVault: AdrenaClient.lmStakingRewardTokenVault,
|
||||
lpStakingRewardTokenVault: AdrenaClient.lpStakingRewardTokenVault,
|
||||
lpTokenMint: AdrenaClient.lpTokenMint,
|
||||
userProfile: userProfile ? userProfilePda : null,
|
||||
protocolFeeRecipient: client.cortex.protocolFeeRecipient,
|
||||
systemProgram: SystemProgram.programId,
|
||||
tokenProgram: TOKEN_PROGRAM_ID,
|
||||
adrenaProgram: ADRENA_PROGRAM_ID,
|
||||
})
|
||||
.instruction();
|
||||
|
||||
return sendTx(agent, [...preInstructions, instruction]);
|
||||
}
|
||||
@@ -16,6 +16,7 @@ export async function getMainAllDomainsDomain(
|
||||
mainDomain = await _getFavoriteDomain(agent.connection, owner);
|
||||
return mainDomain.stale ? null : mainDomain.reverse;
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,8 @@ export async function getPrimaryDomain(
|
||||
);
|
||||
}
|
||||
return reverse;
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
throw new Error(
|
||||
`Failed to get primary domain for account: ${account.toBase58()}`,
|
||||
);
|
||||
|
||||
10
src/tools/get_wallet_address.ts
Normal file
10
src/tools/get_wallet_address.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { SolanaAgentKit } from "../agent";
|
||||
|
||||
/**
|
||||
* Get the agents wallet address
|
||||
* @param agent - SolanaAgentKit instance
|
||||
* @returns string
|
||||
*/
|
||||
export function get_wallet_address(agent: SolanaAgentKit) {
|
||||
return agent.wallet_address.toBase58();
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from "./adrena_perp_trading";
|
||||
export * from "./batch_order";
|
||||
export * from "./cancel_all_orders";
|
||||
export * from "./create_gibwork_task";
|
||||
@@ -16,6 +17,7 @@ export * from "./get_owned_domains_for_tld";
|
||||
export * from "./get_primary_domain";
|
||||
export * from "./get_token_data";
|
||||
export * from "./get_tps";
|
||||
export * from "./get_wallet_address";
|
||||
export * from "./launch_pumpfun_token";
|
||||
export * from "./lend";
|
||||
export * from "./limit_order";
|
||||
@@ -40,6 +42,7 @@ export * from "./rock_paper_scissor";
|
||||
export * from "./rugcheck";
|
||||
export * from "./send_compressed_airdrop";
|
||||
export * from "./stake_with_jup";
|
||||
export * from "./stake_with_solayer";
|
||||
export * from "./tensor_trade";
|
||||
export * from "./trade";
|
||||
export * from "./transfer";
|
||||
|
||||
@@ -24,7 +24,8 @@ export async function resolveSolDomain(
|
||||
|
||||
try {
|
||||
return await resolve(agent.connection, domain);
|
||||
} catch (error) {
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
throw new Error(`Failed to resolve domain: ${domain}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@ export async function sendCompressedAirdrop(
|
||||
agent.wallet.publicKey,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw new Error(
|
||||
"Source token account not found and failed to create it. Please add funds to your wallet and try again.",
|
||||
);
|
||||
|
||||
64
src/tools/stake_with_solayer.ts
Normal file
64
src/tools/stake_with_solayer.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { VersionedTransaction } from "@solana/web3.js";
|
||||
import { SolanaAgentKit } from "../index";
|
||||
|
||||
/**
|
||||
* Stake SOL with Solayer
|
||||
* @param agent SolanaAgentKit instance
|
||||
* @param amount Amount of SOL to stake
|
||||
* @returns Transaction signature
|
||||
*/
|
||||
export async function stakeWithSolayer(
|
||||
agent: SolanaAgentKit,
|
||||
amount: number,
|
||||
): Promise<string> {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://app.solayer.org/api/action/restake/ssol?amount=${amount}`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
account: agent.wallet.publicKey.toBase58(),
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.message || "Staking request failed");
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Deserialize and prepare transaction
|
||||
const txn = VersionedTransaction.deserialize(
|
||||
Buffer.from(data.transaction, "base64"),
|
||||
);
|
||||
|
||||
// Update blockhash
|
||||
const { blockhash } = await agent.connection.getLatestBlockhash();
|
||||
txn.message.recentBlockhash = blockhash;
|
||||
|
||||
// Sign and send transaction
|
||||
txn.sign([agent.wallet]);
|
||||
const signature = await agent.connection.sendTransaction(txn, {
|
||||
preflightCommitment: "confirmed",
|
||||
maxRetries: 3,
|
||||
});
|
||||
|
||||
// Wait for confirmation
|
||||
const latestBlockhash = await agent.connection.getLatestBlockhash();
|
||||
await agent.connection.confirmTransaction({
|
||||
signature,
|
||||
blockhash: latestBlockhash.blockhash,
|
||||
lastValidBlockHeight: latestBlockhash.lastValidBlockHeight,
|
||||
});
|
||||
|
||||
return signature;
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
throw new Error(`Solayer sSOL staking failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ export async function listNFTForSale(
|
||||
throw new Error(`You don't own this NFT (${nftMint.toString()})`);
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error(error);
|
||||
throw new Error(
|
||||
`No token account found for mint ${nftMint.toString()}. Make sure you own this NFT.`,
|
||||
);
|
||||
|
||||
220
src/utils/AdrenaClient.ts
Normal file
220
src/utils/AdrenaClient.ts
Normal file
@@ -0,0 +1,220 @@
|
||||
import { Connection, PublicKey } from "@solana/web3.js";
|
||||
import { SolanaAgentKit } from "../index";
|
||||
import { AnchorProvider, IdlAccounts, Program } from "@coral-xyz/anchor";
|
||||
import { Adrena, IDL as ADRENA_IDL } from "../idls/adrena";
|
||||
|
||||
import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet";
|
||||
import {
|
||||
ASSOCIATED_TOKEN_PROGRAM_ID,
|
||||
createAssociatedTokenAccountInstruction,
|
||||
TOKEN_PROGRAM_ID,
|
||||
} from "@solana/spl-token";
|
||||
import { TOKENS } from "../constants";
|
||||
|
||||
export type AdrenaProgram = Program<Adrena>;
|
||||
|
||||
type Accounts = IdlAccounts<Adrena>;
|
||||
|
||||
export type Cortex = Accounts["cortex"];
|
||||
export type Custody = Accounts["custody"] & { pubkey: PublicKey };
|
||||
export type Pool = Accounts["pool"];
|
||||
|
||||
export default class AdrenaClient {
|
||||
public static programId = new PublicKey(
|
||||
"13gDzEXCdocbj8iAiqrScGo47NiSuYENGsRqi3SEAwet",
|
||||
);
|
||||
|
||||
constructor(
|
||||
public program: AdrenaProgram,
|
||||
public mainPool: Pool,
|
||||
public cortex: Cortex,
|
||||
public custodies: Custody[],
|
||||
) {}
|
||||
|
||||
public static mainPool = new PublicKey(
|
||||
"4bQRutgDJs6vuh6ZcWaPVXiQaBzbHketjbCDjL4oRN34",
|
||||
);
|
||||
|
||||
public static async load(agent: SolanaAgentKit): Promise<AdrenaClient> {
|
||||
const program = new Program<Adrena>(
|
||||
ADRENA_IDL,
|
||||
AdrenaClient.programId,
|
||||
new AnchorProvider(agent.connection, new NodeWallet(agent.wallet), {
|
||||
commitment: "processed",
|
||||
skipPreflight: true,
|
||||
}),
|
||||
);
|
||||
|
||||
const [cortex, mainPool] = await Promise.all([
|
||||
program.account.cortex.fetch(AdrenaClient.cortex),
|
||||
program.account.pool.fetch(AdrenaClient.mainPool),
|
||||
]);
|
||||
|
||||
const custodiesAddresses = mainPool.custodies.filter(
|
||||
(custody) => !custody.equals(PublicKey.default),
|
||||
);
|
||||
|
||||
const custodies =
|
||||
await program.account.custody.fetchMultiple(custodiesAddresses);
|
||||
|
||||
if (!custodies.length || custodies.some((c) => c === null)) {
|
||||
throw new Error("Custodies not found");
|
||||
}
|
||||
|
||||
return new AdrenaClient(
|
||||
program,
|
||||
mainPool,
|
||||
cortex,
|
||||
(custodies as Custody[]).map((c, i) => ({
|
||||
...c,
|
||||
pubkey: custodiesAddresses[i],
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
public static findCustodyAddress(mint: PublicKey): PublicKey {
|
||||
return PublicKey.findProgramAddressSync(
|
||||
[
|
||||
Buffer.from("custody"),
|
||||
AdrenaClient.mainPool.toBuffer(),
|
||||
mint.toBuffer(),
|
||||
],
|
||||
AdrenaClient.programId,
|
||||
)[0];
|
||||
}
|
||||
|
||||
public static findCustodyTokenAccountAddress(mint: PublicKey) {
|
||||
return PublicKey.findProgramAddressSync(
|
||||
[
|
||||
Buffer.from("custody_token_account"),
|
||||
AdrenaClient.mainPool.toBuffer(),
|
||||
mint.toBuffer(),
|
||||
],
|
||||
AdrenaClient.programId,
|
||||
)[0];
|
||||
}
|
||||
|
||||
public static findPositionAddress(
|
||||
owner: PublicKey,
|
||||
custody: PublicKey,
|
||||
side: "long" | "short",
|
||||
) {
|
||||
return PublicKey.findProgramAddressSync(
|
||||
[
|
||||
Buffer.from("position"),
|
||||
owner.toBuffer(),
|
||||
AdrenaClient.mainPool.toBuffer(),
|
||||
custody.toBuffer(),
|
||||
Buffer.from([
|
||||
{
|
||||
long: 1,
|
||||
short: 2,
|
||||
}[side],
|
||||
]),
|
||||
],
|
||||
AdrenaClient.programId,
|
||||
)[0];
|
||||
}
|
||||
|
||||
public static cortex = PublicKey.findProgramAddressSync(
|
||||
[Buffer.from("cortex")],
|
||||
AdrenaClient.programId,
|
||||
)[0];
|
||||
|
||||
public static lpTokenMint = PublicKey.findProgramAddressSync(
|
||||
[Buffer.from("lp_token_mint"), AdrenaClient.mainPool.toBuffer()],
|
||||
AdrenaClient.programId,
|
||||
)[0];
|
||||
|
||||
public static lmTokenMint = PublicKey.findProgramAddressSync(
|
||||
[Buffer.from("lm_token_mint")],
|
||||
AdrenaClient.programId,
|
||||
)[0];
|
||||
|
||||
public static getStakingPda(stakedTokenMint: PublicKey) {
|
||||
return PublicKey.findProgramAddressSync(
|
||||
[Buffer.from("staking"), stakedTokenMint.toBuffer()],
|
||||
AdrenaClient.programId,
|
||||
)[0];
|
||||
}
|
||||
|
||||
public static lmStaking = AdrenaClient.getStakingPda(
|
||||
AdrenaClient.lmTokenMint,
|
||||
);
|
||||
|
||||
public static lpStaking = AdrenaClient.getStakingPda(
|
||||
AdrenaClient.lpTokenMint,
|
||||
);
|
||||
|
||||
public static transferAuthority = PublicKey.findProgramAddressSync(
|
||||
[Buffer.from("transfer_authority")],
|
||||
AdrenaClient.programId,
|
||||
)[0];
|
||||
|
||||
public static findATAAddressSync(
|
||||
wallet: PublicKey,
|
||||
mint: PublicKey,
|
||||
): PublicKey {
|
||||
return PublicKey.findProgramAddressSync(
|
||||
[wallet.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer()],
|
||||
ASSOCIATED_TOKEN_PROGRAM_ID,
|
||||
)[0];
|
||||
}
|
||||
|
||||
public getCustodyByMint(mint: PublicKey): Custody {
|
||||
const custody = this.custodies.find((custody) => custody.mint.equals(mint));
|
||||
|
||||
if (!custody) {
|
||||
throw new Error(`Cannot find custody for mint ${mint.toBase58()}`);
|
||||
}
|
||||
|
||||
return custody;
|
||||
}
|
||||
|
||||
public static getUserProfilePda(wallet: PublicKey) {
|
||||
return PublicKey.findProgramAddressSync(
|
||||
[Buffer.from("user_profile"), wallet.toBuffer()],
|
||||
AdrenaClient.programId,
|
||||
)[0];
|
||||
}
|
||||
|
||||
public static stakingRewardTokenMint = TOKENS.USDC;
|
||||
|
||||
public static getStakingRewardTokenVaultPda(stakingPda: PublicKey) {
|
||||
return PublicKey.findProgramAddressSync(
|
||||
[Buffer.from("staking_reward_token_vault"), stakingPda.toBuffer()],
|
||||
AdrenaClient.programId,
|
||||
)[0];
|
||||
}
|
||||
|
||||
public static lmStakingRewardTokenVault =
|
||||
AdrenaClient.getStakingRewardTokenVaultPda(AdrenaClient.lmStaking);
|
||||
public static lpStakingRewardTokenVault =
|
||||
AdrenaClient.getStakingRewardTokenVaultPda(AdrenaClient.lpStaking);
|
||||
|
||||
public static async isAccountInitialized(
|
||||
connection: Connection,
|
||||
address: PublicKey,
|
||||
): Promise<boolean> {
|
||||
return !!(await connection.getAccountInfo(address));
|
||||
}
|
||||
|
||||
public static createATAInstruction({
|
||||
ataAddress,
|
||||
mint,
|
||||
owner,
|
||||
payer = owner,
|
||||
}: {
|
||||
ataAddress: PublicKey;
|
||||
mint: PublicKey;
|
||||
owner: PublicKey;
|
||||
payer?: PublicKey;
|
||||
}) {
|
||||
return createAssociatedTokenAccountInstruction(
|
||||
payer,
|
||||
ataAddress,
|
||||
owner,
|
||||
mint,
|
||||
);
|
||||
}
|
||||
}
|
||||
25
src/vercel-ai/index.ts
Normal file
25
src/vercel-ai/index.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { tool, type CoreTool } from "ai";
|
||||
import { SolanaAgentKit } from "../agent";
|
||||
import { executeAction } from "../utils/actionExecutor";
|
||||
import { ACTIONS } from "../actions";
|
||||
|
||||
export function createSolanaTools(
|
||||
solanaAgentKit: SolanaAgentKit,
|
||||
): Record<string, CoreTool> {
|
||||
const tools: Record<string, CoreTool> = {};
|
||||
const actionKeys = Object.keys(ACTIONS);
|
||||
|
||||
for (const key of actionKeys) {
|
||||
const action = ACTIONS[key as keyof typeof ACTIONS];
|
||||
tools[key] = tool({
|
||||
// @ts-expect-error Value matches type however TS still shows error
|
||||
id: action.name,
|
||||
description: action.description,
|
||||
parameters: action.schema,
|
||||
execute: async (params) =>
|
||||
await executeAction(action, solanaAgentKit, params),
|
||||
});
|
||||
}
|
||||
|
||||
return tools;
|
||||
}
|
||||
Reference in New Issue
Block a user