From a4a7a7e4691b615324f1f926ec286e44bbfa2c9f Mon Sep 17 00:00:00 2001 From: "Miquel A. Cabot" Date: Fri, 1 Apr 2022 12:49:38 +0200 Subject: [PATCH 1/7] add transfer() and estTransfer() functions --- src/utils/TokenUtils.ts | 76 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/src/utils/TokenUtils.ts b/src/utils/TokenUtils.ts index dd2cd649..d2f28974 100644 --- a/src/utils/TokenUtils.ts +++ b/src/utils/TokenUtils.ts @@ -82,12 +82,86 @@ export async function approve( }) } catch (e) { LoggerInstance.error( - `ERRPR: Failed to approve spender to spend tokens : ${e.message}` + `ERROR: Failed to approve spender to spend tokens : ${e.message}` ) } return result } +/** + * Estimate gas cost for transfer function + * @param {String} account + * @param {String} tokenAddress + * @param {String} recipient + * @param {String} amount + * @param {String} force + * @param {Contract} contractInstance optional contract instance + * @return {Promise} + */ +export async function estTransfer( + web3: Web3, + account: string, + tokenAddress: string, + recipient: string, + amount: string, + contractInstance?: Contract +): Promise { + const tokenContract = contractInstance || new web3.eth.Contract(minAbi, tokenAddress) + + const gasLimitDefault = GASLIMIT_DEFAULT + let estGas + try { + estGas = await tokenContract.methods + .transfer(recipient, amount) + .estimateGas({ from: account }, (err, estGas) => (err ? gasLimitDefault : estGas)) + } catch (e) { + estGas = gasLimitDefault + LoggerInstance.error('estimate gas failed for approve!', e) + } + return estGas +} + +/** + * Moves amount tokens from the caller’s account to recipient. + * @param {String} account + * @param {String} tokenAddress + * @param {String} recipient + * @param {String} amount (always expressed as wei) + * @param {String} force if true, will overwrite any previous allowence. Else, will check if allowence is enough and will not send a transaction if it's not needed + */ +export async function transfer( + web3: Web3, + account: string, + tokenAddress: string, + recipient: string, + amount: string, + force = false +): Promise { + const tokenContract = new web3.eth.Contract(minAbi, tokenAddress) + + let result = null + const amountFormatted = await amountToUnits(web3, tokenAddress, amount) + const estGas = await estTransfer( + web3, + account, + tokenAddress, + recipient, + amountFormatted, + tokenContract + ) + + try { + result = await tokenContract.methods.transfer(recipient, amountFormatted).send({ + from: account, + gas: estGas + 1, + gasPrice: await getFairGasPrice(web3, null) + }) + } catch (e) { + LoggerInstance.error(`ERROR: Failed to transfer tokens : ${e.message}`) + } + return result +} + /** * Get Allowance for any erc20 * @param {Web3} web3 From 02fa3b89120426e5170c50bd18c0940eb74e3167 Mon Sep 17 00:00:00 2001 From: "Miquel A. Cabot" Date: Fri, 1 Apr 2022 16:27:41 +0200 Subject: [PATCH 2/7] remove unused parameters --- src/utils/TokenUtils.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/utils/TokenUtils.ts b/src/utils/TokenUtils.ts index d2f28974..6d28225f 100644 --- a/src/utils/TokenUtils.ts +++ b/src/utils/TokenUtils.ts @@ -134,8 +134,7 @@ export async function transfer( account: string, tokenAddress: string, recipient: string, - amount: string, - force = false + amount: string ): Promise { const tokenContract = new web3.eth.Contract(minAbi, tokenAddress) From 0735a4b4e3dfda4a9e370d7db6b835b652d18765 Mon Sep 17 00:00:00 2001 From: "Miquel A. Cabot" Date: Fri, 1 Apr 2022 16:31:37 +0200 Subject: [PATCH 3/7] update comments related to amount units --- src/utils/TokenUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/TokenUtils.ts b/src/utils/TokenUtils.ts index 6d28225f..ec29d788 100644 --- a/src/utils/TokenUtils.ts +++ b/src/utils/TokenUtils.ts @@ -45,7 +45,7 @@ export async function estApprove( * @param {String} account * @param {String} tokenAddress * @param {String} spender - * @param {String} amount (always expressed as wei) + * @param {String} amount amount of ERC20 tokens, expressed without decimals (not as wei) * @param {String} force if true, will overwrite any previous allowence. Else, will check if allowence is enough and will not send a transaction if it's not needed */ export async function approve( @@ -126,7 +126,7 @@ export async function estTransfer( * @param {String} account * @param {String} tokenAddress * @param {String} recipient - * @param {String} amount (always expressed as wei) + * @param {String} amount amount of ERC20 tokens, expressed without decimals (not as wei) * @param {String} force if true, will overwrite any previous allowence. Else, will check if allowence is enough and will not send a transaction if it's not needed */ export async function transfer( From f34573be44fc28caca122ae758204f4bc4b85075 Mon Sep 17 00:00:00 2001 From: "Miquel A. Cabot" Date: Mon, 4 Apr 2022 09:37:30 +0200 Subject: [PATCH 4/7] create a generic estimateGas() function --- src/utils/TokenUtils.ts | 90 ++++++++++++----------------------------- 1 file changed, 26 insertions(+), 64 deletions(-) diff --git a/src/utils/TokenUtils.ts b/src/utils/TokenUtils.ts index ec29d788..a2148a3b 100644 --- a/src/utils/TokenUtils.ts +++ b/src/utils/TokenUtils.ts @@ -8,34 +8,31 @@ import Web3 from 'web3' import { GASLIMIT_DEFAULT } from '.' /** - * Estimate gas cost for approval function - * @param {String} account - * @param {String} tokenAddress - * @param {String} spender - * @param {String} amount - * @param {String} force - * @param {Contract} contractInstance optional contract instance - * @return {Promise} + * Estimates the gas used when a function would be executed on chain + * @param {Contract} tokenContract token contract instance + * @param {string} from account that calls the function + * @param {string} functionSignature signature of the function + * @param {...any[]} args arguments of the function + * @return {Promise} gas cost of the function */ -export async function estApprove( - web3: Web3, - account: string, - tokenAddress: string, - spender: string, - amount: string, - contractInstance?: Contract +export async function estimateGas( + tokenContract: Contract, + from: string, + functionSignature: string, + ...args: any[] ): Promise { - const tokenContract = contractInstance || new web3.eth.Contract(minAbi, tokenAddress) - const gasLimitDefault = GASLIMIT_DEFAULT let estGas try { - estGas = await tokenContract.methods - .approve(spender, amount) - .estimateGas({ from: account }, (err, estGas) => (err ? gasLimitDefault : estGas)) + estGas = await tokenContract.methods[functionSignature].apply(null, args).estimateGas( + { + from: from + }, + (err, estGas) => (err ? gasLimitDefault : estGas) + ) } catch (e) { estGas = gasLimitDefault - LoggerInstance.error('estimate gas failed for approve!', e) + LoggerInstance.error(`ERROR: Estimate gas failed for ${functionSignature}!`, e) } return estGas } @@ -65,13 +62,12 @@ export async function approve( } let result = null const amountFormatted = await amountToUnits(web3, tokenAddress, amount) - const estGas = await estApprove( - web3, + const estGas = await estimateGas( + tokenContract, account, - tokenAddress, + 'approve(address,uint256)', spender, - amountFormatted, - tokenContract + amountFormatted ) try { @@ -88,39 +84,6 @@ export async function approve( return result } -/** - * Estimate gas cost for transfer function - * @param {String} account - * @param {String} tokenAddress - * @param {String} recipient - * @param {String} amount - * @param {String} force - * @param {Contract} contractInstance optional contract instance - * @return {Promise} - */ -export async function estTransfer( - web3: Web3, - account: string, - tokenAddress: string, - recipient: string, - amount: string, - contractInstance?: Contract -): Promise { - const tokenContract = contractInstance || new web3.eth.Contract(minAbi, tokenAddress) - - const gasLimitDefault = GASLIMIT_DEFAULT - let estGas - try { - estGas = await tokenContract.methods - .transfer(recipient, amount) - .estimateGas({ from: account }, (err, estGas) => (err ? gasLimitDefault : estGas)) - } catch (e) { - estGas = gasLimitDefault - LoggerInstance.error('estimate gas failed for approve!', e) - } - return estGas -} - /** * Moves amount tokens from the caller’s account to recipient. * @param {String} account @@ -140,13 +103,12 @@ export async function transfer( let result = null const amountFormatted = await amountToUnits(web3, tokenAddress, amount) - const estGas = await estTransfer( - web3, + const estGas = await estimateGas( + tokenContract, account, - tokenAddress, + 'transfer(address,uint256)', recipient, - amountFormatted, - tokenContract + amountFormatted ) try { From 2fe89b476af38ae20adba546905c3d0e80a0ded2 Mon Sep 17 00:00:00 2001 From: "Miquel A. Cabot" Date: Mon, 4 Apr 2022 10:22:03 +0200 Subject: [PATCH 5/7] update comments --- src/utils/TokenUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/TokenUtils.ts b/src/utils/TokenUtils.ts index a2148a3b..0095aac6 100644 --- a/src/utils/TokenUtils.ts +++ b/src/utils/TokenUtils.ts @@ -42,7 +42,7 @@ export async function estimateGas( * @param {String} account * @param {String} tokenAddress * @param {String} spender - * @param {String} amount amount of ERC20 tokens, expressed without decimals (not as wei) + * @param {String} amount amount of ERC20 tokens (not as wei) * @param {String} force if true, will overwrite any previous allowence. Else, will check if allowence is enough and will not send a transaction if it's not needed */ export async function approve( @@ -89,7 +89,7 @@ export async function approve( * @param {String} account * @param {String} tokenAddress * @param {String} recipient - * @param {String} amount amount of ERC20 tokens, expressed without decimals (not as wei) + * @param {String} amount amount of ERC20 tokens (not as wei) * @param {String} force if true, will overwrite any previous allowence. Else, will check if allowence is enough and will not send a transaction if it's not needed */ export async function transfer( From 605812ee7f038f24e4bf06cd8397a3643c10358b Mon Sep 17 00:00:00 2001 From: "Miquel A. Cabot" Date: Tue, 5 Apr 2022 10:24:27 +0200 Subject: [PATCH 6/7] estimate gas without using the function signature --- src/utils/TokenUtils.ts | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/src/utils/TokenUtils.ts b/src/utils/TokenUtils.ts index 0095aac6..18973849 100644 --- a/src/utils/TokenUtils.ts +++ b/src/utils/TokenUtils.ts @@ -1,5 +1,4 @@ import Decimal from 'decimal.js' -import { Contract } from 'web3-eth-contract' import { amountToUnits, getFairGasPrice, unitsToAmount } from './ContractUtils' import { minAbi } from './minAbi' import LoggerInstance from './Logger' @@ -9,32 +8,28 @@ import { GASLIMIT_DEFAULT } from '.' /** * Estimates the gas used when a function would be executed on chain - * @param {Contract} tokenContract token contract instance * @param {string} from account that calls the function - * @param {string} functionSignature signature of the function + * @param {Function} functionToEstimateGas function that we need to estimate the gas * @param {...any[]} args arguments of the function * @return {Promise} gas cost of the function */ export async function estimateGas( - tokenContract: Contract, from: string, - functionSignature: string, + functionToEstimateGas: Function, ...args: any[] ): Promise { - const gasLimitDefault = GASLIMIT_DEFAULT - let estGas + let estimatedGas = GASLIMIT_DEFAULT try { - estGas = await tokenContract.methods[functionSignature].apply(null, args).estimateGas( + estimatedGas = await functionToEstimateGas.apply(null, args).estimateGas( { from: from }, - (err, estGas) => (err ? gasLimitDefault : estGas) + (err, estGas) => (err ? GASLIMIT_DEFAULT : estGas) ) } catch (e) { - estGas = gasLimitDefault - LoggerInstance.error(`ERROR: Estimate gas failed for ${functionSignature}!`, e) + LoggerInstance.error(`ERROR: Estimate gas failed!`, e) } - return estGas + return estimatedGas } /** @@ -63,9 +58,8 @@ export async function approve( let result = null const amountFormatted = await amountToUnits(web3, tokenAddress, amount) const estGas = await estimateGas( - tokenContract, account, - 'approve(address,uint256)', + tokenContract.methods.approve, spender, amountFormatted ) @@ -104,9 +98,8 @@ export async function transfer( let result = null const amountFormatted = await amountToUnits(web3, tokenAddress, amount) const estGas = await estimateGas( - tokenContract, account, - 'transfer(address,uint256)', + tokenContract.methods.transfer, recipient, amountFormatted ) From cab4d5b1a73e41bbf6eea74a2af9d7f52c56e447 Mon Sep 17 00:00:00 2001 From: "Miquel A. Cabot" Date: Mon, 16 May 2022 15:48:40 +0200 Subject: [PATCH 7/7] restore estApprove() function and add estTransfer() function --- src/utils/TokenUtils.ts | 47 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/utils/TokenUtils.ts b/src/utils/TokenUtils.ts index 22d20141..c8008c3f 100644 --- a/src/utils/TokenUtils.ts +++ b/src/utils/TokenUtils.ts @@ -1,4 +1,5 @@ import Decimal from 'decimal.js' +import { Contract } from 'web3-eth-contract' import { amountToUnits, estimateGas, @@ -10,6 +11,29 @@ import LoggerInstance from './Logger' import { TransactionReceipt } from 'web3-core' import Web3 from 'web3' +/** + * Estimate gas cost for approval function + * @param {String} account + * @param {String} tokenAddress + * @param {String} spender + * @param {String} amount + * @param {String} force + * @param {Contract} contractInstance optional contract instance + * @return {Promise} + */ +export async function estApprove( + web3: Web3, + account: string, + tokenAddress: string, + spender: string, + amount: string, + contractInstance?: Contract +): Promise { + const tokenContract = contractInstance || new web3.eth.Contract(minAbi, tokenAddress) + + return estimateGas(account, tokenContract.methods.approve, spender, amount) +} + /** * Approve spender to spent amount tokens * @param {String} account @@ -58,6 +82,29 @@ export async function approve( return result } +/** + * Estimate gas cost for transfer function + * @param {String} account + * @param {String} tokenAddress + * @param {String} recipient + * @param {String} amount + * @param {String} force + * @param {Contract} contractInstance optional contract instance + * @return {Promise} + */ +export async function estTransfer( + web3: Web3, + account: string, + tokenAddress: string, + recipient: string, + amount: string, + contractInstance?: Contract +): Promise { + const tokenContract = contractInstance || new web3.eth.Contract(minAbi, tokenAddress) + + return estimateGas(account, tokenContract.methods.transfer, recipient, amount) +} + /** * Moves amount tokens from the caller’s account to recipient. * @param {String} account