mirror of
https://github.com/d0zingcat/solana-agent-kit.git
synced 2026-06-03 07:36:45 +00:00
Merge pull request #38 from arihantbansal/rename-and-fixes
feat: fix tools, and lint
This commit is contained in:
48
README.md
48
README.md
@@ -183,60 +183,14 @@ import { PublicKey } from "@solana/web3.js";
|
||||
### Fetch Price Data from Pyth
|
||||
|
||||
```typescript
|
||||
import { pythFetchPrice } from "solana-agent-kit";
|
||||
|
||||
const price = await pythFetchPrice(
|
||||
agent,
|
||||
const price = await agent.pythFetchPrice(
|
||||
"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
|
||||
|
||||
The toolkit relies on several key Solana and Metaplex libraries:
|
||||
|
||||
@@ -1 +1 @@
|
||||
window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAE4XTQUvDMBTA8e+Sc7E4dEhvw9KDzm1ob+IhxNc1LH0vJC+gyL67bBNdXfZ26SX//l4T0tcvxfDBqlIv5DTq2RqQHy2rQnnNvaqUcTpGiOV4/arnwalCbSy+q+p6crctfqV7cg4MW8IavKPPAfDIs8gQOm0glrlwDE9up1l46XfPeEH9qUQygGYKeeiwJr3eAJt+FayBZ4ieMEJWOs0k9CF5yxBa2gDWmnWW/B9J4Dw5mhlDCbkG1tZF8WvP59KQJ4v8d/SLphVnnK2lEas0+C7hXCc0vchny0t0k3B/nNLlynQSa3ZXCA6/TkvkjtAu4X7vsTyJxuL0Zvv2DYU9ByOnAwAA"
|
||||
window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAE42TwU7DMAyG3yXniooJJtTbRNUDDJigN8QhCi6JltpR40hMaO+OtiFYWebtkos/f7asP69fiuGTVaVeyGvUsw9AvnesChU0W1Up43WMEMtx/cJy71Whlg7fVXU5uVkXv6Zb8h4MO8IagqdVD7jnc8gwdNpALHPgWDy5nmbFT2HzxhPWH0pUDqCZhrxoV5PaG2BjF4Mz8AwxEEbImg4xSXqXgmMYWloC1pp1VvkfkoTz5GlmDCXkGlg7H8Vtj+PSkAeH/Hf6x6YVZxylpRGL1Icu4VwnNFbUZ8lT6ibh9pxSuDKcqF2xPTMjeVSSm00+YfcvWyK/t3GXcHvYWB5AY+P0av32DUTvPWMEBAAA"
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
117
docs/index.html
117
docs/index.html
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
6
docs/interfaces/PythFetchPriceResponse.html
Normal file
6
docs/interfaces/PythFetchPriceResponse.html
Normal file
File diff suppressed because one or more lines are too long
154
docs/media/CONTRIBUTING.md
Normal file
154
docs/media/CONTRIBUTING.md
Normal file
@@ -0,0 +1,154 @@
|
||||
# Contributing to Solana Agent Kit
|
||||
|
||||
First off, thank you for considering contributing to Solana Agent Kit! 🎉 Your contributions are **greatly appreciated**.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Contributing to Solana Agent Kit](#contributing-to-solana-agent-kit)
|
||||
- [Table of Contents](#table-of-contents)
|
||||
- [Code of Conduct](#code-of-conduct)
|
||||
- [How Can I Contribute?](#how-can-i-contribute)
|
||||
- [Reporting Bugs](#reporting-bugs)
|
||||
- [Suggesting Enhancements](#suggesting-enhancements)
|
||||
- [Your First Code Contribution](#your-first-code-contribution)
|
||||
- [Pull Requests](#pull-requests)
|
||||
- [Style Guides](#style-guides)
|
||||
- [Code Style](#code-style)
|
||||
- [Commit Messages](#commit-messages)
|
||||
- [Naming Conventions](#naming-conventions)
|
||||
- [Development Setup](#development-setup)
|
||||
- [Prerequisites](#prerequisites)
|
||||
- [Installation](#installation)
|
||||
- [Building the Project](#building-the-project)
|
||||
- [Running Tests](#running-tests)
|
||||
- [Generating Documentation](#generating-documentation)
|
||||
- [Security](#security)
|
||||
- [License](#license)
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
This project adheres to the [Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/version/2/0/code_of_conduct/). By participating, you are expected to uphold this code. Please report unacceptable behavior to [aryan@sendai.fun](mailto:aryan@sendai.fun).
|
||||
|
||||
## How Can I Contribute?
|
||||
|
||||
### Reporting Bugs
|
||||
|
||||
**Great**! Opening an issue is the best way to help us improve. Here's how you can report a bug:
|
||||
|
||||
1. **Search** the [existing issues](https://github.com/sendaifun/solana-agent-kit/issues) to make sure it hasn't been reported.
|
||||
2. **Open a new issue** and fill out the template with as much information as possible.
|
||||
3. **Provide reproduction steps** if applicable.
|
||||
|
||||
### Suggesting Enhancements
|
||||
|
||||
We welcome your ideas for improving Solana Agent Kit! To suggest an enhancement:
|
||||
|
||||
1. **Search** the [existing issues](https://github.com/sendaifun/solana-agent-kit/issues) to see if it's already been suggested.
|
||||
2. **Open a new issue** and describe your idea in detail.
|
||||
|
||||
### Your First Code Contribution
|
||||
|
||||
Unsure where to start? You can help out by:
|
||||
|
||||
- Fixing simple bugs.
|
||||
- Improving documentation.
|
||||
- Adding tests.
|
||||
|
||||
Check out the [Good First Issues](https://github.com/sendaifun/solana-agent-kit/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) to get started!
|
||||
|
||||
### Pull Requests
|
||||
|
||||
1. **Fork** the repository.
|
||||
2. **Create** a new branch for your feature or bugfix.
|
||||
```bash
|
||||
git checkout -b feature/your-feature-name
|
||||
```
|
||||
3. **Commit** your changes with clear and descriptive messages.
|
||||
4. **Push** to your fork.
|
||||
```bash
|
||||
git push origin feature/your-feature-name
|
||||
```
|
||||
5. **Open a Pull Request** against the `main` branch of this repository.
|
||||
|
||||
## Style Guides
|
||||
|
||||
### Code Style
|
||||
|
||||
- **Language**: TypeScript
|
||||
- **Formatting**: Follow the existing codebase formatting. Consider using [Prettier](https://prettier.io/) for consistent code formatting.
|
||||
- **Code Quality**: Adhere to the code quality rules defined in `.eslintrc`. Ensure all checks pass before submitting a PR.
|
||||
|
||||
### Commit Messages
|
||||
|
||||
Use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) for your commit messages. Examples:
|
||||
|
||||
- `feat: add ability to deploy new SPL token`
|
||||
- `fix: handle edge case when deploying collection`
|
||||
- `docs: update README with new usage examples`
|
||||
|
||||
### Naming Conventions
|
||||
|
||||
- **Variables and Functions**: `camelCase`
|
||||
- **Classes and Types**: `PascalCase`
|
||||
- **Constants**: `UPPER_SNAKE_CASE`
|
||||
|
||||
## Development Setup
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- **Node.js**: v23.x or higher
|
||||
- **npm**: v10.x or higher
|
||||
- **Git**: Installed and configured
|
||||
|
||||
### Installation
|
||||
|
||||
1. **Clone** the repository:
|
||||
```bash
|
||||
git clone https://github.com/yourusername/solana-agent-kit.git
|
||||
```
|
||||
2. **Navigate** to the project directory:
|
||||
```bash
|
||||
cd solana-agent-kit
|
||||
```
|
||||
3. **Install** dependencies:
|
||||
```bash
|
||||
pnpm install
|
||||
```
|
||||
|
||||
### Building the Project
|
||||
|
||||
To compile the TypeScript code:
|
||||
|
||||
```bash
|
||||
pnpm run build
|
||||
```
|
||||
|
||||
### Running Tests
|
||||
|
||||
To execute the test suite:
|
||||
|
||||
```bash
|
||||
pnpm run test
|
||||
```
|
||||
|
||||
### Generating Documentation
|
||||
|
||||
To generate the project documentation using TypeDoc:
|
||||
|
||||
```bash
|
||||
npm run docs
|
||||
```
|
||||
|
||||
The documentation will be available in the `docs/` directory.
|
||||
|
||||
## Security
|
||||
|
||||
This toolkit handles sensitive information such as private keys and API keys. **Ensure you never commit `.env` files or any sensitive data**. Review the `.gitignore` to confirm that sensitive files are excluded.
|
||||
|
||||
For security vulnerabilities, please follow the [responsible disclosure](mailto:aryan@sendai.fun) process.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the [ISC License](LICENSE).
|
||||
|
||||
---
|
||||
@@ -8,5 +8,6 @@
|
||||
<a href="interfaces/MintCollectionNFTResponse.html" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24"><use href="assets/icons.svg#icon-256"></use></svg><span>Mint<wbr/>CollectionNFTResponse</span></a>
|
||||
<a href="interfaces/PumpfunLaunchResponse.html" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24"><use href="assets/icons.svg#icon-256"></use></svg><span>Pumpfun<wbr/>Launch<wbr/>Response</span></a>
|
||||
<a href="interfaces/PumpFunTokenOptions.html" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24"><use href="assets/icons.svg#icon-256"></use></svg><span>Pump<wbr/>Fun<wbr/>Token<wbr/>Options</span></a>
|
||||
<a href="interfaces/PythFetchPriceResponse.html" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24"><use href="assets/icons.svg#icon-256"></use></svg><span>Pyth<wbr/>Fetch<wbr/>Price<wbr/>Response</span></a>
|
||||
</div></section><section class="tsd-index-section"><h3 class="tsd-index-heading">Functions</h3><div class="tsd-index-list"><a href="functions/createSolanaTools.html" class="tsd-index-link"><svg class="tsd-kind-icon" viewBox="0 0 24 24"><use href="assets/icons.svg#icon-64"></use></svg><span>create<wbr/>Solana<wbr/>Tools</span></a>
|
||||
</div></section></section></section></div><div class="col-sidebar"><div class="page-menu"><div class="tsd-navigation settings"><details class="tsd-accordion"><summary class="tsd-accordion-summary"><h3><svg width="20" height="20" viewBox="0 0 24 24" fill="none"><use href="assets/icons.svg#icon-chevronDown"></use></svg>Settings</h3></summary><div class="tsd-accordion-details"><div class="tsd-filter-visibility"><span class="settings-label">Member Visibility</span><ul id="tsd-filter-options"><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-protected" name="protected"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Protected</span></label></li><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-inherited" name="inherited" checked/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>Inherited</span></label></li><li class="tsd-filter-item"><label class="tsd-filter-input"><input type="checkbox" id="tsd-filter-external" name="external"/><svg width="32" height="32" viewBox="0 0 32 32" aria-hidden="true"><rect class="tsd-checkbox-background" width="30" height="30" x="1" y="1" rx="6" fill="none"></rect><path class="tsd-checkbox-checkmark" d="M8.35422 16.8214L13.2143 21.75L24.6458 10.25" stroke="none" stroke-width="3.5" stroke-linejoin="round" fill="none"></path></svg><span>External</span></label></li></ul></div><div class="tsd-theme-toggle"><label class="settings-label" for="tsd-theme">Theme</label><select id="tsd-theme"><option value="os">OS</option><option value="light">Light</option><option value="dark">Dark</option></select></div></div></details></div></div><div class="site-menu"><nav class="tsd-navigation"><a href="modules.html" class="current"><svg class="tsd-kind-icon" viewBox="0 0 24 24"><use href="assets/icons.svg#icon-1"></use></svg><span>solana-agent-kit</span></a><ul class="tsd-small-nested-navigation" id="tsd-nav-container" data-base="."><li>Loading...</li></ul></nav></div></div></div><footer><p class="tsd-generator">Generated using <a href="https://typedoc.org/" target="_blank">TypeDoc</a></p></footer><div class="overlay"></div></body></html>
|
||||
|
||||
@@ -178,4 +178,4 @@ If you encounter any issues while implementing your custom tool:
|
||||
- Contact the maintainer
|
||||
- Check existing tools for implementation examples
|
||||
|
||||
---
|
||||
---
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
"build": "tsc",
|
||||
"docs": "typedoc src --out docs",
|
||||
"test": "ts-node test/index.ts",
|
||||
"test:domain": "ts-node test/domain_methods.test.ts",
|
||||
"generate": "ts-node src/utils/keypair.ts"
|
||||
},
|
||||
"engines": {
|
||||
@@ -16,7 +15,7 @@
|
||||
"pnpm": ">=8.0.0"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"author": "sendaifun",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@bonfida/spl-name-service": "^3.0.7",
|
||||
|
||||
@@ -25,8 +25,9 @@ import {
|
||||
stakeWithJup,
|
||||
sendCompressedAirdrop,
|
||||
createOrcaSingleSidedWhirlpool,
|
||||
FEE_TIERS,
|
||||
fetchPrice,
|
||||
pythFetchPrice,
|
||||
FEE_TIERS,
|
||||
getAllDomainsTLDs,
|
||||
getAllRegisteredAllDomains,
|
||||
getOwnedDomainsForTLD,
|
||||
@@ -47,7 +48,7 @@ import { NameAccountAndDomain } from "@onsol/tldparser";
|
||||
|
||||
/**
|
||||
* Main class for interacting with Solana blockchain
|
||||
* Provides a unified interface for token operations, NFT management, and trading
|
||||
* Provides a unified interface for token operations, NFT management, trading and more
|
||||
*
|
||||
* @class SolanaAgentKit
|
||||
* @property {Connection} connection - Solana RPC connection
|
||||
@@ -153,6 +154,10 @@ export class SolanaAgentKit {
|
||||
return getTokenDataByTicker(ticker);
|
||||
}
|
||||
|
||||
async fetchTokenPrice(mint: string) {
|
||||
return fetchPrice(new PublicKey(mint));
|
||||
}
|
||||
|
||||
async launchPumpFunToken(
|
||||
tokenName: string,
|
||||
tokenTicker: string,
|
||||
@@ -307,10 +312,10 @@ export class SolanaAgentKit {
|
||||
|
||||
lotSize,
|
||||
tickSize,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
async pythFetchPrice(priceFeedID: string) {
|
||||
return pythFetchPrice(this, priceFeedID);
|
||||
async pythFetchPrice(priceFeedID: string): Promise<string> {
|
||||
return pythFetchPrice(priceFeedID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { SolanaAgentKit } from './agent'; // Move the SolanaAgentKit class to src/agent.ts
|
||||
import { createSolanaTools } from './langchain';
|
||||
import { SolanaAgentKit } from "./agent";
|
||||
import { createSolanaTools } from "./langchain";
|
||||
|
||||
export { SolanaAgentKit, createSolanaTools };
|
||||
|
||||
// Optional: Export types that users might need
|
||||
export * from './types';
|
||||
export * from "./types";
|
||||
|
||||
@@ -3,7 +3,6 @@ import Decimal from "decimal.js";
|
||||
import { Tool } from "langchain/tools";
|
||||
import { PythFetchPriceResponse, SolanaAgentKit } from "../index";
|
||||
import { create_image } from "../tools/create_image";
|
||||
import { fetchPrice } from "../tools/fetch_price";
|
||||
import { BN } from "@coral-xyz/anchor";
|
||||
import { FEE_TIERS } from "../tools";
|
||||
import { toJSON } from "../utils/toJSON";
|
||||
@@ -67,7 +66,7 @@ export class SolanaTransferTool extends Tool {
|
||||
const tx = await this.solanaKit.transfer(
|
||||
recipient,
|
||||
parsedInput.amount,
|
||||
mintAddress
|
||||
mintAddress,
|
||||
);
|
||||
|
||||
return JSON.stringify({
|
||||
@@ -94,7 +93,7 @@ export class SolanaDeployTokenTool extends Tool {
|
||||
|
||||
Inputs (input is a JSON string):
|
||||
name: string, eg "My Token" (required)
|
||||
uri: string, eg "https://example.com/token.json" (required)
|
||||
uri: string, eg "https://example.com/token.json" (required)
|
||||
symbol: string, eg "MTK" (required)
|
||||
decimals?: number, eg 9 (optional, defaults to 9)
|
||||
initialSupply?: number, eg 1000000 (optional)`;
|
||||
@@ -112,7 +111,7 @@ export class SolanaDeployTokenTool extends Tool {
|
||||
parsedInput.uri,
|
||||
parsedInput.symbol,
|
||||
parsedInput.decimals,
|
||||
parsedInput.initialSupply
|
||||
parsedInput.initialSupply,
|
||||
);
|
||||
|
||||
return JSON.stringify({
|
||||
@@ -192,7 +191,7 @@ export class SolanaMintNFTTool extends Tool {
|
||||
},
|
||||
parsedInput.recipient
|
||||
? new PublicKey(parsedInput.recipient)
|
||||
: this.solanaKit.wallet_address
|
||||
: this.solanaKit.wallet_address,
|
||||
);
|
||||
|
||||
return JSON.stringify({
|
||||
@@ -240,7 +239,7 @@ export class SolanaTradeTool extends Tool {
|
||||
parsedInput.inputMint
|
||||
? new PublicKey(parsedInput.inputMint)
|
||||
: new PublicKey("So11111111111111111111111111111111111111112"),
|
||||
parsedInput.slippageBps
|
||||
parsedInput.slippageBps,
|
||||
);
|
||||
|
||||
return JSON.stringify({
|
||||
@@ -320,7 +319,7 @@ export class SolanaRegisterDomainTool extends Tool {
|
||||
|
||||
const tx = await this.solanaKit.registerDomain(
|
||||
parsedInput.name,
|
||||
parsedInput.spaceKB || 1
|
||||
parsedInput.spaceKB || 1,
|
||||
);
|
||||
|
||||
return JSON.stringify({
|
||||
@@ -477,7 +476,7 @@ export class SolanaPumpfunTokenLaunchTool extends Tool {
|
||||
telegram: parsedInput.telegram,
|
||||
website: parsedInput.website,
|
||||
initialLiquiditySOL: parsedInput.initialLiquiditySOL,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
return JSON.stringify({
|
||||
@@ -621,7 +620,7 @@ export class SolanaStakeTool extends Tool {
|
||||
export class SolanaFetchPriceTool extends Tool {
|
||||
name = "solana_fetch_price";
|
||||
description = `Fetch the price of a given token in USDC.
|
||||
|
||||
|
||||
Inputs:
|
||||
- tokenId: string, the mint address of the token, e.g., "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN"`;
|
||||
|
||||
@@ -631,7 +630,7 @@ export class SolanaFetchPriceTool extends Tool {
|
||||
|
||||
async _call(input: string): Promise<string> {
|
||||
try {
|
||||
const price = await fetchPrice(this.solanaKit, input.trim());
|
||||
const price = await this.solanaKit.fetchTokenPrice(input.trim());
|
||||
return JSON.stringify({
|
||||
status: "success",
|
||||
tokenId: input.trim(),
|
||||
@@ -710,7 +709,7 @@ export class SolanaTokenDataByTickerTool extends Tool {
|
||||
export class SolanaCompressedAirdropTool extends Tool {
|
||||
name = "solana_compressed_airdrop";
|
||||
description = `Airdrop SPL tokens with ZK Compression (also called as airdropping tokens)
|
||||
|
||||
|
||||
Inputs (input is a JSON string):
|
||||
mintAddress: string, the mint address of the token, e.g., "JUPyiwrYJFskUPiHa7hkeR8VUtAeFoSYbKedZNsDvCN" (required)
|
||||
amount: number, the amount of tokens to airdrop per recipient, e.g., 42 (required)
|
||||
@@ -733,7 +732,7 @@ export class SolanaCompressedAirdropTool extends Tool {
|
||||
parsedInput.decimals,
|
||||
parsedInput.recipients,
|
||||
parsedInput.priorityFeeInLamports || 30_000,
|
||||
parsedInput.shouldLog || false
|
||||
parsedInput.shouldLog || false,
|
||||
);
|
||||
|
||||
return JSON.stringify({
|
||||
@@ -897,7 +896,7 @@ export class SolanaRaydiumCreateClmm extends Tool {
|
||||
|
||||
export class SolanaRaydiumCreateCpmm extends Tool {
|
||||
name = "raydium_create_cpmm";
|
||||
description = `Raydium's newest CPMM, does not require marketID, supports Token 2022 standard
|
||||
description = `Raydium's newest CPMM, does not require marketID, supports Token 2022 standard
|
||||
|
||||
Inputs (input is a json string):
|
||||
mint1: string (required)
|
||||
@@ -945,7 +944,7 @@ export class SolanaRaydiumCreateCpmm extends Tool {
|
||||
|
||||
export class SolanaOpenbookCreateMarket extends Tool {
|
||||
name = "solana_openbook_create_market";
|
||||
description = `Openbook marketId, required for ammv4
|
||||
description = `Openbook marketId, required for ammv4
|
||||
|
||||
Inputs (input is a json string):
|
||||
baseMint: string (required)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Keypair, PublicKey, Transaction } from "@solana/web3.js";
|
||||
import { SolanaAgentKit } from "../agent";
|
||||
import { SolanaAgentKit } from "../index";
|
||||
import { BN, Wallet } from "@coral-xyz/anchor";
|
||||
import { Decimal } from "decimal.js";
|
||||
import {
|
||||
@@ -40,7 +40,7 @@ import { sendTx } from "../utils/send_tx";
|
||||
* @remarks
|
||||
* Fee tiers determine the percentage of fees collected on swaps, while tick spacing affects
|
||||
* the granularity of price ranges for liquidity positions.
|
||||
*
|
||||
*
|
||||
* For more details, refer to:
|
||||
* - [Whirlpool Fees](https://orca-so.github.io/whirlpools/Architecture%20Overview/Whirlpool%20Fees)
|
||||
* - [Whirlpool Parameters](https://orca-so.github.io/whirlpools/Architecture%20Overview/Whirlpool%20Parameters)
|
||||
@@ -54,17 +54,17 @@ export const FEE_TIERS = {
|
||||
0.04: 4,
|
||||
0.05: 8,
|
||||
0.16: 16,
|
||||
0.30: 64,
|
||||
0.3: 64,
|
||||
0.65: 96,
|
||||
1.00: 128,
|
||||
2.00: 256,
|
||||
1.0: 128,
|
||||
2.0: 256,
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* # Creates a single-sided Whirlpool.
|
||||
*
|
||||
* This function initializes a new Whirlpool (liquidity pool) on Orca and seeds it with liquidity from a single token.
|
||||
*
|
||||
*
|
||||
* ## Example Usage:
|
||||
* You created a new token called SHARK, and you want to set the initial price to 0.001 USDC.
|
||||
* You set `depositTokenMint` to SHARK's mint address and `otherTokenMint` to USDC's mint address.
|
||||
@@ -72,7 +72,7 @@ export const FEE_TIERS = {
|
||||
* 1. Increase the amount of tokens you deposit
|
||||
* 2. Set the initial price very low
|
||||
* 3. Set the maximum price closer to the initial price
|
||||
*
|
||||
*
|
||||
* ### Note for experts:
|
||||
* The Wrhirlpool program initializes the Whirlpool with the in a specific order. This might not be
|
||||
* the order you expect, so the function checks the order and adjusts the inverts the prices. This means that
|
||||
@@ -86,13 +86,13 @@ export const FEE_TIERS = {
|
||||
* @param initialPrice - The initial price of the deposit token in terms of the other token.
|
||||
* @param maxPrice - The maximum price at which liquidity is added.
|
||||
* @param feeTier - The fee tier percentage for the pool, determining tick spacing and fee collection rates.
|
||||
*
|
||||
*
|
||||
* @returns A promise that resolves to a transaction ID (`string`) of the transaction creating the pool.
|
||||
*
|
||||
*
|
||||
* @throws Will throw an error if:
|
||||
* - Mint accounts for the tokens cannot be fetched.
|
||||
* - Prices are out of bounds.
|
||||
*
|
||||
*
|
||||
* @remarks
|
||||
* This function is designed for single-sided deposits where users only contribute one type of token,
|
||||
* and the function manages mint order and necessary calculations.
|
||||
@@ -103,7 +103,7 @@ export const FEE_TIERS = {
|
||||
* import { PublicKey } from "@solana/web3.js";
|
||||
* import { BN } from "@coral-xyz/anchor";
|
||||
* import Decimal from "decimal.js";
|
||||
*
|
||||
*
|
||||
* const agent = new SolanaAgentKit(wallet, connection);
|
||||
* const depositAmount = new BN(1_000_000_000_000); // 1 million SHARK if SHARK has 6 decimals
|
||||
* const depositTokenMint = new PublicKey("DEPOSTI_TOKEN_ADDRESS");
|
||||
@@ -111,7 +111,7 @@ export const FEE_TIERS = {
|
||||
* const initialPrice = new Decimal(0.001);
|
||||
* const maxPrice = new Decimal(5.0);
|
||||
* const feeTier = 0.30;
|
||||
*
|
||||
*
|
||||
* const txId = await createOrcaSingleSidedWhirlpool(
|
||||
* agent,
|
||||
* depositAmount,
|
||||
@@ -134,13 +134,19 @@ export async function createOrcaSingleSidedWhirlpool(
|
||||
feeTier: keyof typeof FEE_TIERS,
|
||||
): Promise<string> {
|
||||
const wallet = new Wallet(agent.wallet);
|
||||
const ctx = WhirlpoolContext.from(agent.connection, wallet, ORCA_WHIRLPOOL_PROGRAM_ID);
|
||||
const ctx = WhirlpoolContext.from(
|
||||
agent.connection,
|
||||
wallet,
|
||||
ORCA_WHIRLPOOL_PROGRAM_ID,
|
||||
);
|
||||
const fetcher = ctx.fetcher;
|
||||
|
||||
const correctTokenOrder = PoolUtil.orderMints(otherTokenMint, depositTokenMint).map(
|
||||
(addr) => addr.toString(),
|
||||
);
|
||||
const isCorrectMintOrder = correctTokenOrder[0] === depositTokenMint.toString();
|
||||
const correctTokenOrder = PoolUtil.orderMints(
|
||||
otherTokenMint,
|
||||
depositTokenMint,
|
||||
).map((addr) => addr.toString());
|
||||
const isCorrectMintOrder =
|
||||
correctTokenOrder[0] === depositTokenMint.toString();
|
||||
let mintA, mintB;
|
||||
if (isCorrectMintOrder) {
|
||||
[mintA, mintB] = [depositTokenMint, otherTokenMint];
|
||||
@@ -151,10 +157,18 @@ export async function createOrcaSingleSidedWhirlpool(
|
||||
}
|
||||
const mintAAccount = await fetcher.getMintInfo(mintA);
|
||||
const mintBAccount = await fetcher.getMintInfo(mintB);
|
||||
if (mintAAccount === null || mintBAccount === null) throw Error('Mint account not found');
|
||||
if (mintAAccount === null || mintBAccount === null)
|
||||
throw Error("Mint account not found");
|
||||
const tickSpacing = FEE_TIERS[feeTier];
|
||||
const tickIndex = PriceMath.priceToTickIndex(initialPrice, mintAAccount.decimals, mintBAccount.decimals);
|
||||
const initialTick = TickUtil.getInitializableTickIndex(tickIndex, tickSpacing);
|
||||
const tickIndex = PriceMath.priceToTickIndex(
|
||||
initialPrice,
|
||||
mintAAccount.decimals,
|
||||
mintBAccount.decimals,
|
||||
);
|
||||
const initialTick = TickUtil.getInitializableTickIndex(
|
||||
tickIndex,
|
||||
tickSpacing,
|
||||
);
|
||||
|
||||
const tokenExtensionCtx: TokenExtensionContextForPool = {
|
||||
...NO_TOKEN_EXTENSION_CONTEXT,
|
||||
@@ -196,17 +210,17 @@ export async function createOrcaSingleSidedWhirlpool(
|
||||
tokenVaultBKeypair,
|
||||
feeTierKey,
|
||||
tickSpacing: tickSpacing,
|
||||
funder: wallet.publicKey
|
||||
funder: wallet.publicKey,
|
||||
};
|
||||
const initPoolIx = !TokenExtensionUtil.isV2IxRequiredPool(tokenExtensionCtx)
|
||||
? WhirlpoolIx.initializePoolIx(ctx.program, baseParamsPool)
|
||||
: WhirlpoolIx.initializePoolV2Ix(ctx.program, {
|
||||
...baseParamsPool,
|
||||
tokenProgramA: tokenExtensionCtx.tokenMintWithProgramA.tokenProgram,
|
||||
tokenProgramB: tokenExtensionCtx.tokenMintWithProgramB.tokenProgram,
|
||||
tokenBadgeA,
|
||||
tokenBadgeB,
|
||||
});
|
||||
...baseParamsPool,
|
||||
tokenProgramA: tokenExtensionCtx.tokenMintWithProgramA.tokenProgram,
|
||||
tokenProgramB: tokenExtensionCtx.tokenMintWithProgramB.tokenProgram,
|
||||
tokenBadgeA,
|
||||
tokenBadgeB,
|
||||
});
|
||||
const initialTickArrayStartTick = TickUtil.getStartTickIndex(
|
||||
initialTick,
|
||||
tickSpacing,
|
||||
@@ -235,14 +249,32 @@ export async function createOrcaSingleSidedWhirlpool(
|
||||
let tickLowerIndex, tickUpperIndex;
|
||||
if (isCorrectMintOrder) {
|
||||
tickLowerIndex = initialTick;
|
||||
tickUpperIndex = PriceMath.priceToTickIndex(maxPrice, mintAAccount.decimals, mintBAccount.decimals);
|
||||
tickUpperIndex = PriceMath.priceToTickIndex(
|
||||
maxPrice,
|
||||
mintAAccount.decimals,
|
||||
mintBAccount.decimals,
|
||||
);
|
||||
} else {
|
||||
tickLowerIndex = PriceMath.priceToTickIndex(maxPrice, mintAAccount.decimals, mintBAccount.decimals);
|
||||
tickLowerIndex = PriceMath.priceToTickIndex(
|
||||
maxPrice,
|
||||
mintAAccount.decimals,
|
||||
mintBAccount.decimals,
|
||||
);
|
||||
tickUpperIndex = initialTick;
|
||||
}
|
||||
const tickLowerInitializableIndex = TickUtil.getInitializableTickIndex(tickLowerIndex, tickSpacing);
|
||||
const tickUpperInitializableIndex = TickUtil.getInitializableTickIndex(tickUpperIndex, tickSpacing);
|
||||
if (!TickUtil.checkTickInBounds(tickLowerInitializableIndex) || !TickUtil.checkTickInBounds(tickUpperInitializableIndex)) throw Error('Prices out of bounds');
|
||||
const tickLowerInitializableIndex = TickUtil.getInitializableTickIndex(
|
||||
tickLowerIndex,
|
||||
tickSpacing,
|
||||
);
|
||||
const tickUpperInitializableIndex = TickUtil.getInitializableTickIndex(
|
||||
tickUpperIndex,
|
||||
tickSpacing,
|
||||
);
|
||||
if (
|
||||
!TickUtil.checkTickInBounds(tickLowerInitializableIndex) ||
|
||||
!TickUtil.checkTickInBounds(tickUpperInitializableIndex)
|
||||
)
|
||||
throw Error("Prices out of bounds");
|
||||
const increasLiquidityQuoteParam: IncreaseLiquidityQuoteParam = {
|
||||
inputTokenAmount: new BN(depositTokenAmount),
|
||||
inputTokenMint: depositTokenMint,
|
||||
@@ -253,11 +285,11 @@ export async function createOrcaSingleSidedWhirlpool(
|
||||
tickLowerIndex: tickLowerInitializableIndex,
|
||||
tickUpperIndex: tickUpperInitializableIndex,
|
||||
tokenExtensionCtx: tokenExtensionCtx,
|
||||
slippageTolerance: Percentage.fromFraction(0, 100)
|
||||
}
|
||||
slippageTolerance: Percentage.fromFraction(0, 100),
|
||||
};
|
||||
const liquidityInput = increaseLiquidityQuoteByInputTokenWithParams(
|
||||
increasLiquidityQuoteParam
|
||||
)
|
||||
increasLiquidityQuoteParam,
|
||||
);
|
||||
const { liquidityAmount: liquidity, tokenMaxA, tokenMaxB } = liquidityInput;
|
||||
|
||||
const positionMintKeypair = Keypair.generate();
|
||||
@@ -285,7 +317,7 @@ export async function createOrcaSingleSidedWhirlpool(
|
||||
...params,
|
||||
positionMint: positionMintPubkey,
|
||||
withTokenMetadataExtension: true,
|
||||
})
|
||||
});
|
||||
|
||||
txBuilder.addInstruction(positionIx);
|
||||
txBuilder.addSigner(positionMintKeypair);
|
||||
@@ -365,35 +397,33 @@ export async function createOrcaSingleSidedWhirlpool(
|
||||
tickArrayUpper: tickArrayUpperPda.publicKey,
|
||||
};
|
||||
|
||||
const liquidityIx = !TokenExtensionUtil.isV2IxRequiredPool(
|
||||
tokenExtensionCtx,
|
||||
)
|
||||
const liquidityIx = !TokenExtensionUtil.isV2IxRequiredPool(tokenExtensionCtx)
|
||||
? increaseLiquidityIx(ctx.program, baseParamsLiquidity)
|
||||
: increaseLiquidityV2Ix(ctx.program, {
|
||||
...baseParamsLiquidity,
|
||||
tokenMintA: mintA,
|
||||
tokenMintB: mintB,
|
||||
tokenProgramA: tokenExtensionCtx.tokenMintWithProgramA.tokenProgram,
|
||||
tokenProgramB: tokenExtensionCtx.tokenMintWithProgramB.tokenProgram,
|
||||
});
|
||||
...baseParamsLiquidity,
|
||||
tokenMintA: mintA,
|
||||
tokenMintB: mintB,
|
||||
tokenProgramA: tokenExtensionCtx.tokenMintWithProgramA.tokenProgram,
|
||||
tokenProgramB: tokenExtensionCtx.tokenMintWithProgramB.tokenProgram,
|
||||
});
|
||||
txBuilder.addInstruction(liquidityIx);
|
||||
|
||||
const txPayload = await txBuilder.build({
|
||||
maxSupportedTransactionVersion: "legacy"
|
||||
maxSupportedTransactionVersion: "legacy",
|
||||
});
|
||||
|
||||
if (txPayload.transaction instanceof Transaction) {
|
||||
try {
|
||||
const txId = await sendTx(
|
||||
agent,
|
||||
txPayload.transaction,
|
||||
[positionMintKeypair, tokenVaultAKeypair, tokenVaultBKeypair],
|
||||
);
|
||||
const txId = await sendTx(agent, txPayload.transaction, [
|
||||
positionMintKeypair,
|
||||
tokenVaultAKeypair,
|
||||
tokenVaultBKeypair,
|
||||
]);
|
||||
return txId;
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to create pool: ${JSON.stringify(error)}`);
|
||||
throw new Error(`Failed to create pool: ${JSON.stringify(error)}`);
|
||||
}
|
||||
} else {
|
||||
throw new Error('Failed to create pool: Transaction not created');
|
||||
throw new Error("Failed to create pool: Transaction not created");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,19 @@
|
||||
import { SolanaAgentKit } from "../index";
|
||||
import { generateSigner, keypairIdentity, publicKey } from "@metaplex-foundation/umi";
|
||||
import { createCollection, mplCore, ruleSet } from "@metaplex-foundation/mpl-core";
|
||||
import {
|
||||
generateSigner,
|
||||
keypairIdentity,
|
||||
publicKey,
|
||||
} from "@metaplex-foundation/umi";
|
||||
import {
|
||||
createCollection,
|
||||
mplCore,
|
||||
ruleSet,
|
||||
} from "@metaplex-foundation/mpl-core";
|
||||
import { CollectionOptions, CollectionDeployment } from "../types";
|
||||
import { fromWeb3JsKeypair, toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters";
|
||||
import {
|
||||
fromWeb3JsKeypair,
|
||||
toWeb3JsPublicKey,
|
||||
} from "@metaplex-foundation/umi-web3js-adapters";
|
||||
import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";
|
||||
|
||||
/**
|
||||
@@ -28,11 +39,11 @@ export async function deploy_collection(
|
||||
address: publicKey(creator.address),
|
||||
percentage: creator.percentage,
|
||||
})) || [
|
||||
{
|
||||
address: publicKey(agent.wallet_address.toString()),
|
||||
percentage: 100,
|
||||
},
|
||||
];
|
||||
{
|
||||
address: publicKey(agent.wallet_address.toString()),
|
||||
percentage: 100,
|
||||
},
|
||||
];
|
||||
|
||||
// Create collection
|
||||
const tx = await createCollection(umi, {
|
||||
|
||||
@@ -2,9 +2,17 @@ import { SolanaAgentKit } from "../index";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";
|
||||
import { generateSigner, keypairIdentity } from "@metaplex-foundation/umi";
|
||||
import { createFungible, mintV1, TokenStandard } from "@metaplex-foundation/mpl-token-metadata";
|
||||
import { fromWeb3JsKeypair, fromWeb3JsPublicKey, toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters";
|
||||
import {mplToolbox} from "@metaplex-foundation/mpl-toolbox"
|
||||
import {
|
||||
createFungible,
|
||||
mintV1,
|
||||
TokenStandard,
|
||||
} from "@metaplex-foundation/mpl-token-metadata";
|
||||
import {
|
||||
fromWeb3JsKeypair,
|
||||
fromWeb3JsPublicKey,
|
||||
toWeb3JsPublicKey,
|
||||
} from "@metaplex-foundation/umi-web3js-adapters";
|
||||
import { mplToolbox } from "@metaplex-foundation/mpl-toolbox";
|
||||
|
||||
/**
|
||||
* Deploy a new SPL token
|
||||
@@ -22,11 +30,11 @@ export async function deploy_token(
|
||||
uri: string,
|
||||
symbol: string,
|
||||
decimals: number = 9,
|
||||
initialSupply?: number
|
||||
initialSupply?: number,
|
||||
): Promise<{ mint: PublicKey }> {
|
||||
try {
|
||||
// Create UMI instance from agent
|
||||
const umi = createUmi(agent.connection.rpcEndpoint).use(mplToolbox())
|
||||
const umi = createUmi(agent.connection.rpcEndpoint).use(mplToolbox());
|
||||
umi.use(keypairIdentity(fromWeb3JsKeypair(agent.wallet)));
|
||||
|
||||
// Create new token mint
|
||||
@@ -52,11 +60,11 @@ export async function deploy_token(
|
||||
tokenStandard: TokenStandard.Fungible,
|
||||
tokenOwner: fromWeb3JsPublicKey(agent.wallet_address),
|
||||
amount: initialSupply,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
builder.sendAndConfirm(umi, { confirm: { commitment: 'finalized' } });
|
||||
builder.sendAndConfirm(umi, { confirm: { commitment: "finalized" } });
|
||||
|
||||
return {
|
||||
mint: toWeb3JsPublicKey(mint.publicKey),
|
||||
|
||||
@@ -1,20 +1,13 @@
|
||||
import { SolanaAgentKit } from "../index";
|
||||
import { Tool } from "langchain/tools";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
|
||||
/**
|
||||
* Fetch the price of a given token in USDC using Jupiter API
|
||||
* @param agent SolanaAgentKit instance
|
||||
* Fetch the price of a given token quoted in USDC using Jupiter API
|
||||
* @param tokenId The token mint address
|
||||
* @returns The price of the token in USDC
|
||||
* @returns The price of the token quoted in USDC
|
||||
*/
|
||||
export async function fetchPrice(
|
||||
agent: SolanaAgentKit,
|
||||
tokenId: string
|
||||
): Promise<string> {
|
||||
export async function fetchPrice(tokenId: PublicKey): Promise<string> {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`https://api.jup.ag/price/v2?ids=${tokenId}`
|
||||
);
|
||||
const response = await fetch(`https://api.jup.ag/price/v2?ids=${tokenId}`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch price: ${response.statusText}`);
|
||||
@@ -22,7 +15,7 @@ export async function fetchPrice(
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
const price = data.data[tokenId]?.price;
|
||||
const price = data.data[tokenId.toBase58()]?.price;
|
||||
|
||||
if (!price) {
|
||||
throw new Error("Price data not available for the given token.");
|
||||
@@ -32,4 +25,4 @@ export async function fetchPrice(
|
||||
} catch (error: any) {
|
||||
throw new Error(`Price fetch failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,22 +16,22 @@ import { SolanaAgentKit } from "../index";
|
||||
*/
|
||||
export async function getPrimaryDomain(
|
||||
agent: SolanaAgentKit,
|
||||
account: PublicKey
|
||||
account: PublicKey,
|
||||
): Promise<string> {
|
||||
try {
|
||||
const { reverse, stale } = await _getPrimaryDomain(
|
||||
agent.connection,
|
||||
account
|
||||
account,
|
||||
);
|
||||
if (stale) {
|
||||
throw new Error(
|
||||
`Primary domain is stale for account: ${account.toBase58()}`
|
||||
`Primary domain is stale for account: ${account.toBase58()}`,
|
||||
);
|
||||
}
|
||||
return reverse;
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to get primary domain for account: ${account.toBase58()}`
|
||||
`Failed to get primary domain for account: ${account.toBase58()}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { VersionedTransaction } from "@solana/web3.js";
|
||||
import { LuloAccountDetailsResponse } from "../types";
|
||||
import { SolanaAgentKit } from "../agent";
|
||||
import { SolanaAgentKit } from "../index";
|
||||
|
||||
/**
|
||||
* Lend tokens for yields using Lulo
|
||||
@@ -10,7 +9,7 @@ import { SolanaAgentKit } from "../agent";
|
||||
*/
|
||||
export async function lendAsset(
|
||||
agent: SolanaAgentKit,
|
||||
amount: number
|
||||
amount: number,
|
||||
): Promise<string> {
|
||||
try {
|
||||
const response = await fetch(
|
||||
@@ -23,14 +22,14 @@ export async function lendAsset(
|
||||
body: JSON.stringify({
|
||||
account: agent.wallet.publicKey.toBase58(),
|
||||
}),
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Deserialize the transaction
|
||||
const luloTxn = VersionedTransaction.deserialize(
|
||||
Buffer.from(data.transaction, "base64")
|
||||
Buffer.from(data.transaction, "base64"),
|
||||
);
|
||||
|
||||
// Get a recent blockhash and set it
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
import { SolanaAgentKit } from "../index";
|
||||
import { generateSigner, keypairIdentity } from '@metaplex-foundation/umi';
|
||||
import { create, mplCore } from '@metaplex-foundation/mpl-core';
|
||||
import { fetchCollection } from '@metaplex-foundation/mpl-core';
|
||||
import { generateSigner, keypairIdentity } from "@metaplex-foundation/umi";
|
||||
import { create, mplCore } from "@metaplex-foundation/mpl-core";
|
||||
import { fetchCollection } from "@metaplex-foundation/mpl-core";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { fromWeb3JsKeypair, fromWeb3JsPublicKey, toWeb3JsPublicKey } from "@metaplex-foundation/umi-web3js-adapters";
|
||||
import { createUmi } from '@metaplex-foundation/umi-bundle-defaults';
|
||||
import { MintCollectionNFTResponse } from '../types';
|
||||
import {
|
||||
fromWeb3JsKeypair,
|
||||
fromWeb3JsPublicKey,
|
||||
toWeb3JsPublicKey,
|
||||
} from "@metaplex-foundation/umi-web3js-adapters";
|
||||
import { createUmi } from "@metaplex-foundation/umi-bundle-defaults";
|
||||
import { MintCollectionNFTResponse } from "../types";
|
||||
|
||||
/**
|
||||
* Mint a new NFT as part of an existing collection
|
||||
@@ -27,7 +31,7 @@ export async function mintCollectionNFT(
|
||||
share: number;
|
||||
}>;
|
||||
},
|
||||
recipient?: PublicKey
|
||||
recipient?: PublicKey,
|
||||
): Promise<MintCollectionNFTResponse> {
|
||||
try {
|
||||
// Create UMI instance from agent
|
||||
@@ -49,15 +53,15 @@ export async function mintCollectionNFT(
|
||||
collection: collection,
|
||||
name: metadata.name,
|
||||
uri: metadata.uri,
|
||||
owner: fromWeb3JsPublicKey(recipient ?? agent.wallet.publicKey)
|
||||
owner: fromWeb3JsPublicKey(recipient ?? agent.wallet.publicKey),
|
||||
}).sendAndConfirm(umi);
|
||||
|
||||
return {
|
||||
mint: toWeb3JsPublicKey(assetSigner.publicKey),
|
||||
// Note: Token account is now handled automatically by the create instruction
|
||||
metadata: toWeb3JsPublicKey(assetSigner.publicKey)
|
||||
metadata: toWeb3JsPublicKey(assetSigner.publicKey),
|
||||
};
|
||||
} catch (error: any) {
|
||||
throw new Error(`Collection NFT minting failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,34 @@
|
||||
import { OPEN_BOOK_PROGRAM, Raydium, TxVersion } from "@raydium-io/raydium-sdk-v2";
|
||||
import {
|
||||
OPEN_BOOK_PROGRAM,
|
||||
Raydium,
|
||||
TxVersion,
|
||||
} from "@raydium-io/raydium-sdk-v2";
|
||||
import { MintLayout, TOKEN_PROGRAM_ID } from "@solana/spl-token";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { SolanaAgentKit } from "../agent";
|
||||
import { SolanaAgentKit } from "../index";
|
||||
|
||||
export async function openbookCreateMarket(
|
||||
agent: SolanaAgentKit,
|
||||
|
||||
baseMint: PublicKey,
|
||||
quoteMint: PublicKey,
|
||||
|
||||
lotSize: number = 1,
|
||||
tickSize: number = 0.01,
|
||||
): Promise<string[]> {
|
||||
|
||||
const raydium = await Raydium.load({
|
||||
owner: agent.wallet,
|
||||
connection: agent.connection,
|
||||
})
|
||||
});
|
||||
|
||||
const baseMintInfo = await agent.connection.getAccountInfo(baseMint)
|
||||
const quoteMintInfo = await agent.connection.getAccountInfo(quoteMint)
|
||||
const baseMintInfo = await agent.connection.getAccountInfo(baseMint);
|
||||
const quoteMintInfo = await agent.connection.getAccountInfo(quoteMint);
|
||||
|
||||
if (
|
||||
baseMintInfo?.owner.toString() !== TOKEN_PROGRAM_ID.toBase58() ||
|
||||
quoteMintInfo?.owner.toString() !== TOKEN_PROGRAM_ID.toBase58()
|
||||
) {
|
||||
throw new Error(
|
||||
'openbook market only support TOKEN_PROGRAM_ID mints, if you want to create pool with token-2022, please create raydium cpmm pool instead'
|
||||
)
|
||||
"openbook market only support TOKEN_PROGRAM_ID mints, if you want to create pool with token-2022, please create raydium cpmm pool instead",
|
||||
);
|
||||
}
|
||||
|
||||
const { execute } = await raydium.marketV2.create({
|
||||
@@ -44,9 +45,9 @@ export async function openbookCreateMarket(
|
||||
dexProgramId: OPEN_BOOK_PROGRAM,
|
||||
|
||||
txVersion: TxVersion.V0,
|
||||
})
|
||||
});
|
||||
|
||||
const { txIds } = await execute({ sequentially: true, })
|
||||
const { txIds } = await execute({ sequentially: true });
|
||||
|
||||
return txIds
|
||||
return txIds;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { SolanaAgentKit } from "../index";
|
||||
import { Tool } from "langchain/tools";
|
||||
import { PriceServiceConnection } from "@pythnetwork/price-service-client";
|
||||
import BN from "bn.js";
|
||||
|
||||
@@ -9,40 +6,35 @@ import BN from "bn.js";
|
||||
* @param agent SolanaAgentKit instance
|
||||
* @param priceFeedID Price feed ID
|
||||
* @returns Latest price value from feed
|
||||
*
|
||||
*
|
||||
* You can find priceFeedIDs here: https://www.pyth.network/developers/price-feed-ids#stable
|
||||
*/
|
||||
export async function pythFetchPrice(
|
||||
agent: SolanaAgentKit,
|
||||
priceFeedID: string
|
||||
) {
|
||||
// get Hermes service URL from https://docs.pyth.network/price-feeds/api-instances-and-providers/hermes
|
||||
export async function pythFetchPrice(priceFeedID: string): Promise<string> {
|
||||
// get Hermes service URL from https://docs.pyth.network/price-feeds/api-instances-and-providers/hermes
|
||||
const stableHermesServiceUrl: string = "https://hermes.pyth.network";
|
||||
const connection = new PriceServiceConnection(stableHermesServiceUrl);
|
||||
const feeds = [priceFeedID];
|
||||
|
||||
try {
|
||||
const currentPrice = await connection.getLatestPriceFeeds(feeds);
|
||||
|
||||
|
||||
if (currentPrice === undefined) {
|
||||
throw new Error("Price data not available for the given token.");
|
||||
}
|
||||
|
||||
|
||||
if (currentPrice.length === 0) {
|
||||
throw new Error("Price data not available for the given token.");
|
||||
}
|
||||
|
||||
|
||||
// get price and exponent from price feed
|
||||
let price = new BN(currentPrice[0].getPriceUnchecked().price);
|
||||
let exponent = new BN(currentPrice[0].getPriceUnchecked().expo);
|
||||
|
||||
|
||||
// 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}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,42 +1,59 @@
|
||||
import { AMM_V4, FEE_DESTINATION_ID, MARKET_STATE_LAYOUT_V3, OPEN_BOOK_PROGRAM, Raydium, TxVersion } from "@raydium-io/raydium-sdk-v2";
|
||||
import {
|
||||
AMM_V4,
|
||||
FEE_DESTINATION_ID,
|
||||
MARKET_STATE_LAYOUT_V3,
|
||||
OPEN_BOOK_PROGRAM,
|
||||
Raydium,
|
||||
TxVersion,
|
||||
} from "@raydium-io/raydium-sdk-v2";
|
||||
import { MintLayout, TOKEN_PROGRAM_ID } from "@solana/spl-token";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import BN from 'bn.js';
|
||||
import { SolanaAgentKit } from "../agent";
|
||||
import BN from "bn.js";
|
||||
import { SolanaAgentKit } from "../index";
|
||||
|
||||
export async function raydiumCreateAmmV4(
|
||||
agent: SolanaAgentKit,
|
||||
|
||||
marketId: PublicKey,
|
||||
|
||||
baseAmount: BN,
|
||||
quoteAmount: BN,
|
||||
|
||||
startTime: BN,
|
||||
): Promise<string> {
|
||||
|
||||
const raydium = await Raydium.load({
|
||||
owner: agent.wallet,
|
||||
connection: agent.connection,
|
||||
})
|
||||
});
|
||||
|
||||
const marketBufferInfo = await agent.connection.getAccountInfo(new PublicKey(marketId))
|
||||
const { baseMint, quoteMint } = MARKET_STATE_LAYOUT_V3.decode(marketBufferInfo!.data)
|
||||
const marketBufferInfo = await agent.connection.getAccountInfo(
|
||||
new PublicKey(marketId),
|
||||
);
|
||||
const { baseMint, quoteMint } = MARKET_STATE_LAYOUT_V3.decode(
|
||||
marketBufferInfo!.data,
|
||||
);
|
||||
|
||||
const baseMintInfo = await agent.connection.getAccountInfo(baseMint)
|
||||
const quoteMintInfo = await agent.connection.getAccountInfo(quoteMint)
|
||||
const baseMintInfo = await agent.connection.getAccountInfo(baseMint);
|
||||
const quoteMintInfo = await agent.connection.getAccountInfo(quoteMint);
|
||||
|
||||
if (
|
||||
baseMintInfo?.owner.toString() !== TOKEN_PROGRAM_ID.toBase58() ||
|
||||
quoteMintInfo?.owner.toString() !== TOKEN_PROGRAM_ID.toBase58()
|
||||
) {
|
||||
throw new Error(
|
||||
'amm pools with openbook market only support TOKEN_PROGRAM_ID mints, if you want to create pool with token-2022, please create cpmm pool instead'
|
||||
)
|
||||
"amm pools with openbook market only support TOKEN_PROGRAM_ID mints, if you want to create pool with token-2022, please create cpmm pool instead",
|
||||
);
|
||||
}
|
||||
|
||||
if (baseAmount.mul(quoteAmount).lte(new BN(1).mul(new BN(10 ** MintLayout.decode(baseMintInfo.data).decimals)).pow(new BN(2)))) {
|
||||
throw new Error('initial liquidity too low, try adding more baseAmount/quoteAmount')
|
||||
if (
|
||||
baseAmount
|
||||
.mul(quoteAmount)
|
||||
.lte(
|
||||
new BN(1)
|
||||
.mul(new BN(10 ** MintLayout.decode(baseMintInfo.data).decimals))
|
||||
.pow(new BN(2)),
|
||||
)
|
||||
) {
|
||||
throw new Error(
|
||||
"initial liquidity too low, try adding more baseAmount/quoteAmount",
|
||||
);
|
||||
}
|
||||
|
||||
const { execute } = await raydium.liquidity.createPoolV4({
|
||||
@@ -63,9 +80,9 @@ export async function raydiumCreateAmmV4(
|
||||
associatedOnly: false,
|
||||
txVersion: TxVersion.V0,
|
||||
feeDestinationId: FEE_DESTINATION_ID,
|
||||
})
|
||||
});
|
||||
|
||||
const { txId } = await execute({ sendAndConfirm: true })
|
||||
const { txId } = await execute({ sendAndConfirm: true });
|
||||
|
||||
return txId
|
||||
}
|
||||
return txId;
|
||||
}
|
||||
|
||||
@@ -1,55 +1,58 @@
|
||||
import { CLMM_PROGRAM_ID, Raydium, TxVersion } from '@raydium-io/raydium-sdk-v2'
|
||||
import { MintLayout } from '@solana/spl-token'
|
||||
import { PublicKey } from '@solana/web3.js'
|
||||
import BN from 'bn.js'
|
||||
import Decimal from 'decimal.js'
|
||||
import { SolanaAgentKit } from '../agent'
|
||||
import {
|
||||
CLMM_PROGRAM_ID,
|
||||
Raydium,
|
||||
TxVersion,
|
||||
} from "@raydium-io/raydium-sdk-v2";
|
||||
import { MintLayout } from "@solana/spl-token";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import BN from "bn.js";
|
||||
import Decimal from "decimal.js";
|
||||
import { SolanaAgentKit } from "../index";
|
||||
|
||||
export async function raydiumCreateClmm(
|
||||
agent: SolanaAgentKit,
|
||||
|
||||
mint1: PublicKey,
|
||||
mint2: PublicKey,
|
||||
|
||||
configId: PublicKey,
|
||||
|
||||
initialPrice: Decimal,
|
||||
startTime: BN,
|
||||
): Promise<string> {
|
||||
|
||||
const raydium = await Raydium.load({
|
||||
owner: agent.wallet,
|
||||
connection: agent.connection,
|
||||
})
|
||||
});
|
||||
|
||||
const [mintInfo1, mintInfo2] = await agent.connection.getMultipleAccountsInfo([mint1, mint2])
|
||||
if (mintInfo1 === null || mintInfo2 === null) throw Error('fetch mint info error')
|
||||
const [mintInfo1, mintInfo2] = await agent.connection.getMultipleAccountsInfo(
|
||||
[mint1, mint2],
|
||||
);
|
||||
if (mintInfo1 === null || mintInfo2 === null)
|
||||
throw Error("fetch mint info error");
|
||||
|
||||
const mintDecodeInfo1 = MintLayout.decode(mintInfo1.data)
|
||||
const mintDecodeInfo2 = MintLayout.decode(mintInfo2.data)
|
||||
const mintDecodeInfo1 = MintLayout.decode(mintInfo1.data);
|
||||
const mintDecodeInfo2 = MintLayout.decode(mintInfo2.data);
|
||||
|
||||
const mintFormatInfo1 = {
|
||||
chainId: 101,
|
||||
address: mint1.toString(),
|
||||
programId: mintInfo1.owner.toString(),
|
||||
logoURI: '',
|
||||
symbol: '',
|
||||
name: '',
|
||||
logoURI: "",
|
||||
symbol: "",
|
||||
name: "",
|
||||
decimals: mintDecodeInfo1.decimals,
|
||||
tags: [],
|
||||
extensions: {}
|
||||
}
|
||||
extensions: {},
|
||||
};
|
||||
const mintFormatInfo2 = {
|
||||
chainId: 101,
|
||||
address: mint2.toString(),
|
||||
programId: mintInfo2.owner.toString(),
|
||||
logoURI: '',
|
||||
symbol: '',
|
||||
name: '',
|
||||
logoURI: "",
|
||||
symbol: "",
|
||||
name: "",
|
||||
decimals: mintDecodeInfo2.decimals,
|
||||
tags: [],
|
||||
extensions: {}
|
||||
}
|
||||
extensions: {},
|
||||
};
|
||||
|
||||
const { execute } = await raydium.clmm.createPool({
|
||||
programId: CLMM_PROGRAM_ID,
|
||||
@@ -65,9 +68,9 @@ export async function raydiumCreateClmm(
|
||||
// units: 600000,
|
||||
// microLamports: 46591500,
|
||||
// },
|
||||
})
|
||||
});
|
||||
|
||||
const { txId } = await execute({ sendAndConfirm: true })
|
||||
const { txId } = await execute({ sendAndConfirm: true });
|
||||
|
||||
return txId
|
||||
return txId;
|
||||
}
|
||||
|
||||
@@ -2,60 +2,58 @@ import {
|
||||
CREATE_CPMM_POOL_FEE_ACC,
|
||||
CREATE_CPMM_POOL_PROGRAM,
|
||||
Raydium,
|
||||
TxVersion
|
||||
} from '@raydium-io/raydium-sdk-v2'
|
||||
import { MintLayout } from '@solana/spl-token'
|
||||
import { PublicKey } from '@solana/web3.js'
|
||||
import BN from 'bn.js'
|
||||
import { SolanaAgentKit } from '../agent'
|
||||
TxVersion,
|
||||
} from "@raydium-io/raydium-sdk-v2";
|
||||
import { MintLayout } from "@solana/spl-token";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import BN from "bn.js";
|
||||
import { SolanaAgentKit } from "../index";
|
||||
|
||||
export async function raydiumCreateCpmm(
|
||||
agent: SolanaAgentKit,
|
||||
|
||||
mintA: PublicKey,
|
||||
mintB: PublicKey,
|
||||
|
||||
configId: PublicKey,
|
||||
|
||||
mintAAmount: BN,
|
||||
mintBAmount: BN,
|
||||
|
||||
startTime: BN,
|
||||
): Promise<string> {
|
||||
|
||||
const raydium = await Raydium.load({
|
||||
owner: agent.wallet,
|
||||
connection: agent.connection,
|
||||
})
|
||||
});
|
||||
|
||||
const [mintInfoA, mintInfoB] = await agent.connection.getMultipleAccountsInfo([mintA, mintB])
|
||||
if (mintInfoA === null || mintInfoB === null) throw Error('fetch mint info error')
|
||||
const [mintInfoA, mintInfoB] = await agent.connection.getMultipleAccountsInfo(
|
||||
[mintA, mintB],
|
||||
);
|
||||
if (mintInfoA === null || mintInfoB === null)
|
||||
throw Error("fetch mint info error");
|
||||
|
||||
const mintDecodeInfoA = MintLayout.decode(mintInfoA.data)
|
||||
const mintDecodeInfoB = MintLayout.decode(mintInfoB.data)
|
||||
const mintDecodeInfoA = MintLayout.decode(mintInfoA.data);
|
||||
const mintDecodeInfoB = MintLayout.decode(mintInfoB.data);
|
||||
|
||||
const mintFormatInfoA = {
|
||||
chainId: 101,
|
||||
address: mintA.toString(),
|
||||
programId: mintInfoA.owner.toString(),
|
||||
logoURI: '',
|
||||
symbol: '',
|
||||
name: '',
|
||||
logoURI: "",
|
||||
symbol: "",
|
||||
name: "",
|
||||
decimals: mintDecodeInfoA.decimals,
|
||||
tags: [],
|
||||
extensions: {}
|
||||
}
|
||||
extensions: {},
|
||||
};
|
||||
const mintFormatInfoB = {
|
||||
chainId: 101,
|
||||
address: mintB.toString(),
|
||||
programId: mintInfoB.owner.toString(),
|
||||
logoURI: '',
|
||||
symbol: '',
|
||||
name: '',
|
||||
logoURI: "",
|
||||
symbol: "",
|
||||
name: "",
|
||||
decimals: mintDecodeInfoB.decimals,
|
||||
tags: [],
|
||||
extensions: {}
|
||||
}
|
||||
extensions: {},
|
||||
};
|
||||
|
||||
const { execute, extInfo } = await raydium.cpmm.createPool({
|
||||
programId: CREATE_CPMM_POOL_PROGRAM,
|
||||
@@ -76,9 +74,9 @@ export async function raydiumCreateCpmm(
|
||||
// units: 600000,
|
||||
// microLamports: 46591500,
|
||||
// },
|
||||
})
|
||||
});
|
||||
|
||||
const { txId } = await execute({ sendAndConfirm: true })
|
||||
const { txId } = await execute({ sendAndConfirm: true });
|
||||
|
||||
return txId
|
||||
return txId;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import { SolanaAgentKit } from "../index";
|
||||
*/
|
||||
export async function resolveSolDomain(
|
||||
agent: SolanaAgentKit,
|
||||
domain: string
|
||||
domain: string,
|
||||
): Promise<PublicKey> {
|
||||
if (!domain || typeof domain !== "string") {
|
||||
throw new Error("Invalid domain. Expected a non-empty string.");
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import {
|
||||
AddressLookupTableAccount,
|
||||
ComputeBudgetProgram,
|
||||
Connection,
|
||||
Keypair,
|
||||
PublicKey,
|
||||
TransactionInstruction,
|
||||
} from "@solana/web3.js";
|
||||
import { SolanaAgentKit } from "../agent/index.js";
|
||||
import { SolanaAgentKit } from "../index";
|
||||
import {
|
||||
buildAndSignTx,
|
||||
calculateComputeUnitPrice,
|
||||
@@ -33,7 +32,7 @@ const MAX_CONCURRENT_TXS = 30;
|
||||
*/
|
||||
export const getAirdropCostEstimate = (
|
||||
numberOfRecipients: number,
|
||||
priorityFeeInLamports: number
|
||||
priorityFeeInLamports: number,
|
||||
) => {
|
||||
const baseFee = 5000;
|
||||
const perRecipientCompressedStateFee = 300;
|
||||
@@ -63,22 +62,21 @@ export async function sendCompressedAirdrop(
|
||||
decimals: number,
|
||||
recipients: PublicKey[],
|
||||
priorityFeeInLamports: number,
|
||||
shouldLog: boolean = false
|
||||
shouldLog: boolean = false,
|
||||
): Promise<string[]> {
|
||||
if (recipients.length > MAX_AIRDROP_RECIPIENTS) {
|
||||
throw new Error(
|
||||
`Max airdrop can be ${MAX_AIRDROP_RECIPIENTS} recipients at a time. For more scale, use open source ZK Compression airdrop tools such as https://github.com/helius-labs/airship.`
|
||||
`Max airdrop can be ${MAX_AIRDROP_RECIPIENTS} recipients at a time. For more scale, use open source ZK Compression airdrop tools such as https://github.com/helius-labs/airship.`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
const url = agent.connection.rpcEndpoint;
|
||||
if (url.includes("devnet")) {
|
||||
throw new Error("Devnet is not supported for airdrop. Please use mainnet.");
|
||||
}
|
||||
if (!url.includes("helius")) {
|
||||
console.warn(
|
||||
"Warning: Must use RPC with ZK Compression support. Double check with your RPC provider if in doubt."
|
||||
"Warning: Must use RPC with ZK Compression support. Double check with your RPC provider if in doubt.",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -88,11 +86,11 @@ export async function sendCompressedAirdrop(
|
||||
agent.connection,
|
||||
agent.wallet,
|
||||
mintAddress,
|
||||
agent.wallet.publicKey
|
||||
agent.wallet.publicKey,
|
||||
);
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
"Source token account not found and failed to create it. Please add funds to your wallet and try again."
|
||||
"Source token account not found and failed to create it. Please add funds to your wallet and try again.",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -100,7 +98,7 @@ export async function sendCompressedAirdrop(
|
||||
await createTokenPool(
|
||||
agent.connection as unknown as Rpc,
|
||||
agent.wallet,
|
||||
mintAddress
|
||||
mintAddress,
|
||||
);
|
||||
} catch (error: any) {
|
||||
if (error.message.includes("already in use")) {
|
||||
@@ -116,7 +114,7 @@ export async function sendCompressedAirdrop(
|
||||
mintAddress,
|
||||
recipients,
|
||||
priorityFeeInLamports,
|
||||
shouldLog
|
||||
shouldLog,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -126,7 +124,7 @@ async function processAll(
|
||||
mint: PublicKey,
|
||||
recipients: PublicKey[],
|
||||
priorityFeeInLamports: number,
|
||||
shouldLog: boolean
|
||||
shouldLog: boolean,
|
||||
): Promise<string[]> {
|
||||
const mintAddress = mint;
|
||||
const payer = agent.wallet;
|
||||
@@ -135,13 +133,13 @@ async function processAll(
|
||||
agent.connection,
|
||||
agent.wallet,
|
||||
mintAddress,
|
||||
agent.wallet.publicKey
|
||||
agent.wallet.publicKey,
|
||||
);
|
||||
|
||||
const maxRecipientsPerInstruction = 5;
|
||||
const maxIxs = 3; // empirically determined (as of 12/15/2024)
|
||||
const lookupTableAddress = new PublicKey(
|
||||
"9NYFyEqPkyXUhkerbGHXUXkvb4qpzeEdHuGpgbgpH1NJ"
|
||||
"9NYFyEqPkyXUhkerbGHXUXkvb4qpzeEdHuGpgbgpH1NJ",
|
||||
);
|
||||
|
||||
const lookupTableAccount = (
|
||||
@@ -164,7 +162,7 @@ async function processAll(
|
||||
ComputeBudgetProgram.setComputeUnitPrice({
|
||||
microLamports: calculateComputeUnitPrice(
|
||||
priorityFeeInLamports,
|
||||
500_000
|
||||
500_000,
|
||||
),
|
||||
}),
|
||||
];
|
||||
@@ -184,13 +182,13 @@ async function processAll(
|
||||
toAddress: batch,
|
||||
amount: batch.map(() => amount),
|
||||
mint: mintAddress,
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const compressIxs = await Promise.all(compressIxPromises);
|
||||
return [...instructions, ...compressIxs];
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
const url = agent.connection.rpcEndpoint;
|
||||
@@ -225,12 +223,12 @@ async function processAll(
|
||||
instructions,
|
||||
payer,
|
||||
lookupTableAccount,
|
||||
i + idx
|
||||
i + idx,
|
||||
).then((signature) => {
|
||||
confirmedCount++;
|
||||
log("\r" + renderProgressBar(confirmedCount, totalBatches));
|
||||
return signature;
|
||||
})
|
||||
}),
|
||||
);
|
||||
|
||||
const batchResults = await Promise.allSettled(batchPromises);
|
||||
@@ -250,7 +248,7 @@ async function processAll(
|
||||
throw new Error(
|
||||
`Failed to process ${failures.length} batches: ${failures
|
||||
.map((f) => f.error)
|
||||
.join(", ")}`
|
||||
.join(", ")}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -262,7 +260,7 @@ async function sendTransactionWithRetry(
|
||||
instructions: TransactionInstruction[],
|
||||
payer: Keypair,
|
||||
lookupTableAccount: AddressLookupTableAccount,
|
||||
batchIndex: number
|
||||
batchIndex: number,
|
||||
): Promise<string> {
|
||||
const MAX_RETRIES = 3;
|
||||
const INITIAL_BACKOFF = 500; // ms
|
||||
@@ -275,7 +273,7 @@ async function sendTransactionWithRetry(
|
||||
payer,
|
||||
blockhash,
|
||||
[],
|
||||
[lookupTableAccount]
|
||||
[lookupTableAccount],
|
||||
);
|
||||
|
||||
const signature = await sendAndConfirmTx(connection, tx);
|
||||
@@ -292,7 +290,7 @@ async function sendTransactionWithRetry(
|
||||
throw new Error(
|
||||
`Batch ${batchIndex} failed after ${attempt + 1} attempts: ${
|
||||
error.message
|
||||
}`
|
||||
}`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { VersionedTransaction } from "@solana/web3.js";
|
||||
import { SolanaAgentKit } from "../agent";
|
||||
import { SolanaAgentKit } from "../index";
|
||||
|
||||
/**
|
||||
* Stake SOL with Jup validator
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { VersionedTransaction, PublicKey, LAMPORTS_PER_SOL } from "@solana/web3.js";
|
||||
import {
|
||||
VersionedTransaction,
|
||||
PublicKey,
|
||||
LAMPORTS_PER_SOL,
|
||||
} from "@solana/web3.js";
|
||||
import { SolanaAgentKit } from "../index";
|
||||
import { TOKENS, DEFAULT_OPTIONS, JUP_API } from "../constants";
|
||||
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import { SolanaAgentKit } from "../index";
|
||||
import {
|
||||
PublicKey,
|
||||
SystemProgram,
|
||||
Transaction
|
||||
} from "@solana/web3.js";
|
||||
import { PublicKey, SystemProgram, Transaction } from "@solana/web3.js";
|
||||
import { LAMPORTS_PER_SOL } from "@solana/web3.js";
|
||||
import {
|
||||
getAssociatedTokenAddress,
|
||||
import {
|
||||
getAssociatedTokenAddress,
|
||||
createTransferInstruction,
|
||||
getMint
|
||||
getMint,
|
||||
} from "@solana/spl-token";
|
||||
|
||||
/**
|
||||
@@ -23,7 +19,7 @@ export async function transfer(
|
||||
agent: SolanaAgentKit,
|
||||
to: PublicKey,
|
||||
amount: number,
|
||||
mint?: PublicKey
|
||||
mint?: PublicKey,
|
||||
): Promise<string> {
|
||||
try {
|
||||
let tx: string;
|
||||
@@ -34,19 +30,19 @@ export async function transfer(
|
||||
SystemProgram.transfer({
|
||||
fromPubkey: agent.wallet_address,
|
||||
toPubkey: to,
|
||||
lamports: amount * LAMPORTS_PER_SOL
|
||||
})
|
||||
lamports: amount * LAMPORTS_PER_SOL,
|
||||
}),
|
||||
);
|
||||
|
||||
tx = await agent.connection.sendTransaction(
|
||||
transaction,
|
||||
[agent.wallet]
|
||||
);
|
||||
tx = await agent.connection.sendTransaction(transaction, [agent.wallet]);
|
||||
} else {
|
||||
// Transfer SPL token
|
||||
const fromAta = await getAssociatedTokenAddress(mint, agent.wallet_address);
|
||||
const fromAta = await getAssociatedTokenAddress(
|
||||
mint,
|
||||
agent.wallet_address,
|
||||
);
|
||||
const toAta = await getAssociatedTokenAddress(mint, to);
|
||||
|
||||
|
||||
// Get mint info to determine decimals
|
||||
const mintInfo = await getMint(agent.connection, mint);
|
||||
const adjustedAmount = amount * Math.pow(10, mintInfo.decimals);
|
||||
@@ -56,18 +52,15 @@ export async function transfer(
|
||||
fromAta,
|
||||
toAta,
|
||||
agent.wallet_address,
|
||||
adjustedAmount
|
||||
)
|
||||
adjustedAmount,
|
||||
),
|
||||
);
|
||||
|
||||
tx = await agent.connection.sendTransaction(
|
||||
transaction,
|
||||
[agent.wallet]
|
||||
);
|
||||
tx = await agent.connection.sendTransaction(transaction, [agent.wallet]);
|
||||
}
|
||||
|
||||
return tx;
|
||||
} catch (error: any) {
|
||||
throw new Error(`Transfer failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ export async function getPriorityFees(connection: Connection): Promise<{
|
||||
const median =
|
||||
sortedFees.length % 2 === 0
|
||||
? ((sortedFees[mid - 1] ?? 0) + (sortedFees[mid] ?? 0)) / 2
|
||||
: sortedFees[mid] ?? 0;
|
||||
: (sortedFees[mid] ?? 0);
|
||||
|
||||
// Helper to create priority fee IX based on chosen strategy
|
||||
const createPriorityFeeIx = (fee: number) => {
|
||||
@@ -76,7 +76,7 @@ export async function getPriorityFees(connection: Connection): Promise<{
|
||||
export async function sendTx(
|
||||
agent: SolanaAgentKit,
|
||||
tx: Transaction,
|
||||
otherKeypairs?: Keypair[]
|
||||
otherKeypairs?: Keypair[],
|
||||
) {
|
||||
tx.recentBlockhash = (await agent.connection.getLatestBlockhash()).blockhash;
|
||||
tx.feePayer = agent.wallet_address;
|
||||
@@ -90,9 +90,8 @@ export async function sendTx(
|
||||
await agent.connection.confirmTransaction({
|
||||
signature: txid,
|
||||
blockhash: (await agent.connection.getLatestBlockhash()).blockhash,
|
||||
lastValidBlockHeight: (
|
||||
await agent.connection.getLatestBlockhash()
|
||||
).lastValidBlockHeight,
|
||||
lastValidBlockHeight: (await agent.connection.getLatestBlockhash())
|
||||
.lastValidBlockHeight,
|
||||
});
|
||||
return txid;
|
||||
}
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
|
||||
import { SolanaAgentKit } from "../src";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import * as dotenv from "dotenv";
|
||||
import { expect } from "chai";
|
||||
import { before, describe, it } from "node:test";
|
||||
import { TldParser } from "@onsol/tldparser";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
describe("Solana Domain Methods Tests", () => {
|
||||
let agent: SolanaAgentKit;
|
||||
|
||||
before(() => {
|
||||
// Initialize the agent before running tests
|
||||
if (
|
||||
!process.env.SOLANA_PRIVATE_KEY ||
|
||||
!process.env.RPC_URL ||
|
||||
!process.env.OPENAI_API_KEY
|
||||
) {
|
||||
throw new Error("Required environment variables are not set");
|
||||
}
|
||||
|
||||
agent = new SolanaAgentKit(
|
||||
process.env.SOLANA_PRIVATE_KEY,
|
||||
process.env.RPC_URL,
|
||||
process.env.OPENAI_API_KEY
|
||||
);
|
||||
});
|
||||
|
||||
describe("resolveAllDomains", () => {
|
||||
it("should resolve a valid domain to a public key", async () => {
|
||||
const testDomain = "hero.sol";
|
||||
const result = await agent.resolveAllDomains(testDomain);
|
||||
expect(result).to.be.instanceof(PublicKey);
|
||||
});
|
||||
it("should perform fetching of an owner an nft domain", async () => {
|
||||
const parser = new TldParser(agent.connection);
|
||||
const domanTld = "miester.sol";
|
||||
const ownerReceived = await parser.getOwnerFromDomainTld(domanTld);
|
||||
const owner = new PublicKey(
|
||||
"2EGGxj2qbNAJNgLCPKca8sxZYetyTjnoRspTPjzN2D67"
|
||||
);
|
||||
expect(ownerReceived).to.be(owner.toString());
|
||||
});
|
||||
it("should return null for non-existent domain", async () => {
|
||||
const nonExistentDomain = "nonexistent123456789.sol";
|
||||
const result = await agent.resolveAllDomains(nonExistentDomain);
|
||||
expect(result).to.be.null;
|
||||
});
|
||||
|
||||
it("should handle invalid domain format", async () => {
|
||||
const invalidDomain = "";
|
||||
try {
|
||||
await agent.resolveAllDomains(invalidDomain);
|
||||
expect.fail("Should have thrown an error");
|
||||
} catch (error) {
|
||||
expect(error).to.be.instanceof(Error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("getOwnedAllDomains", () => {
|
||||
it("should return array of domains for an owner", async () => {
|
||||
const owner = new PublicKey(
|
||||
"2EGGxj2qbNAJNgLCPKca8sxZYetyTjnoRspTPjzN2D67"
|
||||
);
|
||||
const domains = await agent.getOwnedAllDomains(owner);
|
||||
expect(domains).to.be.an("array").that.includes("miester.sol");
|
||||
domains.forEach((domain) => {
|
||||
expect(domain).to.be.a("string");
|
||||
});
|
||||
});
|
||||
it("should return array of domains for an owner", async () => {
|
||||
const owner = new PublicKey(agent.wallet_address);
|
||||
const domains = await agent.getOwnedAllDomains(owner);
|
||||
expect(domains).to.be.an("array");
|
||||
domains.forEach((domain) => {
|
||||
expect(domain).to.be.a("string");
|
||||
});
|
||||
});
|
||||
|
||||
it("should handle owner with no domains", async () => {
|
||||
// Create a new random public key that likely owns no domains
|
||||
const emptyOwner = PublicKey.unique();
|
||||
const domains = await agent.getOwnedAllDomains(emptyOwner);
|
||||
expect(domains).to.be.an("array");
|
||||
expect(domains).to.have.lengthOf(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getOwnedDomainsForTLD", () => {
|
||||
it("should return domains for specific TLD", async () => {
|
||||
const tld = "sol";
|
||||
const domains = await agent.getOwnedDomainsForTLD(tld);
|
||||
expect(domains).to.be.an("array");
|
||||
domains.forEach((domain) => {
|
||||
console.log(`these are the domains ${domain.domain}`)
|
||||
});
|
||||
});
|
||||
|
||||
it("should return empty array for non-existent TLD", async () => {
|
||||
const nonExistentTLD = "nonexistent";
|
||||
const domains = await agent.getOwnedDomainsForTLD(nonExistentTLD);
|
||||
expect(domains).to.be.an("array");
|
||||
expect(domains).to.have.lengthOf(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getAllDomainsTLDs", () => {
|
||||
it("should return array of TLDs", async () => {
|
||||
const tlds = await agent.getAllDomainsTLDs();
|
||||
expect(tlds).to.be.an("array");
|
||||
});
|
||||
});
|
||||
|
||||
describe("getAllRegisteredAllDomains", () => {
|
||||
it("should return array of all registered domains", async () => {
|
||||
const domains = await agent.getAllRegisteredAllDomains();
|
||||
expect(domains).to.be.an("array");
|
||||
domains.forEach((domain) => {
|
||||
expect(domain).to.be.a("string");
|
||||
expect(domain).to.include(".");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("getMainAllDomainsDomain", () => {
|
||||
it("should return main domain or null for an owner", async () => {
|
||||
const owner = new PublicKey(
|
||||
"2EGGxj2qbNAJNgLCPKca8sxZYetyTjnoRspTPjzN2D67"
|
||||
);
|
||||
const mainDomain = await agent.getMainAllDomainsDomain(owner);
|
||||
expect(mainDomain).to.satisfy((domain: string | null) => {
|
||||
return domain === null || typeof domain === "string";
|
||||
});
|
||||
});
|
||||
|
||||
it("should return null for address without main domain", async () => {
|
||||
const emptyOwner = PublicKey.unique();
|
||||
const mainDomain = await agent.getMainAllDomainsDomain(emptyOwner);
|
||||
expect(mainDomain).to.be.null;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@ function validateEnvironment(): void {
|
||||
const missingVars: string[] = [];
|
||||
const requiredVars = ["OPENAI_API_KEY", "RPC_URL", "SOLANA_PRIVATE_KEY"];
|
||||
|
||||
requiredVars.forEach(varName => {
|
||||
requiredVars.forEach((varName) => {
|
||||
if (!process.env[varName]) {
|
||||
missingVars.push(varName);
|
||||
}
|
||||
@@ -22,7 +22,7 @@ function validateEnvironment(): void {
|
||||
|
||||
if (missingVars.length > 0) {
|
||||
console.error("Error: Required environment variables are not set");
|
||||
missingVars.forEach(varName => {
|
||||
missingVars.forEach((varName) => {
|
||||
console.error(`${varName}=your_${varName.toLowerCase()}_here`);
|
||||
});
|
||||
process.exit(1);
|
||||
@@ -50,13 +50,13 @@ async function initializeAgent() {
|
||||
}
|
||||
}
|
||||
|
||||
const solanaKit = new SolanaAgentKit(
|
||||
const solanaAgent = new SolanaAgentKit(
|
||||
process.env.SOLANA_PRIVATE_KEY!,
|
||||
process.env.RPC_URL,
|
||||
process.env.OPENAI_API_KEY!
|
||||
process.env.OPENAI_API_KEY!,
|
||||
);
|
||||
|
||||
const tools = createSolanaTools(solanaKit);
|
||||
const tools = createSolanaTools(solanaAgent);
|
||||
const memory = new MemorySaver();
|
||||
const config = { configurable: { thread_id: "Solana Agent Kit!" } };
|
||||
|
||||
@@ -65,14 +65,13 @@ async function initializeAgent() {
|
||||
tools,
|
||||
checkpointSaver: memory,
|
||||
messageModifier: `
|
||||
You are a helpful agent that can interact onchain using the Solana Agent Kit. You are
|
||||
empowered to interact onchain using your tools. If you ever need funds, you can request them from the
|
||||
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://www.solanaagentkit.xyz for more information. Be
|
||||
concise and helpful with your responses. Refrain from restating your tools' descriptions unless it is
|
||||
explicitly requested.
|
||||
You are a helpful agent that can interact onchain using the Solana Agent Kit. You are
|
||||
empowered to interact onchain using your tools. If you ever need funds, you can request them from the
|
||||
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://www.solanaagentkit.xyz for more information. Be
|
||||
concise and helpful with your responses. Refrain from restating your tools' descriptions unless it is explicitly requested.
|
||||
`,
|
||||
});
|
||||
|
||||
@@ -96,7 +95,10 @@ async function runAutonomousMode(agent: any, config: any, interval = 10) {
|
||||
"Be creative and do something interesting on the blockchain. " +
|
||||
"Choose an action or set of actions and execute it that highlights your abilities.";
|
||||
|
||||
const stream = await agent.stream({ messages: [new HumanMessage(thought)] }, config);
|
||||
const stream = await agent.stream(
|
||||
{ messages: [new HumanMessage(thought)] },
|
||||
config,
|
||||
);
|
||||
|
||||
for await (const chunk of stream) {
|
||||
if ("agent" in chunk) {
|
||||
@@ -107,7 +109,7 @@ async function runAutonomousMode(agent: any, config: any, interval = 10) {
|
||||
console.log("-------------------");
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, interval * 1000));
|
||||
await new Promise((resolve) => setTimeout(resolve, interval * 1000));
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
console.error("Error:", error.message);
|
||||
@@ -126,7 +128,7 @@ async function runChatMode(agent: any, config: any) {
|
||||
});
|
||||
|
||||
const question = (prompt: string): Promise<string> =>
|
||||
new Promise(resolve => rl.question(prompt, resolve));
|
||||
new Promise((resolve) => rl.question(prompt, resolve));
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
@@ -136,7 +138,10 @@ async function runChatMode(agent: any, config: any) {
|
||||
break;
|
||||
}
|
||||
|
||||
const stream = await agent.stream({ messages: [new HumanMessage(userInput)] }, config);
|
||||
const stream = await agent.stream(
|
||||
{ messages: [new HumanMessage(userInput)] },
|
||||
config,
|
||||
);
|
||||
|
||||
for await (const chunk of stream) {
|
||||
if ("agent" in chunk) {
|
||||
@@ -164,7 +169,7 @@ async function chooseMode(): Promise<"chat" | "auto"> {
|
||||
});
|
||||
|
||||
const question = (prompt: string): Promise<string> =>
|
||||
new Promise(resolve => rl.question(prompt, resolve));
|
||||
new Promise((resolve) => rl.question(prompt, resolve));
|
||||
|
||||
while (true) {
|
||||
console.log("\nAvailable modes:");
|
||||
@@ -206,7 +211,7 @@ async function main() {
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
main().catch(error => {
|
||||
main().catch((error) => {
|
||||
console.error("Fatal error:", error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user