From 78d5ba7ec856e6c00588ee95135decb8e7f5293f Mon Sep 17 00:00:00 2001 From: Chibuotu Amadi Date: Tue, 22 Dec 2020 08:25:52 +0100 Subject: [PATCH] feat(binance): integrate BUSD for bounties and hackathons (#8121) * feat: include BUSD case in bounty detail * process BUSD token transfer * refactor utils + token balance checks - add util to get token balance - enforce sufficient balance for transfer * litte refactor * use only jsonRpcRequest util * fix: amount comparison for insufficient balance * add util to get selected account * use only getSelectedAccount util * autofill funderAddress with selected account --- app/assets/v2/js/lib/binance/utils.js | 164 +++++++++++++----- .../pages/bounty_detail/binance_extension.js | 3 +- app/assets/v2/js/pages/bounty_details2.js | 3 + app/assets/v2/js/pages/new_bounty.js | 9 + .../templates/bounty/new_bounty.html | 2 +- 5 files changed, 140 insertions(+), 41 deletions(-) diff --git a/app/assets/v2/js/lib/binance/utils.js b/app/assets/v2/js/lib/binance/utils.js index c0a8e0d5902..3e932eb7222 100644 --- a/app/assets/v2/js/lib/binance/utils.js +++ b/app/assets/v2/js/lib/binance/utils.js @@ -13,6 +13,18 @@ binance_utils.getChainVerbose = chainId => { } } +async function jsonRpcRequest(method, params) { + return new Promise(async (resolve, reject) => { + BinanceChain + .request({ method, params }) + .then(result => { + resolve(result); + }) + .catch(error => { + reject(error); + }); + }); +} /** * Returns wallet's balance on the connected binance network @@ -24,17 +36,45 @@ binance_utils.getAddressBalance = async address => { if (!isConnected || !address) return; - data = { - method: 'eth_getBalance', - params: [address, 'latest'] - }; - - const result = await BinanceChain.request(data); - - // convert hex balance to integer and account for decimal points - const bnbBalance = BigInt(result).toString(10) * 10 ** -18; - - return Promise.resolve(bnbBalance.toFixed(4)); + const result = await jsonRpcRequest('eth_getBalance', [address, 'latest']); + + // convert hex balance to integer + const bnbBalance = BigInt(result).toString(10); + + return Promise.resolve(bnbBalance); +}; + +/** + * Returns wallet's BEP20 token balance on the connected binance network + * @param {String} address + * @param {String} tokenContractAddress + */ +binance_utils.getAddressTokenBalance = async (address, tokenContractAddress) => { + const isConnected = await BinanceChain.isConnected(); + + if (!isConnected || !address || !tokenContractAddress) + return; + + const methodSignature = await jsonRpcRequest( + 'web3_sha3', + ['balanceOf(address)'] + ); + const method_id = methodSignature.substr(0, 10); + const address = address.substr(2).padStart(64, '0'); // remove 0x and pad with zeroes + + const params = [ + { + to: tokenContractAddress, + data: method_id + address + }, + 'latest' + ] + const result = await jsonRpcRequest('eth_call', params); + + // convert hex balance to integer + const tokenBalance = BigInt(result).toString(10); + + return Promise.resolve(tokenBalance); }; @@ -53,15 +93,27 @@ binance_utils.getExtensionConnectedAccounts = async () => { }; +/** + * Get selected account connected in extension + */ +binance_utils.getSelectedAccount = async () => { + const chainVerbose = binance_utils.getChainVerbose(BinanceChain.chainId); + const accounts = await binance_utils.getExtensionConnectedAccounts(); + address = accounts && accounts[0]['addresses'].find(address => address.type === chainVerbose.addressType).address; + return address +} + + /** * Sign and transfer token to another address via extension and returns txn hash - * @param {Number} amount + * @param {Number} amount : in wei * @param {String} to_address + * @param {String} token_name * @param {String} from_address : optional, if not passed takes account first account from getExtensionConnectedAccounts */ -binance_utils.transferViaExtension = async (amount, to_address, from_address) => { +binance_utils.transferViaExtension = async (amount, to_address, from_address, token_name) => { - return new Promise(async(resolve, reject) => { + return new Promise(async (resolve, reject) => { const isConnected = await BinanceChain.isConnected(); @@ -76,40 +128,74 @@ binance_utils.transferViaExtension = async (amount, to_address, from_address) => const chainVerbose = binance_utils.getChainVerbose(BinanceChain.chainId); if (!from_address) { - const accounts = await binance_utils.getExtensionConnectedAccounts(); - from_address = accounts && accounts[0]['addresses'].find(address => address.type === chainVerbose.addressType).address; + from_address = await binance_utils.getSelectedAccount(); } - + if (!from_address) { reject('transferViaExtension: missing param from_address'); } - - const account_balance = await binance_utils.getAddressBalance(from_address); - if (Number(account_balance) < amount) { - reject(`transferViaExtension: insufficent balance in address ${from_address}`); + if (!token_name) { + token_name = 'BNB'; } - if (chainVerbose.addressType === 'eth') { - const params = [ - { - from: from_address, - to: to_address, - value: '0x' + amount.toString(16) // convert amount to hex - }, - ]; - - BinanceChain - .request({ - method: 'eth_sendTransaction', - params - }) - .then(txHash => { + if (token_name === 'BNB') { + + const account_balance = await binance_utils.getAddressBalance(from_address); + + if (Number(account_balance) < amount) { + reject(`transferViaExtension: insufficent balance in address ${from_address}`); + } + + if (chainVerbose.addressType === 'eth') { + const params = [ + { + from: from_address, + to: to_address, + value: '0x' + amount.toString(16) // convert amount to hex + }, + ]; + + try { + const txHash = await jsonRpcRequest('eth_sendTransaction', params) + resolve(txHash) + } catch (error) { + reject('transferViaExtension: something went wrong' + error); + } + } + + } else if (token_name === 'BUSD') { + + const account_balance = await binance_utils.getAddressTokenBalance(from_address); + + if (Number(account_balance) < amount ) { + reject(`transferViaExtension: insufficent balance in address ${from_address}`); + } + + if (chainVerbose.addressType === 'eth') { + try { + const methodSignature = await jsonRpcRequest( + 'web3_sha3', + ['transfer(address, uint256)'] + ); + const method_id = methodSignature.substr(0, 10); + const amount = amount.toString(16).padStart(64, '0'); // convert to hex and pad with zeroes + const to_address = to_address.substr(2).padStart(64, '0'); // remove 0x and pad with zeroes + + const params = [ + { + from: from_address, + to: '0xe9e7cea3dedca5984780bafc599bd69add087d56', // BUSD token contract address + data: method_id + to_address + amount + }, + ] + const txHash = await jsonRpcRequest('eth_sendTransaction', params); + resolve(txHash); - }) - .catch(error => { + } catch (error) { reject('transferViaExtension: something went wrong' + error); - }); + } + } } }); }; diff --git a/app/assets/v2/js/pages/bounty_detail/binance_extension.js b/app/assets/v2/js/pages/bounty_detail/binance_extension.js index ef026305be3..d0c0f9820fb 100644 --- a/app/assets/v2/js/pages/bounty_detail/binance_extension.js +++ b/app/assets/v2/js/pages/bounty_detail/binance_extension.js @@ -7,7 +7,8 @@ const payWithBinanceExtension = (fulfillment_id, to_address, vm, modal) => { binance_utils.transferViaExtension( amount * 10 ** vm.decimals, to_address, - from_address + from_address, + vm.bounty.token_name ).then(txn => { callback(null, from_address, txn); }).catch(err => { diff --git a/app/assets/v2/js/pages/bounty_details2.js b/app/assets/v2/js/pages/bounty_details2.js index 03be9e3f506..a5f580078c7 100644 --- a/app/assets/v2/js/pages/bounty_details2.js +++ b/app/assets/v2/js/pages/bounty_details2.js @@ -79,6 +79,7 @@ Vue.mixin({ break; case 'BNB': + case 'BUSD': url = `https://bscscan.com/tx/${txn}`; break; @@ -126,6 +127,7 @@ Vue.mixin({ break; case 'BNB': + case 'BUSD': url = `https://bscscan.com/address/${address}`; break; @@ -340,6 +342,7 @@ Vue.mixin({ break; case 'BNB': + case 'BUSD': tenant = 'BINANCE'; break; diff --git a/app/assets/v2/js/pages/new_bounty.js b/app/assets/v2/js/pages/new_bounty.js index de876bee74f..8ab28afacb6 100644 --- a/app/assets/v2/js/pages/new_bounty.js +++ b/app/assets/v2/js/pages/new_bounty.js @@ -74,6 +74,11 @@ Vue.mixin({ }); }, + getBinanceSelectedAccount: async function() { + let vm = this; + + vm.form.funderAddress = await binance_utils.getSelectedAccount(); + }, getAmount: function(token) { let vm = this; @@ -680,6 +685,10 @@ Vue.mixin({ await onConnect(); } + if (val === '56') { + this.getBinanceSelectedAccount(); + } + this.getTokens(); } } diff --git a/app/dashboard/templates/bounty/new_bounty.html b/app/dashboard/templates/bounty/new_bounty.html index b8e411aec99..98f1faaf625 100644 --- a/app/dashboard/templates/bounty/new_bounty.html +++ b/app/dashboard/templates/bounty/new_bounty.html @@ -420,7 +420,7 @@

Fund Issue

-
+