Each parachain may have a different configuration for how they store their assets, how to query the account balances of these assets, and from a high-level, what one would consider an account balance for an asset. In the future we may see a Polkadot standard like this.
Furthermore, there may be a difference in how specific assets are stored depending on use cases within the underlying parachain. Take Karura for example, which uses the ORML Tokens pallet. If you want to query the balance of an account in the tokens pallet storage on Karura you will need to pass in the account
and the AcalaPrimitivesCurrencyCurrencyId
along with some additional information:
For Karura, you can see the different assets that are available by querying the assetRegistry
metadata like so:
Knowing the asset you want to query on the tokens pallet, you can write some code:
import { options } from "@acala-network/api";
import { ApiPromise, WsProvider } from "@polkadot/api";
export const fetchKaruraTokens = async () => {
const provider = new WsProvider("wss://karura.api.onfinality.io/public-ws");
const api = new ApiPromise(options({ provider }));
await api.isReadyOrError;
// BNC Balance on tokens pallet
const address = "qoo1mYQvGXLmGkhKM7PcJXdWcHeu9vo9K7xe2gYQZAxXUQ2";
const { free } = await api.query.tokens.accounts(address, { Token: 'BNC' })
console.log("BNC:", free.toHuman());
// TAI Balance on tokens pallet
const TAI = await api.query.tokens.accounts(address, { Token: 'TAI' })
console.log("TAI:", account.free.toHuman());
// LKSM Balance on tokens pallet
const LKSM = await api.query.tokens.accounts(address, { Token: 'LKSM' })
console.log("LKSM:", LKSM.free.toHuman());
// Native Karura Tokens
const { data } = await api.query.system.account(address)
console.log("KAR:",data.free.toHuman());
OR
// Generic way to get tokens associated with an account
const entries = await api.query.tokens.accounts.entries(address)
entries.forEach(entry => console.log(entry[0].toHuman()))
// Example Output: { Token: 'TAI' }, { Token: 'LKSM' }, { Token: 'KUSD' }, { Token: 'BNC' }
// You can then query the balance for each of these tokens for that account
}
Keep in mind, for the examples above, these are balances of the account on the tokens pallet and may not necessarily be the overall "portfolio" balance that you may be looking for - meaning, as a simple example, if an asset on Karura has an option to be collateralized (e.g. Collateral Staking), then the asset in the Collateral Staking will not appear in the token balance: api.query.tokens.accounts(address, { Token: 'LKSM' })
- which makes sense because that account is collateralizing that asset. For the amount of an asset that is being collateralized, you will have to look in the loans
pallet:
// LKSM Collateralized
const collateralizedLKSM = await api.query.loans.positions({ Token: 'LKSM' }, address)
console.log("LKSM Collateral Staking:", collateralizedLKSM.collateral.toHuman());
This is a good example of understanding how it is important to understand how the underlying parachain may have different use cases for the token and therefore if you want to query the "portfolio" balance of an asset for an account requires that you either take it as verbatim - the balance of the asset in the tokens pallet is the "portfolio" balance for that asset for that account OR understand, from a high-level, what is considered as the "portfolio" balance (see example image below) OR that you have your own definition of what you consider to be the "portfolio" balance for an asset for an account and for that requires an understanding of how the asset is stored and how it can be used within the parachain's ecosystem.
Karura Dapp > "Portfolio" Balance (LKSM)
More info can be found here: