Remove console.logs

This commit is contained in:
calintje
2024-12-30 05:00:58 +01:00
84 changed files with 14098 additions and 122 deletions

View File

@@ -31,7 +31,7 @@
},
"overrides": [
{
"files": ["test/**/*", "src/utils/keypair.ts"],
"files": ["test/**/*", "src/utils/keypair.ts", "examples/**/*"],
"rules": {
"no-console": "off"
}

27
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,27 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.

33
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,33 @@
# Pull Request Description
## Related Issue
Fixes # (issue number)
## Changes Made
This PR adds the following changes:
<!-- List the key changes made in this PR -->
-
-
## Implementation Details
<!-- Provide technical details about the implementation -->
-
-
## Transaction executed by agent
<!-- If applicable, provide example usage, transactions, or screenshots -->
Example transaction:
## Prompt Used
<!-- If relevant, include the prompt or configuration used -->
```
```
## Additional Notes
<!-- Any additional information that reviewers should know -->
## Checklist
- [ ] I have tested these changes locally
- [ ] I have updated the documentation
- [ ] I have added a transaction link
- [ ] I have added the prompt used to test it

View File

@@ -2,10 +2,15 @@
# Solana Agent Kit
</div>
![Solana Agent Kit Cover 1 (3)](https://github.com/user-attachments/assets/cfa380f6-79d9-474d-9852-3e1976c6de70)
![NPM Downloads](https://img.shields.io/npm/dm/solana-agent-kit?style=for-the-badge)
![GitHub forks](https://img.shields.io/github/forks/sendaifun/solana-agent-kit?style=for-the-badge)
![GitHub License](https://img.shields.io/github/license/sendaifun/solana-agent-kit?style=for-the-badge)
</div>
An open-source toolkit for connecting AI agents to Solana protocols. Now, any agent, using any model can autonomously perform 15+ Solana actions:
- Trade tokens
@@ -18,6 +23,10 @@ An open-source toolkit for connecting AI agents to Solana protocols. Now, any ag
Anyone - whether an SF-based AI researcher or a crypto-native builder - can bring their AI agents trained with any model and seamlessly integrate with Solana.
[![Run on Repl.it](https://replit.com/badge/github/sendaifun/solana-agent-kit)](https://replit.com/@sendaifun/Solana-Agent-Kit)
> Replit template created by [Arpit Singh](https://github.com/The-x-35)
## 🔧 Core Blockchain Features
- **Token Operations**
@@ -37,17 +46,21 @@ Anyone - whether an SF-based AI researcher or a crypto-native builder - can brin
- Jupiter Exchange swaps
- Launch on Pump via PumpPortal
- Raydium pool creation (CPMM, CLMM, AMMv4)
- Orca whirlpool integration
- Orca Whirlpool integration
- Meteora Dynamic AMM, DLMM Pool, and Alpga Vault
- Openbook market creation
- Register and Resolve SNS
- Jito Bundles
- Pyth Price feeds for fetching Asset Prices
- Register/resolve Alldomains
- **Solana Blinks**
- Lending by Lulo
- Lending by Lulon (Best APR for USDC)
- Send Arcade Games
- JupSOL staking
- **Non-Financial Actions**
- Gib Work for registering bounties
## 🤖 AI Integration Features
@@ -191,6 +204,23 @@ const price = await agent.pythFetchPrice(
console.log("Price in BTC/USD:", price);
```
## Examples
### LangGraph Multi-Agent System
The repository includes an advanced example of building a multi-agent system using LangGraph and Solana Agent Kit. Located in `examples/agent-kit-langgraph`, this example demonstrates:
- Multi-agent architecture using LangGraph's StateGraph
- Specialized agents for different tasks:
- General purpose agent for basic queries
- Transfer/Swap agent for transaction operations
- Read agent for blockchain data queries
- Manager agent for routing and orchestration
- Fully typed TypeScript implementation
- Environment-based configuration
Check out the [LangGraph example](examples/agent-kit-langgraph) for a complete implementation of an advanced Solana agent system.
## Dependencies
The toolkit relies on several key Solana and Metaplex libraries:
@@ -209,10 +239,22 @@ The toolkit relies on several key Solana and Metaplex libraries:
Contributions are welcome! Please feel free to submit a Pull Request.
Refer to [CONTRIBUTING.md](CONTRIBUTING.md) for detailed guidelines on how to contribute to this project.
## Contributors
<a href="https://github.com/sendaifun/solana-agent-kit/graphs/contributors">
<img src="https://contrib.rocks/image?repo=sendaifun/solana-agent-kit" />
</a>
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=sendaifun/solana-agent-kit&type=Date)](https://star-history.com/#sendaifun/solana-agent-kit&Date)
## License
MIT License
Apache-2 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.

63
SECURITY.md Normal file
View File

@@ -0,0 +1,63 @@
# Security Policy
## Reporting a Vulnerability
We take the security of our software seriously. If you believe you have found a security vulnerability, please report it to us following these guidelines:
### Reporting Process
1. **DO NOT** create a public GitHub issue for the vulnerability
2. Email your findings to:
- Primary: aryan@sendai.fun
- Secondary: dev@sendai.fun
### What to Include
Please include the following information in your report:
- A clear description of the vulnerability
- Steps to reproduce the issue
- Affected versions
- Any potential impacts
- Optional: Suggested fixes or mitigations
### Response Timeline
- We will acknowledge receipt of your vulnerability report within 48 hours
- We aim to send a more detailed response within 5 business days
- We will keep you informed of our progress throughout the process
### Security Updates
Security updates will be released as soon as possible after we have confirmed and fixed the vulnerability. Updates will be published through:
- GitHub releases
- Security advisories
- Email notifications to affected parties (if applicable)
## Supported Versions
As an open-source project under the Apache 2.0 license, we focus our security updates on the latest stable release. While you're free to use any version as per the Apache 2.0 license terms, we strongly recommend using the most recent version for the best security posture.
| Version | Security Updates |
| ------- | --------------- |
| Latest Release | ✅ Active |
| Previous Releases | ⚠️ Use at your own risk |
Note: The Apache 2.0 license comes with NO WARRANTIES or CONDITIONS of any kind, either express or implied. Users are responsible for their own security assessment when using any version of this software.
## Security Best Practices
When using this software, please follow these security best practices:
- Keep your private keys secure and never share them
- Regularly update to the latest version
- Review transaction details before signing
- Use appropriate access controls in production environments
## Bug Bounty Program
Currently, we do not offer a bug bounty program. However, we greatly appreciate responsible disclosure of security vulnerabilities.
## License
This security policy is part of our project licensed under [Apache 2.0](LICENSE).

View File

@@ -1 +1 @@
window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAE42TwU7DMAyG3yXniooJJtTbRNUDDJigN8QhCi6JltpR40hMaO+OtiFYWebtkos/f7asP69fiuGTVaVeyGvUsw9AvnesChU0W1Up43WMEMtx/cJy71Whlg7fVXU5uVkXv6Zb8h4MO8IagqdVD7jnc8gwdNpALHPgWDy5nmbFT2HzxhPWH0pUDqCZhrxoV5PaG2BjF4Mz8AwxEEbImg4xSXqXgmMYWloC1pp1VvkfkoTz5GlmDCXkGlg7H8Vtj+PSkAeH/Hf6x6YVZxylpRGL1Icu4VwnNFbUZ8lT6ibh9pxSuDKcqF2xPTMjeVSSm00+YfcvWyK/t3GXcHvYWB5AY+P0av32DUTvPWMEBAAA"
window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAE42TwU7DMAyG3yXniooJJtTbRFUkGDCN3hCHEFwaNXWixBFMaO+O6BCsLPW45OIv3x/ZzuOHIHgnUYgHayTKxSsg3WgSmXCSWlEIZWQIEPJx/aSl3ohMdBpfRHE6u9hmP6ZLawwo0hZLcMZuesA9n0YC30gFIU+BY/HsfJ4U37uvMxyxflOs0oMk69OiXY27XgGpduW1gjUEZzFA0nSIcdIr/fxmfTfEQy1Dt4Zp9RTMBVxHpwl8bTvAUpJMiv9CnHAZjV0oZSNSCSS1CWw7pnEu5FYj/c72rqrZjEmai1jF3jURlzKiall9kjymriIO7eS2N8Gx2g21/1zCNMrJ1bBUu49fW2v2XtxEHBob8gNobJyfbZ8+AWWuiIJlBAAA"

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

View File

@@ -1,6 +1,9 @@
<!DOCTYPE html><html class="default" lang="en"><head><meta charset="utf-8"/><meta http-equiv="x-ua-compatible" content="IE=edge"/><title>solana-agent-kit</title><meta name="description" content="Documentation for solana-agent-kit"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="assets/style.css"/><link rel="stylesheet" href="assets/highlight.css"/><script defer src="assets/main.js"></script><script async src="assets/icons.js" id="tsd-icons-script"></script><script async src="assets/search.js" id="tsd-search-script"></script><script async src="assets/navigation.js" id="tsd-nav-script"></script></head><body><script>document.documentElement.dataset.theme = localStorage.getItem("tsd-theme") || "os";document.body.style.display="none";setTimeout(() => app?app.showPage():document.body.style.removeProperty("display"),500)</script><header class="tsd-page-toolbar"><div class="tsd-toolbar-contents container"><div class="table-cell" id="tsd-search" data-base="."><div class="field"><label for="tsd-search-field" class="tsd-widget tsd-toolbar-icon search no-caption"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><use href="assets/icons.svg#icon-search"></use></svg></label><input type="text" id="tsd-search-field" aria-label="Search"/></div><div class="field"><div id="tsd-toolbar-links"></div></div><ul class="results"><li class="state loading">Preparing search index...</li><li class="state failure">The search index is not available</li></ul><a href="index.html" class="title">solana-agent-kit</a></div><div class="table-cell" id="tsd-widgets"><a href="#" class="tsd-widget tsd-toolbar-icon menu no-caption" data-toggle="menu" aria-label="Menu"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><use href="assets/icons.svg#icon-menu"></use></svg></a></div></div></header><div class="container container-main"><div class="col-content"><div class="tsd-page-title"><h1>solana-agent-kit</h1></div><div class="tsd-panel tsd-typography"><div align="center">
<a id="md:solana-agent-kit" class="tsd-anchor"></a><h1 class="tsd-anchor-link">Solana Agent Kit<a href="#md:solana-agent-kit" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h1></div>
<p><img src="https://github.com/user-attachments/assets/cfa380f6-79d9-474d-9852-3e1976c6de70" alt="Solana Agent Kit Cover 1 (3)"></p>
<a id="md:solana-agent-kit" class="tsd-anchor"></a><h1 class="tsd-anchor-link">Solana Agent Kit<a href="#md:solana-agent-kit" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h1><p><img src="https://github.com/user-attachments/assets/cfa380f6-79d9-474d-9852-3e1976c6de70" alt="Solana Agent Kit Cover 1 (3)"></p>
<p><img src="https://img.shields.io/npm/dm/solana-agent-kit?style=for-the-badge" alt="NPM Downloads">
<img src="https://img.shields.io/github/forks/sendaifun/solana-agent-kit?style=for-the-badge" alt="GitHub forks">
<img src="https://img.shields.io/github/license/sendaifun/solana-agent-kit?style=for-the-badge" alt="GitHub License"></p>
</div>
<p>An open-source toolkit for connecting AI agents to Solana protocols. Now, any agent, using any model can autonomously perform 15+ Solana actions:</p>
<ul>
<li>Trade tokens</li>
@@ -12,6 +15,10 @@
<li>And more...</li>
</ul>
<p>Anyone - whether an SF-based AI researcher or a crypto-native builder - can bring their AI agents trained with any model and seamlessly integrate with Solana.</p>
<p><a href="https://replit.com/@sendaifun/Solana-Agent-Kit" target="_blank" class="external"><img src="https://replit.com/badge/github/sendaifun/solana-agent-kit" alt="Run on Repl.it"></a></p>
<blockquote>
<p>Replit template created by <a href="https://github.com/The-x-35" target="_blank" class="external">Arpit Singh</a></p>
</blockquote>
<a id="md:🔧-core-blockchain-features" class="tsd-anchor"></a><h2 class="tsd-anchor-link">🔧 Core Blockchain Features<a href="#md:🔧-core-blockchain-features" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><ul>
<li>
<p><strong>Token Operations</strong></p>
@@ -38,21 +45,29 @@
<li>Jupiter Exchange swaps</li>
<li>Launch on Pump via PumpPortal</li>
<li>Raydium pool creation (CPMM, CLMM, AMMv4)</li>
<li>Orca whirlpool integration</li>
<li>Orca Whirlpool integration</li>
<li>Meteora Dynamic AMM, DLMM Pool, and Alpga Vault</li>
<li>Openbook market creation</li>
<li>Register and Resolve SNS</li>
<li>Jito Bundles</li>
<li>Pyth Price feeds for fetching Asset Prices</li>
<li>Register/resolve Alldomains</li>
</ul>
</li>
<li>
<p><strong>Solana Blinks</strong></p>
<ul>
<li>Lending by Lulo</li>
<li>Lending by Lulon (Best APR for USDC)</li>
<li>Send Arcade Games</li>
<li>JupSOL staking</li>
</ul>
</li>
<li>
<p><strong>Non-Financial Actions</strong></p>
<ul>
<li>Gib Work for registering bounties</li>
</ul>
</li>
</ul>
<a id="md:🤖-ai-integration-features" class="tsd-anchor"></a><h2 class="tsd-anchor-link">🤖 AI Integration Features<a href="#md:🤖-ai-integration-features" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><ul>
<li>
@@ -110,6 +125,21 @@
<a id="md:fetch-price-data-from-pyth" class="tsd-anchor"></a><h3 class="tsd-anchor-link">Fetch Price Data from Pyth<a href="#md:fetch-price-data-from-pyth" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><pre><code class="typescript"><br/><span class="hl-6">const</span><span class="hl-1"> </span><span class="hl-7">price</span><span class="hl-1"> = </span><span class="hl-3">await</span><span class="hl-1"> </span><span class="hl-4">agent</span><span class="hl-1">.</span><span class="hl-0">pythFetchPrice</span><span class="hl-1">(</span><br/><span class="hl-1"> </span><span class="hl-2">&quot;0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43&quot;</span><br/><span class="hl-1">);</span><br/><br/><span class="hl-4">console</span><span class="hl-1">.</span><span class="hl-0">log</span><span class="hl-1">(</span><span class="hl-2">&quot;Price in BTC/USD:&quot;</span><span class="hl-1">, </span><span class="hl-4">price</span><span class="hl-1">);</span>
</code><button type="button">Copy</button></pre>
<a id="md:examples" class="tsd-anchor"></a><h2 class="tsd-anchor-link">Examples<a href="#md:examples" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><a id="md:langgraph-multi-agent-system" class="tsd-anchor"></a><h3 class="tsd-anchor-link">LangGraph Multi-Agent System<a href="#md:langgraph-multi-agent-system" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h3><p>The repository includes an advanced example of building a multi-agent system using LangGraph and Solana Agent Kit. Located in <code>examples/agent-kit-langgraph</code>, this example demonstrates:</p>
<ul>
<li>Multi-agent architecture using LangGraph's StateGraph</li>
<li>Specialized agents for different tasks:
<ul>
<li>General purpose agent for basic queries</li>
<li>Transfer/Swap agent for transaction operations</li>
<li>Read agent for blockchain data queries</li>
<li>Manager agent for routing and orchestration</li>
</ul>
</li>
<li>Fully typed TypeScript implementation</li>
<li>Environment-based configuration</li>
</ul>
<p>Check out the <a href="examples/agent-kit-langgraph">LangGraph example</a> for a complete implementation of an advanced Solana agent system.</p>
<a id="md:dependencies" class="tsd-anchor"></a><h2 class="tsd-anchor-link">Dependencies<a href="#md:dependencies" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>The toolkit relies on several key Solana and Metaplex libraries:</p>
<ul>
<li>@solana/web3.js</li>
@@ -123,6 +153,10 @@
</ul>
<a id="md:contributing" class="tsd-anchor"></a><h2 class="tsd-anchor-link">Contributing<a href="#md:contributing" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>Contributions are welcome! Please feel free to submit a Pull Request.
Refer to <a href="media/CONTRIBUTING.md">CONTRIBUTING.md</a> for detailed guidelines on how to contribute to this project.</p>
<a id="md:license" class="tsd-anchor"></a><h2 class="tsd-anchor-link">License<a href="#md:license" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>MIT License</p>
<a id="md:contributors" class="tsd-anchor"></a><h2 class="tsd-anchor-link">Contributors<a href="#md:contributors" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><a href="https://github.com/sendaifun/solana-agent-kit/graphs/contributors">
<img src="https://contrib.rocks/image?repo=sendaifun/solana-agent-kit" />
</a>
<a id="md:star-history" class="tsd-anchor"></a><h2 class="tsd-anchor-link">Star History<a href="#md:star-history" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p><a href="https://star-history.com/#sendaifun/solana-agent-kit&amp;Date" target="_blank" class="external"><img src="https://api.star-history.com/svg?repos=sendaifun/solana-agent-kit&amp;type=Date" alt="Star History Chart"></a></p>
<a id="md:license" class="tsd-anchor"></a><h2 class="tsd-anchor-link">License<a href="#md:license" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>Apache-2 License</p>
<a id="md:security" class="tsd-anchor"></a><h2 class="tsd-anchor-link">Security<a href="#md:security" aria-label="Permalink" class="tsd-anchor-icon"><svg viewBox="0 0 24 24"><use href="assets/icons.svg#icon-anchor"></use></svg></a></h2><p>This toolkit handles private keys and transactions. Always ensure you're using it in a secure environment and never share your private keys.</p>
</div></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><details open class="tsd-accordion tsd-page-navigation"><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>On This Page</h3></summary><div class="tsd-accordion-details"><a href="#md:solana-agent-kit"><span>Solana <wbr/>Agent <wbr/>Kit</span></a><ul><li><a href="#md:🔧-core-blockchain-features"><span>🔧 <wbr/>Core <wbr/>Blockchain <wbr/>Features</span></a></li><li><a href="#md:🤖-ai-integration-features"><span>🤖 AI <wbr/>Integration <wbr/>Features</span></a></li><li><a href="#md:📦-installation"><span>📦 <wbr/>Installation</span></a></li><li><a href="#md:quick-start"><span>Quick <wbr/>Start</span></a></li><li><a href="#md:usage-examples"><span>Usage <wbr/>Examples</span></a></li><li><ul><li><a href="#md:deploy-a-new-token"><span>Deploy a <wbr/>New <wbr/>Token</span></a></li><li><a href="#md:create-nft-collection"><span>Create NFT <wbr/>Collection</span></a></li><li><a href="#md:swap-tokens"><span>Swap <wbr/>Tokens</span></a></li><li><a href="#md:lend-tokens"><span>Lend <wbr/>Tokens</span></a></li><li><a href="#md:stake-sol"><span>Stake SOL</span></a></li><li><a href="#md:send-an-spl-token-airdrop-via-zk-compression"><span>Send an SPL <wbr/>Token <wbr/>Airdrop via ZK <wbr/>Compression</span></a></li><li><a href="#md:fetch-price-data-from-pyth"><span>Fetch <wbr/>Price <wbr/>Data from <wbr/>Pyth</span></a></li></ul></li><li><a href="#md:dependencies"><span>Dependencies</span></a></li><li><a href="#md:contributing"><span>Contributing</span></a></li><li><a href="#md:license"><span>License</span></a></li><li><a href="#md:security"><span>Security</span></a></li></ul></div></details></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>
</div></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><details open class="tsd-accordion tsd-page-navigation"><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>On This Page</h3></summary><div class="tsd-accordion-details"><a href="#md:solana-agent-kit"><span>Solana <wbr/>Agent <wbr/>Kit</span></a><ul><li><a href="#md:🔧-core-blockchain-features"><span>🔧 <wbr/>Core <wbr/>Blockchain <wbr/>Features</span></a></li><li><a href="#md:🤖-ai-integration-features"><span>🤖 AI <wbr/>Integration <wbr/>Features</span></a></li><li><a href="#md:📦-installation"><span>📦 <wbr/>Installation</span></a></li><li><a href="#md:quick-start"><span>Quick <wbr/>Start</span></a></li><li><a href="#md:usage-examples"><span>Usage <wbr/>Examples</span></a></li><li><ul><li><a href="#md:deploy-a-new-token"><span>Deploy a <wbr/>New <wbr/>Token</span></a></li><li><a href="#md:create-nft-collection"><span>Create NFT <wbr/>Collection</span></a></li><li><a href="#md:swap-tokens"><span>Swap <wbr/>Tokens</span></a></li><li><a href="#md:lend-tokens"><span>Lend <wbr/>Tokens</span></a></li><li><a href="#md:stake-sol"><span>Stake SOL</span></a></li><li><a href="#md:send-an-spl-token-airdrop-via-zk-compression"><span>Send an SPL <wbr/>Token <wbr/>Airdrop via ZK <wbr/>Compression</span></a></li><li><a href="#md:fetch-price-data-from-pyth"><span>Fetch <wbr/>Price <wbr/>Data from <wbr/>Pyth</span></a></li></ul></li><li><a href="#md:examples"><span>Examples</span></a></li><li><ul><li><a href="#md:langgraph-multi-agent-system"><span>Lang<wbr/>Graph <wbr/>Multi-<wbr/>Agent <wbr/>System</span></a></li></ul></li><li><a href="#md:dependencies"><span>Dependencies</span></a></li><li><a href="#md:contributing"><span>Contributing</span></a></li><li><a href="#md:contributors"><span>Contributors</span></a></li><li><a href="#md:star-history"><span>Star <wbr/>History</span></a></li><li><a href="#md:license"><span>License</span></a></li><li><a href="#md:security"><span>Security</span></a></li></ul></div></details></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>

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

File diff suppressed because one or more lines are too long

View File

@@ -3,6 +3,7 @@
<a href="interfaces/CollectionOptions.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>Collection<wbr/>Options</span></a>
<a href="interfaces/Creator.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>Creator</span></a>
<a href="interfaces/FetchPriceResponse.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>Fetch<wbr/>Price<wbr/>Response</span></a>
<a href="interfaces/GibworkCreateTaskReponse.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>Gibwork<wbr/>Create<wbr/>Task<wbr/>Reponse</span></a>
<a href="interfaces/JupiterTokenData.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>Jupiter<wbr/>Token<wbr/>Data</span></a>
<a href="interfaces/LuloAccountDetailsResponse.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>Lulo<wbr/>Account<wbr/>Details<wbr/>Response</span></a>
<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>

View File

@@ -0,0 +1,4 @@
OPENAI_API_KEY=
RPC_URL=
SOLANA_PRIVATE_KEY=
TAVILY_API_KEY= #Optional: for search functionality

11
examples/agent-kit-langgraph/.gitignore vendored Normal file
View File

@@ -0,0 +1,11 @@
# Dependencies
node_modules/
package-lock.json
# Environment variables
.env
.env.local
# Build output
dist/
build/

View File

@@ -0,0 +1,87 @@
# Agent Kit LangGraph Example
This example demonstrates how to build an advanced Solana agent using LangGraph and the Solana Agent Kit. It showcases a multi-agent system that can handle various Solana-related tasks through a directed workflow.
![Solana Agent Kit LangGraph Architecture](./assets/architecture.png)
## Features
- Multi-agent architecture using LangGraph's StateGraph
- Specialized agents for different tasks:
- General purpose agent for basic queries (with optional Tavily search integration)
- Transfer/Swap agent for transaction operations
- Read agent for blockchain data queries
- Manager agent for routing and orchestration
- Environment-based configuration
- TypeScript implementation with full type safety
## Prerequisites
- Node.js (v16 or higher)
- pnpm package manager
- Solana development environment
## Installation
1. Clone the repository and navigate to the example directory:
```bash
cd examples/agent-kit-langgraph
```
2. Install dependencies:
```bash
pnpm install
```
3. Configure environment variables:
```bash
cp .env.example .env
```
Edit the `.env` file with your configuration:
- Add your OpenAI API key
- Add your Tavily API key (optional, enables web search capabilities)
- Configure any other required environment variables
## Project Structure
```
src/
├── agents/ # Individual agent implementations
├── helper/ # Helper utilities and examples
├── prompts/ # Agent prompts and templates
├── tools/ # Custom tools for agents
└── utils/ # Utility functions and configurations
```
## Usage
To run the example:
```bash
pnpm dev src/index.ts
```
The example demonstrates a workflow where:
1. The manager agent receives the initial query
2. Based on the query type, it routes to the appropriate specialized agent:
- General queries → Generalist Agent
- Transfer/Swap operations → TransferSwap Agent
- Blockchain data queries → Read Agent
## Dependencies
- `@langchain/community`: LangChain community tools and utilities
- Includes Tavily search integration for enhanced query responses
- `@langchain/core`: Core LangChain functionality
- `@langchain/langgraph`: Graph-based agent workflows
- `solana-agent-kit`: Solana Agent Kit for blockchain interactions
- `zod`: Runtime type checking
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
ISC License

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

View File

@@ -0,0 +1,9 @@
{
"node_version": "20",
"dockerfile_lines": [],
"dependencies": ["."],
"graphs": {
"solanaAgent": "./src/index.ts:graph"
},
"env": ".env"
}

View File

@@ -0,0 +1,31 @@
{
"name": "agent-kit-langgraph",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "tsx"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@langchain/community": "^0.3.20",
"@langchain/core": "^0.3.26",
"@langchain/langgraph": "^0.2.36",
"dotenv": "^16.4.7",
"solana-agent-kit": "^1.3.0",
"zod": "^3.24.1"
},
"devDependencies": {
"ts-node": "^10.9.2",
"tsx": "^4.19.2",
"typescript": "^5.0.0"
},
"ts-node": {
"esm": true,
"experimentalSpecifierResolution": "node"
}
}

4462
examples/agent-kit-langgraph/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { gpt4o } from "../utils/model";
import { solanaAgentState } from "../utils/state";
import { TavilySearchResults } from "@langchain/community/tools/tavily_search";
// Initialize tools array
const searchTools = [];
// Only add Tavily search if API key is available
if (process.env.TAVILY_API_KEY) {
searchTools.push(new TavilySearchResults());
}
const generalAgent = createReactAgent({
llm: gpt4o,
tools: searchTools,
});
export const generalistNode = async (state: typeof solanaAgentState.State) => {
const { messages } = state;
const result = await generalAgent.invoke({ messages });
return { messages: [...result.messages] };
};

View File

@@ -0,0 +1,23 @@
import { prompt, parser } from "../prompts/manager";
import { RunnableSequence } from "@langchain/core/runnables";
import { solanaAgentState } from "../utils/state";
import { gpt4o } from "../utils/model";
const chain = RunnableSequence.from([prompt, gpt4o, parser]);
export const managerNode = async (state: typeof solanaAgentState.State) => {
const { messages } = state;
const result = await chain.invoke({
formatInstructions: parser.getFormatInstructions(),
messages: messages,
});
const { isSolanaReadQuery, isSolanaWriteQuery, isGeneralQuery } = result;
return {
isSolanaReadQuery,
isSolanaWriteQuery,
isGeneralQuery,
};
};

View File

@@ -0,0 +1,21 @@
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { gpt4o } from "../utils/model";
import { solanaAgentState } from "../utils/state";
import { agentKit } from "../utils/solanaAgent";
import {
SolanaBalanceTool,
SolanaFetchPriceTool,
} from "solana-agent-kit/dist/langchain";
const readAgent = createReactAgent({
llm: gpt4o,
tools: [new SolanaBalanceTool(agentKit), new SolanaFetchPriceTool(agentKit)],
});
export const readNode = async (state: typeof solanaAgentState.State) => {
const { messages } = state;
const result = await readAgent.invoke({ messages });
return { messages: [...result.messages] };
};

View File

@@ -0,0 +1,25 @@
import { gpt4o } from "../utils/model";
import { agentKit } from "../utils/solanaAgent";
import { solanaAgentState } from "../utils/state";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { SolanaTransferTool } from "solana-agent-kit/dist/langchain";
import { transferSwapPrompt } from "../prompts/transferSwap";
import { swapTool } from "../tools/swap";
const transferOrSwapAgent = createReactAgent({
stateModifier: transferSwapPrompt,
llm: gpt4o,
tools: [new SolanaTransferTool(agentKit), swapTool],
});
export const transferSwapNode = async (
state: typeof solanaAgentState.State,
) => {
const { messages } = state;
const result = await transferOrSwapAgent.invoke({
messages,
});
return result;
};

View File

@@ -0,0 +1,9 @@
import { HumanMessage } from "@langchain/core/messages";
export const generalQuestion = [
new HumanMessage("Who is the president of Ecuador?"),
];
export const solanaReadQuery = [new HumanMessage("what is the price of SOL")];
export const solanaWriteQuery = [new HumanMessage("swap 0.1 usdc to sol")];

View File

@@ -0,0 +1,50 @@
export const tokenList = [
{
name: "USDC",
ticker: "USDC",
decimal: 6,
mintAddress: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
},
{
name: "USDT",
ticker: "USDT",
decimal: 6,
mintAddress: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB",
},
{
name: "USDS",
ticker: "USDS",
decimal: 6,
mintAddress: "USDSwr9ApdHk5bvJKMjzff41FfuX8bSxdKcR81vTwcA",
},
{
name: "SOL",
ticker: "SOL",
decimal: 9,
mintAddress: "So11111111111111111111111111111111111111112",
},
{
name: "jitoSOL",
ticker: "jitoSOL",
decimal: 9,
mintAddress: "J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn",
},
{
name: "bSOL",
ticker: "bSOL",
decimal: 9,
mintAddress: "bSo13r4TkiE4KumL71LsHTPpL2euBYLFx6h9HP3piy1",
},
{
name: "mSOL",
ticker: "mSOL",
decimal: 9,
mintAddress: "mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So",
},
{
name: "BONK",
ticker: "BONK",
decimal: 9,
mintAddress: "DezXAZ8z7PnrnRJjz3wXBoRgixCa6xjnB7YaB1pPB263",
},
];

View File

@@ -0,0 +1,28 @@
import { StateGraph } from "@langchain/langgraph";
import { solanaAgentState } from "./utils/state";
import { generalistNode } from "./agents/generalAgent";
import { transferSwapNode } from "./agents/transferOrSwap";
import { managerNode } from "./agents/manager";
import { readNode } from "./agents/readAgent";
import { START, END } from "@langchain/langgraph";
import { managerRouter } from "./utils/route";
import { HumanMessage } from "@langchain/core/messages";
const workflow = new StateGraph(solanaAgentState)
.addNode("generalist", generalistNode)
.addNode("manager", managerNode)
.addNode("transferSwap", transferSwapNode)
.addNode("read", readNode)
.addEdge(START, "manager")
.addConditionalEdges("manager", managerRouter)
.addEdge("generalist", END)
.addEdge("transferSwap", END)
.addEdge("read", END);
export const graph = workflow.compile();
const result = await graph.invoke({
messages: [new HumanMessage("what is the price of SOL")],
});
console.log(result);

View File

@@ -0,0 +1,48 @@
import { StructuredOutputParser } from "@langchain/core/output_parsers";
import { z } from "zod";
import { PromptTemplate } from "@langchain/core/prompts";
export const parser = StructuredOutputParser.fromZodSchema(
z.object({
isSolanaReadQuery: z
.boolean()
.describe("Query requires reading data from Solana blockchain"),
isSolanaWriteQuery: z
.boolean()
.describe("Query requires writing/modifying data on Solana blockchain"),
isGeneralQuery: z
.boolean()
.describe("Query is about non-blockchain topics"),
}),
);
export const prompt = PromptTemplate.fromTemplate(
`
You are the Chief Routing Officer for a multi-blockchain agent network. Your role is to:
1. Analyze and classify incoming queries
2. Determine if the query requires Solana read operations, write operations, or is general
Format your response according to:
{formatInstructions}
Classification Guidelines:
- Solana Read Operations include:
* Checking account balances
* Viewing NFT metadata
* Getting program data
* Querying transaction history
* Checking token prices or holdings
- Solana Write Operations include:
* Creating or updating programs
* Sending tokens or SOL
* Minting NFTs
* Creating accounts
* Any transaction that modifies blockchain state
- General queries include:
* Non-blockchain topics
* Internet searches
* General knowledge questions
\n {messages} \n
`,
);

View File

@@ -0,0 +1,43 @@
import {
ChatPromptTemplate,
MessagesPlaceholder,
} from "@langchain/core/prompts";
import { tokenList } from "../helper/tokenList";
// Convert token list to a more readable format for the prompt
const formattedTokenList = tokenList
.map(
(token) =>
`- ${token.name} (${token.ticker}) - Decimals: ${token.decimal} - Address: ${token.mintAddress}`,
)
.join("\n ");
export const transferSwapPrompt = ChatPromptTemplate.fromMessages([
[
"system",
`You are an agent that is an expert in Solana transactions, specialized in token transfers and swaps. You can execute these transactions using the available tools based on user input.
When processing token amounts:
1. Use EXACTLY the decimal amount specified by the user without any modifications
2. Do not round or adjust the numbers
3. Maintain precise decimal places as provided in the user input
For transfers:
- User must specify the token, amount, and recipient address
- The same token will be used for input and output
For swaps:
- User must specify the input token, output token, and amount to swap
- Input and output tokens must be different
- Select tokens from this list of supported tokens:
${formattedTokenList}
Example amounts:
If you say "0.01 SOL", I will use exactly 0.01 (not 0.010 or 0.0100)
If you say "1.234 USDC", I will use exactly 1.234 (not 1.23 or 1.2340)
For swaps, have the slippage be 200 bps
`,
],
new MessagesPlaceholder("messages"),
]);

View File

@@ -0,0 +1,45 @@
import { agentKit } from "../utils/solanaAgent";
import { tool } from "@langchain/core/tools";
import { z } from "zod";
import { PublicKey } from "@solana/web3.js";
export const swapTool = tool(
async ({ outputMint, inputAmount, inputMint, inputDecimal }) => {
try {
const inputAmountWithDecimals = inputAmount * 10 ** inputDecimal;
const outputMintAddress = new PublicKey(outputMint);
const inputMintAddress = new PublicKey(inputMint);
console.log(inputAmountWithDecimals, outputMintAddress, inputMintAddress);
const tx = await agentKit.trade(
outputMintAddress,
inputAmountWithDecimals,
inputMintAddress,
200,
);
return tx;
} catch (error) {
console.error(error);
return "error";
}
},
{
name: "swap",
description:
"call to swap/trade tokens from one token to the other using Jupiter exchange",
schema: z.object({
outputMint: z
.string()
.describe("The mint address of destination token to be swapped to"),
inputAmount: z
.number()
.describe(
"the input amount of the token to be swapped without adding any decimals",
),
inputMint: z.string().describe("The mint address of the origin token "),
inputDecimal: z
.number()
.describe("The decimal of the input token that is being traded"),
}),
},
);

View File

@@ -0,0 +1,12 @@
import { ChatOpenAI } from "@langchain/openai";
import "dotenv/config";
export const gpt4o = new ChatOpenAI({
modelName: "gpt-4o",
apiKey: process.env.OPENAI_API_KEY!,
});
export const gpt4oMini = new ChatOpenAI({
modelName: "gpt-4o-mini",
apiKey: process.env.OPENAI_API_KEY!,
});

View File

@@ -0,0 +1,16 @@
import { solanaAgentState } from "./state";
import { END } from "@langchain/langgraph";
export const managerRouter = (state: typeof solanaAgentState.State) => {
const { isSolanaReadQuery, isSolanaWriteQuery, isGeneralQuery } = state;
if (isGeneralQuery) {
return "generalist";
} else if (isSolanaWriteQuery) {
return "transferSwap";
} else if (isSolanaReadQuery) {
return "read";
} else {
return END;
}
};

View File

@@ -0,0 +1,9 @@
import { SolanaAgentKit, createSolanaTools } from "solana-agent-kit";
export const agentKit = new SolanaAgentKit(
process.env.SOLANA_PRIVATE_KEY!,
process.env.RPC_URL!,
process.env.OPENAI_API_KEY!,
);
export const solanaTools = createSolanaTools(agentKit);

View File

@@ -0,0 +1,25 @@
import { Annotation } from "@langchain/langgraph";
import { BaseMessage } from "@langchain/core/messages";
import { messagesStateReducer } from "@langchain/langgraph";
export const solanaAgentState = Annotation.Root({
messages: Annotation<BaseMessage[]>({
reducer: messagesStateReducer,
default: () => [],
}),
isSolanaReadQuery: Annotation<boolean>({
reducer: (x, y) => y ?? x ?? false,
default: () => false,
}),
isSolanaWriteQuery: Annotation<boolean>({
reducer: (x, y) => y ?? x ?? false,
default: () => false,
}),
isGeneralQuery: Annotation<boolean>({
reducer: (x, y) => y ?? x ?? false,
default: () => false,
}),
});

View File

@@ -0,0 +1,12 @@
{
"compilerOptions": {
"target": "es2022",
"module": "es2022",
"moduleResolution": "node",
"esModuleInterop": true,
"strict": true,
"outDir": "./dist"
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}

View File

@@ -0,0 +1,4 @@
OPENAI_API_KEY=
RPC_URL=
SOLANA_PRIVATE_KEY=
POSTGRES_DB_URL=

View File

@@ -0,0 +1,66 @@
# Persistent Agent with PostgreSQL
This example showcases a persistent agent that retains memory across sessions using a PostgreSQL database. It ensures that the agent can remember previous conversations even after being restarted, enhancing the user experience in applications requiring long-term context retention.
[Reference](https://langchain-ai.github.io/langgraphjs/reference/classes/checkpoint_postgres.PostgresSaver.html)
## Key Features
- **Persistent Memory**: The agent stores chat history in a PostgreSQL database, allowing it to remember past interactions across sessions.
- **Seamless Integration**: Designed to integrate smoothly with existing setups.
- **Scalable Solution**: Ideal for applications requiring long-term memory capabilities.
## Prerequisites
To use this feature, ensure you have the following:
1. **PostgreSQL Database URL**: Create and host ur PostgreSQL databse and enter the URL. It will be of the format "postgresql://user:password@localhost:5432/db"
## Before applying persistance
```
Available modes:
1. chat
- Interactive chat mode
2. auto
- Autonomous action mode
Choose a mode (enter number or name: 1
Starting chat mode... Type 'exit' to end.
Prompt: i am arpit
Hello Arpit! How can I assist you today?
Prompt: ^С
® arpitsingh Mac persistance-agent & ts-node index.ts
Starting Agent...
Available modes:
1. chat
- Interactive chat mode
2. auto
- Autonomous action mode
Choose a mode (enter number or name): 1
Starting chat mode... Type 'exit' to end.
Prompt: do u know my name
I don't know your name yet. If you'd like, you can share it.
```
## After applying persistence
```
Available modes:
1. chat
- Interactive chat mode
2. auto
- Autonomous action mode
Choose a mode (enter number or name: 1
Starting chat mode... Type 'exit' to end.
Prompt: i am arpit
Hello Arpit! How can I assist you today?
Prompt: ^С
® arpitsingh Mac persistance-agent & ts-node index.ts
Starting Agent...
Available modes:
1. chat
- Interactive chat mode
2. auto
- Autonomous action mode
Choose a mode (enter number or name): 1
Starting chat mode... Type 'exit' to end.
Prompt: do u know my name
Yes, you mentioned that your name is Arpit. How can I help you today?
```

View File

@@ -0,0 +1,223 @@
import { SolanaAgentKit, createSolanaTools } from "solana-agent-kit";
import { HumanMessage } from "@langchain/core/messages";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { ChatOpenAI } from "@langchain/openai";
import * as dotenv from "dotenv";
import * as fs from "fs";
import * as readline from "readline";
import { PostgresSaver } from "@langchain/langgraph-checkpoint-postgres";
dotenv.config();
const checkpointer = PostgresSaver.fromConnString(process.env.POSTGRES_DB_URL!);
function validateEnvironment(): void {
const missingVars: string[] = [];
const requiredVars = [
"OPENAI_API_KEY",
"RPC_URL",
"SOLANA_PRIVATE_KEY",
"POSTGRES_DB_URL",
];
requiredVars.forEach((varName) => {
if (!process.env[varName]) {
missingVars.push(varName);
}
});
if (missingVars.length > 0) {
console.error("Error: Required environment variables are not set");
missingVars.forEach((varName) => {
console.error(`${varName}=your_${varName.toLowerCase()}_here`);
});
process.exit(1);
}
}
validateEnvironment();
const WALLET_DATA_FILE = "wallet_data.txt";
async function initializeAgent() {
try {
const llm = new ChatOpenAI({
modelName: "gpt-4o-mini",
temperature: 0.7,
});
let walletDataStr: string | null = null;
if (fs.existsSync(WALLET_DATA_FILE)) {
try {
walletDataStr = fs.readFileSync(WALLET_DATA_FILE, "utf8");
} catch (error) {
console.error("Error reading wallet data:", error);
}
}
const solanaAgent = new SolanaAgentKit(
process.env.SOLANA_PRIVATE_KEY!,
process.env.RPC_URL,
process.env.OPENAI_API_KEY!,
);
const tools = createSolanaTools(solanaAgent);
await checkpointer.setup();
const config = { configurable: { thread_id: "Solana Agent Kit!" } };
const agent = createReactAgent({
llm,
tools,
checkpointSaver: checkpointer,
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.
`,
});
if (walletDataStr) {
fs.writeFileSync(WALLET_DATA_FILE, walletDataStr);
}
return { agent, config };
} catch (error) {
console.error("Failed to initialize agent:", error);
throw error;
}
}
async function runAutonomousMode(agent: any, config: any, interval = 10) {
console.log("Starting autonomous mode...");
while (true) {
try {
const thought =
"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,
);
for await (const chunk of stream) {
if ("agent" in chunk) {
console.log(chunk.agent.messages[0].content);
} else if ("tools" in chunk) {
console.log(chunk.tools.messages[0].content);
}
console.log("-------------------");
}
await new Promise((resolve) => setTimeout(resolve, interval * 1000));
} catch (error) {
if (error instanceof Error) {
console.error("Error:", error.message);
}
process.exit(1);
}
}
}
async function runChatMode(agent: any, config: any) {
console.log("Starting chat mode... Type 'exit' to end.");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const question = (prompt: string): Promise<string> =>
new Promise((resolve) => rl.question(prompt, resolve));
try {
while (true) {
const userInput = await question("\nPrompt: ");
if (userInput.toLowerCase() === "exit") {
break;
}
const stream = await agent.stream(
{ messages: [new HumanMessage(userInput)] },
config,
);
for await (const chunk of stream) {
if ("agent" in chunk) {
console.log(chunk.agent.messages[0].content);
} else if ("tools" in chunk) {
console.log(chunk.tools.messages[0].content);
}
console.log("-------------------");
}
}
} catch (error) {
if (error instanceof Error) {
console.error("Error:", error.message);
}
process.exit(1);
} finally {
rl.close();
}
}
async function chooseMode(): Promise<"chat" | "auto"> {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const question = (prompt: string): Promise<string> =>
new Promise((resolve) => rl.question(prompt, resolve));
while (true) {
console.log("\nAvailable modes:");
console.log("1. chat - Interactive chat mode");
console.log("2. auto - Autonomous action mode");
const choice = (await question("\nChoose a mode (enter number or name): "))
.toLowerCase()
.trim();
rl.close();
if (choice === "1" || choice === "chat") {
return "chat";
} else if (choice === "2" || choice === "auto") {
return "auto";
}
console.log("Invalid choice. Please try again.");
}
}
async function main() {
try {
console.log("Starting Agent...");
const { agent, config } = await initializeAgent();
const mode = await chooseMode();
if (mode === "chat") {
await runChatMode(agent, config);
} else {
await runAutonomousMode(agent, config);
}
} catch (error) {
if (error instanceof Error) {
console.error("Error:", error.message);
}
process.exit(1);
}
}
if (require.main === module) {
main().catch((error) => {
console.error("Fatal error:", error);
process.exit(1);
});
}

View File

@@ -0,0 +1,16 @@
{
"name": "persistance-agent",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@langchain/langgraph-checkpoint-postgres": "^0.0.2",
"solana-agent-kit": "^1.3.0"
}
}

3095
examples/persistance-agent/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
OPENAI_API_KEY=
RPC_URL=
SOLANA_PRIVATE_KEY=
TELEGRAM_BOT_TOKEN=

41
examples/tg-bot-starter/.gitignore vendored Normal file
View File

@@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts

View File

@@ -0,0 +1,22 @@
# Telegram Bot Starter with Solana Agent Kit
This example showcases how we can make a telegram bot with the Solana Agent Kit by Send AI.
## How to get the telegram bot token
You can check [here](https://help.zoho.com/portal/en/kb/desk/support-channels/instant-messaging/telegram/articles/telegram-integration-with-zoho-desk#How_to_find_a_token_for_an_existing_Telegram_Bot) how you can obtain a bot token for your telegram bot.
## How to setup the project
- Set env variables
- Run ``` pnpm install ```
- Run ``` pnpm run dev ```
- Run ``` ngrok http 3000 ```
- With the URL you got from ngrok, where your bot is hosted at https://yourUrl.app/api/bot
- Set the webhook by using this command ``` curl https://api.telegram.org/bot<telegram_bot_token>/setWebhook?url=https://<your-deployment-url>.app/api/bot ``` or simply clicking on that link.
- You can host it on Vercel too as we have used NextJs in this.
- Once the URL is set successfully, you will see this ``` {"ok":true,"result":true,"description":"Webhook was set"} ```
Done!!! Congrtulations you just hosted Solana Agent Kit on a Telegram bot.

View File

@@ -0,0 +1,7 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
};
export default nextConfig;

View File

@@ -0,0 +1,31 @@
{
"name": "tg-bot-starter",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@langchain/core": "^0.3.26",
"@langchain/langgraph": "^0.2.36",
"@langchain/openai": "^0.3.16",
"grammy": "^1.33.0",
"messages": "link:@langchain/core/messages",
"next": "15.1.3",
"prebuilt": "link:@langchain/langgraph/prebuilt",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"solana-agent-kit": "^1.3.0"
},
"devDependencies": {
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5"
}
}

4296
examples/tg-bot-starter/pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
},
};
export default config;

View File

@@ -0,0 +1 @@
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 391 B

View File

@@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 128 B

View File

@@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>

After

Width:  |  Height:  |  Size: 385 B

View File

@@ -0,0 +1,86 @@
export const dynamic = 'force-dynamic';
export const fetchCache = 'force-no-store';
export const maxDuration = 300;
import { Bot, webhookCallback } from 'grammy';
import { SolanaAgentKit, createSolanaTools } from "solana-agent-kit";
import { ChatOpenAI } from "@langchain/openai";
import { MemorySaver } from "@langchain/langgraph";
import { createReactAgent } from "@langchain/langgraph/prebuilt";
import { HumanMessage } from "@langchain/core/messages";
const token = process.env.TELEGRAM_BOT_TOKEN;
if (!token) throw new Error('TELEGRAM_BOT_TOKEN environment variable not found.');
const bot = new Bot(token);
async function initializeAgent(userId: string) {
try {
const llm = new ChatOpenAI({
modelName: "gpt-4o-mini",
temperature: 0.7,
});
const solanaKit = new SolanaAgentKit(
process.env.SOLANA_PRIVATE_KEY!,
process.env.RPC_URL,
process.env.OPENAI_API_KEY!
);
const tools = createSolanaTools(solanaKit);
const memory = new MemorySaver();
const config = { configurable: { thread_id: userId } };
const agent = createReactAgent({
llm,
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.
`,
});
return { agent, config };
} catch (error) {
console.error("Failed to initialize agent:", error);
throw error;
}
}
// Telegram bot handler
bot.on('message:text', async (ctx:any) => {
const userId = ctx.from?.id.toString();
if (!userId) return;
const {agent, config} = await initializeAgent(userId);
const stream = await agent.stream({ messages: [new HumanMessage(ctx.message.text)] }, config);
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 20000));
try {
for await (const chunk of await Promise.race([stream, timeoutPromise]) as AsyncIterable<{ agent?: any; tools?: any }>) {
if ("agent" in chunk) {
if (chunk.agent.messages[0].content) await ctx.reply(String(chunk.agent.messages[0].content));
}
}
} catch (error: any) {
if (error.message === 'Timeout') {
await ctx.reply("I'm sorry, the operation took too long and timed out. Please try again.");
} else {
console.error("Error processing stream:", error);
await ctx.reply("I'm sorry, an error occurred while processing your request.");
}
}
});
// Export webhook handler
export const POST = async (req: Request) => {
// Mark the function as a background function for Vercel
const headers = new Headers();
headers.set('x-vercel-background', 'true');
const handler = webhookCallback(bot, 'std/http'); // Use the correct callback
// Handle the incoming webhook request
return handler(req);
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -0,0 +1,21 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--background: #ffffff;
--foreground: #171717;
}
@media (prefers-color-scheme: dark) {
:root {
--background: #0a0a0a;
--foreground: #ededed;
}
}
body {
color: var(--foreground);
background: var(--background);
font-family: Arial, Helvetica, sans-serif;
}

View File

@@ -0,0 +1,34 @@
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
</body>
</html>
);
}

View File

@@ -0,0 +1,101 @@
import Image from "next/image";
export default function Home() {
return (
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
<main className="flex flex-col gap-8 row-start-2 items-center sm:items-start">
<Image
className="dark:invert"
src="/next.svg"
alt="Next.js logo"
width={180}
height={38}
priority
/>
<ol className="list-inside list-decimal text-sm text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
<li className="mb-2">
Get started by editing{" "}
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-semibold">
src/app/page.tsx
</code>
.
</li>
<li>Save and see your changes instantly.</li>
</ol>
<div className="flex gap-4 items-center flex-col sm:flex-row">
<a
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={20}
height={20}
/>
Deploy now
</a>
<a
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:min-w-44"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Read our docs
</a>
</div>
</main>
<footer className="row-start-3 flex gap-6 flex-wrap items-center justify-center">
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/file.svg"
alt="File icon"
width={16}
height={16}
/>
Learn
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/window.svg"
alt="Window icon"
width={16}
height={16}
/>
Examples
</a>
<a
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
aria-hidden
src="/globe.svg"
alt="Globe icon"
width={16}
height={16}
/>
Go to nextjs.org
</a>
</footer>
</div>
);
}

View File

@@ -0,0 +1,18 @@
import type { Config } from "tailwindcss";
export default {
content: [
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
colors: {
background: "var(--background)",
foreground: "var(--foreground)",
},
},
},
plugins: [],
} satisfies Config;

View File

@@ -0,0 +1,27 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
}

View File

@@ -18,7 +18,7 @@ Extending the **Solana Agent Kit** with custom tools allows you to add specializ
Create a new TypeScript file in the `src/tools/` directory for your tool (e.g., `custom_tool.ts`).
### 2. Implement the Tool Class
> `src/langchain/index.ts`
```typescript:src/langchain/index.ts
import { Tool } from "langchain/tools";
import { SolanaAgentKit } from "../agent";
@@ -51,7 +51,7 @@ export class CustomTool extends Tool {
```
### 3. Add Supporting Functions to SolanaAgentKit
> `src/agent/index.ts`
```typescript:src/agent/index.ts
export class SolanaAgentKit {
// ... existing code ...
@@ -64,7 +64,7 @@ export class SolanaAgentKit {
```
### 4. Export the Tool
> `src/tools/index.ts`
```typescript:src/tools/index.ts
export * from "./request_faucet_funds";
export * from "./deploy_token";
@@ -72,7 +72,7 @@ export * from "./custom_tool"; // Add your new tool
```
### 5. Integrate with Agent
> `src/langchain/index.ts`
```typescript:src/langchain/index.ts
import { CustomTool } from "../tools/custom_tool";
@@ -104,6 +104,10 @@ if (customTool) {
const result = await customTool._call("your-input");
console.log(result);
}
// or alternatively
const result = await agent.customFunction("your-input"); // assuming you have a `customFunction` method in SolanaAgentKit
console.log(result);
```
## Best Practices
@@ -117,7 +121,7 @@ if (customTool) {
## Example: Token Price Fetching Tool
Here's a complete example of implementing a tool to fetch token prices:
> `src/tools/fetch_token_price.ts`
```typescript:src/tools/fetch_token_price.ts
import { Tool } from "langchain/tools";
import { SolanaAgentKit } from "../agent";
@@ -150,7 +154,7 @@ export class FetchTokenPriceTool extends Tool {
```
Add the supporting function to SolanaAgentKit:
> `src/agent/index.ts`
```typescript:src/agent/index.ts
export class SolanaAgentKit {
async getTokenPrice(tokenSymbol: string): Promise<number> {
@@ -170,6 +174,21 @@ export class SolanaAgentKit {
}
```
Then it can be used as such:
```typescript
import { SolanaAgentKit } from "solana-agent-kit";
const agent = new SolanaAgentKit(
"your-wallet-private-key-as-base58",
"https://api.mainnet-beta.solana.com",
"your-openai-api-key"
);
const result = await agent.getTokenPrice("SOL");
console.log(result);
```
## Need Help?
If you encounter any issues while implementing your custom tool:

View File

@@ -49,12 +49,12 @@ The project includes a test script located at `test/index.ts`. To execute the te
### Token Deployment
```typescript
import { deploy_token } from "solana-agent-kit";
import { SolanaAgentKit } from "solana-agent-kit";
const result = await deploy_token(
agent,
9, // decimals
1000000 // initial supply
const agent = new SolanaAgentKit("your_private_key");
const result = await agent.deployToken(
9, // decimals
);
console.log("Token Mint Address:", result.mint.toString());
@@ -62,9 +62,11 @@ console.log("Token Mint Address:", result.mint.toString());
### NFT Collection Creation
```typescript
import { deploy_collection } from "solana-agent-kit";
import { SolanaAgentKit } from "solana-agent-kit";
const collection = await deploy_collection(agent, {
const agent = new SolanaAgentKit("your_private_key");
const collection = await agent.deployCollection({
name: "My NFT Collection",
uri: "https://arweave.net/metadata.json",
royaltyBasisPoints: 500, // 5%

View File

@@ -1,6 +1,6 @@
{
"name": "solana-agent-kit",
"version": "1.2.0",
"version": "1.3.1",
"description": "connect any ai agents to solana protocols",
"main": "dist/index.js",
"types": "dist/index.d.ts",
@@ -14,7 +14,7 @@
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\""
},
"engines": {
"node": ">=23.1.0",
"node": ">=22.0.0",
"pnpm": ">=8.0.0"
},
"keywords": [],
@@ -23,9 +23,9 @@
"dependencies": {
"@bonfida/spl-name-service": "^3.0.7",
"@coral-xyz/anchor": "0.29",
"@langchain/core": "^0.3.18",
"@langchain/core": "^0.3.26",
"@langchain/groq": "^0.1.2",
"@langchain/langgraph": "^0.2.27",
"@langchain/langgraph": "^0.2.34",
"@langchain/openai": "^0.3.13",
"@lightprotocol/compressed-token": "^0.17.1",
"@lightprotocol/stateless.js": "^0.17.1",
@@ -42,6 +42,7 @@
"@raydium-io/raydium-sdk-v2": "0.1.95-alpha",
"@solana/spl-token": "^0.4.9",
"@solana/web3.js": "^1.95.4",
"@tiplink/api": "^0.3.1",
"bn.js": "^5.2.1",
"bs58": "^6.0.0",
"chai": "^5.1.2",
@@ -56,13 +57,13 @@
"@types/bn.js": "^5.1.5",
"@types/chai": "^5.0.1",
"@types/node": "^22.9.0",
"ts-node": "^10.9.2",
"typescript": "^5.7.2",
"@typescript-eslint/eslint-plugin": "^7.0.0",
"@typescript-eslint/parser": "^7.0.0",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
"@typescript-eslint/eslint-plugin": "^7.0.0",
"@typescript-eslint/parser": "^7.0.0",
"prettier": "^3.2.5"
"prettier": "^3.2.5",
"ts-node": "^10.9.2",
"typescript": "^5.7.2"
}
}

220
pnpm-lock.yaml generated
View File

@@ -15,14 +15,19 @@ importers:
specifier: '0.29'
version: 0.29.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)
'@langchain/core':
specifier: ^0.3.18
specifier: ^0.3.26
version: 0.3.26(openai@4.77.0(zod@3.24.1))
'@langchain/groq':
specifier: ^0.1.2
version: 0.1.2(@langchain/core@0.3.26(openai@4.77.0(zod@3.24.1)))
'@langchain/langgraph':
<<<<<<< HEAD
specifier: ^0.2.27
version: 0.2.36(@langchain/core@0.3.26(openai@4.77.0(zod@3.24.1)))
=======
specifier: ^0.2.34
version: 0.2.34(@langchain/core@0.3.26(openai@4.77.0(zod@3.24.1)))
>>>>>>> main
'@langchain/openai':
specifier: ^0.3.13
version: 0.3.16(@langchain/core@0.3.26(openai@4.77.0(zod@3.24.1)))
@@ -71,6 +76,9 @@ importers:
'@solana/web3.js':
specifier: ^1.95.4
version: 1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)
'@tiplink/api':
specifier: ^0.3.1
version: 0.3.1(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(sodium-native@3.4.1)(utf-8-validate@5.0.10)
bn.js:
specifier: ^5.2.1
version: 5.2.1
@@ -578,6 +586,12 @@ packages:
peerDependencies:
'@solana/web3.js': ^1.95.3
'@solana/spl-token@0.3.11':
resolution: {integrity: sha512-bvohO3rIMSVL24Pb+I4EYTJ6cL82eFpInEXD/I8K8upOGjpqHsKUoAempR/RnUlI1qSFNyFlWJfu6MNUgfbCQQ==}
engines: {node: '>=16'}
peerDependencies:
'@solana/web3.js': ^1.88.0
'@solana/spl-token@0.4.6':
resolution: {integrity: sha512-1nCnUqfHVtdguFciVWaY/RKcQz1IF4b31jnKgAmjU9QVN1q7dRUkTEWJZgTYIEtsULjVnC9jRqlhgGN39WbKKA==}
engines: {node: '>=16'}
@@ -609,6 +623,9 @@ packages:
'@swc/helpers@0.5.15':
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
'@tiplink/api@0.3.1':
resolution: {integrity: sha512-HjnXethjKOHTYT0IP1BewlMS7wZJ+hsoDgRa6jA1cNvxvwQjE1WHOyvOUPpAi+DJDw4P4/omFtyHr7dwLfnB/g==}
'@tsconfig/node10@1.0.11':
resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==}
@@ -822,6 +839,10 @@ packages:
base64-js@1.5.1:
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
big-integer@1.6.52:
resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==}
engines: {node: '>=0.6'}
big.js@6.2.2:
resolution: {integrity: sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==}
@@ -870,6 +891,9 @@ packages:
resolution: {integrity: sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA==}
engines: {node: '>=4.5'}
buffer@5.7.1:
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
buffer@6.0.3:
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
@@ -1352,6 +1376,9 @@ packages:
resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==}
engines: {node: '>= 0.4'}
is-typedarray@1.0.0:
resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==}
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
@@ -1464,6 +1491,18 @@ packages:
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
engines: {node: '>= 0.8.0'}
libsodium-sumo@0.7.15:
resolution: {integrity: sha512-5tPmqPmq8T8Nikpm1Nqj0hBHvsLFCXvdhBFV7SGOitQPZAA6jso8XoL0r4L7vmfKXr486fiQInvErHtEvizFMw==}
libsodium-wrappers-sumo@0.7.15:
resolution: {integrity: sha512-aSWY8wKDZh5TC7rMvEdTHoyppVq/1dTSAeAR7H6pzd6QRT3vQWcT5pGwCotLcpPEOLXX6VvqihSPkpEhYAjANA==}
libsodium-wrappers@0.7.15:
resolution: {integrity: sha512-E4anqJQwcfiC6+Yrl01C1m8p99wEhLmJSs0VQqST66SbQXXBoaJY0pF4BNjRYa/sOQAxx6lXAaAFIlx+15tXJQ==}
libsodium@0.7.15:
resolution: {integrity: sha512-sZwRknt/tUpE2AwzHq3jEyUU5uvIZHtSssktXq7owd++3CSgn8RGrv6UZJJBpP7+iBghBqe7Z06/2M31rI2NKw==}
linkify-it@5.0.0:
resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
@@ -1554,6 +1593,11 @@ packages:
resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==}
hasBin: true
nanoid@3.3.8:
resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
natural-compare@1.4.0:
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
@@ -1669,6 +1713,9 @@ packages:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
poly1305-js@0.4.4:
resolution: {integrity: sha512-5B6/S+vg5AOr66wJDkh5LOpU/F3EKANDy4VXKsNZLXea1uCy6CiOWOZ3VhcC0nYdhE7vJUMcLxqcVlrv2g/+Rg==}
possible-typed-array-names@1.0.0:
resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==}
engines: {node: '>= 0.4'}
@@ -1768,6 +1815,14 @@ packages:
snake-case@3.0.4:
resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==}
sodium-native@3.4.1:
resolution: {integrity: sha512-PaNN/roiFWzVVTL6OqjzYct38NSXewdl2wz8SRB51Br/MLIJPrbM3XexhVWkq7D3UWMysfrhKVf1v1phZq6MeQ==}
sodium-plus@0.9.0:
resolution: {integrity: sha512-WWKxrd81qDL7C1A10yxNmZ135yovEZuIRnZ/BIf/FcajYBupbKbPdgzwlusPHLVxkMDDamcarq9PxxRBUSqpCw==}
peerDependencies:
sodium-native: ^3.2.0
space-separated-tokens@2.0.2:
resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
@@ -1862,6 +1917,9 @@ packages:
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
tweetnacl-util@0.15.1:
resolution: {integrity: sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==}
tweetnacl@1.0.3:
resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==}
@@ -1873,6 +1931,9 @@ packages:
resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
engines: {node: '>=10'}
typedarray-to-buffer@3.1.5:
resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==}
typedoc@0.26.11:
resolution: {integrity: sha512-sFEgRRtrcDl2FxVP58Ze++ZK2UQAEvtvvH8rRlig1Ja3o7dDaMHmaBfvJmdGnNEFaLTpQsN8dpvZaTqJSu/Ugw==}
engines: {node: '>= 18'}
@@ -1880,6 +1941,11 @@ packages:
peerDependencies:
typescript: 4.6.x || 4.7.x || 4.8.x || 4.9.x || 5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x
typescript@4.9.5:
resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==}
engines: {node: '>=4.2.0'}
hasBin: true
typescript@5.7.2:
resolution: {integrity: sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==}
engines: {node: '>=14.17'}
@@ -2001,6 +2067,9 @@ packages:
utf-8-validate:
optional: true
xsalsa20@1.2.0:
resolution: {integrity: sha512-FIr/DEeoHfj7ftfylnoFt3rAIRoWXpx2AoDfrT2qD2wtp7Dp+COajvs/Icb7uHqRW9m60f5iXZwdsJJO3kvb7w==}
yaml@2.6.1:
resolution: {integrity: sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==}
engines: {node: '>= 14'}
@@ -2514,6 +2583,11 @@ snapshots:
'@solana/errors': 2.0.0-preview.4(typescript@5.7.2)
typescript: 5.7.2
'@solana/codecs-core@2.0.0-rc.1(typescript@4.9.5)':
dependencies:
'@solana/errors': 2.0.0-rc.1(typescript@4.9.5)
typescript: 4.9.5
'@solana/codecs-core@2.0.0-rc.1(typescript@5.7.2)':
dependencies:
'@solana/errors': 2.0.0-rc.1(typescript@5.7.2)
@@ -2532,6 +2606,13 @@ snapshots:
'@solana/errors': 2.0.0-preview.4(typescript@5.7.2)
typescript: 5.7.2
'@solana/codecs-data-structures@2.0.0-rc.1(typescript@4.9.5)':
dependencies:
'@solana/codecs-core': 2.0.0-rc.1(typescript@4.9.5)
'@solana/codecs-numbers': 2.0.0-rc.1(typescript@4.9.5)
'@solana/errors': 2.0.0-rc.1(typescript@4.9.5)
typescript: 4.9.5
'@solana/codecs-data-structures@2.0.0-rc.1(typescript@5.7.2)':
dependencies:
'@solana/codecs-core': 2.0.0-rc.1(typescript@5.7.2)
@@ -2550,6 +2631,12 @@ snapshots:
'@solana/errors': 2.0.0-preview.4(typescript@5.7.2)
typescript: 5.7.2
'@solana/codecs-numbers@2.0.0-rc.1(typescript@4.9.5)':
dependencies:
'@solana/codecs-core': 2.0.0-rc.1(typescript@4.9.5)
'@solana/errors': 2.0.0-rc.1(typescript@4.9.5)
typescript: 4.9.5
'@solana/codecs-numbers@2.0.0-rc.1(typescript@5.7.2)':
dependencies:
'@solana/codecs-core': 2.0.0-rc.1(typescript@5.7.2)
@@ -2571,6 +2658,14 @@ snapshots:
fastestsmallesttextencoderdecoder: 1.0.22
typescript: 5.7.2
'@solana/codecs-strings@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5)':
dependencies:
'@solana/codecs-core': 2.0.0-rc.1(typescript@4.9.5)
'@solana/codecs-numbers': 2.0.0-rc.1(typescript@4.9.5)
'@solana/errors': 2.0.0-rc.1(typescript@4.9.5)
fastestsmallesttextencoderdecoder: 1.0.22
typescript: 4.9.5
'@solana/codecs-strings@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)':
dependencies:
'@solana/codecs-core': 2.0.0-rc.1(typescript@5.7.2)
@@ -2600,6 +2695,17 @@ snapshots:
transitivePeerDependencies:
- fastestsmallesttextencoderdecoder
'@solana/codecs@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5)':
dependencies:
'@solana/codecs-core': 2.0.0-rc.1(typescript@4.9.5)
'@solana/codecs-data-structures': 2.0.0-rc.1(typescript@4.9.5)
'@solana/codecs-numbers': 2.0.0-rc.1(typescript@4.9.5)
'@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5)
'@solana/options': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5)
typescript: 4.9.5
transitivePeerDependencies:
- fastestsmallesttextencoderdecoder
'@solana/codecs@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)':
dependencies:
'@solana/codecs-core': 2.0.0-rc.1(typescript@5.7.2)
@@ -2622,6 +2728,12 @@ snapshots:
commander: 12.1.0
typescript: 5.7.2
'@solana/errors@2.0.0-rc.1(typescript@4.9.5)':
dependencies:
chalk: 5.4.0
commander: 12.1.0
typescript: 4.9.5
'@solana/errors@2.0.0-rc.1(typescript@5.7.2)':
dependencies:
chalk: 5.4.1
@@ -2644,6 +2756,17 @@ snapshots:
transitivePeerDependencies:
- fastestsmallesttextencoderdecoder
'@solana/options@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5)':
dependencies:
'@solana/codecs-core': 2.0.0-rc.1(typescript@4.9.5)
'@solana/codecs-data-structures': 2.0.0-rc.1(typescript@4.9.5)
'@solana/codecs-numbers': 2.0.0-rc.1(typescript@4.9.5)
'@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5)
'@solana/errors': 2.0.0-rc.1(typescript@4.9.5)
typescript: 4.9.5
transitivePeerDependencies:
- fastestsmallesttextencoderdecoder
'@solana/options@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)':
dependencies:
'@solana/codecs-core': 2.0.0-rc.1(typescript@5.7.2)
@@ -2688,6 +2811,14 @@ snapshots:
- fastestsmallesttextencoderdecoder
- typescript
'@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5)':
dependencies:
'@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5)
'@solana/web3.js': 1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)
transitivePeerDependencies:
- fastestsmallesttextencoderdecoder
- typescript
'@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)':
dependencies:
'@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.7.2)
@@ -2696,6 +2827,20 @@ snapshots:
- fastestsmallesttextencoderdecoder
- typescript
'@solana/spl-token@0.3.11(@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@4.9.5)(utf-8-validate@5.0.10)':
dependencies:
'@solana/buffer-layout': 4.0.1
'@solana/buffer-layout-utils': 0.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)
'@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@4.9.5)
'@solana/web3.js': 1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)
buffer: 6.0.3
transitivePeerDependencies:
- bufferutil
- encoding
- fastestsmallesttextencoderdecoder
- typescript
- utf-8-validate
'@solana/spl-token@0.4.6(@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)':
dependencies:
'@solana/buffer-layout': 4.0.1
@@ -2793,6 +2938,26 @@ snapshots:
dependencies:
tslib: 2.8.1
'@tiplink/api@0.3.1(bufferutil@4.0.8)(fastestsmallesttextencoderdecoder@1.0.22)(sodium-native@3.4.1)(utf-8-validate@5.0.10)':
dependencies:
'@coral-xyz/anchor': 0.29.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)
'@solana/spl-token': 0.3.11(@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@4.9.5)(utf-8-validate@5.0.10)
'@solana/web3.js': 1.98.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)
bs58: 5.0.0
libsodium: 0.7.15
libsodium-wrappers-sumo: 0.7.15
nanoid: 3.3.8
sodium-plus: 0.9.0(sodium-native@3.4.1)
tweetnacl: 1.0.3
tweetnacl-util: 0.15.1
typescript: 4.9.5
transitivePeerDependencies:
- bufferutil
- encoding
- fastestsmallesttextencoderdecoder
- sodium-native
- utf-8-validate
'@tsconfig/node10@1.0.11': {}
'@tsconfig/node12@1.0.11': {}
@@ -3026,6 +3191,8 @@ snapshots:
base64-js@1.5.1: {}
big-integer@1.6.52: {}
big.js@6.2.2: {}
bigint-buffer@1.1.5:
@@ -3077,6 +3244,11 @@ snapshots:
buffer-layout@1.2.2: {}
buffer@5.7.1:
dependencies:
base64-js: 1.5.1
ieee754: 1.2.1
buffer@6.0.3:
dependencies:
base64-js: 1.5.1
@@ -3572,6 +3744,8 @@ snapshots:
dependencies:
which-typed-array: 1.1.18
is-typedarray@1.0.0: {}
isexe@2.0.0: {}
isomorphic-ws@4.0.1(ws@7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10)):
@@ -3672,6 +3846,18 @@ snapshots:
prelude-ls: 1.2.1
type-check: 0.4.0
libsodium-sumo@0.7.15: {}
libsodium-wrappers-sumo@0.7.15:
dependencies:
libsodium-sumo: 0.7.15
libsodium-wrappers@0.7.15:
dependencies:
libsodium: 0.7.15
libsodium@0.7.15: {}
linkify-it@5.0.0:
dependencies:
uc.micro: 2.1.0
@@ -3765,6 +3951,8 @@ snapshots:
mustache@4.2.0: {}
nanoid@3.3.8: {}
natural-compare@1.4.0: {}
no-case@3.0.4:
@@ -3778,8 +3966,7 @@ snapshots:
dependencies:
whatwg-url: 5.0.0
node-gyp-build@4.8.4:
optional: true
node-gyp-build@4.8.4: {}
object-is@1.1.6:
dependencies:
@@ -3876,6 +4063,10 @@ snapshots:
picomatch@2.3.1: {}
poly1305-js@0.4.4:
dependencies:
big-integer: 1.6.52
possible-typed-array-names@1.0.0: {}
prelude-ls@1.2.1: {}
@@ -3971,6 +4162,19 @@ snapshots:
dot-case: 3.0.4
tslib: 2.8.1
sodium-native@3.4.1:
dependencies:
node-gyp-build: 4.8.4
sodium-plus@0.9.0(sodium-native@3.4.1):
dependencies:
buffer: 5.7.1
libsodium-wrappers: 0.7.15
poly1305-js: 0.4.4
sodium-native: 3.4.1
typedarray-to-buffer: 3.1.5
xsalsa20: 1.2.0
space-separated-tokens@2.0.2: {}
stringify-entities@4.0.4:
@@ -4053,6 +4257,8 @@ snapshots:
tslib@2.8.1: {}
tweetnacl-util@0.15.1: {}
tweetnacl@1.0.3: {}
type-check@0.4.0:
@@ -4061,6 +4267,10 @@ snapshots:
type-fest@0.20.2: {}
typedarray-to-buffer@3.1.5:
dependencies:
is-typedarray: 1.0.0
typedoc@0.26.11(typescript@5.7.2):
dependencies:
lunr: 2.3.9
@@ -4070,6 +4280,8 @@ snapshots:
typescript: 5.7.2
yaml: 2.6.1
typescript@4.9.5: {}
typescript@5.7.2: {}
uc.micro@2.1.0: {}
@@ -4181,6 +4393,8 @@ snapshots:
bufferutil: 4.0.8
utf-8-validate: 5.0.10
xsalsa20@1.2.0: {}
yaml@2.6.1: {}
yn@3.1.1: {}

View File

@@ -6,6 +6,7 @@ import {
deploy_collection,
deploy_token,
get_balance,
get_balance_other,
getTPS,
resolveSolDomain,
getPrimaryDomain,
@@ -40,6 +41,8 @@ import {
create_gibwork_task,
orcaClosePosition,
orcaFetchPositions,
rock_paper_scissor,
create_TipLink,
} from "../tools";
import {
@@ -66,12 +69,12 @@ export class SolanaAgentKit {
public connection: Connection;
public wallet: Keypair;
public wallet_address: PublicKey;
public openai_api_key: string;
public openai_api_key: string | null;
constructor(
private_key: string,
rpc_url = "https://api.mainnet-beta.solana.com",
openai_api_key: string,
openai_api_key: string | null = null,
) {
this.connection = new Connection(rpc_url);
this.wallet = Keypair.fromSecretKey(bs58.decode(private_key));
@@ -104,6 +107,13 @@ export class SolanaAgentKit {
return get_balance(this, token_address);
}
async getBalanceOther(
walletAddress: PublicKey,
tokenAddress?: PublicKey,
): Promise<number> {
return get_balance_other(this, walletAddress, tokenAddress);
}
async mintNFT(
collectionMint: PublicKey,
metadata: Parameters<typeof mintCollectionNFT>[2],
@@ -406,4 +416,14 @@ export class SolanaAgentKit {
payer ? new PublicKey(payer) : undefined,
);
}
async rockPaperScissors(
amount: number,
choice: "rock" | "paper" | "scissors",
) {
return rock_paper_scissor(this, amount, choice);
}
async createTiplink(amount: number, splmintAddress?: PublicKey) {
return create_TipLink(this, amount, splmintAddress);
}
}

View File

@@ -9,8 +9,6 @@ import {
import { create_image } from "../tools/create_image";
import { BN } from "@coral-xyz/anchor";
import { FEE_TIERS } from "../tools";
import { toJSON } from "../utils/toJSON";
import { s } from "@raydium-io/raydium-sdk-v2/lib/api-0eb57ba2";
export class SolanaBalanceTool extends Tool {
name = "solana_balance";
@@ -19,7 +17,7 @@ export class SolanaBalanceTool extends Tool {
If you want to get the balance of your wallet, you don't need to provide the tokenAddress.
If no tokenAddress is provided, the balance will be in SOL.
Inputs:
Inputs ( input is a JSON string ):
tokenAddress: string, eg "So11111111111111111111111111111111111111112" (optional)`;
constructor(private solanaKit: SolanaAgentKit) {
@@ -33,7 +31,7 @@ export class SolanaBalanceTool extends Tool {
return JSON.stringify({
status: "success",
balance: balance,
balance,
token: input || "SOL",
});
} catch (error: any) {
@@ -46,6 +44,49 @@ export class SolanaBalanceTool extends Tool {
}
}
export class SolanaBalanceOtherTool extends Tool {
name = "solana_balance_other";
description = `Get the balance of a Solana wallet or token account different from the agent's wallet.
If no tokenAddress is provided, the SOL balance of the wallet will be returned.
Inputs ( input is a JSON string ):
walletAddress: string, eg "GDEkQF7UMr7RLv1KQKMtm8E2w3iafxJLtyXu3HVQZnME" (required)
tokenAddress: string, eg "SENDdRQtYMWaQrBroBrJ2Q53fgVuq95CV9UPGEvpCxa" (optional)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const { walletAddress, tokenAddress } = JSON.parse(input);
const tokenPubKey = tokenAddress
? new PublicKey(tokenAddress)
: undefined;
const balance = await this.solanaKit.getBalanceOther(
new PublicKey(walletAddress),
tokenPubKey,
);
return JSON.stringify({
status: "success",
balance,
wallet: walletAddress,
token: tokenAddress || "SOL",
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaTransferTool extends Tool {
name = "solana_transfer";
description = `Transfer tokens or SOL to another address ( also called as wallet address ).
@@ -319,7 +360,7 @@ export class SolanaRegisterDomainTool extends Tool {
protected async _call(input: string): Promise<string> {
try {
const parsedInput = toJSON(input);
const parsedInput = JSON.parse(input);
this.validateInput(parsedInput);
const tx = await this.solanaKit.registerDomain(
@@ -556,7 +597,7 @@ export class SolanaLendAssetTool extends Tool {
status: "success",
message: "Asset lent successfully",
transaction: tx,
amount: amount,
amount,
});
} catch (error: any) {
return JSON.stringify({
@@ -670,7 +711,7 @@ export class SolanaTokenDataTool extends Tool {
return JSON.stringify({
status: "success",
tokenData: tokenData,
tokenData,
});
} catch (error: any) {
return JSON.stringify({
@@ -699,7 +740,7 @@ export class SolanaTokenDataByTickerTool extends Tool {
const tokenData = await this.solanaKit.getTokenDataByTicker(ticker);
return JSON.stringify({
status: "success",
tokenData: tokenData,
tokenData,
});
} catch (error: any) {
return JSON.stringify({
@@ -1224,7 +1265,7 @@ export class SolanaPythFetchPrice extends Tool {
const response: PythFetchPriceResponse = {
status: "success",
priceFeedID: input,
price: price,
price,
};
return JSON.stringify(response);
} catch (error: any) {
@@ -1298,7 +1339,7 @@ export class SolanaGetOwnedDomains extends Tool {
return JSON.stringify({
status: "success",
message: "Owned domains fetched successfully",
domains: domains,
domains,
});
} catch (error: any) {
return JSON.stringify({
@@ -1328,7 +1369,7 @@ export class SolanaGetOwnedTldDomains extends Tool {
return JSON.stringify({
status: "success",
message: "TLD domains fetched successfully",
domains: domains,
domains,
});
} catch (error: any) {
return JSON.stringify({
@@ -1355,7 +1396,7 @@ export class SolanaGetAllTlds extends Tool {
return JSON.stringify({
status: "success",
message: "TLDs fetched successfully",
tlds: tlds,
tlds,
});
} catch (error: any) {
return JSON.stringify({
@@ -1448,9 +1489,107 @@ export class SolanaCreateGibworkTask extends Tool {
}
}
export class SolanaRockPaperScissorsTool extends Tool {
name = "rock_paper_scissors";
description = `Play rock paper scissors to win SEND coins.
Inputs (input is a JSON string):
choice: string, either "rock", "paper", or "scissors" (required)
amount: number, amount of SOL to play with - must be 0.1, 0.01, or 0.005 SOL (required)`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
private validateInput(input: any): void {
if (input.choice !== undefined) {
throw new Error("choice is required.");
}
if (
input.amount !== undefined &&
(typeof input.spaceKB !== "number" || input.spaceKB <= 0)
) {
throw new Error("amount must be a positive number when provided");
}
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
this.validateInput(parsedInput);
const result = await this.solanaKit.rockPaperScissors(
Number(parsedInput['"amount"']),
parsedInput['"choice"'].replace(/^"|"$/g, "") as
| "rock"
| "paper"
| "scissors",
);
return JSON.stringify({
status: "success",
message: result,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export class SolanaTipLinkTool extends Tool {
name = "solana_tiplink";
description = `Create a TipLink for transferring SOL or SPL tokens.
Input is a JSON string with:
- amount: number (required) - Amount to transfer
- splmintAddress: string (optional) - SPL token mint address`;
constructor(private solanaKit: SolanaAgentKit) {
super();
}
protected async _call(input: string): Promise<string> {
try {
const parsedInput = JSON.parse(input);
if (!parsedInput.amount) {
throw new Error("Amount is required");
}
const amount = parseFloat(parsedInput.amount);
const splmintAddress = parsedInput.splmintAddress
? new PublicKey(parsedInput.splmintAddress)
: undefined;
const { url, signature } = await this.solanaKit.createTiplink(
amount,
splmintAddress,
);
return JSON.stringify({
status: "success",
url,
signature,
amount,
tokenType: splmintAddress ? "SPL" : "SOL",
message: `TipLink created successfully`,
});
} catch (error: any) {
return JSON.stringify({
status: "error",
message: error.message,
code: error.code || "UNKNOWN_ERROR",
});
}
}
}
export function createSolanaTools(solanaKit: SolanaAgentKit) {
return [
new SolanaBalanceTool(solanaKit),
new SolanaBalanceOtherTool(solanaKit),
new SolanaTransferTool(solanaKit),
new SolanaDeployTokenTool(solanaKit),
new SolanaDeployCollectionTool(solanaKit),
@@ -1487,5 +1626,7 @@ export function createSolanaTools(solanaKit: SolanaAgentKit) {
new SolanaGetMainDomain(solanaKit),
new SolanaResolveAllDomainsTool(solanaKit),
new SolanaCreateGibworkTask(solanaKit),
new SolanaRockPaperScissorsTool(solanaKit),
new SolanaTipLinkTool(solanaKit),
];
}

View File

@@ -0,0 +1,112 @@
import { TipLink } from "@tiplink/api";
import {
Transaction,
SystemProgram,
LAMPORTS_PER_SOL,
sendAndConfirmTransaction,
PublicKey,
ComputeBudgetProgram,
} from "@solana/web3.js";
import {
getAssociatedTokenAddress,
createTransferInstruction,
getMint,
createAssociatedTokenAccountInstruction,
} from "@solana/spl-token";
import { SolanaAgentKit } from "../index";
const MINIMUM_SOL_BALANCE = 0.003 * LAMPORTS_PER_SOL;
export async function create_TipLink(
agent: SolanaAgentKit,
amount: number,
splmintAddress?: PublicKey,
): Promise<{ url: string; signature: string }> {
try {
const tiplink = await TipLink.create();
if (!splmintAddress) {
const transaction = new Transaction();
transaction.add(
SystemProgram.transfer({
fromPubkey: agent.wallet_address,
toPubkey: tiplink.keypair.publicKey,
lamports: amount * LAMPORTS_PER_SOL,
}),
);
const signature = await sendAndConfirmTransaction(
agent.connection,
transaction,
[agent.wallet],
{ commitment: "confirmed" },
);
return {
url: tiplink.url.toString(),
signature,
};
} else {
const fromAta = await getAssociatedTokenAddress(
splmintAddress,
agent.wallet_address,
);
const toAta = await getAssociatedTokenAddress(
splmintAddress,
tiplink.keypair.publicKey,
);
const mintInfo = await getMint(agent.connection, splmintAddress);
const adjustedAmount = amount * Math.pow(10, mintInfo.decimals);
const transaction = new Transaction();
transaction.add(
ComputeBudgetProgram.setComputeUnitPrice({
microLamports: 5000,
}),
);
transaction.add(
SystemProgram.transfer({
fromPubkey: agent.wallet_address,
toPubkey: tiplink.keypair.publicKey,
lamports: MINIMUM_SOL_BALANCE,
}),
);
transaction.add(
createAssociatedTokenAccountInstruction(
agent.wallet_address,
toAta,
tiplink.keypair.publicKey,
splmintAddress,
),
);
transaction.add(
createTransferInstruction(
fromAta,
toAta,
agent.wallet_address,
adjustedAmount,
),
);
const signature = await sendAndConfirmTransaction(
agent.connection,
transaction,
[agent.wallet],
{ commitment: "confirmed" },
);
return {
url: tiplink.url.toString(),
signature,
};
}
} catch (error: any) {
console.error("Error creating TipLink or sending funds:", error.message);
throw new Error(`Failed to create TipLink: ${error.message}`);
}
}

View File

@@ -0,0 +1,50 @@
import {
LAMPORTS_PER_SOL,
ParsedAccountData,
PublicKey,
} from "@solana/web3.js";
import { SolanaAgentKit } from "../index";
/**
* Get the balance of SOL or an SPL token for the specified wallet address (other than the agent's wallet)
* @param agent - SolanaAgentKit instance
* @param wallet_address - Public key of the wallet to check balance for
* @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 0 if account doesn't exist
*/
export async function get_balance_other(
agent: SolanaAgentKit,
wallet_address: PublicKey,
token_address?: PublicKey,
): Promise<number> {
try {
if (!token_address) {
return (
(await agent.connection.getBalance(wallet_address)) / LAMPORTS_PER_SOL
);
}
const tokenAccounts = await agent.connection.getTokenAccountsByOwner(
wallet_address,
{ mint: token_address },
);
if (tokenAccounts.value.length === 0) {
console.warn(
`No token accounts found for wallet ${wallet_address.toString()} and token ${token_address.toString()}`,
);
return 0;
}
const tokenAccount = await agent.connection.getParsedAccountInfo(
tokenAccounts.value[0].pubkey,
);
const tokenData = tokenAccount.value?.data as ParsedAccountData;
return tokenData.parsed?.info?.tokenAmount?.uiAmount || 0;
} catch (error) {
throw new Error(
`Error fetching on-chain balance for ${token_address?.toString()}: ${error}`,
);
}
}

View File

@@ -2,6 +2,7 @@ export * from "./request_faucet_funds";
export * from "./deploy_token";
export * from "./deploy_collection";
export * from "./get_balance";
export * from "./get_balance_other";
export * from "./mint_nft";
export * from "./transfer";
export * from "./trade";
@@ -42,3 +43,6 @@ export * from "./openbook_create_market";
export * from "./pyth_fetch_price";
export * from "./create_gibwork_task";
export * from "./rock_paper_scissor";
export * from "./create_tiplinks";

View File

@@ -118,7 +118,6 @@ export async function orcaCreateCLMM(
whirlpoolAddress: poolKey.toString(),
});
} catch (error) {
console.log(error)
throw new Error(`${error}`);
}
}

View File

@@ -98,7 +98,6 @@ export async function orcaFetchPositions(
}
return JSON.stringify(result);
} catch (error) {
console.log(error)
throw new Error(`${error}`);
}
}

View File

@@ -136,7 +136,6 @@ export async function orcaOpenCenteredPositionWithLiquidity(
positionMint: positionMint.toString(),
})
} catch (error) {
console.log(error)
throw new Error(`${error}`);
}
}

View File

@@ -61,7 +61,6 @@ export async function orcaOpenSingleSidedPosition(
wallet,
ORCA_WHIRLPOOL_PROGRAM_ID,
);
// ctx.accountResolverOpts.createWrappedSolAccountMethod = "ata";
const client = buildWhirlpoolClient(ctx);
const whirlpool = await client.getPool(whirlpoolAddress);
@@ -151,9 +150,6 @@ export async function orcaOpenSingleSidedPosition(
const txPayloadDecompiled = TransactionMessage.decompile((txPayload.transaction as VersionedTransaction).message);
const instructions = txPayloadDecompiled.instructions;
const signers = txPayload.signers as Keypair[];
for (const signer of signers) {
console.log(signer.publicKey.toBase58());
}
const positionTxId = await sendTx(agent, instructions, signers);
txIds += positionTxId;
@@ -163,7 +159,6 @@ export async function orcaOpenSingleSidedPosition(
positionMint: positionMint.toString(),
});
} catch (error) {
console.log(error);
throw new Error(`${error}`);
}
}

View File

@@ -0,0 +1,132 @@
import { sendAndConfirmTransaction, Transaction } from "@solana/web3.js";
import { SolanaAgentKit } from "../agent";
export async function rock_paper_scissor(
agent: SolanaAgentKit,
amount: number,
choice: "rock" | "paper" | "scissors",
) {
try {
const res = await fetch(
`https://rps.sendarcade.fun/api/actions/bot?amount=${amount}&choice=${choice}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
account: agent.wallet.publicKey.toBase58(),
}),
},
);
const data = await res.json();
if (data.transaction) {
const txn = Transaction.from(Buffer.from(data.transaction, "base64"));
txn.sign(agent.wallet);
txn.recentBlockhash = (
await agent.connection.getLatestBlockhash()
).blockhash;
const sig = await sendAndConfirmTransaction(
agent.connection,
txn,
[agent.wallet],
{ commitment: "confirmed" },
);
const href = data.links?.next?.href;
return await outcome(agent, sig, href);
} else {
return "failed";
}
} catch (error: any) {
console.error(error);
throw new Error(`RPS game failed: ${error.message}`);
}
}
async function outcome(
agent: SolanaAgentKit,
sig: string,
href: string,
): Promise<string> {
try {
const res = await fetch(
"https://rps.sendarcade.fun" + href, // href = /api/actions/outcome?id=...
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
account: agent.wallet.publicKey.toBase58(),
signature: sig,
}),
},
);
const data: any = await res.json();
const title = data.title;
if (title.startsWith("You lost")) {
return title;
}
const next_href = data.links?.actions?.[0]?.href;
return title + "\n" + (await won(agent, next_href));
} catch (error: any) {
console.error(error);
throw new Error(`RPS outcome failed: ${error.message}`);
}
}
async function won(agent: SolanaAgentKit, href: string): Promise<string> {
try {
const res = await fetch(
"https://rps.sendarcade.fun" + href, // href = /api/actions/won?id=...
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
account: agent.wallet.publicKey.toBase58(),
}),
},
);
const data: any = await res.json();
if (data.transaction) {
const txn = Transaction.from(Buffer.from(data.transaction, "base64"));
txn.partialSign(agent.wallet);
await agent.connection.sendRawTransaction(txn.serialize(), {
preflightCommitment: "confirmed",
});
} else {
return "Failed to claim prize.";
}
const next_href = data.links?.next?.href;
return await postWin(agent, next_href);
} catch (error: any) {
console.error(error);
throw new Error(`RPS outcome failed: ${error.message}`);
}
}
async function postWin(agent: SolanaAgentKit, href: string): Promise<string> {
try {
const res = await fetch(
"https://rps.sendarcade.fun" + href, // href = /api/actions/postwin?id=...
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
account: agent.wallet.publicKey.toBase58(),
}),
},
);
const data: any = await res.json();
const title = data.title;
return "Prize claimed Successfully" + "\n" + title;
} catch (error: any) {
console.error(error);
throw new Error(`RPS outcome failed: ${error.message}`);
}
}

View File

@@ -1,11 +1,7 @@
import {
VersionedTransaction,
PublicKey,
LAMPORTS_PER_SOL,
} from "@solana/web3.js";
import { VersionedTransaction, PublicKey } from "@solana/web3.js";
import { SolanaAgentKit } from "../index";
import { TOKENS, DEFAULT_OPTIONS, JUP_API } from "../constants";
import { getMint } from "@solana/spl-token";
/**
* Swap tokens using Jupiter Exchange
* @param agent SolanaAgentKit instance
@@ -23,12 +19,23 @@ export async function trade(
slippageBps: number = DEFAULT_OPTIONS.SLIPPAGE_BPS,
): Promise<string> {
try {
// Check if input token is native SOL
const isNativeSol = inputMint.equals(TOKENS.SOL);
// For native SOL, we use LAMPORTS_PER_SOL, otherwise fetch mint info
const inputDecimals = isNativeSol
? 9 // SOL always has 9 decimals
: (await getMint(agent.connection, inputMint)).decimals;
// Calculate the correct amount based on actual decimals
const scaledAmount = inputAmount * Math.pow(10, inputDecimals);
const quoteResponse = await (
await fetch(
`${JUP_API}/quote?` +
`inputMint=${inputMint.toString()}` +
`inputMint=${isNativeSol ? TOKENS.SOL.toString() : inputMint.toString()}` +
`&outputMint=${outputMint.toString()}` +
`&amount=${inputAmount * LAMPORTS_PER_SOL}` +
`&amount=${scaledAmount}` +
`&slippageBps=${slippageBps}` +
`&onlyDirectRoutes=true` +
`&maxAccounts=20`,

View File

@@ -1,26 +0,0 @@
export const toJSON = (str: string): any => {
try {
// Remove curly braces and split by comma
const pairs = str.trim().slice(1, -1).split(",");
// Convert to object with explicit type
const obj: Record<string, any> = {};
pairs.forEach((pair) => {
const [key, value] = pair
.trim()
.split(":")
.map((s) => s.trim());
if (!key || value === undefined) {
throw new Error("Invalid key-value pair format");
}
obj[key] = isNaN(Number(value)) ? value : Number(value);
});
return JSON.parse(JSON.stringify(obj));
} catch (error) {
throw new Error(`Failed to parse string to JSON: ${error}`);
}
};