Merge branch 'main' into feature/m

This commit is contained in:
bartosz-lipinski 2021-04-16 23:37:26 -05:00
commit 37656808e0
25 changed files with 142 additions and 82 deletions

View File

@ -6,7 +6,7 @@ header.ant-layout-header.App-Bar {
width: 100%; width: 100%;
background: transparent; background: transparent;
display: flex; display: flex;
justify-content: center; justify-content: center !important;
height: 80px; height: 80px;
.nav-burger { .nav-burger {
@ -30,6 +30,9 @@ header.ant-layout-header.App-Bar {
justify-content: center; justify-content: center;
height: auto; height: auto;
align-items: center; align-items: center;
button.app-bar-item {
padding: 0;
}
.app-bar-item { .app-bar-item {
cursor: pointer; cursor: pointer;
padding: 0 30px; padding: 0 30px;

View File

@ -0,0 +1,32 @@
import React from 'react';
import { useWallet, WALLET_PROVIDERS } from '@oyster/common';
import { shortenAddress } from '@oyster/common';
export const CurrentUserWalletBadge = (props: { showDisconnect?: boolean }) => {
const { wallet, disconnect } = useWallet();
if (!wallet || !wallet.publicKey) {
return null;
}
return (
<div className="wallet-wrapper">
<div className="wallet-key">
<img
alt={'icon'}
width={20}
height={20}
src={WALLET_PROVIDERS.filter(p => p.name === 'Sollet')[0]?.icon}
style={{ marginRight: 8 }}
/>
{shortenAddress(`${wallet.publicKey}`)}
{props.showDisconnect && (
<span className={'disconnect'} onClick={() => disconnect()}>
X
</span>
)}
</div>
</div>
);
};

View File

@ -13,6 +13,7 @@ import { TokenSelectModal } from '../TokenSelectModal';
import { chainToName } from '../../utils/assets'; import { chainToName } from '../../utils/assets';
import { TokenChain } from '../TokenDisplay/tokenChain'; import { TokenChain } from '../TokenDisplay/tokenChain';
import { EthereumConnect } from '../EthereumConnect'; import { EthereumConnect } from '../EthereumConnect';
import { CurrentUserWalletBadge } from '../CurrentUserWalletBadge';
export function Input(props: { export function Input(props: {
title: string; title: string;
@ -106,7 +107,10 @@ export function Input(props: {
</div> </div>
{props.chain === ASSET_CHAIN.Ethereum ? ( {props.chain === ASSET_CHAIN.Ethereum ? (
<EthereumConnect /> <EthereumConnect />
) : (<ConnectButton type="text" size="large" allowWalletChange={true} /> ) : connected ? (
<CurrentUserWalletBadge showDisconnect={true} />
) : (
<ConnectButton type="text" size="large" allowWalletChange={true} />
)} )}
</div> </div>
); );

View File

@ -145,7 +145,11 @@ export const RecentTransactionsTable = () => {
render(text: string, record: any) { render(text: string, record: any) {
return { return {
props: { style: {} }, props: { style: {} },
children: <span className={`${record.status?.toLowerCase()}`}>{record.status}</span>, children: (
<span className={`${record.status?.toLowerCase()}`}>
{record.status}
</span>
),
}; };
}, },
}, },

View File

@ -65,10 +65,7 @@ const queryWrappedMetaTransactions = async (
}, },
]; ];
let wh = WormholeFactory.connect( let wh = WormholeFactory.connect(programIds().wormhole.bridge, provider);
programIds().wormhole.bridge,
provider,
);
const resp = await (connection as any)._rpcRequest('getProgramAccounts', [ const resp = await (connection as any)._rpcRequest('getProgramAccounts', [
WORMHOLE_PROGRAM_ID.toBase58(), WORMHOLE_PROGRAM_ID.toBase58(),
{ {
@ -227,7 +224,6 @@ export const useWormholeTransactions = () => {
bridge, bridge,
).then(() => setLoading(false)); ).then(() => setLoading(false));
})(); })();
}, [connection, setTransfers]); }, [connection, setTransfers]);
const coingeckoTimer = useRef<number>(0); const coingeckoTimer = useRef<number>(0);

View File

@ -158,9 +158,7 @@ export const HomeView = () => {
</div> </div>
<div className={'description-title'}>Bridge in any direction</div> <div className={'description-title'}>Bridge in any direction</div>
<div className={'description-text'}> <div className={'description-text'}>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis
nisi at praesent sed sollicitudin ullamcorper malesuada in.
Molestie sed morbi vitae in amet ultrices.
</div> </div>
</div> </div>
<div className={'home-description-item'}> <div className={'home-description-item'}>
@ -169,9 +167,7 @@ export const HomeView = () => {
</div> </div>
<div className={'description-title'}>Staking & Validation</div> <div className={'description-title'}>Staking & Validation</div>
<div className={'description-text'}> <div className={'description-text'}>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis
nisi at praesent sed sollicitudin ullamcorper malesuada in.
Molestie sed morbi vitae in amet ultrices.
</div> </div>
</div> </div>
<div className={'home-description-item'}> <div className={'home-description-item'}>
@ -180,9 +176,7 @@ export const HomeView = () => {
</div> </div>
<div className={'description-title'}>Layers and Capabilities</div> <div className={'description-title'}>Layers and Capabilities</div>
<div className={'description-text'}> <div className={'description-text'}>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis
nisi at praesent sed sollicitudin ullamcorper malesuada in.
Molestie sed morbi vitae in amet ultrices.
</div> </div>
</div> </div>
</div> </div>

View File

@ -65,27 +65,17 @@ export function approve(
const transferAuthority = existingTransferAuthority || new Account(); const transferAuthority = existingTransferAuthority || new Account();
const delegateKey = delegate ?? transferAuthority.publicKey; const delegateKey = delegate ?? transferAuthority.publicKey;
const instruction = Token.createApproveInstruction( instructions.push(
tokenProgram, Token.createApproveInstruction(
account, tokenProgram,
delegateKey, account,
owner, delegate ?? transferAuthority.publicKey,
[], owner,
amount, [],
amount,
),
); );
// Temp. workaround for a bug in Token.createApproveInstruction which doesn't add the delegate account to signers
instruction.keys = instruction.keys.map(k =>
k.pubkey.equals(delegateKey)
? {
...k,
isSigner: true,
}
: k,
);
instructions.push(instruction);
if (autoRevoke) { if (autoRevoke) {
cleanupInstructions.push( cleanupInstructions.push(
Token.createRevokeInstruction(tokenProgram, account, owner, []), Token.createRevokeInstruction(tokenProgram, account, owner, []),

View File

@ -8,6 +8,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 { import {
GOVERNANCE_AUTHORITY_SEED,
CustomSingleSignerTimelockTransactionLayout, CustomSingleSignerTimelockTransactionLayout,
TimelockSet, TimelockSet,
TimelockState, TimelockState,
@ -50,7 +51,7 @@ export const addCustomSingleSignerTransaction = async (
}); });
const [authority] = await PublicKey.findProgramAddress( const [authority] = await PublicKey.findProgramAddress(
[proposal.pubkey.toBuffer()], [Buffer.from(GOVERNANCE_AUTHORITY_SEED), proposal.pubkey.toBuffer()],
PROGRAM_IDS.timelock.programId, PROGRAM_IDS.timelock.programId,
); );

View File

@ -12,7 +12,11 @@ import {
actions, actions,
} from '@oyster/common'; } from '@oyster/common';
import { TimelockSet, TimelockState } from '../models/timelock'; import {
GOVERNANCE_AUTHORITY_SEED,
TimelockSet,
TimelockState,
} from '../models/timelock';
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;
@ -47,7 +51,7 @@ export const addSigner = async (
); );
const [mintAuthority] = await PublicKey.findProgramAddress( const [mintAuthority] = await PublicKey.findProgramAddress(
[proposal.pubkey.toBuffer()], [Buffer.from(GOVERNANCE_AUTHORITY_SEED), proposal.pubkey.toBuffer()],
PROGRAM_IDS.timelock.programId, PROGRAM_IDS.timelock.programId,
); );

View File

@ -10,6 +10,7 @@ import { contexts, utils, actions, ParsedAccount } from '@oyster/common';
import { AccountLayout, MintLayout } from '@solana/spl-token'; import { AccountLayout, MintLayout } from '@solana/spl-token';
import { initTimelockSetInstruction } from '../models/initTimelockSet'; import { initTimelockSetInstruction } from '../models/initTimelockSet';
import { import {
GOVERNANCE_AUTHORITY_SEED,
TimelockConfig, TimelockConfig,
TimelockSetLayout, TimelockSetLayout,
TimelockStateLayout, TimelockStateLayout,
@ -45,7 +46,7 @@ export const createProposal = async (
connection, connection,
useGovernance useGovernance
? timelockConfig.info.governanceMint ? timelockConfig.info.governanceMint
: timelockConfig.info.councilMint, : timelockConfig.info.councilMint!,
) )
).decimals; ).decimals;
@ -135,7 +136,7 @@ export const createProposal = async (
sourceHoldingAccount, sourceHoldingAccount,
useGovernance useGovernance
? timelockConfig.info.governanceMint ? timelockConfig.info.governanceMint
: timelockConfig.info.councilMint, : timelockConfig.info.councilMint!,
authority, authority,
description, description,
name, name,
@ -205,7 +206,10 @@ async function getAssociatedAccountsAndInstructions(
const PROGRAM_IDS = utils.programIds(); const PROGRAM_IDS = utils.programIds();
const [authority] = await PublicKey.findProgramAddress( const [authority] = await PublicKey.findProgramAddress(
[newProposalKey.publicKey.toBuffer()], [
Buffer.from(GOVERNANCE_AUTHORITY_SEED),
newProposalKey.publicKey.toBuffer(),
],
PROGRAM_IDS.timelock.programId, PROGRAM_IDS.timelock.programId,
); );
@ -342,7 +346,7 @@ async function getAssociatedAccountsAndInstructions(
accountRentExempt, accountRentExempt,
useGovernance useGovernance
? timelockConfig.info.governanceMint ? timelockConfig.info.governanceMint
: timelockConfig.info.councilMint, : timelockConfig.info.councilMint!,
authority, authority,
holdingSigners, holdingSigners,
); );

View File

@ -12,7 +12,12 @@ import {
actions, actions,
} from '@oyster/common'; } from '@oyster/common';
import { TimelockConfig, TimelockSet, TimelockState } from '../models/timelock'; import {
GOVERNANCE_AUTHORITY_SEED,
TimelockConfig,
TimelockSet,
TimelockState,
} from '../models/timelock';
import { AccountLayout } from '@solana/spl-token'; import { AccountLayout } from '@solana/spl-token';
@ -66,6 +71,7 @@ export const depositSourceTokensAndVote = async (
const [governanceVotingRecord] = await PublicKey.findProgramAddress( const [governanceVotingRecord] = await PublicKey.findProgramAddress(
[ [
Buffer.from(GOVERNANCE_AUTHORITY_SEED),
PROGRAM_IDS.timelock.programId.toBuffer(), PROGRAM_IDS.timelock.programId.toBuffer(),
proposal.pubkey.toBuffer(), proposal.pubkey.toBuffer(),
existingVoteAccount.toBuffer(), existingVoteAccount.toBuffer(),
@ -107,7 +113,7 @@ export const depositSourceTokensAndVote = async (
} }
const [mintAuthority] = await PublicKey.findProgramAddress( const [mintAuthority] = await PublicKey.findProgramAddress(
[proposal.pubkey.toBuffer()], [Buffer.from(GOVERNANCE_AUTHORITY_SEED), proposal.pubkey.toBuffer()],
PROGRAM_IDS.timelock.programId, PROGRAM_IDS.timelock.programId,
); );

View File

@ -45,7 +45,7 @@ export const mintSourceTokens = async (
accountRentExempt, accountRentExempt,
useGovernance useGovernance
? timelockConfig.info.governanceMint ? timelockConfig.info.governanceMint
: timelockConfig.info.councilMint, : timelockConfig.info.councilMint!,
e.owner, e.owner,
signers, signers,
); );
@ -55,7 +55,7 @@ export const mintSourceTokens = async (
PROGRAM_IDS.token, PROGRAM_IDS.token,
useGovernance useGovernance
? timelockConfig.info.governanceMint ? timelockConfig.info.governanceMint
: timelockConfig.info.councilMint, : timelockConfig.info.councilMint!,
e.sourceAccount, e.sourceAccount,
wallet.publicKey, wallet.publicKey,
[], [],

View File

@ -8,6 +8,7 @@ import { contexts, utils, actions } from '@oyster/common';
import { AccountLayout, MintLayout, Token } from '@solana/spl-token'; import { AccountLayout, MintLayout, Token } from '@solana/spl-token';
import { import {
GOVERNANCE_AUTHORITY_SEED,
ConsensusAlgorithm, ConsensusAlgorithm,
ExecutionType, ExecutionType,
TimelockConfig, TimelockConfig,
@ -26,6 +27,7 @@ export const registerProgramGovernance = async (
connection: Connection, connection: Connection,
wallet: any, wallet: any,
uninitializedTimelockConfig: Partial<TimelockConfig>, uninitializedTimelockConfig: Partial<TimelockConfig>,
useCouncil: boolean,
): Promise<PublicKey> => { ): Promise<PublicKey> => {
const PROGRAM_IDS = utils.programIds(); const PROGRAM_IDS = utils.programIds();
let signers: Account[] = []; let signers: Account[] = [];
@ -43,7 +45,7 @@ export const registerProgramGovernance = async (
if (!uninitializedTimelockConfig.program) if (!uninitializedTimelockConfig.program)
uninitializedTimelockConfig.program = new Account().publicKey; // Random generation if none given uninitializedTimelockConfig.program = new Account().publicKey; // Random generation if none given
if (!uninitializedTimelockConfig.councilMint) { if (!uninitializedTimelockConfig.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( uninitializedTimelockConfig.councilMint = createMint(
@ -111,11 +113,16 @@ export const registerProgramGovernance = async (
); );
} }
const councilMintSeed = uninitializedTimelockConfig.councilMint
? uninitializedTimelockConfig.councilMint.toBuffer()
: Buffer.from('');
const [timelockConfigKey] = await PublicKey.findProgramAddress( const [timelockConfigKey] = await PublicKey.findProgramAddress(
[ [
Buffer.from(GOVERNANCE_AUTHORITY_SEED),
PROGRAM_IDS.timelock.programId.toBuffer(), PROGRAM_IDS.timelock.programId.toBuffer(),
uninitializedTimelockConfig.governanceMint.toBuffer(), uninitializedTimelockConfig.governanceMint.toBuffer(),
uninitializedTimelockConfig.councilMint.toBuffer(), councilMintSeed,
uninitializedTimelockConfig.program.toBuffer(), uninitializedTimelockConfig.program.toBuffer(),
], ],
PROGRAM_IDS.timelock.programId, PROGRAM_IDS.timelock.programId,
@ -126,8 +133,8 @@ export const registerProgramGovernance = async (
timelockConfigKey, timelockConfigKey,
uninitializedTimelockConfig.program, uninitializedTimelockConfig.program,
uninitializedTimelockConfig.governanceMint, uninitializedTimelockConfig.governanceMint,
uninitializedTimelockConfig.councilMint,
wallet.publicKey, wallet.publicKey,
uninitializedTimelockConfig.councilMint,
), ),
); );
instructions.push( instructions.push(
@ -135,7 +142,7 @@ export const registerProgramGovernance = async (
timelockConfigKey, timelockConfigKey,
uninitializedTimelockConfig.program, uninitializedTimelockConfig.program,
uninitializedTimelockConfig.governanceMint, uninitializedTimelockConfig.governanceMint,
uninitializedTimelockConfig.councilMint,
uninitializedTimelockConfig.consensusAlgorithm || uninitializedTimelockConfig.consensusAlgorithm ||
ConsensusAlgorithm.Majority, ConsensusAlgorithm.Majority,
uninitializedTimelockConfig.executionType || ExecutionType.Independent, uninitializedTimelockConfig.executionType || ExecutionType.Independent,
@ -145,6 +152,7 @@ export const registerProgramGovernance = async (
uninitializedTimelockConfig.minimumSlotWaitingPeriod || new BN(0), uninitializedTimelockConfig.minimumSlotWaitingPeriod || new BN(0),
uninitializedTimelockConfig.timeLimit || new BN(0), uninitializedTimelockConfig.timeLimit || new BN(0),
uninitializedTimelockConfig.name || '', uninitializedTimelockConfig.name || '',
uninitializedTimelockConfig.councilMint,
), ),
); );

View File

@ -6,7 +6,7 @@ import {
} from '@solana/web3.js'; } from '@solana/web3.js';
import { contexts, utils, models, ParsedAccount } from '@oyster/common'; import { contexts, utils, models, ParsedAccount } from '@oyster/common';
import { TimelockSet } from '../models/timelock'; import { GOVERNANCE_AUTHORITY_SEED, TimelockSet } from '../models/timelock';
import { removeSignerInstruction } from '../models/removeSigner'; import { removeSignerInstruction } from '../models/removeSigner';
const { sendTransaction } = contexts.Connection; const { sendTransaction } = contexts.Connection;
const { notify } = utils; const { notify } = utils;
@ -25,7 +25,7 @@ export const removeSigner = async (
let instructions: TransactionInstruction[] = []; let instructions: TransactionInstruction[] = [];
const [mintAuthority] = await PublicKey.findProgramAddress( const [mintAuthority] = await PublicKey.findProgramAddress(
[proposal.pubkey.toBuffer()], [Buffer.from(GOVERNANCE_AUTHORITY_SEED), proposal.pubkey.toBuffer()],
PROGRAM_IDS.timelock.programId, PROGRAM_IDS.timelock.programId,
); );

View File

@ -6,7 +6,11 @@ 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 { TimelockSet, TimelockState } from '../models/timelock'; import {
GOVERNANCE_AUTHORITY_SEED,
TimelockSet,
TimelockState,
} from '../models/timelock';
import { signInstruction } from '../models/sign'; import { signInstruction } from '../models/sign';
const { sendTransaction } = contexts.Connection; const { sendTransaction } = contexts.Connection;
@ -26,7 +30,7 @@ export const sign = async (
let instructions: TransactionInstruction[] = []; let instructions: TransactionInstruction[] = [];
const [mintAuthority] = await PublicKey.findProgramAddress( const [mintAuthority] = await PublicKey.findProgramAddress(
[proposal.pubkey.toBuffer()], [Buffer.from(GOVERNANCE_AUTHORITY_SEED), proposal.pubkey.toBuffer()],
PROGRAM_IDS.timelock.programId, PROGRAM_IDS.timelock.programId,
); );

View File

@ -13,6 +13,7 @@ import {
} from '@oyster/common'; } from '@oyster/common';
import { import {
GOVERNANCE_AUTHORITY_SEED,
TimelockSet, TimelockSet,
TimelockState, TimelockState,
TimelockStateStatus, TimelockStateStatus,
@ -79,7 +80,7 @@ export const withdrawVotingTokens = async (
} }
const [mintAuthority] = await PublicKey.findProgramAddress( const [mintAuthority] = await PublicKey.findProgramAddress(
[proposal.pubkey.toBuffer()], [Buffer.from(GOVERNANCE_AUTHORITY_SEED), proposal.pubkey.toBuffer()],
PROGRAM_IDS.timelock.programId, PROGRAM_IDS.timelock.programId,
); );
@ -117,6 +118,7 @@ export const withdrawVotingTokens = async (
const [governanceVotingRecord] = await PublicKey.findProgramAddress( const [governanceVotingRecord] = await PublicKey.findProgramAddress(
[ [
Buffer.from(GOVERNANCE_AUTHORITY_SEED),
PROGRAM_IDS.timelock.programId.toBuffer(), PROGRAM_IDS.timelock.programId.toBuffer(),
proposal.pubkey.toBuffer(), proposal.pubkey.toBuffer(),
existingVoteAccount.toBuffer(), existingVoteAccount.toBuffer(),

View File

@ -33,7 +33,7 @@ export default function MintSourceTokens({
const connection = useConnection(); const connection = useConnection();
const mintKey = useGovernance const mintKey = useGovernance
? timelockConfig.info.governanceMint ? timelockConfig.info.governanceMint
: timelockConfig.info.councilMint; : timelockConfig.info.councilMint!;
const mint = useMint(mintKey); const mint = useMint(mintKey);
const [saving, setSaving] = useState(false); const [saving, setSaving] = useState(false);

View File

@ -13,16 +13,16 @@ import * as Layout from '../utils/layout';
/// program account key, governance mint key, council mint key, and timelock program account key. /// program account key, governance mint key, council mint key, and timelock program account key.
/// 1. `[]` Program account to tie this config to. /// 1. `[]` Program account to tie this config to.
/// 2. `[]` Governance mint to tie this config to /// 2. `[]` Governance mint to tie this config to
/// 3. `[]` Council mint [optional] to tie this config to [Pass in 0s otherwise] /// 3. `[]` Payer
/// 4. `[]` Payer /// 4. `[]` Timelock program pub key.
/// 6. `[]` Timelock program pub key. /// 5. `[]` System account.
/// 7. `[]` System account. /// 6. `[]` Council mint [optional] to tie this config to [Optional]
export const createEmptyTimelockConfigInstruction = ( export const createEmptyTimelockConfigInstruction = (
timelockConfigAccount: PublicKey, timelockConfigAccount: PublicKey,
programAccount: PublicKey, programAccount: PublicKey,
governanceMint: PublicKey, governanceMint: PublicKey,
councilMint: PublicKey,
payer: PublicKey, payer: PublicKey,
councilMint?: PublicKey,
): TransactionInstruction => { ): TransactionInstruction => {
const PROGRAM_IDS = utils.programIds(); const PROGRAM_IDS = utils.programIds();
@ -41,7 +41,6 @@ export const createEmptyTimelockConfigInstruction = (
{ pubkey: timelockConfigAccount, isSigner: false, isWritable: false }, { pubkey: timelockConfigAccount, isSigner: false, isWritable: false },
{ pubkey: programAccount, isSigner: false, isWritable: false }, { pubkey: programAccount, isSigner: false, isWritable: false },
{ pubkey: governanceMint, isSigner: false, isWritable: false }, { pubkey: governanceMint, isSigner: false, isWritable: false },
{ pubkey: councilMint, isSigner: false, isWritable: false },
{ pubkey: payer, isSigner: true, isWritable: false }, { pubkey: payer, isSigner: true, isWritable: false },
{ {
@ -51,6 +50,11 @@ export const createEmptyTimelockConfigInstruction = (
}, },
{ pubkey: PROGRAM_IDS.system, isSigner: false, isWritable: false }, { pubkey: PROGRAM_IDS.system, isSigner: false, isWritable: false },
]; ];
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.timelock.programId,

View File

@ -55,7 +55,7 @@ export const depositSourceTokensInstruction = (
{ 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: timelockSetAccount, isSigner: false, isWritable: false },
{ pubkey: transferAuthority, isSigner: false, 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 },
]; ];

View File

@ -13,12 +13,11 @@ import * as Layout from '../utils/layout';
/// program account key, governance mint key, council mint key, timelock program account key. /// program account key, governance mint key, council mint key, timelock program account key.
/// 1. `[]` Program account that this config uses /// 1. `[]` Program account that this config uses
/// 2. `[]` Governance mint that this config uses /// 2. `[]` Governance mint that this config uses
/// 3. `[]` Council mint that this config uses [Optional] [Pass in 0s otherwise] /// 3. `[]` Council mint that this config uses [Optional]
export const initTimelockConfigInstruction = ( export const initTimelockConfigInstruction = (
timelockConfigAccount: PublicKey, timelockConfigAccount: PublicKey,
programAccount: PublicKey, programAccount: PublicKey,
governanceMint: PublicKey, governanceMint: PublicKey,
councilMint: PublicKey,
consensusAlgorithm: number, consensusAlgorithm: number,
executionType: number, executionType: number,
timelockType: number, timelockType: number,
@ -26,6 +25,7 @@ export const initTimelockConfigInstruction = (
minimumSlotWaitingPeriod: BN, minimumSlotWaitingPeriod: BN,
timeLimit: BN, timeLimit: BN,
name: string, name: string,
councilMint?: PublicKey,
): TransactionInstruction => { ): TransactionInstruction => {
const PROGRAM_IDS = utils.programIds(); const PROGRAM_IDS = utils.programIds();
@ -69,8 +69,12 @@ export const initTimelockConfigInstruction = (
{ pubkey: timelockConfigAccount, isSigner: false, isWritable: true }, { pubkey: timelockConfigAccount, 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 },
{ pubkey: councilMint, isSigner: false, isWritable: false },
]; ];
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.timelock.programId,

View File

@ -10,8 +10,9 @@ export const CONFIG_NAME_LENGTH = 32;
export const INSTRUCTION_LIMIT = 450; export const INSTRUCTION_LIMIT = 450;
export const TRANSACTION_SLOTS = 5; export const TRANSACTION_SLOTS = 5;
export const TEMP_FILE_TXN_SIZE = 1000; export const TEMP_FILE_TXN_SIZE = 1000;
// Key chosen to represent an unused key, a dummy empty. Points to system program.
export const ZERO_KEY = '11111111111111111111111111111111'; /// Seed for proposal authority
export const GOVERNANCE_AUTHORITY_SEED = 'governance';
export enum TimelockInstruction { export enum TimelockInstruction {
InitTimelockSet = 1, InitTimelockSet = 1,
@ -71,7 +72,7 @@ export interface TimelockConfig {
/// Governance mint /// Governance mint
governanceMint: PublicKey; governanceMint: PublicKey;
/// Council mint (Optional) /// Council mint (Optional)
councilMint: PublicKey; councilMint?: PublicKey;
/// Program ID that is tied to this config (optional) /// Program ID that is tied to this config (optional)
program: PublicKey; program: PublicKey;
/// Time limit in slots for proposal to be open to voting /// Time limit in slots for proposal to be open to voting
@ -91,12 +92,13 @@ export const TimelockConfigLayout: typeof BufferLayout.Structure = BufferLayout.
BufferLayout.u8('votingEntryRule'), BufferLayout.u8('votingEntryRule'),
Layout.uint64('minimumSlotWaitingPeriod'), Layout.uint64('minimumSlotWaitingPeriod'),
Layout.publicKey('governanceMint'), Layout.publicKey('governanceMint'),
BufferLayout.u8('councilMintOption'),
Layout.publicKey('councilMint'), Layout.publicKey('councilMint'),
Layout.publicKey('program'), Layout.publicKey('program'),
Layout.uint64('timeLimit'), Layout.uint64('timeLimit'),
BufferLayout.seq(BufferLayout.u8(), CONFIG_NAME_LENGTH, 'name'), BufferLayout.seq(BufferLayout.u8(), CONFIG_NAME_LENGTH, 'name'),
BufferLayout.u32('count'), BufferLayout.u32('count'),
BufferLayout.seq(BufferLayout.u8(), 296, 'padding'), BufferLayout.seq(BufferLayout.u8(), 295, 'padding'),
], ],
); );
@ -428,7 +430,7 @@ export const TimelockConfigParser = (
votingEntryRule: data.votingEntryRule, votingEntryRule: data.votingEntryRule,
minimumSlotWaitingPeriod: data.minimumSlotWaitingPeriod, minimumSlotWaitingPeriod: data.minimumSlotWaitingPeriod,
governanceMint: data.governanceMint, governanceMint: data.governanceMint,
councilMint: data.councilMint, councilMint: data.councilMintOption == 1 ? data.councilMint : null,
program: data.program, program: data.program,
timeLimit: data.timeLimit, timeLimit: data.timeLimit,
name: utils.fromUTF8Array(data.name).replaceAll('\u0000', ''), name: utils.fromUTF8Array(data.name).replaceAll('\u0000', ''),

View File

@ -6,7 +6,7 @@ import {
PublicKey, PublicKey,
Message, Message,
} from '@solana/web3.js'; } from '@solana/web3.js';
import { TimelockSet } from '../models/timelock'; import { GOVERNANCE_AUTHORITY_SEED, TimelockSet } from '../models/timelock';
export async function serializeInstruction({ export async function serializeInstruction({
connection, connection,
instr, instr,
@ -23,7 +23,7 @@ export async function serializeInstruction({
await connection.getRecentBlockhash('max') await connection.getRecentBlockhash('max')
).blockhash; ).blockhash;
const [authority] = await PublicKey.findProgramAddress( const [authority] = await PublicKey.findProgramAddress(
[proposal.pubkey.toBuffer()], [Buffer.from(GOVERNANCE_AUTHORITY_SEED), proposal.pubkey.toBuffer()],
PROGRAM_IDS.timelock.programId, PROGRAM_IDS.timelock.programId,
); );
instructionTransaction.setSigners(authority); instructionTransaction.setSigners(authority);

View File

@ -16,7 +16,6 @@ import {
TimelockConfig, TimelockConfig,
TimelockType, TimelockType,
VotingEntryRule, VotingEntryRule,
ZERO_KEY,
} from '../../models/timelock'; } from '../../models/timelock';
import { PublicKey } from '@solana/web3.js'; import { PublicKey } from '@solana/web3.js';
import { Table } from 'antd'; import { Table } from 'antd';
@ -91,7 +90,7 @@ const columns = [
render: (config: ParsedAccount<TimelockConfig>) => ( render: (config: ParsedAccount<TimelockConfig>) => (
<> <>
<MintSourceTokens timelockConfig={config} useGovernance={true} /> <MintSourceTokens timelockConfig={config} useGovernance={true} />
{config.info.councilMint.toBase58() != ZERO_KEY && ( {config.info.councilMint && (
<MintSourceTokens timelockConfig={config} useGovernance={false} /> <MintSourceTokens timelockConfig={config} useGovernance={false} />
)} )}
</> </>

View File

@ -8,7 +8,6 @@ import {
ExecutionType, ExecutionType,
TimelockType, TimelockType,
VotingEntryRule, VotingEntryRule,
ZERO_KEY,
} from '../../models/timelock'; } from '../../models/timelock';
import { LABELS } from '../../constants'; import { LABELS } from '../../constants';
import { contexts, utils, tryParseKey } from '@oyster/common'; import { contexts, utils, tryParseKey } from '@oyster/common';
@ -132,9 +131,8 @@ export function NewForm({
: undefined, : undefined,
councilMint: values.councilMint councilMint: values.councilMint
? new PublicKey(values.councilMint) ? new PublicKey(values.councilMint)
: councilVisible : undefined,
? undefined // if visible but empty, set undefined so we instantiate one
: new PublicKey(ZERO_KEY), // default empty case, just make it padding since user doesnt want one.
program: new PublicKey(values.program), program: new PublicKey(values.program),
name: values.name, name: values.name,
timeLimit: new BN(values.timeLimit), timeLimit: new BN(values.timeLimit),
@ -144,6 +142,7 @@ export function NewForm({
connection, connection,
wallet.wallet, wallet.wallet,
uninitializedConfig, uninitializedConfig,
councilVisible,
); );
handleOk(newConfig); handleOk(newConfig);
}; };

View File

@ -2,7 +2,7 @@ import React, { useState } from 'react';
import { Button, ButtonProps, Modal, Radio } from 'antd'; import { Button, ButtonProps, Modal, Radio } from 'antd';
import { Form, Input, Select } from 'antd'; import { Form, Input, Select } from 'antd';
import { Account } from '@solana/web3.js'; import { Account } from '@solana/web3.js';
import { DESC_SIZE, NAME_SIZE, ZERO_KEY } from '../../models/timelock'; import { DESC_SIZE, NAME_SIZE } from '../../models/timelock';
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';
@ -80,7 +80,7 @@ export function NewForm({
if ( if (
values.proposalMintType === ProposalMintType.Council && values.proposalMintType === ProposalMintType.Council &&
config.info.councilMint.toBase58() === ZERO_KEY !config.info.councilMint
) { ) {
notify({ notify({
message: LABELS.THIS_CONFIG_LACKS_COUNCIL, message: LABELS.THIS_CONFIG_LACKS_COUNCIL,