Skip to content

Commit

Permalink
Refactor canVote and canRelinquishVote
Browse files Browse the repository at this point in the history
  • Loading branch information
ChewingGlass committed Jul 16, 2024
1 parent 0ee3697 commit b2f6677
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 127 deletions.
89 changes: 61 additions & 28 deletions packages/voter-stake-registry-hooks/src/hooks/useRelinquishVote.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,65 @@
import {
Status,
batchParallelInstructions,
truthy
} from "@helium/spl-utils";
import { Status, batchParallelInstructions, truthy } from "@helium/spl-utils";
import { init, voteMarkerKey } from "@helium/voter-stake-registry-sdk";
import { PublicKey, TransactionInstruction } from "@solana/web3.js";
import { useCallback, useMemo } from "react";
import { useAsyncCallback } from "react-async-hook";
import { useHeliumVsrState } from "../contexts/heliumVsrContext";
import { useVoteMarkers } from "./useVoteMarkers";
import { MAX_TRANSACTIONS_PER_SIGNATURE_BATCH } from "../constants";
import { useSolanaUnixNow } from "@helium/helium-react-hooks";
import { calcPositionVotingPower } from "../utils/calcPositionVotingPower";
import BN from "bn.js";
import { proxyAssignmentKey } from "@helium/nft-proxy-sdk";

export const useRelinquishVote = (proposal: PublicKey) => {
const { positions, provider } = useHeliumVsrState();
const { positions, provider, registrar } = useHeliumVsrState();
const unixNow = useSolanaUnixNow();
const sortedPositions = useMemo(() => {
return (
unixNow &&
positions?.sort((a, b) => {
return -calcPositionVotingPower({
position: a,
registrar: registrar || null,
unixNow: new BN(unixNow),
}).cmp(
calcPositionVotingPower({
position: b,
registrar: registrar || null,
unixNow: new BN(unixNow),
})
);
})
);
}, [positions, unixNow]);
const voteMarkerKeys = useMemo(() => {
return positions
? positions.map((p) => voteMarkerKey(p.mint, proposal)[0])
return sortedPositions
? sortedPositions.map((p) => voteMarkerKey(p.mint, proposal)[0])
: [];
}, [positions]);
}, [sortedPositions]);
const { accounts: markers } = useVoteMarkers(voteMarkerKeys);
const canPositionRelinquishVote = useCallback(
(index: number, choice: number) => {
const position = sortedPositions?.[index];
const marker = markers?.[index]?.info;
const earlierDelegateVoted =
position &&
position.proxy &&
marker &&
position.proxy.index > marker.proxyIndex;
return !earlierDelegateVoted && marker?.choices.includes(choice);
},
[markers]
);
const canRelinquishVote = useCallback(
(choice: number) => {
if (!markers) return false;

return markers.some((m, index) => {
const position = positions?.[index];
const earlierDelegateVoted =
position &&
position.proxy &&
m.info &&
position.proxy.index > m.info.proxyIndex;
return !earlierDelegateVoted && m.info?.choices.includes(choice);
});
return markers.some((_, index) =>
canPositionRelinquishVote(index, choice)
);
},
[markers]
[markers, canPositionRelinquishVote]
);

const { error, loading, execute } = useAsyncCallback(
Expand All @@ -50,7 +76,8 @@ export const useRelinquishVote = (proposal: PublicKey) => {
onProgress?: (status: Status) => void;
maxSignatureBatch?: number;
}) => {
const isInvalid = !provider || !positions || positions.length === 0;
const isInvalid =
!provider || !sortedPositions || sortedPositions.length === 0;

if (isInvalid) {
throw new Error(
Expand All @@ -60,16 +87,16 @@ export const useRelinquishVote = (proposal: PublicKey) => {
const vsrProgram = await init(provider);
const instructions = (
await Promise.all(
positions.map(async (position, index) => {
sortedPositions.map(async (position, index) => {
const canRelinquishVote = canPositionRelinquishVote(
index,
choice
);
const marker = markers?.[index]?.info;
const alreadyVotedThisChoice = marker?.choices.includes(choice);

if (marker && alreadyVotedThisChoice) {
if (marker && canRelinquishVote) {
if (position.isProxiedToMe) {
if (
marker.proxyIndex <
(position.proxy?.index || 0)
) {
if (marker.proxyIndex < (position.proxy?.index || 0)) {
// Do not vote with a position that has been delegated to us, but voting overidden
return;
}
Expand All @@ -82,6 +109,12 @@ export const useRelinquishVote = (proposal: PublicKey) => {
proposal,
voter: provider.wallet.publicKey,
position: position.pubkey,
marker: voteMarkerKey(position.mint, proposal)[0],
proxyAssignment: proxyAssignmentKey(
registrar!.proxyConfig,
position.mint,
provider.wallet.publicKey,
)[0],
})
.instruction();
}
Expand Down Expand Up @@ -109,7 +142,7 @@ export const useRelinquishVote = (proposal: PublicKey) => {
onProgress,
triesRemaining: 10,
extraSigners: [],
maxSignatureBatch
maxSignatureBatch,
});
}
}
Expand Down
189 changes: 90 additions & 99 deletions packages/voter-stake-registry-hooks/src/hooks/useVote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,29 @@ import { useVoteMarkers } from "./useVoteMarkers";
import { calcPositionVotingPower } from "../utils/calcPositionVotingPower";
import { proxyAssignmentKey } from "@helium/nft-proxy-sdk";
import { useSolanaUnixNow } from "@helium/helium-react-hooks";
import { PositionWithMeta } from "../sdk/types";

export const useVote = (proposalKey: PublicKey) => {
const { info: proposal } = useProposal(proposalKey);
const { positions, provider, registrar } = useHeliumVsrState();
const unixNow = useSolanaUnixNow()
const unixNow = useSolanaUnixNow();
const sortedPositions = useMemo(() => {
return unixNow && positions?.sort((a, b) => {
return -calcPositionVotingPower({
position: a,
registrar: registrar || null,
unixNow: new BN(unixNow),
}).cmp(
calcPositionVotingPower({
position: b,
return (
unixNow &&
positions?.sort((a, b) => {
return -calcPositionVotingPower({
position: a,
registrar: registrar || null,
unixNow: new BN(unixNow),
})
);
});
}).cmp(
calcPositionVotingPower({
position: b,
registrar: registrar || null,
unixNow: new BN(unixNow),
})
);
})
);
}, [positions, unixNow]);
const voteMarkerKeys = useMemo(() => {
return sortedPositions
Expand Down Expand Up @@ -82,33 +86,50 @@ export const useVote = (proposalKey: PublicKey) => {
);
}
}, [markers, sortedPositions]);
const canVote = useCallback(
(choice: number) => {
if (!markers) return false;
const canPositionVote = useCallback(
(index: number, choice: number) => {
const position = sortedPositions?.[index];
const marker = markers?.[index];

return markers.some((m, index) => {
const position = sortedPositions?.[index];
const earlierDelegateVoted =
position &&
position.proxy &&
m.info &&
position.proxy.index > m.info.proxyIndex;
const noMarker = !m?.info;
const maxChoicesReached =
(m?.info?.choices.length || 0) >= (proposal?.maxChoicesPerVoter || 0);
const alreadyVotedThisChoice = m.info?.choices.includes(choice);
const proxyExpired =
position?.proxy?.expirationTime &&
new BN(position.proxy.expirationTime).lt(new BN(Date.now() / 1000));
const canVote = !proxyExpired &&
(noMarker ||
const earlierDelegateVoted =
position &&
position.proxy &&
marker?.info &&
position.proxy.index > marker.info.proxyIndex;
const noMarker = !marker?.info;
const maxChoicesReached =
(marker?.info?.choices.length || 0) >=
(proposal?.maxChoicesPerVoter || 0);
const alreadyVotedThisChoice = marker?.info?.choices.includes(choice);
const now = unixNow && new BN(unixNow);
const proxyExpired =
position?.proxy?.expirationTime &&
now &&
new BN(position.proxy.expirationTime).lt(now);
const votingPowerIsZero =
now &&
calcPositionVotingPower({
position,
registrar: registrar || null,
unixNow: now,
}).isZero();
const canVote =
!proxyExpired &&
!votingPowerIsZero &&
(noMarker ||
(!maxChoicesReached &&
!alreadyVotedThisChoice &&
!earlierDelegateVoted));
return canVote;
});
return canVote;
},
[markers]
[registrar, unixNow]
);
const canVote = useCallback(
(choice: number) => {
if (!markers) return false;
return markers.some((_, index) => canPositionVote(index, choice));
},
[markers, canPositionVote]
);
const { error, loading, execute } = useAsyncCallback(
async ({
Expand All @@ -132,86 +153,56 @@ export const useVote = (proposalKey: PublicKey) => {
"Unable to vote without positions. Please stake tokens first."
);
} else {
const clock = await provider.connection.getAccountInfo(
SYSVAR_CLOCK_PUBKEY
);
const vsrProgram = await init(provider);
const instructions = (
await Promise.all(
// vote with bigger positions first.
sortedPositions
.map(async (position, index) => {
const marker = markers?.[index]?.info;
const alreadyVotedThisChoice = marker?.choices.includes(choice);
const maxChoicesReached =
(marker?.choices.length || 0) >=
(proposal?.maxChoicesPerVoter || 0);

const proxyExpired =
position?.proxy?.expirationTime &&
unixNow &&
new BN(position.proxy.expirationTime).lt(new BN(unixNow));

// Ignore positions that have 0 voting power and haven't already voted
if (
proxyExpired ||
(!marker && unixNow &&
calcPositionVotingPower({
position,
registrar: registrar || null,
unixNow: new BN(unixNow),
}).isZero())
) {
return;
}
if (
!marker ||
(!alreadyVotedThisChoice && !maxChoicesReached)
) {
if (position.isProxiedToMe) {
if (
marker &&
(marker.proxyIndex < (position.proxy?.index || 0) ||
marker.choices.includes(choice))
) {
// Do not vote with a position that has been delegated to us, but voting overidden
// Also ignore voting for the same choice twice
return;
}

return await vsrProgram.methods
.proxiedVoteV0({
choice,
})
.accounts({
proposal: proposalKey,
voter: provider.wallet.publicKey,
position: position.pubkey,
registrar: registrar?.pubkey,
marker: voteMarkerKey(
position.mint,
proposalKey
)[0],
proxyAssignment: proxyAssignmentKey(
registrar!.proxyConfig,
position.mint,
provider.wallet.publicKey
)[0],
})
.instruction();
sortedPositions.map(async (position, index) => {
const marker = markers?.[index]?.info;
const canVote = canPositionVote(index, choice);
if (canVote) {
if (position.isProxiedToMe) {
if (
marker &&
(marker.proxyIndex < (position.proxy?.index || 0) ||
marker.choices.includes(choice))
) {
// Do not vote with a position that has been delegated to us, but voting overidden
// Also ignore voting for the same choice twice
return;
}

return await vsrProgram.methods
.voteV0({
.proxiedVoteV0({
choice,
})
.accounts({
proposal: proposalKey,
voter: provider.wallet.publicKey,
position: position.pubkey,
registrar: registrar?.pubkey,
marker: voteMarkerKey(position.mint, proposalKey)[0],
proxyAssignment: proxyAssignmentKey(
registrar!.proxyConfig,
position.mint,
provider.wallet.publicKey
)[0],
})
.instruction();
}
})
return await vsrProgram.methods
.voteV0({
choice,
})
.accounts({
proposal: proposalKey,
voter: provider.wallet.publicKey,
position: position.pubkey,
marker: voteMarkerKey(position.mint, proposalKey)[0],
})
.instruction();
}
})
)
).filter(truthy);

Expand Down

0 comments on commit b2f6677

Please sign in to comment.