diff --git a/web-frontend/src/custom/smart-contract/useFTTransfer.tsx b/web-frontend/src/custom/smart-contract/useFTTransfer.tsx index 3a78788ba..421a28028 100644 --- a/web-frontend/src/custom/smart-contract/useFTTransfer.tsx +++ b/web-frontend/src/custom/smart-contract/useFTTransfer.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { Client, @@ -8,6 +8,8 @@ import { Args, ClientFactory, MAINNET_CHAIN_ID, + strToBytes, + STORAGE_BYTE_COST, } from '@massalabs/massa-web3'; import { ToastContent, parseAmount, toast } from '@massalabs/react-ui-kit'; import { providers } from '@massalabs/wallet-provider'; @@ -57,31 +59,36 @@ export function useFTTransfer(nickname: string) { callSmartContract, } = useWriteSmartContract(client, isMainnet); - const transfer = ( - recipient: string, - tokenAddress: string, - amount: string, - decimals: number, - ) => { - if (!client) { - throw new Error('Massa client not found'); - } + const transfer = useCallback( + ( + recipient: string, + tokenAddress: string, + amount: string, + decimals: number, + ) => { + if (!client) { + throw new Error('Massa client not found'); + } - const rawAmount = parseAmount(amount, decimals); - const args = new Args().addString(recipient).addU256(rawAmount); - - callSmartContract( - 'transfer', - tokenAddress, - args.serialize(), - { - pending: Intl.t('send-coins.steps.ft-transfer-pending'), - success: Intl.t('send-coins.steps.ft-transfer-success'), - error: Intl.t('send-coins.steps.ft-transfer-failed'), - }, - BigInt(0), - ); - }; + const rawAmount = parseAmount(amount, decimals); + const args = new Args().addString(recipient).addU256(rawAmount); + + estimateCoinsCost(client, tokenAddress, recipient).then((coins) => { + callSmartContract( + 'transfer', + tokenAddress, + args.serialize(), + { + pending: Intl.t('send-coins.steps.ft-transfer-pending'), + success: Intl.t('send-coins.steps.ft-transfer-success'), + error: Intl.t('send-coins.steps.ft-transfer-failed'), + }, + coins, + ); + }); + }, + [client, callSmartContract], + ); return { opId, @@ -249,3 +256,32 @@ function useWriteSmartContract(client?: Client, isMainnet?: boolean) { callSmartContract, }; } +async function estimateCoinsCost( + client: Client, + tokenAddress: string, + recipient: string, +): Promise { + const addrInfo = await client.publicApi().getAddresses([tokenAddress]); + const allKeys = addrInfo[0].candidate_datastore_keys; + const key = balanceKey(recipient); + const foundKey = allKeys.find((k) => { + return JSON.stringify(k) === JSON.stringify(key); + }); + + if (foundKey) { + return 0n; + } + + const storage = + 4n + // space of a key/value in the datastore + BigInt(key.length) + // key length + 32n; // length of the value of the balance + + return STORAGE_BYTE_COST * storage; +} + +function balanceKey(address: string): number[] { + const BALANCE_KEY_PREFIX = 'BALANCE'; + + return Array.from(strToBytes(BALANCE_KEY_PREFIX + address)); +}