Merge branch 'feature/m' into feature/m-jordan
|
@ -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"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import {
|
import {
|
||||||
|
AccountInfo,
|
||||||
PublicKey,
|
PublicKey,
|
||||||
SystemProgram,
|
SystemProgram,
|
||||||
SYSVAR_CLOCK_PUBKEY,
|
SYSVAR_CLOCK_PUBKEY,
|
||||||
|
@ -9,6 +10,7 @@ import { programIds } from '../utils/ids';
|
||||||
import { deserializeBorsh } from './../utils/borsh';
|
import { deserializeBorsh } from './../utils/borsh';
|
||||||
import { serialize } from 'borsh';
|
import { serialize } from 'borsh';
|
||||||
import BN from 'bn.js';
|
import BN from 'bn.js';
|
||||||
|
import { AccountParser, cache } from '../contexts';
|
||||||
|
|
||||||
export const AUCTION_PREFIX = 'auction';
|
export const AUCTION_PREFIX = 'auction';
|
||||||
export const METADATA = 'metadata';
|
export const METADATA = 'metadata';
|
||||||
|
@ -59,14 +61,41 @@ export class BidState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const AuctionParser: AccountParser = (
|
||||||
|
pubkey: PublicKey,
|
||||||
|
account: AccountInfo<Buffer>,
|
||||||
|
) => ({
|
||||||
|
pubkey,
|
||||||
|
account,
|
||||||
|
info: decodeAuction(account.data),
|
||||||
|
});
|
||||||
|
|
||||||
export const decodeAuction = (buffer: Buffer) => {
|
export const decodeAuction = (buffer: Buffer) => {
|
||||||
return deserializeBorsh(AUCTION_SCHEMA, AuctionData, buffer) as AuctionData;
|
return deserializeBorsh(AUCTION_SCHEMA, AuctionData, buffer) as AuctionData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const BidderPotParser: AccountParser = (
|
||||||
|
pubkey: PublicKey,
|
||||||
|
account: AccountInfo<Buffer>,
|
||||||
|
) => ({
|
||||||
|
pubkey,
|
||||||
|
account,
|
||||||
|
info: decodeBidderPot(account.data),
|
||||||
|
});
|
||||||
|
|
||||||
export const decodeBidderPot = (buffer: Buffer) => {
|
export const decodeBidderPot = (buffer: Buffer) => {
|
||||||
return deserializeBorsh(AUCTION_SCHEMA, BidderPot, buffer) as BidderPot;
|
return deserializeBorsh(AUCTION_SCHEMA, BidderPot, buffer) as BidderPot;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const BidderMetadataParser: AccountParser = (
|
||||||
|
pubkey: PublicKey,
|
||||||
|
account: AccountInfo<Buffer>,
|
||||||
|
) => ({
|
||||||
|
pubkey,
|
||||||
|
account,
|
||||||
|
info: decodeBidderMetadata(account.data),
|
||||||
|
});
|
||||||
|
|
||||||
export const decodeBidderMetadata = (buffer: Buffer) => {
|
export const decodeBidderMetadata = (buffer: Buffer) => {
|
||||||
return deserializeBorsh(
|
return deserializeBorsh(
|
||||||
AUCTION_SCHEMA,
|
AUCTION_SCHEMA,
|
||||||
|
|
|
@ -425,26 +425,26 @@ export function AccountsProvider({ children = null as any }) {
|
||||||
// This can return different types of accounts: token-account, mint, multisig
|
// This can return different types of accounts: token-account, mint, multisig
|
||||||
// TODO: web3.js expose ability to filter.
|
// TODO: web3.js expose ability to filter.
|
||||||
// this should use only filter syntax to only get accounts that are owned by user
|
// this should use only filter syntax to only get accounts that are owned by user
|
||||||
// const tokenSubID = connection.onProgramAccountChange(
|
const tokenSubID = connection.onProgramAccountChange(
|
||||||
// programIds().token,
|
programIds().token,
|
||||||
// info => {
|
info => {
|
||||||
// // TODO: fix type in web3.js
|
// TODO: fix type in web3.js
|
||||||
// const id = (info.accountId as unknown) as string;
|
const id = (info.accountId as unknown) as string;
|
||||||
// // TODO: do we need a better way to identify layout (maybe a enum identifing type?)
|
// TODO: do we need a better way to identify layout (maybe a enum identifing type?)
|
||||||
// if (info.accountInfo.data.length === AccountLayout.span) {
|
if (info.accountInfo.data.length === AccountLayout.span) {
|
||||||
// const data = deserializeAccount(info.accountInfo.data);
|
const data = deserializeAccount(info.accountInfo.data);
|
||||||
|
|
||||||
// if (PRECACHED_OWNERS.has(data.owner.toBase58())) {
|
if (PRECACHED_OWNERS.has(data.owner.toBase58())) {
|
||||||
// cache.add(id, info.accountInfo, TokenAccountParser);
|
cache.add(id, info.accountInfo, TokenAccountParser);
|
||||||
// setTokenAccounts(selectUserAccounts());
|
setTokenAccounts(selectUserAccounts());
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
// },
|
},
|
||||||
// 'singleGossip',
|
'singleGossip',
|
||||||
// );
|
);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
// connection.removeProgramAccountChangeListener(tokenSubID);
|
connection.removeProgramAccountChangeListener(tokenSubID);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, [connection, connected, publicKey, selectUserAccounts]);
|
}, [connection, connected, publicKey, selectUserAccounts]);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
},
|
},
|
|
@ -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": {
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 216 KiB After Width: | Height: | Size: 216 KiB |
|
@ -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);
|
|
@ -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(
|
|
@ -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,
|
||||||
);
|
);
|
|
@ -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,
|
|
@ -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);
|
||||||
|
}
|
|
@ -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,
|
||||||
[],
|
[],
|
|
@ -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();
|
|
@ -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(
|
|
@ -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(
|
|
@ -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];
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 8.0 KiB |
|
@ -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
|
||||||
|
|
|
@ -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();
|
|
@ -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" />;
|
||||||
}
|
}
|
|
@ -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,
|
|
@ -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();
|
|
@ -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();
|
|
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
|
@ -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,
|
|
@ -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
|
|
@ -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,
|
|
@ -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',
|
|
@ -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);
|
|
@ -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
|
||||||
) {
|
) {
|
|
@ -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",
|
|
@ -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,
|
||||||
});
|
});
|
||||||
};
|
};
|
|
@ -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,
|
||||||
});
|
});
|
||||||
};
|
};
|
|
@ -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,
|
||||||
});
|
});
|
||||||
};
|
};
|
|
@ -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,
|
||||||
});
|
});
|
||||||
};
|
};
|
|
@ -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,
|
||||||
});
|
});
|
||||||
};
|
};
|
|
@ -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,
|
||||||
});
|
});
|
||||||
};
|
};
|
|
@ -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,
|
|
@ -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,
|
||||||
});
|
});
|
||||||
};
|
};
|
|
@ -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,
|
||||||
});
|
});
|
||||||
};
|
};
|
|
@ -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,
|
||||||
});
|
});
|
||||||
};
|
};
|
|
@ -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,
|
||||||
});
|
});
|
||||||
};
|
};
|
|
@ -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,
|
||||||
});
|
});
|
||||||
};
|
};
|
|
@ -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,
|
||||||
});
|
});
|
||||||
};
|
};
|
|
@ -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: [
|
|
@ -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();
|
|
@ -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);
|
|
@ -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 />;
|
|
@ -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} />;
|
||||||
};
|
};
|
|
@ -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
|
|
@ -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 (
|
||||||
<>
|
<>
|
|
@ -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(),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
|
@ -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 }]}
|
||||||
>
|
>
|
|
@ -45,7 +45,7 @@ export const ProposalsView = () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return newListData;
|
return newListData;
|
||||||
}, [proposals]);
|
}, [proposals, id, mint, states]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Row
|
<Row
|
|
@ -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,
|
||||||
});
|
});
|
||||||
}
|
}
|
After Width: | Height: | Size: 220 KiB |
|
@ -0,0 +1,102 @@
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
font-family: Graphik Web Regular;
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 2rem;
|
||||||
|
line-height: 40px;
|
||||||
|
|
||||||
|
color: #FFFFFF;
|
||||||
|
border: 1px solid #000000;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.subheader {
|
||||||
|
font-family: Graphik Web Regular;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 36px;
|
||||||
|
line-height: 48px;
|
||||||
|
|
||||||
|
color: #FFFFFF;
|
||||||
|
margin: 42px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text {
|
||||||
|
font-family: Graphik Web Regular;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 1.5;
|
||||||
|
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
width: 100%;
|
||||||
|
height: 100vw;
|
||||||
|
|
||||||
|
background: url(image.png);
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* button {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20px 10px;
|
||||||
|
|
||||||
|
position: static;
|
||||||
|
width: 350px;
|
||||||
|
height: 59px;
|
||||||
|
left: 0px;
|
||||||
|
top: 69px;
|
||||||
|
|
||||||
|
background: linear-gradient(270deg, #616774 7.29%, #403F4C 100%);
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
flex: none;
|
||||||
|
order: 1;
|
||||||
|
flex-grow: 0;
|
||||||
|
margin: 15px 0px;
|
||||||
|
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.email-input {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px 10px 16px 24px;
|
||||||
|
|
||||||
|
position: static;
|
||||||
|
width: 348px;
|
||||||
|
height: 54px;
|
||||||
|
left: 0px;
|
||||||
|
top: 0px;
|
||||||
|
|
||||||
|
background: #282828;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
flex: none;
|
||||||
|
order: 0;
|
||||||
|
flex-grow: 0;
|
||||||
|
margin: 15px 0px;
|
||||||
|
} */
|
||||||
|
|
||||||
|
.typeform-widget {
|
||||||
|
height: 360px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#form {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
margin-bottom: 50px;
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>Metaplex NFT Marketplace</title>
|
||||||
|
<meta content="width=device-width, initial-scale=1" name="viewport" />
|
||||||
|
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/css/bootstrap.min.css" rel="stylesheet"
|
||||||
|
integrity="sha384-eOJMYsd53ii+scO/bJGFsiCZc+5NDVN2yr8+0RDqr0Ql0h+rP48ckxlpbzKgwra6" crossorigin="anonymous">
|
||||||
|
<link href="//db.onlinewebfonts.com/c/105007d99d9df64c50cc24d696d79555?family=Graphik+Web+Regular" rel="stylesheet"
|
||||||
|
type="text/css" />
|
||||||
|
<link rel="stylesheet" href="landing.css">
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/js/bootstrap.bundle.min.js"
|
||||||
|
integrity="sha384-JEW9xMcG8R+pH31jmWH6WWP0WintQrMb4s7ZOdauHnUtxwoG2vI5DkLtS3qm9Ekf"
|
||||||
|
crossorigin="anonymous"></script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header my-3 my-md-5">METAPLEX</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<div>
|
||||||
|
<div class="subheader">NFT platforms. Push to start.</div>
|
||||||
|
<div class="text">
|
||||||
|
Metaplex is an open source NFT framework built on Solana that puts creators and artists in the drivers
|
||||||
|
seat.
|
||||||
|
<br />
|
||||||
|
<br />
|
||||||
|
With Metaplex, you can launch an NFT marketplace in minutes.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<!-- <input class="email-input" placeholder="Email Address"/> -->
|
||||||
|
<!-- <button>SIGN UP FOR EARLY ACCESS</button> -->
|
||||||
|
<div id="form" data-tf-widget="gZ8mozPq"></div>
|
||||||
|
<script src="//embed.typeform.com/next/embed.js"></script>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6">
|
||||||
|
<div class="image mx-auto"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
|
@ -25,6 +25,15 @@ body {
|
||||||
.App {
|
.App {
|
||||||
background: #101010 !important;
|
background: #101010 !important;
|
||||||
padding: 0px 30px;
|
padding: 0px 30px;
|
||||||
|
|
||||||
|
> section {
|
||||||
|
max-width: 1280px;
|
||||||
|
|
||||||
|
@media (min-width: 600px) {
|
||||||
|
padding-left: 24px;
|
||||||
|
padding-right: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
|
|
|
@ -151,7 +151,7 @@ export const mintNFT = async (
|
||||||
|
|
||||||
// Force wait for max confirmations
|
// Force wait for max confirmations
|
||||||
// await connection.confirmTransaction(txid, 'max');
|
// await connection.confirmTransaction(txid, 'max');
|
||||||
await connection.getParsedConfirmedTransaction(txid);
|
await connection.getParsedConfirmedTransaction(txid, 'confirmed');
|
||||||
|
|
||||||
// this means we're done getting AR txn setup. Ship it off to ARWeave!
|
// this means we're done getting AR txn setup. Ship it off to ARWeave!
|
||||||
const data = new FormData();
|
const data = new FormData();
|
||||||
|
|
|
@ -12,6 +12,11 @@ import {
|
||||||
useUserAccounts,
|
useUserAccounts,
|
||||||
hooks,
|
hooks,
|
||||||
contexts,
|
contexts,
|
||||||
|
cache,
|
||||||
|
BidderMetadataParser,
|
||||||
|
BidderMetadata,
|
||||||
|
ParsedAccount,
|
||||||
|
Identicon,
|
||||||
} from '@oyster/common';
|
} from '@oyster/common';
|
||||||
import { AuctionView, AuctionViewState } from '../../hooks';
|
import { AuctionView, AuctionViewState } from '../../hooks';
|
||||||
import { sendPlaceBid } from '../../actions/sendPlaceBid';
|
import { sendPlaceBid } from '../../actions/sendPlaceBid';
|
||||||
|
@ -91,6 +96,7 @@ export const AuctionCard = ({ auctionView }: { auctionView: AuctionView }) => {
|
||||||
autoFocus
|
autoFocus
|
||||||
className="input"
|
className="input"
|
||||||
value={value}
|
value={value}
|
||||||
|
style={{ width: '100%', backgroundColor: 'black', marginTop: 20 }}
|
||||||
onChange={setValue}
|
onChange={setValue}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -138,18 +144,33 @@ export const AuctionCard = ({ auctionView }: { auctionView: AuctionView }) => {
|
||||||
PLACE BID
|
PLACE BID
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
<AuctionBids view={auctionView} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AuctionBidders = (auctionID: string) => {
|
export const AuctionBids = ({view}: {view : AuctionView}) => {
|
||||||
const bids: any[] = [];
|
const bids = cache.byParser(BidderMetadataParser).filter(id => {
|
||||||
|
const bidder = cache.get(id) as ParsedAccount<BidderMetadata>;
|
||||||
|
if(!bidder) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bidder.info.auctionPubkey.toBase58() === view.auction.pubkey.toBase58();
|
||||||
|
}).map(id => {
|
||||||
|
const bidder = cache.get(id) as ParsedAccount<BidderMetadata>;
|
||||||
|
return bidder;
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Col>
|
<Col style={{ width: '100%' }}>
|
||||||
{bids.map((bid, index) => {
|
{bids.map((bid, index) => {
|
||||||
|
const bidder = bid.info.bidderPubkey.toBase58();
|
||||||
return (
|
return (
|
||||||
<Row>
|
<Row>
|
||||||
{index + 1}. {shortenAddress(bid.address)} {bid.amount}
|
<Col span={1}>{index + 1}.</Col>
|
||||||
|
<Col span={17}><Row><Identicon style={{ width: 24, height: 24, marginRight: 10, marginTop: 2 }} address={bidder} /> {shortenAddress(bidder)}</Row></Col>
|
||||||
|
<Col span={5} style={{ textAlign: 'right' }}>{bid.info.lastBid.toString()}</Col>
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {
|
||||||
useConnection,
|
useConnection,
|
||||||
decodeMetadata,
|
decodeMetadata,
|
||||||
decodeNameSymbolTuple,
|
decodeNameSymbolTuple,
|
||||||
decodeAuction,
|
AuctionParser,
|
||||||
decodeEdition,
|
decodeEdition,
|
||||||
decodeMasterEdition,
|
decodeMasterEdition,
|
||||||
Metadata,
|
Metadata,
|
||||||
|
@ -21,9 +21,9 @@ import {
|
||||||
VaultKey,
|
VaultKey,
|
||||||
decodeSafetyDeposit,
|
decodeSafetyDeposit,
|
||||||
BidderMetadata,
|
BidderMetadata,
|
||||||
decodeBidderMetadata,
|
BidderMetadataParser,
|
||||||
BidderPot,
|
BidderPot,
|
||||||
decodeBidderPot,
|
BidderPotParser,
|
||||||
BIDDER_METADATA_LEN,
|
BIDDER_METADATA_LEN,
|
||||||
BIDDER_POT_LEN,
|
BIDDER_POT_LEN,
|
||||||
decodeVault,
|
decodeVault,
|
||||||
|
@ -116,6 +116,7 @@ export function MetaProvider({ children = null as any }) {
|
||||||
const [vaults, setVaults] = useState<Record<string, ParsedAccount<Vault>>>(
|
const [vaults, setVaults] = useState<Record<string, ParsedAccount<Vault>>>(
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
|
|
||||||
const [
|
const [
|
||||||
bidderMetadataByAuctionAndBidder,
|
bidderMetadataByAuctionAndBidder,
|
||||||
setBidderMetadataByAuctionAndBidder,
|
setBidderMetadataByAuctionAndBidder,
|
||||||
|
@ -134,21 +135,21 @@ export function MetaProvider({ children = null as any }) {
|
||||||
(async () => {
|
(async () => {
|
||||||
const processAuctions = async (a: PublicKeyAndAccount<Buffer>) => {
|
const processAuctions = async (a: PublicKeyAndAccount<Buffer>) => {
|
||||||
try {
|
try {
|
||||||
const auction = await decodeAuction(a.account.data);
|
const account = cache.add(
|
||||||
auction.auctionManagerKey = await getAuctionManagerKey(
|
a.pubkey,
|
||||||
auction.resource,
|
a.account,
|
||||||
|
AuctionParser) as
|
||||||
|
ParsedAccount<AuctionData>;
|
||||||
|
|
||||||
|
account.info.auctionManagerKey = await getAuctionManagerKey(
|
||||||
|
account.info.resource,
|
||||||
a.pubkey,
|
a.pubkey,
|
||||||
);
|
);
|
||||||
const payerAcct = accountByMint.get(auction.tokenMint.toBase58());
|
const payerAcct = accountByMint.get(account.info.tokenMint.toBase58());
|
||||||
if (payerAcct)
|
if (payerAcct)
|
||||||
auction.bidRedemptionKey = (
|
account.info.bidRedemptionKey = (
|
||||||
await getBidderKeys(a.pubkey, payerAcct.pubkey)
|
await getBidderKeys(a.pubkey, payerAcct.pubkey)
|
||||||
).bidRedemption;
|
).bidRedemption;
|
||||||
const account: ParsedAccount<AuctionData> = {
|
|
||||||
pubkey: a.pubkey,
|
|
||||||
account: a.account,
|
|
||||||
info: auction,
|
|
||||||
};
|
|
||||||
setAuctions(e => ({
|
setAuctions(e => ({
|
||||||
...e,
|
...e,
|
||||||
[a.pubkey.toBase58()]: account,
|
[a.pubkey.toBase58()]: account,
|
||||||
|
@ -166,18 +167,17 @@ export function MetaProvider({ children = null as any }) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (a.account.data.length == BIDDER_METADATA_LEN) {
|
if (a.account.data.length == BIDDER_METADATA_LEN) {
|
||||||
const bidderMetadata = await decodeBidderMetadata(a.account.data);
|
const account = cache.add(
|
||||||
|
a.pubkey,
|
||||||
|
a.account,
|
||||||
|
BidderMetadataParser) as
|
||||||
|
ParsedAccount<BidderMetadata>;
|
||||||
|
|
||||||
const account: ParsedAccount<BidderMetadata> = {
|
|
||||||
pubkey: a.pubkey,
|
|
||||||
account: a.account,
|
|
||||||
info: bidderMetadata,
|
|
||||||
};
|
|
||||||
setBidderMetadataByAuctionAndBidder(e => ({
|
setBidderMetadataByAuctionAndBidder(e => ({
|
||||||
...e,
|
...e,
|
||||||
[bidderMetadata.auctionPubkey.toBase58() +
|
[account.info.auctionPubkey.toBase58() +
|
||||||
'-' +
|
'-' +
|
||||||
bidderMetadata.bidderPubkey.toBase58()]: account,
|
account.info.bidderPubkey.toBase58()]: account,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
@ -186,17 +186,17 @@ export function MetaProvider({ children = null as any }) {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (a.account.data.length == BIDDER_POT_LEN) {
|
if (a.account.data.length == BIDDER_POT_LEN) {
|
||||||
const bidderPot = await decodeBidderPot(a.account.data);
|
const account = cache.add(
|
||||||
const account: ParsedAccount<BidderPot> = {
|
a.pubkey,
|
||||||
pubkey: a.pubkey,
|
a.account,
|
||||||
account: a.account,
|
BidderPotParser) as
|
||||||
info: bidderPot,
|
ParsedAccount<BidderPot>;
|
||||||
};
|
|
||||||
setBidderPotsByAuctionAndBidder(e => ({
|
setBidderPotsByAuctionAndBidder(e => ({
|
||||||
...e,
|
...e,
|
||||||
[bidderPot.auctionAct.toBase58() +
|
[account.info.auctionAct.toBase58() +
|
||||||
'-' +
|
'-' +
|
||||||
bidderPot.bidderAct.toBase58()]: account,
|
account.info.bidderAct.toBase58()]: account,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
|
|
@ -53,6 +53,7 @@ import {
|
||||||
WinningConstraint,
|
WinningConstraint,
|
||||||
} from '../../models/metaplex';
|
} from '../../models/metaplex';
|
||||||
import { serialize } from 'borsh';
|
import { serialize } from 'borsh';
|
||||||
|
import moment from 'moment'
|
||||||
import {
|
import {
|
||||||
createAuctionManager,
|
createAuctionManager,
|
||||||
SafetyDepositDraft,
|
SafetyDepositDraft,
|
||||||
|
@ -94,7 +95,8 @@ export interface AuctionState {
|
||||||
// suggested date time when auction should end UTC+0
|
// suggested date time when auction should end UTC+0
|
||||||
endDate?: Date;
|
endDate?: Date;
|
||||||
|
|
||||||
// Jose's attributes
|
|
||||||
|
//////////////////
|
||||||
category: AuctionCategory;
|
category: AuctionCategory;
|
||||||
saleType?: 'auction' | 'sale';
|
saleType?: 'auction' | 'sale';
|
||||||
|
|
||||||
|
@ -530,12 +532,12 @@ const CopiesStep = (props: {
|
||||||
tiers:
|
tiers:
|
||||||
!props.attributes.tiers || props.attributes.tiers?.length == 0
|
!props.attributes.tiers || props.attributes.tiers?.length == 0
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
to: 0,
|
to: 0,
|
||||||
name: 'Default Tier',
|
name: 'Default Tier',
|
||||||
items: props.attributes.items,
|
items: props.attributes.items,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: props.attributes.tiers,
|
: props.attributes.tiers,
|
||||||
});
|
});
|
||||||
props.confirm();
|
props.confirm();
|
||||||
|
@ -603,28 +605,39 @@ const TierWinners = (props: {
|
||||||
tier: Tier;
|
tier: Tier;
|
||||||
setTier: Function;
|
setTier: Function;
|
||||||
previousTo?: number;
|
previousTo?: number;
|
||||||
|
lastTier?: Tier;
|
||||||
}) => {
|
}) => {
|
||||||
|
const from = (props.previousTo || 0) + 1
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Divider />
|
<Divider />
|
||||||
<label className="action-field">
|
<label className="action-field">
|
||||||
<span className="field-title">Winners</span>
|
<span className="field-title">Winners</span>
|
||||||
<div>
|
<div>
|
||||||
<Input
|
<InputNumber
|
||||||
disabled={props.idx === 0}
|
disabled={true}
|
||||||
defaultValue={(props.previousTo || 0) + 1}
|
value={from}
|
||||||
type="number"
|
type="number"
|
||||||
className="input"
|
className="input"
|
||||||
style={{ width: '30%' }}
|
style={{ width: '30%' }}
|
||||||
onChange={info => null}
|
onChange={value =>
|
||||||
|
null
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
to
|
to
|
||||||
<Input
|
<InputNumber
|
||||||
|
min={from + 1}
|
||||||
|
disabled={props.lastTier?.to == props.tier.to}
|
||||||
defaultValue={props.tier.to}
|
defaultValue={props.tier.to}
|
||||||
type="number"
|
type="number"
|
||||||
className="input"
|
className="input"
|
||||||
style={{ width: '30%' }}
|
style={{ width: '30%' }}
|
||||||
onChange={info => null}
|
onChange={value =>
|
||||||
|
props.setTier(props.idx, {
|
||||||
|
...props.tier,
|
||||||
|
to: value || props.tier.to,
|
||||||
|
})
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
@ -658,7 +671,7 @@ const TierWinners = (props: {
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<ArtSelector selected={[]} setSelected={_ => {}} allowMultiple={true} />
|
<ArtSelector selected={[]} setSelected={_ => { }} allowMultiple={true} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -707,7 +720,7 @@ const TierStep = (props: {
|
||||||
(_, idx) => ({
|
(_, idx) => ({
|
||||||
to: Math.trunc(
|
to: Math.trunc(
|
||||||
((idx + 1) * (props.attributes.spots as number)) /
|
((idx + 1) * (props.attributes.spots as number)) /
|
||||||
parseInt(info.target.value),
|
parseInt(info.target.value),
|
||||||
),
|
),
|
||||||
name: '',
|
name: '',
|
||||||
description: '',
|
description: '',
|
||||||
|
@ -726,8 +739,10 @@ const TierStep = (props: {
|
||||||
tier={tier}
|
tier={tier}
|
||||||
setTier={setTier}
|
setTier={setTier}
|
||||||
previousTo={tiers[idx - 1]?.to}
|
previousTo={tiers[idx - 1]?.to}
|
||||||
|
lastTier={tiers.slice(-1)[0]}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<Row>
|
<Row>
|
||||||
|
@ -907,7 +922,6 @@ const PriceAuction = (props: {
|
||||||
<Input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
min={0}
|
min={0}
|
||||||
autoFocus
|
|
||||||
className="input"
|
className="input"
|
||||||
placeholder="Tick size in USD"
|
placeholder="Tick size in USD"
|
||||||
prefix="$"
|
prefix="$"
|
||||||
|
@ -945,6 +959,31 @@ const InitialPhaseStep = (props: {
|
||||||
const [startNow, setStartNow] = useState<boolean>(true);
|
const [startNow, setStartNow] = useState<boolean>(true);
|
||||||
const [listNow, setListNow] = useState<boolean>(true);
|
const [listNow, setListNow] = useState<boolean>(true);
|
||||||
|
|
||||||
|
const [saleMoment, setSaleMoment] = useState<moment.Moment | undefined>(props.attributes.startSaleTS ? moment.unix(props.attributes.startSaleTS) : undefined)
|
||||||
|
const [listMoment, setListMoment] = useState<moment.Moment | undefined>(props.attributes.startListTS ? moment.unix(props.attributes.startListTS) : undefined)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
props.setAttributes({
|
||||||
|
...props.attributes,
|
||||||
|
startSaleTS: saleMoment && saleMoment.unix() * 1000
|
||||||
|
})
|
||||||
|
}, [saleMoment])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
props.setAttributes({
|
||||||
|
...props.attributes,
|
||||||
|
startListTS: listMoment && listMoment.unix() * 1000
|
||||||
|
})
|
||||||
|
}, [listMoment])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (startNow) setSaleMoment(moment())
|
||||||
|
}, [startNow])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (listNow) setListMoment(moment())
|
||||||
|
}, [listNow])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Row className="call-to-action">
|
<Row className="call-to-action">
|
||||||
|
@ -1108,7 +1147,6 @@ const EndingPhaseAuction = (props: {
|
||||||
</span>
|
</span>
|
||||||
<Input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
autoFocus
|
|
||||||
className="input"
|
className="input"
|
||||||
placeholder="Duration in minutes"
|
placeholder="Duration in minutes"
|
||||||
suffix="minutes"
|
suffix="minutes"
|
||||||
|
@ -1130,7 +1168,6 @@ const EndingPhaseAuction = (props: {
|
||||||
</span>
|
</span>
|
||||||
<Input
|
<Input
|
||||||
type="number"
|
type="number"
|
||||||
autoFocus
|
|
||||||
className="input"
|
className="input"
|
||||||
placeholder="Percentage"
|
placeholder="Percentage"
|
||||||
suffix="%"
|
suffix="%"
|
||||||
|
@ -1164,6 +1201,18 @@ const EndingPhaseSale = (props: {
|
||||||
confirm: () => void;
|
confirm: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const [untilSold, setUntilSold] = useState<boolean>(true);
|
const [untilSold, setUntilSold] = useState<boolean>(true);
|
||||||
|
const [endMoment, setEndMoment] = useState<moment.Moment | undefined>(props.attributes.endTS ? moment.unix(props.attributes.endTS) : undefined)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
props.setAttributes({
|
||||||
|
...props.attributes,
|
||||||
|
endTS: endMoment && endMoment.unix() * 1000
|
||||||
|
})
|
||||||
|
}, [endMoment])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (untilSold) setEndMoment(undefined)
|
||||||
|
}, [untilSold])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -1203,12 +1252,33 @@ const EndingPhaseSale = (props: {
|
||||||
<DatePicker
|
<DatePicker
|
||||||
className="field-date"
|
className="field-date"
|
||||||
size="large"
|
size="large"
|
||||||
onChange={(dt, dtString) => console.log(dt?.unix())}
|
disabledDate={current => current && current < moment().endOf('day')}
|
||||||
|
value={endMoment}
|
||||||
|
onChange={value => {
|
||||||
|
if (!value) return
|
||||||
|
if (!endMoment) return setEndMoment(value)
|
||||||
|
|
||||||
|
const currentMoment = endMoment.clone()
|
||||||
|
currentMoment.hour(value.hour())
|
||||||
|
currentMoment.minute(value.minute())
|
||||||
|
currentMoment.second(value.second())
|
||||||
|
setEndMoment(currentMoment)
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
<TimePicker
|
<TimePicker
|
||||||
className="field-date"
|
className="field-date"
|
||||||
size="large"
|
size="large"
|
||||||
onChange={(dt, dtString) => console.log(dt?.unix())}
|
value={endMoment}
|
||||||
|
onChange={value => {
|
||||||
|
if (!value) return
|
||||||
|
if (!endMoment) return setEndMoment(value)
|
||||||
|
|
||||||
|
const currentMoment = endMoment.clone()
|
||||||
|
currentMoment.hour(value.hour())
|
||||||
|
currentMoment.minute(value.minute())
|
||||||
|
currentMoment.second(value.second())
|
||||||
|
setEndMoment(currentMoment)
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
)}
|
)}
|
||||||
|
@ -1246,7 +1316,7 @@ const ParticipationStep = (props: {
|
||||||
<Col className="section" xl={24}>
|
<Col className="section" xl={24}>
|
||||||
<ArtSelector
|
<ArtSelector
|
||||||
selected={[]}
|
selected={[]}
|
||||||
setSelected={() => {}}
|
setSelected={() => { }}
|
||||||
allowMultiple={false}
|
allowMultiple={false}
|
||||||
>
|
>
|
||||||
Select NFT
|
Select NFT
|
||||||
|
|