Add support for querying other wallet balance (#79)

# Pull Request Description

## Related Issue
Implements #60 

## Changes Made
This PR adds the following changes:
- Added `getBalanceOther` method in `SolanaAgentKit` class
- Implemented `get_balance_other` tool
  
## Implementation Details
- This tool enables the agent to query SOL and SPL token balances of
other wallets

## Prompt Used
```
Prompt: What is the SOL balance of HHELE9Q7LsARJACq7cMCkoPStjZUomn4JphdHUMyK3op?
-------------------
{"status":"success","balance":49.893951194,"wallet":"HHELE9Q7LsARJACq7cMCkoPStjZUomn4JphdHUMyK3op","token":"SOL"}
-------------------
The wallet balance of HHELE9Q7LsARJACq7cMCkoPStjZUomn4JphdHUMyK3op is approximately 49.89 SOL.
-------------------

Prompt: Now get the balance of sSo14endRuUbvQaJS3dq36Q829a3A6BEfoeeRGJywEh token for that same wallet

-------------------
{"status":"success","balance":60.338328396,"wallet":"HHELE9Q7LsARJACq7cMCkoPStjZUomn4JphdHUMyK3op","token":"sSo14endRuUbvQaJS3dq36Q829a3A6BEfoeeRGJywEh"}
-------------------
The balance of the token sSo14endRuUbvQaJS3dq36Q829a3A6BEfoeeRGJywEh in the wallet HHELE9Q7LsARJACq7cMCkoPStjZUomn4JphdHUMyK3op is approximately 60.34 tokens.
-------------------
```

## Checklist
- [x] I have tested these changes locally
- [x] I have updated the documentation
~~- [ ] I have added a transaction link~~
- [x] I have added the prompt used to test it
This commit is contained in:
ARYAN
2024-12-30 06:35:56 +05:30
committed by GitHub
19 changed files with 148 additions and 44 deletions

View File

@@ -1 +1 @@
window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAE42TwU4DIRCG34VzY2OjjemtcbMmWrWpezMeEKcuWZYhMEQb03cXt8a6lp164QAf3w/D8PghCN5JzMQDGmnl/BUs3WgSI+Ek1WleGRkChHF//aSm1iSo0fZFzE4nF9vRj+kSjQFFGm0BzuCmTVv2Pm0J/FqqpMyBffHkfJoV37uvMRyxflOs0oMk9HnRbo3bXgKpeum1ghUEl7IgazrEOOmVfn5D33TxUMnQrGBYPQRzAdfR6SSosAFbSJJZ8V+IEy6iwblSGC0VQFKbwJZjGOdCbpNh/7Z3ZcVmDNJcxDK2bh3tQkaralafJY+py2i7cnLdm+FY7YbqfzZhHuXkqmuq3cevEM2vE6e7d4UN4wOob5yebZ8+AWWuiIJlBAAA"
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

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

@@ -6,6 +6,7 @@ import {
deploy_collection,
deploy_token,
get_balance,
get_balance_other,
getTPS,
resolveSolDomain,
getPrimaryDomain,
@@ -62,12 +63,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));
@@ -100,6 +101,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],

View File

@@ -17,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) {
@@ -31,7 +31,7 @@ export class SolanaBalanceTool extends Tool {
return JSON.stringify({
status: "success",
balance: balance,
balance,
token: input || "SOL",
});
} catch (error: any) {
@@ -44,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 ).
@@ -554,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({
@@ -668,7 +711,7 @@ export class SolanaTokenDataTool extends Tool {
return JSON.stringify({
status: "success",
tokenData: tokenData,
tokenData,
});
} catch (error: any) {
return JSON.stringify({
@@ -697,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({
@@ -1004,7 +1047,7 @@ export class SolanaPythFetchPrice extends Tool {
const response: PythFetchPriceResponse = {
status: "success",
priceFeedID: input,
price: price,
price,
};
return JSON.stringify(response);
} catch (error: any) {
@@ -1078,7 +1121,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({
@@ -1108,7 +1151,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({
@@ -1135,7 +1178,7 @@ export class SolanaGetAllTlds extends Tool {
return JSON.stringify({
status: "success",
message: "TLDs fetched successfully",
tlds: tlds,
tlds,
});
} catch (error: any) {
return JSON.stringify({
@@ -1259,9 +1302,9 @@ export class SolanaRockPaperScissorsTool extends Tool {
const result = await this.solanaKit.rockPaperScissors(
Number(parsedInput['"amount"']),
parsedInput['"choice"'].replace(/^"|"$/g, "") as
| "rock"
| "paper"
| "scissors",
| "rock"
| "paper"
| "scissors",
);
return JSON.stringify({
@@ -1328,6 +1371,7 @@ export class SolanaTipLinkTool extends Tool {
export function createSolanaTools(solanaKit: SolanaAgentKit) {
return [
new SolanaBalanceTool(solanaKit),
new SolanaBalanceOtherTool(solanaKit),
new SolanaTransferTool(solanaKit),
new SolanaDeployTokenTool(solanaKit),
new SolanaDeployCollectionTool(solanaKit),

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";