@@ -5,11 +5,8 @@ import fetch from 'node-fetch'
55import toHex from 'array-buffer-to-hex'
66import https from 'https'
77import http from 'http'
8- import * as NeonJS from '@cityofzion/neon-js'
98import Promievent from 'promievent'
10- // import { Transaction as EthTransaction } from 'ethereumjs-tx'
11- import Web3 from 'web3'
12- import { Contract } from 'web3-eth-contract'
9+
1310import BigNumber from 'bignumber.js'
1411import { ApolloError } from './ApolloError'
1512import { LIST_MARKETS_QUERY } from '../queries/market/listMarkets'
@@ -140,6 +137,13 @@ import {
140137 CREATE_APIKEY_MUTATION
141138} from '../mutations/account/createApiKey'
142139import { PaillierProof } from '../mutations/account/generatePallierProof'
140+ import {
141+ InputApproveTransaction ,
142+ ITERATE_TRANSATION_MUTATION ,
143+ PrepareTransactionParams ,
144+ PrepareTransactionResponse ,
145+ PREPARE_TRANSATION_MUTATION
146+ } from '../mutations/movements/prepareTransaction'
143147
144148import {
145149 normalizePriceForMarket ,
@@ -170,7 +174,6 @@ import {
170174 CurrencyPrice ,
171175 LegacyLoginParams ,
172176 NonceSet ,
173- SignMovementResult ,
174177 AssetData ,
175178 Asset ,
176179 MissingNonceError ,
@@ -198,7 +201,6 @@ import {
198201 Blockchain ,
199202 bufferize ,
200203 Config ,
201- createAddMovementParams ,
202204 createCancelOrderParams ,
203205 createGetAssetsNoncesParams ,
204206 createListMovementsParams ,
@@ -235,13 +237,7 @@ import {
235237 SignStatesFields
236238} from 'mutations/stateSyncing/fragments/signStatesFragment'
237239
238- import {
239- prefixWith0xIfNeeded
240- // serializeEthTx
241- } from './ethUtils'
242-
243- import { SettlementABI } from './abi/eth/settlementABI'
244- import { Erc20ABI } from './abi/eth/erc20ABI'
240+ import { prefixWith0xIfNeeded } from './ethUtils'
245241
246242export * from './environments'
247243import {
@@ -257,7 +253,8 @@ const WebSocket = require('websocket').w3cwebsocket
257253const BLOCKCHAIN_TO_BIP44 = {
258254 [ Blockchain . ETH ] : BIP44 . ETH ,
259255 [ Blockchain . BTC ] : BIP44 . BTC ,
260- [ Blockchain . NEO ] : BIP44 . NEO
256+ [ Blockchain . NEO ] : BIP44 . NEO ,
257+ [ Blockchain . AVAXC ] : BIP44 . AVAXC
261258}
262259
263260/** @internal */
@@ -280,7 +277,7 @@ export const BIG_NUMBER_FORMAT = {
280277
281278export const UNLIMITED_APPROVAL =
282279 '0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe'
283-
280+ export const ACCEPTABLE_APPROVAL = '0xffffffffffffffffffffffffffffe'
284281export class Client {
285282 private _socket = null
286283 private mode : ClientMode = ClientMode . NONE
@@ -308,9 +305,7 @@ export class Client {
308305
309306 private wsToken : string
310307 private wsUri : string
311- private isMainNet : boolean
312308 private gql : GQL
313- private web3 : Web3
314309 private authorization : string
315310 private walletIndices : { [ key : string ] : number }
316311
@@ -324,8 +319,6 @@ export class Client {
324319 /** @internal */
325320 public apiKey : APIKey
326321 /** @internal */
327- public ethVaultContract : Contract
328- /** @internal */
329322 public marketData : { [ key : string ] : Market }
330323 /** @internal */
331324 public nashProtocolMarketData : ReturnType < typeof mapMarketsForNashProtocol >
@@ -356,9 +349,6 @@ export class Client {
356349 headers : { } ,
357350 ...clientOpts
358351 }
359- this . isMainNet = this . opts . host === EnvironmentConfiguration . production . host
360-
361- this . web3 = new Web3 ( this . opts . ethNetworkSettings . nodes [ 0 ] )
362352
363353 if ( ! opts . host || ( opts . host . indexOf ( '.' ) === - 1 && ! opts . isLocal ) ) {
364354 throw new Error ( `Invalid API host '${ opts . host } '` )
@@ -416,15 +406,6 @@ export class Client {
416406 this . opts . maxEthCostPrTransaction
417407 )
418408 }
419- const network = new NeonJS . rpc . Network ( {
420- ...this . opts . neoNetworkSettings ,
421- name : this . opts . neoNetworkSettings . name
422- } )
423- NeonJS . default . add . network ( network , true )
424- this . ethVaultContract = new this . web3 . eth . Contract (
425- SettlementABI ,
426- this . opts . ethNetworkSettings . contracts . vault . contract
427- )
428409
429410 const query : GQL [ 'query' ] = async params => {
430411 let obj : GQLResp < any >
@@ -2525,28 +2506,6 @@ export class Client {
25252506 )
25262507 }
25272508
2528- public async queryAllowance ( assetData : AssetData ) : Promise < BigNumber > {
2529- let approvalPower = assetData . blockchainPrecision
2530- if ( assetData . symbol === CryptoCurrency . USDC ) {
2531- approvalPower = this . isMainNet ? 6 : 18
2532- }
2533- const erc20Contract = new this . web3 . eth . Contract (
2534- Erc20ABI ,
2535- `0x${ assetData . hash } `
2536- )
2537- try {
2538- const res = await erc20Contract . methods
2539- . allowance (
2540- `0x${ this . apiKey . child_keys [ BIP44 . ETH ] . address } ` ,
2541- this . opts . ethNetworkSettings . contracts . vault . contract
2542- )
2543- . call ( )
2544- return new BigNumber ( res ) . div ( Math . pow ( 10 , approvalPower ) )
2545- } catch ( e ) {
2546- return new BigNumber ( 0 )
2547- }
2548- }
2549-
25502509 private getBlockchainFees = async (
25512510 blockchain : Blockchain
25522511 ) : Promise < BlockchainFees > => {
@@ -2764,14 +2723,20 @@ export class Client {
27642723 }
27652724 const assetData = this . assetData [ quantity . currency ]
27662725 const blockchain = assetData . blockchain
2767- let address
2768- try {
2769- const childKey = this . apiKey . child_keys [
2770- BLOCKCHAIN_TO_BIP44 [ blockchain . toUpperCase ( ) as Blockchain ]
2771- ]
2772- address = childKey . address
2773- } catch ( e ) {
2774- address = this . nashCoreConfig . wallets [ blockchain ] . address
2726+ const childKey = this . apiKey . child_keys [
2727+ BLOCKCHAIN_TO_BIP44 [ blockchain . toUpperCase ( ) as Blockchain ]
2728+ ]
2729+ const address = childKey . address
2730+
2731+ if (
2732+ blockchain === 'eth' &&
2733+ movementType === MovementTypeDeposit &&
2734+ quantity . currency !== CryptoCurrency . ETH
2735+ ) {
2736+ await this . approveAndAwaitAllowance (
2737+ quantity ,
2738+ this . opts . ethNetworkSettings . contracts . vault . contract
2739+ )
27752740 }
27762741
27772742 const blockchainFees = await this . getBlockchainFees (
@@ -2956,51 +2921,125 @@ export class Client {
29562921 return null
29572922 }
29582923
2959- /**
2960- * Sign a withdraw request.
2961- *
2962- * @param address
2963- * @param quantity
2964- * @returns
2965- *
2966- * Example
2967- * ```typescript
2968- * import { createCurrencyAmount } from '@neon-exchange/api-client-ts'
2969- *
2970- * const address = 'd5480a0b20e2d056720709a9538b17119fbe9fd6';
2971- * const amount = createCurrencyAmount('1.5', CryptoCurrency.ETH);
2972- * const signedMovement = await nash.signWithdrawRequest(address, amount);
2973- * console.log(signedMovement)
2974- * ```
2975- */
2976- public async signWithdrawRequest (
2977- address : string ,
2978- quantity : CurrencyAmount ,
2979- nonce ?: number
2980- ) : Promise < SignMovementResult > {
2981- const signMovementParams = createAddMovementParams (
2982- address ,
2983- false ,
2984- quantity ,
2985- MovementTypeWithdrawal ,
2986- nonce
2924+ public approveAndAwaitAllowance = async (
2925+ amount : CurrencyAmount ,
2926+ targetAddress : string ,
2927+ feeLevel : 'low' | 'med' | 'high' = 'med'
2928+ ) : Promise < boolean > => {
2929+ const assetData = this . assetData [ amount . currency ]
2930+ const blockchain = assetData . blockchain . toUpperCase ( ) as Blockchain
2931+ const childKey = this . apiKey . child_keys [ BLOCKCHAIN_TO_BIP44 [ blockchain ] ]
2932+ const address = childKey . address
2933+
2934+ const blockchainFees = await this . getBlockchainFees (
2935+ blockchain . toUpperCase ( ) as Blockchain
29872936 )
2988- const signedPayload = await this . signPayload ( signMovementParams )
2989- const result = await this . gql . mutate ( {
2990- mutation : ADD_MOVEMENT_MUTATION ,
2991- variables : {
2992- payload : signedPayload . payload ,
2993- signature : signedPayload . signature
2937+
2938+ let gasPrice = blockchainFees . priceMedium
2939+ switch ( feeLevel ) {
2940+ case 'low' :
2941+ gasPrice = blockchainFees . priceLow
2942+ break
2943+ case 'high' :
2944+ gasPrice = blockchainFees . priceHigh
2945+ break
2946+ }
2947+
2948+ const approveParams : InputApproveTransaction = {
2949+ minimumQuantity : {
2950+ amount : new BigNumber ( ACCEPTABLE_APPROVAL , 16 ) . toString ( ) ,
2951+ assetHash : assetData . hash ,
2952+ blockchain
2953+ } ,
2954+ quantity : {
2955+ amount : new BigNumber ( UNLIMITED_APPROVAL , 16 ) . toString ( ) ,
2956+ assetHash : assetData . hash ,
2957+ blockchain
2958+ } ,
2959+ targetAddress : targetAddress . replace ( '0x' , '' )
2960+ }
2961+
2962+ const prepareParams : PrepareTransactionParams = {
2963+ address,
2964+ approve : approveParams ,
2965+ blockchain,
2966+ gasPrice,
2967+ timestamp : new Date ( ) . getTime ( )
2968+ }
2969+
2970+ const sendPrepare = async (
2971+ params : PrepareTransactionParams
2972+ ) : Promise < PrepareTransactionResponse > => {
2973+ params . timestamp = new Date ( ) . getTime ( )
2974+
2975+ const signedPrepareTx = await this . signPayload ( {
2976+ payload : prepareParams ,
2977+ kind : SigningPayloadID . prepareTransactionPayload
2978+ } )
2979+ try {
2980+ const prepareTxResult = await this . gql . mutate ( {
2981+ mutation : PREPARE_TRANSATION_MUTATION ,
2982+ variables : {
2983+ payload : signedPrepareTx . payload ,
2984+ signature : signedPrepareTx . signature
2985+ }
2986+ } )
2987+ return {
2988+ ...prepareTxResult . data . prepareTransaction ,
2989+ approvalNeeded : true
2990+ }
2991+ } catch ( e ) {
2992+ if (
2993+ e . message . includes (
2994+ 'Specified minimum amount to approve is already covered by current allowance'
2995+ )
2996+ ) {
2997+ return {
2998+ reference : '' ,
2999+ transaction : null ,
3000+ transactionElements : [ ] ,
3001+ approvalNeeded : false
3002+ }
3003+ }
3004+ throw e
29943005 }
3006+ }
3007+
3008+ const prepareResult = await sendPrepare ( prepareParams )
3009+ if ( ! prepareResult . approvalNeeded ) {
3010+ return true
3011+ }
3012+
3013+ const signedIteratePayload = await this . signPayload ( {
3014+ payload : {
3015+ reference : prepareResult . reference ,
3016+ transactionElements : prepareResult . transactionElements ,
3017+ timestamp : new Date ( ) . getTime ( )
3018+ } ,
3019+ kind : SigningPayloadID . iterateTransactionPayload
29953020 } )
29963021
2997- // after deposit or withdrawal we want to update nonces
2998- await this . updateTradedAssetNonces ( )
3022+ const iterateTxResult = await this . gql . mutate ( {
3023+ mutation : ITERATE_TRANSATION_MUTATION ,
3024+ variables : {
3025+ payload : signedIteratePayload . payload ,
3026+ signature : signedIteratePayload . signature
3027+ }
3028+ } )
29993029
3000- return {
3001- result : result . data . addMovement ,
3002- blockchain_data : signedPayload . blockchain_data
3030+ if (
3031+ iterateTxResult . data . iterateTransaction &&
3032+ iterateTxResult . data . iterateTransaction . transactionElements . length === 0
3033+ ) {
3034+ let result = prepareResult
3035+ while ( result . approvalNeeded ) {
3036+ await sleep ( 10000 )
3037+ result = await sendPrepare ( prepareParams )
3038+ }
3039+ return true
30033040 }
3041+
3042+ return false
30043043 }
30053044
30063045 /**
0 commit comments