UNPKG

tronair-gui

Version:

Web application/GUI for performing airdrops on the TRON blockchain

1,432 lines (1,306 loc) 48.1 kB
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> ) } }