diff --git a/package.json b/package.json index 1d82115..c953c48 100644 --- a/package.json +++ b/package.json @@ -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", @@ -126,4 +126,4 @@ "ws": "^8.17.1" }, "packageManager": "yarn@1.22.21+sha1.1959a18351b811cdeedbd484a8f86c3cc3bbaf72" -} +} \ No newline at end of file diff --git a/packages/client/src/getBalances.ts b/packages/client/src/getBalances.ts index 25a2132..0ab443d 100644 --- a/packages/client/src/getBalances.ts +++ b/packages/client/src/getBalances.ts @@ -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", @@ -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) => @@ -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 }); }) ); @@ -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; }; diff --git a/packages/tasks/src/balances.ts b/packages/tasks/src/balances.ts index 2e5ecb0..1bff4aa 100644 --- a/packages/tasks/src/balances.ts +++ b/packages/tasks/src/balances.ts @@ -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}` : "" } `); diff --git a/yarn.lock b/yarn.lock index 4f94c9d..d82f9f8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2781,7 +2781,14 @@ typescript "5.5.4" zod "3.22.4" -"@zetachain/networks@^10.0.0", "@zetachain/networks@v10.0.0": +"@zetachain/networks@10.0.0-rc3": + 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==