Skip to content

Commit

Permalink
fatcat: allow user to override vote (#426)
Browse files Browse the repository at this point in the history
  • Loading branch information
yurushao authored Feb 20, 2025
1 parent 9325fa5 commit e41d6ac
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 66 deletions.
93 changes: 38 additions & 55 deletions anchor/src/client/jupiter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import { BaseClient, TxOptions } from "./base";
import {
GOVERNANCE_PROGRAM_ID,
JUP,
JUP_STAKE_LOCKER,
JUP_VOTE_PROGRAM,
JUPITER_PROGRAM_ID,
WSOL,
Expand Down Expand Up @@ -75,6 +74,8 @@ type SwapInstructions = {
addressLookupTableAddresses: string[];
};

const BASE = new PublicKey("bJ1TRoFo2P6UHVwqdiipp6Qhp2HaaHpLowZ5LHet8Gm");

export class JupiterSwapClient {
public constructor(readonly base: BaseClient) {}

Expand Down Expand Up @@ -163,8 +164,10 @@ export class JupiterSwapClient {
const swapIx: { data: any; keys: AccountMeta[] } =
this.toTransactionInstruction(swapInstruction, vault.toBase58());

const inputTokenProgram = await this.getTokenProgram(inputMint);
const outputTokenProgram = await this.getTokenProgram(outputMint);
const [inputTokenProgram, outputTokenProgram] = await Promise.all([
this.getTokenProgram(inputMint),
this.getTokenProgram(outputMint),
]);

const inputStakePool =
ASSETS_MAINNET.get(inputMint.toBase58())?.stateAccount || null;
Expand All @@ -180,30 +183,17 @@ export class JupiterSwapClient {
inputTokenProgram,
outputTokenProgram,
);
//@ts-ignore
const tx = await this.base.program.methods
.jupiterSwap(amount, swapIx.data)
.accountsPartial({
.accounts({
state: statePda,
signer,
vault,
inputVaultAta: this.base.getVaultAta(
statePda,
inputMint,
inputTokenProgram,
),
outputVaultAta: this.base.getVaultAta(
statePda,
outputMint,
outputTokenProgram,
),
inputMint,
outputMint,
inputTokenProgram,
outputTokenProgram,
inputStakePool,
outputStakePool,
jupiterProgram: JUPITER_PROGRAM_ID,
})
.remainingAccounts(swapIx.keys)
.preInstructions(preInstructions)
Expand Down Expand Up @@ -248,34 +238,18 @@ export class JupiterSwapClient {
inputTokenProgram: PublicKey = TOKEN_PROGRAM_ID,
outputTokenProgram: PublicKey = TOKEN_PROGRAM_ID,
): Promise<TransactionInstruction[]> => {
let preInstructions = [];

const ataParams = [
{
payer: signer,
ata: this.base.getVaultAta(statePda, outputMint, outputTokenProgram),
owner: this.base.getVaultPda(statePda),
mint: outputMint,
tokenProgram: outputTokenProgram,
},
const vault = this.base.getVaultPda(statePda);
const ata = this.base.getAta(outputMint, vault, outputTokenProgram);

const preInstructions = [
createAssociatedTokenAccountIdempotentInstruction(
signer,
ata,
vault,
outputMint,
outputTokenProgram,
),
];
for (const { payer, ata, owner, mint, tokenProgram } of ataParams) {
// const ataAccountInfo = await this.base.provider.connection.getAccountInfo(
// ata
// );
// if (ataAccountInfo) {
// continue;
// }
preInstructions.push(
createAssociatedTokenAccountIdempotentInstruction(
payer,
ata,
owner,
mint,
tokenProgram,
),
);
}

// Transfer SOL to wSOL ATA if needed for the vault
if (inputMint.equals(WSOL)) {
Expand Down Expand Up @@ -389,7 +363,7 @@ export class JupiterVoteClient {
.initLockedVoterEscrow()
.accounts({
state: statePda,
locker: JUP_STAKE_LOCKER,
locker: this.stakeLocker,
escrow,
})
.instruction(),
Expand All @@ -399,7 +373,7 @@ export class JupiterVoteClient {
.toggleMaxLock(true)
.accounts({
state: statePda,
locker: JUP_STAKE_LOCKER,
locker: this.stakeLocker,
escrow,
})
.instruction(),
Expand All @@ -418,7 +392,7 @@ export class JupiterVoteClient {
.increaseLockedAmount(amount)
.accounts({
state: statePda,
locker: JUP_STAKE_LOCKER,
locker: this.stakeLocker,
escrow,
escrowJupAta,
vaultJupAta,
Expand Down Expand Up @@ -450,11 +424,10 @@ export class JupiterVoteClient {
.toggleMaxLock(false)
.accounts({
state: statePda,
locker: JUP_STAKE_LOCKER,
locker: this.stakeLocker,
escrow,
})
.transaction();

const vTx = await this.base.intoVersionedTransaction({
tx,
...txOptions,
Expand All @@ -473,7 +446,7 @@ export class JupiterVoteClient {
.withdrawAllUnstakedJup()
.accounts({
state: statePda,
locker: JUP_STAKE_LOCKER,
locker: this.stakeLocker,
escrow,
escrowJupAta,
vaultJupAta,
Expand Down Expand Up @@ -504,7 +477,7 @@ export class JupiterVoteClient {
.toggleMaxLock(true)
.accounts({
state: statePda,
locker: JUP_STAKE_LOCKER,
locker: this.stakeLocker,
escrow,
})
.transaction();
Expand All @@ -522,15 +495,13 @@ export class JupiterVoteClient {
*
* @param statePda
* @param proposal
* @param governor
* @param side
* @param txOptions
* @returns
*/
public async voteOnProposal(
statePda: PublicKey,
proposal: PublicKey,
governor: PublicKey,
side: number,
txOptions: TxOptions = {},
): Promise<TransactionSignature> {
Expand All @@ -539,6 +510,10 @@ export class JupiterVoteClient {
[Buffer.from("Vote"), proposal.toBuffer(), vault.toBuffer()],
GOVERNANCE_PROGRAM_ID,
);
const [governor] = PublicKey.findProgramAddressSync(
[Buffer.from("Governor"), BASE.toBuffer()],
GOVERNANCE_PROGRAM_ID,
);

const voteAccountInfo =
await this.base.provider.connection.getAccountInfo(vote);
Expand Down Expand Up @@ -566,7 +541,7 @@ export class JupiterVoteClient {
escrow,
proposal,
vote,
locker: JUP_STAKE_LOCKER,
locker: this.stakeLocker,
governor,
})
.transaction();
Expand All @@ -579,9 +554,17 @@ export class JupiterVoteClient {
/*
* Utils
*/
get stakeLocker() {
const [locker] = PublicKey.findProgramAddressSync(
[Buffer.from("Locker"), BASE.toBuffer()],
JUP_VOTE_PROGRAM,
);
return locker;
}

getEscrowPda(owner: PublicKey): PublicKey {
const [escrow] = PublicKey.findProgramAddressSync(
[Buffer.from("Escrow"), JUP_STAKE_LOCKER.toBuffer(), owner.toBuffer()],
[Buffer.from("Escrow"), this.stakeLocker.toBuffer(), owner.toBuffer()],
JUP_VOTE_PROGRAM,
);
return escrow;
Expand Down
13 changes: 3 additions & 10 deletions anchor/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ export const SANCTUM_STAKE_POOL_PROGRAM_ID = new PublicKey(
export const GOVERNANCE_PROGRAM_ID = new PublicKey(
"GovaE4iu227srtG2s3tZzB4RmWBzw8sTwrCLZz7kN7rY",
);
export const JUP_VOTE_PROGRAM = new PublicKey(
"voTpe3tHQ7AjQHMapgSue2HJFAh2cGsdokqN3XqmVSj",
);

/**
* Stake pools
Expand All @@ -72,13 +75,3 @@ export const JITO_STAKE_POOL = new PublicKey(
export const JUPSOL_STAKE_POOL = new PublicKey(
"8VpRhuxa7sUUepdY3kQiTmX9rS5vx4WgaXiAnXq4KCtr",
);

/**
* JUP staking & voting
*/
export const JUP_STAKE_LOCKER = new PublicKey(
"CVMdMd79no569tjc5Sq7kzz8isbfCcFyBS5TLGsrZ5dN",
);
export const JUP_VOTE_PROGRAM = new PublicKey(
"voTpe3tHQ7AjQHMapgSue2HJFAh2cGsdokqN3XqmVSj",
);
43 changes: 43 additions & 0 deletions apps/fatcat/src/components/vote-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ import {
} from "@/components/ui/tooltip";
import { Loader2 } from "lucide-react";
import Link from "next/link";
import { useGlamClient } from "@/providers/clientProvider";
import { toast } from "./ui/use-toast";
import { ExplorerLink } from "./ExplorerLink";
import { parseTxError } from "@/lib/error";

const PROPOSALS_LIMIT = 9999;
const DEFAULT_PROPOSAL = {
Expand Down Expand Up @@ -159,6 +163,44 @@ const ProposalItem = memo(
onVoteChange: (key: string, value: string) => void;
formatDate: (date: string | null) => string;
}) => {
const { glamClient } = useGlamClient();

const handleOverrideVote = useCallback(async () => {
const selectedOption = selectedVotes[proposal.key];
console.log("Override Vote:", {
proposal: {
key: proposal.key,
title: proposal.title,
},
selectedOption,
});
if (!selectedOption) {
toast({
title: "Vote side not selected",
description: "Please select a vote side",
variant: "destructive",
});
return;
}

try {
const txSig = await glamClient.castVote(
proposal.key,
parseInt(selectedOption),
);
toast({
title: `Vote succeeded`,
description: <ExplorerLink path={`tx/${txSig}`} label={txSig} />,
});
} catch (error) {
toast({
title: `Vote succeeded`,
description: parseTxError(error),
variant: "destructive",
});
}
}, [proposal, selectedVotes]);

const status = useMemo(
() =>
getProposalStatus(
Expand Down Expand Up @@ -362,6 +404,7 @@ const ProposalItem = memo(
!selectedVotes[proposal.key] || status !== "ongoing"
}
className="text-foreground dark:text-background shadow-none w-full"
onClick={handleOverrideVote}
>
Override Vote
</Button>
Expand Down
12 changes: 12 additions & 0 deletions apps/fatcat/src/lib/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,18 @@ export class FatcatGlamClient extends GlamClient {
return tx;
};

castVote = async (proposal: string, side: number) => {
console.log("Casting vote:", { proposal, side });
const { state } = this.getFatcatState();
const tx = await this.jupiterVote.voteOnProposal(
state,
new PublicKey(proposal),
side,
{ ...this.priorityFeeTxOptions },
);
return tx;
};

private isCacheValid(): boolean {
return (
this.cachedBalances.lastFetch !== undefined &&
Expand Down
1 change: 0 additions & 1 deletion cli/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -617,7 +617,6 @@ const vote = program
const txId = await glamClient.jupiterVote.voteOnProposal(
statePda,
proposal,
governor,
Number(side),
);
console.log("castVote:", txId);
Expand Down

0 comments on commit e41d6ac

Please sign in to comment.