mirror of
https://github.com/d0zingcat/solana-agent-kit.git
synced 2026-05-21 23:26:45 +00:00
init
This commit is contained in:
18
src/constants/index.ts
Normal file
18
src/constants/index.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
|
||||
/**
|
||||
* Common token addresses used across the toolkit
|
||||
*/
|
||||
export const TOKENS = {
|
||||
USDC: new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"),
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
export const DEFAULT_OPTIONS = {
|
||||
SLIPPAGE_BPS: 300,
|
||||
TOKEN_DECIMALS: 9,
|
||||
} as const;
|
||||
78
src/index.ts
Normal file
78
src/index.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { Connection, Keypair, PublicKey } from "@solana/web3.js";
|
||||
import bs58 from "bs58";
|
||||
import {
|
||||
request_faucet_funds,
|
||||
deploy_token,
|
||||
deploy_collection,
|
||||
get_balance,
|
||||
mintCollectionNFT,
|
||||
transfer,
|
||||
trade,
|
||||
} from "./tools";
|
||||
import { CollectionOptions } from "./types";
|
||||
import { DEFAULT_OPTIONS } from "./constants";
|
||||
|
||||
/**
|
||||
* Main class for interacting with Solana blockchain
|
||||
* Provides a unified interface for token operations, NFT management, and trading
|
||||
*
|
||||
* @class SolanaAgentKit
|
||||
* @property {Connection} connection - Solana RPC connection
|
||||
* @property {Keypair} wallet - Wallet keypair for signing transactions
|
||||
* @property {PublicKey} wallet_address - Public key of the wallet
|
||||
*/
|
||||
export class SolanaAgentKit {
|
||||
public connection: Connection;
|
||||
public wallet: Keypair;
|
||||
public wallet_address: PublicKey;
|
||||
|
||||
constructor(
|
||||
private_key: string,
|
||||
rpc_url = "https://api.mainnet-beta.solana.com"
|
||||
) {
|
||||
this.connection = new Connection(rpc_url);
|
||||
this.wallet = Keypair.fromSecretKey(bs58.decode(private_key));
|
||||
this.wallet_address = this.wallet.publicKey;
|
||||
}
|
||||
|
||||
// Tool methods
|
||||
async requestFaucetFunds() {
|
||||
return request_faucet_funds(this);
|
||||
}
|
||||
|
||||
async deployToken(
|
||||
decimals: number = DEFAULT_OPTIONS.TOKEN_DECIMALS,
|
||||
initialSupply?: number
|
||||
) {
|
||||
return deploy_token(this, decimals, initialSupply);
|
||||
}
|
||||
|
||||
async deployCollection(options: CollectionOptions) {
|
||||
return deploy_collection(this, options);
|
||||
}
|
||||
|
||||
async getBalance(token_address?: PublicKey) {
|
||||
return get_balance(this, token_address);
|
||||
}
|
||||
|
||||
async mintNFT(
|
||||
collectionMint: PublicKey,
|
||||
metadata: Parameters<typeof mintCollectionNFT>[2],
|
||||
recipient?: PublicKey
|
||||
) {
|
||||
return mintCollectionNFT(this, collectionMint, metadata, recipient);
|
||||
}
|
||||
|
||||
async transfer(to: PublicKey, amount: number, mint?: PublicKey) {
|
||||
return transfer(this, to, amount, mint);
|
||||
}
|
||||
|
||||
async trade(
|
||||
outputMint: PublicKey,
|
||||
inputAmount: number,
|
||||
inputMint?: PublicKey,
|
||||
slippageBps: number = DEFAULT_OPTIONS.SLIPPAGE_BPS
|
||||
) {
|
||||
return trade(this, outputMint, inputAmount, inputMint, slippageBps);
|
||||
}
|
||||
}
|
||||
60
src/tools/deploy_collection.ts
Normal file
60
src/tools/deploy_collection.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { SolanaAgentKit } from "../index";
|
||||
import {
|
||||
createUmi,
|
||||
generateSigner,
|
||||
publicKey,
|
||||
} from '@metaplex-foundation/umi';
|
||||
import { createCollection, ruleSet } from '@metaplex-foundation/mpl-core';
|
||||
import { mplTokenMetadata } from '@metaplex-foundation/mpl-token-metadata';
|
||||
import { CollectionOptions, CollectionDeployment } from '../types';
|
||||
import { toWeb3JsPublicKey } from '@metaplex-foundation/umi-web3js-adapters';
|
||||
/**
|
||||
* Deploy a new NFT collection
|
||||
* @param agent SolanaAgentKit instance
|
||||
* @param options Collection options including name, URI, royalties, and creators
|
||||
* @returns Object containing collection address and metadata
|
||||
*/
|
||||
export async function deploy_collection(
|
||||
agent: SolanaAgentKit,
|
||||
options: CollectionOptions
|
||||
): Promise<CollectionDeployment> {
|
||||
try {
|
||||
// Initialize Umi
|
||||
const umi = createUmi()
|
||||
.use(mplTokenMetadata());
|
||||
|
||||
// Generate collection signer
|
||||
const collectionSigner = generateSigner(umi);
|
||||
|
||||
// Format creators if provided
|
||||
const formattedCreators = options.creators?.map(creator => ({
|
||||
address: publicKey(creator.address),
|
||||
percentage: creator.percentage,
|
||||
})) || [{
|
||||
address: publicKey(agent.wallet_address.toString()),
|
||||
percentage: 100,
|
||||
}];
|
||||
|
||||
// Create collection
|
||||
const tx = await createCollection(umi, {
|
||||
collection: collectionSigner,
|
||||
name: options.name,
|
||||
uri: options.uri,
|
||||
plugins: [
|
||||
{
|
||||
type: 'Royalties',
|
||||
basisPoints: options.royaltyBasisPoints || 500, // Default 5%
|
||||
creators: formattedCreators,
|
||||
ruleSet: ruleSet('None'), // Compatibility rule set
|
||||
},
|
||||
],
|
||||
}).sendAndConfirm(umi);
|
||||
|
||||
return {
|
||||
collectionAddress: toWeb3JsPublicKey(collectionSigner.publicKey),
|
||||
signature: tx.signature
|
||||
};
|
||||
} catch (error: any) {
|
||||
throw new Error(`Collection deployment failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
64
src/tools/deploy_token.ts
Normal file
64
src/tools/deploy_token.ts
Normal file
@@ -0,0 +1,64 @@
|
||||
import { SolanaAgentKit } from "../index";
|
||||
import {
|
||||
createMint,
|
||||
getOrCreateAssociatedTokenAccount,
|
||||
mintTo,
|
||||
TOKEN_PROGRAM_ID,
|
||||
Account
|
||||
} from "@solana/spl-token";
|
||||
|
||||
/**
|
||||
* Deploy a new SPL token
|
||||
* @param agent SolanaAgentKit instance
|
||||
* @param decimals Number of decimals for the token (default: 9)
|
||||
* @param initialSupply Initial supply to mint (optional)
|
||||
* @returns Object containing token mint address and initial account (if supply was minted)
|
||||
*/
|
||||
export async function deploy_token(
|
||||
agent: SolanaAgentKit,
|
||||
decimals: number = 9,
|
||||
initialSupply?: number
|
||||
) {
|
||||
try {
|
||||
// Create new token mint
|
||||
const mint = await createMint(
|
||||
agent.connection,
|
||||
agent.wallet, // Payer
|
||||
agent.wallet_address, // Mint authority
|
||||
agent.wallet_address, // Freeze authority (optional)
|
||||
decimals,
|
||||
undefined, // Optional keypair
|
||||
undefined, // Confirmation options
|
||||
TOKEN_PROGRAM_ID
|
||||
);
|
||||
|
||||
// If initial supply is specified, mint tokens
|
||||
let tokenAccount: Account | undefined = undefined;
|
||||
if (initialSupply) {
|
||||
// Create associated token account for the wallet
|
||||
tokenAccount = await getOrCreateAssociatedTokenAccount(
|
||||
agent.connection,
|
||||
agent.wallet,
|
||||
mint,
|
||||
agent.wallet_address
|
||||
);
|
||||
|
||||
// Mint the initial supply
|
||||
await mintTo(
|
||||
agent.connection,
|
||||
agent.wallet,
|
||||
mint,
|
||||
tokenAccount.address,
|
||||
agent.wallet_address,
|
||||
initialSupply * Math.pow(10, decimals)
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
mint: mint,
|
||||
tokenAccount: tokenAccount?.address
|
||||
};
|
||||
} catch (error: any) {
|
||||
throw new Error(`Token deployment failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
19
src/tools/get_balance.ts
Normal file
19
src/tools/get_balance.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { SolanaAgentKit } from "../index";
|
||||
|
||||
/**
|
||||
* Get the balance of SOL or an SPL token for the agent's wallet
|
||||
* @param agent - SolanaAgentKit instance
|
||||
* @param token_address - Optional SPL token mint address. If not provided, returns SOL balance
|
||||
* @returns Promise resolving to the balance as a number (in UI units) or null if account doesn't exist
|
||||
*/
|
||||
export async function get_balance(
|
||||
agent: SolanaAgentKit,
|
||||
token_address?: PublicKey
|
||||
) {
|
||||
if (!token_address)
|
||||
return await agent.connection.getBalance(agent.wallet_address);
|
||||
|
||||
const token_account = await agent.connection.getTokenAccountBalance(token_address);
|
||||
return token_account.value.uiAmount;
|
||||
}
|
||||
8
src/tools/index.ts
Normal file
8
src/tools/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export * from './request_faucet_funds';
|
||||
export * from './deploy_token';
|
||||
export * from './deploy_collection';
|
||||
export * from './get_balance';
|
||||
export * from './mint_nft';
|
||||
export * from './transfer';
|
||||
export * from './trade';
|
||||
export * from './register_domain';
|
||||
63
src/tools/mint_nft.ts
Normal file
63
src/tools/mint_nft.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { SolanaAgentKit } from "../index";
|
||||
import { generateSigner } from '@metaplex-foundation/umi';
|
||||
import { create } from '@metaplex-foundation/mpl-core';
|
||||
import { fetchCollection } from '@metaplex-foundation/mpl-core';
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { 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
|
||||
* @param agent SolanaAgentKit instance
|
||||
* @param collectionMint Address of the collection's master NFT
|
||||
* @param metadata NFT metadata object
|
||||
* @param recipient Optional recipient address (defaults to wallet address)
|
||||
* @returns Object containing NFT mint address and token account
|
||||
*/
|
||||
export async function mintCollectionNFT(
|
||||
agent: SolanaAgentKit,
|
||||
collectionMint: PublicKey,
|
||||
metadata: {
|
||||
name: string;
|
||||
symbol: string;
|
||||
uri: string;
|
||||
sellerFeeBasisPoints?: number;
|
||||
creators?: Array<{
|
||||
address: string;
|
||||
share: number;
|
||||
}>;
|
||||
},
|
||||
recipient?: PublicKey
|
||||
): Promise<MintCollectionNFTResponse> {
|
||||
try {
|
||||
// Create UMI instance from agent
|
||||
const umi = createUmi(agent.connection)
|
||||
|
||||
// Convert collection mint to UMI format
|
||||
const umiCollectionMint = fromWeb3JsPublicKey(collectionMint);
|
||||
|
||||
// Fetch the existing collection
|
||||
const collection = await fetchCollection(umi, umiCollectionMint);
|
||||
|
||||
// Generate a new signer for the NFT
|
||||
const assetSigner = generateSigner(umi);
|
||||
|
||||
// Create the NFT in the collection
|
||||
await create(umi, {
|
||||
asset: assetSigner,
|
||||
collection: collection,
|
||||
name: metadata.name,
|
||||
uri: metadata.uri,
|
||||
owner: fromWeb3JsPublicKey(recipient!)
|
||||
}).sendAndConfirm(umi);
|
||||
|
||||
return {
|
||||
mint: toWeb3JsPublicKey(assetSigner.publicKey),
|
||||
// Note: Token account is now handled automatically by the create instruction
|
||||
metadata: toWeb3JsPublicKey(assetSigner.publicKey)
|
||||
};
|
||||
} catch (error: any) {
|
||||
throw new Error(`Collection NFT minting failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
57
src/tools/register_domain.ts
Normal file
57
src/tools/register_domain.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { registerDomainNameV2 } from "@bonfida/spl-name-service";
|
||||
import { Transaction } from "@solana/web3.js";
|
||||
import { SolanaAgentKit } from "../index";
|
||||
import { getAssociatedTokenAddressSync } from "@solana/spl-token";
|
||||
import { TOKENS } from "../constants";
|
||||
/**
|
||||
* Register a .sol domain name using Bonfida Name Service
|
||||
* @param agent SolanaAgentKit instance
|
||||
* @param name Domain name to register (without .sol)
|
||||
* @param spaceKB Space allocation in KB (max 10KB)
|
||||
* @returns Transaction signature
|
||||
*/
|
||||
export async function registerDomain(
|
||||
agent: SolanaAgentKit,
|
||||
name: string,
|
||||
spaceKB: number = 1
|
||||
): Promise<string> {
|
||||
try {
|
||||
// Validate space size
|
||||
if (spaceKB > 10) {
|
||||
throw new Error("Maximum domain size is 10KB");
|
||||
}
|
||||
|
||||
// Convert KB to bytes
|
||||
const space = spaceKB * 1_000;
|
||||
|
||||
const buyerTokenAccount = await getAssociatedTokenAddressSync(
|
||||
agent.wallet_address,
|
||||
TOKENS.USDC
|
||||
);
|
||||
|
||||
// Create registration instruction
|
||||
const instruction = await registerDomainNameV2(
|
||||
agent.connection,
|
||||
name,
|
||||
space,
|
||||
agent.wallet_address,
|
||||
buyerTokenAccount
|
||||
);
|
||||
|
||||
// Create and sign transaction
|
||||
const transaction = new Transaction().add(...instruction);
|
||||
transaction.recentBlockhash = (
|
||||
await agent.connection.getLatestBlockhash()
|
||||
).blockhash;
|
||||
transaction.feePayer = agent.wallet_address;
|
||||
|
||||
// Sign and send transaction
|
||||
const signature = await agent.connection.sendTransaction(transaction, [
|
||||
agent.wallet,
|
||||
]);
|
||||
|
||||
return signature;
|
||||
} catch (error: any) {
|
||||
throw new Error(`Domain registration failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
16
src/tools/request_faucet_funds.ts
Normal file
16
src/tools/request_faucet_funds.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { SolanaAgentKit } from "../index";
|
||||
import { LAMPORTS_PER_SOL } from "@solana/web3.js";
|
||||
|
||||
/**
|
||||
* Request SOL from the Solana faucet (devnet/testnet only)
|
||||
* @param agent - SolanaAgentKit instance
|
||||
* @returns Promise that resolves when the airdrop is confirmed
|
||||
* @throws Error if the request fails or times out
|
||||
*/
|
||||
export async function request_faucet_funds(agent: SolanaAgentKit) {
|
||||
const tx = await agent.connection.requestAirdrop(
|
||||
agent.wallet_address,
|
||||
5 * LAMPORTS_PER_SOL
|
||||
);
|
||||
await agent.connection.confirmTransaction(tx);
|
||||
}
|
||||
66
src/tools/trade.ts
Normal file
66
src/tools/trade.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import {
|
||||
VersionedTransaction,
|
||||
PublicKey,
|
||||
} from "@solana/web3.js";
|
||||
import { SolanaAgentKit } from "../index";
|
||||
import { TOKENS, DEFAULT_OPTIONS } from "../constants";
|
||||
/**
|
||||
* Swap tokens using Jupiter Exchange
|
||||
* @param agent SolanaAgentKit instance
|
||||
* @param outputMint Target token mint address
|
||||
* @param inputAmount Amount to swap (in token decimals)
|
||||
* @param inputMint Source token mint address (defaults to USDC)
|
||||
* @param slippageBps Slippage tolerance in basis points (default: 300 = 3%)
|
||||
* @returns Transaction signature
|
||||
*/
|
||||
export async function trade(
|
||||
agent: SolanaAgentKit,
|
||||
outputMint: PublicKey,
|
||||
inputAmount: number,
|
||||
inputMint: PublicKey = TOKENS.USDC,
|
||||
slippageBps: number = DEFAULT_OPTIONS.SLIPPAGE_BPS
|
||||
): Promise<string> {
|
||||
try {
|
||||
// Get quote for the swap
|
||||
const quoteResponse = await (
|
||||
await fetch(
|
||||
`https://quote-api.jup.ag/v6/quote?` +
|
||||
`inputMint=${inputMint.toString()}` +
|
||||
`&outputMint=${outputMint.toString()}` +
|
||||
`&amount=${inputAmount}` +
|
||||
`&slippageBps=${slippageBps}` +
|
||||
`&onlyDirectRoutes=true` +
|
||||
`&maxAccounts=20`
|
||||
)
|
||||
).json();
|
||||
|
||||
// Get serialized transaction
|
||||
const { swapTransaction } = await (
|
||||
await fetch("https://quote-api.jup.ag/v6/swap", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
quoteResponse,
|
||||
userPublicKey: agent.wallet_address.toString(),
|
||||
wrapAndUnwrapSol: true,
|
||||
dynamicComputeUnitLimit: true,
|
||||
prioritizationFeeLamports: "auto",
|
||||
}),
|
||||
})
|
||||
).json();
|
||||
|
||||
// Deserialize transaction
|
||||
const swapTransactionBuf = Buffer.from(swapTransaction, "base64");
|
||||
const transaction = VersionedTransaction.deserialize(swapTransactionBuf);
|
||||
|
||||
// Sign and send transaction
|
||||
transaction.sign([agent.wallet]);
|
||||
const signature = await agent.connection.sendTransaction(transaction);
|
||||
|
||||
return signature;
|
||||
} catch (error: any) {
|
||||
throw new Error(`Swap failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
73
src/tools/transfer.ts
Normal file
73
src/tools/transfer.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { SolanaAgentKit } from "../index";
|
||||
import {
|
||||
PublicKey,
|
||||
SystemProgram,
|
||||
Transaction
|
||||
} from "@solana/web3.js";
|
||||
import { LAMPORTS_PER_SOL } from "@solana/web3.js";
|
||||
import {
|
||||
getAssociatedTokenAddress,
|
||||
createTransferInstruction,
|
||||
getMint
|
||||
} from "@solana/spl-token";
|
||||
|
||||
/**
|
||||
* Transfer SOL or SPL tokens to a recipient
|
||||
* @param agent SolanaAgentKit instance
|
||||
* @param to Recipient's public key
|
||||
* @param amount Amount to transfer
|
||||
* @param mint Optional mint address for SPL tokens
|
||||
* @returns Transaction signature
|
||||
*/
|
||||
export async function transfer(
|
||||
agent: SolanaAgentKit,
|
||||
to: PublicKey,
|
||||
amount: number,
|
||||
mint?: PublicKey
|
||||
): Promise<string> {
|
||||
try {
|
||||
let tx: string;
|
||||
|
||||
if (!mint) {
|
||||
// Transfer native SOL
|
||||
const transaction = new Transaction().add(
|
||||
SystemProgram.transfer({
|
||||
fromPubkey: agent.wallet_address,
|
||||
toPubkey: to,
|
||||
lamports: amount * LAMPORTS_PER_SOL
|
||||
})
|
||||
);
|
||||
|
||||
tx = await agent.connection.sendTransaction(
|
||||
transaction,
|
||||
[agent.wallet]
|
||||
);
|
||||
} else {
|
||||
// Transfer SPL token
|
||||
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);
|
||||
|
||||
const transaction = new Transaction().add(
|
||||
createTransferInstruction(
|
||||
fromAta,
|
||||
toAta,
|
||||
agent.wallet_address,
|
||||
adjustedAmount
|
||||
)
|
||||
);
|
||||
|
||||
tx = await agent.connection.sendTransaction(
|
||||
transaction,
|
||||
[agent.wallet]
|
||||
);
|
||||
}
|
||||
|
||||
return tx;
|
||||
} catch (error: any) {
|
||||
throw new Error(`Transfer failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
24
src/types/index.ts
Normal file
24
src/types/index.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
|
||||
export interface Creator {
|
||||
address: string;
|
||||
percentage: number;
|
||||
}
|
||||
|
||||
export interface CollectionOptions {
|
||||
name: string;
|
||||
uri: string;
|
||||
royaltyBasisPoints?: number;
|
||||
creators?: Creator[];
|
||||
}
|
||||
|
||||
// Add return type interface
|
||||
export interface CollectionDeployment {
|
||||
collectionAddress: PublicKey;
|
||||
signature: Uint8Array;
|
||||
}
|
||||
|
||||
export interface MintCollectionNFTResponse {
|
||||
mint: PublicKey;
|
||||
metadata: PublicKey;
|
||||
}
|
||||
Reference in New Issue
Block a user