feat: approval and allocation in separate operations (#35)

This commit is contained in:
Richard Ramos 2019-06-05 08:59:00 -04:00 committed by GitHub
parent 9c5640605b
commit af55ca0311
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 126 additions and 36 deletions

View File

@ -12,13 +12,19 @@ import {
removeContributor,
forfeitAllocation,
lastForfeited,
allocate
allocate,
getAllowance,
approve,
resetAllowance,
getSNTBalance
} from '../services/Meritocracy';
import { sortByAlpha } from '../utils';
import moment from 'moment';
import './admin.scss';
const toBN = web3.utils.toBN;
class Admin extends React.Component {
state = {
contributorName: '',
@ -32,7 +38,9 @@ class Admin extends React.Component {
sortBy: 'label',
tab: 'admin',
sntPerContributor: 0,
lastForfeited: null
lastForfeited: null,
allowance: '0',
balance: '0',
};
async componentDidMount() {
@ -42,11 +50,18 @@ class Admin extends React.Component {
this.setState({ busy: false, contributorList });
this.getLastForfeitDate();
this.getAllowance();
} catch (error) {
this.setState({ errorMsg: error.message || error });
}
}
getAllowance = async () => {
const allowance = await getAllowance();
const balance = await getSNTBalance();
this.setState({allowance, balance});
}
onChange = (name, e) => {
this.setState({ [name]: e.target.value });
};
@ -84,7 +99,50 @@ class Admin extends React.Component {
try {
await allocate(sntAmount);
this.setState({ busy: false, successMsg: 'Funds allocated!' });
this.setState({ busy: false, successMsg: 'Funds allocated!'});
this.getAllowance();
} catch (error) {
this.setState({ error: error.message || error, busy: false });
}
};
approve = async e => {
e.preventDefault();
/* eslint-disable-next-line no-alert*/
if (!confirm('Are you sure?')) return;
this.setState({ busy: true, successMsg: '', error: '' });
const { contributorList, sntPerContributor } = this.state;
const sntAmount = web3.utils.toWei((contributorList.length * parseInt(sntPerContributor, 10)).toString(), 'ether');
try {
await approve(sntAmount);
this.setState({
busy: false,
successMsg: (contributorList.length * parseInt(sntPerContributor, 10)) + ' SNT approved for allocation',
allowance: sntAmount
});
this.getAllowance();
} catch (error) {
this.setState({ error: error.message || error, busy: false });
}
};
resetAllowance = async e => {
e.preventDefault();
/* eslint-disable-next-line no-alert*/
if (!confirm('Are you sure?')) return;
this.setState({ busy: true, successMsg: '', error: '' });
try {
await resetAllowance();
this.setState({ busy: false, successMsg: 'Allowance reset to 0', allowance: '0' });
this.getAllowance();
} catch (error) {
this.setState({ error: error.message || error, busy: false });
}
@ -141,13 +199,23 @@ class Admin extends React.Component {
successMsg,
focusedContributorIndex,
tab,
sntPerContributor
sntPerContributor,
allowance,
balance
} = this.state;
const currentContributor = focusedContributorIndex > -1 ? contributorList[focusedContributorIndex] : {};
const nextForfeit = (lastForfeited ? lastForfeited * 1000 : new Date().getTime()) + 86400 * 6 * 1000;
const nextForfeitDate =
new Date(nextForfeit).toLocaleDateString() + ' ' + new Date(nextForfeit).toLocaleTimeString();
const totalSntForContributors = web3.utils.toWei(toBN(contributorList.length * parseInt(sntPerContributor || '0', 10)), "ether");
const enoughBalance = toBN(balance).gte(toBN(totalSntForContributors));
const shouldApprove = toBN(totalSntForContributors).gt(toBN(0)) && toBN(allowance).lt(toBN(totalSntForContributors));
const shouldReset = toBN(allowance).gt(toBN(0)) && toBN(allowance).lt(toBN(totalSntForContributors));
const canAllocate = toBN(totalSntForContributors).gt(toBN(0)) && toBN(allowance).gte(toBN(totalSntForContributors));
return (
<Fragment>
<Tabs className="home-tabs mb-3" activeKey={tab} onSelect={tab => this.setState({ tab })}>
@ -214,7 +282,7 @@ class Admin extends React.Component {
<Form.Group controlId="fundAllocation">
<Form.Label>SNT per contributor</Form.Label>
<Form.Text className="text-muted">
Total: {contributorList.length * parseInt(sntPerContributor, 10) || 0} SNT
Total: {contributorList.length * parseInt(sntPerContributor, 10) || 0} SNT, (Balance: {web3.utils.fromWei(balance, "ether")} SNT, Approved: {web3.utils.fromWei(allowance, "ether")} SNT)
</Form.Text>
<Input
type="text"
@ -225,9 +293,15 @@ class Admin extends React.Component {
validations={[required, isNumber, higherThan.bind(null, 0)]}
/>
</Form.Group>
<Button variant="primary" onClick={this.allocateFunds}>
Allocate Funds
</Button>
{ enoughBalance && canAllocate && <Button variant="primary" disabled={busy} onClick={this.allocateFunds}>
Allocate {contributorList.length * parseInt(sntPerContributor || '0', 10)} SNT
</Button> }
{ enoughBalance && shouldApprove && !shouldReset && <Button disabled={busy} variant="primary" onClick={this.approve}>
Approve {contributorList.length * parseInt(sntPerContributor || '0', 10)} SNT
</Button> }
{ shouldApprove && shouldReset && <Button disabled={busy} variant="primary" onClick={this.resetAllowance}>
Reset existing approval
</Button> }
</ValidatedForm>
<hr className="mt-5 mb-5" />
<ValidatedForm>

View File

@ -106,45 +106,63 @@ export function forfeitAllocation() {
resolve(receipt);
} catch (error) {
const message = 'Error forfeiting allocation';
console.error(message);
console.error(error);
reject(message);
}
});
}
export function getSNTBalance() {
return new Promise(async (resolve) => {
const mainAccount = web3.eth.defaultAccount;
resolve(await SNT.methods.balanceOf(mainAccount).call());
});
}
export function getAllowance() {
return new Promise(async (resolve) => {
const mainAccount = web3.eth.defaultAccount;
resolve(await SNT.methods.allowance(mainAccount, Meritocracy.options.address).call());
});
}
export function resetAllowance() {
return new Promise(async (resolve, reject) => {
const mainAccount = web3.eth.defaultAccount;
try {
const toSend = SNT.methods.approve(Meritocracy.options.address, '0');
const gas = await toSend.estimateGas({ from: mainAccount });
const receipt = await toSend.send({ from: mainAccount, gas: gas + 1000 });
resolve(receipt);
} catch(error) {
reject(error);
}
});
}
export function approve(sntAmount) {
return new Promise(async (resolve, reject) => {
const mainAccount = web3.eth.defaultAccount;
try {
const toSend = SNT.methods.approve(Meritocracy.options.address, sntAmount);
const gas = await toSend.estimateGas({ from: mainAccount });
const receipt = await toSend.send({ from: mainAccount, gas: gas + 1000 });
resolve(receipt);
} catch(error) {
reject(error);
}
});
}
export function allocate(sntAmount) {
return new Promise(async (resolve, reject) => {
const mainAccount = web3.eth.defaultAccount;
try {
let toSend, gas;
const balance = web3.utils.toBN(await SNT.methods.balanceOf(mainAccount).call());
const allowance = web3.utils.toBN(await SNT.methods.allowance(mainAccount, Meritocracy.options.address).call());
if (balance.lt(web3.utils.toBN(sntAmount))) {
throw new Error('Not enough SNT');
}
if (allowance.gt(web3.utils.toBN('0')) && allowance.lt(web3.utils.toBN(sntAmount))) {
alert('Reset allowance to 0');
toSend = SNT.methods.approve(Meritocracy.options.address, '0');
gas = await toSend.estimateGas({ from: mainAccount });
await toSend.send({ from: mainAccount, gas: gas + 1000 });
}
if (allowance.eq(web3.utils.toBN('0'))) {
alert(`Approving ${web3.utils.fromWei(sntAmount, 'ether')} to meritocracy contract`);
toSend = SNT.methods.approve(Meritocracy.options.address, sntAmount);
gas = await toSend.estimateGas({ from: mainAccount });
await toSend.send({ from: mainAccount, gas: gas + 1000 });
}
alert('Allocating SNT');
toSend = Meritocracy.methods.allocate(sntAmount);
gas = await toSend.estimateGas({ from: mainAccount });
await toSend.send({ from: mainAccount, gas: gas + 1000 });
resolve(true);
} catch (error) {
let message;
@ -152,10 +170,8 @@ export function allocate(sntAmount) {
if (error.message === 'Not enough SNT') {
message = 'Not enough SNT';
} else {
message = 'Error forfeiting allocation';
message = 'Error doing allocation';
}
console.error(message);
console.error(error);
reject(message);
}