michaelhly / solana-py

Solana Python SDK
https://michaelhly.github.io/solana-py
MIT License
1.05k stars 271 forks source link

how to get number of SPL tokens in a account, help and thx so much !!!!!!! #398

Open Mrfranken opened 8 months ago

Mrfranken commented 8 months ago

hi, I own USDC and other tokens in my account, I want to get how many USDC in my account, what should I do? I've tried method "get_token_accounts_by_owner", just like

from solana.rpc.api import Client
from solana.rpc import types

http_client = Client("https://api.mainnet-beta.solana.com")

token_accounts = http_client.get_token_accounts_by_owner(
    Pubkey.from_string("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"),
    types.TokenAccountOpts(program_id=Pubkey.from_string("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"))
)
print(token_accounts)

but it shows complicated dict, seems solana is so different from evm chain, could u help to give a example? many thx

caomingpei commented 8 months ago

get_token_account_balance may be more suitable for your needs.

For example, system account of circle has may token account, and 3emsAVdmGKERbHjmGfQ6oZ1e35dkf5iYcS6U4CPKFVaa is his token account of USDC.
You may use get_token_account_balance method to get the balance of this kind of account, which represents the USDC balance of your account.

from solana.rpc.api import Client, Pubkey

http_client = Client("https://api.mainnet-beta.solana.com")

token_accounts = http_client.get_token_account_balance(
    Pubkey.from_string("3emsAVdmGKERbHjmGfQ6oZ1e35dkf5iYcS6U4CPKFVaa")
)
print(token_accounts)

The output is a dict, just like: GetTokenAccountBalanceResp { context: RpcResponseContext { slot: 253876481, api_version: Some("1.17.21") }, value: UiTokenAmount(UiTokenAmount { ui_amount: Some(296529493.035438), decimals: 6, amount: "296529493035438", ui_amount_string: "296529493.035438" }) }

Well, the amount and decimals show the balance.

skywalkerscott commented 6 months ago

get_token_account_balance may be more suitable for your needs.

For example, system account of circle has may token account, and 3emsAVdmGKERbHjmGfQ6oZ1e35dkf5iYcS6U4CPKFVaa is his token account of USDC. You may use get_token_account_balance method to get the balance of this kind of account, which represents the USDC balance of your account.

from solana.rpc.api import Client, Pubkey

http_client = Client("https://api.mainnet-beta.solana.com")

token_accounts = http_client.get_token_account_balance(
    Pubkey.from_string("3emsAVdmGKERbHjmGfQ6oZ1e35dkf5iYcS6U4CPKFVaa")
)
print(token_accounts)

The output is a dict, just like: GetTokenAccountBalanceResp { context: RpcResponseContext { slot: 253876481, api_version: Some("1.17.21") }, value: UiTokenAmount(UiTokenAmount { ui_amount: Some(296529493.035438), decimals: 6, amount: "296529493035438", ui_amount_string: "296529493.035438" }) }

Well, the amount and decimals show the balance.

The method get_token_count_balance does not solve the problem of multiple token balances being returned simultaneously, and the token data returned by the method get_token_counts.by_owner cannot be used.

caomingpei commented 6 months ago

get_token_account_balance may be more suitable for your needs. For example, system account of circle has may token account, and 3emsAVdmGKERbHjmGfQ6oZ1e35dkf5iYcS6U4CPKFVaa is his token account of USDC. You may use get_token_account_balance method to get the balance of this kind of account, which represents the USDC balance of your account.

from solana.rpc.api import Client, Pubkey

http_client = Client("https://api.mainnet-beta.solana.com")

token_accounts = http_client.get_token_account_balance(
    Pubkey.from_string("3emsAVdmGKERbHjmGfQ6oZ1e35dkf5iYcS6U4CPKFVaa")
)
print(token_accounts)

The output is a dict, just like: GetTokenAccountBalanceResp { context: RpcResponseContext { slot: 253876481, api_version: Some("1.17.21") }, value: UiTokenAmount(UiTokenAmount { ui_amount: Some(296529493.035438), decimals: 6, amount: "296529493035438", ui_amount_string: "296529493.035438" }) } Well, the amount and decimals show the balance.

The method get_token_count_balance does not solve the problem of multiple token balances being returned simultaneously, and the token data returned by the method get_token_counts.by_owner cannot be used.

Yeah, you are right. This problem is collecting all numbers of usdc of one's account. Therefore, need to use the get_token_accounts_by_owner first to find all usdc minted by one's account, and get all token account balance. The following is an update example:

import json
from solana.rpc.api import Client, Pubkey
from solana.rpc.types import TokenAccountOpts

http_client = Client("https://api.mainnet-beta.solana.com")

# Token Program ID and USDC Mint Address
token_program_id = Pubkey.from_string("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
usdc_mint_address = Pubkey.from_string("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")
opts = TokenAccountOpts(program_id=token_program_id, mint=usdc_mint_address)

response = http_client.get_token_accounts_by_owner(
    Pubkey.from_string("7VHUFJHWu2CuExkJcJrzhQPJ2oygupTWkL2A2For4BmE"),
    opts
)
token_accounts = json.loads(response.to_json())
all_usdc_accounts = [account['pubkey'] for account in token_accounts['result']['value']]

def get_all_usdc_accounts(all_accounts):
    for address in all_accounts:
        balance = json.loads(http_client.get_token_account_balance(Pubkey.from_string(address)).to_json())
        token_amount = balance['result']['value']['uiAmount']
        print("{}: {}".format(address, token_amount))

get_all_usdc_accounts(all_usdc_accounts)
skywalkerscott commented 6 months ago

get_token_account_balance may be more suitable for your needs. For example, system account of circle has may token account, and 3emsAVdmGKERbHjmGfQ6oZ1e35dkf5iYcS6U4CPKFVaa is his token account of USDC. You may use get_token_account_balance method to get the balance of this kind of account, which represents the USDC balance of your account.

from solana.rpc.api import Client, Pubkey

http_client = Client("https://api.mainnet-beta.solana.com")

token_accounts = http_client.get_token_account_balance(
    Pubkey.from_string("3emsAVdmGKERbHjmGfQ6oZ1e35dkf5iYcS6U4CPKFVaa")
)
print(token_accounts)

The output is a dict, just like: GetTokenAccountBalanceResp { context: RpcResponseContext { slot: 253876481, api_version: Some("1.17.21") }, value: UiTokenAmount(UiTokenAmount { ui_amount: Some(296529493.035438), decimals: 6, amount: "296529493035438", ui_amount_string: "296529493.035438" }) } Well, the amount and decimals show the balance.

The method get_token_count_balance does not solve the problem of multiple token balances being returned simultaneously, and the token data returned by the method get_token_counts.by_owner cannot be used.

Yeah, you are right. This problem is collecting all numbers of usdc of one's account. Therefore, need to use the get_token_accounts_by_owner first to find all usdc minted by one's account, and get all token account balance. The following is an update example:

import json
from solana.rpc.api import Client, Pubkey
from solana.rpc.types import TokenAccountOpts

http_client = Client("https://api.mainnet-beta.solana.com")

# Token Program ID and USDC Mint Address
token_program_id = Pubkey.from_string("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
usdc_mint_address = Pubkey.from_string("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")
opts = TokenAccountOpts(program_id=token_program_id, mint=usdc_mint_address)

response = http_client.get_token_accounts_by_owner(
    Pubkey.from_string("7VHUFJHWu2CuExkJcJrzhQPJ2oygupTWkL2A2For4BmE"),
    opts
)
token_accounts = json.loads(response.to_json())
all_usdc_accounts = [account['pubkey'] for account in token_accounts['result']['value']]

def get_all_usdc_accounts(all_accounts):
    for address in all_accounts:
        balance = json.loads(http_client.get_token_account_balance(Pubkey.from_string(address)).to_json())
        token_amount = balance['result']['value']['uiAmount']
        print("{}: {}".format(address, token_amount))

get_all_usdc_accounts(all_usdc_accounts)

If the encoding parameter is used jsonParsed, executing the following method will result in an error.

address = Pubkey.from_string("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
program_id = Pubkey.from_string("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
opts = TokenAccountOpts(program_id=program_id, encoding="jsonParsed")
ret = http_client.get_token_accounts_by_owner(address, opts)

The error is like this.

Traceback (most recent call last):
  File "/Users/scott/dev/blockchain-dev/venv/lib/python3.11/site-packages/solana/exceptions.py", line 43, in argument_decorator
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/scott/dev/blockchain-dev/venv/lib/python3.11/site-packages/solana/rpc/providers/http.py", line 49, in make_request
    return _parse_raw(raw, parser=parser)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/scott/dev/blockchain-dev/venv/lib/python3.11/site-packages/solana/rpc/providers/core.py", line 95, in _parse_raw
    parsed = parser.from_json(raw)  # type: ignore
             ^^^^^^^^^^^^^^^^^^^^^
solders.SerdeJSONError: data did not match any variant of untagged enum Resp
caomingpei commented 6 months ago

get_token_account_balance may be more suitable for your needs. For example, system account of circle has may token account, and 3emsAVdmGKERbHjmGfQ6oZ1e35dkf5iYcS6U4CPKFVaa is his token account of USDC. You may use get_token_account_balance method to get the balance of this kind of account, which represents the USDC balance of your account.

from solana.rpc.api import Client, Pubkey

http_client = Client("https://api.mainnet-beta.solana.com")

token_accounts = http_client.get_token_account_balance(
    Pubkey.from_string("3emsAVdmGKERbHjmGfQ6oZ1e35dkf5iYcS6U4CPKFVaa")
)
print(token_accounts)

The output is a dict, just like: GetTokenAccountBalanceResp { context: RpcResponseContext { slot: 253876481, api_version: Some("1.17.21") }, value: UiTokenAmount(UiTokenAmount { ui_amount: Some(296529493.035438), decimals: 6, amount: "296529493035438", ui_amount_string: "296529493.035438" }) } Well, the amount and decimals show the balance.

The method get_token_count_balance does not solve the problem of multiple token balances being returned simultaneously, and the token data returned by the method get_token_counts.by_owner cannot be used.

Yeah, you are right. This problem is collecting all numbers of usdc of one's account. Therefore, need to use the get_token_accounts_by_owner first to find all usdc minted by one's account, and get all token account balance. The following is an update example:

import json
from solana.rpc.api import Client, Pubkey
from solana.rpc.types import TokenAccountOpts

http_client = Client("https://api.mainnet-beta.solana.com")

# Token Program ID and USDC Mint Address
token_program_id = Pubkey.from_string("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
usdc_mint_address = Pubkey.from_string("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v")
opts = TokenAccountOpts(program_id=token_program_id, mint=usdc_mint_address)

response = http_client.get_token_accounts_by_owner(
    Pubkey.from_string("7VHUFJHWu2CuExkJcJrzhQPJ2oygupTWkL2A2For4BmE"),
    opts
)
token_accounts = json.loads(response.to_json())
all_usdc_accounts = [account['pubkey'] for account in token_accounts['result']['value']]

def get_all_usdc_accounts(all_accounts):
    for address in all_accounts:
        balance = json.loads(http_client.get_token_account_balance(Pubkey.from_string(address)).to_json())
        token_amount = balance['result']['value']['uiAmount']
        print("{}: {}".format(address, token_amount))

get_all_usdc_accounts(all_usdc_accounts)

If the encoding parameter is used jsonParsed, executing the following method will result in an error.

address = Pubkey.from_string("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")
program_id = Pubkey.from_string("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA")
opts = TokenAccountOpts(program_id=program_id, encoding="jsonParsed")
ret = http_client.get_token_accounts_by_owner(address, opts)

The error is like this.

Traceback (most recent call last):
  File "/Users/scott/dev/blockchain-dev/venv/lib/python3.11/site-packages/solana/exceptions.py", line 43, in argument_decorator
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/Users/scott/dev/blockchain-dev/venv/lib/python3.11/site-packages/solana/rpc/providers/http.py", line 49, in make_request
    return _parse_raw(raw, parser=parser)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/scott/dev/blockchain-dev/venv/lib/python3.11/site-packages/solana/rpc/providers/core.py", line 95, in _parse_raw
    parsed = parser.from_json(raw)  # type: ignore
             ^^^^^^^^^^^^^^^^^^^^^
solders.SerdeJSONError: data did not match any variant of untagged enum Resp

If you would like to get the json format results, perhaps should use the get_token_accounts_by_owner_json_parsed. But this method is unstable (https://michaelhly.com/solana-py/rpc/async_api/#solana.rpc.async_api.AsyncClient.get_token_accounts_by_owner_json_parsed).

As for the encoding parameters in TokenAccountOpts, it seems to only support base58 and base64 now. (https://michaelhly.com/solana-py/rpc/types/#solana.rpc.types.TokenAccountOpts)

The code should update to:

...
opts = TokenAccountOpts(program_id=token_program_id, mint=usdc_mint_address)
response = http_client.get_token_accounts_by_owner_json_parsed(
    Pubkey.from_string("7VHUFJHWu2CuExkJcJrzhQPJ2oygupTWkL2A2For4BmE"),
    opts
)
...

The response is in json format.