- Added bunkTransferTokens: bulk transfer tokens to multiple receivers
- usage: floWebWallet.bunkTransferTokens(sender, privKey, token, receivers)
sender: floID of sender
privKey: private key of sender
token: token to send
receivers: an object representing receiver floID and token amount
ie, {receiverID1: amount1, receiverID2: amount2....}
eg: {Fxyz: 123, Fpqr:321, Fabc: 987}
166 lines
7.3 KiB
JavaScript
166 lines
7.3 KiB
JavaScript
(function (EXPORTS) { //floTokenAPI v1.0.4
|
|
/* Token Operator to send/receive tokens via blockchain using API calls*/
|
|
'use strict';
|
|
const tokenAPI = EXPORTS;
|
|
|
|
const DEFAULT = {
|
|
apiURL: floGlobals.tokenURL || "https://ranchimallflo.duckdns.org/",
|
|
currency: floGlobals.currency || "rupee"
|
|
}
|
|
|
|
Object.defineProperties(tokenAPI, {
|
|
URL: {
|
|
get: () => DEFAULT.apiURL
|
|
},
|
|
currency: {
|
|
get: () => DEFAULT.currency,
|
|
set: currency => DEFAULT.currency = currency
|
|
}
|
|
});
|
|
|
|
if (floGlobals.currency) tokenAPI.currency = floGlobals.currency;
|
|
|
|
Object.defineProperties(floGlobals, {
|
|
currency: {
|
|
get: () => DEFAULT.currency,
|
|
set: currency => DEFAULT.currency = currency
|
|
}
|
|
});
|
|
|
|
const fetch_api = tokenAPI.fetch = function (apicall) {
|
|
return new Promise((resolve, reject) => {
|
|
console.debug(DEFAULT.apiURL + apicall);
|
|
fetch(DEFAULT.apiURL + apicall).then(response => {
|
|
if (response.ok)
|
|
response.json().then(data => resolve(data));
|
|
else
|
|
reject(response)
|
|
}).catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
const getBalance = tokenAPI.getBalance = function (floID, token = DEFAULT.currency) {
|
|
return new Promise((resolve, reject) => {
|
|
fetch_api(`api/v1.0/getFloAddressBalance?token=${token}&floAddress=${floID}`)
|
|
.then(result => resolve(result.balance || 0))
|
|
.catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
tokenAPI.getTx = function (txID) {
|
|
return new Promise((resolve, reject) => {
|
|
fetch_api(`api/v1.0/getTransactionDetails/${txID}`).then(res => {
|
|
if (res.result === "error")
|
|
reject(res.description);
|
|
else if (!res.parsedFloData)
|
|
reject("Data piece (parsedFloData) missing");
|
|
else if (!res.transactionDetails)
|
|
reject("Data piece (transactionDetails) missing");
|
|
else
|
|
resolve(res);
|
|
}).catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
tokenAPI.sendToken = function (privKey, amount, receiverID, message = "", token = DEFAULT.currency, options = {}) {
|
|
return new Promise((resolve, reject) => {
|
|
let senderID = floCrypto.getFloID(privKey);
|
|
if (typeof amount !== "number" || isNaN(amount) || amount <= 0)
|
|
return reject("Invalid amount");
|
|
getBalance(senderID, token).then(bal => {
|
|
if (amount > bal)
|
|
return reject(`Insufficient ${token}# balance`);
|
|
floBlockchainAPI.writeData(senderID, `send ${amount} ${token}# ${message}`, privKey, receiverID, options)
|
|
.then(txid => resolve(txid))
|
|
.catch(error => reject(error))
|
|
}).catch(error => reject(error))
|
|
});
|
|
}
|
|
|
|
function sendTokens_raw(privKey, receiverID, token, amount, utxo, vout, scriptPubKey) {
|
|
return new Promise((resolve, reject) => {
|
|
var trx = bitjs.transaction();
|
|
trx.addinput(utxo, vout, scriptPubKey)
|
|
trx.addoutput(receiverID, floBlockchainAPI.sendAmt);
|
|
trx.addflodata(`send ${amount} ${token}#`);
|
|
var signedTxHash = trx.sign(privKey, 1);
|
|
floBlockchainAPI.broadcastTx(signedTxHash)
|
|
.then(txid => resolve([receiverID, txid]))
|
|
.catch(error => reject([receiverID, error]))
|
|
})
|
|
}
|
|
|
|
//bulk transfer tokens
|
|
tokenAPI.bunkTransferTokens = function (sender, privKey, token, receivers) {
|
|
return new Promise((resolve, reject) => {
|
|
if (typeof receivers !== 'object')
|
|
return reject("receivers must be object in format {receiver1: amount1, receiver2:amount2...}")
|
|
|
|
let receiver_list = Object.keys(receivers), amount_list = Object.values(receivers);
|
|
let invalidReceivers = receiver_list.filter(id => !floCrypto.validateFloID(id));
|
|
let invalidAmount = amount_list.filter(val => typeof val !== 'number' || val <= 0);
|
|
if (invalidReceivers.length)
|
|
return reject(`Invalid receivers: ${invalidReceivers}`);
|
|
else if (invalidAmount.length)
|
|
return reject(`Invalid amounts: ${invalidAmount}`);
|
|
|
|
if (receiver_list.length == 0)
|
|
return reject("Receivers cannot be empty");
|
|
|
|
if (receiver_list.length == 1) {
|
|
let receiver = receiver_list[0], amount = amount_list[0];
|
|
floTokenAPI.sendToken(privKey, amount, receiver, "", token)
|
|
.then(txid => resolve({ success: { [receiver]: txid } }))
|
|
.catch(error => reject(error))
|
|
} else {
|
|
//check for token balance
|
|
floTokenAPI.getBalance(sender, token).then(token_balance => {
|
|
let total_token_amout = amount_list.reduce((a, e) => a + e, 0);
|
|
if (total_token_amout > token_balance)
|
|
return reject(`Insufficient ${token}# balance`);
|
|
|
|
//split utxos
|
|
floBlockchainAPI.splitUTXOs(sender, privKey, receiver_list.length).then(split_txid => {
|
|
//wait for the split utxo to get confirmation
|
|
floBlockchainAPI.waitForConfirmation(split_txid).then(split_tx => {
|
|
//send tokens using the split-utxo
|
|
var scriptPubKey = split_tx.vout[0].scriptPubKey.hex;
|
|
let promises = [];
|
|
for (let i in receiver_list)
|
|
promises.push(sendTokens_raw(privKey, receiver_list[i], token, amount_list[i], split_txid, i, scriptPubKey));
|
|
Promise.allSettled(promises).then(results => {
|
|
let success = Object.fromEntries(results.filter(r => r.status == 'fulfilled').map(r => r.value));
|
|
let failed = Object.fromEntries(results.filter(r => r.status == 'rejected').map(r => r.reason));
|
|
resolve({ success, failed });
|
|
})
|
|
}).catch(error => reject(error))
|
|
}).catch(error => reject(error))
|
|
}).catch(error => reject(error))
|
|
}
|
|
|
|
})
|
|
}
|
|
|
|
tokenAPI.getAllTxs = function (floID, token = DEFAULT.currency) {
|
|
return new Promise((resolve, reject) => {
|
|
fetch_api(`api/v1.0/getFloAddressTransactions?token=${token}&floAddress=${floID}`)
|
|
.then(result => resolve(result))
|
|
.catch(error => reject(error))
|
|
})
|
|
}
|
|
|
|
const util = tokenAPI.util = {};
|
|
|
|
util.parseTxData = function (txData) {
|
|
let parsedData = {};
|
|
for (let p in txData.parsedFloData)
|
|
parsedData[p] = txData.parsedFloData[p];
|
|
parsedData.sender = txData.transactionDetails.vin[0].addr;
|
|
for (let vout of txData.transactionDetails.vout)
|
|
if (vout.scriptPubKey.addresses[0] !== parsedData.sender)
|
|
parsedData.receiver = vout.scriptPubKey.addresses[0];
|
|
parsedData.time = txData.transactionDetails.time;
|
|
return parsedData;
|
|
}
|
|
|
|
})('object' === typeof module ? module.exports : window.floTokenAPI = {}); |