status-mobile/resources/js/bots/transactor_personal/bot.js

551 lines
17 KiB
JavaScript

function calculateFee(n, tx) {
var estimatedGas = 21000;
if (tx !== null) {
estimatedGas = web3.eth.estimateGas(tx);
}
var gasMultiplicator = Math.pow(1.4, n).toFixed(3);
var weiFee = web3.eth.gasPrice * gasMultiplicator * estimatedGas;
// force fee in eth to be of BigNumber type
var ethFee = web3.toBigNumber(web3.fromWei(weiFee, "ether"));
// always display 7 decimal places
return ethFee.toFixed(7);
}
function calculateGasPrice(n) {
var gasMultiplicator = Math.pow(1.4, n).toFixed(3);
return web3.eth.gasPrice * gasMultiplicator;
}
status.defineSubscription(
"calculatedFee",
{value: ["sliderValue"], tx: ["transaction"]},
function (params) {
return calculateFee(params.value, params.tx);
}
);
function getFeeExplanation(n) {
return I18n.t('send_explanation') + I18n.t('send_explanation_' + (n + 2));
}
status.defineSubscription(
"feeExplanation",
{value: ["sliderValue"]},
function(params) {
return getFeeExplanation(params.value);
}
)
function amountParameterBox(params, context) {
if (!params["bot-db"]) {
params["bot-db"] = {};
}
var contactAddress = context.to;
var txData;
var amount;
try {
amount = params.args[0];
txData = {
to: contactAddress,
value: web3.toWei(amount) || 0
};
} catch (err) {
amount = null;
txData = {
to: contactAddress,
value: 0
};
}
var sliderValue = params["bot-db"]["sliderValue"] || 0;
status.setDefaultDb({
transaction: txData,
calculatedFee: calculateFee(sliderValue, txData),
feeExplanation: getFeeExplanation(sliderValue),
sliderValue: sliderValue
});
return {
title: I18n.t('send_title'),
showBack: true,
markup: status.components.scrollView(
{
keyboardShouldPersistTaps: "always"
},
[status.components.view(
{
flex: 1
},
[
status.components.text(
{
style: {
fontSize: 14,
color: "rgb(147, 155, 161)",
paddingTop: 12,
paddingLeft: 16,
paddingRight: 16,
paddingBottom: 20
}
},
I18n.t('send_specify_amount')
),
status.components.touchable(
{
onPress: status.components.dispatch([status.events.FOCUS_INPUT, []])
},
status.components.view(
{
flexDirection: "row",
alignItems: "center",
textAlign: "center",
justifyContent: "center"
},
[
status.components.text(
{
font: "light",
numberOfLines: 1,
ellipsizeMode: "tail",
style: {
maxWidth: 250,
fontSize: 38,
marginLeft: 8,
color: "black"
}
},
amount || "0.00"
),
status.components.text(
{
font: "light",
style: {
fontSize: 38,
marginLeft: 8,
color: "rgb(147, 155, 161)"
}
},
I18n.t('eth')
),
]
)
),
status.components.text(
{
style: {
fontSize: 14,
color: "rgb(147, 155, 161)",
paddingTop: 14,
paddingLeft: 16,
paddingRight: 16,
paddingBottom: 5
}
},
I18n.t('send_fee')
),
status.components.view(
{
flexDirection: "row"
},
[
status.components.text(
{
style: {
fontSize: 17,
color: "black",
paddingLeft: 16
}
},
[status.components.subscribe(["calculatedFee"])]
),
status.components.text(
{
style: {
fontSize: 17,
color: "rgb(147, 155, 161)",
paddingLeft: 4,
paddingRight: 4
}
},
I18n.t('eth')
)
]
),
status.components.slider(
{
maximumValue: 2,
minimumValue: -2,
onSlidingComplete: status.components.dispatch(
[status.events.UPDATE_DB, "sliderValue"]
),
step: 1,
style: {
marginLeft: 16,
marginRight: 16
}
}
),
status.components.view(
{
flexDirection: "row"
},
[
status.components.text(
{
style: {
flex: 1,
fontSize: 14,
color: "rgb(147, 155, 161)",
paddingLeft: 16,
alignSelf: "flex-start"
}
},
I18n.t('send_cheaper')
),
status.components.text(
{
style: {
flex: 1,
fontSize: 14,
color: "rgb(147, 155, 161)",
paddingRight: 16,
alignSelf: "flex-end",
textAlign: "right"
}
},
I18n.t('send_faster')
)
]
),
status.components.text(
{
style: {
fontSize: 14,
color: "black",
paddingTop: 16,
paddingLeft: 16,
paddingRight: 16,
paddingBottom: 16,
lineHeight: 24
}
},
[status.components.subscribe(["feeExplanation"])]
)
]
)]
)
};
}
var paramsSend = [
{
name: "amount",
type: status.types.NUMBER,
suggestions: amountParameterBox
}
];
function validateSend(params, context) {
if (!params["bot-db"]) {
params["bot-db"] = {};
}
if (!params["amount"]) {
return {
markup: status.components.validationMessage(
I18n.t('validation_title'),
I18n.t('validation_amount_specified')
)
};
}
var amount = params["amount"].replace(",", ".");
var amountSplitted = amount.split(".");
if (amountSplitted.length === 2 && amountSplitted[1].length > 18) {
return {
markup: status.components.validationMessage(
I18n.t('validation_title'),
I18n.t('validation_amount_is_too_small')
)
};
}
if (isNaN(parseFloat(params.amount.replace(",", ".")))) {
return {
markup: status.components.validationMessage(
I18n.t('validation_title'),
I18n.t('validation_invalid_number')
)
};
}
try {
var val = web3.toWei(amount, "ether");
if (val < 0) {
throw new Error();
}
} catch (err) {
return {
markup: status.components.validationMessage(
I18n.t('validation_title'),
I18n.t('validation_invalid_number')
)
};
}
var balance = web3.eth.getBalance(context.from);
var fee = calculateFee(
params["bot-db"]["sliderValue"],
{
to: context.to,
value: val
}
);
if (bn(val).plus(bn(web3.toWei(fee, "ether"))).greaterThan(bn(balance))) {
return {
markup: status.components.validationMessage(
I18n.t('validation_title'),
I18n.t('validation_insufficient_amount')
+ web3.fromWei(balance, "ether")
+ " ETH)"
)
};
}
}
function handleSend(params, context) {
var val = web3.toWei(params["amount"].replace(",", "."), "ether");
var gasPrice = calculateGasPrice(params["bot-db"]["sliderValue"]);
var data = {
from: context.from,
to: context.to,
value: val
};
if (gasPrice) {
data.gasPrice = gasPrice;
}
web3.eth.sendTransaction(data, function(error, hash) {
if (error) {
// Do nothing, as error handling will be done as response to transaction.failed event from go
} else {
status.sendSignal("handler-result", {
status: "success",
hash: hash,
origParams: context["orig-params"]
});
}
});
// async handler, so we don't return anything immediately
}
function previewSend(params, context) {
var amountStyle = {
fontSize: 36,
color: "#000000",
height: 40
};
var amount = status.components.view(
{
flexDirection: "column",
alignItems: "flex-end",
maxWidth: 250
},
[status.components.text(
{
style: amountStyle,
numberOfLines: 1,
ellipsizeMode: "tail",
font: "light"
},
status.localizeNumber(params.amount, context.delimiter, context.separator)
)]);
var currency = status.components.view(
{
style: {
flexDirection: "column",
justifyContent: "flex-end",
paddingBottom: 0
}
},
[status.components.text(
{
style: {
color: "#9199a0",
fontSize: 16,
lineHeight: 18,
marginLeft: 7.5
}
},
I18n.t('eth')
)]
);
var row = status.components.view(
{
style: {
flexDirection: "row",
justifyContent: "space-between",
marginTop: 8,
marginBottom: 8
}
},
[amount, currency]
);
return {
markup: status.components.view(
{
style: {
flexDirection: "column"
}
},
[row]
)
};
}
function shortPreviewSend(params, context) {
return {
markup: status.components.text(
{},
I18n.t('send_title') + ": "
+ status.localizeNumber(params.amount, context.delimiter, context.separator)
+ " ETH"
)
};
}
var send = {
name: "send",
scope: ["personal-chats"],
icon: "money_white",
color: "#5fc48d",
title: I18n.t('send_title'),
description: I18n.t('send_description'),
params: paramsSend,
validator: validateSend,
handler: handleSend,
asyncHandler: true,
preview: previewSend,
shortPreview: shortPreviewSend
};
status.command(send);
status.response(send);
var paramsRequest = [
{
name: "amount",
type: status.types.NUMBER
}
];
status.command({
name: "request",
scope: ["personal-chats"],
color: "#5fc48d",
title: I18n.t('request_title'),
description: I18n.t('request_description'),
params: paramsRequest,
handler: function (params, context) {
var val = params["amount"].replace(",", ".");
return {
event: "request",
request: {
command: "send",
params: {
amount: val
},
prefill: [val]
}
};
},
preview: function (params, context) {
var row = status.components.text(
{},
I18n.t('request_requesting') + " "
+ status.localizeNumber(params.amount, context.delimiter, context.separator)
+ " ETH"
);
return {
markup: status.components.view(
{
style: {
flexDirection: "column"
}
},
[row]
)
};
},
shortPreview: function (params, context) {
return {
markup: status.components.text(
{},
I18n.t('request_requesting') + " "
+ status.localizeNumber(params.amount, context.delimiter, context.separator)
+ " ETH"
)
};
},
validator: function (params) {
if (!params["bot-db"]) {
params["bot-db"] = {};
}
if (!params["amount"]) {
return {
markup: status.components.validationMessage(
I18n.t('validation_title'),
I18n.t('validation_amount_specified')
)
};
}
var amount = params.amount.replace(",", ".");
var amountSplitted = amount.split(".");
if (amountSplitted.length === 2 && amountSplitted[1].length > 18) {
return {
markup: status.components.validationMessage(
I18n.t('validation_title'),
I18n.t('validation_amount_is_too_small')
)
};
}
if (isNaN(parseFloat(params.amount.replace(",", ".")))) {
return {
markup: status.components.validationMessage(
I18n.t('validation_title'),
I18n.t('validation_invalid_number')
)
};
}
try {
var val = web3.toWei(amount, "ether");
if (val < 0) {
throw new Error();
}
} catch (err) {
return {
markup: status.components.validationMessage(
I18n.t('validation_title'),
I18n.t('validation_invalid_number')
)
};
}
}
});