Merge remote-tracking branch 'origin/main' into feature/m

This commit is contained in:
bartosz-lipinski 2021-04-27 22:30:30 -05:00
commit 3891bdcc47
85 changed files with 744 additions and 747 deletions

View File

@ -8,6 +8,6 @@
"typescript.enablePromptUseWorkspaceTsdk": true, "typescript.enablePromptUseWorkspaceTsdk": true,
"prettier.prettierPath": ".vscode/pnpify/prettier/index.js", "prettier.prettierPath": ".vscode/pnpify/prettier/index.js",
"cSpell.words": [ "cSpell.words": [
"Timelock"
] ]
} }

View File

@ -46,7 +46,7 @@ let WORMHOLE_BRIDGE: {
wrappedMaster: string; wrappedMaster: string;
}; };
let TIMELOCK: { let GOVERNANCE: {
programId: PublicKey; programId: PublicKey;
}; };
@ -66,7 +66,7 @@ export const ENABLE_FEES_INPUT = false;
export const PROGRAM_IDS = [ export const PROGRAM_IDS = [
{ {
name: 'mainnet-beta', name: 'mainnet-beta',
timelock: () => ({ governance: () => ({
programId: new PublicKey('9iAeqqppjn7g1Jn8o2cQCqU5aQVV3h4q9bbWdKRbeC2w'), programId: new PublicKey('9iAeqqppjn7g1Jn8o2cQCqU5aQVV3h4q9bbWdKRbeC2w'),
}), }),
wormhole: () => ({ wormhole: () => ({
@ -87,7 +87,7 @@ export const PROGRAM_IDS = [
}, },
{ {
name: 'testnet', name: 'testnet',
timelock: () => ({ governance: () => ({
programId: new PublicKey('DCVPuhaGNMLh73FRWFroH4o3ERUhBKMRWfBgJV94VqRk'), programId: new PublicKey('DCVPuhaGNMLh73FRWFroH4o3ERUhBKMRWfBgJV94VqRk'),
}), }),
wormhole: () => ({ wormhole: () => ({
@ -105,7 +105,7 @@ export const PROGRAM_IDS = [
}, },
{ {
name: 'devnet', name: 'devnet',
timelock: () => ({ governance: () => ({
programId: new PublicKey('DCVPuhaGNMLh73FRWFroH4o3ERUhBKMRWfBgJV94VqRk'), programId: new PublicKey('DCVPuhaGNMLh73FRWFroH4o3ERUhBKMRWfBgJV94VqRk'),
}), }),
wormhole: () => ({ wormhole: () => ({
@ -123,8 +123,8 @@ export const PROGRAM_IDS = [
}, },
{ {
name: 'localnet', name: 'localnet',
timelock: () => ({ governance: () => ({
programId: new PublicKey('3KEiR9eX7isb8xeFzTzbLZij8tKH6YFYUbMyjBp8ygDK'), programId: new PublicKey('2uWrXQ3tMurqTLe3Dmue6DzasUGV9UPqK7AK7HzS7v3D'),
}), }),
wormhole: () => ({ wormhole: () => ({
pubkey: new PublicKey('WormT3McKhFJ2RkiGpdw9GKvNCrB2aB54gb2uV9MfQC'), pubkey: new PublicKey('WormT3McKhFJ2RkiGpdw9GKvNCrB2aB54gb2uV9MfQC'),
@ -155,7 +155,7 @@ export const setProgramIds = (envName: string) => {
SWAP_PROGRAM_LAYOUT = swap.current.layout; SWAP_PROGRAM_LAYOUT = swap.current.layout;
SWAP_PROGRAM_LEGACY_IDS = swap.legacy; SWAP_PROGRAM_LEGACY_IDS = swap.legacy;
TIMELOCK = instance.timelock(); GOVERNANCE = instance.governance();
if (envName === 'mainnet-beta') { if (envName === 'mainnet-beta') {
LENDING_PROGRAM_ID = new PublicKey( LENDING_PROGRAM_ID = new PublicKey(
@ -172,7 +172,7 @@ export const programIds = () => {
swapLayout: SWAP_PROGRAM_LAYOUT, swapLayout: SWAP_PROGRAM_LAYOUT,
lending: LENDING_PROGRAM_ID, lending: LENDING_PROGRAM_ID,
wormhole: WORMHOLE_BRIDGE, wormhole: WORMHOLE_BRIDGE,
timelock: TIMELOCK, governance: GOVERNANCE,
associatedToken: SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID, associatedToken: SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID,
bpf_upgrade_loader: BPF_UPGRADE_LOADER_ID, bpf_upgrade_loader: BPF_UPGRADE_LOADER_ID,
system: SYSTEM, system: SYSTEM,

View File

@ -10,7 +10,7 @@ module.exports = {
webpack: { webpack: {
configure: (webpackConfig, { env, paths }) => { configure: (webpackConfig, { env, paths }) => {
paths.appBuild = webpackConfig.output.path = path.resolve( paths.appBuild = webpackConfig.output.path = path.resolve(
'./../../build/proposals', './../../build/governance',
); );
return webpackConfig; return webpackConfig;
}, },

View File

@ -1,5 +1,5 @@
{ {
"name": "proposals", "name": "governance",
"version": "0.0.1", "version": "0.0.1",
"dependencies": { "dependencies": {
"@ant-design/icons": "^4.4.0", "@ant-design/icons": "^4.4.0",
@ -60,8 +60,8 @@
"localnet:down": "solana-localnet down", "localnet:down": "solana-localnet down",
"localnet:logs": "solana-localnet logs -f", "localnet:logs": "solana-localnet logs -f",
"predeploy": "git pull --ff-only && yarn && yarn build", "predeploy": "git pull --ff-only && yarn && yarn build",
"deploy": "gh-pages -d ../../build/proposals --repo https://github.com/solana-labs/oyster-gov", "deploy": "gh-pages -d ../../build/governance --repo https://github.com/solana-labs/oyster-gov",
"deploy:ar": "arweave deploy-dir ../../build/proposals --key-file ", "deploy:ar": "arweave deploy-dir ../../build/governance --key-file ",
"format:fix": "prettier --write \"**/*.+(js|jsx|ts|tsx|json|css|md)\"" "format:fix": "prettier --write \"**/*.+(js|jsx|ts|tsx|json|css|md)\""
}, },
"eslintConfig": { "eslintConfig": {

View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

Before

Width:  |  Height:  |  Size: 216 KiB

After

Width:  |  Height:  |  Size: 216 KiB

View File

@ -8,10 +8,10 @@ import {
import { contexts, utils, models, ParsedAccount } from '@oyster/common'; import { contexts, utils, models, ParsedAccount } from '@oyster/common';
import { import {
GOVERNANCE_AUTHORITY_SEED, GOVERNANCE_AUTHORITY_SEED,
CustomSingleSignerTimelockTransactionLayout, CustomSingleSignerTransactionLayout,
TimelockSet, Proposal,
TimelockState, ProposalState,
} from '../models/timelock'; } from '../models/governance';
import { addCustomSingleSignerTransactionInstruction } from '../models/addCustomSingleSignerTransaction'; import { addCustomSingleSignerTransactionInstruction } from '../models/addCustomSingleSignerTransaction';
const { sendTransaction } = contexts.Connection; const { sendTransaction } = contexts.Connection;
@ -21,8 +21,8 @@ const { approve } = models;
export const addCustomSingleSignerTransaction = async ( export const addCustomSingleSignerTransaction = async (
connection: Connection, connection: Connection,
wallet: any, wallet: any,
proposal: ParsedAccount<TimelockSet>, proposal: ParsedAccount<Proposal>,
state: ParsedAccount<TimelockState>, state: ParsedAccount<ProposalState>,
sigAccount: PublicKey, sigAccount: PublicKey,
slot: string, slot: string,
instruction: string, instruction: string,
@ -34,7 +34,7 @@ export const addCustomSingleSignerTransaction = async (
let instructions: TransactionInstruction[] = []; let instructions: TransactionInstruction[] = [];
const rentExempt = await connection.getMinimumBalanceForRentExemption( const rentExempt = await connection.getMinimumBalanceForRentExemption(
CustomSingleSignerTimelockTransactionLayout.span, CustomSingleSignerTransactionLayout.span,
); );
const txnKey = new Account(); const txnKey = new Account();
@ -43,13 +43,13 @@ export const addCustomSingleSignerTransaction = async (
fromPubkey: wallet.publicKey, fromPubkey: wallet.publicKey,
newAccountPubkey: txnKey.publicKey, newAccountPubkey: txnKey.publicKey,
lamports: rentExempt, lamports: rentExempt,
space: CustomSingleSignerTimelockTransactionLayout.span, space: CustomSingleSignerTransactionLayout.span,
programId: PROGRAM_IDS.timelock.programId, programId: PROGRAM_IDS.governance.programId,
}); });
const [authority] = await PublicKey.findProgramAddress( const [authority] = await PublicKey.findProgramAddress(
[Buffer.from(GOVERNANCE_AUTHORITY_SEED), proposal.pubkey.toBuffer()], [Buffer.from(GOVERNANCE_AUTHORITY_SEED), proposal.pubkey.toBuffer()],
PROGRAM_IDS.timelock.programId, PROGRAM_IDS.governance.programId,
); );
signers.push(txnKey); signers.push(txnKey);

View File

@ -14,9 +14,9 @@ import {
import { import {
GOVERNANCE_AUTHORITY_SEED, GOVERNANCE_AUTHORITY_SEED,
TimelockSet, Proposal,
TimelockState, ProposalState,
} from '../models/timelock'; } from '../models/governance';
import { AccountLayout } from '@solana/spl-token'; import { AccountLayout } from '@solana/spl-token';
import { addSignerInstruction } from '../models/addSigner'; import { addSignerInstruction } from '../models/addSigner';
const { createTokenAccount } = actions; const { createTokenAccount } = actions;
@ -27,8 +27,8 @@ const { approve } = models;
export const addSigner = async ( export const addSigner = async (
connection: Connection, connection: Connection,
wallet: any, wallet: any,
proposal: ParsedAccount<TimelockSet>, proposal: ParsedAccount<Proposal>,
state: ParsedAccount<TimelockState>, state: ParsedAccount<ProposalState>,
adminAccount: PublicKey, adminAccount: PublicKey,
newSignatoryAccountOwner: PublicKey, newSignatoryAccountOwner: PublicKey,
) => { ) => {
@ -52,7 +52,7 @@ export const addSigner = async (
const [mintAuthority] = await PublicKey.findProgramAddress( const [mintAuthority] = await PublicKey.findProgramAddress(
[Buffer.from(GOVERNANCE_AUTHORITY_SEED), proposal.pubkey.toBuffer()], [Buffer.from(GOVERNANCE_AUTHORITY_SEED), proposal.pubkey.toBuffer()],
PROGRAM_IDS.timelock.programId, PROGRAM_IDS.governance.programId,
); );
const transferAuthority = approve( const transferAuthority = approve(

View File

@ -14,13 +14,13 @@ import {
} from '@oyster/common'; } from '@oyster/common';
import { AccountLayout, MintLayout } from '@solana/spl-token'; import { AccountLayout, MintLayout } from '@solana/spl-token';
import { initTimelockSetInstruction } from '../models/initTimelockSet'; import { initProposalInstruction } from '../models/initProposal';
import { import {
GOVERNANCE_AUTHORITY_SEED, GOVERNANCE_AUTHORITY_SEED,
TimelockConfig, Governance,
TimelockSetLayout, ProposalLayout,
TimelockStateLayout, ProposalStateLayout,
} from '../models/timelock'; } from '../models/governance';
const { cache } = contexts.Accounts; const { cache } = contexts.Accounts;
const { sendTransactions } = contexts.Connection; const { sendTransactions } = contexts.Connection;
@ -33,7 +33,7 @@ export const createProposal = async (
name: string, name: string,
description: string, description: string,
useGovernance: boolean, useGovernance: boolean,
timelockConfig: ParsedAccount<TimelockConfig>, governance: ParsedAccount<Governance>,
): Promise<Account> => { ): Promise<Account> => {
const PROGRAM_IDS = utils.programIds(); const PROGRAM_IDS = utils.programIds();
@ -51,12 +51,12 @@ export const createProposal = async (
await cache.queryMint( await cache.queryMint(
connection, connection,
useGovernance useGovernance
? timelockConfig.info.governanceMint ? governance.info.governanceMint
: timelockConfig.info.councilMint!, : governance.info.councilMint!,
) )
).decimals; ).decimals;
const timelockSetKey = new Account(); const proposalKey = new Account();
const { const {
sigMint, sigMint,
@ -79,54 +79,54 @@ export const createProposal = async (
wallet, wallet,
accountRentExempt, accountRentExempt,
mintRentExempt, mintRentExempt,
timelockConfig, governance,
useGovernance, useGovernance,
sourceMintDecimals, sourceMintDecimals,
timelockSetKey, proposalKey,
); );
let createTimelockAccountsSigners: Account[] = []; let createGovernanceAccountsSigners: Account[] = [];
let createTimelockAccountsInstructions: TransactionInstruction[] = []; let createGovernanceAccountsInstructions: TransactionInstruction[] = [];
const timelockRentExempt = await connection.getMinimumBalanceForRentExemption( const proposalRentExempt = await connection.getMinimumBalanceForRentExemption(
TimelockSetLayout.span, ProposalLayout.span,
); );
const timelockStateRentExempt = await connection.getMinimumBalanceForRentExemption( const proposalStateRentExempt = await connection.getMinimumBalanceForRentExemption(
TimelockStateLayout.span, ProposalStateLayout.span,
); );
const timelockStateKey = new Account(); const proposalStateKey = new Account();
const uninitializedTimelockStateInstruction = SystemProgram.createAccount({ const uninitializedProposalStateInstruction = SystemProgram.createAccount({
fromPubkey: wallet.publicKey, fromPubkey: wallet.publicKey,
newAccountPubkey: timelockStateKey.publicKey, newAccountPubkey: proposalStateKey.publicKey,
lamports: timelockStateRentExempt, lamports: proposalStateRentExempt,
space: TimelockStateLayout.span, space: ProposalStateLayout.span,
programId: PROGRAM_IDS.timelock.programId, programId: PROGRAM_IDS.governance.programId,
}); });
signers.push(timelockStateKey); signers.push(proposalStateKey);
createTimelockAccountsSigners.push(timelockStateKey); createGovernanceAccountsSigners.push(proposalStateKey);
createTimelockAccountsInstructions.push( createGovernanceAccountsInstructions.push(
uninitializedTimelockStateInstruction, uninitializedProposalStateInstruction,
); );
const uninitializedTimelockSetInstruction = SystemProgram.createAccount({ const uninitializedProposalInstruction = SystemProgram.createAccount({
fromPubkey: wallet.publicKey, fromPubkey: wallet.publicKey,
newAccountPubkey: timelockSetKey.publicKey, newAccountPubkey: proposalKey.publicKey,
lamports: timelockRentExempt, lamports: proposalRentExempt,
space: TimelockSetLayout.span, space: ProposalLayout.span,
programId: PROGRAM_IDS.timelock.programId, programId: PROGRAM_IDS.governance.programId,
}); });
signers.push(timelockSetKey); signers.push(proposalKey);
createTimelockAccountsSigners.push(timelockSetKey); createGovernanceAccountsSigners.push(proposalKey);
createTimelockAccountsInstructions.push(uninitializedTimelockSetInstruction); createGovernanceAccountsInstructions.push(uninitializedProposalInstruction);
instructions.push( instructions.push(
initTimelockSetInstruction( initProposalInstruction(
timelockStateKey.publicKey, proposalStateKey.publicKey,
timelockSetKey.publicKey, proposalKey.publicKey,
timelockConfig.pubkey, governance.pubkey,
sigMint, sigMint,
adminMint, adminMint,
voteMint, voteMint,
@ -141,8 +141,8 @@ export const createProposal = async (
noVoteDumpAccount, noVoteDumpAccount,
sourceHoldingAccount, sourceHoldingAccount,
useGovernance useGovernance
? timelockConfig.info.governanceMint ? governance.info.governanceMint
: timelockConfig.info.councilMint!, : governance.info.councilMint!,
authority, authority,
description, description,
name, name,
@ -161,11 +161,12 @@ export const createProposal = async (
wallet, wallet,
[ [
...associatedInstructions, ...associatedInstructions,
createTimelockAccountsInstructions, createGovernanceAccountsInstructions,
instructions, instructions,
], ],
[...associatedSigners, createTimelockAccountsSigners, signers], [...associatedSigners, createGovernanceAccountsSigners, signers],
SequenceType.Sequential, true,
true,
); );
notify({ notify({
@ -174,7 +175,7 @@ export const createProposal = async (
description: `Transaction - ${tx}`, description: `Transaction - ${tx}`,
}); });
return timelockSetKey; return proposalKey;
} catch (ex) { } catch (ex) {
console.error(ex); console.error(ex);
throw new Error(); throw new Error();
@ -204,7 +205,7 @@ async function getAssociatedAccountsAndInstructions(
wallet: any, wallet: any,
accountRentExempt: number, accountRentExempt: number,
mintRentExempt: number, mintRentExempt: number,
timelockConfig: ParsedAccount<TimelockConfig>, governance: ParsedAccount<Governance>,
useGovernance: boolean, useGovernance: boolean,
sourceMintDecimals: number, sourceMintDecimals: number,
newProposalKey: Account, newProposalKey: Account,
@ -216,7 +217,7 @@ async function getAssociatedAccountsAndInstructions(
Buffer.from(GOVERNANCE_AUTHORITY_SEED), Buffer.from(GOVERNANCE_AUTHORITY_SEED),
newProposalKey.publicKey.toBuffer(), newProposalKey.publicKey.toBuffer(),
], ],
PROGRAM_IDS.timelock.programId, PROGRAM_IDS.governance.programId,
); );
let mintSigners: Account[] = []; let mintSigners: Account[] = [];
@ -351,8 +352,8 @@ async function getAssociatedAccountsAndInstructions(
wallet.publicKey, wallet.publicKey,
accountRentExempt, accountRentExempt,
useGovernance useGovernance
? timelockConfig.info.governanceMint ? governance.info.governanceMint
: timelockConfig.info.councilMint!, : governance.info.councilMint!,
authority, authority,
holdingSigners, holdingSigners,
); );

View File

@ -14,10 +14,10 @@ import {
import { import {
GOVERNANCE_AUTHORITY_SEED, GOVERNANCE_AUTHORITY_SEED,
TimelockConfig, Governance,
TimelockSet, Proposal,
TimelockState, ProposalState,
} from '../models/timelock'; } from '../models/governance';
import { AccountLayout } from '@solana/spl-token'; import { AccountLayout } from '@solana/spl-token';
@ -35,13 +35,13 @@ const { approve } = models;
export const depositSourceTokensAndVote = async ( export const depositSourceTokensAndVote = async (
connection: Connection, connection: Connection,
wallet: any, wallet: any,
proposal: ParsedAccount<TimelockSet>, proposal: ParsedAccount<Proposal>,
existingVoteAccount: PublicKey | undefined, existingVoteAccount: PublicKey | undefined,
existingYesVoteAccount: PublicKey | undefined, existingYesVoteAccount: PublicKey | undefined,
existingNoVoteAccount: PublicKey | undefined, existingNoVoteAccount: PublicKey | undefined,
sourceAccount: PublicKey, sourceAccount: PublicKey,
timelockConfig: ParsedAccount<TimelockConfig>, governance: ParsedAccount<Governance>,
state: ParsedAccount<TimelockState>, state: ParsedAccount<ProposalState>,
yesVotingTokenAmount: number, yesVotingTokenAmount: number,
noVotingTokenAmount: number, noVotingTokenAmount: number,
) => { ) => {
@ -72,11 +72,11 @@ export const depositSourceTokensAndVote = async (
const [governanceVotingRecord] = await PublicKey.findProgramAddress( const [governanceVotingRecord] = await PublicKey.findProgramAddress(
[ [
Buffer.from(GOVERNANCE_AUTHORITY_SEED), Buffer.from(GOVERNANCE_AUTHORITY_SEED),
PROGRAM_IDS.timelock.programId.toBuffer(), PROGRAM_IDS.governance.programId.toBuffer(),
proposal.pubkey.toBuffer(), proposal.pubkey.toBuffer(),
existingVoteAccount.toBuffer(), existingVoteAccount.toBuffer(),
], ],
PROGRAM_IDS.timelock.programId, PROGRAM_IDS.governance.programId,
); );
if (needToCreateGovAccountToo) { if (needToCreateGovAccountToo) {
@ -114,7 +114,7 @@ export const depositSourceTokensAndVote = async (
const [mintAuthority] = await PublicKey.findProgramAddress( const [mintAuthority] = await PublicKey.findProgramAddress(
[Buffer.from(GOVERNANCE_AUTHORITY_SEED), proposal.pubkey.toBuffer()], [Buffer.from(GOVERNANCE_AUTHORITY_SEED), proposal.pubkey.toBuffer()],
PROGRAM_IDS.timelock.programId, PROGRAM_IDS.governance.programId,
); );
const depositAuthority = approve( const depositAuthority = approve(
@ -166,7 +166,7 @@ export const depositSourceTokensAndVote = async (
proposal.info.noVotingMint, proposal.info.noVotingMint,
proposal.info.sourceMint, proposal.info.sourceMint,
proposal.pubkey, proposal.pubkey,
timelockConfig.pubkey, governance.pubkey,
voteAuthority.publicKey, voteAuthority.publicKey,
mintAuthority, mintAuthority,
yesVotingTokenAmount, yesVotingTokenAmount,

View File

@ -0,0 +1,71 @@
import {
Account,
Connection,
Message,
TransactionInstruction,
} from '@solana/web3.js';
import { contexts, utils, ParsedAccount } from '@oyster/common';
import {
Proposal,
ProposalState,
GovernanceTransaction,
} from '../models/governance';
import { executeInstruction } from '../models/execute';
import { LABELS } from '../constants';
import { getMessageAccountInfos } from '../utils/transactions';
const { sendTransaction } = contexts.Connection;
const { notify } = utils;
export const execute = async (
connection: Connection,
wallet: any,
proposal: ParsedAccount<Proposal>,
state: ParsedAccount<ProposalState>,
transaction: ParsedAccount<GovernanceTransaction>,
) => {
let signers: Account[] = [];
let instructions: TransactionInstruction[] = [];
const actualMessage = decodeBufferIntoMessage(transaction.info.instruction);
const accountInfos = getMessageAccountInfos(actualMessage);
instructions.push(
executeInstruction(
transaction.pubkey,
state.pubkey,
proposal.pubkey,
actualMessage.accountKeys[actualMessage.instructions[0].programIdIndex],
proposal.info.config,
accountInfos,
),
);
notify({
message: LABELS.EXECUTING,
description: LABELS.PLEASE_WAIT,
type: 'warn',
});
try {
let tx = await sendTransaction(
connection,
wallet,
instructions,
signers,
true,
);
notify({
message: LABELS.EXECUTED,
type: 'success',
description: LABELS.TRANSACTION + ` ${tx}`,
});
} catch (ex) {
console.error(ex);
throw new Error();
}
};
function decodeBufferIntoMessage(instruction: number[]): Message {
return Message.from(instruction);
}

View File

@ -12,7 +12,7 @@ import {
SequenceType, SequenceType,
} from '@oyster/common'; } from '@oyster/common';
import { TimelockConfig } from '../models/timelock'; import { Governance } from '../models/governance';
import { AccountLayout, Token } from '@solana/spl-token'; import { AccountLayout, Token } from '@solana/spl-token';
import { LABELS } from '../constants'; import { LABELS } from '../constants';
const { createTokenAccount } = actions; const { createTokenAccount } = actions;
@ -26,7 +26,7 @@ export interface SourceEntryInterface {
export const mintSourceTokens = async ( export const mintSourceTokens = async (
connection: Connection, connection: Connection,
wallet: any, wallet: any,
timelockConfig: ParsedAccount<TimelockConfig>, governance: ParsedAccount<Governance>,
useGovernance: boolean, useGovernance: boolean,
entries: SourceEntryInterface[], entries: SourceEntryInterface[],
setSavePerc: (num: number) => void, setSavePerc: (num: number) => void,
@ -50,8 +50,8 @@ export const mintSourceTokens = async (
wallet.publicKey, wallet.publicKey,
accountRentExempt, accountRentExempt,
useGovernance useGovernance
? timelockConfig.info.governanceMint ? governance.info.governanceMint
: timelockConfig.info.councilMint!, : governance.info.councilMint!,
e.owner, e.owner,
signers, signers,
); );
@ -60,8 +60,8 @@ export const mintSourceTokens = async (
Token.createMintToInstruction( Token.createMintToInstruction(
PROGRAM_IDS.token, PROGRAM_IDS.token,
useGovernance useGovernance
? timelockConfig.info.governanceMint ? governance.info.governanceMint
: timelockConfig.info.councilMint!, : governance.info.councilMint!,
e.sourceAccount, e.sourceAccount,
wallet.publicKey, wallet.publicKey,
[], [],

View File

@ -10,13 +10,13 @@ import { AccountLayout, MintLayout, Token } from '@solana/spl-token';
import { import {
GOVERNANCE_AUTHORITY_SEED, GOVERNANCE_AUTHORITY_SEED,
ExecutionType, ExecutionType,
TimelockConfig, Governance,
TimelockType, GovernanceType,
VotingEntryRule, VotingEntryRule,
} from '../models/timelock'; } from '../models/governance';
import { initTimelockConfigInstruction } from '../models/initTimelockConfig'; import { initGovernanceInstruction } from '../models/initGovernance';
import BN from 'bn.js'; import BN from 'bn.js';
import { createEmptyTimelockConfigInstruction } from '../models/createEmptyTimelockConfig'; import { createEmptyGovernanceInstruction } from '../models/createEmptyGovernance';
const { sendTransactions } = contexts.Connection; const { sendTransactions } = contexts.Connection;
const { createMint, createTokenAccount } = actions; const { createMint, createTokenAccount } = actions;
@ -25,7 +25,7 @@ const { notify } = utils;
export const registerProgramGovernance = async ( export const registerProgramGovernance = async (
connection: Connection, connection: Connection,
wallet: any, wallet: any,
uninitializedTimelockConfig: Partial<TimelockConfig>, uninitializedGovernance: Partial<Governance>,
useCouncil: boolean, useCouncil: boolean,
): Promise<PublicKey> => { ): Promise<PublicKey> => {
const PROGRAM_IDS = utils.programIds(); const PROGRAM_IDS = utils.programIds();
@ -41,13 +41,13 @@ export const registerProgramGovernance = async (
AccountLayout.span, AccountLayout.span,
); );
if (!uninitializedTimelockConfig.program) if (!uninitializedGovernance.program)
uninitializedTimelockConfig.program = new Account().publicKey; // Random generation if none given uninitializedGovernance.program = new Account().publicKey; // Random generation if none given
if (!uninitializedTimelockConfig.councilMint && useCouncil) { if (!uninitializedGovernance.councilMint && useCouncil) {
// Initialize the mint, an account for the admin, and give them one council token // Initialize the mint, an account for the admin, and give them one council token
// to start their lives with. // to start their lives with.
uninitializedTimelockConfig.councilMint = createMint( uninitializedGovernance.councilMint = createMint(
mintInstructions, mintInstructions,
wallet.publicKey, wallet.publicKey,
mintRentExempt, mintRentExempt,
@ -61,7 +61,7 @@ export const registerProgramGovernance = async (
mintInstructions, mintInstructions,
wallet.publicKey, wallet.publicKey,
accountRentExempt, accountRentExempt,
uninitializedTimelockConfig.councilMint, uninitializedGovernance.councilMint,
wallet.publicKey, wallet.publicKey,
mintSigners, mintSigners,
); );
@ -69,7 +69,7 @@ export const registerProgramGovernance = async (
mintInstructions.push( mintInstructions.push(
Token.createMintToInstruction( Token.createMintToInstruction(
PROGRAM_IDS.token, PROGRAM_IDS.token,
uninitializedTimelockConfig.councilMint, uninitializedGovernance.councilMint,
adminsCouncilToken, adminsCouncilToken,
wallet.publicKey, wallet.publicKey,
[], [],
@ -78,10 +78,10 @@ export const registerProgramGovernance = async (
); );
} }
if (!uninitializedTimelockConfig.governanceMint) { if (!uninitializedGovernance.governanceMint) {
// Initialize the mint, an account for the admin, and give them one governance token // Initialize the mint, an account for the admin, and give them one governance token
// to start their lives with. // to start their lives with.
uninitializedTimelockConfig.governanceMint = createMint( uninitializedGovernance.governanceMint = createMint(
mintInstructions, mintInstructions,
wallet.publicKey, wallet.publicKey,
mintRentExempt, mintRentExempt,
@ -95,7 +95,7 @@ export const registerProgramGovernance = async (
mintInstructions, mintInstructions,
wallet.publicKey, wallet.publicKey,
accountRentExempt, accountRentExempt,
uninitializedTimelockConfig.governanceMint, uninitializedGovernance.governanceMint,
wallet.publicKey, wallet.publicKey,
mintSigners, mintSigners,
); );
@ -103,7 +103,7 @@ export const registerProgramGovernance = async (
mintInstructions.push( mintInstructions.push(
Token.createMintToInstruction( Token.createMintToInstruction(
PROGRAM_IDS.token, PROGRAM_IDS.token,
uninitializedTimelockConfig.governanceMint, uninitializedGovernance.governanceMint,
adminsGovernanceToken, adminsGovernanceToken,
wallet.publicKey, wallet.publicKey,
[], [],
@ -112,52 +112,44 @@ export const registerProgramGovernance = async (
); );
} }
const councilMintSeed = uninitializedTimelockConfig.councilMint const [governanceKey] = await PublicKey.findProgramAddress(
? uninitializedTimelockConfig.councilMint.toBuffer()
: Buffer.from('');
const [timelockConfigKey] = await PublicKey.findProgramAddress(
[ [
Buffer.from(GOVERNANCE_AUTHORITY_SEED), Buffer.from(GOVERNANCE_AUTHORITY_SEED),
PROGRAM_IDS.timelock.programId.toBuffer(), uninitializedGovernance.program.toBuffer(),
uninitializedTimelockConfig.governanceMint.toBuffer(),
councilMintSeed,
uninitializedTimelockConfig.program.toBuffer(),
], ],
PROGRAM_IDS.timelock.programId, PROGRAM_IDS.governance.programId,
); );
const [programDataAccount] = await PublicKey.findProgramAddress( const [programDataAccount] = await PublicKey.findProgramAddress(
[uninitializedTimelockConfig.program.toBuffer()], [uninitializedGovernance.program.toBuffer()],
PROGRAM_IDS.bpf_upgrade_loader, PROGRAM_IDS.bpf_upgrade_loader,
); );
instructions.push( instructions.push(
createEmptyTimelockConfigInstruction( createEmptyGovernanceInstruction(
timelockConfigKey, governanceKey,
uninitializedTimelockConfig.program, uninitializedGovernance.program,
programDataAccount, programDataAccount,
wallet.publicKey, wallet.publicKey,
uninitializedTimelockConfig.governanceMint, uninitializedGovernance.governanceMint,
wallet.publicKey, wallet.publicKey,
uninitializedTimelockConfig.councilMint, uninitializedGovernance.councilMint,
), ),
); );
instructions.push( instructions.push(
initTimelockConfigInstruction( initGovernanceInstruction(
timelockConfigKey, governanceKey,
uninitializedTimelockConfig.program, uninitializedGovernance.program,
uninitializedTimelockConfig.governanceMint, uninitializedGovernance.governanceMint,
uninitializedTimelockConfig.voteThreshold!, uninitializedGovernance.voteThreshold!,
uninitializedTimelockConfig.executionType || ExecutionType.Independent, uninitializedGovernance.executionType || ExecutionType.Independent,
uninitializedTimelockConfig.timelockType || uninitializedGovernance.governanceType || GovernanceType.Governance,
TimelockType.CustomSingleSignerV1, uninitializedGovernance.votingEntryRule || VotingEntryRule.Anytime,
uninitializedTimelockConfig.votingEntryRule || VotingEntryRule.Anytime, uninitializedGovernance.minimumSlotWaitingPeriod || new BN(0),
uninitializedTimelockConfig.minimumSlotWaitingPeriod || new BN(0), uninitializedGovernance.timeLimit || new BN(0),
uninitializedTimelockConfig.timeLimit || new BN(0), uninitializedGovernance.name || '',
uninitializedTimelockConfig.name || '', uninitializedGovernance.councilMint,
uninitializedTimelockConfig.councilMint,
), ),
); );
@ -184,7 +176,7 @@ export const registerProgramGovernance = async (
description: `Transaction - ${tx}`, description: `Transaction - ${tx}`,
}); });
return timelockConfigKey; return governanceKey;
} catch (ex) { } catch (ex) {
console.error(ex); console.error(ex);
throw new Error(); throw new Error();

View File

@ -6,7 +6,7 @@ import {
} from '@solana/web3.js'; } from '@solana/web3.js';
import { contexts, utils, models, ParsedAccount } from '@oyster/common'; import { contexts, utils, models, ParsedAccount } from '@oyster/common';
import { GOVERNANCE_AUTHORITY_SEED, TimelockSet } from '../models/timelock'; import { GOVERNANCE_AUTHORITY_SEED, Proposal } from '../models/governance';
import { removeSignerInstruction } from '../models/removeSigner'; import { removeSignerInstruction } from '../models/removeSigner';
const { sendTransaction } = contexts.Connection; const { sendTransaction } = contexts.Connection;
const { notify } = utils; const { notify } = utils;
@ -15,7 +15,7 @@ const { approve } = models;
export const removeSigner = async ( export const removeSigner = async (
connection: Connection, connection: Connection,
wallet: any, wallet: any,
proposal: ParsedAccount<TimelockSet>, proposal: ParsedAccount<Proposal>,
adminAccount: PublicKey, adminAccount: PublicKey,
sigAccount: PublicKey, sigAccount: PublicKey,
) => { ) => {
@ -26,7 +26,7 @@ export const removeSigner = async (
const [mintAuthority] = await PublicKey.findProgramAddress( const [mintAuthority] = await PublicKey.findProgramAddress(
[Buffer.from(GOVERNANCE_AUTHORITY_SEED), proposal.pubkey.toBuffer()], [Buffer.from(GOVERNANCE_AUTHORITY_SEED), proposal.pubkey.toBuffer()],
PROGRAM_IDS.timelock.programId, PROGRAM_IDS.governance.programId,
); );
const transferAuthority = approve( const transferAuthority = approve(

View File

@ -8,9 +8,9 @@ import { contexts, utils, models, ParsedAccount } from '@oyster/common';
import { import {
GOVERNANCE_AUTHORITY_SEED, GOVERNANCE_AUTHORITY_SEED,
TimelockSet, Proposal,
TimelockState, ProposalState,
} from '../models/timelock'; } from '../models/governance';
import { signInstruction } from '../models/sign'; import { signInstruction } from '../models/sign';
const { sendTransaction } = contexts.Connection; const { sendTransaction } = contexts.Connection;
@ -20,8 +20,8 @@ const { approve } = models;
export const sign = async ( export const sign = async (
connection: Connection, connection: Connection,
wallet: any, wallet: any,
proposal: ParsedAccount<TimelockSet>, proposal: ParsedAccount<Proposal>,
state: ParsedAccount<TimelockState>, state: ParsedAccount<ProposalState>,
sigAccount: PublicKey, sigAccount: PublicKey,
) => { ) => {
const PROGRAM_IDS = utils.programIds(); const PROGRAM_IDS = utils.programIds();
@ -31,7 +31,7 @@ export const sign = async (
const [mintAuthority] = await PublicKey.findProgramAddress( const [mintAuthority] = await PublicKey.findProgramAddress(
[Buffer.from(GOVERNANCE_AUTHORITY_SEED), proposal.pubkey.toBuffer()], [Buffer.from(GOVERNANCE_AUTHORITY_SEED), proposal.pubkey.toBuffer()],
PROGRAM_IDS.timelock.programId, PROGRAM_IDS.governance.programId,
); );
const transferAuthority = approve( const transferAuthority = approve(

View File

@ -14,10 +14,10 @@ import {
import { import {
GOVERNANCE_AUTHORITY_SEED, GOVERNANCE_AUTHORITY_SEED,
TimelockSet, Proposal,
TimelockState, ProposalState,
TimelockStateStatus, ProposalStateStatus,
} from '../models/timelock'; } from '../models/governance';
import { AccountLayout } from '@solana/spl-token'; import { AccountLayout } from '@solana/spl-token';
import { withdrawVotingTokensInstruction } from '../models/withdrawVotingTokens'; import { withdrawVotingTokensInstruction } from '../models/withdrawVotingTokens';
import { LABELS } from '../constants'; import { LABELS } from '../constants';
@ -29,8 +29,8 @@ const { approve } = models;
export const withdrawVotingTokens = async ( export const withdrawVotingTokens = async (
connection: Connection, connection: Connection,
wallet: any, wallet: any,
proposal: ParsedAccount<TimelockSet>, proposal: ParsedAccount<Proposal>,
state: ParsedAccount<TimelockState>, state: ParsedAccount<ProposalState>,
existingVoteAccount: PublicKey | undefined, existingVoteAccount: PublicKey | undefined,
existingYesVoteAccount: PublicKey | undefined, existingYesVoteAccount: PublicKey | undefined,
existingNoVoteAccount: PublicKey | undefined, existingNoVoteAccount: PublicKey | undefined,
@ -81,7 +81,7 @@ export const withdrawVotingTokens = async (
const [mintAuthority] = await PublicKey.findProgramAddress( const [mintAuthority] = await PublicKey.findProgramAddress(
[Buffer.from(GOVERNANCE_AUTHORITY_SEED), proposal.pubkey.toBuffer()], [Buffer.from(GOVERNANCE_AUTHORITY_SEED), proposal.pubkey.toBuffer()],
PROGRAM_IDS.timelock.programId, PROGRAM_IDS.governance.programId,
); );
// We dont know in this scope how much is in each account so we just ask for all in each. // We dont know in this scope how much is in each account so we just ask for all in each.
@ -119,11 +119,11 @@ export const withdrawVotingTokens = async (
const [governanceVotingRecord] = await PublicKey.findProgramAddress( const [governanceVotingRecord] = await PublicKey.findProgramAddress(
[ [
Buffer.from(GOVERNANCE_AUTHORITY_SEED), Buffer.from(GOVERNANCE_AUTHORITY_SEED),
PROGRAM_IDS.timelock.programId.toBuffer(), PROGRAM_IDS.governance.programId.toBuffer(),
proposal.pubkey.toBuffer(), proposal.pubkey.toBuffer(),
existingVoteAccount.toBuffer(), existingVoteAccount.toBuffer(),
], ],
PROGRAM_IDS.timelock.programId, PROGRAM_IDS.governance.programId,
); );
signers.push(transferAuthority); signers.push(transferAuthority);
@ -150,7 +150,7 @@ export const withdrawVotingTokens = async (
); );
const [msg, completedMsg] = const [msg, completedMsg] =
state.info.status === TimelockStateStatus.Voting state.info.status === ProposalStateStatus.Voting
? [LABELS.WITHDRAWING_YOUR_VOTE, LABELS.VOTE_WITHDRAWN] ? [LABELS.WITHDRAWING_YOUR_VOTE, LABELS.VOTE_WITHDRAWN]
: [LABELS.REFUNDING_YOUR_TOKENS, LABELS.TOKENS_REFUNDED]; : [LABELS.REFUNDING_YOUR_TOKENS, LABELS.TOKENS_REFUNDED];

View File

@ -1,53 +1,49 @@
import * as CANNON from 'cannon' import * as CANNON from 'cannon';
import React, { useState, useEffect, useContext, useRef } from 'react' import React, { useState, useEffect, useContext, useRef } from 'react';
import { useFrame } from 'react-three-fiber' import { useFrame } from 'react-three-fiber';
// Cannon-world context provider // Cannon-world context provider
export const CannonContext = React.createContext<any | null>( export const CannonContext = React.createContext<any | null>(null);
null,
);
export function Provider({ children = null as any }) { export function Provider({ children = null as any }) {
// Set up physics // Set up physics
const [world] = useState(() => new CANNON.World()); const [world] = useState(() => new CANNON.World());
useEffect(() => { useEffect(() => {
world.broadphase = new CANNON.NaiveBroadphase() world.broadphase = new CANNON.NaiveBroadphase();
world.solver.iterations = 10 world.solver.iterations = 10;
world.gravity.set(0, 0, -25) world.gravity.set(0, 0, -25);
}, [world]); }, [world]);
// Run world stepper every frame // Run world stepper every frame
useFrame(() => world.step(1 / 60)); useFrame(() => world.step(1 / 60));
// Distribute world via context // Distribute world via context
return ( return <CannonContext.Provider value={world} children={children} />;
<CannonContext.Provider value={world} children={children} />
);
} }
// Custom hook to maintain a world physics body // Custom hook to maintain a world physics body
export function useCannon({ ...props }, fn: any, deps = []) { export function useCannon({ ...props }, fn: any, deps = []) {
const ref = useRef<any>() const ref = useRef<any>();
// Get cannon world object // Get cannon world object
const world = useContext(CannonContext) const world = useContext(CannonContext);
// Instanciate a physics body // Instanciate a physics body
const [body] = useState(() => new CANNON.Body(props)) const [body] = useState(() => new CANNON.Body(props));
useEffect(() => { useEffect(() => {
// Call function so the user can add shapes // Call function so the user can add shapes
fn(body) fn(body);
// Add body to world on mount // Add body to world on mount
world.addBody(body) world.addBody(body);
// Remove body on unmount // Remove body on unmount
return () => world.removeBody(body) return () => world.removeBody(body);
}, deps) }, deps); //eslint-disable-line
useFrame(() => { useFrame(() => {
if (ref.current) { if (ref.current) {
// Transport cannon physics into the referenced threejs object // Transport cannon physics into the referenced threejs object
ref.current.position.copy(body.position) ref.current.position.copy(body.position);
ref.current.quaternion.copy(body.quaternion) ref.current.quaternion.copy(body.quaternion);
} }
}) });
return ref return ref;
} }

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import './../../App.less'; import './../../App.less';
import { Breadcrumb, Layout } from 'antd'; import { Layout } from 'antd';
import { Link, useLocation } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { LABELS } from '../../constants'; import { LABELS } from '../../constants';
import { components } from '@oyster/common'; import { components } from '@oyster/common';
@ -11,30 +11,31 @@ import Logo from './dark-horizontal-combined-rainbow.inline.svg';
const { AppBar } = components; const { AppBar } = components;
export const AppLayout = React.memo((props: any) => { export const AppLayout = React.memo((props: any) => {
const location = useLocation(); // const location = useLocation();
const breadcrumbNameMap: any = { // const breadcrumbNameMap: any = {
'/governance': 'Governance', // '/governance': 'Governance',
'/apps/1': 'Application1', // '/apps/1': 'Application1',
'/apps/2': 'Application2', // '/apps/2': 'Application2',
'/apps/1/detail': 'Detail', // '/apps/1/detail': 'Detail',
'/apps/2/detail': 'Detail', // '/apps/2/detail': 'Detail',
}; // };
const pathSnippets = location.pathname.split('/').filter(i => i); //const pathSnippets = location.pathname.split('/').filter(i => i);
const extraBreadcrumbItems = pathSnippets.map((_, index) => { // const extraBreadcrumbItems = pathSnippets.map((_, index) => {
const url = `/${pathSnippets.slice(0, index + 1).join('/')}`; // const url = `/${pathSnippets.slice(0, index + 1).join('/')}`;
return ( // return (
<Breadcrumb.Item key={url}> // <Breadcrumb.Item key={url}>
<Link to={url}>{breadcrumbNameMap[url]}</Link> // <Link to={url}>{breadcrumbNameMap[url]}</Link>
</Breadcrumb.Item> // </Breadcrumb.Item>
); // );
}); // });
const breadcrumbItems = [
<Breadcrumb.Item key="home"> // const breadcrumbItems = [
<Link to="/">Home</Link> // <Breadcrumb.Item key="home">
</Breadcrumb.Item>, // <Link to="/">Home</Link>
].concat(extraBreadcrumbItems); // </Breadcrumb.Item>,
// ].concat(extraBreadcrumbItems);
// TODO: add breadcrumb // TODO: add breadcrumb

View File

@ -1,7 +1,7 @@
import { ParsedAccount } from '@oyster/common'; import { ParsedAccount } from '@oyster/common';
import { Button, Modal, Input, Form, Progress } from 'antd'; import { Button, Modal, Input, Form, Progress } from 'antd';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { TimelockSet, TimelockState } from '../../models/timelock'; import { Proposal, ProposalState } from '../../models/governance';
import { utils, contexts, hooks } from '@oyster/common'; import { utils, contexts, hooks } from '@oyster/common';
import { addSigner } from '../../actions/addSigner'; import { addSigner } from '../../actions/addSigner';
import { PublicKey } from '@solana/web3.js'; import { PublicKey } from '@solana/web3.js';
@ -22,8 +22,8 @@ export default function AddSigners({
proposal, proposal,
state, state,
}: { }: {
proposal: ParsedAccount<TimelockSet>; proposal: ParsedAccount<Proposal>;
state: ParsedAccount<TimelockState>; state: ParsedAccount<ProposalState>;
}) { }) {
const wallet = useWallet(); const wallet = useWallet();
const connection = useConnection(); const connection = useConnection();

View File

@ -7,17 +7,19 @@ import {
RedoOutlined, RedoOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import { ParsedAccount, contexts } from '@oyster/common'; import { ParsedAccount, contexts } from '@oyster/common';
import { Card } from 'antd'; import { Message } from '@solana/web3.js';
import { Card, Button } from 'antd';
import Meta from 'antd/lib/card/Meta'; import Meta from 'antd/lib/card/Meta';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useMemo, useState } from 'react';
import { execute } from '../../actions/execute'; import { execute } from '../../actions/execute';
import { LABELS } from '../../constants'; import { LABELS } from '../../constants';
import { import {
TimelockSet, Proposal,
TimelockState, ProposalState,
TimelockStateStatus, ProposalStateStatus,
TimelockTransaction, GovernanceTransaction,
} from '../../models/timelock'; } from '../../models/governance';
import './style.less'; import './style.less';
const { useWallet } = contexts.Wallet; const { useWallet } = contexts.Wallet;
@ -35,22 +37,33 @@ export function InstructionCard({
state, state,
position, position,
}: { }: {
instruction: ParsedAccount<TimelockTransaction>; instruction: ParsedAccount<GovernanceTransaction>;
proposal: ParsedAccount<TimelockSet>; proposal: ParsedAccount<Proposal>;
state: ParsedAccount<TimelockState>; state: ParsedAccount<ProposalState>;
position: number; position: number;
}) { }) {
const [tabKey, setTabKey] = useState('info'); const [tabKey, setTabKey] = useState('info');
const [playing, setPlaying] = useState( const [playing, setPlaying] = useState(
instruction.info.executed === 1 ? Playstate.Played : Playstate.Unplayed, instruction.info.executed === 1 ? Playstate.Played : Playstate.Unplayed,
); );
const instructionDetails = useMemo(() => {
const message = Message.from(instruction.info.instruction);
return {
instructionProgramID:
message.accountKeys[message.instructions[0].programIdIndex],
instructionData: message.instructions[0].data,
};
}, [instruction]);
const contentList: Record<string, JSX.Element> = { const contentList: Record<string, JSX.Element> = {
info: ( info: (
<Meta <Meta
title={'Program: TODO'} title={`${LABELS.PROGRAM_ID}: ${instructionDetails.instructionProgramID}`}
description={ description={
<> <>
<p>Instruction: TODO</p> <p>{`${LABELS.INSTRUCTION}: ${instructionDetails.instructionData}`}</p>
<p> <p>
{LABELS.DELAY}: {instruction.info.slot.toNumber()} {LABELS.DELAY}: {instruction.info.slot.toNumber()}
</p> </p>
@ -93,9 +106,9 @@ function PlayStatusButton({
setPlaying, setPlaying,
instruction, instruction,
}: { }: {
proposal: ParsedAccount<TimelockSet>; proposal: ParsedAccount<Proposal>;
state: ParsedAccount<TimelockState>; state: ParsedAccount<ProposalState>;
instruction: ParsedAccount<TimelockTransaction>; instruction: ParsedAccount<GovernanceTransaction>;
playing: Playstate; playing: Playstate;
setPlaying: React.Dispatch<React.SetStateAction<Playstate>>; setPlaying: React.Dispatch<React.SetStateAction<Playstate>>;
}) { }) {
@ -131,25 +144,25 @@ function PlayStatusButton({
}; };
if ( if (
state.info.status !== TimelockStateStatus.Executing && state.info.status !== ProposalStateStatus.Executing &&
state.info.status !== TimelockStateStatus.Completed state.info.status !== ProposalStateStatus.Completed
) )
return null; return null;
if (ineligibleToSee) return null; if (ineligibleToSee) return null;
if (playing === Playstate.Unplayed) if (playing === Playstate.Unplayed)
return ( return (
<a onClick={run}> <Button onClick={run}>
<PlayCircleOutlined style={{ color: 'green' }} key="play" /> <PlayCircleOutlined style={{ color: 'green' }} key="play" />
</a> </Button>
); );
else if (playing === Playstate.Playing) else if (playing === Playstate.Playing)
return <LoadingOutlined style={{ color: 'orange' }} key="loading" />; return <LoadingOutlined style={{ color: 'orange' }} key="loading" />;
else if (playing === Playstate.Error) else if (playing === Playstate.Error)
return ( return (
<a onClick={run}> <Button onClick={run}>
<RedoOutlined style={{ color: 'orange' }} key="play" /> <RedoOutlined style={{ color: 'orange' }} key="play" />
</a> </Button>
); );
else return <CheckCircleOutlined style={{ color: 'green' }} key="played" />; else return <CheckCircleOutlined style={{ color: 'green' }} key="played" />;
} }

View File

@ -1,7 +1,7 @@
import { ParsedAccount } from '@oyster/common'; import { ParsedAccount } from '@oyster/common';
import { Button, Modal, Input, Form, Progress, InputNumber, Radio } from 'antd'; import { Button, Modal, Input, Form, Progress, InputNumber, Radio } from 'antd';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { TimelockConfig } from '../../models/timelock'; import { Governance } from '../../models/governance';
import { utils, contexts } from '@oyster/common'; import { utils, contexts } from '@oyster/common';
import { PublicKey } from '@solana/web3.js'; import { PublicKey } from '@solana/web3.js';
import { LABELS } from '../../constants'; import { LABELS } from '../../constants';
@ -22,18 +22,18 @@ const layout = {
}; };
export default function MintSourceTokens({ export default function MintSourceTokens({
timelockConfig, governance,
useGovernance, useGovernance,
}: { }: {
timelockConfig: ParsedAccount<TimelockConfig>; governance: ParsedAccount<Governance>;
useGovernance: boolean; useGovernance: boolean;
}) { }) {
const PROGRAM_IDS = utils.programIds(); const PROGRAM_IDS = utils.programIds();
const wallet = useWallet(); const wallet = useWallet();
const connection = useConnection(); const connection = useConnection();
const mintKey = useGovernance const mintKey = useGovernance
? timelockConfig.info.governanceMint ? governance.info.governanceMint
: timelockConfig.info.councilMint!; : governance.info.councilMint!;
const mint = useMint(mintKey); const mint = useMint(mintKey);
const [saving, setSaving] = useState(false); const [saving, setSaving] = useState(false);
@ -108,7 +108,7 @@ export default function MintSourceTokens({
try { try {
if (sourceHolders[i].owner) { if (sourceHolders[i].owner) {
const tokenAccounts = await connection.getTokenAccountsByOwner( const tokenAccounts = await connection.getTokenAccountsByOwner(
sourceHolders[i].owner || PROGRAM_IDS.timelock, sourceHolders[i].owner || PROGRAM_IDS.governance,
{ {
programId: PROGRAM_IDS.token, programId: PROGRAM_IDS.token,
}, },
@ -130,7 +130,7 @@ export default function MintSourceTokens({
await mintSourceTokens( await mintSourceTokens(
connection, connection,
wallet.wallet, wallet.wallet,
timelockConfig, governance,
useGovernance, useGovernance,
sourceHoldersToRun, sourceHoldersToRun,
setSavePerc, setSavePerc,

View File

@ -3,10 +3,10 @@ import { Card } from 'antd';
import { Form, Input } from 'antd'; import { Form, Input } from 'antd';
import { import {
INSTRUCTION_LIMIT, INSTRUCTION_LIMIT,
TimelockConfig, Governance,
TimelockSet, Proposal,
TimelockState, ProposalState,
} from '../../models/timelock'; } from '../../models/governance';
import { contexts, ParsedAccount, hooks, utils } from '@oyster/common'; import { contexts, ParsedAccount, hooks, utils } from '@oyster/common';
import { addCustomSingleSignerTransaction } from '../../actions/addCustomSingleSignerTransaction'; import { addCustomSingleSignerTransaction } from '../../actions/addCustomSingleSignerTransaction';
import { SaveOutlined } from '@ant-design/icons'; import { SaveOutlined } from '@ant-design/icons';
@ -28,9 +28,9 @@ export function NewInstructionCard({
position, position,
config, config,
}: { }: {
proposal: ParsedAccount<TimelockSet>; proposal: ParsedAccount<Proposal>;
state: ParsedAccount<TimelockState>; state: ParsedAccount<ProposalState>;
config: ParsedAccount<TimelockConfig>; config: ParsedAccount<Governance>;
position: number; position: number;
}) { }) {
const [form] = Form.useForm(); const [form] = Form.useForm();

View File

@ -3,7 +3,7 @@ import { ParsedAccount, hooks, contexts, utils } from '@oyster/common';
import { Button, Modal } from 'antd'; import { Button, Modal } from 'antd';
import React from 'react'; import React from 'react';
import { sign } from '../../actions/sign'; import { sign } from '../../actions/sign';
import { TimelockSet, TimelockState } from '../../models/timelock'; import { Proposal, ProposalState } from '../../models/governance';
const { confirm } = Modal; const { confirm } = Modal;
const { useWallet } = contexts.Wallet; const { useWallet } = contexts.Wallet;
@ -15,8 +15,8 @@ export default function SignButton({
proposal, proposal,
state, state,
}: { }: {
proposal: ParsedAccount<TimelockSet>; proposal: ParsedAccount<Proposal>;
state: ParsedAccount<TimelockState>; state: ParsedAccount<ProposalState>;
}) { }) {
const wallet = useWallet(); const wallet = useWallet();
const connection = useConnection(); const connection = useConnection();

View File

@ -3,15 +3,15 @@ import { Badge, Tag } from 'antd';
import React from 'react'; import React from 'react';
import { import {
STATE_COLOR, STATE_COLOR,
TimelockState, ProposalState,
TimelockStateStatus, ProposalStateStatus,
} from '../../models/timelock'; } from '../../models/governance';
export function StateBadgeRibbon({ export function StateBadgeRibbon({
state, state,
children, children,
}: { }: {
state: ParsedAccount<TimelockState>; state: ParsedAccount<ProposalState>;
children?: any; children?: any;
}) { }) {
const status = state.info.status; const status = state.info.status;
@ -19,19 +19,19 @@ export function StateBadgeRibbon({
return ( return (
<Badge.Ribbon <Badge.Ribbon
style={{ backgroundColor: color }} style={{ backgroundColor: color }}
text={TimelockStateStatus[status]} text={ProposalStateStatus[status]}
> >
{children} {children}
</Badge.Ribbon> </Badge.Ribbon>
); );
} }
export function StateBadge({ state }: { state: ParsedAccount<TimelockState> }) { export function StateBadge({ state }: { state: ParsedAccount<ProposalState> }) {
const status = state.info.status; const status = state.info.status;
let color = STATE_COLOR[status]; let color = STATE_COLOR[status];
return ( return (
<Tag color={color} style={{ borderWidth: 0 }}> <Tag color={color} style={{ borderWidth: 0 }}>
{TimelockStateStatus[status]} {ProposalStateStatus[status]}
</Tag> </Tag>
); );
} }

View File

@ -2,11 +2,11 @@ import { ParsedAccount } from '@oyster/common';
import { Button, Col, Modal, Row } from 'antd'; import { Button, Col, Modal, Row } from 'antd';
import React from 'react'; import React from 'react';
import { import {
TimelockConfig, Governance,
TimelockSet, Proposal,
TimelockState, ProposalState,
TimelockStateStatus, ProposalStateStatus,
} from '../../models/timelock'; } from '../../models/governance';
import { LABELS } from '../../constants'; import { LABELS } from '../../constants';
import { depositSourceTokensAndVote } from '../../actions/depositSourceTokensAndVote'; import { depositSourceTokensAndVote } from '../../actions/depositSourceTokensAndVote';
import { contexts, hooks } from '@oyster/common'; import { contexts, hooks } from '@oyster/common';
@ -22,12 +22,12 @@ const { confirm } = Modal;
export function Vote({ export function Vote({
proposal, proposal,
state, state,
timelockConfig, governance,
yeahVote, yeahVote,
}: { }: {
proposal: ParsedAccount<TimelockSet>; proposal: ParsedAccount<Proposal>;
state: ParsedAccount<TimelockState>; state: ParsedAccount<ProposalState>;
timelockConfig: ParsedAccount<TimelockConfig>; governance: ParsedAccount<Governance>;
yeahVote: boolean; yeahVote: boolean;
}) { }) {
const wallet = useWallet(); const wallet = useWallet();
@ -42,7 +42,7 @@ export function Vote({
const eligibleToView = const eligibleToView =
userTokenAccount && userTokenAccount &&
userTokenAccount.info.amount.toNumber() > 0 && userTokenAccount.info.amount.toNumber() > 0 &&
state.info.status === TimelockStateStatus.Voting; state.info.status === ProposalStateStatus.Voting;
const [btnLabel, title, msg, icon] = yeahVote const [btnLabel, title, msg, icon] = yeahVote
? [ ? [
@ -90,7 +90,7 @@ export function Vote({
yesVoteAccount?.pubkey, yesVoteAccount?.pubkey,
noVoteAccount?.pubkey, noVoteAccount?.pubkey,
userTokenAccount.pubkey, userTokenAccount.pubkey,
timelockConfig, governance,
state, state,
yesTokenAmount, yesTokenAmount,
noTokenAmount, noTokenAmount,

View File

@ -14,10 +14,6 @@ const MAX_BUBBLE_AMOUNT = 50;
export function VoterBubbleGraph(props: IVoterBubbleGraph) { export function VoterBubbleGraph(props: IVoterBubbleGraph) {
const { data, width, height, endpoint } = props; const { data, width, height, endpoint } = props;
const subdomain = endpoint
.replace('http://', '')
.replace('https://', '')
.split('.')[0];
// For some reason giving this a type causes an issue where setRef // For some reason giving this a type causes an issue where setRef
// cant be used with ref={} prop...not sure why. SetStateAction nonsense. // cant be used with ref={} prop...not sure why. SetStateAction nonsense.
@ -31,24 +27,30 @@ export function VoterBubbleGraph(props: IVoterBubbleGraph) {
d.name.slice(d.name.length - 3, d.name.length), d.name.slice(d.name.length - 3, d.name.length),
})); }));
//console.log('Data', limitedData); //console.log('Data', limitedData);
const format = d3.format(',d');
const color = d3
.scaleOrdinal()
.domain([VoteType.Undecided, VoteType.Yes, VoteType.No])
.range(['grey', 'green', 'red']);
const pack = (data: Array<VoterDisplayData>) => {
return d3
.pack()
.size([width - 2, height - 2])
.padding(3)(
//@ts-ignore
d3.hierarchy({ children: data }).sum(d => (d.value ? d.value : 0)),
);
};
useEffect(() => { useEffect(() => {
if (ref) { if (ref) {
const subdomain = endpoint
.replace('http://', '')
.replace('https://', '')
.split('.')[0];
const format = d3.format(',d');
const color = d3
.scaleOrdinal()
.domain([VoteType.Undecided, VoteType.Yes, VoteType.No])
.range(['grey', 'green', 'red']);
const pack = (data: Array<VoterDisplayData>) => {
return d3
.pack()
.size([width - 2, height - 2])
.padding(3)(
//@ts-ignore
d3.hierarchy({ children: data }).sum(d => (d.value ? d.value : 0)),
);
};
ref.innerHTML = ''; ref.innerHTML = '';
const root = pack(limitedData); const root = pack(limitedData);
// console.log('Re-rendered'); // console.log('Re-rendered');
@ -127,7 +129,7 @@ export function VoterBubbleGraph(props: IVoterBubbleGraph) {
}${format(d.value)}`, }${format(d.value)}`,
); );
} }
}, [ref, limitedData, height, width]); }, [ref, limitedData, height, width, endpoint]);
return ( return (
<div <div

View File

@ -2,11 +2,10 @@ import { ParsedAccount } from '@oyster/common';
import { Button, Col, Modal, Row } from 'antd'; import { Button, Col, Modal, Row } from 'antd';
import React from 'react'; import React from 'react';
import { import {
TimelockConfig, Proposal,
TimelockSet, ProposalState,
TimelockState, ProposalStateStatus,
TimelockStateStatus, } from '../../models/governance';
} from '../../models/timelock';
import { LABELS } from '../../constants'; import { LABELS } from '../../constants';
import { withdrawVotingTokens } from '../../actions/withdrawVotingTokens'; import { withdrawVotingTokens } from '../../actions/withdrawVotingTokens';
import { contexts, hooks } from '@oyster/common'; import { contexts, hooks } from '@oyster/common';
@ -21,9 +20,8 @@ export function WithdrawVote({
proposal, proposal,
state, state,
}: { }: {
proposal: ParsedAccount<TimelockSet>; proposal: ParsedAccount<Proposal>;
state: ParsedAccount<TimelockState>; state: ParsedAccount<ProposalState>;
timelockConfig: ParsedAccount<TimelockConfig>;
}) { }) {
const wallet = useWallet(); const wallet = useWallet();
const connection = useConnection(); const connection = useConnection();
@ -41,13 +39,13 @@ export function WithdrawVote({
const eligibleToView = const eligibleToView =
votingTokens > 0 && votingTokens > 0 &&
(state.info.status === TimelockStateStatus.Voting || (state.info.status === ProposalStateStatus.Voting ||
state.info.status === TimelockStateStatus.Completed || state.info.status === ProposalStateStatus.Completed ||
state.info.status === TimelockStateStatus.Executing || state.info.status === ProposalStateStatus.Executing ||
state.info.status === TimelockStateStatus.Defeated); state.info.status === ProposalStateStatus.Defeated);
const [btnLabel, title, msg, action] = const [btnLabel, title, msg, action] =
state.info.status === TimelockStateStatus.Voting state.info.status === ProposalStateStatus.Voting
? [ ? [
LABELS.WITHDRAW_VOTE, LABELS.WITHDRAW_VOTE,
LABELS.WITHDRAW_YOUR_VOTE_QUESTION, LABELS.WITHDRAW_YOUR_VOTE_QUESTION,

View File

@ -99,7 +99,10 @@ export const LABELS = {
TOKENS_REFUNDED: 'Your voting tokens have been refunded', TOKENS_REFUNDED: 'Your voting tokens have been refunded',
REGISTER_GOVERNANCE: 'Register', REGISTER_GOVERNANCE: 'Register',
PROGRAM: 'Program ID',
PROGRAM_ID: 'Program ID',
INSTRUCTION: 'Instruction',
GOVERNANCE: 'Governance Token Holders', GOVERNANCE: 'Governance Token Holders',
COUNCIL: 'The Council', COUNCIL: 'The Council',
GOVERNANCE_MINT: 'Governance Mint ID', GOVERNANCE_MINT: 'Governance Mint ID',

View File

@ -15,26 +15,26 @@ import {
cache, cache,
} from '@oyster/common'; } from '@oyster/common';
import { import {
CustomSingleSignerTimelockTransactionLayout, CustomSingleSignerTransactionLayout,
CustomSingleSignerTimelockTransactionParser, CustomSingleSignerTransactionParser,
TimelockConfig, Governance,
TimelockConfigLayout, GovernanceLayout,
TimelockConfigParser, GovernanceParser,
TimelockSet, Proposal,
TimelockState, ProposalState,
TimelockSetLayout, ProposalLayout,
TimelockSetParser, ProposalParser,
TimelockTransaction, GovernanceTransaction,
TimelockStateParser, ProposalStateParser,
TimelockStateLayout, ProposalStateLayout,
CustomSingleSignerTimelockTransaction, CustomSingleSignerTransaction,
} from '../models/timelock'; } from '../models/governance';
export interface ProposalsContextState { export interface ProposalsContextState {
proposals: Record<string, ParsedAccount<TimelockSet>>; proposals: Record<string, ParsedAccount<Proposal>>;
transactions: Record<string, ParsedAccount<TimelockTransaction>>; transactions: Record<string, ParsedAccount<GovernanceTransaction>>;
states: Record<string, ParsedAccount<TimelockState>>; states: Record<string, ParsedAccount<ProposalState>>;
configs: Record<string, ParsedAccount<TimelockConfig>>; configs: Record<string, ParsedAccount<Governance>>;
} }
export const ProposalsContext = React.createContext<ProposalsContextState | null>( export const ProposalsContext = React.createContext<ProposalsContextState | null>(
@ -82,49 +82,47 @@ function useSetupProposalsCache({
setStates: React.Dispatch<React.SetStateAction<{}>>; setStates: React.Dispatch<React.SetStateAction<{}>>;
setConfigs: React.Dispatch<React.SetStateAction<{}>>; setConfigs: React.Dispatch<React.SetStateAction<{}>>;
}) { }) {
const PROGRAM_IDS = utils.programIds();
useEffect(() => { useEffect(() => {
const PROGRAM_IDS = utils.programIds();
const query = async () => { const query = async () => {
const programAccounts = await connection.getProgramAccounts( const programAccounts = await connection.getProgramAccounts(
PROGRAM_IDS.timelock.programId, PROGRAM_IDS.governance.programId,
); );
return programAccounts; return programAccounts;
}; };
Promise.all([query()]).then((all: PublicKeyAndAccount<Buffer>[][]) => { Promise.all([query()]).then((all: PublicKeyAndAccount<Buffer>[][]) => {
const newProposals: Record<string, ParsedAccount<TimelockSet>> = {}; const newProposals: Record<string, ParsedAccount<Proposal>> = {};
const newTransactions: Record< const newTransactions: Record<
string, string,
ParsedAccount<TimelockTransaction> ParsedAccount<GovernanceTransaction>
> = {}; > = {};
const newStates: Record<string, ParsedAccount<TimelockState>> = {}; const newStates: Record<string, ParsedAccount<ProposalState>> = {};
const newConfigs: Record<string, ParsedAccount<TimelockConfig>> = {}; const newConfigs: Record<string, ParsedAccount<Governance>> = {};
all[0].forEach(a => { all[0].forEach(a => {
let cached; let cached;
switch (a.account.data.length) { switch (a.account.data.length) {
case TimelockSetLayout.span: case ProposalLayout.span:
cache.add(a.pubkey, a.account, TimelockSetParser); cache.add(a.pubkey, a.account, ProposalParser);
cached = cache.get(a.pubkey) as ParsedAccount<TimelockSet>; cached = cache.get(a.pubkey) as ParsedAccount<Proposal>;
newProposals[a.pubkey.toBase58()] = cached; newProposals[a.pubkey.toBase58()] = cached;
break; break;
case CustomSingleSignerTimelockTransactionLayout.span: case CustomSingleSignerTransactionLayout.span:
cache.add( cache.add(a.pubkey, a.account, CustomSingleSignerTransactionParser);
cached = cache.get(
a.pubkey, a.pubkey,
a.account, ) as ParsedAccount<GovernanceTransaction>;
CustomSingleSignerTimelockTransactionParser,
);
cached = cache.get(a.pubkey) as ParsedAccount<TimelockTransaction>;
newTransactions[a.pubkey.toBase58()] = cached; newTransactions[a.pubkey.toBase58()] = cached;
break; break;
case TimelockConfigLayout.span: case GovernanceLayout.span:
cache.add(a.pubkey, a.account, TimelockConfigParser); cache.add(a.pubkey, a.account, GovernanceParser);
cached = cache.get(a.pubkey) as ParsedAccount<TimelockConfig>; cached = cache.get(a.pubkey) as ParsedAccount<Governance>;
newConfigs[a.pubkey.toBase58()] = cached; newConfigs[a.pubkey.toBase58()] = cached;
break; break;
case TimelockStateLayout.span: case ProposalStateLayout.span:
cache.add(a.pubkey, a.account, TimelockStateParser); cache.add(a.pubkey, a.account, ProposalStateParser);
cached = cache.get(a.pubkey) as ParsedAccount<TimelockState>; cached = cache.get(a.pubkey) as ParsedAccount<ProposalState>;
newStates[a.pubkey.toBase58()] = cached; newStates[a.pubkey.toBase58()] = cached;
break; break;
} }
@ -136,46 +134,42 @@ function useSetupProposalsCache({
setConfigs(newConfigs); setConfigs(newConfigs);
}); });
const subID = connection.onProgramAccountChange( const subID = connection.onProgramAccountChange(
PROGRAM_IDS.timelock.programId, PROGRAM_IDS.governance.programId,
async (info: KeyedAccountInfo) => { async (info: KeyedAccountInfo) => {
const pubkey = typeof info.accountId === 'string' ? const pubkey = typeof info.accountId === 'string' ?
new PublicKey((info.accountId as unknown) as string) : new PublicKey((info.accountId as unknown) as string) :
info.accountId; info.accountId;
[ [
[TimelockSetLayout.span, TimelockSetParser, setProposals], [ProposalLayout.span, ProposalParser, setProposals],
[ [
CustomSingleSignerTimelockTransactionLayout.span, CustomSingleSignerTransactionLayout.span,
CustomSingleSignerTimelockTransactionParser, CustomSingleSignerTransactionParser,
setTransactions, setTransactions,
], ],
[TimelockStateLayout.span, TimelockStateParser, setStates], [ProposalStateLayout.span, ProposalStateParser, setStates],
[TimelockConfigLayout.span, TimelockConfigParser, setConfigs], [GovernanceLayout.span, GovernanceParser, setConfigs],
].forEach(arr => { ].forEach(arr => {
const [span, parser, setter] = arr; const [span, parser, setter] = arr;
if (info.accountInfo.data.length === span) { if (info.accountInfo.data.length === span) {
cache.add(info.accountId, info.accountInfo, parser); cache.add(info.accountId, info.accountInfo, parser);
let cached: any; let cached: any;
switch (info.accountInfo.data.length) { switch (info.accountInfo.data.length) {
case TimelockSetLayout.span: case ProposalLayout.span:
cached = cache.get( cached = cache.get(info.accountId) as ParsedAccount<Proposal>;
pubkey,
) as ParsedAccount<TimelockSet>;
break; break;
case CustomSingleSignerTimelockTransactionLayout.span: case CustomSingleSignerTransactionLayout.span:
cached = cache.get( cached = cache.get(
pubkey, info.accountId,
) as ParsedAccount<CustomSingleSignerTimelockTransaction>; ) as ParsedAccount<CustomSingleSignerTransaction>;
break; break;
case TimelockConfigLayout.span: case GovernanceLayout.span:
cached = cache.get( cached = cache.get(info.accountId) as ParsedAccount<Governance>;
pubkey,
) as ParsedAccount<TimelockConfig>;
break; break;
case TimelockStateLayout.span: case ProposalStateLayout.span:
cached = cache.get( cached = cache.get(
pubkey, info.accountId,
) as ParsedAccount<TimelockState>; ) as ParsedAccount<ProposalState>;
break; break;
} }
setter((obj: any) => ({ setter((obj: any) => ({
@ -190,7 +184,7 @@ function useSetupProposalsCache({
return () => { return () => {
connection.removeProgramAccountChangeListener(subID); connection.removeProgramAccountChangeListener(subID);
}; };
}, [connection, PROGRAM_IDS.timelock.programId.toBase58()]); }, [connection]); //eslint-disable-line
} }
export const useProposals = () => { export const useProposals = () => {
const context = useContext(ProposalsContext); const context = useContext(ProposalsContext);

View File

@ -11,7 +11,7 @@ import {
GovernanceVotingRecord, GovernanceVotingRecord,
GovernanceVotingRecordLayout, GovernanceVotingRecordLayout,
GovernanceVotingRecordParser, GovernanceVotingRecordParser,
} from '../models/timelock'; } from '../models/governance';
import { getGovernanceVotingRecords } from '../utils/lookups'; import { getGovernanceVotingRecords } from '../utils/lookups';
export function useVotingRecords(proposal: PublicKey) { export function useVotingRecords(proposal: PublicKey) {
@ -31,9 +31,9 @@ export function useVotingRecords(proposal: PublicKey) {
const records = await getGovernanceVotingRecords(proposal, endpoint); const records = await getGovernanceVotingRecords(proposal, endpoint);
setVotingRecords(records); setVotingRecords(records);
const { timelock } = utils.programIds(); const { governance } = utils.programIds();
return connection.onProgramAccountChange(timelock.programId, info => { return connection.onProgramAccountChange(governance.programId, info => {
if ( if (
info.accountInfo.data.length === GovernanceVotingRecordLayout.span info.accountInfo.data.length === GovernanceVotingRecordLayout.span
) { ) {

View File

@ -1,6 +1,6 @@
{ {
"name": "Oyster Proposals", "name": "Oyster Governance",
"short_name": "Oyster Proposals", "short_name": "Oyster Governance",
"display": "standalone", "display": "standalone",
"start_url": "./", "start_url": "./",
"theme_color": "#002140", "theme_color": "#002140",

View File

@ -5,32 +5,32 @@ import * as Layout from '../utils/layout';
import * as BufferLayout from 'buffer-layout'; import * as BufferLayout from 'buffer-layout';
import { import {
INSTRUCTION_LIMIT, INSTRUCTION_LIMIT,
TimelockInstruction, GovernanceInstruction,
TRANSACTION_SLOTS, MAX_TRANSACTIONS,
} from './timelock'; } from './governance';
import BN from 'bn.js'; import BN from 'bn.js';
/// [Requires Signatory token] /// [Requires Signatory token]
/// Adds a Transaction to the Timelock Set. Max of 10 of any Transaction type. More than 10 will throw error. /// Adds a Transaction to the Proposal. Max of 10 of any Transaction type. More than 10 will throw error.
/// Creates a PDA using your authority to be used to later execute the instruction. /// Creates a PDA using your authority to be used to later execute the instruction.
/// This transaction needs to contain authority to execute the program. /// This transaction needs to contain authority to execute the program.
/// ///
/// 0. `[writable]` Uninitialized Timelock Transaction account. /// 0. `[writable]` Uninitialized Proposal Transaction account.
/// 1. `[writable]` Timelock set account. /// 1. `[writable]` Proposal account.
/// 2. `[writable]` Signatory account /// 2. `[writable]` Signatory account
/// 3. `[writable]` Signatory validation account. /// 3. `[writable]` Signatory validation account.
/// 4. `[]` Timelock Set account. /// 4. `[]` Proposal account.
/// 5. `[]` Timelock Config account. /// 5. `[]` Governance account.
/// 6. `[]` Transfer authority /// 6. `[]` Transfer authority
/// 7. `[]` Timelock mint authority /// 7. `[]` Governance mint authority
/// 8. `[]` Token program account. /// 8. `[]` Token program account.
export const addCustomSingleSignerTransactionInstruction = ( export const addCustomSingleSignerTransactionInstruction = (
timelockTransactionAccount: PublicKey, proposalTransactionAccount: PublicKey,
timelockStateAccount: PublicKey, proposalStateAccount: PublicKey,
signatoryAccount: PublicKey, signatoryAccount: PublicKey,
signatoryValidationAccount: PublicKey, signatoryValidationAccount: PublicKey,
timelockSetAccount: PublicKey, proposalAccount: PublicKey,
timelockConfigAccount: PublicKey, governanceAccount: PublicKey,
transferAuthority: PublicKey, transferAuthority: PublicKey,
authority: PublicKey, authority: PublicKey,
slot: string, slot: string,
@ -53,9 +53,9 @@ export const addCustomSingleSignerTransactionInstruction = (
); );
} }
if (position > TRANSACTION_SLOTS) { if (position > MAX_TRANSACTIONS) {
throw new Error( throw new Error(
'Position is more than ' + TRANSACTION_SLOTS + ' which is not allowed.', 'Position is more than ' + MAX_TRANSACTIONS + ' which is not allowed.',
); );
} }
@ -75,7 +75,7 @@ export const addCustomSingleSignerTransactionInstruction = (
dataLayout.encode( dataLayout.encode(
{ {
instruction: TimelockInstruction.AddCustomSingleSignerTransaction, instruction: GovernanceInstruction.AddCustomSingleSignerTransaction,
slot: new BN(slot), slot: new BN(slot),
instructions: instructionAsBytes, instructions: instructionAsBytes,
position: position, position: position,
@ -85,12 +85,12 @@ export const addCustomSingleSignerTransactionInstruction = (
); );
const keys = [ const keys = [
{ pubkey: timelockTransactionAccount, isSigner: true, isWritable: true }, { pubkey: proposalTransactionAccount, isSigner: true, isWritable: true },
{ pubkey: timelockStateAccount, isSigner: false, isWritable: true }, { pubkey: proposalStateAccount, isSigner: false, isWritable: true },
{ pubkey: signatoryAccount, isSigner: false, isWritable: true }, { pubkey: signatoryAccount, isSigner: false, isWritable: true },
{ pubkey: signatoryValidationAccount, isSigner: false, isWritable: true }, { pubkey: signatoryValidationAccount, isSigner: false, isWritable: true },
{ pubkey: timelockSetAccount, isSigner: false, isWritable: false }, { pubkey: proposalAccount, isSigner: false, isWritable: false },
{ pubkey: timelockConfigAccount, isSigner: false, isWritable: false }, { pubkey: governanceAccount, isSigner: false, isWritable: false },
{ pubkey: transferAuthority, isSigner: true, isWritable: false }, { pubkey: transferAuthority, isSigner: true, isWritable: false },
{ pubkey: authority, isSigner: false, isWritable: false }, { pubkey: authority, isSigner: false, isWritable: false },
{ pubkey: PROGRAM_IDS.token, isSigner: false, isWritable: false }, { pubkey: PROGRAM_IDS.token, isSigner: false, isWritable: false },
@ -98,7 +98,7 @@ export const addCustomSingleSignerTransactionInstruction = (
return new TransactionInstruction({ return new TransactionInstruction({
keys, keys,
programId: PROGRAM_IDS.timelock.programId, programId: PROGRAM_IDS.governance.programId,
data, data,
}); });
}; };

View File

@ -1,10 +1,10 @@
import { PublicKey, TransactionInstruction } from '@solana/web3.js'; import { PublicKey, TransactionInstruction } from '@solana/web3.js';
import { utils } from '@oyster/common'; import { utils } from '@oyster/common';
import * as BufferLayout from 'buffer-layout'; import * as BufferLayout from 'buffer-layout';
import { TimelockInstruction } from './timelock'; import { GovernanceInstruction } from './governance';
/// [Requires Admin token] /// [Requires Admin token]
/// Adds a signatory to the Timelock which means that this timelock can't leave Draft state until yet another signatory burns /// Adds a signatory to the Proposal which means that this Proposal can't leave Draft state until yet another signatory burns
/// their signatory token indicating they are satisfied with the instruction queue. They'll receive an signatory token /// their signatory token indicating they are satisfied with the instruction queue. They'll receive an signatory token
/// as a result of this call that they can burn later. /// as a result of this call that they can burn later.
/// ///
@ -12,18 +12,18 @@ import { TimelockInstruction } from './timelock';
/// 1. `[writable]` Initialized Signatory mint account. /// 1. `[writable]` Initialized Signatory mint account.
/// 2. `[writable]` Admin account. /// 2. `[writable]` Admin account.
/// 3. `[writable]` Admin validation account. /// 3. `[writable]` Admin validation account.
/// 4. `[writable]` Timelock set account. /// 4. `[writable]` Proposal account.
/// 5. `[]` Timelock set account. /// 5. `[]` Proposal account.
/// 6. `[]` Transfer authority /// 6. `[]` Transfer authority
/// 7. `[]` Timelock program mint authority /// 7. `[]` Governance program mint authority
/// 8. '[]` Token program id. /// 8. '[]` Token program id.
export const addSignerInstruction = ( export const addSignerInstruction = (
signatoryAccount: PublicKey, signatoryAccount: PublicKey,
signatoryMintAccount: PublicKey, signatoryMintAccount: PublicKey,
adminAccount: PublicKey, adminAccount: PublicKey,
adminValidationAccount: PublicKey, adminValidationAccount: PublicKey,
timelockStateAccount: PublicKey, proposalStateAccount: PublicKey,
timelockSetAccount: PublicKey, proposalAccount: PublicKey,
transferAuthority: PublicKey, transferAuthority: PublicKey,
mintAuthority: PublicKey, mintAuthority: PublicKey,
): TransactionInstruction => { ): TransactionInstruction => {
@ -35,7 +35,7 @@ export const addSignerInstruction = (
dataLayout.encode( dataLayout.encode(
{ {
instruction: TimelockInstruction.AddSigner, instruction: GovernanceInstruction.AddSigner,
}, },
data, data,
); );
@ -45,15 +45,15 @@ export const addSignerInstruction = (
{ pubkey: signatoryMintAccount, isSigner: false, isWritable: true }, { pubkey: signatoryMintAccount, isSigner: false, isWritable: true },
{ pubkey: adminAccount, isSigner: false, isWritable: true }, { pubkey: adminAccount, isSigner: false, isWritable: true },
{ pubkey: adminValidationAccount, isSigner: false, isWritable: true }, { pubkey: adminValidationAccount, isSigner: false, isWritable: true },
{ pubkey: timelockStateAccount, isSigner: false, isWritable: true }, { pubkey: proposalStateAccount, isSigner: false, isWritable: true },
{ pubkey: timelockSetAccount, isSigner: false, isWritable: false }, { pubkey: proposalAccount, isSigner: false, isWritable: false },
{ pubkey: transferAuthority, isSigner: true, isWritable: false }, { pubkey: transferAuthority, isSigner: true, isWritable: false },
{ pubkey: mintAuthority, isSigner: false, isWritable: false }, { pubkey: mintAuthority, isSigner: false, isWritable: false },
{ pubkey: PROGRAM_IDS.token, isSigner: false, isWritable: false }, { pubkey: PROGRAM_IDS.token, isSigner: false, isWritable: false },
]; ];
return new TransactionInstruction({ return new TransactionInstruction({
keys, keys,
programId: PROGRAM_IDS.timelock.programId, programId: PROGRAM_IDS.governance.programId,
data, data,
}); });
}; };

View File

@ -1,18 +1,18 @@
import { PublicKey, TransactionInstruction } from '@solana/web3.js'; import { PublicKey, TransactionInstruction } from '@solana/web3.js';
import { utils } from '@oyster/common'; import { utils } from '@oyster/common';
import * as BufferLayout from 'buffer-layout'; import * as BufferLayout from 'buffer-layout';
import { TimelockInstruction } from './timelock'; import { GovernanceInstruction } from './governance';
/// 0. `[]` Timelock config key. Needs to be set with pubkey set to PDA with seeds of the /// 0. `[]` Governance account. The account pubkey needs to be set to PDA with the following seeds:
/// program account key, governance mint key, council mint key, and timelock program account key. /// 1) 'governance' const prefix, 2) Governed Program account key
/// 1. `[]` Program account to tie this config to. /// 1. `[]` Account of the Program governed by this Governance account
/// 2. `[]` Governance mint to tie this config to /// 2. `[writable]` Program Data account of the Program governed by this Governance account
/// 3. `[]` Payer /// 3. `[signer]` Current Upgrade Authority account of the Program governed by this Governance account
/// 4. `[]` Timelock program pub key. /// 4. `[signer]` Payer
/// 5. `[]` System account. /// 5. `[]` System account.
/// 6. `[]` Council mint [optional] to tie this config to [Optional] /// 6. `[]` bpf_upgrade_loader account.
export const createEmptyTimelockConfigInstruction = ( export const createEmptyGovernanceInstruction = (
timelockConfigAccount: PublicKey, governanceAccount: PublicKey,
programAccount: PublicKey, programAccount: PublicKey,
programDataAccount: PublicKey, programDataAccount: PublicKey,
programUpgradeAuthority: PublicKey, programUpgradeAuthority: PublicKey,
@ -28,17 +28,17 @@ export const createEmptyTimelockConfigInstruction = (
dataLayout.encode( dataLayout.encode(
{ {
instruction: TimelockInstruction.CreateEmptyTimelockConfig, instruction: GovernanceInstruction.CreateEmptyGovernance,
}, },
data, data,
); );
const keys = [ const keys = [
{ pubkey: timelockConfigAccount, isSigner: false, isWritable: false }, { pubkey: governanceAccount, isSigner: false, isWritable: false },
{ pubkey: programAccount, isSigner: false, isWritable: false }, { pubkey: programAccount, isSigner: false, isWritable: false },
{ pubkey: programDataAccount, isSigner: false, isWritable: true }, { pubkey: programDataAccount, isSigner: false, isWritable: true },
{ pubkey: programUpgradeAuthority, isSigner: true, isWritable: false }, { pubkey: programUpgradeAuthority, isSigner: true, isWritable: false },
{ pubkey: governanceMint, isSigner: false, isWritable: false },
{ pubkey: payer, isSigner: true, isWritable: false }, { pubkey: payer, isSigner: true, isWritable: false },
{ pubkey: PROGRAM_IDS.system, isSigner: false, isWritable: false }, { pubkey: PROGRAM_IDS.system, isSigner: false, isWritable: false },
{ {
@ -48,13 +48,9 @@ export const createEmptyTimelockConfigInstruction = (
}, },
]; ];
if (councilMint) {
keys.push({ pubkey: councilMint, isSigner: false, isWritable: false });
}
return new TransactionInstruction({ return new TransactionInstruction({
keys, keys,
programId: PROGRAM_IDS.timelock.programId, programId: PROGRAM_IDS.governance.programId,
data, data,
}); });
}; };

View File

@ -1,14 +1,14 @@
import { PublicKey, TransactionInstruction } from '@solana/web3.js'; import { PublicKey, TransactionInstruction } from '@solana/web3.js';
import { utils } from '@oyster/common'; import { utils } from '@oyster/common';
import * as BufferLayout from 'buffer-layout'; import * as BufferLayout from 'buffer-layout';
import { TimelockInstruction } from './timelock'; import { GovernanceInstruction } from './governance';
/// 0. `[]` Governance voting record key. Needs to be set with pubkey set to PDA with seeds of the /// 0. `[]` Governance voting record key. Needs to be set with pubkey set to PDA with seeds of the
/// program account key, proposal key, your voting account key. /// program account key, proposal key, your voting account key.
/// 1. `[]` Proposal key /// 1. `[]` Proposal key
/// 2. `[]` Your voting account /// 2. `[]` Your voting account
/// 3. `[]` Payer /// 3. `[]` Payer
/// 4. `[]` Timelock program pub key. /// 4. `[]` Governance program pub key
/// 5. `[]` System account. /// 5. `[]` System account.
export const createEmptyGovernanceVotingRecordInstruction = ( export const createEmptyGovernanceVotingRecordInstruction = (
governanceRecordAccount: PublicKey, governanceRecordAccount: PublicKey,
@ -24,7 +24,7 @@ export const createEmptyGovernanceVotingRecordInstruction = (
dataLayout.encode( dataLayout.encode(
{ {
instruction: TimelockInstruction.CreateGovernanceVotingRecord, instruction: GovernanceInstruction.CreateGovernanceVotingRecord,
}, },
data, data,
); );
@ -35,7 +35,7 @@ export const createEmptyGovernanceVotingRecordInstruction = (
{ pubkey: votingAccount, isSigner: false, isWritable: false }, { pubkey: votingAccount, isSigner: false, isWritable: false },
{ pubkey: payer, isSigner: true, isWritable: false }, { pubkey: payer, isSigner: true, isWritable: false },
{ {
pubkey: PROGRAM_IDS.timelock.programId, pubkey: PROGRAM_IDS.governance.programId,
isSigner: false, isSigner: false,
isWritable: false, isWritable: false,
}, },
@ -43,7 +43,7 @@ export const createEmptyGovernanceVotingRecordInstruction = (
]; ];
return new TransactionInstruction({ return new TransactionInstruction({
keys, keys,
programId: PROGRAM_IDS.timelock.programId, programId: PROGRAM_IDS.governance.programId,
data, data,
}); });
}; };

View File

@ -3,22 +3,22 @@ import { utils } from '@oyster/common';
import * as Layout from '../utils/layout'; import * as Layout from '../utils/layout';
import * as BufferLayout from 'buffer-layout'; import * as BufferLayout from 'buffer-layout';
import { TimelockInstruction } from './timelock'; import { GovernanceInstruction } from './governance';
import BN from 'bn.js'; import BN from 'bn.js';
/// [Requires tokens of the Governance mint or Council mint depending on type of TimelockSet] /// [Requires tokens of the Governance mint or Council mint depending on type of Proposal]
/// Deposits voting tokens to be used during the voting process in a timelock. /// Deposits voting tokens to be used during the voting process in a Proposal.
/// These tokens are removed from your account and can be returned by withdrawing /// These tokens are removed from your account and can be returned by withdrawing
/// them from the timelock (but then you will miss the vote.) /// them from the Proposal (but then you will miss the vote.)
/// ///
/// 0. `[writable]` Governance voting record account. See Vote docs for more detail. /// 0. `[writable]` Governance voting record account. See Vote docs for more detail.
/// 1. `[writable]` Initialized Voting account to hold your received voting tokens. /// 1. `[writable]` Initialized Voting account to hold your received voting tokens.
/// 2. `[writable]` User token account to deposit tokens from. /// 2. `[writable]` User token account to deposit tokens from.
/// 3. `[writable]` Source holding account for timelock that will accept the tokens in escrow. /// 3. `[writable]` Source holding account for Proposal that will accept the tokens in escrow.
/// 4. `[writable]` Voting mint account. /// 4. `[writable]` Voting mint account.
/// 5. `[]` Timelock set account. /// 5. `[]` Proposal account.
/// 6. `[]` Transfer authority /// 6. `[]` Transfer authority
/// 7. `[]` Timelock program mint authority /// 7. `[]` Governance program mint authority (pda with seed of Proposal key)
/// 8. `[]` Token program account. /// 8. `[]` Token program account.
export const depositSourceTokensInstruction = ( export const depositSourceTokensInstruction = (
governanceVotingRecord: PublicKey, governanceVotingRecord: PublicKey,
@ -26,7 +26,7 @@ export const depositSourceTokensInstruction = (
sourceAccount: PublicKey, sourceAccount: PublicKey,
sourceHoldingAccount: PublicKey, sourceHoldingAccount: PublicKey,
votingMint: PublicKey, votingMint: PublicKey,
timelockSetAccount: PublicKey, proposalAccount: PublicKey,
transferAuthority: PublicKey, transferAuthority: PublicKey,
mintAuthority: PublicKey, mintAuthority: PublicKey,
votingTokenAmount: number, votingTokenAmount: number,
@ -42,7 +42,7 @@ export const depositSourceTokensInstruction = (
dataLayout.encode( dataLayout.encode(
{ {
instruction: TimelockInstruction.DepositGovernanceTokens, instruction: GovernanceInstruction.DepositGovernanceTokens,
votingTokenAmount: new BN(votingTokenAmount), votingTokenAmount: new BN(votingTokenAmount),
}, },
data, data,
@ -54,7 +54,7 @@ export const depositSourceTokensInstruction = (
{ pubkey: sourceAccount, isSigner: false, isWritable: true }, { pubkey: sourceAccount, isSigner: false, isWritable: true },
{ pubkey: sourceHoldingAccount, isSigner: false, isWritable: true }, { pubkey: sourceHoldingAccount, isSigner: false, isWritable: true },
{ pubkey: votingMint, isSigner: false, isWritable: true }, { pubkey: votingMint, isSigner: false, isWritable: true },
{ pubkey: timelockSetAccount, isSigner: false, isWritable: false }, { pubkey: proposalAccount, isSigner: false, isWritable: false },
{ pubkey: transferAuthority, isSigner: true, isWritable: false }, { pubkey: transferAuthority, isSigner: true, isWritable: false },
{ pubkey: mintAuthority, isSigner: false, isWritable: false }, { pubkey: mintAuthority, isSigner: false, isWritable: false },
{ pubkey: PROGRAM_IDS.token, isSigner: false, isWritable: false }, { pubkey: PROGRAM_IDS.token, isSigner: false, isWritable: false },
@ -62,7 +62,7 @@ export const depositSourceTokensInstruction = (
return new TransactionInstruction({ return new TransactionInstruction({
keys, keys,
programId: PROGRAM_IDS.timelock.programId, programId: PROGRAM_IDS.governance.programId,
data, data,
}); });
}; };

View File

@ -5,23 +5,24 @@ import {
} from '@solana/web3.js'; } from '@solana/web3.js';
import { utils } from '@oyster/common'; import { utils } from '@oyster/common';
import * as BufferLayout from 'buffer-layout'; import * as BufferLayout from 'buffer-layout';
import { TimelockInstruction } from './timelock'; import { GovernanceInstruction } from './governance';
/// Executes a command in the timelock set. /// Executes a command in the Proposal
/// ///
/// 0. `[writable]` Transaction account you wish to execute. /// 0. `[writable]` Transaction account you wish to execute.
/// 1. `[writable]` Timelock state account. /// 1. `[writable]` Proposal state account.
/// 2. `[]` Program being invoked account /// 2. `[]` Program being invoked account
/// 3. `[]` Timelock set account. /// 3. `[]` Proposal account.
/// 4. `[]` Timelock config /// 4. `[]` Governance account
/// 5. `[]` Clock sysvar. /// 5. `[]` Governance program account pub key.
/// 6. `[]` Clock sysvar.
/// 7+ Any extra accounts that are part of the instruction, in order /// 7+ Any extra accounts that are part of the instruction, in order
export const executeInstruction = ( export const executeInstruction = (
transactionAccount: PublicKey, transactionAccount: PublicKey,
timelockStateAccount: PublicKey, proposalStateAccount: PublicKey,
timelockSetAccount: PublicKey, proposalAccount: PublicKey,
programBeingInvokedAccount: PublicKey, programBeingInvokedAccount: PublicKey,
timelockConfig: PublicKey, governance: PublicKey,
accountInfos: { pubkey: PublicKey; isWritable: boolean; isSigner: boolean }[], accountInfos: { pubkey: PublicKey; isWritable: boolean; isSigner: boolean }[],
): TransactionInstruction => { ): TransactionInstruction => {
const PROGRAM_IDS = utils.programIds(); const PROGRAM_IDS = utils.programIds();
@ -35,7 +36,7 @@ export const executeInstruction = (
dataLayout.encode( dataLayout.encode(
{ {
instruction: TimelockInstruction.Execute, instruction: GovernanceInstruction.Execute,
numberOfExtraAccounts: accountInfos.length, numberOfExtraAccounts: accountInfos.length,
}, },
data, data,
@ -44,16 +45,16 @@ export const executeInstruction = (
const keys = [ const keys = [
// just a note this were all set to writable true...come back and check on this // just a note this were all set to writable true...come back and check on this
{ pubkey: transactionAccount, isSigner: false, isWritable: true }, { pubkey: transactionAccount, isSigner: false, isWritable: true },
{ pubkey: timelockStateAccount, isSigner: false, isWritable: true }, { pubkey: proposalStateAccount, isSigner: false, isWritable: true },
{ pubkey: programBeingInvokedAccount, isSigner: false, isWritable: false }, { pubkey: programBeingInvokedAccount, isSigner: false, isWritable: false },
{ pubkey: timelockSetAccount, isSigner: false, isWritable: false }, { pubkey: proposalAccount, isSigner: false, isWritable: false },
{ pubkey: timelockConfig, isSigner: false, isWritable: false }, { pubkey: governance, isSigner: false, isWritable: false },
{ pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false }, { pubkey: SYSVAR_CLOCK_PUBKEY, isSigner: false, isWritable: false },
...accountInfos, ...accountInfos,
]; ];
return new TransactionInstruction({ return new TransactionInstruction({
keys, keys,
programId: PROGRAM_IDS.timelock.programId, programId: PROGRAM_IDS.governance.programId,
data, data,
}); });
}; };

View File

@ -8,25 +8,25 @@ export const DESC_SIZE = 200;
export const NAME_SIZE = 32; export const NAME_SIZE = 32;
export const CONFIG_NAME_LENGTH = 32; export const CONFIG_NAME_LENGTH = 32;
export const INSTRUCTION_LIMIT = 450; export const INSTRUCTION_LIMIT = 450;
export const TRANSACTION_SLOTS = 5; export const MAX_TRANSACTIONS = 5;
export const TEMP_FILE_TXN_SIZE = 1000; export const TEMP_FILE_TXN_SIZE = 1000;
/// Seed for proposal authority /// Seed for proposal authority
export const GOVERNANCE_AUTHORITY_SEED = 'governance'; export const GOVERNANCE_AUTHORITY_SEED = 'governance';
export enum TimelockInstruction { export enum GovernanceInstruction {
InitTimelockSet = 1, InitProposal = 1,
AddSigner = 2, AddSigner = 2,
RemoveSigner = 3, RemoveSigner = 3,
AddCustomSingleSignerTransaction = 4, AddCustomSingleSignerTransaction = 4,
Sign = 8, Sign = 8,
Vote = 9, Vote = 9,
InitTimelockConfig = 10, InitGovernance = 10,
Execute = 11, Execute = 11,
DepositGovernanceTokens = 12, DepositGovernanceTokens = 12,
WithdrawVotingTokens = 13, WithdrawVotingTokens = 13,
CreateEmptyTimelockConfig = 14, CreateEmptyGovernance = 14,
CreateGovernanceVotingRecord = 15, CreateGovernanceVotingRecord = 15,
} }
@ -58,15 +58,15 @@ export const GovernanceVotingRecordLayout: typeof BufferLayout.Structure = Buffe
], ],
); );
export interface TimelockConfig { export interface Governance {
/// Account type /// Account type
accountType: GovernanceAccountType; accountType: GovernanceAccountType;
/// Vote threshold /// Vote threshold
voteThreshold: number; voteThreshold: number;
/// Execution type /// Execution type
executionType: ExecutionType; executionType: ExecutionType;
/// Timelock Type /// Governance Type
timelockType: TimelockType; governanceType: GovernanceType;
/// Voting entry rule /// Voting entry rule
votingEntryRule: VotingEntryRule; votingEntryRule: VotingEntryRule;
/// Minimum slot time-distance from creation of proposal for an instruction to be placed /// Minimum slot time-distance from creation of proposal for an instruction to be placed
@ -94,12 +94,12 @@ export enum GovernanceAccountType {
CustomSingleSignerTransaction = 5, CustomSingleSignerTransaction = 5,
} }
export const TimelockConfigLayout: typeof BufferLayout.Structure = BufferLayout.struct( export const GovernanceLayout: typeof BufferLayout.Structure = BufferLayout.struct(
[ [
BufferLayout.u8('accountType'), BufferLayout.u8('accountType'),
BufferLayout.u8('voteThreshold'), BufferLayout.u8('voteThreshold'),
BufferLayout.u8('executionType'), BufferLayout.u8('executionType'),
BufferLayout.u8('timelockType'), BufferLayout.u8('governanceType'),
BufferLayout.u8('votingEntryRule'), BufferLayout.u8('votingEntryRule'),
Layout.uint64('minimumSlotWaitingPeriod'), Layout.uint64('minimumSlotWaitingPeriod'),
Layout.publicKey('governanceMint'), Layout.publicKey('governanceMint'),
@ -121,11 +121,11 @@ export enum ExecutionType {
Independent = 0, Independent = 0,
} }
export enum TimelockType { export enum GovernanceType {
CustomSingleSignerV1 = 0, Governance = 0,
} }
export enum TimelockStateStatus { export enum ProposalStateStatus {
/// Draft /// Draft
Draft = 0, Draft = 0,
/// Taking votes /// Taking votes
@ -145,21 +145,21 @@ export enum TimelockStateStatus {
} }
export const STATE_COLOR: Record<string, string> = { export const STATE_COLOR: Record<string, string> = {
[TimelockStateStatus.Draft]: 'orange', [ProposalStateStatus.Draft]: 'orange',
[TimelockStateStatus.Voting]: 'blue', [ProposalStateStatus.Voting]: 'blue',
[TimelockStateStatus.Executing]: 'green', [ProposalStateStatus.Executing]: 'green',
[TimelockStateStatus.Completed]: 'purple', [ProposalStateStatus.Completed]: 'purple',
[TimelockStateStatus.Deleted]: 'gray', [ProposalStateStatus.Deleted]: 'gray',
[TimelockStateStatus.Defeated]: 'red', [ProposalStateStatus.Defeated]: 'red',
}; };
export interface TimelockState { export interface ProposalState {
/// Account type /// Account type
accountType: GovernanceAccountType; accountType: GovernanceAccountType;
timelockSet: PublicKey; proposal: PublicKey;
status: TimelockStateStatus; status: ProposalStateStatus;
totalSigningTokensMinted: BN; totalSigningTokensMinted: BN;
timelockTransactions: PublicKey[]; proposalTransactions: PublicKey[];
name: string; name: string;
descLink: string; descLink: string;
votingEndedAt: BN; votingEndedAt: BN;
@ -171,12 +171,12 @@ export interface TimelockState {
usedTxnSlots: number; usedTxnSlots: number;
} }
const timelockTxns = []; const proposalTxns = [];
for (let i = 0; i < TRANSACTION_SLOTS; i++) { for (let i = 0; i < MAX_TRANSACTIONS; i++) {
timelockTxns.push(Layout.publicKey('timelockTxn' + i.toString())); proposalTxns.push(Layout.publicKey('proposalTxn' + i.toString()));
} }
export const TimelockSetLayout: typeof BufferLayout.Structure = BufferLayout.struct( export const ProposalLayout: typeof BufferLayout.Structure = BufferLayout.struct(
[ [
BufferLayout.u8('accountType'), BufferLayout.u8('accountType'),
Layout.publicKey('config'), Layout.publicKey('config'),
@ -198,11 +198,11 @@ export const TimelockSetLayout: typeof BufferLayout.Structure = BufferLayout.str
], ],
); );
export const TimelockStateLayout: typeof BufferLayout.Structure = BufferLayout.struct( export const ProposalStateLayout: typeof BufferLayout.Structure = BufferLayout.struct(
[ [
BufferLayout.u8('accountType'), BufferLayout.u8('accountType'),
Layout.publicKey('timelockSet'), Layout.publicKey('proposal'),
BufferLayout.u8('timelockStateStatus'), BufferLayout.u8('proposalStateStatus'),
Layout.uint64('totalSigningTokensMinted'), Layout.uint64('totalSigningTokensMinted'),
BufferLayout.seq(BufferLayout.u8(), DESC_SIZE, 'descLink'), BufferLayout.seq(BufferLayout.u8(), DESC_SIZE, 'descLink'),
BufferLayout.seq(BufferLayout.u8(), NAME_SIZE, 'name'), BufferLayout.seq(BufferLayout.u8(), NAME_SIZE, 'name'),
@ -213,12 +213,12 @@ export const TimelockStateLayout: typeof BufferLayout.Structure = BufferLayout.s
Layout.uint64('deletedAt'), Layout.uint64('deletedAt'),
BufferLayout.u8('executions'), BufferLayout.u8('executions'),
BufferLayout.u8('usedTxnSlots'), BufferLayout.u8('usedTxnSlots'),
...timelockTxns, ...proposalTxns,
BufferLayout.seq(BufferLayout.u8(), 300, 'padding'), BufferLayout.seq(BufferLayout.u8(), 300, 'padding'),
], ],
); );
export interface TimelockSet { export interface Proposal {
/// Account type /// Account type
accountType: GovernanceAccountType; accountType: GovernanceAccountType;
@ -270,7 +270,7 @@ export interface TimelockSet {
noVotingDump: PublicKey; noVotingDump: PublicKey;
} }
export const CustomSingleSignerTimelockTransactionLayout: typeof BufferLayout.Structure = BufferLayout.struct( export const CustomSingleSignerTransactionLayout: typeof BufferLayout.Structure = BufferLayout.struct(
[ [
BufferLayout.u8('accountType'), BufferLayout.u8('accountType'),
Layout.uint64('slot'), Layout.uint64('slot'),
@ -281,7 +281,7 @@ export const CustomSingleSignerTimelockTransactionLayout: typeof BufferLayout.St
], ],
); );
export interface TimelockTransaction { export interface GovernanceTransaction {
/// Account type /// Account type
accountType: GovernanceAccountType; accountType: GovernanceAccountType;
@ -293,15 +293,14 @@ export interface TimelockTransaction {
instructionEndIndex: number; instructionEndIndex: number;
} }
export interface CustomSingleSignerTimelockTransaction export interface CustomSingleSignerTransaction extends GovernanceTransaction {}
extends TimelockTransaction {}
export const TimelockSetParser = ( export const ProposalParser = (
pubKey: PublicKey, pubKey: PublicKey,
info: AccountInfo<Buffer>, info: AccountInfo<Buffer>,
) => { ) => {
const buffer = Buffer.from(info.data); const buffer = Buffer.from(info.data);
const data = TimelockSetLayout.decode(buffer); const data = ProposalLayout.decode(buffer);
const details = { const details = {
pubkey: pubKey, pubkey: pubKey,
account: { account: {
@ -354,16 +353,16 @@ export const GovernanceVotingRecordParser = (
return details; return details;
}; };
export const TimelockStateParser = ( export const ProposalStateParser = (
pubKey: PublicKey, pubKey: PublicKey,
info: AccountInfo<Buffer>, info: AccountInfo<Buffer>,
) => { ) => {
const buffer = Buffer.from(info.data); const buffer = Buffer.from(info.data);
const data = TimelockStateLayout.decode(buffer); const data = ProposalStateLayout.decode(buffer);
const timelockTxns = []; const proposalTxns = [];
for (let i = 0; i < TRANSACTION_SLOTS; i++) { for (let i = 0; i < MAX_TRANSACTIONS; i++) {
timelockTxns.push(data['timelockTxn' + i.toString()]); proposalTxns.push(data['proposalTxn' + i.toString()]);
} }
const details = { const details = {
@ -373,12 +372,12 @@ export const TimelockStateParser = (
}, },
info: { info: {
accountType: data.accountType, accountType: data.accountType,
timelockSet: data.timelockSet, proposal: data.proposal,
status: data.timelockStateStatus, status: data.proposalStateStatus,
totalSigningTokensMinted: data.totalSigningTokensMinted, totalSigningTokensMinted: data.totalSigningTokensMinted,
descLink: utils.fromUTF8Array(data.descLink).replaceAll('\u0000', ''), descLink: utils.fromUTF8Array(data.descLink).replaceAll('\u0000', ''),
name: utils.fromUTF8Array(data.name).replaceAll('\u0000', ''), name: utils.fromUTF8Array(data.name).replaceAll('\u0000', ''),
timelockTransactions: timelockTxns, proposalTransactions: proposalTxns,
votingEndedAt: data.votingEndedAt, votingEndedAt: data.votingEndedAt,
votingBeganAt: data.votingBeganAt, votingBeganAt: data.votingBeganAt,
createdAt: data.createdAt, createdAt: data.createdAt,
@ -392,12 +391,12 @@ export const TimelockStateParser = (
return details; return details;
}; };
export const CustomSingleSignerTimelockTransactionParser = ( export const CustomSingleSignerTransactionParser = (
pubKey: PublicKey, pubKey: PublicKey,
info: AccountInfo<Buffer>, info: AccountInfo<Buffer>,
) => { ) => {
const buffer = Buffer.from(info.data); const buffer = Buffer.from(info.data);
const data = CustomSingleSignerTimelockTransactionLayout.decode(buffer); const data = CustomSingleSignerTransactionLayout.decode(buffer);
const details = { const details = {
pubkey: pubKey, pubkey: pubKey,
@ -417,12 +416,12 @@ export const CustomSingleSignerTimelockTransactionParser = (
return details; return details;
}; };
export const TimelockConfigParser = ( export const GovernanceParser = (
pubKey: PublicKey, pubKey: PublicKey,
info: AccountInfo<Buffer>, info: AccountInfo<Buffer>,
) => { ) => {
const buffer = Buffer.from(info.data); const buffer = Buffer.from(info.data);
const data = TimelockConfigLayout.decode(buffer); const data = GovernanceLayout.decode(buffer);
const details = { const details = {
pubkey: pubKey, pubkey: pubKey,
@ -433,7 +432,7 @@ export const TimelockConfigParser = (
accountType: data.accountType, accountType: data.accountType,
voteThreshold: data.voteThreshold, voteThreshold: data.voteThreshold,
executionType: data.executionType, executionType: data.executionType,
timelockType: data.timelockType, governanceType: data.governanceType,
votingEntryRule: data.votingEntryRule, votingEntryRule: data.votingEntryRule,
minimumSlotWaitingPeriod: data.minimumSlotWaitingPeriod, minimumSlotWaitingPeriod: data.minimumSlotWaitingPeriod,
governanceMint: data.governanceMint, governanceMint: data.governanceMint,

View File

@ -1,22 +1,22 @@
import { PublicKey, TransactionInstruction } from '@solana/web3.js'; import { PublicKey, TransactionInstruction } from '@solana/web3.js';
import { utils } from '@oyster/common'; import { utils } from '@oyster/common';
import * as BufferLayout from 'buffer-layout'; import * as BufferLayout from 'buffer-layout';
import { CONFIG_NAME_LENGTH, TimelockInstruction } from './timelock'; import { CONFIG_NAME_LENGTH, GovernanceInstruction } from './governance';
import BN from 'bn.js'; import BN from 'bn.js';
import * as Layout from '../utils/layout'; import * as Layout from '../utils/layout';
/// 0. `[writable]` Timelock config key. Needs to be set with pubkey set to PDA with seeds of the /// 0. `[writable]` Governance account. The account pubkey needs to be set to PDA with the following seeds:
/// program account key, governance mint key, council mint key, timelock program account key. /// 1) 'governance' const prefix, 2) Governed Program account key
/// 1. `[]` Program account that this config uses /// 1. `[]` Account of the Program governed by this Governance account
/// 2. `[]` Governance mint that this config uses /// 2. `[]` Governance mint that this Governance uses
/// 3. `[]` Council mint that this config uses [Optional] /// 3. `[]` Council mint that this Governance uses [Optional]
export const initTimelockConfigInstruction = ( export const initGovernanceInstruction = (
timelockConfigAccount: PublicKey, governanceAccount: PublicKey,
programAccount: PublicKey, programAccount: PublicKey,
governanceMint: PublicKey, governanceMint: PublicKey,
voteThreshold: number, voteThreshold: number,
executionType: number, executionType: number,
timelockType: number, governanceType: number,
votingEntryRule: number, votingEntryRule: number,
minimumSlotWaitingPeriod: BN, minimumSlotWaitingPeriod: BN,
timeLimit: BN, timeLimit: BN,
@ -33,7 +33,7 @@ export const initTimelockConfigInstruction = (
BufferLayout.u8('instruction'), BufferLayout.u8('instruction'),
BufferLayout.u8('voteThreshold'), BufferLayout.u8('voteThreshold'),
BufferLayout.u8('executionType'), BufferLayout.u8('executionType'),
BufferLayout.u8('timelockType'), BufferLayout.u8('governanceType'),
BufferLayout.u8('votingEntryRule'), BufferLayout.u8('votingEntryRule'),
Layout.uint64('minimumSlotWaitingPeriod'), Layout.uint64('minimumSlotWaitingPeriod'),
Layout.uint64('timeLimit'), Layout.uint64('timeLimit'),
@ -49,10 +49,10 @@ export const initTimelockConfigInstruction = (
dataLayout.encode( dataLayout.encode(
{ {
instruction: TimelockInstruction.InitTimelockConfig, instruction: GovernanceInstruction.InitGovernance,
voteThreshold, voteThreshold,
executionType, executionType,
timelockType, governanceType,
votingEntryRule, votingEntryRule,
minimumSlotWaitingPeriod, minimumSlotWaitingPeriod,
timeLimit, timeLimit,
@ -62,7 +62,7 @@ export const initTimelockConfigInstruction = (
); );
const keys = [ const keys = [
{ pubkey: timelockConfigAccount, isSigner: false, isWritable: true }, { pubkey: governanceAccount, isSigner: false, isWritable: true },
{ pubkey: programAccount, isSigner: false, isWritable: false }, { pubkey: programAccount, isSigner: false, isWritable: false },
{ pubkey: governanceMint, isSigner: false, isWritable: false }, { pubkey: governanceMint, isSigner: false, isWritable: false },
]; ];
@ -73,7 +73,7 @@ export const initTimelockConfigInstruction = (
return new TransactionInstruction({ return new TransactionInstruction({
keys, keys,
programId: PROGRAM_IDS.timelock.programId, programId: PROGRAM_IDS.governance.programId,
data, data,
}); });
}; };

View File

@ -5,14 +5,14 @@ import {
} from '@solana/web3.js'; } from '@solana/web3.js';
import { utils } from '@oyster/common'; import { utils } from '@oyster/common';
import * as BufferLayout from 'buffer-layout'; import * as BufferLayout from 'buffer-layout';
import { DESC_SIZE, NAME_SIZE, TimelockInstruction } from './timelock'; import { DESC_SIZE, NAME_SIZE, GovernanceInstruction } from './governance';
/// Initializes a new empty Timelocked set of Instructions that will be executed at various slots in the future in draft mode. /// Initializes a new empty Proposal for Instructions that will be executed at various slots in the future in draft mode.
/// Grants Admin token to caller. /// Grants Admin token to caller.
/// ///
/// 0. `[writable]` Uninitialized Timelock state account . /// 0. `[writable]` Uninitialized Proposal state account .
/// 1. `[writable]` Uninitialized Timelock set account . /// 1. `[writable]` Uninitialized Proposal account .
/// 2. `[writable]` Initialized Timelock config account. /// 2. `[writable]` Initialized Governance account.
/// 3. `[writable]` Initialized Signatory Mint account /// 3. `[writable]` Initialized Signatory Mint account
/// 4. `[writable]` Initialized Admin Mint account /// 4. `[writable]` Initialized Admin Mint account
/// 5. `[writable]` Initialized Voting Mint account /// 5. `[writable]` Initialized Voting Mint account
@ -27,13 +27,13 @@ import { DESC_SIZE, NAME_SIZE, TimelockInstruction } from './timelock';
/// 14. `[writable]` Initialized No voting dump account /// 14. `[writable]` Initialized No voting dump account
/// 15. `[writable]` Initialized source holding account /// 15. `[writable]` Initialized source holding account
/// 16. `[]` Source mint /// 16. `[]` Source mint
/// 17. `[]` Timelock minting authority /// 17. `[]` Governance minting authority (pda with seed of Proposal key)
/// 18. '[]` Token program id /// 18. '[]` Token program id
/// 19. `[]` Rent sysvar /// 19. `[]` Rent sysvar
export const initTimelockSetInstruction = ( export const initProposalInstruction = (
timelockStateAccount: PublicKey, proposalStateAccount: PublicKey,
timelockSetAccount: PublicKey, proposalAccount: PublicKey,
timelockConfigAccount: PublicKey, governanceAccount: PublicKey,
signatoryMintAccount: PublicKey, signatoryMintAccount: PublicKey,
adminMintAccount: PublicKey, adminMintAccount: PublicKey,
votingMintAccount: PublicKey, votingMintAccount: PublicKey,
@ -80,7 +80,7 @@ export const initTimelockSetInstruction = (
dataLayout.encode( dataLayout.encode(
{ {
instruction: TimelockInstruction.InitTimelockSet, instruction: GovernanceInstruction.InitProposal,
descLink: descAsBytes, descLink: descAsBytes,
name: nameAsBytes, name: nameAsBytes,
}, },
@ -88,9 +88,9 @@ export const initTimelockSetInstruction = (
); );
const keys = [ const keys = [
{ pubkey: timelockStateAccount, isSigner: true, isWritable: true }, { pubkey: proposalStateAccount, isSigner: true, isWritable: true },
{ pubkey: timelockSetAccount, isSigner: true, isWritable: true }, { pubkey: proposalAccount, isSigner: true, isWritable: true },
{ pubkey: timelockConfigAccount, isSigner: false, isWritable: true }, { pubkey: governanceAccount, isSigner: false, isWritable: true },
{ pubkey: signatoryMintAccount, isSigner: false, isWritable: true }, { pubkey: signatoryMintAccount, isSigner: false, isWritable: true },
{ pubkey: adminMintAccount, isSigner: false, isWritable: true }, { pubkey: adminMintAccount, isSigner: false, isWritable: true },
{ pubkey: votingMintAccount, isSigner: false, isWritable: true }, { pubkey: votingMintAccount, isSigner: false, isWritable: true },
@ -115,7 +115,7 @@ export const initTimelockSetInstruction = (
]; ];
return new TransactionInstruction({ return new TransactionInstruction({
keys, keys,
programId: PROGRAM_IDS.timelock.programId, programId: PROGRAM_IDS.governance.programId,
data, data,
}); });
}; };

View File

@ -1,7 +1,7 @@
import { PublicKey, TransactionInstruction } from '@solana/web3.js'; import { PublicKey, TransactionInstruction } from '@solana/web3.js';
import { utils } from '@oyster/common'; import { utils } from '@oyster/common';
import * as BufferLayout from 'buffer-layout'; import * as BufferLayout from 'buffer-layout';
import { TimelockInstruction } from './timelock'; import { GovernanceInstruction } from './governance';
/// [Requires Admin token] /// [Requires Admin token]
/// Removes a signer from the set. /// Removes a signer from the set.
@ -10,16 +10,17 @@ import { TimelockInstruction } from './timelock';
/// 1. `[writable]` Signatory mint account. /// 1. `[writable]` Signatory mint account.
/// 2. `[writable]` Admin account. /// 2. `[writable]` Admin account.
/// 3. `[writable]` Admin validation account. /// 3. `[writable]` Admin validation account.
/// 4. `[]` Timelock set account. /// 4. `[writable]` Proposal state account.
/// 5. `[]` Transfer authority /// 5. `[]` Proposal account.
/// 6. `[]` Timelock program mint authority /// 6. `[]` Transfer authority
/// 7. '[]` Token program id. /// 7. `[]` Governance program mint authority (pda of seed with Proposal key)
/// 8. '[]` Token program id.
export const removeSignerInstruction = ( export const removeSignerInstruction = (
signatoryAccount: PublicKey, signatoryAccount: PublicKey,
signatoryMintAccount: PublicKey, signatoryMintAccount: PublicKey,
adminAccount: PublicKey, adminAccount: PublicKey,
adminValidationAccount: PublicKey, adminValidationAccount: PublicKey,
timelockSetAccount: PublicKey, proposalAccount: PublicKey,
transferAuthority: PublicKey, transferAuthority: PublicKey,
mintAuthority: PublicKey, mintAuthority: PublicKey,
): TransactionInstruction => { ): TransactionInstruction => {
@ -31,7 +32,7 @@ export const removeSignerInstruction = (
dataLayout.encode( dataLayout.encode(
{ {
instruction: TimelockInstruction.RemoveSigner, instruction: GovernanceInstruction.RemoveSigner,
}, },
data, data,
); );
@ -41,14 +42,14 @@ export const removeSignerInstruction = (
{ pubkey: signatoryMintAccount, isSigner: false, isWritable: true }, { pubkey: signatoryMintAccount, isSigner: false, isWritable: true },
{ pubkey: adminAccount, isSigner: false, isWritable: true }, { pubkey: adminAccount, isSigner: false, isWritable: true },
{ pubkey: adminValidationAccount, isSigner: false, isWritable: true }, { pubkey: adminValidationAccount, isSigner: false, isWritable: true },
{ pubkey: timelockSetAccount, isSigner: false, isWritable: true }, { pubkey: proposalAccount, isSigner: false, isWritable: true },
{ pubkey: transferAuthority, isSigner: true, isWritable: false }, { pubkey: transferAuthority, isSigner: true, isWritable: false },
{ pubkey: mintAuthority, isSigner: false, isWritable: false }, { pubkey: mintAuthority, isSigner: false, isWritable: false },
{ pubkey: PROGRAM_IDS.token, isSigner: false, isWritable: false }, { pubkey: PROGRAM_IDS.token, isSigner: false, isWritable: false },
]; ];
return new TransactionInstruction({ return new TransactionInstruction({
keys, keys,
programId: PROGRAM_IDS.timelock.programId, programId: PROGRAM_IDS.governance.programId,
data, data,
}); });
}; };

View File

@ -5,25 +5,25 @@ import {
} from '@solana/web3.js'; } from '@solana/web3.js';
import { utils } from '@oyster/common'; import { utils } from '@oyster/common';
import * as BufferLayout from 'buffer-layout'; import * as BufferLayout from 'buffer-layout';
import { TimelockInstruction } from './timelock'; import { GovernanceInstruction } from './governance';
/// [Requires Signatory token] /// [Requires Signatory token]
/// Burns signatory token, indicating you approve of moving this Timelock set from Draft state to Voting state. /// Burns signatory token, indicating you approve of moving this Proposal from Draft state to Voting state.
/// The last Signatory token to be burned moves the state to Voting. /// The last Signatory token to be burned moves the state to Voting.
/// ///
/// 0. `[writable]` Timelock state account pub key. /// 0. `[writable]` Proposal state account pub key.
/// 1. `[writable]` Signatory account /// 1. `[writable]` Signatory account
/// 2. `[writable]` Signatory mint account. /// 2. `[writable]` Signatory mint account.
/// 3. `[]` Timelock set account pub key. /// 3. `[]` Proposal account pub key.
/// 4. `[]` Transfer authority /// 4. `[]` Transfer authority
/// 5. `[]` Timelock mint authority /// 5. `[]` Governance mint authority (pda of seed Proposal key)
/// 6. `[]` Token program account. /// 7. `[]` Token program account.
/// 7. `[]` Clock sysvar. /// 8. `[]` Clock sysvar.
export const signInstruction = ( export const signInstruction = (
timelockStateAccount: PublicKey, proposalStateAccount: PublicKey,
signatoryAccount: PublicKey, signatoryAccount: PublicKey,
signatoryMintAccount: PublicKey, signatoryMintAccount: PublicKey,
timelockSetAccount: PublicKey, proposalAccount: PublicKey,
transferAuthority: PublicKey, transferAuthority: PublicKey,
mintAuthority: PublicKey, mintAuthority: PublicKey,
): TransactionInstruction => { ): TransactionInstruction => {
@ -35,16 +35,16 @@ export const signInstruction = (
dataLayout.encode( dataLayout.encode(
{ {
instruction: TimelockInstruction.Sign, instruction: GovernanceInstruction.Sign,
}, },
data, data,
); );
const keys = [ const keys = [
{ pubkey: timelockStateAccount, isSigner: false, isWritable: true }, { pubkey: proposalStateAccount, isSigner: false, isWritable: true },
{ pubkey: signatoryAccount, isSigner: false, isWritable: true }, { pubkey: signatoryAccount, isSigner: false, isWritable: true },
{ pubkey: signatoryMintAccount, isSigner: false, isWritable: true }, { pubkey: signatoryMintAccount, isSigner: false, isWritable: true },
{ pubkey: timelockSetAccount, isSigner: false, isWritable: false }, { pubkey: proposalAccount, isSigner: false, isWritable: false },
{ pubkey: transferAuthority, isSigner: true, isWritable: false }, { pubkey: transferAuthority, isSigner: true, isWritable: false },
{ pubkey: mintAuthority, isSigner: false, isWritable: false }, { pubkey: mintAuthority, isSigner: false, isWritable: false },
{ pubkey: PROGRAM_IDS.token, isSigner: false, isWritable: false }, { pubkey: PROGRAM_IDS.token, isSigner: false, isWritable: false },
@ -52,7 +52,7 @@ export const signInstruction = (
]; ];
return new TransactionInstruction({ return new TransactionInstruction({
keys, keys,
programId: PROGRAM_IDS.timelock.programId, programId: PROGRAM_IDS.governance.programId,
data, data,
}); });
}; };

View File

@ -7,17 +7,17 @@ import { utils } from '@oyster/common';
import * as Layout from '../utils/layout'; import * as Layout from '../utils/layout';
import * as BufferLayout from 'buffer-layout'; import * as BufferLayout from 'buffer-layout';
import { TimelockInstruction } from './timelock'; import { GovernanceInstruction } from './governance';
import BN from 'bn.js'; import BN from 'bn.js';
/// [Requires Voting tokens] /// [Requires Voting tokens]
/// Burns voting tokens, indicating you approve and/or disapprove of running this set of transactions. If you tip the vote threshold, /// Burns voting tokens, indicating you approve and/or disapprove of running this set of transactions. If you tip the consensus,
/// then the transactions can begin to be run at their time slots when people click execute. /// then the transactions can begin to be run at their time slots when people click execute. You are then given yes and/or no tokens.
/// ///
/// 0. `[writable]` Governance voting record account. /// 0. `[writable]` Governance voting record account.
/// Can be uninitialized or initialized(if already used once in this proposal) /// Can be uninitialized or initialized(if already used once in this proposal)
/// Must have address with PDA having seed tuple [timelock acct key, proposal key, your voting account key] /// Must have address with PDA having seed tuple [Governance acct key, proposal key, your voting account key]
/// 1. `[writable]` Timelock state account. /// 1. `[writable]` Proposal state account.
/// 2. `[writable]` Your Voting account. /// 2. `[writable]` Your Voting account.
/// 3. `[writable]` Your Yes-Voting account. /// 3. `[writable]` Your Yes-Voting account.
/// 4. `[writable]` Your No-Voting account. /// 4. `[writable]` Your No-Voting account.
@ -25,15 +25,15 @@ import BN from 'bn.js';
/// 6. `[writable]` Yes Voting mint account. /// 6. `[writable]` Yes Voting mint account.
/// 7. `[writable]` No Voting mint account. /// 7. `[writable]` No Voting mint account.
/// 8. `[]` Source mint account /// 8. `[]` Source mint account
/// 9. `[]` Timelock set account. /// 9. `[]` Proposal account.
/// 10. `[]` Timelock config account. /// 10. `[]` Governance account.
/// 12. `[]` Transfer authority /// 12. `[]` Transfer authority
/// 13. `[]` Timelock program mint authority /// 13. `[]` Governance program mint authority (pda of seed Proposal key)
/// 14. `[]` Token program account. /// 14. `[]` Token program account.
/// 15. `[]` Clock sysvar. /// 15. `[]` Clock sysvar.
export const voteInstruction = ( export const voteInstruction = (
governanceVotingRecord: PublicKey, governanceVotingRecord: PublicKey,
timelockStateAccount: PublicKey, proposalStateAccount: PublicKey,
votingAccount: PublicKey, votingAccount: PublicKey,
yesVotingAccount: PublicKey, yesVotingAccount: PublicKey,
noVotingAccount: PublicKey, noVotingAccount: PublicKey,
@ -41,8 +41,8 @@ export const voteInstruction = (
yesVotingMint: PublicKey, yesVotingMint: PublicKey,
noVotingMint: PublicKey, noVotingMint: PublicKey,
sourceMint: PublicKey, sourceMint: PublicKey,
timelockSetAccount: PublicKey, proposalAccount: PublicKey,
timelockConfig: PublicKey, governance: PublicKey,
transferAuthority: PublicKey, transferAuthority: PublicKey,
mintAuthority: PublicKey, mintAuthority: PublicKey,
yesVotingTokenAmount: number, yesVotingTokenAmount: number,
@ -60,7 +60,7 @@ export const voteInstruction = (
dataLayout.encode( dataLayout.encode(
{ {
instruction: TimelockInstruction.Vote, instruction: GovernanceInstruction.Vote,
yesVotingTokenAmount: new BN(yesVotingTokenAmount), yesVotingTokenAmount: new BN(yesVotingTokenAmount),
noVotingTokenAmount: new BN(noVotingTokenAmount), noVotingTokenAmount: new BN(noVotingTokenAmount),
}, },
@ -69,7 +69,7 @@ export const voteInstruction = (
const keys = [ const keys = [
{ pubkey: governanceVotingRecord, isSigner: false, isWritable: true }, { pubkey: governanceVotingRecord, isSigner: false, isWritable: true },
{ pubkey: timelockStateAccount, isSigner: false, isWritable: true }, { pubkey: proposalStateAccount, isSigner: false, isWritable: true },
{ pubkey: votingAccount, isSigner: false, isWritable: true }, { pubkey: votingAccount, isSigner: false, isWritable: true },
{ pubkey: yesVotingAccount, isSigner: false, isWritable: true }, { pubkey: yesVotingAccount, isSigner: false, isWritable: true },
{ pubkey: noVotingAccount, isSigner: false, isWritable: true }, { pubkey: noVotingAccount, isSigner: false, isWritable: true },
@ -77,8 +77,8 @@ export const voteInstruction = (
{ pubkey: yesVotingMint, isSigner: false, isWritable: true }, { pubkey: yesVotingMint, isSigner: false, isWritable: true },
{ pubkey: noVotingMint, isSigner: false, isWritable: true }, { pubkey: noVotingMint, isSigner: false, isWritable: true },
{ pubkey: sourceMint, isSigner: false, isWritable: false }, { pubkey: sourceMint, isSigner: false, isWritable: false },
{ pubkey: timelockSetAccount, isSigner: false, isWritable: false }, { pubkey: proposalAccount, isSigner: false, isWritable: false },
{ pubkey: timelockConfig, isSigner: false, isWritable: false }, { pubkey: governance, isSigner: false, isWritable: false },
{ pubkey: transferAuthority, isSigner: true, isWritable: false }, { pubkey: transferAuthority, isSigner: true, isWritable: false },
{ pubkey: mintAuthority, isSigner: false, isWritable: false }, { pubkey: mintAuthority, isSigner: false, isWritable: false },
{ pubkey: PROGRAM_IDS.token, isSigner: false, isWritable: false }, { pubkey: PROGRAM_IDS.token, isSigner: false, isWritable: false },
@ -87,7 +87,7 @@ export const voteInstruction = (
return new TransactionInstruction({ return new TransactionInstruction({
keys, keys,
programId: PROGRAM_IDS.timelock.programId, programId: PROGRAM_IDS.governance.programId,
data, data,
}); });
}; };

View File

@ -3,24 +3,27 @@ import { utils } from '@oyster/common';
import * as Layout from '../utils/layout'; import * as Layout from '../utils/layout';
import * as BufferLayout from 'buffer-layout'; import * as BufferLayout from 'buffer-layout';
import { TimelockInstruction } from './timelock'; import { GovernanceInstruction } from './governance';
import BN from 'bn.js'; import BN from 'bn.js';
/// [Requires voting tokens]
/// Withdraws voting tokens.
///
/// 0. `[writable]` Governance voting record account. See Vote docs for more detail. /// 0. `[writable]` Governance voting record account. See Vote docs for more detail.
/// 1. `[writable]` Initialized Voting account from which to remove your voting tokens. /// 1. `[writable]` Initialized Voting account from which to remove your voting tokens.
/// 2. `[writable]` Initialized Yes Voting account from which to remove your voting tokens. /// 2. `[writable]` Initialized Yes Voting account from which to remove your voting tokens.
/// 3. `[writable]` Initialized No Voting account from which to remove your voting tokens. /// 3. `[writable]` Initialized No Voting account from which to remove your voting tokens.
/// 4. `[writable]` User token account that you wish your actual tokens to be returned to. /// 4. `[writable]` User token account that you wish your actual tokens to be returned to.
/// 5. `[writable]` Source holding account owned by the timelock that will has the actual tokens in escrow. /// 5. `[writable]` Source holding account owned by the Governance that will has the actual tokens in escrow.
/// 6. `[writable]` Initialized Yes Voting dump account owned by timelock set to which to send your voting tokens. /// 6. `[writable]` Initialized Yes Voting dump account owned by Proposal to which to send your voting tokens.
/// 7. `[writable]` Initialized No Voting dump account owned by timelock set to which to send your voting tokens. /// 7. `[writable]` Initialized No Voting dump account owned by Proposal to which to send your voting tokens.
/// 8. `[writable]` Voting mint account. /// 8. `[writable]` Voting mint account.
/// 9. `[writable]` Yes Voting mint account. /// 9. `[writable]` Yes Voting mint account.
/// 10. `[writable]` No Voting mint account. /// 10. `[writable]` No Voting mint account.
/// 11. `[]` Timelock state account. /// 11. `[]` Proposal state account.
/// 12. `[]` Timelock set account. /// 12. `[]` Proposal account.
/// 13. `[]` Transfer authority /// 13. `[]` Transfer authority
/// 14. `[]` Timelock program mint authority /// 14. `[]` Governance program mint authority (pda of seed Proposal key)
/// 15. `[]` Token program account. /// 15. `[]` Token program account.
export const withdrawVotingTokensInstruction = ( export const withdrawVotingTokensInstruction = (
governanceVotingRecord: PublicKey, governanceVotingRecord: PublicKey,
@ -34,8 +37,8 @@ export const withdrawVotingTokensInstruction = (
votingMint: PublicKey, votingMint: PublicKey,
yesVotingMint: PublicKey, yesVotingMint: PublicKey,
noVotingMint: PublicKey, noVotingMint: PublicKey,
timelockStateAccount: PublicKey, proposalStateAccount: PublicKey,
timelockSetAccount: PublicKey, proposalAccount: PublicKey,
transferAuthority: PublicKey, transferAuthority: PublicKey,
mintAuthority: PublicKey, mintAuthority: PublicKey,
votingTokenAmount: number, votingTokenAmount: number,
@ -51,7 +54,7 @@ export const withdrawVotingTokensInstruction = (
dataLayout.encode( dataLayout.encode(
{ {
instruction: TimelockInstruction.WithdrawVotingTokens, instruction: GovernanceInstruction.WithdrawVotingTokens,
votingTokenAmount: new BN(votingTokenAmount), votingTokenAmount: new BN(votingTokenAmount),
}, },
data, data,
@ -69,8 +72,8 @@ export const withdrawVotingTokensInstruction = (
{ pubkey: votingMint, isSigner: false, isWritable: true }, { pubkey: votingMint, isSigner: false, isWritable: true },
{ pubkey: yesVotingMint, isSigner: false, isWritable: true }, { pubkey: yesVotingMint, isSigner: false, isWritable: true },
{ pubkey: noVotingMint, isSigner: false, isWritable: true }, { pubkey: noVotingMint, isSigner: false, isWritable: true },
{ pubkey: timelockStateAccount, isSigner: false, isWritable: false }, { pubkey: proposalStateAccount, isSigner: false, isWritable: false },
{ pubkey: timelockSetAccount, isSigner: false, isWritable: false }, { pubkey: proposalAccount, isSigner: false, isWritable: false },
{ pubkey: transferAuthority, isSigner: true, isWritable: false }, { pubkey: transferAuthority, isSigner: true, isWritable: false },
{ pubkey: mintAuthority, isSigner: false, isWritable: false }, { pubkey: mintAuthority, isSigner: false, isWritable: false },
{ pubkey: PROGRAM_IDS.token, isSigner: false, isWritable: false }, { pubkey: PROGRAM_IDS.token, isSigner: false, isWritable: false },
@ -78,7 +81,7 @@ export const withdrawVotingTokensInstruction = (
return new TransactionInstruction({ return new TransactionInstruction({
keys, keys,
programId: PROGRAM_IDS.timelock.programId, programId: PROGRAM_IDS.governance.programId,
data, data,
}); });
}; };

View File

@ -5,7 +5,7 @@ import {
GovernanceVotingRecord, GovernanceVotingRecord,
GovernanceVotingRecordLayout, GovernanceVotingRecordLayout,
GovernanceVotingRecordParser, GovernanceVotingRecordParser,
} from '../models/timelock'; } from '../models/governance';
const MAX_LOOKUPS = 5000; const MAX_LOOKUPS = 5000;
export async function getGovernanceVotingRecords( export async function getGovernanceVotingRecords(
@ -25,7 +25,7 @@ export async function getGovernanceVotingRecords(
id: 1, id: 1,
method: 'getProgramAccounts', method: 'getProgramAccounts',
params: [ params: [
PROGRAM_IDS.timelock.programId.toBase58(), PROGRAM_IDS.governance.programId.toBase58(),
{ {
commitment: 'single', commitment: 'single',
filters: [ filters: [

View File

@ -6,7 +6,7 @@ import {
PublicKey, PublicKey,
Message, Message,
} from '@solana/web3.js'; } from '@solana/web3.js';
import { GOVERNANCE_AUTHORITY_SEED, TimelockSet } from '../models/timelock'; import { GOVERNANCE_AUTHORITY_SEED, Proposal } from '../models/governance';
export async function serializeInstruction({ export async function serializeInstruction({
connection, connection,
instr, instr,
@ -14,7 +14,7 @@ export async function serializeInstruction({
}: { }: {
connection: Connection; connection: Connection;
instr: TransactionInstruction; instr: TransactionInstruction;
proposal: ParsedAccount<TimelockSet>; proposal: ParsedAccount<Proposal>;
}): Promise<{ base64: string; byteArray: Uint8Array }> { }): Promise<{ base64: string; byteArray: Uint8Array }> {
const PROGRAM_IDS = utils.programIds(); const PROGRAM_IDS = utils.programIds();
let instructionTransaction = new Transaction(); let instructionTransaction = new Transaction();
@ -24,7 +24,7 @@ export async function serializeInstruction({
).blockhash; ).blockhash;
const [authority] = await PublicKey.findProgramAddress( const [authority] = await PublicKey.findProgramAddress(
[Buffer.from(GOVERNANCE_AUTHORITY_SEED), proposal.pubkey.toBuffer()], [Buffer.from(GOVERNANCE_AUTHORITY_SEED), proposal.pubkey.toBuffer()],
PROGRAM_IDS.timelock.programId, PROGRAM_IDS.governance.programId,
); );
instructionTransaction.setSigners(authority); instructionTransaction.setSigners(authority);
const msg: Message = instructionTransaction.compileMessage(); const msg: Message = instructionTransaction.compileMessage();

View File

@ -1,76 +1,6 @@
import { import { Message, PublicKey } from '@solana/web3.js';
Account,
Connection,
Message,
PublicKey,
TransactionInstruction,
} from '@solana/web3.js';
import { contexts, utils, ParsedAccount } from '@oyster/common';
import { export function getMessageAccountInfos(
TimelockSet,
TimelockState,
TimelockTransaction,
} from '../models/timelock';
import { executeInstruction } from '../models/execute';
import { LABELS } from '../constants';
const { sendTransaction } = contexts.Connection;
const { notify } = utils;
export const execute = async (
connection: Connection,
wallet: any,
proposal: ParsedAccount<TimelockSet>,
state: ParsedAccount<TimelockState>,
transaction: ParsedAccount<TimelockTransaction>,
) => {
let signers: Account[] = [];
let instructions: TransactionInstruction[] = [];
const actualMessage = decodeBufferIntoMessage(transaction.info.instruction);
const accountInfos = getAccountInfos(actualMessage);
instructions.push(
executeInstruction(
transaction.pubkey,
state.pubkey,
proposal.pubkey,
actualMessage.accountKeys[actualMessage.instructions[0].programIdIndex],
proposal.info.config,
accountInfos,
),
);
notify({
message: LABELS.EXECUTING,
description: LABELS.PLEASE_WAIT,
type: 'warn',
});
try {
let tx = await sendTransaction(
connection,
wallet,
instructions,
signers,
true,
);
notify({
message: LABELS.EXECUTED,
type: 'success',
description: LABELS.TRANSACTION + ` ${tx}`,
});
} catch (ex) {
console.error(ex);
throw new Error();
}
};
function decodeBufferIntoMessage(instruction: number[]): Message {
return Message.from(instruction);
}
function getAccountInfos(
actualMessage: Message, actualMessage: Message,
): { pubkey: PublicKey; isSigner: boolean; isWritable: boolean }[] { ): { pubkey: PublicKey; isSigner: boolean; isWritable: boolean }[] {
console.log(actualMessage); console.log(actualMessage);

View File

@ -4,7 +4,7 @@ import { GUTTER } from '../../constants';
import { contexts, hooks, ParsedAccount } from '@oyster/common'; import { contexts, hooks, ParsedAccount } from '@oyster/common';
import './style.less'; import './style.less';
import { useProposals } from '../../contexts/proposals'; import { useProposals } from '../../contexts/proposals';
import { TimelockSet } from '../../models/timelock'; import { Proposal } from '../../models/governance';
import { Connection } from '@solana/web3.js'; import { Connection } from '@solana/web3.js';
import { WalletAdapter } from '@solana/wallet-base'; import { WalletAdapter } from '@solana/wallet-base';
const { useWallet } = contexts.Wallet; const { useWallet } = contexts.Wallet;
@ -38,7 +38,7 @@ function InnerDummyView({
}: { }: {
connection: Connection; connection: Connection;
wallet: WalletAdapter; wallet: WalletAdapter;
proposal: ParsedAccount<TimelockSet>; proposal: ParsedAccount<Proposal>;
}) { }) {
const sigAccount = useAccountByMint(proposal.info.signatoryMint); const sigAccount = useAccountByMint(proposal.info.signatoryMint);
if (!sigAccount) return <Spin />; if (!sigAccount) return <Spin />;

View File

@ -11,10 +11,10 @@ import {
} from '@oyster/common'; } from '@oyster/common';
import { import {
ExecutionType, ExecutionType,
TimelockConfig, Governance,
TimelockType, GovernanceType,
VotingEntryRule, VotingEntryRule,
} from '../../models/timelock'; } from '../../models/governance';
import { PublicKey } from '@solana/web3.js'; import { PublicKey } from '@solana/web3.js';
import { Table } from 'antd'; import { Table } from 'antd';
import MintSourceTokens from '../../components/Proposal/MintSourceTokens'; import MintSourceTokens from '../../components/Proposal/MintSourceTokens';
@ -40,9 +40,9 @@ const columns = [
}, },
{ {
title: LABELS.PROPOSAL_TYPE, title: LABELS.PROPOSAL_TYPE,
dataIndex: 'timelockType', dataIndex: 'governanceType',
key: 'timelockType', key: 'governanceType',
render: (number: number) => <span>{TimelockType[number]}</span>, render: (number: number) => <span>{GovernanceType[number]}</span>,
}, },
{ {
title: LABELS.VOTING_ENTRY_RULES, title: LABELS.VOTING_ENTRY_RULES,
@ -75,7 +75,7 @@ const columns = [
render: (key: PublicKey) => <span>{key?.toBase58()}</span>, render: (key: PublicKey) => <span>{key?.toBase58()}</span>,
}, },
{ {
title: LABELS.PROGRAM, title: LABELS.PROGRAM_ID,
dataIndex: 'program', dataIndex: 'program',
key: 'program', key: 'program',
render: (key: PublicKey) => <span>{key.toBase58()}</span>, render: (key: PublicKey) => <span>{key.toBase58()}</span>,
@ -85,11 +85,11 @@ const columns = [
title: LABELS.ACTIONS, title: LABELS.ACTIONS,
dataIndex: 'config', dataIndex: 'config',
key: 'config', key: 'config',
render: (config: ParsedAccount<TimelockConfig>) => ( render: (config: ParsedAccount<Governance>) => (
<> <>
<MintSourceTokens timelockConfig={config} useGovernance={true} /> <MintSourceTokens governance={config} useGovernance={true} />
{config.info.councilMint && ( {config.info.councilMint && (
<MintSourceTokens timelockConfig={config} useGovernance={false} /> <MintSourceTokens governance={config} useGovernance={false} />
)} )}
</> </>
), ),
@ -133,7 +133,7 @@ export const GovernanceDashboard = () => {
})), })),
); );
}); });
}, [configs.length, myTokenAccts.join(',')]); }, [configs.length, myTokenAccts.join(',')]); //eslint-disable-line
return <Table columns={columns} dataSource={data} />; return <Table columns={columns} dataSource={data} />;
}; };

View File

@ -5,9 +5,9 @@ import { PublicKey } from '@solana/web3.js';
import { import {
CONFIG_NAME_LENGTH, CONFIG_NAME_LENGTH,
ExecutionType, ExecutionType,
TimelockType, GovernanceType,
VotingEntryRule, VotingEntryRule,
} from '../../models/timelock'; } from '../../models/governance';
import { LABELS } from '../../constants'; import { LABELS } from '../../constants';
import { contexts, utils, tryParseKey } from '@oyster/common'; import { contexts, utils, tryParseKey } from '@oyster/common';
import { registerProgramGovernance } from '../../actions/registerProgramGovernance'; import { registerProgramGovernance } from '../../actions/registerProgramGovernance';
@ -68,7 +68,7 @@ export function NewForm({
const wallet = useWallet(); const wallet = useWallet();
const connection = useConnection(); const connection = useConnection();
const onFinish = async (values: { const onFinish = async (values: {
timelockType: TimelockType; governanceType: GovernanceType;
executionType: ExecutionType; executionType: ExecutionType;
voteThreshold: number; voteThreshold: number;
votingEntryRule: VotingEntryRule; votingEntryRule: VotingEntryRule;
@ -119,8 +119,8 @@ export function NewForm({
return; return;
} }
const uninitializedConfig = { const uninitializedGovernance = {
timelockType: values.timelockType, governanceType: values.governanceType,
executionType: values.executionType, executionType: values.executionType,
voteThreshold: values.voteThreshold, voteThreshold: values.voteThreshold,
votingEntryRule: values.votingEntryRule, votingEntryRule: values.votingEntryRule,
@ -140,7 +140,7 @@ export function NewForm({
const newConfig = await registerProgramGovernance( const newConfig = await registerProgramGovernance(
connection, connection,
wallet.wallet, wallet.wallet,
uninitializedConfig, uninitializedGovernance,
councilVisible, councilVisible,
); );
handleOk(newConfig); handleOk(newConfig);
@ -158,7 +158,7 @@ export function NewForm({
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name="program" name="program"
label={LABELS.PROGRAM} label={LABELS.PROGRAM_ID}
rules={[{ required: true }]} rules={[{ required: true }]}
> >
<Input /> <Input />
@ -215,15 +215,13 @@ export function NewForm({
</Select> </Select>
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name="timelockType" name="governanceType"
label={LABELS.PROPOSAL_TYPE} label={LABELS.PROPOSAL_TYPE}
rules={[{ required: true }]} rules={[{ required: true }]}
initialValue={TimelockType.CustomSingleSignerV1} initialValue={GovernanceType.Governance}
> >
<Select placeholder={LABELS.SELECT_PROPOSAL_TYPE}> <Select placeholder={LABELS.SELECT_PROPOSAL_TYPE}>
<Option value={TimelockType.CustomSingleSignerV1}> <Option value={GovernanceType.Governance}>Single Signer</Option>
Single Signer
</Option>
</Select> </Select>
</Form.Item> </Form.Item>
<Form.Item <Form.Item

View File

@ -6,7 +6,7 @@ import { TokenIcon, useWallet } from '@oyster/common';
import { Background } from './../../components/Background'; import { Background } from './../../components/Background';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { RegisterGovernanceMenuItem } from '../governance/register'; import { RegisterGovernanceMenuItem } from '../governance/register';
import { TimelockStateStatus } from '../../models/timelock'; import { ProposalStateStatus } from '../../models/governance';
export const HomeView = () => { export const HomeView = () => {
const history = useHistory(); const history = useHistory();
@ -26,8 +26,8 @@ export const HomeView = () => {
if (proposal.info.config.toBase58() === configKey) { if (proposal.info.config.toBase58() === configKey) {
acc.active = acc.active =
acc.active + acc.active +
(state.info.status === TimelockStateStatus.Voting || (state.info.status === ProposalStateStatus.Voting ||
state.info.status === TimelockStateStatus.Draft state.info.status === ProposalStateStatus.Draft
? 1 ? 1
: 0); : 0);
@ -56,7 +56,7 @@ export const HomeView = () => {
}); });
}); });
return newListData; return newListData;
}, [configs, proposals]); }, [configs, proposals, states]);
return ( return (
<> <>

View File

@ -1,15 +1,15 @@
import { Card, Col, Grid, Row, Spin, Statistic, Tabs } from 'antd'; import { Card, Col, Row, Spin, Statistic, Tabs } from 'antd';
import React, { useMemo, useState } from 'react'; import React, { useMemo, useState } from 'react';
import { LABELS } from '../../constants'; import { LABELS } from '../../constants';
import { ParsedAccount, TokenIcon } from '@oyster/common'; import { ParsedAccount, TokenIcon } from '@oyster/common';
import { import {
INSTRUCTION_LIMIT, INSTRUCTION_LIMIT,
TimelockConfig, Governance,
TimelockSet, Proposal,
TimelockState, ProposalState,
TimelockStateStatus, ProposalStateStatus,
TimelockTransaction, GovernanceTransaction,
} from '../../models/timelock'; } from '../../models/governance';
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import ReactMarkdown from 'react-markdown'; import ReactMarkdown from 'react-markdown';
import { useProposals } from '../../contexts/proposals'; import { useProposals } from '../../contexts/proposals';
@ -30,11 +30,12 @@ import { VoterBubbleGraph } from '../../components/Proposal/VoterBubbleGraph';
import { VoterTable } from '../../components/Proposal/VoterTable'; import { VoterTable } from '../../components/Proposal/VoterTable';
const { TabPane } = Tabs; const { TabPane } = Tabs;
// eslint-disable-next-line
export const urlRegex = /[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/; export const urlRegex = /[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/;
const { useMint } = contexts.Accounts; const { useMint } = contexts.Accounts;
const { useConnectionConfig } = contexts.Connection; const { useConnectionConfig } = contexts.Connection;
const { useAccountByMint } = hooks; const { useAccountByMint } = hooks;
const { useBreakpoint } = Grid; //const { useBreakpoint } = Grid;
export enum VoteType { export enum VoteType {
Undecided = 'Undecided', Undecided = 'Undecided',
@ -46,8 +47,8 @@ export const ProposalView = () => {
const context = useProposals(); const context = useProposals();
const { id } = useParams<{ id: string }>(); const { id } = useParams<{ id: string }>();
const proposal = context.proposals[id]; const proposal = context.proposals[id];
const timelockConfig = context.configs[proposal?.info.config.toBase58()]; const governance = context.configs[proposal?.info.config.toBase58()];
const timelockState = context.states[proposal?.info.state.toBase58()]; const proposalState = context.states[proposal?.info.state.toBase58()];
const { endpoint } = useConnectionConfig(); const { endpoint } = useConnectionConfig();
const sigMint = useMint(proposal?.info.signatoryMint); const sigMint = useMint(proposal?.info.signatoryMint);
const votingMint = useMint(proposal?.info.votingMint); const votingMint = useMint(proposal?.info.votingMint);
@ -68,8 +69,8 @@ export const ProposalView = () => {
noVotingMint ? ( noVotingMint ? (
<InnerProposalView <InnerProposalView
proposal={proposal} proposal={proposal}
timelockState={timelockState} proposalState={proposalState}
timelockConfig={timelockConfig} governance={governance}
sourceMint={sourceMint} sourceMint={sourceMint}
votingMint={votingMint} votingMint={votingMint}
yesVotingMint={yesVotingMint} yesVotingMint={yesVotingMint}
@ -93,7 +94,7 @@ function useLoadGist({
setMsg, setMsg,
setContent, setContent,
isGist, isGist,
timelockState, proposalState,
}: { }: {
loading: boolean; loading: boolean;
setLoading: (b: boolean) => void; setLoading: (b: boolean) => void;
@ -101,11 +102,11 @@ function useLoadGist({
setFailed: (b: boolean) => void; setFailed: (b: boolean) => void;
setContent: (b: string) => void; setContent: (b: string) => void;
isGist: boolean; isGist: boolean;
timelockState: ParsedAccount<TimelockState>; proposalState: ParsedAccount<ProposalState>;
}) { }) {
useMemo(() => { useMemo(() => {
if (loading) { if (loading) {
let toFetch = timelockState.info.descLink; let toFetch = proposalState.info.descLink;
const pieces = toFetch.match(urlRegex); const pieces = toFetch.match(urlRegex);
if (isGist && pieces) { if (isGist && pieces) {
const justIdWithoutUser = pieces[1].split('/')[2]; const justIdWithoutUser = pieces[1].split('/')[2];
@ -134,7 +135,7 @@ function useLoadGist({
setLoading(false); setLoading(false);
}); });
} }
}, [loading]); }, [loading]); //eslint-disable-line
} }
interface PartialGovernanceRecord { interface PartialGovernanceRecord {
info: { yesCount: BN; noCount: BN; undecidedCount: BN }; info: { yesCount: BN; noCount: BN; undecidedCount: BN };
@ -155,6 +156,7 @@ function voterDisplayData(
title: key, title: key,
group: label, group: label,
value: amount, value: amount,
key: key,
}); });
const undecidedData = [ const undecidedData = [
@ -198,51 +200,52 @@ function voterDisplayData(
const data = [...undecidedData, ...yesData, ...noData].sort( const data = [...undecidedData, ...yesData, ...noData].sort(
(a, b) => b.value - a.value, (a, b) => b.value - a.value,
); );
return data; return data;
} }
function InnerProposalView({ function InnerProposalView({
proposal, proposal,
timelockState, proposalState,
sigMint, sigMint,
votingMint, votingMint,
yesVotingMint, yesVotingMint,
noVotingMint, noVotingMint,
instructions, instructions,
timelockConfig, governance,
sourceMint, sourceMint,
votingDisplayData, votingDisplayData,
endpoint, endpoint,
}: { }: {
proposal: ParsedAccount<TimelockSet>; proposal: ParsedAccount<Proposal>;
timelockConfig: ParsedAccount<TimelockConfig>; governance: ParsedAccount<Governance>;
timelockState: ParsedAccount<TimelockState>; proposalState: ParsedAccount<ProposalState>;
sigMint: MintInfo; sigMint: MintInfo;
votingMint: MintInfo; votingMint: MintInfo;
yesVotingMint: MintInfo; yesVotingMint: MintInfo;
noVotingMint: MintInfo; noVotingMint: MintInfo;
sourceMint: MintInfo; sourceMint: MintInfo;
instructions: Record<string, ParsedAccount<TimelockTransaction>>; instructions: Record<string, ParsedAccount<GovernanceTransaction>>;
votingDisplayData: Array<VoterDisplayData>; votingDisplayData: Array<VoterDisplayData>;
endpoint: string; endpoint: string;
}) { }) {
const sigAccount = useAccountByMint(proposal.info.signatoryMint); const sigAccount = useAccountByMint(proposal.info.signatoryMint);
const adminAccount = useAccountByMint(proposal.info.adminMint); const adminAccount = useAccountByMint(proposal.info.adminMint);
const instructionsForProposal: ParsedAccount<TimelockTransaction>[] = timelockState.info.timelockTransactions const instructionsForProposal: ParsedAccount<GovernanceTransaction>[] = proposalState.info.proposalTransactions
.map(k => instructions[k.toBase58()]) .map(k => instructions[k.toBase58()])
.filter(k => k); .filter(k => k);
const isUrl = !!timelockState.info.descLink.match(urlRegex); const isUrl = !!proposalState.info.descLink.match(urlRegex);
const isGist = const isGist =
!!timelockState.info.descLink.match(/gist/i) && !!proposalState.info.descLink.match(/gist/i) &&
!!timelockState.info.descLink.match(/github/i); !!proposalState.info.descLink.match(/github/i);
const [content, setContent] = useState(timelockState.info.descLink); const [content, setContent] = useState(proposalState.info.descLink);
const [loading, setLoading] = useState(isUrl); const [loading, setLoading] = useState(isUrl);
const [failed, setFailed] = useState(false); const [failed, setFailed] = useState(false);
const [msg, setMsg] = useState(''); const [msg, setMsg] = useState('');
const [width, setWidth] = useState<number>(); const [width, setWidth] = useState<number>();
const [height, setHeight] = useState<number>(); const [height, setHeight] = useState<number>();
const breakpoint = useBreakpoint(); // const breakpoint = useBreakpoint();
useLoadGist({ useLoadGist({
loading, loading,
@ -251,7 +254,7 @@ function InnerProposalView({
setMsg, setMsg,
setContent, setContent,
isGist, isGist,
timelockState, proposalState: proposalState,
}); });
return ( return (
@ -265,8 +268,8 @@ function InnerProposalView({
size={60} size={60}
/> />
<Col> <Col>
<h1>{timelockState.info.name}</h1> <h1>{proposalState.info.name}</h1>
<StateBadge state={timelockState} /> <StateBadge state={proposalState} />
</Col> </Col>
</Row> </Row>
</Col> </Col>
@ -274,36 +277,32 @@ function InnerProposalView({
<div className="proposal-actions"> <div className="proposal-actions">
{adminAccount && {adminAccount &&
adminAccount.info.amount.toNumber() === 1 && adminAccount.info.amount.toNumber() === 1 &&
timelockState.info.status === TimelockStateStatus.Draft && ( proposalState.info.status === ProposalStateStatus.Draft && (
<AddSigners proposal={proposal} state={timelockState} /> <AddSigners proposal={proposal} state={proposalState} />
)} )}
{sigAccount && {sigAccount &&
sigAccount.info.amount.toNumber() === 1 && sigAccount.info.amount.toNumber() === 1 &&
timelockState.info.status === TimelockStateStatus.Draft && ( proposalState.info.status === ProposalStateStatus.Draft && (
<SignButton proposal={proposal} state={timelockState} /> <SignButton proposal={proposal} state={proposalState} />
)} )}
<MintSourceTokens <MintSourceTokens
timelockConfig={timelockConfig} governance={governance}
useGovernance={ useGovernance={
proposal.info.sourceMint.toBase58() === proposal.info.sourceMint.toBase58() ===
timelockConfig.info.governanceMint.toBase58() governance.info.governanceMint.toBase58()
} }
/> />
<WithdrawVote <WithdrawVote proposal={proposal} state={proposalState} />
timelockConfig={timelockConfig}
proposal={proposal}
state={timelockState}
/>
<Vote <Vote
timelockConfig={timelockConfig} governance={governance}
proposal={proposal} proposal={proposal}
state={timelockState} state={proposalState}
yeahVote={true} yeahVote={true}
/> />
<Vote <Vote
timelockConfig={timelockConfig} governance={governance}
proposal={proposal} proposal={proposal}
state={timelockState} state={proposalState}
yeahVote={false} yeahVote={false}
/> />
</div> </div>
@ -367,10 +366,10 @@ function InnerProposalView({
<Statistic <Statistic
title={LABELS.SIG_GIVEN} title={LABELS.SIG_GIVEN}
value={ value={
timelockState.info.totalSigningTokensMinted.toNumber() - proposalState.info.totalSigningTokensMinted.toNumber() -
sigMint.supply.toNumber() sigMint.supply.toNumber()
} }
suffix={`/ ${timelockState.info.totalSigningTokensMinted.toNumber()}`} suffix={`/ ${proposalState.info.totalSigningTokensMinted.toNumber()}`}
/> />
</Card> </Card>
</Col> </Col>
@ -388,7 +387,7 @@ function InnerProposalView({
<Statistic <Statistic
valueStyle={{ color: 'green' }} valueStyle={{ color: 'green' }}
title={LABELS.VOTES_REQUIRED} title={LABELS.VOTES_REQUIRED}
value={getVotesRequired(timelockConfig, sourceMint)} value={getVotesRequired(governance, sourceMint)}
/> />
</Card> </Card>
</Col> </Col>
@ -409,7 +408,7 @@ function InnerProposalView({
<p> <p>
{LABELS.DESCRIPTION}:{' '} {LABELS.DESCRIPTION}:{' '}
<a <a
href={timelockState.info.descLink} href={proposalState.info.descLink}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
> >
@ -436,17 +435,17 @@ function InnerProposalView({
proposal={proposal} proposal={proposal}
position={position + 1} position={position + 1}
instruction={instruction} instruction={instruction}
state={timelockState} state={proposalState}
/> />
</Col> </Col>
))} ))}
{instructionsForProposal.length < INSTRUCTION_LIMIT && {instructionsForProposal.length < INSTRUCTION_LIMIT &&
timelockState.info.status === TimelockStateStatus.Draft && ( proposalState.info.status === ProposalStateStatus.Draft && (
<Col xs={24} sm={24} md={12} lg={8}> <Col xs={24} sm={24} md={12} lg={8}>
<NewInstructionCard <NewInstructionCard
proposal={proposal} proposal={proposal}
state={timelockState} state={proposalState}
config={timelockConfig} config={governance}
position={instructionsForProposal.length} position={instructionsForProposal.length}
/> />
</Col> </Col>
@ -462,13 +461,12 @@ function InnerProposalView({
} }
function getVotesRequired( function getVotesRequired(
timelockConfig: ParsedAccount<TimelockConfig>, governance: ParsedAccount<Governance>,
sourceMint: MintInfo, sourceMint: MintInfo,
): number { ): number {
return timelockConfig.info.voteThreshold === 100 return governance.info.voteThreshold === 100
? sourceMint.supply.toNumber() ? sourceMint.supply.toNumber()
: Math.ceil( : Math.ceil(
(timelockConfig.info.voteThreshold / 100) * (governance.info.voteThreshold / 100) * sourceMint.supply.toNumber(),
sourceMint.supply.toNumber(),
); );
} }

View File

@ -2,7 +2,7 @@ import React, { useState } from 'react';
import { Button, ButtonProps, Modal, Radio } from 'antd'; import { Button, ButtonProps, Modal, Radio } from 'antd';
import { Form, Input, Select } from 'antd'; import { Form, Input, Select } from 'antd';
import { Account } from '@solana/web3.js'; import { Account } from '@solana/web3.js';
import { DESC_SIZE, NAME_SIZE } from '../../models/timelock'; import { DESC_SIZE, NAME_SIZE } from '../../models/governance';
import { LABELS } from '../../constants'; import { LABELS } from '../../constants';
import { contexts, utils } from '@oyster/common'; import { contexts, utils } from '@oyster/common';
import { createProposal } from '../../actions/createProposal'; import { createProposal } from '../../actions/createProposal';
@ -74,9 +74,9 @@ export function NewForm({
name: string; name: string;
proposalMintType: string; proposalMintType: string;
description: string; description: string;
timelockConfigKey: string; governanceKey: string;
}) => { }) => {
const config = context.configs[values.timelockConfigKey]; const config = context.configs[values.governanceKey];
if ( if (
values.proposalMintType === ProposalMintType.Council && values.proposalMintType === ProposalMintType.Council &&
@ -132,7 +132,7 @@ export function NewForm({
<Input maxLength={DESC_SIZE} placeholder={LABELS.GIST_PLACEHOLDER} /> <Input maxLength={DESC_SIZE} placeholder={LABELS.GIST_PLACEHOLDER} />
</Form.Item> </Form.Item>
<Form.Item <Form.Item
name="timelockConfigKey" name="governanceKey"
label={LABELS.CONFIG} label={LABELS.CONFIG}
rules={[{ required: true }]} rules={[{ required: true }]}
> >

View File

@ -45,7 +45,7 @@ export const ProposalsView = () => {
}); });
}); });
return newListData; return newListData;
}, [proposals]); }, [proposals, id, mint, states]);
return ( return (
<Row <Row

View File

@ -3,6 +3,6 @@ import React from 'react';
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
const whyDidYouRender = require('@welldone-software/why-did-you-render'); const whyDidYouRender = require('@welldone-software/why-did-you-render');
whyDidYouRender(React, { whyDidYouRender(React, {
trackAllPureComponents: true, trackAllPureComponents: false,
}); });
} }