Skip to content

Commit

Permalink
feat: load examples from menu
Browse files Browse the repository at this point in the history
  • Loading branch information
sohrab- committed Jul 27, 2022
1 parent f07b13e commit 5b40a68
Show file tree
Hide file tree
Showing 9 changed files with 280 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,19 @@ import {
} from "@chakra-ui/react";
import { useWallet } from "@solana/wallet-adapter-react";
import React from "react";
import { useTransactionStore } from "../../hooks/useTransactionStore";
import { EXAMPLES } from "../../models/examples";
import { mapFromTransactionExt } from "../../models/external-mappers";

export const Example: React.FC = () => {
const { publicKey: walletPublicKey } = useWallet();
const setTransaction = useTransactionStore((state) => state.setTransaction);

const loadExample = (name: string) => {
setTransaction(
mapFromTransactionExt(EXAMPLES[name](walletPublicKey?.toBase58()!))
);
};

return (
<Menu>
Expand All @@ -36,8 +46,21 @@ export const Example: React.FC = () => {
</Tooltip>

<MenuList fontSize="md">
<MenuItem>System Program: Transfer SOL</MenuItem>
<MenuItem>System Program: Create Account</MenuItem>
<MenuItem
onClick={() => {
loadExample("systemProgramCreateAccount");
}}
>
System Program: Create Account
</MenuItem>
<MenuItem
onClick={() => {
loadExample("systemProgramTransfer");
}}
>
System Program: Transfer SOL
</MenuItem>

{/* TODO more examples */}
</MenuList>
</Menu>
Expand Down
2 changes: 1 addition & 1 deletion src/components/header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { useState } from "react";
import { FaRedo, FaUndo, FaWrench } from "react-icons/fa";
import { useTransactionStore } from "../../hooks/useTransactionStore";
import { ColorModeSwitcher } from "./ColorModeSwitcher";
import { Example } from "./Example";
import { Example } from "./Examples";
import { WalletButton } from "./WalletButton";

export const Header: React.FC = () => {
Expand Down
23 changes: 19 additions & 4 deletions src/hooks/useTransactionStore.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import produce from "immer";
import { WritableDraft } from "immer/dist/internal";
import create from "zustand";
import { addTo, removeFrom } from "../models/sortable";
import { addTo, IID, removeFrom } from "../models/sortable";
import {
DEFAULT_TRANSACTION_STATE,
DEFAULT_UI_INSTRUCTION_STATE,
} from "../models/state-default";
import { TransactionState } from "../models/state-types";
import { TransactionState, UIInstructionState } from "../models/state-types";

const LOCAL_STORAGE_KEY = "bcsolTransactionState";

Expand Down Expand Up @@ -41,9 +42,23 @@ export const useTransactionStore = create<TransactionState>((set) => {
set(produce(fn));
},
// define these helpers since they interact with different slices of state
setTransaction: (transaction) => {
set(
produce((state: WritableDraft<TransactionState>) => {
state.transaction = transaction;
state.uiState.instructions = transaction.instructions.order.reduce(
(acc, id) => {
acc[id] = DEFAULT_UI_INSTRUCTION_STATE;
return acc;
},
{} as Record<IID, UIInstructionState>
);
})
);
},
addInstruction: (instruction) => {
set(
produce((state) => {
produce((state: WritableDraft<TransactionState>) => {
addTo(state.transaction.instructions, instruction);
state.uiState.instructions[instruction.id] =
DEFAULT_UI_INSTRUCTION_STATE;
Expand All @@ -52,7 +67,7 @@ export const useTransactionStore = create<TransactionState>((set) => {
},
removeInstruction: (instructionId) => {
set(
produce((state) => {
produce((state: WritableDraft<TransactionState>) => {
removeFrom(state.transaction.instructions, instructionId);
delete state.uiState.instructions[instructionId];
})
Expand Down
97 changes: 97 additions & 0 deletions src/models/examples.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { Keypair } from "@solana/web3.js";
import { ITransactionExt } from "./external-types";

// TODO use export model instead and map across

export const EXAMPLES: Record<
string,
(walletPublicKey: string) => ITransactionExt
> = {
systemProgramCreateAccount: (walletPublicKey: string) => ({
name: "System Program: Create Account",
instructions: [
{
name: "Create Account",
programId: "11111111111111111111111111111111",
accounts: [
{
name: "From",
pubkey: walletPublicKey,
isWritable: true,
isSigner: true,
},
{
name: "To",
pubkey: Keypair.generate().publicKey.toBase58(),
isWritable: true,
isSigner: false,
},
],
data: {
format: "bufferLayout",
value: [
{
name: "Instruction",
type: "u32",
value: 0,
},
{
name: "Lamport",
type: "u64",
value: 1400000,
},
{
name: "Space",
type: "u64",
value: 100,
},
{
name: "Program ID",
type: "publicKey",
value: 100,
},
],
},
},
],
}),

systemProgramTransfer: (walletPublicKey: string) => ({
name: "System Program: Transfer",
instructions: [
{
name: "Transfer",
programId: "11111111111111111111111111111111",
accounts: [
{
name: "From",
pubkey: walletPublicKey,
isWritable: true,
isSigner: true,
},
{
name: "To",
pubkey: "GoctE4EU5jZqbWg1Ffo5sjCqjrnzW1m76JmWwd84pwtV",
isWritable: true,
isSigner: false,
},
],
data: {
format: "bufferLayout",
value: [
{
name: "Instruction",
type: "u32",
value: 2,
},
{
name: "Lamport",
type: "u64",
value: 1,
},
],
},
},
],
}),
};
98 changes: 98 additions & 0 deletions src/models/external-mappers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { v4 as uuid } from "uuid";
import { IInstrctionDataFieldExt, ITransactionExt } from "./external-types";
import { IInstrctionDataField, ITransaction } from "./internal-types";
import {
Identifiable,
SortableCollection,
toSortableCollection,
toSortedArray,
} from "./sortable";

const mapToIInstrctionDataFieldExt = ({
name,
type,
value,
}: IInstrctionDataField): IInstrctionDataFieldExt => ({
name,
type,
value,
});

export const mapToTransactionExt = ({
name,
instructions,
}: ITransaction): ITransactionExt => ({
name,
instructions: toSortedArray(instructions).map(
({ name, programId, accounts, data }) => ({
name,
programId,
accounts: toSortedArray(accounts).map(
({ name, pubkey, isWritable, isSigner }) => ({
name,
pubkey,
isWritable,
isSigner,
})
),
data: {
format: data.format,
value:
data.format === "borsh"
? toSortedArray(data.borsh).map(mapToIInstrctionDataFieldExt)
: data.format === "bufferLayout"
? toSortedArray(data.bufferLayout).map(mapToIInstrctionDataFieldExt)
: data.raw,
},
})
),
});

const mapToSortableCollection = <T>(
items: T[]
): SortableCollection<T & Identifiable> =>
toSortableCollection(items.map((item) => ({ ...item, id: uuid() })));

export const mapFromTransactionExt = ({
name,
instructions,
}: ITransactionExt): ITransaction => ({
name,
instructions: mapToSortableCollection(
instructions.map(({ name, programId, accounts, data }) => ({
name,
programId,
accounts: mapToSortableCollection(
accounts.map(({ name, pubkey, isWritable, isSigner }) => ({
name,
pubkey,
isWritable,
isSigner,
}))
),
data: {
format: data.format,
raw: data.format === "raw" ? (data.value as string) : "",
borsh: mapToSortableCollection(
data.format === "borsh"
? (data.value as IInstrctionDataFieldExt[])
: []
),
bufferLayout: mapToSortableCollection(
data.format === "bufferLayout"
? (data.value as IInstrctionDataFieldExt[])
: []
),
},
}))
),
});

// const mapFromTransactionExport = (
// transaction: ITransactionExport
// ): ITransaction => {
// };

// export const serialiseTransaction = (transaction: ITransaction): string => {};

// export const deserialiseTransaction = (serialised: string): ITransaction => {};
35 changes: 35 additions & 0 deletions src/models/external-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// Models for the transaction to be exported

import {
DataFormat,
InstructionDataFieldType,
IPubKey,
} from "./internal-types";

export interface IInstrctionDataFieldExt {
name?: string;
type: InstructionDataFieldType;
value: any;
}

export interface IAccountExt {
name?: string;
pubkey: IPubKey;
isWritable: boolean;
isSigner: boolean;
}

export interface IInstructionExt {
name?: string;
programId: IPubKey;
accounts: IAccountExt[];
data: {
format: DataFormat;
value: IInstrctionDataFieldExt[] | string;
};
}

export interface ITransactionExt {
name?: string;
instructions: IInstructionExt[];
}
40 changes: 0 additions & 40 deletions src/models/external.ts

This file was deleted.

1 change: 1 addition & 0 deletions src/models/state-default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export const DEFAULT_TRANSACTION_STATE: TransactionState = {
welcomeOpen: true,
},
set: () => {}, // set by the hook
setTransaction: (_) => {}, // set by the hook
addInstruction: (_) => {}, // set by the hook
removeInstruction: (_) => {}, // set by the hook
};
Expand Down
4 changes: 4 additions & 0 deletions src/models/state-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,15 @@ export type OptionsState = {
set: (fn: (state: Draft<OptionsState>) => void) => void;
};

// TODO split ITransaction out of the rest of the state
// TODO implement undo/redo using https://github.com/charkour/zundo

export type TransactionState = {
readonly transaction: ITransaction;
readonly results: IResults;
readonly uiState: UIState;
set: (fn: (state: Draft<TransactionState>) => void) => void;
setTransaction: (transaction: ITransaction) => void;
addInstruction: (instruction: IInstruction) => void;
removeInstruction: (instructionId: IID) => void;
};

0 comments on commit 5b40a68

Please sign in to comment.