tronair-gui
Version:
Web application/GUI for performing airdrops on the TRON blockchain
1,432 lines (1,306 loc) • 48.1 kB
JavaScript
import React, { Component } from 'react';
import Swal from 'sweetalert2';
import ModalAtlas, { ModalTransition } from '@atlaskit/modal-dialog';
import { Icon, Slider, Tooltip, Row, Col, Button, Modal } from 'antd';
import { Checkbox, InputNumber, Drawer } from 'antd';
import "antd/dist/antd.css";
import Select from "react-select";
import makeAnimated from 'react-select/lib/animated';
import './App.css';
const and_or_choice = [
{value: 0,label: 'and'},
{value: 1,label: 'or'},
]
const drop_types = [
{value: 0, label: 'Total Drop Amount', type: 'total'},
{value: 1, label: 'Drop Amount to Each', type: 'each'},
{value: 2, label: 'Drop By Ratio', type: 'ratio'},
]
const fetch_head = {
method: 'GET',
headers: {}
}
export default class Options extends Component {
constructor(props) {
super(props);
const w_list = this.props.appActions.dataSlots.ds_airdrop_array;
var wl = w_list.length;
var smax = 1;
var ts = 1;
if (wl > 0) {
var tm = this.props.appActions.dataSlots.ds_max_amount/wl;
ts = Math.pow(10, Math.round(tm).toString().length - 2)*2;
smax = (ts*Math.round(tm/ts))-ts;
}
var curr_addr = [];
for (var i=0; i<w_list.length; i++) {
var temp = {
value: i,
label: w_list[i].address + ' (balance: ' + w_list[i].balance + ')',
}
curr_addr.push(temp);
}
var a_bl = this.props.appActions.dataSlots.ds_airdrop.blacklist;
var bl = [];
for (i=0; i < a_bl.length; i++) {
temp = {
value: i,
label: a_bl[i]
};
bl.push(temp);
}
this.state = {
isOpen: false,
adv_visible: false,
slide_max: smax,
max_step: ts,
ex_amount: 0,
slide_disabled: true,
addr_list: curr_addr,
bl_choice: bl,
num_ex: 0,
drop_min: 0,
drop_max: 1,
max_hv: Infinity,
};
}
open = () => {
this.setState({
isOpen: true,
adv_visible: false,
});
}
close = () => {
this.update_whitelist();
this.setState({
isOpen: false,
adv_visible: true,
});
}
cancel = () => {
this.setState({ isOpen: false });
}
openAdvanced = () => {
this.setState({
adv_visible: true,
});
}
closeAdvanced = () => {
this.setState({
adv_visible: false,
});
}
numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
update_hold_vote = () => {
const is_hold = this.props.appActions.dataSlots.ds_drop_to_holders;
const hold_addr = this.props.appActions.dataSlots.ds_token_hold_addr;
const is_vote = this.props.appActions.dataSlots.ds_drop_to_voters;
const vote_addr = this.props.appActions.dataSlots.ds_sr_vote_addr;
if (is_hold === 'true' && is_vote === 'true') {
if (hold_addr !== '' && vote_addr !== '') {
Swal.fire({
title: 'Updating holder and voter information...',
allowOutsideClick: false,
allowEscapeKey: false,
});
Swal.showLoading();
this.get_ad_holders()
.then(h_list => {
this.get_ad_voters()
.then(v_list => {
this.props.appActions.updateDataSlot("ds_holder_list", h_list);
this.props.appActions.updateDataSlot("ds_voter_list", v_list);
})
.catch((ex) => {
console.log(ex);
});
})
.catch((ex) => {
console.log(ex);
});
Swal.close();
}
} else if (is_hold === 'true' && hold_addr !== '') {
this.update_token_holders();
} else if (is_vote === 'true' && vote_addr !== '') {
this.update_token_voters();
}
}
drop_token_callback = (choice: string) => {
var damt = 0;
var dpick = '';
const ds = this.props.appActions.dataSlots;
var dtype = ds.ds_drop_type.type;
var adrop = ds.ds_airdrop;
if (choice) {
if (choice.label === 'TRX') {
adrop.trx = true;
adrop.token_name = 'TRX';
adrop.token_abbr = 'TRX';
adrop.token_id = '0';
adrop.token_precision = 6;
} else {
adrop.trx = false;
adrop.token_name = choice.label;
adrop.token_abbr = choice.label;
adrop.token_id = choice.id;
adrop.token_precision = choice.precision;
}
adrop.token_balance = choice.value;
adrop.amount = choice.value;
dpick = choice.label;
damt = choice.value;
} else {
adrop.trx = false;
adrop.token_name = '';
adrop.token_abbr = '';
adrop.token_id = '';
adrop.token_precision = choice.precision;
}
this.props.appActions.updateDataSlot("ds_airdrop", adrop);
this.props.appActions.updateDataSlot("ds_drop_token_def", choice);
this.props.appActions.updateDataSlot("ds_drop_token", dpick);
this.props.appActions.updateDataSlot("ds_drop_lim", damt);
if (dtype && ds.ds_drop_num) {
if (dtype === 'each' && ds.ds_drop_num > 0) {
damt = damt/ds.ds_drop_num;
} else if (dtype === 'ratio' && ds.ds_max_amount > 0) {
damt = damt/ds.ds_max_amount;
}
}
if (adrop.token_precision === 0) {damt = Math.floor(damt);};
// this.props.appActions.updateDataSlot("ds_drop_amount", damt);
this.props.appActions.updateDataSlot("ds_drop_amount", 0);
this.update_whitelist();
}
get_holder_urls(np,ta) {
var url_arr = [];
var n_loop = 1;
var sort_str = '-balance';
if (np === 200) {n_loop = 2};
for (var i=0; i<n_loop; i++) {
if (i === 1) {sort_str = 'balance'};
for (var j=0; j<np; j++) {
for (var k=0; k<3; k++) {
var curr_num = Math.floor((50*j) - (50*(k/3)));
if (curr_num < 0) {curr_num = 0};
var url = 'https://apilist.tronscan.org/api/';
url += 'tokenholders?sort=' + sort_str;
url += '&limit=50&start=' + curr_num;
url += '&count=true&address=' + ta;
url_arr.push(url);
}
}
}
return url_arr;
}
async update_token_holders() {
const t2_name = this.props.appActions.dataSlots.ds_airdrop.token2_name;
const token_addr = this.props.appActions.dataSlots.ds_airdrop.token2_ownerAddress;
Swal.fire({
title: 'Loading ' + t2_name + ' holder information...',
allowOutsideClick: false,
allowEscapeKey: false,
});
Swal.showLoading();
var url = 'https://apilist.tronscan.org/api/';
url += 'tokenholders?sort=-balance&limit=1';
url += '&start=0&count=true&address=' + token_addr;
let rdata = await fetch(url,fetch_head)
.then(resp => resp.json())
.catch(e => {
Swal.close();
Modal.error({
title: 'Error Finding Holders',
content: 'There was an error while trying to load the token holders API resource. Please try again later and we apologize for the inconvenience.',
});
return;
})
var h_total = rdata.total;
var num_pages = Math.floor(rdata.total/50) + 1;
if (num_pages > 200) {num_pages = 200};
var url_arr = this.get_holder_urls(num_pages,token_addr);
const r_list = await Promise.all(url_arr.map(url =>
fetch(url,fetch_head)
.then(resp => resp.json())
.then(r => r.data)
.catch(e => {})
))
var h_list = [];
var curr_list = [];
r_list.forEach(group => {
try {
if (group.length > 0) {
group.forEach(item => {
if (!curr_list.includes(item.address)) {
h_list.push(item);
curr_list.push(item.address);
}
})
}
} catch {}
})
if (h_total < 15000) {
if ((h_total - h_list.length) > 20) {h_list = []};
}
var total_tokens_held = 0;
var max_bal = this.state.drop_max;
if (h_list.length > 0) {
for (var i=0; i < h_list.length; i++) {
if (h_list[i].balance > max_bal) {
max_bal = h_list[i].balance;
}
total_tokens_held += h_list[i].balance;
}
} else {
Swal.close();
Modal.error({
title: 'Error Finding Holders',
content: 'There was an error while trying to load the token holders API resource. Please try again later and we apologize for the inconvenience.',
});
return;
}
var adrop = this.props.appActions.dataSlots.ds_airdrop;
adrop.max_thresh = max_bal;
this.setState({
drop_min: 0,
drop_max: max_bal
});
this.props.appActions.updateDataSlot("ds_airdrop", adrop);
this.props.appActions.updateDataSlot("ds_max_amount", total_tokens_held);
this.props.appActions.updateDataSlot("ds_holder_list", h_list);
this.update_whitelist();
Swal.close();
}
get_voter_urls(np,va) {
var url_arr = [];
var n_loop = 1;
var sort_str = '-votes';
if (np === 200) {n_loop = 2};
for (var i=0; i<n_loop; i++) {
if (i === 1) {sort_str = 'votes'};
for (var j=0; j<np; j++) {
for (var k=0; k<3; k++) {
var curr_num = Math.floor((50*j) - (50*(k/3)));
if (curr_num < 0) {curr_num = 0};
var url = 'https://apilist.tronscan.org/api/vote?sort=' + sort_str;
url += '&limit=50&start=' + curr_num + '&candidate=' + va;
url_arr.push(url);
}
}
}
return url_arr;
}
async update_token_voters() {
const sr_name = this.props.appActions.dataSlots.ds_airdrop.SR_name;
const vote_addr = this.props.appActions.dataSlots.ds_airdrop.SR_address;
Swal.fire({
title: 'Loading ' + sr_name + ' voter information...',
allowOutsideClick: false,
allowEscapeKey: false,
});
Swal.showLoading();
var url = 'https://apilist.tronscan.org/api/';
url += 'vote?sort=-votes&limit=1&';
url += 'start=0&candidate=' + vote_addr;
let rdata = await fetch(url,fetch_head)
.then(resp => resp.json())
.catch(e => {
Swal.close();
Modal.error({
title: 'Error Finding Voters',
content: 'There was an error while trying to load the SR voters API resource. Please try again later and we apologize for the inconvenience.',
});
return;
})
var v_total = rdata.total;
var num_pages = Math.floor(rdata.total/50) + 1;
if (num_pages > 200) {num_pages = 200};
var url_arr = this.get_voter_urls(num_pages,vote_addr);
const r_list = await Promise.all(url_arr.map(url =>
fetch(url,fetch_head)
.then(resp => resp.json())
.then(r => r.data)
.catch(e => {})
))
var v_list = [];
var curr_list = [];
var count = 0;
r_list.forEach(group => {
try {
if (group.length > 0) {
group.forEach(item => {
if (!curr_list.includes(item.voterAddress)) {
v_list.push({
index: count,
address: item.voterAddress,
balance: item.votes,
});
curr_list.push(item.voterAddress);
count++;
}
})
}
} catch {}
})
if (v_total < 15000) {
if ((v_total - v_list.length) > 20) {v_list = []};
}
var total_votes = 0;
var max_bal = this.state.drop_max;
if (v_list.length > 0) {
for (var i=0; i < v_list.length; i++) {
if (v_list[i].balance > max_bal) {
max_bal = v_list[i].balance;
}
total_votes += v_list[i].balance;
}
} else {
Swal.close();
Modal.error({
title: 'Error Finding Voters',
content: 'There was an error while trying to load the SR voters API resource. Please try again later and we apologize for the inconvenience.',
});
return;
}
var adrop = this.props.appActions.dataSlots.ds_airdrop;
adrop.max_thresh = max_bal;
this.setState({
drop_min: 0,
drop_max: max_bal
});
this.props.appActions.updateDataSlot("ds_airdrop", adrop);
this.props.appActions.updateDataSlot("ds_max_amount", total_votes);
this.props.appActions.updateDataSlot("ds_voter_list", v_list);
this.update_whitelist();
Swal.close();
}
drop_holder_callback = (choice: string) => {
this.props.appActions.updateDataSlot("ds_holder_list", []);
if (choice) {
var adrop = this.props.appActions.dataSlots.ds_airdrop;
var clab = choice.label;
var strlen = clab.length - 14;
var tname = clab.substr(0, strlen);
adrop.token2_ownerAddress = choice.address;
adrop.token2_name = tname;
adrop.token2_abbr = choice.abbr;
adrop.token2_id = choice.id;
adrop.token2_precision = 0;
this.props.appActions.updateDataSlot("ds_airdrop", adrop);
this.props.appActions.updateDataSlot("ds_token_hold_addr_def", choice);
this.props.appActions.updateDataSlot("ds_token_hold_addr", tname);
this.props.appActions.updateDataSlot("ds_hold_num", choice.num_holders);
this.update_token_holders();
} else {
this.props.appActions.updateDataSlot("ds_token_hold_addr_def", null);
this.props.appActions.updateDataSlot("ds_token_hold_addr", '');
this.props.appActions.updateDataSlot("ds_hold_num", 0);
this.props.appActions.updateDataSlot("ds_max_amount", 0);
this.update_whitelist();
}
}
update_drop_num(drop_type) {
const ds = this.props.appActions.dataSlots;
const curr_lim = (ds.ds_drop_lim) ? ds.ds_drop_lim:0;
const num_rec = (ds.ds_drop_num) ? ds.ds_drop_num:0;
const total_num = (ds.ds_max_amount) ? ds.ds_max_amount:0;
const is_trx = ds.ds_airdrop.trx;
var can_be_weighted = ds.ds_is_weighted;
var dis_weight = false;
var new_amt = curr_lim;
if (drop_type !== 'total') {
if (drop_type === 'each') {
if (num_rec > 0) {new_amt = new_amt/num_rec;};
} else {
if (total_num > 0) {new_amt = new_amt/total_num};
new_amt = (new_amt > 1) ? 1:new_amt;
}
can_be_weighted = 'false';
dis_weight = true;
}
new_amt = Math.floor(new_amt*100)/100;
if (!is_trx && drop_type !== 'ratio') {
new_amt = Math.floor(new_amt);
}
this.props.appActions.updateDataSlot("ds_drop_amount", new_amt);
this.props.appActions.updateDataSlot("ds_can_weight", dis_weight);
this.props.appActions.updateDataSlot("ds_is_weighted", can_be_weighted);
this.update_whitelist();
}
drop_amount_type_callback = (choice: string) => {
this.props.appActions.updateDataSlot("ds_drop_type", choice);
this.update_drop_num(choice.type);
}
drop_and_or_callback = (choice: string) => {
this.props.appActions.updateDataSlot("ds_and_or", choice.label);
this.props.appActions.updateDataSlot("ds_and_or_def", choice);
this.update_whitelist();
}
drop_voter_callback = (choice: string) => {
this.props.appActions.updateDataSlot("ds_voter_list", []);
if (choice) {
var adrop = this.props.appActions.dataSlots.ds_airdrop;
adrop.SR_address = choice.addr;
adrop.SR_name = choice.label;
this.props.appActions.updateDataSlot("ds_airdrop", adrop);
this.props.appActions.updateDataSlot("ds_sr_vote_addr_def", choice);
this.props.appActions.updateDataSlot("ds_sr_vote_addr", choice.label);
this.update_token_voters();
} else {
this.props.appActions.updateDataSlot("ds_sr_vote_addr_def", null);
this.props.appActions.updateDataSlot("ds_sr_vote_addr", '');
this.props.appActions.updateDataSlot("ds_max_amount", 0);
this.update_whitelist();
}
}
checkboxChanged_holder_check = (event) => {
var can_be_weighted = this.props.appActions.dataSlots.ds_is_weighted;
var dis_weight = false;
if (this.props.appActions.dataSlots.ds_drop_to_voters === 'true' && event.target.checked) {
can_be_weighted = 'false';
dis_weight = true;
}
dis_weight = (this.props.appActions.dataSlots.ds_drop_type.type !== 'total') ? true:dis_weight;
this.props.appActions.updateDataSlot("ds_can_weight", dis_weight);
this.props.appActions.updateDataSlot("ds_drop_to_holders", (event.target.checked ? 'true' : 'false'));
this.props.appActions.updateDataSlot("ds_is_weighted", can_be_weighted);
this.update_can_review();
if (event.target.checked && this.props.appActions.dataSlots.ds_token_hold_addr === '') {
return;
}
this.update_whitelist();
}
checkboxChanged_voter_check = (event) => {
var can_be_weighted = this.props.appActions.dataSlots.ds_is_weighted;
var dis_weight = false;
if (event.target.checked && this.props.appActions.dataSlots.ds_drop_to_holders === 'true') {
can_be_weighted = 'false';
dis_weight = true;
}
dis_weight = (this.props.appActions.dataSlots.ds_drop_type.type !== 'total') ? true:dis_weight;
this.props.appActions.updateDataSlot("ds_can_weight", dis_weight);
this.props.appActions.updateDataSlot("ds_drop_to_voters", (event.target.checked ? 'true' : 'false'));
this.props.appActions.updateDataSlot("ds_is_weighted", can_be_weighted);
this.update_can_review();
if (event.target.checked && this.props.appActions.dataSlots.ds_sr_vote_addr === '') {
return;
}
this.update_whitelist();
}
changed_drop_amount = (value) => {
const ds = this.props.appActions.dataSlots;
if (!isNaN(value) && value >= 0) {
if (ds.ds_airdrop.token_precision > 0 || ds.ds_drop_type.type === 'ratio') {
this.props.appActions.updateDataSlot("ds_drop_amount", value);
} else {
this.props.appActions.updateDataSlot("ds_drop_amount", Math.floor(value));
}
this.update_whitelist();
this.update_can_review();
}
}
checkboxChanged_weighted_check = (event) => {
this.props.appActions.updateDataSlot("ds_is_weighted", (event.target.checked ? 'true' : 'false'));
this.update_whitelist();
}
update_can_review () {
const ds = this.props.appActions.dataSlots;
const dlim = ds.ds_drop_lim;
var n = Number(ds.ds_drop_amount);
if (n !== Infinity && n > 0) {
if (ds.ds_drop_to_holders === 'true' || ds.ds_drop_to_voters === 'true') {
if (ds.ds_drop_type.type === 'each') {n = n*ds.ds_drop_num;};
if (ds.ds_drop_type.type === 'ratio') {n = n*ds.ds_max_amount;};
if (n <= dlim && n > 0) {
if (ds.ds_drop_token !== '') {
if (ds.ds_token_hold_addr !== '' || ds.ds_sr_vote_addr !== '') {
if (ds.ds_help_pct > 0 && ds.ds_bw_pct > 0) {
this.props.appActions.updateDataSlot("ds_can_review", 'true');
return;
}
}
}
}
}
}
this.props.appActions.updateDataSlot("ds_can_review", 'false');
}
sliderChange = (value) => {
this.setState({ ex_amount: value });
}
slideFormatter = (value) => {
try {
if (this.props.appActions.dataSlots.ds_drop_to_holders === 'true') {
return `${value} tokens`
}
return `${value} votes`
} catch (err) {
console.log(err);
}
return value
}
render_drop_text (d_amt) {
var n = Number(d_amt);
const ds = this.props.appActions.dataSlots;
var d_tkn = ds.ds_drop_token;
var h = ds.ds_drop_to_holders;
var h_name = ds.ds_token_hold_addr;
var v = ds.ds_drop_to_voters;
var v_name = ds.ds_sr_vote_addr;
var ao = ds.ds_and_or;
var w = ds.ds_is_weighted;
var t = ds.ds_drop_type.type;
var nr = ds.ds_drop_num;
var xa = this.state.ex_amount;
var dm = ds.ds_airdrop.min_thresh;
if (!d_tkn) return 'Choose one of your tokens to airdrop.';
if (isNaN(n)) return 'Drop amount must be a valid number.';
if (h !== 'true' && v !== 'true') return 'Choose to either drop to token holders or SR voters.';
if (n <= 0) return 'Drop amount must greater than zero.';
if (isNaN(this.state.ex_amount)) return 'Use the slider to choose an amount.';
if (h === 'true' && !h_name) return 'Make a selection from the token holders dropdown list.';
if (v === 'true' && !v_name) return 'Make a selection from the SR voters dropdown list.';
if (nr === 0) return 'You must drop to at least one address.';
if (ds.ds_bw_pct <= 0) return 'You do not have enough TRX/bandwidth to complete this drop.';
var total_dropped = n;
if (t === 'each') {total_dropped = n*nr};
if (t === 'ratio') {total_dropped = n*ds.ds_max_amount};
if (total_dropped > ds.ds_drop_lim) return 'You do not have enough tokens to drop.';
var temp_num = n;
if (w === 'true') {
temp_num = (xa/ds.ds_max_amount)*n;
temp_num = Math.round(temp_num*100)/100;
} else {
if (t === 'total') {temp_num = Math.round(n/nr*100)/100};
if (t === 'each') {xa = 1};
if (t === 'ratio') {
if (temp_num < 1) {
var n_dec = (-Math.floor(Math.log10(temp_num)/Math.log10(10) + 1)) + 1;
// n_dec = n_dec*10;
xa = Math.pow(10,n_dec);
// xa = Math.ceil(1/temp_num)*n_dec;
}
temp_num = temp_num*xa;
}
}
if (isNaN(temp_num)) {
return 'You do not have enough tokens to drop.';
}
var temp_str = 'An address ';
if (h === 'true' && v === 'true') {
if (h_name === '') return 'Please choose a token from the dropdown list.'
if (v_name === '') return 'Please choose an SR candidate from the dropdown list.'
temp_str += 'holding ' + h_name + ' ' + ao + ' voting for ' + v_name;
} else if (h === 'true') {
if (h_name === '') return 'Please choose a token from the dropdown list.'
temp_str += 'holding ';
if (w === 'true' || t === 'ratio') {
temp_str += this.numberWithCommas(xa) + ' ' + h_name;
} else {
if (dm > 0) {
temp_str += 'more than ' + this.numberWithCommas(dm) + ' ' + h_name;
} else {
temp_str += 'any amount of ' + h_name;
}
}
} else {
if (v_name === '') return 'Please choose an SR candidate from the dropdown list.'
temp_str += 'with ';
if (w === 'true' || t === 'ratio') {
temp_str += this.numberWithCommas(xa) + ' votes for ' + v_name;
} else {
if (dm > 0) {
temp_str += 'more than ' + this.numberWithCommas(dm) + ' votes for ' + v_name;
} else {
temp_str += 'any amount of votes for ' + v_name;
}
}
}
if (!ds.ds_airdrop.trx) {
temp_num = Math.floor(temp_num);
}
temp_str += ' will receive ' + this.numberWithCommas(temp_num) + ' ' + d_tkn + '.';
return temp_str;
}
set_minimum = (value) => {
if (!isNaN(Number(value)) && value !== '') {
this.setState({ drop_min: value });
}
}
set_maximum = (value) => {
if (!isNaN(Number(value)) && value !== '') {
this.setState({ drop_max: value });
}
}
reset_minmax = () => {
this.setState({
drop_min: 0,
drop_max: this.state.max_hv
});
}
setmin_ok = () => {
const ds = this.props.appActions.dataSlots;
var adrop = ds.ds_airdrop;
var mn = this.state.drop_min;
var mx = this.state.drop_max;
mn = (mn !== '' && mn !== null && mn !== undefined) ? mn:0;
mx = (mx !== '' && mx !== null && mx !== undefined) ? mx:this.state.max_hv;
if (mn > mx) {mn = mx};
adrop.min_thresh = mn;
adrop.max_thresh = mx;
this.props.appActions.updateDataSlot("ds_airdrop", adrop);
if (ds.ds_drop_to_holders === 'true' || this.props.appActions.dataSlots.ds_drop_to_voters === 'true') {
// this.update_whitelist();
this.update_drop_num(ds.ds_drop_type.type);
}
this.setState({
drop_min: mn,
drop_max: mx,
setmin_vis:false
});
}
setmin_close = () => {
const curr_min = this.props.appActions.dataSlots.ds_airdrop.min_thresh;
const curr_max = this.props.appActions.dataSlots.ds_airdrop.max_thresh;
this.setState({
drop_min:curr_min,
drop_max:curr_max,
setmin_vis:false
});
}
update_whitelist = (is_sun = true) => {
const ds = this.props.appActions.dataSlots;
const ds_hold = ds.ds_drop_to_holders;
const ds_hold_addr = ds.ds_token_hold_addr;
const ds_vote = ds.ds_drop_to_voters;
const ds_vote_addr = ds.ds_sr_vote_addr;
this.props.appActions.updateDataSlot("ds_airdrop_array", []);
if (ds_hold === 'true' && ds_hold_addr === '') {
this.props.appActions.updateDataSlot("ds_holder_list", []);
}
if (ds_vote === 'true' && ds_vote_addr === '') {
this.props.appActions.updateDataSlot("ds_voter_list", []);
}
if (ds.ds_drop_to_holders === 'true' || ds.ds_drop_to_voters === 'true') {
var d_amt = ds.ds_drop_amount;
var w_list = [];
const h_list = ds.ds_holder_list;
const v_list = ds.ds_voter_list;
var voter = {};
var holder = {};
if (ds.ds_drop_to_holders === ds.ds_drop_to_voters) {
var ds_and_or = ds.ds_and_or;
var curr_addr_list = [];
if (ds_and_or === 'and') {
for(voter in v_list) {
curr_addr_list.push(v_list[voter].address);
}
for(holder in h_list) {
if(curr_addr_list.includes(h_list[holder].address)) {
w_list.push(h_list[holder]);
}
}
} else { // holder or voter
for(voter in v_list) {
curr_addr_list.push(v_list[voter].address);
w_list.push(v_list[voter]);
}
for(holder in h_list) {
if(!curr_addr_list.includes(h_list[holder].address)) {
w_list.push(h_list[holder]);
}
}
}
} else {
if (ds.ds_drop_to_holders === 'true') {
w_list = h_list;
} else {
w_list = v_list;
}
}
const blist = ds.ds_airdrop.blacklist;
// const b_thresh = ds.ds_airdrop.min_thresh;
const this_addr = ds.ds_acc_addr;
var total_balance = 0;
var max_num = 1;
w_list = w_list.filter(addr => {
var passed = (!blist.includes(addr.address));
if (addr.balance > max_num) {max_num = addr.balance;};
if (passed) {passed = (addr.balance >= ds.ds_airdrop.min_thresh);};
if (passed) {passed = (addr.balance <= ds.ds_airdrop.max_thresh);};
if (passed) {passed = (this_addr !== addr.address);};
if (passed) {total_balance += addr.balance;};
return passed;
});
var rec;
const wc = ds.ds_is_weighted;
const tpr = ds.ds_airdrop.token_precision;
var temp_amt = 0;
if (wc === 'false') {
for (rec in w_list) {
if (ds.ds_drop_type.type === 'total') {
w_list[rec]['amount'] = Math.floor((d_amt*Math.pow(10,tpr))/w_list.length);
} else if (ds.ds_drop_type.type === 'each') {
w_list[rec]['amount'] = Math.floor(d_amt*Math.pow(10,tpr));
} else {
temp_amt = w_list[rec].balance*d_amt;
w_list[rec]['amount'] = Math.floor(temp_amt*Math.pow(10,tpr));
}
}
} else {
for (rec in w_list) {
temp_amt = d_amt*(w_list[rec].balance/total_balance);
// w_list[rec]['amount'] = d_amt*(w_list[rec].balance/total_balance);
w_list[rec]['amount'] = Math.floor(temp_amt*Math.pow(10,tpr));
}
}
var num_excl = 0;
var token_num = 0;
w_list = w_list.filter(wallet => {
if (wallet.amount > 0) {
token_num += (wallet.amount/Math.pow(10,tpr));
return true;
}
num_excl++;
return false;
});
var temp_step = this.state.max_step;
var slidemax = this.state.slide_max;
if (w_list.length > 0) {
var temp_max = total_balance/w_list.length;
temp_step = Math.pow(10, Math.round(temp_max).toString().length - 2)*2;
slidemax = (temp_step*Math.round(temp_max/temp_step))-temp_step;
}
var bw_pct = 0;
var bw_trx = ds.ds_trx_total*100000;
if (ds.ds_bw_total > 0) {
if (ds.ds_drop_token === 'TRX') {
bw_trx = bw_trx - (token_num*100000);
}
var tl_mult = 1;
if (ds.ds_login_method === 'tronlink') {tl_mult = 2};
var bw_rem = (ds.ds_bw_total + bw_trx) - (w_list.length*250*tl_mult);
if (bw_rem < 0) {bw_rem = 0};
bw_pct = 100*bw_rem/(ds.ds_bw_total + bw_trx);
}
var help_pct = 0;
if (ds.ds_help_total > 0) {
var help_rem = ds.ds_help_total - (w_list.length*1);
if (help_rem < 0) {help_rem = 0};
help_pct = 100*help_rem/ds.ds_help_total;
}
this.setState({
max_hv: max_num,
slide_max: slidemax,
max_step: temp_step,
ex_amount: Math.round(slidemax/(2*temp_step))*temp_step,
num_ex:num_excl,
});
this.props.appActions.updateDataSlot("ds_max_amount", total_balance);
this.props.appActions.updateDataSlot("ds_airdrop_array", w_list);
this.props.appActions.updateDataSlot("ds_bw_pct", bw_pct);
this.props.appActions.updateDataSlot("ds_help_pct", help_pct);
this.props.appActions.updateDataSlot("ds_drop_num", w_list.length);
this.get_curr_addresses();
}
this.update_can_review();
}
update_blacklist = (choice: string) => {
var adrop = this.props.appActions.dataSlots.ds_airdrop;
var blist = [];
var bl_choice = [];
for (var i=0; i < choice.length; i++) {
var c_label = choice[i].label;
var addr_json = {
value: i,
label: c_label
};
bl_choice.push(addr_json);
blist.push(c_label.substr(0, 34));
}
adrop.blacklist = blist;
this.setState({
bl_choice: bl_choice,
});
this.props.appActions.updateDataSlot("ds_airdrop", adrop);
}
reset_blacklist = (choice: string) => {
var adrop = this.props.appActions.dataSlots.ds_airdrop;
adrop.blacklist = [];
this.setState({
bl_choice: [],
});
this.props.appActions.updateDataSlot("ds_airdrop", adrop);
this.close();
}
get_curr_addresses = () => {
const w_list = this.props.appActions.dataSlots.ds_airdrop_array;
var curr_addr = [];
for (var i=0; i<w_list.length; i++) {
var temp = {
value: i,
label: w_list[i].address + ' (balance: ' + w_list[i].balance + ')',
}
curr_addr.push(temp);
}
this.setState({
addr_list: curr_addr,
});
}
render() {
// eslint-disable-next-line no-unused-vars
let baseStyle = {};
// eslint-disable-next-line no-unused-vars
let layoutFlowStyle = {};
const style_elBackground = {
width: '100%',
height: '100%',
};
const style_elBackground_outer = {
backgroundColor: '#f6f6f6',
};
const style_elText = {
color: 'rgba(0, 0, 0, 0.8500)',
textAlign: 'right',
};
const dataSource_holder_pick = this.props.appActions.getDataSheet('token_list');
const all_data = dataSource_holder_pick.items;
var all_t = [];
for (var i=0; i < all_data.length; i++) {
var temp = {};
temp['value'] = i;
temp['label'] = all_data[i]['name'] + ' (ID: ' + all_data[i]['tokenID'] + ')';
temp['address'] = all_data[i]['ownerAddress'];
temp['num_holders'] = all_data[i]['nrOfTokenHolders'];
temp['id'] = all_data[i]['tokenID'];
temp['abbr'] = all_data[i]['abbr'];
all_t.push(temp);
}
all_t.sort(function(a, b) {
var textA = a.label.toUpperCase();
var textB = b.label.toUpperCase();
return (textA < textB) ? -1 : (textA > textB) ? 1 : 0;
});
const acc_t = this.props.appActions.dataSlots.ds_acc_tokens;
const style_elDrop_token_pick_outer = {
pointerEvents: 'auto',
};
const style_elTextCopy = {
color: 'rgba(0, 0, 0, 0.8500)',
marginBottom: '15px',
marginLeft: '15px'
};
const holder_voter_drop = {
pointerEvents: 'auto',
position: 'relative',
marginTop: '10px',
marginLeft: '20%',
marginRight: '45%'
};
const style_elHolder_check_outer = {
pointerEvents: 'auto',
};
const style_elAnd_or_pick_outer = {
pointerEvents: 'auto',
position: 'relative',
marginTop: '15px',
marginLeft: '40%',
marginRight: '45%',
};
const dataSource_voter_pick = this.props.appActions.getDataSheet('SR_list');
const sr_data = dataSource_voter_pick.items;
var sr_addr = [];
for (i=0; i < sr_data.length; i++) {
temp = {};
temp['value'] = i;
temp['label'] = sr_data[i]['name'];
temp['addr'] = sr_data[i]['address'];
temp['votes'] = sr_data[i]['realTimeVotes'];
sr_addr.push(temp);
}
const drop_both = (this.props.appActions.dataSlots.ds_drop_to_holders === 'true' && this.props.appActions.dataSlots.ds_drop_to_voters === 'true') ? 'true' : 'false';
const style_elVoter_check_outer = {
pointerEvents: 'auto',
};
const value_drop_amount = this.props.appActions.dataSlots.ds_drop_amount;
const style_elText2 = {
color: 'rgba(0, 0, 0, 0.8500)',
textAlign: 'center',
marginBottom:'5px'
};
const faded_text = {
color: 'rgba(0, 0, 0, 0.3000)',
textAlign: 'center',
};
const wrapperStyle = {
pointerEvents: 'auto',
marginLeft: '20%',
marginRight: '10%',
};
const wrapperStyle2 = {
pointerEvents: 'auto',
marginTop: '30px',
marginLeft: '20%',
marginRight: '10%',
}
const wrapperStyle3 = {
pointerEvents: 'auto',
marginTop: '25px',
marginLeft: '5%',
marginRight: '5%',
}
const value_drop_examples_txt = this.render_drop_text(this.props.appActions.dataSlots.ds_drop_amount);
var slide_disabled = false;
var hv_true = false;
if (this.props.appActions.dataSlots.ds_drop_to_holders === this.props.appActions.dataSlots.ds_drop_to_voters) {
slide_disabled = true;
hv_true = true;
} else if (this.props.appActions.dataSlots.ds_is_weighted === 'false') {
slide_disabled = true;
} else if (isNaN(Number(this.props.appActions.dataSlots.ds_drop_amount))) {
slide_disabled = true;
} else if (this.props.appActions.dataSlots.ds_drop_amount < 0) {
slide_disabled = true;
} else if (this.props.appActions.dataSlots.ds_sr_vote_addr === '' && this.props.appActions.dataSlots.ds_token_hold_addr === '') {
slide_disabled = true;
} else if (!this.props.appActions.dataSlots.ds_drop_token) {
slide_disabled = true;
}
const use_tools = this.props.appActions.dataSlots.ds_drop_to_holders === 'true' || this.props.appActions.dataSlots.ds_drop_to_voters === 'true';
const inputValue = this.state.ex_amount;
const no_token_chosen = (this.props.appActions.dataSlots.ds_drop_token === '') ? true:false;
const ex_text_style = (slide_disabled) ? faded_text:style_elText2;
const dslot = this.props.appActions.dataSlots;
const dtyp = dslot.ds_drop_type.type;
var drop_lim = (dslot.ds_drop_lim) ? dslot.ds_drop_lim:0;
var numr = (dslot.ds_drop_num) ? dslot.ds_drop_num:0;
var maxa = (dslot.ds_max_amount) ? dslot.ds_max_amount:0;
if (drop_lim > 0) {
if (dtyp === 'each' && numr > 0) {drop_lim=drop_lim/numr;};
if (dtyp === 'ratio' && maxa > 0) {drop_lim=drop_lim/maxa;};
}
const any_excluded = (this.state.num_ex > 0);
// const nx_text_style = (any_excluded) ? style_elText2:faded_text;
const nx_text_style = {
color: 'rgba(0, 0, 0, 0.5000)',
textAlign: 'center',
marginTop: '10px',
};
var nx_text = '';
if (any_excluded) {
if (this.state.num_ex > 1) {
nx_text = this.state.num_ex + ' addresses will receive 0 tokens at the current weighted amount';
} else {
nx_text = this.state.num_ex + ' address will receive 0 tokens at the current weighted amount';
}
}
var min_type;
if (hv_true) {
// if (this.props.appActions.dataSlots.ds_drop_to_holders === 'true') {min_type = 'tokens/votes';};
min_type = 'tokens/votes';
} else {
min_type = (this.props.appActions.dataSlots.ds_drop_to_holders === 'true') ? 'tokens':'votes';
}
const minmax_text = 'Set the minimum and maximum of ' + min_type + ' an address needs to receive airdrop.';
return (
<div className="Options" style={baseStyle}>
<div className="background">
<div className='appBg containerMinHeight elBackground' style={style_elBackground_outer}>
<div style={style_elBackground} />
</div>
</div>
<div className="layoutFlow">
<div className='headlineFont elText'>
<div style={style_elText}>
<div>{this.props.locStrings.component2_text_366518}</div>
</div>
</div>
<div className='baseFont elDrop_token_pick' style={style_elDrop_token_pick_outer}>
<Select
isClearable={true}
components={makeAnimated()}
defaultValue={this.props.appActions.dataSlots.ds_drop_token_def}
options={acc_t}
onChange={this.drop_token_callback}
/>
</div>
<Row style={wrapperStyle3}>
<Col span={6} style={{textAlign:'right',width:'35%'}}>
<Button
onClick={this.openAdvanced}
type="ghost"
>
<Icon type="tool" />
Advanced
</Button>
</Col>
<Col span={14} style={{textAlign:'left',left:'5px',width:'65%'}}>
<div className='headlineFont elTextCopy'>
<div style={style_elTextCopy}>
<div>{this.props.locStrings.component2_textcopy_1032644}</div>
</div>
</div>
</Col>
</Row>
<Drawer
style={{fontWeight:'bold',textAlign:'center'}}
title="Advanced Settings"
width={window.innerWidth*0.2}
onClose={this.closeAdvanced}
visible={this.state.adv_visible}>
<div style={{marginTop:'25%',width:'100%'}}>
<Tooltip title="Set Minimum and Maximum Tokens/Votes">
<Button
style={{height:'40px'}}
block
onClick={() => {this.setState({setmin_vis:true});}}>
<Icon
type="sliders"
style={{ fontSize: '18px', marginTop: '2px' }}
/>
Set Min/Max
</Button>
</Tooltip>
<Modal
title={minmax_text}
visible={this.state.setmin_vis}
onOk={this.setmin_ok}
style={{textAlign:'center','fontSize':'18px'}}
// confirmLoading={confirmLoading}
onCancel={this.setmin_close}>
<Row style={{textAlign:'center'}}>
<strong>Minimum</strong>
<InputNumber
min={0}
max={this.state.max_hv}
style={{ marginTop: '5px', marginLeft: '10px', width: '40%' }}
value={this.state.drop_min}
onChange={this.set_minimum}
/>
</Row>
<Row style={{textAlign:'center'}}>
<strong>Maximum</strong>
<InputNumber
min={this.state.drop_min}
max={this.state.max_hv}
style={{ marginTop: '15px', marginLeft: '10px', width: '40%' }}
value={this.state.drop_max}
onChange={this.set_maximum}
/>
</Row>
<Button
style={{
width: '30%',
marginTop: '15px',
}}
icon="reload"
onClick={this.reset_minmax}>
Reset
</Button>
</Modal>
</div>
<div style={{marginTop:'25%',width:'100%'}}>
<Tooltip title="Edit Drop Address List">
<Button
style={{height:'40px'}}
block
disabled={(use_tools) ? false:true}
onClick={this.open}>
<Icon
type="edit"
style={{ fontSize: '18px', marginTop: '2px' }}
/>
Edit Addresses
</Button>
</Tooltip>
<ModalTransition>
{this.state.isOpen && (
<ModalAtlas
onClose={this.close}
heading="Select All Addresses that you want to exclude from the airdrop"
shouldCloseOnOverlayClick={false}>
<div>
<Select
isClearable={true}
isMulti
closeMenuOnSelect={false}
styles={{ menuPortal: base => ({ ...base, zIndex: 9999 }) }}
menuPortalTarget={document.body}
isSearchable
defaultValue={this.state.bl_choice}
options={this.state.addr_list}
onChange={this.update_blacklist}
/>
</div>
<div>
<Row
style={{
marginTop: '10px',
marginBottom: '10px',
marginLeft: '30%',
}}>
<Button
style={{
width: '30%',
margin: '5px',
}}
onClick={this.cancel}>
Cancel
</Button>
<Button
style={{
width: '30%',
margin: '5px',
}}
icon="reload"
onClick={this.reset_blacklist}>
Reset
</Button>
<Button
style={{
width: '30%',
margin: '5px',
}}
type="primary"
icon="delete"
onClick={this.close}>
Remove
</Button>
</Row>
</div>
</ModalAtlas>
)}
</ModalTransition>
</div>
<div
style={{
position: 'absolute',
left: 0,
bottom: 0,
width: '100%',
borderTop: '1px solid #e9e9e9',
padding: '10px 16px',
background: '#fff',
textAlign: 'right',
}}>
<Button
style={{width:'60%'}}
onClick={this.closeAdvanced}
type="primary">
Close
</Button>
</div>
</Drawer>
<div style={holder_voter_drop}>
<Select
isClearable={true}
isDisabled={this.props.appActions.dataSlots.ds_drop_to_holders !== 'true'}
options={all_t}
components={makeAnimated()}
defaultValue={this.props.appActions.dataSlots.ds_token_hold_addr_def}
onChange={this.drop_holder_callback}
/>
</div>
<div className='baseFont elHolder_check' style={style_elHolder_check_outer}>
<Checkbox
onChange={this.checkboxChanged_holder_check}
checked={this.props.appActions.dataSlots.ds_drop_to_holders === 'true'}
>Token Holders</Checkbox>
</div>
<div className='baseFont' style={style_elAnd_or_pick_outer}>
<Select
isDisabled={drop_both !== 'true'}
options={and_or_choice}
onChange={this.drop_and_or_callback}
value={this.props.appActions.dataSlots.ds_and_or_def}
/>
</div>
<div style={holder_voter_drop}>
<Select
isClearable={true}
isDisabled={this.props.appActions.dataSlots.ds_drop_to_voters !== 'true'}
components={makeAnimated()}
defaultValue={this.props.appActions.dataSlots.ds_sr_vote_addr_def}
options={sr_addr}
onChange={this.drop_voter_callback}
/>
</div>
<div className='baseFont elVoter_check' style={style_elVoter_check_outer}>
<Checkbox
onChange={this.checkboxChanged_voter_check}
checked={this.props.appActions.dataSlots.ds_drop_to_voters === 'true'}
>SR Voters</Checkbox>
</div>
<Row style={wrapperStyle2}>
<Col span={12} style={{ paddingRight: '10px' }}>
<Select
value={this.props.appActions.dataSlots.ds_drop_type}
options={drop_types}
onChange={this.drop_amount_type_callback}
/>
</Col>
<Col span={12}>
<InputNumber
min={0}
max={drop_lim}
style={{ marginRight: '10px', height: '38px', width: '50%' }}
size="large"
value={value_drop_amount !== undefined ? value_drop_amount : 1}
onChange={this.changed_drop_amount}
disabled={no_token_chosen}
/>
<Checkbox
disabled={this.props.appActions.dataSlots.ds_can_weight}
onChange={this.checkboxChanged_weighted_check}
checked={this.props.appActions.dataSlots.ds_is_weighted === 'true'}
>Weighted</Checkbox>
</Col>
</Row>
<div>
<div style={nx_text_style}>
{nx_text}
</div>
</div>
<div className='headlineFont elText2'>
<div style={ex_text_style}>
TEST IT OUT
</div>
</div>
<div style={wrapperStyle}>
<Row>
<Col span={14}>
<Slider
disabled={slide_disabled}
tipFormatter={this.slideFormatter}
min={0}
max={this.state.slide_max}
onChange={this.sliderChange}
step={this.state.max_step}
value={typeof inputValue === 'number' && inputValue > 0 ? inputValue : 0}
/>
</Col>
<Col span={10}>
<InputNumber
disabled={slide_disabled}
min={0}
max={this.state.slide_max}
style={{ marginLeft: 16, width: '60%' }}
value={inputValue > 0 ? inputValue : 0}
onChange={this.sliderChange}
/>
</Col>
</Row>
</div>
<div className='headlineFont elText2'>
<div style={style_elText2}>
<div dangerouslySetInnerHTML={{__html: value_drop_examples_txt.replace(/\n/g, '<br>')}}></div>
</div>
</div>
</div>
</div>
)
}
}