This commit is contained in:
aryan
2024-11-17 21:39:36 +07:00
commit f0de3cf025
17 changed files with 3912 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@@ -0,0 +1,8 @@
dist
node_modules
.env
# VSCode
.vscode
.DS_Store

131
README.md Normal file
View File

@@ -0,0 +1,131 @@
# Solana Agent Kit
A powerful toolkit for interacting with the Solana blockchain, providing easy-to-use functions for token operations, NFT management, and trading.
## Features
- 🪙 Token Operations
- Deploy new SPL tokens
- Transfer SOL and SPL tokens
- Check token balances
- 🖼️ NFT Management
- Deploy NFT collections
- Mint NFTs to collections
- Manage metadata and royalties
- 💱 Trading
- Integrated Jupiter Exchange support
- Token swaps with customizable slippage
- Direct routing options
## Installation
```bash
npm install solana-agent-kit
```
## Quick Start
```typescript
import { SolanaAgentKit } from 'solana-agent-kit';
// Initialize with private key and optional RPC URL
const agent = new SolanaAgentKit(
'your-private-key',
'https://api.mainnet-beta.solana.com'
);
```
## Usage Examples
### Deploy a New Token
```typescript
import { deploy_token } from 'solana-agent-kit';
const result = await deploy_token(
agent,
9, // decimals
1000000 // initial supply
);
console.log('Token Mint Address:', result.mint.toString());
```
### Create NFT Collection
```typescript
import { deploy_collection } from 'solana-agent-kit';
const collection = await deploy_collection(agent, {
name: "My NFT Collection",
uri: "https://arweave.net/metadata.json",
royaltyBasisPoints: 500, // 5%
creators: [
{
address: "creator-wallet-address",
percentage: 100
}
]
});
```
### Swap Tokens
```typescript
import { trade } from 'solana-agent-kit';
import { PublicKey } from '@solana/web3.js';
const signature = await trade(
agent,
new PublicKey('target-token-mint'),
100, // amount
new PublicKey('source-token-mint'),
300 // 3% slippage
);
```
## API Reference
### Core Functions
#### `deploy_token(agent, decimals?, initialSupply?)`
Deploy a new SPL token with optional initial supply.
#### `deploy_collection(agent, options)`
Create a new NFT collection with customizable metadata and royalties.
#### `mintCollectionNFT(agent, collectionMint, metadata, recipient?)`
Mint a new NFT as part of an existing collection.
#### `transfer(agent, to, amount, mint?)`
Transfer SOL or SPL tokens to a recipient.
#### `trade(agent, outputMint, inputAmount, inputMint?, slippageBps?)`
Swap tokens using Jupiter Exchange integration.
#### `get_balance(agent, token_address)`
Check SOL or token balance for the agent's wallet.
## Dependencies
The toolkit relies on several key Solana and Metaplex libraries:
- @solana/web3.js
- @solana/spl-token
- @metaplex-foundation/js
- @metaplex-foundation/mpl-token-metadata
- @metaplex-foundation/umi
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
ISC License
## Security
This toolkit handles private keys and transactions. Always ensure you're using it in a secure environment and never share your private keys.

28
package.json Normal file
View File

@@ -0,0 +1,28 @@
{
"name": "solana-agent-kit",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@bonfida/spl-name-service": "^3.0.7",
"@metaplex-foundation/js": "^0.20.1",
"@metaplex-foundation/mpl-core": "^1.1.1",
"@metaplex-foundation/mpl-token-metadata": "^3.3.0",
"@metaplex-foundation/umi": "^0.9.2",
"@metaplex-foundation/umi-bundle-defaults": "^0.9.2",
"@metaplex-foundation/umi-web3js-adapters": "^0.9.2",
"@solana/spl-token": "^0.4.9",
"@solana/web3.js": "^1.95.4",
"bs58": "^6.0.0"
},
"devDependencies": {
"@types/node": "^22.9.0"
}
}

3164
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

18
src/constants/index.ts Normal file
View 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
View 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);
}
}

View 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
View 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
View 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
View 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
View 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}`);
}
}

View 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}`);
}
}

View 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
View 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
View 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
View 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;
}

35
tsconfig.json Normal file
View File

@@ -0,0 +1,35 @@
{
"compilerOptions": {
"target": "es2020",
"module": "commonjs",
"lib": ["es2020", "dom"],
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"useUnknownInCatchVariables": true,
"alwaysStrict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"exactOptionalPropertyTypes": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"resolveJsonModule": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.test.ts"]
}