diff --git a/src/facades/TradingWalletFacade.js b/src/facades/TradingWalletFacade.js index 7c9b450..6383ec9 100644 --- a/src/facades/TradingWalletFacade.js +++ b/src/facades/TradingWalletFacade.js @@ -1,5 +1,5 @@ const log = require('../logger') -const { QuantityNotAllowedError, TransactionNotMinedError } = require('../utils').errors +const { QuantityNotEnoughError } = require('../utils').errors const { TransactionLibBuilder } = require('../factories') /** @@ -33,40 +33,67 @@ class TradingWalletFacade { } async depositTokenAsync(personalWalletAddress, tradingWalletAddress, quantity, tokenAddress, privateKey) { - const approveTransactionHash = await this.erc20TokenService.approveTrasferAsync( - personalWalletAddress, - tradingWalletAddress, - quantity, - privateKey, - ) - const isApproveMined = await this.transactionLib.isTransactionMined(approveTransactionHash, tradingWalletAddress) + let approveToZeroTransactionHash = null + let approveTransactionHash = null - if (!isApproveMined) { - const errMsg = 'Approve transaction not mined.' - this.log.error({ - fn: 'depositTokenAsync', - isApproveMined, + const assetBalance = this.erc20TokenService.getBalanceOfAsync(personalWalletAddress) + + if (assetBalance < quantity) { + const errorMessage = 'The asset balance is < than the quantity to depoist!' + this.log.info({ personalWalletAddress, tradingWalletAddress, quantity, - }, errMsg) - throw new TransactionNotMinedError(errMsg) + tokenAddress, + assetBalance, + fn: 'depositTokenAsync', + }, + errorMessage) + throw new QuantityNotEnoughError(errorMessage) } - const allowedQuantity = await this.erc20TokenService.getAllowanceAsync(personalWalletAddress, tradingWalletAddress) + const allowance = await this.erc20TokenService.getAllowanceAsync(personalWalletAddress, tradingWalletAddress) - if (quantity <= allowedQuantity) { - const errorMessage = 'The quantity to deposit is not allowed!' - this.log.error({ + if (quantity > allowance && allowance > 0) { + this.log.info({ personalWalletAddress, tradingWalletAddress, quantity, tokenAddress, - allowedQuantity, + allowance, + fn: 'depositTokenAsync', }, - errorMessage) - throw new QuantityNotAllowedError(errorMessage) + 'The quantity to deposit is not completely allowed!') + + const zeroQuantity = 0 + approveToZeroTransactionHash = await this.erc20TokenService.approveTrasferAsync( + personalWalletAddress, + tradingWalletAddress, + zeroQuantity, + privateKey, + ) + + this.log.info({ + approveToZeroTransactionHash, + fn: 'depositTokenAsync', + }, + 'Approve to zero quantity done successfully.') } + + if (allowance === 0 || (quantity > allowance && allowance > 0)) { + approveTransactionHash = await this.erc20TokenService.approveTrasferAsync( + personalWalletAddress, + tradingWalletAddress, + quantity, + privateKey, + ) + this.log.info({ + approveTransactionHash, + fn: 'depositTokenAsync', + }, + 'Approve quantity done successfully.') + } + const depositTransactionHash = await this.tradingWalletService.depositTokenAsync( personalWalletAddress, tradingWalletAddress, @@ -74,19 +101,6 @@ class TradingWalletFacade { tokenAddress, privateKey, ) - const isDepositMined = await this.transactionLib.isTransactionMined(depositTransactionHash, tradingWalletAddress) - - if (!isDepositMined) { - const errMsg = 'Deposit transaction not mined.' - this.log.error({ - fn: 'depositTokenAsync', - isDepositMined, - personalWalletAddress, - tradingWalletAddress, - quantity, - }, errMsg) - throw new TransactionNotMinedError(errMsg) - } this.log.info({ fn: 'depositTokenAsync', @@ -95,8 +109,15 @@ class TradingWalletFacade { quantity, tokenAddress, privateKey, + depositTransactionHash, }, 'Deposit token completed successfully.') + + return { + approveToZeroTransactionHash, + approveTransactionHash, + depositTransactionHash, + } } } diff --git a/src/utils/errors.js b/src/utils/errors.js index cb9b968..567b021 100644 --- a/src/utils/errors.js +++ b/src/utils/errors.js @@ -57,9 +57,9 @@ class TransactionCallError extends BaseError {} class TradingWalletNotFoundError extends BaseError {} /** - * This error will be raised if the transaction is not mined. + * This error will be raised if the asset balance is less then the quantity to deposit. */ -class TransactionNotMinedError extends BaseError {} +class QuantityNotEnoughError extends BaseError {} /** * This error will be raised if the allowed quantity is lower than approved quantity @@ -89,5 +89,5 @@ module.exports = { TradingWalletNotFoundError, TransactionCallError, TransactionExecutionError, - TransactionNotMinedError, + QuantityNotEnoughError, } diff --git a/tests/unit/facades/TradingWalletFacade.test.js b/tests/unit/facades/TradingWalletFacade.test.js index 0abd3b9..03da146 100644 --- a/tests/unit/facades/TradingWalletFacade.test.js +++ b/tests/unit/facades/TradingWalletFacade.test.js @@ -1,7 +1,7 @@ /* global describe, expect, test */ const sandbox = require('sinon').createSandbox() -const { QuantityNotAllowedError, TransactionNotMinedError } = require('../../../index').utils.errors +const { QuantityNotEnoughError } = require('../../../index').utils.errors const { Erc20TokenServiceBuilder, TradingWalletServiceBuilder } = require('../../../index').factories const { TradingWalletFacade } = require('../../../index').facades @@ -23,9 +23,9 @@ afterEach(() => { }) describe('DepositTokenAsync', () => { - const approvedTxHash = '0xApprovedTxHash' - test('should raise TransactionNotMinedError if the approved transaction was not mined.', async () => { - sandbox.stub(tradingWalletFacade.erc20TokenService, 'approveTrasferAsync').returns(approvedTxHash) + test('should raise QuantityNotEnoughError if the asset balance is not enought.', async () => { + const balanceOfQuantity = '10000000000000000' + sandbox.stub(tradingWalletFacade.erc20TokenService, 'getBalanceOfAsync').returns(balanceOfQuantity) const isApprovedMinedMock = sandbox.stub(tradingWalletFacade.transactionLib, 'isTransactionMined') isApprovedMinedMock.onFirstCall().returns(false) @@ -35,21 +35,84 @@ describe('DepositTokenAsync', () => { quantityToDeposit, tokenAddress, privateKey, - )).rejects.toBeInstanceOf(TransactionNotMinedError) + )).rejects.toBeInstanceOf(QuantityNotEnoughError) }) - test('should raise TransactionNotMinedError if deposit transaction was not mined.', async () => { - sandbox.stub(tradingWalletFacade.erc20TokenService, 'approveTrasferAsync').returns(approvedTxHash) - const isApprovedMinedMock = sandbox.stub(tradingWalletFacade.transactionLib, 'isTransactionMined') - isApprovedMinedMock.onFirstCall().returns(true) - sandbox.stub(tradingWalletFacade.erc20TokenService, 'getAllowanceAsync').returns(quantityToDeposit + 10000) + test('should call directly depositToken becuase the quantity its already approved', async () => { + const balanceOfQuantity = '500000000000000000' + const depositTxHash = '0xDepositTxHash' + const expecetedResult = { + approveToZeroTransactionHash: null, + approveTransactionHash: null, + depositTransactionHash: depositTxHash, + } + sandbox.stub(tradingWalletFacade.erc20TokenService, 'getBalanceOfAsync').returns(balanceOfQuantity) + sandbox.stub(tradingWalletFacade.erc20TokenService, 'getAllowanceAsync').returns(quantityToDeposit) + sandbox.stub(tradingWalletFacade.tradingWalletService, 'depositTokenAsync').returns(depositTxHash) - return expect(tradingWalletFacade.depositTokenAsync( + const result = await tradingWalletFacade.depositTokenAsync( + personalWalletAddress, + tradingWalletAddress, + quantityToDeposit, + tokenAddress, + privateKey, + ) + + expect(result).toMatchObject(expecetedResult) + }) + + test('should call approve becuase the allowance is zero', async () => { + const balanceOfQuantity = '500000000000000000' + const allowance = 0 + const depositTxHash = '0xDepositTxHash' + const approveTxHash = '0xApproveTxHash' + const expecetedResult = { + approveToZeroTransactionHash: null, + approveTransactionHash: approveTxHash, + depositTransactionHash: depositTxHash, + } + sandbox.stub(tradingWalletFacade.erc20TokenService, 'getBalanceOfAsync').returns(balanceOfQuantity) + sandbox.stub(tradingWalletFacade.erc20TokenService, 'getAllowanceAsync').returns(allowance) + sandbox.stub(tradingWalletFacade.erc20TokenService, 'approveTrasferAsync').returns(approveTxHash) + sandbox.stub(tradingWalletFacade.tradingWalletService, 'depositTokenAsync').returns(depositTxHash) + + const result = await tradingWalletFacade.depositTokenAsync( personalWalletAddress, tradingWalletAddress, quantityToDeposit, tokenAddress, privateKey, - )).rejects.toBeInstanceOf(QuantityNotAllowedError) + ) + + expect(result).toMatchObject(expecetedResult) + }) + + test('should call directly approve ', async () => { + const balanceOfQuantity = '500000000000000000' + const allowance = balanceOfQuantity - 1000 + const depositTxHash = '0xDepositTxHash' + const approveZeroTxHash = '0xApproveZeroTxHash' + const approveTxHash = '0xApproveTxHash' + const expecetedResult = { + approveToZeroTransactionHash: approveZeroTxHash, + approveTransactionHash: approveTxHash, + depositTransactionHash: depositTxHash, + } + sandbox.stub(tradingWalletFacade.erc20TokenService, 'getBalanceOfAsync').returns(balanceOfQuantity) + sandbox.stub(tradingWalletFacade.erc20TokenService, 'getAllowanceAsync').returns(allowance) + const approveTrasferMock = sandbox.stub(tradingWalletFacade.erc20TokenService, 'approveTrasferAsync') + approveTrasferMock.onFirstCall().returns(approveZeroTxHash) + approveTrasferMock.onSecondCall().returns(approveTxHash) + sandbox.stub(tradingWalletFacade.tradingWalletService, 'depositTokenAsync').returns(depositTxHash) + + const result = await tradingWalletFacade.depositTokenAsync( + personalWalletAddress, + tradingWalletAddress, + quantityToDeposit, + tokenAddress, + privateKey, + ) + + expect(result).toMatchObject(expecetedResult) }) })