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 { ParsedAccount } from '@oyster/common';
|
||||
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 { vote } from './vote';
|
||||
import { depositSourceTokens } from './depositSourceTokens';
|
||||
import { AccountLayout } from '@solana/spl-token';
|
||||
|
||||
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 (
|
||||
connection: Connection,
|
||||
|
@ -22,31 +43,156 @@ export const depositSourceTokensAndVote = async (
|
|||
const votingTokenAmount =
|
||||
yesVotingTokenAmount > 0 ? yesVotingTokenAmount : noVotingTokenAmount;
|
||||
|
||||
const {
|
||||
voteAccount,
|
||||
yesVoteAccount,
|
||||
noVoteAccount,
|
||||
} = await depositSourceTokens(
|
||||
connection,
|
||||
wallet,
|
||||
proposal,
|
||||
const PROGRAM_IDS = utils.programIds();
|
||||
|
||||
let depositSigners: Account[] = [];
|
||||
let depositInstructions: TransactionInstruction[] = [];
|
||||
|
||||
const accountRentExempt = await connection.getMinimumBalanceForRentExemption(
|
||||
AccountLayout.span,
|
||||
);
|
||||
|
||||
let needToCreateGovAccountToo = !existingVoteAccount;
|
||||
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,
|
||||
existingYesVoteAccount,
|
||||
existingNoVoteAccount,
|
||||
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,
|
||||
wallet.publicKey,
|
||||
votingTokenAmount,
|
||||
);
|
||||
|
||||
await vote(
|
||||
connection,
|
||||
wallet,
|
||||
proposal,
|
||||
timelockConfig,
|
||||
state,
|
||||
voteAccount,
|
||||
yesVoteAccount,
|
||||
noVoteAccount,
|
||||
depositSigners.push(depositAuthority);
|
||||
|
||||
depositInstructions.push(
|
||||
depositSourceTokensInstruction(
|
||||
governanceVotingRecord,
|
||||
existingVoteAccount,
|
||||
sourceAccount,
|
||||
proposal.info.sourceHolding,
|
||||
proposal.info.votingMint,
|
||||
proposal.pubkey,
|
||||
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,
|
||||
cancelText: LABELS.CANCEL,
|
||||
onOk: async () => {
|
||||
if (userTokenAccount && vote != 0) {
|
||||
const vote = await getLatestVote();
|
||||
|
||||
if (userTokenAccount && vote != 0) {
|
||||
const voteAmount = userTokenAccount.info.amount.toNumber();
|
||||
|
||||
const yesTokenAmount = vote > 0 ? voteAmount : 0;
|
||||
|
|
|
@ -39,8 +39,12 @@ export const LABELS = {
|
|||
ADD: 'Add',
|
||||
REMOVE: 'Remove',
|
||||
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',
|
||||
PLEASE_WAIT: 'Please wait...',
|
||||
GOVERNANCE_TOKENS_ADDED: 'Governance tokens added.',
|
||||
|
@ -61,8 +65,6 @@ export const LABELS = {
|
|||
ADD_GOVERNANCE_TOKENS: 'Add Governance Tokens',
|
||||
ADD_COUNCIL_TOKENS: 'Add Council Tokens',
|
||||
ACTIONS: 'Actions',
|
||||
BURNING_VOTES: 'Burning your votes...',
|
||||
VOTES_BURNED: 'Votes burned',
|
||||
VOTE: 'Vote',
|
||||
EXECUTING: 'Executing...',
|
||||
EXECUTED: 'Executed.',
|
||||
|
|
Loading…
Reference in New Issue