Skip to content

Commit c39902c

Browse files
committed
Allow voting with vesting contracts
For vesting contracts, only count the unlocked balance for votes.
1 parent 8a47ad1 commit c39902c

File tree

1 file changed

+38
-7
lines changed

1 file changed

+38
-7
lines changed

src/App.vue

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,36 @@ async function trySubmittingVote() {
192192
}
193193
}
194194
195+
/**
196+
* Calculate the available balance of an account, which is the balance that can actually be used for voting.
197+
*
198+
* For basic accounts, this is just the balance. For vesting contracts, it is calculated how much of the balance is still locked.
199+
*/
200+
function availableBalance(account: Nimiq.PlainAccount, voteEndTime: number): number {
201+
if (account.type === 'basic') {
202+
return account.balance;
203+
} else if (account.type === 'vesting') {
204+
const now = Math.min(new Date().getTime(), voteEndTime);
205+
let unlocked = 0;
206+
let start = account.startTime; // All times on the account are in milliseconds
207+
while ((start += account.timeStep) <= now) {
208+
unlocked += account.stepAmount;
209+
if (unlocked >= account.totalAmount) {
210+
unlocked = account.totalAmount;
211+
break;
212+
}
213+
}
214+
const stillLocked = account.totalAmount - unlocked;
215+
// If the contract had balance still locked, but all balance has been withdrawn since the end of the vote,
216+
// the returned "available balance" is negative.
217+
// That still works, because then through the tx history the outgoing tx will be re-added to the balance,
218+
// bringing it to what was actually available at the end of the vote (which can be 0).
219+
return account.balance - stillLocked;
220+
} else {
221+
return 0;
222+
}
223+
}
224+
195225
async function submitVote() {
196226
const config = votingConfig.value;
197227
const voteData: BaseVote = { name: config!.name, choices: serializeChoices() };
@@ -210,8 +240,8 @@ async function submitVote() {
210240
disableDisclaimer: true,
211241
});
212242
213-
if (signedTransaction.raw.senderType !== Nimiq.AccountType.Basic) {
214-
throw new Error('You can only vote with basic accounts. Vesting and HTLC contracts are not allowed.');
243+
if (![Nimiq.AccountType.Basic, Nimiq.AccountType.Vesting].includes(signedTransaction.raw.senderType)) {
244+
throw new Error('You can only vote with basic and vesting accounts. HTLC contracts are not allowed.');
215245
}
216246
217247
const { sender, value: txValue } = signedTransaction.raw;
@@ -230,7 +260,7 @@ async function submitVote() {
230260
client.value?.getStaker(sender),
231261
]);
232262
233-
const accountBalance = account?.balance || 0;
263+
const accountBalance = account ? availableBalance(account, Infinity) : 0;
234264
const stakerBalance = staker ? staker.balance + staker.inactiveBalance + staker.retiredBalance : 0;
235265
236266
vote.value.value = accountBalance + stakerBalance;
@@ -289,13 +319,14 @@ async function countVotes(config = votingConfig.value!): Promise<ElectionResults
289319
// Get accounts details in parallel for all chunks
290320
const balancesByAddress = new Map<string, number>();
291321
try {
322+
const votingEndTime = blockDateUtil(config.end, height.value).getTime();
292323
await Promise.all(addressChunk.map(async (chunk) => {
293324
const accounts = await nimiqClient.getAccounts(chunk);
294325
accounts.forEach((account, i) => {
295326
const address = chunk[i];
296-
// Only keep BASIC accounts
297-
if (account.type === 'basic') {
298-
balancesByAddress.set(address, account.balance);
327+
const balance = availableBalance(account, votingEndTime);
328+
if (balance) {
329+
balancesByAddress.set(address, balance);
299330
} else {
300331
balancesByAddress.delete(address);
301332
}
@@ -329,7 +360,7 @@ async function countVotes(config = votingConfig.value!): Promise<ElectionResults
329360
error('Failed to get account balances when counting votes', e as Error, 'Try reloading.');
330361
}
331362
332-
// Remove votes from non-basic accounts, it's not allowed to vote with vesting or HTLC contracts
363+
// Remove votes from unallowed accounts - it's not allowed to vote with HTLC contracts
333364
votes = votes.filter((v) => balancesByAddress.has(v.tx.sender));
334365
335366
stats.votes = votes.length;

0 commit comments

Comments
 (0)