Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add deposit token with approve #12

Merged
merged 24 commits into from
Feb 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8391bed
#11 add nowait rule in eslint
ceres3idoo Feb 18, 2019
5fbd82e
#11 Add tests for DepositAsync
ceres3idoo Feb 18, 2019
c355a1e
#11 Add isTransactionMined in TransactionLib
ceres3idoo Feb 18, 2019
5ad8a7c
#11 Add TradingWalletFacade
ceres3idoo Feb 18, 2019
e83268a
#11 Add TradingWalletFacedBuilder
ceres3idoo Feb 18, 2019
26ac0bd
#11 Adjust import for facade
ceres3idoo Feb 18, 2019
6a5ab25
#11 Add Example
ceres3idoo Feb 18, 2019
a27c999
#11 Add getBalanceAsync in Erc20TokenService
ceres3idoo Feb 19, 2019
2d672a2
#11 Adjust getBalanceOfAsync
ceres3idoo Feb 19, 2019
b4ce6ce
#11 Adjust getBalanceOf example
ceres3idoo Feb 19, 2019
4c54c39
#11 Refactor TradingWalletFacade
ceres3idoo Feb 19, 2019
7bd790d
#11 remove unused curtom error
ceres3idoo Feb 19, 2019
4b582b4
#11 Update readme and changelog
ceres3idoo Feb 19, 2019
e28f04b
#11 Adjust log level in TransactionLib
ceres3idoo Feb 19, 2019
dc29e48
#11 Adjust import in depositToken examples
ceres3idoo Feb 19, 2019
d5d6dcd
#11 Remove isTransactionMined and transfrom getEstimationGas from pri…
ceres3idoo Feb 22, 2019
1804bc6
#11 Adjust tests
ceres3idoo Feb 22, 2019
8f48340
#11 Get gas estimation to set gas in deposit token transaction
ceres3idoo Feb 22, 2019
04038ce
#11 Get nonce is public now in TransactionLiv
ceres3idoo Feb 22, 2019
bd308f6
#11 Set nonce manually in TradingWalletFacade
ceres3idoo Feb 22, 2019
85ba160
#11 Adjust deposit with approve example
ceres3idoo Feb 22, 2019
914fa0a
#11 Adjust typo from approveTrasferAsync to approveTransferAsync
ceres3idoo Feb 22, 2019
275c7ed
#11 Add new transaction lib public functions to its interface and int…
andreafspeziale Feb 22, 2019
6826a92
#11 Add log to deposit with approve example
andreafspeziale Feb 22, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"error",
120
],
"no-await-in-loop": "off",
"no-shadow": "off",
"no-underscore-dangle": "off",
"object-curly-newline": [
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Release date: 2019-02-REPLEACE_ME.
### Added

- [CLI withdraw command](https://github.com/eidoo/hybrid-exchange-sdk/issues/13)
- [Deposit Token with approve](https://github.com/eidoo/hybrid-exchange-sdk/issues/11)
- [Allowance](https://github.com/eidoo/hybrid-exchange-sdk/issues/7).
- [Private key from mnemonic](https://github.com/eidoo/hybrid-exchange-sdk/issues/5).
- [Pair fee](https://github.com/eidoo/hybrid-exchange-sdk/issues/1).
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ In order to deposit the above assets your ethereum wallet needs to be funded.<br

Please note that as soon as you are ready to deposit some ERC20 token you must [approve](./examples/lib/tradingWallet/approve/approve.js) your trading wallet as spender of the deposit amount since the trading wallet will perform a `transferFrom` of the token from your ethereum wallet to itself.<br>
You can check the status of the approval verifying the [allowance](./examples/lib/tradingWallet/allowance/getAllowance.js).
You can decide also to deposit ERC20 token with auto managed approve operation ([example](./examples/lib/tradingWallet/depositToken/depositTokenWithApprove.js)).

You will always be the only one able to withdraw [ERC20 token](./examples/lib/tradingWallet/withdraw/withdraw.js) and [ether](./examples/lib/tradingWallet/withdraw/withdraw.js) from your trading wallet to the connected ethereum wallet.

Expand Down
13 changes: 13 additions & 0 deletions examples/lib/Erc20TokenBalances/balanceOf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
(async () => {
const { Erc20TokenServiceBuilder } = require('@eidoo/hybrid-exchange-sdk').factories

const tokenAddress = '0x9727e2fb13f7f42d5a6f1a4a9877d4a7e0404d6a'
const personalWalletAddress = '0xcf4b07a79b5d29988f488f30c4a676ecaad35c02'

const erc20TokenServiceBuilder = new Erc20TokenServiceBuilder(tokenAddress)
const erc20TokenService = erc20TokenServiceBuilder.build()
const assetBalance = await erc20TokenService.getBalanceOfAsync(
personalWalletAddress,
)
console.log('allowance: ', assetBalance)
})()
2 changes: 1 addition & 1 deletion examples/lib/tradingWallet/approve/approve.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
const erc20TokenServiceBuilder = new Erc20TokenServiceBuilder(tokenAddress)
const erc20TokenService = erc20TokenServiceBuilder.build()

const transactionHash = await erc20TokenService.approveTrasferAsync(
const transactionHash = await erc20TokenService.approveTransferAsync(
personalWalletAddress,
tradingWalletAddress,
toDeposit,
Expand Down
45 changes: 45 additions & 0 deletions examples/lib/tradingWallet/depositToken/depositTokenWithApprove.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
(async () => {
const { TradingWalletTransactionBuilder, Erc20TokenServiceBuilder, Erc20TokenTransactionBuilder } = require('@eidoo/hybrid-exchange-sdk').factories
const { TradingWalletFacade } = require('@eidoo/hybrid-exchange-sdk').facades
const { TransactionLib } = require('@eidoo/hybrid-exchange-sdk').lib.TransactionLib

const Web3 = require('web3')
const providerUrl = 'providerUrl'
const web3 = new Web3(new Web3.providers.HttpProvider(providerUrl))

const tokenAddress = '0x9727e2fb13f7f42d5a6f1a4a9877d4a7e0404d6a'
const personalWalletAddress = '0xcf4b07a79b5d29988f488f30c4a676ecaad35c02'
const tradingWalletAddress = '0x9c6d1840381cc570235a4ed867bf8465e32ce753'
const privateKey = '0x4c7ee440ad699493b22732031e4a3277d2d8aa834b727aa0b358e3310aa37384'
const quantityToDeposit = '500000000000000000'

const tradingWalletTransactionBuilder = new TradingWalletTransactionBuilder(web3)
const erc20TokenServiceBuilder = new Erc20TokenServiceBuilder(tokenAddress)
const transactionLib = new TransactionLib()
const erc20TokenTransactionBuilder = new Erc20TokenTransactionBuilder(
web3,
{ erc20TokenSmartContractAddress: tokenAddress,
transactionLib },
)
const tradingWalletFacade = new TradingWalletFacade(
tradingWalletTransactionBuilder,
erc20TokenServiceBuilder.build(),
erc20TokenTransactionBuilder,
)

const {
approveToZeroTransactionHash,
approveTransactionHash,
depositTransactionHash,
} = await tradingWalletFacade.depositTokenAsync(
personalWalletAddress,
tradingWalletAddress,
quantityToDeposit,
tokenAddress,
privateKey,
)

console.log('approveToZeroTransactionHash: ', approveToZeroTransactionHash)
console.log('approveTransactionHash: ', approveTransactionHash)
console.log('depositTransactionHash: ', depositTransactionHash)
})()
151 changes: 151 additions & 0 deletions src/facades/TradingWalletFacade.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
const log = require('../logger')
const { QuantityNotEnoughError } = require('../utils').errors
const { TransactionLibBuilder } = require('../factories')

/**
* Class representing a facade of tradingWallet utilities.
*/

class TradingWalletFacade {
constructor(tradingWalletTransactionBuilder, erc20TokenService, erc20TokenTransactionBuilder,
transactionLib = TransactionLibBuilder.build(), logger = log) {
if (!logger) {
throw new TypeError(`Invalid "logger" value: ${logger}`)
}
this.log = logger.child({ module: this.constructor.name })

if (!tradingWalletTransactionBuilder) {
const errorMessage = `Invalid "tradingWalletTransactionBuilder" value: ${tradingWalletTransactionBuilder}`
throw new TypeError(errorMessage)
}
this.tradingWalletTransactionBuilder = tradingWalletTransactionBuilder

if (!erc20TokenService) {
const errorMessage = `Invalid "erc20TokenService" value: ${erc20TokenService}`
throw new TypeError(errorMessage)
}
this.erc20TokenService = erc20TokenService

if (!erc20TokenTransactionBuilder) {
const errorMessage = `Invalid "erc20TokenTransactionBuilder" value: ${erc20TokenTransactionBuilder}`
throw new TypeError(errorMessage)
}
this.erc20TokenTransactionBuilder = erc20TokenTransactionBuilder

if (!transactionLib) {
const errorMessage = `Invalid "transactionLib" value: ${transactionLib}`
throw new TypeError(errorMessage)
}
this.transactionLib = transactionLib
}

async throwIfTokenWalletEnought(personalWalletAddress, quantity) {
const assetBalance = await this.erc20TokenService.getBalanceOfAsync(personalWalletAddress)

const assetBalanceToBigNumber = this.transactionLib.web3.toBigNumber(assetBalance)
const quantityToBigNumber = this.transactionLib.web3.toBigNumber(quantity)
if (assetBalanceToBigNumber.lt(quantityToBigNumber)) {
const errorMessage = 'The asset balance is < than the quantity to depoist!'
this.log.info({
personalWalletAddress,
quantity,
assetBalance,
fn: 'checkIfTokenWalletEnought',
},
errorMessage)
throw new QuantityNotEnoughError(errorMessage)
}
}

async depositTokenAsync(personalWalletAddress, tradingWalletAddress, quantity, tokenAddress, privateKey) {
andreafspeziale marked this conversation as resolved.
Show resolved Hide resolved
this.log.info({
fn: 'depositTokenAsync',
personalWalletAddress,
tradingWalletAddress,
tokenAddress,
quantity,
}, 'Deposit token.')

let approveToZeroTransactionHash = null
let approveTransactionHash = null

await this.throwIfTokenWalletEnought(personalWalletAddress, quantity)

const allowance = await this.erc20TokenService.getAllowanceAsync(personalWalletAddress, tradingWalletAddress)
const allowanceToInt = +allowance

if (quantity > allowance && allowance > 0) {
this.log.info({
personalWalletAddress,
tradingWalletAddress,
quantity,
tokenAddress,
allowance,
fn: 'depositTokenAsync',
},
'The quantity to deposit is not completely allowed!')

const zeroQuantity = 0
approveToZeroTransactionHash = await this.erc20TokenService.approveTransferAsync(
personalWalletAddress,
tradingWalletAddress,
zeroQuantity,
privateKey,
)
}

const approveTransactionDraftObject = await this.erc20TokenTransactionBuilder.buildApproveTrasferTransactionDraft(
personalWalletAddress,
tradingWalletAddress,
quantity,
)
const gasEstimationForApprove = await this.transactionLib.getGasEstimation(approveTransactionDraftObject)
const nonceForApprove = await this.transactionLib.getNonce(personalWalletAddress)

if (allowanceToInt === 0 || (quantity > allowanceToInt && allowanceToInt > 0)) {
const signedApproveData = await this.transactionLib.sign(
approveTransactionDraftObject,
privateKey,
nonceForApprove,
gasEstimationForApprove.gas,
gasEstimationForApprove.gasPrice,
)
approveTransactionHash = await this.transactionLib.execute(signedApproveData)
}

const depositTokenTransactionDraft = await this.tradingWalletTransactionBuilder
.buildDepositTokenTransactionDraft(personalWalletAddress, tradingWalletAddress, quantity, tokenAddress)
const depositTokenFixedGasEstimation = 100000

const signedTransactionDataForDeposit = await this.transactionLib.sign(
depositTokenTransactionDraft,
privateKey,
nonceForApprove + 1,
depositTokenFixedGasEstimation,
gasEstimationForApprove.gasPrice,
)

const depositTransactionHash = await this.transactionLib
.execute(signedTransactionDataForDeposit, privateKey)

this.log.info({
fn: 'depositTokenAsync',
personalWalletAddress,
tradingWalletAddress,
quantity,
tokenAddress,
approveToZeroTransactionHash,
approveTransactionHash,
depositTransactionHash,
},
'Deposit token completed successfully.')

return {
approveToZeroTransactionHash,
approveTransactionHash,
depositTransactionHash,
}
}
}

module.exports = TradingWalletFacade
5 changes: 5 additions & 0 deletions src/facades/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const TradingWalletFacade = require('./TradingWalletFacade')

module.exports = {
TradingWalletFacade,
}
19 changes: 19 additions & 0 deletions src/factories/Erc20TokenTransactionBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,25 @@ class Erc20TokenTransactionBuilder extends BaseTransactionBuilder {
)
return transactionDraft
}

buildGetBalanceOfTransactionDraft(personalWalletAddress) {
this.constructor.checkEtherumAddress(personalWalletAddress)

const smartContractMethodName = 'balanceOf'
const smartContractParams = [personalWalletAddress]
const transactionParams = {
from: personalWalletAddress,
to: this.erc20TokenSmartContractAddress,
}

const transactionDraft = this.transactionLib.buildDraft(this.erc20TokenSmartContractInstance,
transactionParams, smartContractMethodName, smartContractParams)
this.log.debug(
{ fn: 'buildGetBalanceOfTransactionDraft', personalWalletAddress },
'Erc20 token get balance of transaction draft builded successfully.',
)
return transactionDraft
}
}

module.exports = Erc20TokenTransactionBuilder
19 changes: 19 additions & 0 deletions src/factories/TransactionLibBuilder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const Web3 = require('web3')

const logger = require('../logger')

const { TransactionLib } = require('../lib/TransactionLib')

const providerUrl = 'urlToProvider'
const web3 = new Web3(new Web3.providers.HttpProvider(providerUrl))

/**
* Class representing a simple factory to build Transaction lib object.
*/
class TransactionLibBuilder {
static build() {
return new TransactionLib(web3, logger)
}
}

module.exports = TransactionLibBuilder
2 changes: 2 additions & 0 deletions src/factories/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ const Erc20TokenServiceBuilder = require('./Erc20TokenServiceBuilder')
const OrderPayloadBuilder = require('./OrderPayloadBuilder')
const TradingWalletServiceBuilder = require('./TradingWalletServiceBuilder')
const TradingWalletTransactionBuilder = require('./TradingWalletTransactionBuilder')
const TransactionLibBuilder = require('./TransactionLibBuilder')

module.exports = {
Erc20TokenTransactionBuilder,
Erc20TokenServiceBuilder,
OrderPayloadBuilder,
TradingWalletServiceBuilder,
TradingWalletTransactionBuilder,
TransactionLibBuilder,
}
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const commands = require('./commands')
const config = require('./config')
const facades = require('./facades')
const factories = require('./factories')
const helpers = require('./helpers')
const lib = require('./lib')
Expand All @@ -11,6 +12,7 @@ const validators = require('./validators')
module.exports = {
commands,
config,
facades,
factories,
helpers,
lib,
Expand Down
10 changes: 10 additions & 0 deletions src/lib/TransactionLib/ITransactionLib.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ class ITransactionLib {
return Promise.reject(new Error('Method "buildDraft" has not been implemented yet.'))
}

// eslint-disable-next-line class-methods-use-this
getGasEstimation() {
return Promise.reject(new Error('Method "getGasEstimation" has not been implemented yet.'))
}

// eslint-disable-next-line class-methods-use-this
getNonce() {
return Promise.reject(new Error('Method "getNonce" has not been implemented yet.'))
}

// eslint-disable-next-line class-methods-use-this
sign() {
return Promise.reject(new Error('Method "sign" has not been implemented yet.'))
Expand Down
Loading