diff --git a/src/consts/interfaces.consts.ts b/src/consts/interfaces.consts.ts index 9d790ad..a534343 100644 --- a/src/consts/interfaces.consts.ts +++ b/src/consts/interfaces.consts.ts @@ -607,6 +607,8 @@ export enum EGenericErrorCodes { ERROR_UNSUPPORTED_CHAIN, ERROR_NAME_TOO_LONG, ERROR_PROHIBITED_SYMBOL, + ERROR_GETTING_TOKENBALANCE, + ERROR_UNSUPPORTED_TOKEN, // Add more generic error codes here if needed } diff --git a/src/index.ts b/src/index.ts index a22fa45..ef756b4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2789,6 +2789,84 @@ async function getTokenContractDetails({ } } +/** + * Function to get the balance of a token + * ! only works for native and ERC20 tokens ! + * @param tokenAddress: the address of the token + * @param walletAddress: the address of the wallet + * @param chainId(optional) : the chainId + * @param tokenType(optional) : the type of the token, has to be of type EPeanutLinkType + * @param tokenDecimals(optional) : the decimals of the token + * @param provider(optional) : the provider + * @returns the balance of the token formatted with the decimals + */ +async function getTokenBalance({ + tokenAddress, + walletAddress, + chainId, + tokenType = undefined, + tokenDecimals = undefined, + provider = undefined, +}: { + tokenAddress: string + walletAddress: string + chainId: string + tokenType?: interfaces.EPeanutLinkType + tokenDecimals?: number + provider?: ethers.providers.Provider //TODO: make this optional URL if we decide to remove ethers dependency +}): Promise { + try { + if (!provider) provider = await getDefaultProvider(chainId) + + if (tokenAddress === '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee') { + tokenAddress = ethers.constants.AddressZero.toLowerCase() + } + + if (!tokenType || !tokenDecimals) { + const tokenDetails = await getTokenContractDetails({ address: tokenAddress, provider: provider }) + tokenType = tokenDetails.type + tokenDecimals = tokenDetails.decimals + } + + if (tokenType == interfaces.EPeanutLinkType.native) { + const balance = await provider.getBalance(walletAddress) + + return ethers.utils.formatUnits(balance, tokenDecimals) + } else { + let contractABI + switch (tokenType) { + case interfaces.EPeanutLinkType.erc20: { + contractABI = ERC20_ABI + break + } + case interfaces.EPeanutLinkType.erc721: { + throw new interfaces.SDKStatus( + interfaces.EGenericErrorCodes.ERROR_UNSUPPORTED_TOKEN, + 'This token type is not supported for fetching balance' + ) + } + case interfaces.EPeanutLinkType.erc1155: { + throw new interfaces.SDKStatus( + interfaces.EGenericErrorCodes.ERROR_UNSUPPORTED_TOKEN, + 'This token type is not supported for fetching balance' + ) + } + } + + const contract = new ethers.Contract(tokenAddress, contractABI, provider) + + const balance = await contract.balanceOf(walletAddress) + return ethers.utils.formatUnits(balance, tokenDecimals) + } + } catch (error) { + console.error(error) + throw new interfaces.SDKStatus( + interfaces.EGenericErrorCodes.ERROR_GETTING_TOKENBALANCE, + 'Error fetching token balance' + ) + } +} + /** * @deprecated Use prepareDepositTxs instead. prepareTxs will be removed in February 2024. */ @@ -2890,6 +2968,7 @@ const peanut = { getTokenContractDetails, validateUserName, getTxReceiptFromHash, + getTokenBalance, ...raffle, } @@ -2988,4 +3067,5 @@ export { getTokenContractDetails, validateUserName, getTxReceiptFromHash, + getTokenBalance, } diff --git a/test/basic/getTokenBalance.test.ts b/test/basic/getTokenBalance.test.ts new file mode 100644 index 0000000..fd5533b --- /dev/null +++ b/test/basic/getTokenBalance.test.ts @@ -0,0 +1,66 @@ +import * as peanut from '../../src/index' + +describe('getTokenBalance', function () { + it('get erc20 token balance with type and decimals', async () => { + const tokenAddress = '0xe9bc9ad74cca887aff32ba09a121b1256fc9f052' + const walletAddress = '0x2d826aD1EAD5c8a2bC46ab93d9D0c6BEe0d39918' + const chainId = '137' + const tokenType = peanut.interfaces.EPeanutLinkType.erc20 + const tokenDecimals = 18 + + const result = await peanut.getTokenBalance({ tokenAddress, walletAddress, chainId, tokenType, tokenDecimals }) + + expect(Number(result)).toBeGreaterThan(0) + }, 1000000000) + + it('get erc20 token balance with type', async () => { + const tokenAddress = '0xe9bc9ad74cca887aff32ba09a121b1256fc9f052' + const walletAddress = '0x2d826aD1EAD5c8a2bC46ab93d9D0c6BEe0d39918' + const chainId = '137' + const tokenType = peanut.interfaces.EPeanutLinkType.erc20 + + const result = await peanut.getTokenBalance({ tokenAddress, walletAddress, chainId, tokenType }) + + expect(Number(result)).toBeGreaterThan(0) + }, 1000000000) + + it('get erc20 token balance with decimals', async () => { + const tokenAddress = '0xe9bc9ad74cca887aff32ba09a121b1256fc9f052' + const walletAddress = '0x2d826aD1EAD5c8a2bC46ab93d9D0c6BEe0d39918' + const chainId = '137' + const tokenDecimals = 18 + const result = await peanut.getTokenBalance({ tokenAddress, walletAddress, chainId, tokenDecimals }) + + expect(Number(result)).toBeGreaterThan(0) + }, 1000000000) + + it('get erc20 token balance without type and decimals', async () => { + const tokenAddress = '0xe9bc9ad74cca887aff32ba09a121b1256fc9f052' + const walletAddress = '0x2d826aD1EAD5c8a2bC46ab93d9D0c6BEe0d39918' + const chainId = '137' + + const result = await peanut.getTokenBalance({ tokenAddress, walletAddress, chainId }) + + expect(Number(result)).toBeGreaterThan(0) + }, 1000000000) + + it('get native token balance without type and decimals', async () => { + const tokenAddress = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee' + const walletAddress = '0x2d826aD1EAD5c8a2bC46ab93d9D0c6BEe0d39918' + const chainId = '137' + + const result = await peanut.getTokenBalance({ tokenAddress, walletAddress, chainId }) + + expect(Number(result)).toBeGreaterThan(0) + }, 1000000000) + + it('get native token balance without type and decimals', async () => { + const tokenAddress = '0x0000000000000000000000000000000000000000' + const walletAddress = '0x2d826aD1EAD5c8a2bC46ab93d9D0c6BEe0d39918' + const chainId = '137' + + const result = await peanut.getTokenBalance({ tokenAddress, walletAddress, chainId }) + + expect(Number(result)).toBeGreaterThan(0) + }, 1000000000) +})