mirror of https://github.com/certusone/oyster.git
fix: combine deposit source toenas and vote into a single transaction
This commit is contained in:
parent
15958d211a
commit
63467f1acb
|
@ -1,163 +0,0 @@
|
||||||
import {
|
|
||||||
Account,
|
|
||||||
Connection,
|
|
||||||
PublicKey,
|
|
||||||
TransactionInstruction,
|
|
||||||
} from '@solana/web3.js';
|
|
||||||
import {
|
|
||||||
contexts,
|
|
||||||
utils,
|
|
||||||
models,
|
|
||||||
ParsedAccount,
|
|
||||||
actions,
|
|
||||||
} from '@oyster/common';
|
|
||||||
|
|
||||||
import { TimelockSet } from '../models/timelock';
|
|
||||||
|
|
||||||
import { AccountLayout } from '@solana/spl-token';
|
|
||||||
import { depositSourceTokensInstruction } from '../models/depositSourceTokens';
|
|
||||||
import { LABELS } from '../constants';
|
|
||||||
import { createEmptyGovernanceVotingRecordInstruction } from '../models/createEmptyGovernanceVotingRecord';
|
|
||||||
|
|
||||||
const { createTokenAccount } = actions;
|
|
||||||
const { sendTransaction } = contexts.Connection;
|
|
||||||
const { notify } = utils;
|
|
||||||
const { approve } = models;
|
|
||||||
|
|
||||||
export const depositSourceTokens = async (
|
|
||||||
connection: Connection,
|
|
||||||
wallet: any,
|
|
||||||
proposal: ParsedAccount<TimelockSet>,
|
|
||||||
existingVoteAccount: PublicKey | undefined,
|
|
||||||
existingYesVoteAccount: PublicKey | undefined,
|
|
||||||
existingNoVoteAccount: PublicKey | undefined,
|
|
||||||
sourceAccount: PublicKey,
|
|
||||||
votingTokenAmount: number,
|
|
||||||
): Promise<{
|
|
||||||
voteAccount: PublicKey;
|
|
||||||
yesVoteAccount: PublicKey;
|
|
||||||
noVoteAccount: PublicKey;
|
|
||||||
}> => {
|
|
||||||
const PROGRAM_IDS = utils.programIds();
|
|
||||||
|
|
||||||
let signers: Account[] = [];
|
|
||||||
let instructions: TransactionInstruction[] = [];
|
|
||||||
|
|
||||||
const accountRentExempt = await connection.getMinimumBalanceForRentExemption(
|
|
||||||
AccountLayout.span,
|
|
||||||
);
|
|
||||||
|
|
||||||
let needToCreateGovAccountToo = !existingVoteAccount;
|
|
||||||
if (!existingVoteAccount) {
|
|
||||||
existingVoteAccount = createTokenAccount(
|
|
||||||
instructions,
|
|
||||||
wallet.publicKey,
|
|
||||||
accountRentExempt,
|
|
||||||
proposal.info.votingMint,
|
|
||||||
wallet.publicKey,
|
|
||||||
signers,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const [governanceVotingRecord] = await PublicKey.findProgramAddress(
|
|
||||||
[
|
|
||||||
PROGRAM_IDS.timelock.programAccountId.toBuffer(),
|
|
||||||
proposal.pubkey.toBuffer(),
|
|
||||||
existingVoteAccount.toBuffer(),
|
|
||||||
],
|
|
||||||
PROGRAM_IDS.timelock.programId,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (needToCreateGovAccountToo) {
|
|
||||||
instructions.push(
|
|
||||||
createEmptyGovernanceVotingRecordInstruction(
|
|
||||||
governanceVotingRecord,
|
|
||||||
proposal.pubkey,
|
|
||||||
existingVoteAccount,
|
|
||||||
wallet.publicKey,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!existingYesVoteAccount) {
|
|
||||||
existingYesVoteAccount = createTokenAccount(
|
|
||||||
instructions,
|
|
||||||
wallet.publicKey,
|
|
||||||
accountRentExempt,
|
|
||||||
proposal.info.yesVotingMint,
|
|
||||||
wallet.publicKey,
|
|
||||||
signers,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!existingNoVoteAccount) {
|
|
||||||
existingNoVoteAccount = createTokenAccount(
|
|
||||||
instructions,
|
|
||||||
wallet.publicKey,
|
|
||||||
accountRentExempt,
|
|
||||||
proposal.info.noVotingMint,
|
|
||||||
wallet.publicKey,
|
|
||||||
signers,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const [mintAuthority] = await PublicKey.findProgramAddress(
|
|
||||||
[PROGRAM_IDS.timelock.programAccountId.toBuffer()],
|
|
||||||
PROGRAM_IDS.timelock.programId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const transferAuthority = approve(
|
|
||||||
instructions,
|
|
||||||
[],
|
|
||||||
sourceAccount,
|
|
||||||
wallet.publicKey,
|
|
||||||
votingTokenAmount,
|
|
||||||
);
|
|
||||||
|
|
||||||
signers.push(transferAuthority);
|
|
||||||
|
|
||||||
instructions.push(
|
|
||||||
depositSourceTokensInstruction(
|
|
||||||
governanceVotingRecord,
|
|
||||||
existingVoteAccount,
|
|
||||||
sourceAccount,
|
|
||||||
proposal.info.sourceHolding,
|
|
||||||
proposal.info.votingMint,
|
|
||||||
proposal.pubkey,
|
|
||||||
transferAuthority.publicKey,
|
|
||||||
mintAuthority,
|
|
||||||
votingTokenAmount,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
notify({
|
|
||||||
message: LABELS.ADDING_VOTES_TO_VOTER,
|
|
||||||
description: LABELS.PLEASE_WAIT,
|
|
||||||
type: 'warn',
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
let tx = await sendTransaction(
|
|
||||||
connection,
|
|
||||||
wallet,
|
|
||||||
instructions,
|
|
||||||
signers,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
notify({
|
|
||||||
message: LABELS.VOTES_ADDED,
|
|
||||||
type: 'success',
|
|
||||||
description: LABELS.TRANSACTION + ` ${tx}`,
|
|
||||||
});
|
|
||||||
} catch (ex) {
|
|
||||||
console.error(ex);
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
voteAccount: existingVoteAccount,
|
|
||||||
yesVoteAccount: existingYesVoteAccount,
|
|
||||||
noVoteAccount: existingNoVoteAccount,
|
|
||||||
};
|
|
||||||
};
|
|
|
@ -1,10 +1,31 @@
|
||||||
import { Connection, PublicKey } from '@solana/web3.js';
|
import {
|
||||||
import { ParsedAccount } from '@oyster/common';
|
Account,
|
||||||
|
Connection,
|
||||||
|
PublicKey,
|
||||||
|
TransactionInstruction,
|
||||||
|
} from '@solana/web3.js';
|
||||||
|
import {
|
||||||
|
contexts,
|
||||||
|
utils,
|
||||||
|
models,
|
||||||
|
ParsedAccount,
|
||||||
|
actions,
|
||||||
|
} from '@oyster/common';
|
||||||
|
|
||||||
import { TimelockConfig, TimelockSet, TimelockState } from '../models/timelock';
|
import { TimelockConfig, TimelockSet, TimelockState } from '../models/timelock';
|
||||||
|
|
||||||
import { vote } from './vote';
|
import { AccountLayout } from '@solana/spl-token';
|
||||||
import { depositSourceTokens } from './depositSourceTokens';
|
|
||||||
|
import { LABELS } from '../constants';
|
||||||
|
|
||||||
|
import { depositSourceTokensInstruction } from '../models/depositSourceTokens';
|
||||||
|
import { createEmptyGovernanceVotingRecordInstruction } from '../models/createEmptyGovernanceVotingRecord';
|
||||||
|
import { voteInstruction } from '../models/vote';
|
||||||
|
|
||||||
|
const { createTokenAccount } = actions;
|
||||||
|
const { sendTransactions } = contexts.Connection;
|
||||||
|
const { notify } = utils;
|
||||||
|
const { approve } = models;
|
||||||
|
|
||||||
export const depositSourceTokensAndVote = async (
|
export const depositSourceTokensAndVote = async (
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
|
@ -22,31 +43,156 @@ export const depositSourceTokensAndVote = async (
|
||||||
const votingTokenAmount =
|
const votingTokenAmount =
|
||||||
yesVotingTokenAmount > 0 ? yesVotingTokenAmount : noVotingTokenAmount;
|
yesVotingTokenAmount > 0 ? yesVotingTokenAmount : noVotingTokenAmount;
|
||||||
|
|
||||||
const {
|
const PROGRAM_IDS = utils.programIds();
|
||||||
voteAccount,
|
|
||||||
yesVoteAccount,
|
let depositSigners: Account[] = [];
|
||||||
noVoteAccount,
|
let depositInstructions: TransactionInstruction[] = [];
|
||||||
} = await depositSourceTokens(
|
|
||||||
connection,
|
const accountRentExempt = await connection.getMinimumBalanceForRentExemption(
|
||||||
wallet,
|
AccountLayout.span,
|
||||||
proposal,
|
);
|
||||||
existingVoteAccount,
|
|
||||||
existingYesVoteAccount,
|
let needToCreateGovAccountToo = !existingVoteAccount;
|
||||||
existingNoVoteAccount,
|
if (!existingVoteAccount) {
|
||||||
|
existingVoteAccount = createTokenAccount(
|
||||||
|
depositInstructions,
|
||||||
|
wallet.publicKey,
|
||||||
|
accountRentExempt,
|
||||||
|
proposal.info.votingMint,
|
||||||
|
wallet.publicKey,
|
||||||
|
depositSigners,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [governanceVotingRecord] = await PublicKey.findProgramAddress(
|
||||||
|
[
|
||||||
|
PROGRAM_IDS.timelock.programAccountId.toBuffer(),
|
||||||
|
proposal.pubkey.toBuffer(),
|
||||||
|
existingVoteAccount.toBuffer(),
|
||||||
|
],
|
||||||
|
PROGRAM_IDS.timelock.programId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (needToCreateGovAccountToo) {
|
||||||
|
depositInstructions.push(
|
||||||
|
createEmptyGovernanceVotingRecordInstruction(
|
||||||
|
governanceVotingRecord,
|
||||||
|
proposal.pubkey,
|
||||||
|
existingVoteAccount,
|
||||||
|
wallet.publicKey,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!existingYesVoteAccount) {
|
||||||
|
existingYesVoteAccount = createTokenAccount(
|
||||||
|
depositInstructions,
|
||||||
|
wallet.publicKey,
|
||||||
|
accountRentExempt,
|
||||||
|
proposal.info.yesVotingMint,
|
||||||
|
wallet.publicKey,
|
||||||
|
depositSigners,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!existingNoVoteAccount) {
|
||||||
|
existingNoVoteAccount = createTokenAccount(
|
||||||
|
depositInstructions,
|
||||||
|
wallet.publicKey,
|
||||||
|
accountRentExempt,
|
||||||
|
proposal.info.noVotingMint,
|
||||||
|
wallet.publicKey,
|
||||||
|
depositSigners,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const [mintAuthority] = await PublicKey.findProgramAddress(
|
||||||
|
[PROGRAM_IDS.timelock.programAccountId.toBuffer()],
|
||||||
|
PROGRAM_IDS.timelock.programId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const depositAuthority = approve(
|
||||||
|
depositInstructions,
|
||||||
|
[],
|
||||||
sourceAccount,
|
sourceAccount,
|
||||||
|
wallet.publicKey,
|
||||||
votingTokenAmount,
|
votingTokenAmount,
|
||||||
);
|
);
|
||||||
|
|
||||||
await vote(
|
depositSigners.push(depositAuthority);
|
||||||
connection,
|
|
||||||
wallet,
|
depositInstructions.push(
|
||||||
proposal,
|
depositSourceTokensInstruction(
|
||||||
timelockConfig,
|
governanceVotingRecord,
|
||||||
state,
|
existingVoteAccount,
|
||||||
voteAccount,
|
sourceAccount,
|
||||||
yesVoteAccount,
|
proposal.info.sourceHolding,
|
||||||
noVoteAccount,
|
proposal.info.votingMint,
|
||||||
yesVotingTokenAmount,
|
proposal.pubkey,
|
||||||
noVotingTokenAmount,
|
depositAuthority.publicKey,
|
||||||
|
mintAuthority,
|
||||||
|
votingTokenAmount,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let voteSigners: Account[] = [];
|
||||||
|
let voteInstructions: TransactionInstruction[] = [];
|
||||||
|
|
||||||
|
const voteAuthority = approve(
|
||||||
|
voteInstructions,
|
||||||
|
[],
|
||||||
|
existingVoteAccount,
|
||||||
|
wallet.publicKey,
|
||||||
|
yesVotingTokenAmount + noVotingTokenAmount,
|
||||||
|
);
|
||||||
|
|
||||||
|
voteSigners.push(voteAuthority);
|
||||||
|
|
||||||
|
voteInstructions.push(
|
||||||
|
voteInstruction(
|
||||||
|
governanceVotingRecord,
|
||||||
|
state.pubkey,
|
||||||
|
existingVoteAccount,
|
||||||
|
existingYesVoteAccount,
|
||||||
|
existingNoVoteAccount,
|
||||||
|
proposal.info.votingMint,
|
||||||
|
proposal.info.yesVotingMint,
|
||||||
|
proposal.info.noVotingMint,
|
||||||
|
proposal.info.sourceMint,
|
||||||
|
proposal.pubkey,
|
||||||
|
timelockConfig.pubkey,
|
||||||
|
voteAuthority.publicKey,
|
||||||
|
mintAuthority,
|
||||||
|
yesVotingTokenAmount,
|
||||||
|
noVotingTokenAmount,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
notify({
|
||||||
|
message: LABELS.VOTING_FOR_PROPOSAL,
|
||||||
|
description: LABELS.PLEASE_WAIT,
|
||||||
|
type: 'warn',
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await sendTransactions(
|
||||||
|
connection,
|
||||||
|
wallet,
|
||||||
|
[depositInstructions, voteInstructions],
|
||||||
|
[depositSigners, voteSigners],
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
notify({
|
||||||
|
message: LABELS.PROPOSAL_VOTED,
|
||||||
|
type: 'success',
|
||||||
|
description:
|
||||||
|
yesVotingTokenAmount > 0
|
||||||
|
? `${yesVotingTokenAmount} ${LABELS.TOKENS_VOTED_FOR_THE_PROPOSAL}.`
|
||||||
|
: `${noVotingTokenAmount} ${LABELS.TOKENS_VOTED_AGAINST_THE_PROPOSAL}.`,
|
||||||
|
});
|
||||||
|
} catch (ex) {
|
||||||
|
console.error(ex);
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,108 +0,0 @@
|
||||||
import {
|
|
||||||
Account,
|
|
||||||
Connection,
|
|
||||||
PublicKey,
|
|
||||||
TransactionInstruction,
|
|
||||||
} from '@solana/web3.js';
|
|
||||||
import {
|
|
||||||
contexts,
|
|
||||||
utils,
|
|
||||||
models,
|
|
||||||
ParsedAccount,
|
|
||||||
actions,
|
|
||||||
} from '@oyster/common';
|
|
||||||
|
|
||||||
import { TimelockConfig, TimelockSet, TimelockState } from '../models/timelock';
|
|
||||||
import { LABELS } from '../constants';
|
|
||||||
import { voteInstruction } from '../models/vote';
|
|
||||||
const { createTokenAccount } = actions;
|
|
||||||
const { sendTransaction } = contexts.Connection;
|
|
||||||
const { notify } = utils;
|
|
||||||
const { approve } = models;
|
|
||||||
|
|
||||||
export const vote = async (
|
|
||||||
connection: Connection,
|
|
||||||
wallet: any,
|
|
||||||
proposal: ParsedAccount<TimelockSet>,
|
|
||||||
timelockConfig: ParsedAccount<TimelockConfig>,
|
|
||||||
state: ParsedAccount<TimelockState>,
|
|
||||||
votingAccount: PublicKey,
|
|
||||||
yesVotingAccount: PublicKey,
|
|
||||||
noVotingAccount: PublicKey,
|
|
||||||
yesVotingTokenAmount: number,
|
|
||||||
noVotingTokenAmount: number,
|
|
||||||
) => {
|
|
||||||
const PROGRAM_IDS = utils.programIds();
|
|
||||||
|
|
||||||
let signers: Account[] = [];
|
|
||||||
let instructions: TransactionInstruction[] = [];
|
|
||||||
|
|
||||||
const [mintAuthority] = await PublicKey.findProgramAddress(
|
|
||||||
[PROGRAM_IDS.timelock.programAccountId.toBuffer()],
|
|
||||||
PROGRAM_IDS.timelock.programId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const [governanceVotingRecord] = await PublicKey.findProgramAddress(
|
|
||||||
[
|
|
||||||
PROGRAM_IDS.timelock.programAccountId.toBuffer(),
|
|
||||||
proposal.pubkey.toBuffer(),
|
|
||||||
votingAccount.toBuffer(),
|
|
||||||
],
|
|
||||||
PROGRAM_IDS.timelock.programId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const transferAuthority = approve(
|
|
||||||
instructions,
|
|
||||||
[],
|
|
||||||
votingAccount,
|
|
||||||
wallet.publicKey,
|
|
||||||
yesVotingTokenAmount + noVotingTokenAmount,
|
|
||||||
);
|
|
||||||
|
|
||||||
signers.push(transferAuthority);
|
|
||||||
|
|
||||||
instructions.push(
|
|
||||||
voteInstruction(
|
|
||||||
governanceVotingRecord,
|
|
||||||
state.pubkey,
|
|
||||||
votingAccount,
|
|
||||||
yesVotingAccount,
|
|
||||||
noVotingAccount,
|
|
||||||
proposal.info.votingMint,
|
|
||||||
proposal.info.yesVotingMint,
|
|
||||||
proposal.info.noVotingMint,
|
|
||||||
proposal.info.sourceMint,
|
|
||||||
proposal.pubkey,
|
|
||||||
timelockConfig.pubkey,
|
|
||||||
transferAuthority.publicKey,
|
|
||||||
mintAuthority,
|
|
||||||
yesVotingTokenAmount,
|
|
||||||
noVotingTokenAmount,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
notify({
|
|
||||||
message: LABELS.BURNING_VOTES,
|
|
||||||
description: LABELS.PLEASE_WAIT,
|
|
||||||
type: 'warn',
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
let tx = await sendTransaction(
|
|
||||||
connection,
|
|
||||||
wallet,
|
|
||||||
instructions,
|
|
||||||
signers,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
|
|
||||||
notify({
|
|
||||||
message: LABELS.VOTES_BURNED,
|
|
||||||
type: 'success',
|
|
||||||
description: LABELS.TRANSACTION + ` ${tx}`,
|
|
||||||
});
|
|
||||||
} catch (ex) {
|
|
||||||
console.error(ex);
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -74,8 +74,9 @@ export function Vote({
|
||||||
okText: LABELS.CONFIRM,
|
okText: LABELS.CONFIRM,
|
||||||
cancelText: LABELS.CANCEL,
|
cancelText: LABELS.CANCEL,
|
||||||
onOk: async () => {
|
onOk: async () => {
|
||||||
|
const vote = await getLatestVote();
|
||||||
|
|
||||||
if (userTokenAccount && vote != 0) {
|
if (userTokenAccount && vote != 0) {
|
||||||
const vote = await getLatestVote();
|
|
||||||
const voteAmount = userTokenAccount.info.amount.toNumber();
|
const voteAmount = userTokenAccount.info.amount.toNumber();
|
||||||
|
|
||||||
const yesTokenAmount = vote > 0 ? voteAmount : 0;
|
const yesTokenAmount = vote > 0 ? voteAmount : 0;
|
||||||
|
|
|
@ -39,8 +39,12 @@ export const LABELS = {
|
||||||
ADD: 'Add',
|
ADD: 'Add',
|
||||||
REMOVE: 'Remove',
|
REMOVE: 'Remove',
|
||||||
ADDING_OR_REMOVING: 'Type',
|
ADDING_OR_REMOVING: 'Type',
|
||||||
ADDING_VOTES_TO_VOTER: 'Converting governance tokens to voting tokens',
|
|
||||||
VOTES_ADDED: 'Governance tokens converted.',
|
VOTING_FOR_PROPOSAL: 'Voting for proposal.',
|
||||||
|
PROPOSAL_VOTED: 'Proposal voted.',
|
||||||
|
TOKENS_VOTED_FOR_THE_PROPOSAL: 'tokens voted for the proposal',
|
||||||
|
TOKENS_VOTED_AGAINST_THE_PROPOSAL: 'tokens voted against the proposal',
|
||||||
|
|
||||||
ADDING_GOVERNANCE_TOKENS: 'Adding governance tokens',
|
ADDING_GOVERNANCE_TOKENS: 'Adding governance tokens',
|
||||||
PLEASE_WAIT: 'Please wait...',
|
PLEASE_WAIT: 'Please wait...',
|
||||||
GOVERNANCE_TOKENS_ADDED: 'Governance tokens added.',
|
GOVERNANCE_TOKENS_ADDED: 'Governance tokens added.',
|
||||||
|
@ -61,8 +65,6 @@ export const LABELS = {
|
||||||
ADD_GOVERNANCE_TOKENS: 'Add Governance Tokens',
|
ADD_GOVERNANCE_TOKENS: 'Add Governance Tokens',
|
||||||
ADD_COUNCIL_TOKENS: 'Add Council Tokens',
|
ADD_COUNCIL_TOKENS: 'Add Council Tokens',
|
||||||
ACTIONS: 'Actions',
|
ACTIONS: 'Actions',
|
||||||
BURNING_VOTES: 'Burning your votes...',
|
|
||||||
VOTES_BURNED: 'Votes burned',
|
|
||||||
VOTE: 'Vote',
|
VOTE: 'Vote',
|
||||||
EXECUTING: 'Executing...',
|
EXECUTING: 'Executing...',
|
||||||
EXECUTED: 'Executed.',
|
EXECUTED: 'Executed.',
|
||||||
|
|
Loading…
Reference in New Issue