Skip to content

Commit

Permalink
feat: SPL token balances (#204)
Browse files Browse the repository at this point in the history
  • Loading branch information
fadeev authored Jan 13, 2025
1 parent 70298d0 commit a0892ba
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 19 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
"@solana/web3.js": "^1.95.3",
"@uniswap/v2-periphery": "^1.1.0-beta.0",
"@zetachain/faucet-cli": "^4.1.1",
"@zetachain/networks": "v10.0.0",
"@zetachain/networks": "10.0.0-rc3",
"@zetachain/protocol-contracts": "11.0.0-rc4",
"axios": "^1.4.0",
"bech32": "^2.0.0",
Expand All @@ -126,4 +126,4 @@
"ws": "^8.17.1"
},
"packageManager": "[email protected]+sha1.1959a18351b811cdeedbd484a8f86c3cc3bbaf72"
}
}
107 changes: 92 additions & 15 deletions packages/client/src/getBalances.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,26 @@ export const getBalances = async function (
symbol: token.symbol,
});
} else if (token.coin_type === "ERC20") {
tokens.push({
chain_id: token.foreign_chain_id,
coin_type: "ERC20",
contract: token.asset,
symbol: token.symbol,
zrc20: token.zrc20_contract_address,
});
const supportedChain = supportedChains.find(
(c: any) => c.chain_id === token.foreign_chain_id
);
if (supportedChain.vm === "evm") {
tokens.push({
chain_id: token.foreign_chain_id,
coin_type: "ERC20",
contract: token.asset,
symbol: token.symbol,
zrc20: token.zrc20_contract_address,
});
} else if (supportedChain.vm === "svm") {
tokens.push({
chain_id: token.foreign_chain_id,
coin_type: "SPL",
contract: token.asset,
symbol: token.symbol,
zrc20: token.zrc20_contract_address,
});
}
tokens.push({
chain_id: this.getChainId(`zeta_${this.network}`),
coin_type: "ZRC20",
Expand Down Expand Up @@ -184,16 +197,16 @@ export const getBalances = async function (
t.contract === calls[index].target
);
if (token) {
const balance = ethers.utils.defaultAbiCoder.decode(
["uint256"],
data
)[0];
const formattedBalance = formatUnits(balance, token.decimals);
const balance = BigInt(
ethers.utils.defaultAbiCoder.decode(["uint256"], data)[0]
);
const formattedBalance = (
balance / BigInt(10 ** token.decimals)
).toString();
balances.push({ ...token, balance: formattedBalance });
}
});
} catch (error) {
console.error(`Multicall failed for ${chainName}:`, error);
// Fallback to individual calls if multicall fails
for (const token of tokens.filter(
(t) =>
Expand Down Expand Up @@ -262,9 +275,10 @@ export const getBalances = async function (
const r = await response.json();
const { funded_txo_sum, spent_txo_sum } = r.chain_stats;
const balance = (
(funded_txo_sum - spent_txo_sum) /
100000000
(BigInt(funded_txo_sum) - BigInt(spent_txo_sum)) /
BigInt(100000000)
).toString();

balances.push({ ...token, balance });
})
);
Expand Down Expand Up @@ -303,5 +317,68 @@ export const getBalances = async function (
})
);

const splTokens = tokens.filter(
(token) =>
token.coin_type === "SPL" &&
["solana_mainnet", "solana_testnet", "solana_devnet"].includes(
token.chain_name
)
);

await Promise.all(
splTokens.map(async (token) => {
try {
const API = this.getEndpoint("solana", token.chain_name);
const response = await fetch(API, {
body: JSON.stringify({
id: 1,
jsonrpc: "2.0",
method: "getTokenAccountsByOwner",
params: [
solanaAddress,
{ mint: token.contract },
{
encoding: "jsonParsed",
},
],
}),
headers: {
"Content-Type": "application/json",
},
method: "POST",
});

if (!response.ok) {
console.error(
`Failed to fetch SPL token accounts for ${token.symbol}`,
response
);
balances.push({ ...token, balance: "0" });
return;
}

const r = await response.json();

if (r.result && r.result.value && r.result.value.length > 0) {
let totalBalance = BigInt(0);
for (const acc of r.result.value) {
const amount = acc.account.data.parsed.info.tokenAmount.amount;
const decimals = acc.account.data.parsed.info.tokenAmount.decimals;
totalBalance += BigInt(amount) / BigInt(10 ** decimals);
}
balances.push({ ...token, balance: totalBalance.toString() });
} else {
balances.push({ ...token, balance: "0" });
}
} catch (err) {
console.error(
`Failed to get SPL balance for ${token.symbol} on ${token.chain_name}:`,
err
);
balances.push({ ...token, balance: "0" });
}
})
);

return balances;
};
2 changes: 1 addition & 1 deletion packages/tasks/src/balances.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ const main = async (args: any, hre: HardhatRuntimeEnvironment) => {
} else {
spinner.stop();
console.log(`
EVM: ${evmAddress} ${btcAddress ? `\nBitcoin: ${btcAddress}` : ""} ${
EVM: ${evmAddress} ${btcAddress ? `\nBitcoin: ${btcAddress}` : ""} ${
solanaAddress ? `\nSolana: ${solanaAddress}` : ""
}
`);
Expand Down
9 changes: 8 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2781,7 +2781,14 @@
typescript "5.5.4"
zod "3.22.4"

"@zetachain/networks@^10.0.0", "@zetachain/[email protected]":
"@zetachain/[email protected]":
version "10.0.0-rc3"
resolved "https://registry.yarnpkg.com/@zetachain/networks/-/networks-10.0.0-rc3.tgz#5c36ce182606ead7f39c42d0588427b366b13301"
integrity sha512-7rsNUXvtmLZ8f5VSfGaihYVHkGNEw3hlCj3WPh3PD985RYHTtNXvhfb9blqcW4ltGAeg6J6iPhnPxNCrU1nUwQ==
dependencies:
dotenv "^16.1.4"

"@zetachain/networks@^10.0.0":
version "10.0.0"
resolved "https://registry.yarnpkg.com/@zetachain/networks/-/networks-10.0.0.tgz#dd5d14a0870f6b658644aded8c96859f15531089"
integrity sha512-FPolaO19oVkSLSPDUA/Hu+8AhG3lDEslRDpLnMzbMbnNSC669Fkah0/TEf+6egrQbAifBRfFLzwWidAGs8oxtA==
Expand Down

0 comments on commit a0892ba

Please sign in to comment.