Merge branch 'main' into readme-fix

This commit is contained in:
aryan
2024-12-23 11:30:54 +05:30
11 changed files with 223 additions and 11 deletions

View File

@@ -180,6 +180,62 @@ import { PublicKey } from "@solana/web3.js";
})();
```
### Fetch Price Data from Pyth
```typescript
import { pythFetchPrice } from "solana-agent-kit";
const price = await pythFetchPrice(
agent,
"0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43"
);
console.log("Price in BTC/USD:", price);
```
## API Reference
### Core Functions
#### `deploy_token(agent, decimals?, name, uri, symbol, initialSupply?)`
Deploy a new SPL token with optional initial supply. If not specified, decimals default to 9.
#### `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.
#### `lendAsset(agent, assetMint, amount, apiKey)`
Lend idle assets to earn interest with Lulo.
#### `stakeWithJup(agent, amount)`
Stake SOL with Jupiter to earn rewards.
#### `sendCompressedAirdrop(agent, mintAddress, amount, recipients, priorityFeeInLamports?, shouldLog?)`
Send an SPL token airdrop to many recipients at low cost via ZK Compression.
#### `pythFetchPrice(agent, priceFeedID)`
Fetch price data from Pyth's Hermes service.
## Dependencies
@@ -192,6 +248,7 @@ The toolkit relies on several key Solana and Metaplex libraries:
- @metaplex-foundation/umi
- @lightprotocol/compressed-token
- @lightprotocol/stateless.js
- @pythnetwork/price-service-client
## Contributing

View File

@@ -19,7 +19,7 @@ Create a new TypeScript file in the `src/tools/` directory for your tool (e.g.,
### 2. Implement the Tool Class
```typescript:src/tools/custom_tool.ts
```typescript:src/langchain/index.ts
import { Tool } from "langchain/tools";
import { SolanaAgentKit } from "../agent";
@@ -86,6 +86,8 @@ export function createSolanaTools(agent: SolanaAgentKit) {
### 6. Usage Example
Add a code example in the `README.md` file.
```typescript
import { SolanaAgentKit, createSolanaTools } from "solana-agent-kit";

View File

@@ -18,7 +18,6 @@
"author": "",
"license": "ISC",
"dependencies": {
"@raydium-io/raydium-sdk-v2": "0.1.95-alpha",
"@bonfida/spl-name-service": "^3.0.7",
"@coral-xyz/anchor": "0.29",
"@langchain/core": "^0.3.18",
@@ -29,16 +28,19 @@
"@lightprotocol/stateless.js": "^0.17.1",
"@metaplex-foundation/mpl-core": "^1.1.1",
"@metaplex-foundation/mpl-token-metadata": "^3.3.0",
"@metaplex-foundation/mpl-toolbox": "^0.9.4",
"@metaplex-foundation/umi": "^0.9.2",
"@metaplex-foundation/umi-bundle-defaults": "^0.9.2",
"@metaplex-foundation/umi-web3js-adapters": "^0.9.2",
"@orca-so/common-sdk": "0.6.4",
"@orca-so/whirlpools-sdk": "^0.13.12",
"@pythnetwork/price-service-client": "^1.9.0",
"@raydium-io/raydium-sdk-v2": "0.1.95-alpha",
"@solana/spl-token": "^0.4.9",
"@solana/web3.js": "^1.95.4",
"bn.js": "^5.2.1",
"bs58": "^6.0.0",
"decimal.js": "^10.4.3",
"bn.js": "^5.2.1",
"dotenv": "^16.4.5",
"form-data": "^4.0.1",
"langchain": "^0.3.6",
@@ -51,4 +53,4 @@
"ts-node": "^10.9.2",
"typescript": "^5.7.2"
}
}
}

54
pnpm-lock.yaml generated
View File

@@ -38,6 +38,9 @@ importers:
'@metaplex-foundation/mpl-token-metadata':
specifier: ^3.3.0
version: 3.3.0(@metaplex-foundation/umi@0.9.2)
'@metaplex-foundation/mpl-toolbox':
specifier: ^0.9.4
version: 0.9.4(@metaplex-foundation/umi@0.9.2)
'@metaplex-foundation/umi':
specifier: ^0.9.2
version: 0.9.2
@@ -53,6 +56,9 @@ importers:
'@orca-so/whirlpools-sdk':
specifier: ^0.13.12
version: 0.13.12(@coral-xyz/anchor@0.29.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(@orca-so/common-sdk@0.6.4(@solana/spl-token@0.4.9(@solana/web3.js@1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10))(@solana/web3.js@1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(decimal.js@10.4.3))(@solana/spl-token@0.4.9(@solana/web3.js@1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10))(@solana/web3.js@1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(decimal.js@10.4.3)
'@pythnetwork/price-service-client':
specifier: ^1.9.0
version: 1.9.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)
'@raydium-io/raydium-sdk-v2':
specifier: 0.1.95-alpha
version: 0.1.95-alpha(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10)
@@ -316,6 +322,13 @@ packages:
'@solana/web3.js': ^1.90.0
decimal.js: ^10.4.3
'@pythnetwork/price-service-client@1.9.0':
resolution: {integrity: sha512-SLm3IFcfmy9iMqHeT4Ih6qMNZhJEefY14T9yTlpsH2D/FE5+BaGGnfcexUifVlfH6M7mwRC4hEFdNvZ6ebZjJg==}
deprecated: This package is deprecated and is no longer maintained. Please use @pythnetwork/hermes-client instead.
'@pythnetwork/price-service-sdk@1.8.0':
resolution: {integrity: sha512-tFZ1thj3Zja06DzPIX2dEWSi7kIfIyqreoywvw5NQ3Z1pl5OJHQGMEhxt6Li3UCGSp2ooYZS9wl8/8XfrfrNSA==}
'@raydium-io/raydium-sdk-v2@0.1.95-alpha':
resolution: {integrity: sha512-+u7yxo/R1JDysTCzOuAlh90ioBe2DlM2Hbcz/tFsxP/YzmnYQzShvNjcmc0361a4zJhmlrEJfpFXW0J3kkX5vA==}
@@ -592,6 +605,9 @@ packages:
asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
axios-retry@3.9.1:
resolution: {integrity: sha512-8PJDLJv7qTTMMwdnbMvrLYuvB47M81wRtxQmEdV5w4rgbTXTt+vtPkXwajOfOdSyv/wZICJOC+/UhXH4aQ/R+w==}
axios@1.7.9:
resolution: {integrity: sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==}
@@ -826,6 +842,10 @@ packages:
resolution: {integrity: sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==}
engines: {node: '>= 10'}
is-retry-allowed@2.2.0:
resolution: {integrity: sha512-XVm7LOeLpTW4jV19QSH38vkswxoLud8sQ57YwJVTPWdiaI9I8keEhGFpBlslyVsgdQy4Opg8QOLb8YRgsyZiQg==}
engines: {node: '>=10'}
isomorphic-ws@4.0.1:
resolution: {integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==}
peerDependencies:
@@ -1126,6 +1146,9 @@ packages:
trim-lines@3.0.1:
resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
ts-log@2.2.7:
resolution: {integrity: sha512-320x5Ggei84AxzlXp91QkIGSw5wgaLT6GeAH0KsqDmRZdVWW2OiSeVvElVoatk3f7nicwXlElXsoFkARiGE2yg==}
ts-node@10.9.2:
resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==}
hasBin: true
@@ -1576,6 +1599,24 @@ snapshots:
decimal.js: 10.4.3
tiny-invariant: 1.3.3
'@pythnetwork/price-service-client@1.9.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)':
dependencies:
'@pythnetwork/price-service-sdk': 1.8.0
'@types/ws': 8.5.13
axios: 1.7.9
axios-retry: 3.9.1
isomorphic-ws: 4.0.1(ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))
ts-log: 2.2.7
ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)
transitivePeerDependencies:
- bufferutil
- debug
- utf-8-validate
'@pythnetwork/price-service-sdk@1.8.0':
dependencies:
bn.js: 5.2.1
'@raydium-io/raydium-sdk-v2@0.1.95-alpha(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)(utf-8-validate@5.0.10)':
dependencies:
'@solana/buffer-layout': 4.0.1
@@ -2016,6 +2057,11 @@ snapshots:
asynckit@0.4.0: {}
axios-retry@3.9.1:
dependencies:
'@babel/runtime': 7.26.0
is-retry-allowed: 2.2.0
axios@1.7.9:
dependencies:
follow-redirects: 1.15.9
@@ -2235,10 +2281,16 @@ snapshots:
ipaddr.js@2.2.0: {}
is-retry-allowed@2.2.0: {}
isomorphic-ws@4.0.1(ws@7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10)):
dependencies:
ws: 7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10)
isomorphic-ws@4.0.1(ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)):
dependencies:
ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)
jayson@4.1.3(bufferutil@4.0.8)(utf-8-validate@5.0.10):
dependencies:
'@types/connect': 3.4.38
@@ -2522,6 +2574,8 @@ snapshots:
trim-lines@3.0.1: {}
ts-log@2.2.7: {}
ts-node@10.9.2(@types/node@22.10.2)(typescript@5.7.2):
dependencies:
'@cspotcode/source-map-support': 0.8.1

View File

@@ -26,6 +26,7 @@ import {
sendCompressedAirdrop,
createOrcaSingleSidedWhirlpool,
FEE_TIERS,
pythFetchPrice,
} from "../tools";
import {
CollectionDeployment,
@@ -270,6 +271,10 @@ export class SolanaAgentKit {
lotSize,
tickSize,
);
)
}
async pythFetchPrice(priceFeedID: string) {
return pythFetchPrice(this, priceFeedID);
}
}

View File

@@ -1,7 +1,7 @@
import { PublicKey } from "@solana/web3.js";
import Decimal from "decimal.js";
import { Tool } from "langchain/tools";
import { SolanaAgentKit } from "../index";
import { PythFetchPriceResponse, SolanaAgentKit } from "../index";
import { create_image } from "../tools/create_image";
import { fetchPrice } from "../tools/fetch_price";
import { BN } from "@coral-xyz/anchor";
@@ -980,6 +980,38 @@ export class SolanaOpenbookCreateMarket extends Tool {
}
}
export class SolanaPythFetchPrice extends Tool {
name = "solana_pyth_fetch_price";
description = `Fetch the price of a given price feed from Pyth's Hermes service
Inputs:
priceFeedID: string, the price feed ID, e.g., "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43" for BTC/USD`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
async _call(input: string): Promise<string> {
try {
const price = await this.solanaKit.pythFetchPrice(input);
let response: PythFetchPriceResponse = {
status: "success",
priceFeedID: input,
price: price,
};
return JSON.stringify(response);
} catch (error: any) {
let response: PythFetchPriceResponse = {
status: "error",
priceFeedID: input,
message: error.message,
code: error.code || "UNKNOWN_ERROR",
};
return JSON.stringify(response);
}
}
}
export function createSolanaTools(solanaKit: SolanaAgentKit) {
return [
new SolanaBalanceTool(solanaKit),
@@ -1007,5 +1039,7 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) {
new SolanaRaydiumCreateCpmm(solanaKit),
new SolanaOpenbookCreateMarket(solanaKit),
new SolanaCreateSingleSidedWhirlpoolTool(solanaKit),
new SolanaPythFetchPrice(solanaKit),
];
}

View File

@@ -4,6 +4,7 @@ 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"
/**
* Deploy a new SPL token
@@ -25,7 +26,7 @@ export async function deploy_token(
): Promise<{ mint: PublicKey }> {
try {
// Create UMI instance from agent
const umi = createUmi(agent.connection.rpcEndpoint)
const umi = createUmi(agent.connection.rpcEndpoint).use(mplToolbox())
umi.use(keypairIdentity(fromWeb3JsKeypair(agent.wallet)));
// Create new token mint

View File

@@ -15,8 +15,9 @@ export * from "./get_token_data";
export * from "./stake_with_jup";
export * from "./fetch_price";
export * from "./send_compressed_airdrop";
export * from "./create_orca_single_sided_whirlpool";export * from "./raydium_create_ammV4";
export * from "./create_orca_single_sided_whirlpool";
export * from "./raydium_create_ammV4";
export * from "./raydium_create_clmm";
export * from "./raydium_create_cpmm";
export * from "./openbook_create_market";
export * from "./openbook_create_market";
export * from "./pyth_fetch_price";

View File

@@ -0,0 +1,48 @@
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";
/**
* Fetch the price of a given price feed from Pyth
* @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
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);
// convert to scaled price
let scaledPrice = price.div(new BN(10).pow(exponent));
return scaledPrice.toString();
} catch (error: any) {
throw new Error(`Fetching price from Pyth failed: ${error.message}`);
}
}

View File

@@ -77,3 +77,11 @@ export interface FetchPriceResponse {
message?: string;
code?: string;
}
export interface PythFetchPriceResponse {
status: "success" | "error";
priceFeedID: string;
price?: string;
message?: string;
code?: string;
}

View File

@@ -70,7 +70,7 @@ async function initializeAgent() {
faucet. If not, you can provide your wallet details and request funds from the user. If there is a 5XX
(internal) HTTP error code, ask the user to try again later. If someone asks you to do something you
can't do with your currently available tools, you must say so, and encourage them to implement it
themselves using the Solana Agent Kit, recommend they go to https://sendai.fun/kit for more information. Be
themselves using the Solana Agent Kit, recommend they go to https://www.solanaagentkit.xyz for more information. Be
concise and helpful with your responses. Refrain from restating your tools' descriptions unless it is
explicitly requested.
`,