From 1883bfd8eeb5b9ee295f2c7152c711a4c27418cf Mon Sep 17 00:00:00 2001 From: Dummy Tester 123 Date: Thu, 25 Feb 2021 16:03:12 -0600 Subject: [PATCH] Added complete working display of instructions and adding instructions --- .../addCustomSingleSignerTransaction.ts | 9 ++- .../components/Proposal/InstructionCard.tsx | 47 +++++++++++ .../Proposal/NewInstructionCard.tsx | 77 +++++++++++++++++++ .../Proposal}/style.less | 0 packages/proposals/src/contexts/proposals.tsx | 18 +++-- .../addCustomSingleSignerTransaction.ts | 1 - packages/proposals/src/models/timelock.ts | 5 +- .../proposals/src/views/dashboard/index.tsx | 3 + .../proposals/src/views/proposal/index.tsx | 77 +++++-------------- 9 files changed, 170 insertions(+), 67 deletions(-) create mode 100644 packages/proposals/src/components/Proposal/InstructionCard.tsx create mode 100644 packages/proposals/src/components/Proposal/NewInstructionCard.tsx rename packages/proposals/src/{views/proposal => components/Proposal}/style.less (100%) diff --git a/packages/proposals/src/actions/addCustomSingleSignerTransaction.ts b/packages/proposals/src/actions/addCustomSingleSignerTransaction.ts index dd19b7e..53a7bdf 100644 --- a/packages/proposals/src/actions/addCustomSingleSignerTransaction.ts +++ b/packages/proposals/src/actions/addCustomSingleSignerTransaction.ts @@ -22,6 +22,9 @@ export const addCustomSingleSignerTransaction = async ( wallet: any, proposal: ParsedAccount, sigAccount: PublicKey, + slot: string, + instruction: string, + position: number, ) => { const PROGRAM_IDS = utils.programIds(); @@ -67,9 +70,9 @@ export const addCustomSingleSignerTransaction = async ( proposal.info.signatoryValidation, transferAuthority.publicKey, authority, - '123', - '12345', - 0, + slot, + instruction, + position, ), ); diff --git a/packages/proposals/src/components/Proposal/InstructionCard.tsx b/packages/proposals/src/components/Proposal/InstructionCard.tsx new file mode 100644 index 0000000..f7db7fe --- /dev/null +++ b/packages/proposals/src/components/Proposal/InstructionCard.tsx @@ -0,0 +1,47 @@ +import { DeleteOutlined, EditOutlined } from '@ant-design/icons'; +import { ParsedAccount } from '@oyster/common'; +import { Card } from 'antd'; +import Meta from 'antd/lib/card/Meta'; +import React, { useState } from 'react'; +import { TimelockTransaction } from '../../models/timelock'; +import './style.less'; + +export function InstructionCard({ + instruction, + position, +}: { + instruction: ParsedAccount; + position: number; +}) { + const [tabKey, setTabKey] = useState('info'); + + const contentList: Record = { + info: ( + +

Instruction: TODO

+

Slot: {instruction.info.slot.toNumber()}

+ + } + /> + ), + data:

{instruction.info.instruction}

, + }; + + return ( + , ]} + > + {contentList[tabKey]} + + ); +} diff --git a/packages/proposals/src/components/Proposal/NewInstructionCard.tsx b/packages/proposals/src/components/Proposal/NewInstructionCard.tsx new file mode 100644 index 0000000..d5fcbb9 --- /dev/null +++ b/packages/proposals/src/components/Proposal/NewInstructionCard.tsx @@ -0,0 +1,77 @@ +import React from 'react'; +import { Card, Spin } from 'antd'; +import { Form, Input } from 'antd'; +import { INSTRUCTION_LIMIT, TimelockSet } from '../../models/timelock'; +import { contexts, ParsedAccount, hooks, utils } from '@oyster/common'; +import { addCustomSingleSignerTransaction } from '../../actions/addCustomSingleSignerTransaction'; +import { SaveOutlined } from '@ant-design/icons'; + +const { useWallet } = contexts.Wallet; +const { useConnection } = contexts.Connection; +const { useAccountByMint } = hooks; +const { notify } = utils; + +const layout = { + labelCol: { span: 8 }, + wrapperCol: { span: 16 }, +}; + +export function NewInstructionCard({ + proposal, + position, +}: { + proposal: ParsedAccount; + position: number; +}) { + const [form] = Form.useForm(); + const wallet = useWallet(); + const connection = useConnection(); + const sigAccount = useAccountByMint(proposal.info.signatoryMint); + const onFinish = async (values: { slot: string; instruction: string }) => { + if (!values.slot.match(/^\d*$/)) { + notify({ + message: 'Slot can only be numeric', + type: 'error', + }); + return; + } + if (sigAccount) { + await addCustomSingleSignerTransaction( + connection, + wallet.wallet, + proposal, + sigAccount.pubkey, + values.slot, + values.instruction, + position, + ); + form.resetFields(); + } + }; + return ( + ]} + > + {!sigAccount ? ( + + ) : ( +
+ + + + + + +
+ )} +
+ ); +} diff --git a/packages/proposals/src/views/proposal/style.less b/packages/proposals/src/components/Proposal/style.less similarity index 100% rename from packages/proposals/src/views/proposal/style.less rename to packages/proposals/src/components/Proposal/style.less diff --git a/packages/proposals/src/contexts/proposals.tsx b/packages/proposals/src/contexts/proposals.tsx index 67db4a9..a0d0006 100644 --- a/packages/proposals/src/contexts/proposals.tsx +++ b/packages/proposals/src/contexts/proposals.tsx @@ -15,7 +15,7 @@ import { TimelockSet, TimelockSetLayout, TimelockSetParser, - Transaction, + TimelockTransaction, } from '../models/timelock'; const { useConnectionConfig } = contexts.Connection; @@ -23,7 +23,7 @@ const { cache } = contexts.Accounts; export interface ProposalsContextState { proposals: Record>; - transactions: Record>; + transactions: Record>; } export const ProposalsContext = React.createContext( @@ -72,7 +72,10 @@ function useSetupProposalsCache({ }; Promise.all([query()]).then((all: PublicKeyAndAccount[][]) => { const newProposals: Record> = {}; - const newTransactions: Record> = {}; + const newTransactions: Record< + string, + ParsedAccount + > = {}; all[0].forEach(a => { if (a.account.data.length === TimelockSetLayout.span) { @@ -89,11 +92,14 @@ function useSetupProposalsCache({ a.account, CustomSingleSignerTimelockTransactionParser, ); - const cached = cache.get(a.pubkey) as ParsedAccount; + const cached = cache.get( + a.pubkey, + ) as ParsedAccount; newTransactions[a.pubkey.toBase58()] = cached; } }); setProposals(newProposals); + setTransactions(newTransactions); }); const subID = connection.onProgramAccountChange( PROGRAM_IDS.timelock.programId, @@ -112,7 +118,9 @@ function useSetupProposalsCache({ const cached = span === TimelockSetLayout.span ? (cache.get(info.accountId) as ParsedAccount) - : (cache.get(info.accountId) as ParsedAccount); + : (cache.get( + info.accountId, + ) as ParsedAccount); setter((obj: any) => ({ ...obj, [typeof info.accountId === 'string' diff --git a/packages/proposals/src/models/addCustomSingleSignerTransaction.ts b/packages/proposals/src/models/addCustomSingleSignerTransaction.ts index 644f205..c42e15c 100644 --- a/packages/proposals/src/models/addCustomSingleSignerTransaction.ts +++ b/packages/proposals/src/models/addCustomSingleSignerTransaction.ts @@ -60,7 +60,6 @@ export const addCustomSingleSignerTransactionInstruction = ( for (let i = instructionAsBytes.length; i <= INSTRUCTION_LIMIT - 1; i++) { instructionAsBytes.push(0); } - console.log('Num', new BN(slot).toNumber()); dataLayout.encode( { diff --git a/packages/proposals/src/models/timelock.ts b/packages/proposals/src/models/timelock.ts index 33b7d48..439c92d 100644 --- a/packages/proposals/src/models/timelock.ts +++ b/packages/proposals/src/models/timelock.ts @@ -203,13 +203,14 @@ export const CustomSingleSignerTimelockTransactionLayout: typeof BufferLayout.St ], ); -export interface Transaction { +export interface TimelockTransaction { version: number; slot: BN; instruction: string; } -export interface CustomSingleSignerTimelockTransaction extends Transaction { +export interface CustomSingleSignerTimelockTransaction + extends TimelockTransaction { authorityKey: PublicKey; } diff --git a/packages/proposals/src/views/dashboard/index.tsx b/packages/proposals/src/views/dashboard/index.tsx index cec39ef..906c788 100644 --- a/packages/proposals/src/views/dashboard/index.tsx +++ b/packages/proposals/src/views/dashboard/index.tsx @@ -75,6 +75,9 @@ function InnerDummyView({ wallet, proposal, sigAccount.pubkey, + '123', + '12345', + 0, ) } > diff --git a/packages/proposals/src/views/proposal/index.tsx b/packages/proposals/src/views/proposal/index.tsx index b43e306..a7c4703 100644 --- a/packages/proposals/src/views/proposal/index.tsx +++ b/packages/proposals/src/views/proposal/index.tsx @@ -1,11 +1,12 @@ -import { Card, Col, Divider, Row, Space, Spin, Statistic } from 'antd'; +import { Col, Divider, Row, Space, Spin, Statistic } from 'antd'; import React, { useMemo, useState } from 'react'; import { LABELS } from '../../constants'; import { ParsedAccount } from '@oyster/common'; import { ConsensusAlgorithm, + INSTRUCTION_LIMIT, TimelockSet, - Transaction, + TimelockTransaction, } from '../../models/timelock'; import { useParams } from 'react-router-dom'; import ReactMarkdown from 'react-markdown'; @@ -13,9 +14,8 @@ import { useProposals } from '../../contexts/proposals'; import { StateBadge } from '../../components/Proposal/StateBadge'; import { contexts } from '@oyster/common'; import { MintInfo } from '@solana/spl-token'; -import Meta from 'antd/lib/card/Meta'; -import './style.less'; -import { DeleteOutlined, EditOutlined } from '@ant-design/icons'; +import { InstructionCard } from '../../components/Proposal/InstructionCard'; +import { NewInstructionCard } from '../../components/Proposal/NewInstructionCard'; export const urlRegex = /[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/; const { useMint } = contexts.Accounts; @@ -50,8 +50,11 @@ function InnerProposalView({ proposal: ParsedAccount; sigMint: MintInfo; votingMint: MintInfo; - instructions: Record>; + instructions: Record>; }) { + const instructionsForProposal: ParsedAccount[] = proposal.info.state.timelockTransactions + .map(k => instructions[k.toBase58()]) + .filter(k => k); const isUrl = !!proposal.info.state.descLink.match(urlRegex); const isGist = !!proposal.info.state.descLink.match(/gist/i) && @@ -170,66 +173,28 @@ function InnerProposalView({ { xs: 8, sm: 16, md: 24, lg: 32 }, ]} > - {[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map(position => ( - + {instructionsForProposal.map((instruction, position) => ( + ))} + {instructionsForProposal.length < INSTRUCTION_LIMIT && ( + + + + )} ); } -export function InstructionCard({ - instruction, - position, -}: { - instruction: any; //ParsedAccount; - position: number; -}) { - const [tabKey, setTabKey] = useState('info'); - - const contentList: Record = { - info: ( - -

Instruction: TODO

-

Slot: {instruction.info.slot}

- - } - /> - ), - data:

{instruction.info.instruction}

, - }; - - return ( - , ]} - > - {contentList[tabKey]} - - ); -} - function getVotesRequired(proposal: ParsedAccount): number { if (proposal.info.config.consensusAlgorithm === ConsensusAlgorithm.Majority) { return proposal.info.state.totalVotingTokensMinted.toNumber() * 0.5;