diff --git a/components/account/AccountButton.tsx b/components/account/AccountButton.tsx index 53717b2c8..e88789054 100644 --- a/components/account/AccountButton.tsx +++ b/components/account/AccountButton.tsx @@ -88,11 +88,11 @@ const AccountButton: FC<{ selectWallet, disconnectWallet, isNovaWallet, - proxyFor, + getProxyFor, realAddress, } = useWallet(); - const proxy = proxyFor?.[activeAccount?.address]; + const proxy = getProxyFor(activeAccount?.address); const accountModals = useAccountModals(); const { locationAllowed, isUsingVPN } = useUserLocation(); @@ -266,17 +266,23 @@ const AccountButton: FC<{ -
-
- {shortenAddress(realAddress, 7, 7)} + {realAddress && ( +
+
+ {shortenAddress( + realAddress, + 7, + 7, + )} +
+
+ +
-
- -
-
+ )}
diff --git a/components/account/AccountModalContent.tsx b/components/account/AccountModalContent.tsx index d5c2672a9..2add34cf3 100644 --- a/components/account/AccountModalContent.tsx +++ b/components/account/AccountModalContent.tsx @@ -7,10 +7,9 @@ import { ZTG } from "@zeitgeistpm/sdk-next"; import { useChainConstants } from "lib/hooks/queries/useChainConstants"; const AccountModalContent: FC = () => { - const { activeAccount, disconnectWallet, accounts, selectAccount, proxyFor } = + const { activeAccount, disconnectWallet, accounts, selectAccount } = useWallet(); - const proxy = proxyFor?.[activeAccount?.address]; const { data: activeBalance } = useZtgBalance(activeAccount?.address); const { data: constants } = useChainConstants(); diff --git a/components/create/editor/Editor.tsx b/components/create/editor/Editor.tsx index 2122309d8..4435e8c01 100644 --- a/components/create/editor/Editor.tsx +++ b/components/create/editor/Editor.tsx @@ -42,7 +42,7 @@ const QuillEditor = dynamic(() => import("components/ui/QuillEditor"), { const createMarketStateAtom = persistentAtom({ key: "market-creation-form", defaultValue: MarketDraft.empty(), - migrations: [], + migrations: [() => MarketDraft.empty()], }); export const MarketEditor = () => { diff --git a/components/create/editor/Publishing.tsx b/components/create/editor/Publishing.tsx index 30696b9ea..caaad09d7 100644 --- a/components/create/editor/Publishing.tsx +++ b/components/create/editor/Publishing.tsx @@ -43,20 +43,20 @@ export const Publishing = ({ editor }: PublishingProps) => { const params = useMemo(() => { let signer = wallet.getSigner(); - const real = - wallet.proxyFor?.enabled && wallet.proxyFor?.address - ? (wallet.activeAccount as KeyringPairOrExtSigner) - : signer; + if (!editor.isValid || !chainTime || !signer) return; - if (editor.isValid && chainTime && signer) { + const proxy = wallet.getProxyFor(wallet.activeAccount?.address); + + if (proxy && proxy.enabled) { return marketFormDataToExtrinsicParams( editor.form, - real, + { address: wallet.realAddress } as KeyringPairOrExtSigner, chainTime, signer, ); } - return; + + return marketFormDataToExtrinsicParams(editor.form, signer, chainTime); }, [editor.form, chainTime, wallet.activeAccount]); const feesEnabled = !( @@ -87,12 +87,12 @@ export const Publishing = ({ editor }: PublishingProps) => { (a) => a.name === editor.form.currency, ); - const { data: ztgBalance } = useBalance(wallet.activeAccount?.address, { + const { data: ztgBalance } = useBalance(wallet.realAddress, { Ztg: null, }); const { data: foreignAssetBalance } = useBalance( - wallet.activeAccount?.address, + wallet.realAddress, baseCurrency?.assetId, ); diff --git a/components/create/editor/Summary.tsx b/components/create/editor/Summary.tsx index e2db7989e..25d85e33f 100644 --- a/components/create/editor/Summary.tsx +++ b/components/create/editor/Summary.tsx @@ -211,9 +211,7 @@ export const MarketSummary = ({ editor }: MarketSummaryProps) => { : Intl.DateTimeFormat("default", { dateStyle: "medium", timeStyle: "short", - }).format( - blockDate(chainTime!, form.gracePeriod?.block!).getTime(), - )} + }).format(new Date(form.gracePeriod?.date!).getTime())}
diff --git a/components/create/editor/inputs/BlockPeriod.tsx b/components/create/editor/inputs/BlockPeriod.tsx index 9cf770f5c..8b1361f5c 100644 --- a/components/create/editor/inputs/BlockPeriod.tsx +++ b/components/create/editor/inputs/BlockPeriod.tsx @@ -64,7 +64,7 @@ export const BlockPeriodPicker: React.FC = ({ name, value: { type: "date", - block: dateBlock(chainTime, new Date(event.target.value)), + date: event.target.value, }, }, }); @@ -78,7 +78,7 @@ export const BlockPeriodPicker: React.FC = ({ name, value: { type: "date", - block: dateBlock(chainTime, new Date(event.target.value)), + date: event.target.value, }, }, }); @@ -159,11 +159,7 @@ export const BlockPeriodPicker: React.FC = ({ }`} placeholder="Set Custom Date" isValid={value?.type === "date" && isValid} - value={ - chainTime && value?.type === "date" - ? blockDate(chainTime, value.block).toISOString() - : undefined - } + value={chainTime && value?.type === "date" ? value.date : undefined} onChange={handleDateChange} onBlur={handleDateBlur} /> diff --git a/components/create/editor/inputs/DateTime.tsx b/components/create/editor/inputs/DateTime.tsx index bff9a0312..f2079a57c 100644 --- a/components/create/editor/inputs/DateTime.tsx +++ b/components/create/editor/inputs/DateTime.tsx @@ -68,7 +68,11 @@ export const DateTimePicker: React.FC = ({ ref={inputRef} name={name} type="datetime-local" - value={moment(value).format("YYYY-MM-DDTHH:mm")} + value={ + value + ? moment(value).format("YYYY-MM-DDTHH:mm") + : moment().hours(0).minutes(0).format("YYYY-MM-DDTHH:mm") + } onChange={handleChange} onBlurCapture={handleBlur} /> diff --git a/components/create/editor/inputs/Oracle.tsx b/components/create/editor/inputs/Oracle.tsx index 49637a813..6518edc6d 100644 --- a/components/create/editor/inputs/Oracle.tsx +++ b/components/create/editor/inputs/Oracle.tsx @@ -48,6 +48,7 @@ export const OracleInput = forwardRef( }; const handleUseConnectedAccount = () => { + if (!wallet?.realAddress) return; onChange?.({ type: "change", target: { @@ -68,11 +69,11 @@ export const OracleInput = forwardRef( }; const isSelectedAccount = wallet.realAddress === value; - const proxy = wallet.proxyFor?.[wallet.activeAccount?.address]; + const proxy = wallet.getProxyFor(wallet.activeAccount?.address); const accountname = - proxy && proxy.enabled + proxy && proxy?.enabled ? "Proxied" - : wallet.activeAccount.name ?? "Account"; + : wallet.activeAccount?.name ?? "Account"; return (
@@ -123,18 +124,23 @@ export const OracleInput = forwardRef( isSelectedAccount ? "bg-nyanza-base" : "bg-gray-200" }`} > -
- -
- - {accountname ? ( - <> - {accountname} {shortenAddress(wallet.realAddress, 0, 6)} - - ) : ( - <>{shortenAddress(wallet.realAddress, 6, 6)} - )} - + {wallet.realAddress && ( + <> +
+ +
+ + {accountname ? ( + <> + {accountname}{" "} + {shortenAddress(wallet.realAddress, 0, 6)} + + ) : ( + <>{shortenAddress(wallet.realAddress, 6, 6)} + )} + + + )}
diff --git a/components/create/editor/inputs/answers/Categorical.tsx b/components/create/editor/inputs/answers/Categorical.tsx index 55efcc197..b9859f288 100644 --- a/components/create/editor/inputs/answers/Categorical.tsx +++ b/components/create/editor/inputs/answers/Categorical.tsx @@ -23,7 +23,7 @@ import { MdOutlineDragIndicator } from "react-icons/md"; import { FormEvent } from "../../types"; export type CategoricalAnswersInputProps = { - name?: string; + name: string; value?: CategoricalAnswers | YesNoAnswers; onChange?: (event: FormEvent) => void; onBlur?: (event: FormEvent) => void; @@ -57,7 +57,9 @@ export const CategoricalAnswersInput = ({ value: { type: "categorical", answers: - value?.answers.map((v, i) => (i === index ? answer : v)) ?? [], + value?.answers.map((v: string, i: number) => + i === index ? answer : v, + ) ?? [], }, }, }); @@ -93,10 +95,12 @@ export const CategoricalAnswersInput = ({ const handleDragEnd = (event: DragEndEvent) => { const { active, over } = event; - if (active.id !== over.id) { + if (over && active.id !== over.id) { const oldIndex = value?.answers.findIndex((v) => v === active.id); const newIndex = value?.answers.findIndex((v) => v === over.id); + if (!oldIndex || !newIndex || !value?.answers) return; + onChange?.({ type: "change", target: { @@ -112,7 +116,8 @@ export const CategoricalAnswersInput = ({ const draggingDisabled = disabled || - value?.answers.length < 2 || + !value?.answers || + value?.answers?.length < 2 || uniq(value?.answers).length < value?.answers.length; return ( @@ -135,7 +140,7 @@ export const CategoricalAnswersInput = ({ ) => void; onBlur?: (event: FormEvent) => void; @@ -50,7 +50,7 @@ export const ScalarAnswersInput = ({ name, value: { type: "scalar", - numberType: value.numberType, + numberType: value?.numberType ?? "number", answers: (value?.answers.map((v, i) => i === index ? newValue : v, ) ?? []) as [number, number], @@ -75,7 +75,7 @@ export const ScalarAnswersInput = ({ name, value: { type: "scalar", - numberType: value.numberType, + numberType: value?.numberType ?? "number", answers: (value?.answers.map((v, i) => i === index ? newValue : v, ) ?? []) as [number, number], diff --git a/components/create/editor/inputs/answers/index.tsx b/components/create/editor/inputs/answers/index.tsx index 9f97b8684..1c20c8d35 100644 --- a/components/create/editor/inputs/answers/index.tsx +++ b/components/create/editor/inputs/answers/index.tsx @@ -87,6 +87,7 @@ export const AnswersInput = ({
{value?.type === "categorical" && ( )} {value?.type === "yes/no" && ( - + )}
diff --git a/components/portfolio/PortfolioIdentity.tsx b/components/portfolio/PortfolioIdentity.tsx index e8fbd9402..eb2dd6baf 100644 --- a/components/portfolio/PortfolioIdentity.tsx +++ b/components/portfolio/PortfolioIdentity.tsx @@ -10,7 +10,7 @@ const PortfolioIdentity = ({ address }: { address: string }) => { const wallet = useWallet(); const { data: identity } = useIdentity(address); - const proxy = wallet.proxyFor?.[wallet.activeAccount?.address]; + const proxy = wallet.getProxyFor(wallet.activeAccount?.address); const isProxying = Boolean( proxy && proxy.enabled && proxy.address === address, diff --git a/lib/hooks/useCrossChainExtrinsic.ts b/lib/hooks/useCrossChainExtrinsic.ts index 0ce636aa4..f8261586c 100644 --- a/lib/hooks/useCrossChainExtrinsic.ts +++ b/lib/hooks/useCrossChainExtrinsic.ts @@ -44,15 +44,16 @@ export const useCrossChainExtrinsic = ( let extrinsic = extrinsicFn(params); - const proxy = wallet?.proxyFor?.[wallet.activeAccount?.address]; + const proxy = wallet?.getProxyFor(wallet.activeAccount?.address); let signer = wallet.getSigner(); - if (proxy?.enabled && proxy?.address) { + if (extrinsic && proxy?.enabled && proxy?.address) { console.info("Proxying cross chain transaction"); extrinsic = sdk.api.tx.proxy.proxy(proxy?.address, "Any", extrinsic); } - if (!extrinsic || !sourceChainApi || !destinationChainApi) return; + if (!signer || !extrinsic || !sourceChainApi || !destinationChainApi) + return; signAndSend( extrinsic, signer, diff --git a/lib/hooks/useExtrinsic.ts b/lib/hooks/useExtrinsic.ts index 448d00558..082758550 100644 --- a/lib/hooks/useExtrinsic.ts +++ b/lib/hooks/useExtrinsic.ts @@ -31,13 +31,15 @@ export const useExtrinsic = ( throw new Error("SDK is not RPC"); } + let signer = wallet.getSigner(); + if (!signer) return; + setIsLoading(true); let extrinsic = extrinsicFn(params); if (!extrinsic) return; - const proxy = wallet?.proxyFor?.[wallet.activeAccount?.address]; - let signer = wallet.getSigner(); + const proxy = wallet?.getProxyFor(wallet.activeAccount?.address); if (proxy?.enabled && proxy?.address) { console.info("Proxying transaction"); diff --git a/lib/state/chaintime.ts b/lib/state/chaintime.ts index 649bab1fb..78747ca85 100644 --- a/lib/state/chaintime.ts +++ b/lib/state/chaintime.ts @@ -10,7 +10,7 @@ const store = getDefaultStore(); let sub: Subscription; -store.sub(sdkAtom, () => { +const onSdkChange = () => { const sdk = store.get(sdkAtom); if (sub) sub.unsubscribe(); if (isRpcSdk(sdk)) { @@ -18,7 +18,16 @@ store.sub(sdkAtom, () => { store.set(chainTimeAtom, time); }); } -}); +}; + +/** + * In dev the subscription is sometimes not set up on first render. + * So we need to check if the sdk is already set up and if so update the chaintime atom. + */ +const sdk = store.get(sdkAtom); +if (sdk) onSdkChange(); + +store.sub(sdkAtom, onSdkChange); export const useChainTime = (): ChainTime | null => { const [chainTime] = useAtom(chainTimeAtom); diff --git a/lib/state/market-creation/editor.ts b/lib/state/market-creation/editor.ts index 1efa681c6..2299c963d 100644 --- a/lib/state/market-creation/editor.ts +++ b/lib/state/market-creation/editor.ts @@ -16,7 +16,7 @@ import { MarketCreationStep, MarketCreationStepType, marketCreationSteps, - stepForFormKey, + sectionForFormKey, stepFormKeys, } from "./types/step"; import { useMarketCreationFormValidator } from "./types/validation"; @@ -148,6 +148,8 @@ export const useMarketDraftEditor = ({ const validator = useMarketCreationFormValidator(draft.form); const fieldsState = useMemo(() => { + if (!validator) return FieldsState.empty(); + const parsed = validator.safeParse(draft.form); const fieldsState = marketCreationFormKeys.reduce( @@ -267,7 +269,7 @@ export const useMarketDraftEditor = ({ touchState: { ...draft.touchState, [key]: true }, }; if (!draft.isWizard) { - const section = stepForFormKey(key); + const section = sectionForFormKey(key); newDraft.stepReachState[section] = true; } update(newDraft); @@ -280,7 +282,7 @@ export const useMarketDraftEditor = ({ touchState: { ...draft.touchState, [key]: true }, }; if (!draft.isWizard) { - const section = stepForFormKey(key); + const section = sectionForFormKey(key); newDraft.stepReachState[section] = true; } update(newDraft); @@ -309,7 +311,7 @@ export const useMarketDraftEditor = ({ const prevAnswersLength = usePrevious(draft.form.answers?.answers?.length); useEffect(() => { - if (!draft.form.answers) return; + if (!draft.form.answers || !draft.form.liquidity) return; const baseAmount = minBaseLiquidity[draft.form.currency!] ? `${minBaseLiquidity[draft.form.currency!] / 2}` diff --git a/lib/state/market-creation/types/draft.ts b/lib/state/market-creation/types/draft.ts index 0816c7290..fe0af4f99 100644 --- a/lib/state/market-creation/types/draft.ts +++ b/lib/state/market-creation/types/draft.ts @@ -46,7 +46,7 @@ export type MarketDraftState = { /** * Create a new empty draft state. */ -export const empty = (): MarketDraftState => ({ +export const empty = () => ({ isWizard: true, currentStep: { label: "Currency", @@ -79,4 +79,4 @@ export const empty = (): MarketDraftState => ({ Currency: true, }, isPublished: false, -}); +} satisfies MarketDraftState); diff --git a/lib/state/market-creation/types/form.ts b/lib/state/market-creation/types/form.ts index 248df7b3e..28ac98188 100644 --- a/lib/state/market-creation/types/form.ts +++ b/lib/state/market-creation/types/form.ts @@ -1,7 +1,6 @@ import { CreateMarketBaseParams, CreateMarketParams, - MarketMetadata, MetadataStorage, RpcContext, ZTG, @@ -11,9 +10,11 @@ import { KeyringPairOrExtSigner } from "@zeitgeistpm/sdk/dist/types"; import { ChainTime } from "@zeitgeistpm/utility/dist/time"; import Decimal from "decimal.js"; import { BLOCK_TIME_SECONDS } from "lib/constants"; +import { getMetadataForCurrency } from "lib/constants/supported-currencies"; import moment from "moment"; import { DeepRequired } from "react-hook-form"; import * as z from "zod"; +import { tickersForAnswers } from "../util/tickers"; import { timelineAsBlocks } from "./timeline"; import { IOAnswers, @@ -34,8 +35,6 @@ import { IOTags, IOYesNoAnswers, } from "./validation"; -import { tickersForAnswers } from "../util/tickers"; -import { getMetadataForCurrency } from "lib/constants/supported-currencies"; /** * This is the type of the full market creation form data that is used to create a market. diff --git a/lib/state/market-creation/types/step.ts b/lib/state/market-creation/types/step.ts index f7e06ed60..45b5b5ee2 100644 --- a/lib/state/market-creation/types/step.ts +++ b/lib/state/market-creation/types/step.ts @@ -75,11 +75,14 @@ export const stepFormKeys: Record< * Get the market step type a given form key is related to. * @param key - the form key to get the section for */ -export const stepForFormKey = ( +export const sectionForFormKey = ( key: keyof MarketFormData, ): MarketCreationStepType => { for (const sectionKey in stepFormKeys) { if (stepFormKeys[sectionKey].includes(key)) return sectionKey as MarketCreationStepType; } + throw new Error( + `[SHOULD BE UNREACHABLE] No section found for form key ${key}`, + ); }; diff --git a/lib/state/market-creation/types/timeline.ts b/lib/state/market-creation/types/timeline.ts index e474d1752..039e48348 100644 --- a/lib/state/market-creation/types/timeline.ts +++ b/lib/state/market-creation/types/timeline.ts @@ -25,19 +25,19 @@ export const timelineAsBlocks = ( const gracePeriodEndBlock = form.gracePeriod?.type === "date" - ? form.gracePeriod?.block + ? dateBlock(chainTime, new Date(form.gracePeriod?.date)) : marketEndBlock + (form.gracePeriod ? durationasBlocks(form.gracePeriod) : 0); const reportPeriodEndBlock = form.reportingPeriod?.type === "date" - ? form.reportingPeriod?.block + ? dateBlock(chainTime, new Date(form.reportingPeriod?.date)) : gracePeriodEndBlock + (form.reportingPeriod ? durationasBlocks(form.reportingPeriod) : 0); const disputePeriodEndBlock = form.disputePeriod?.type === "date" - ? form.disputePeriod?.block + ? dateBlock(chainTime, new Date(form.disputePeriod?.date)) : reportPeriodEndBlock + (form.disputePeriod ? durationasBlocks(form.disputePeriod) : 0); diff --git a/lib/state/market-creation/types/validation.ts b/lib/state/market-creation/types/validation.ts index f68ed5fd1..e6f110c04 100644 --- a/lib/state/market-creation/types/validation.ts +++ b/lib/state/market-creation/types/validation.ts @@ -36,7 +36,7 @@ export const createMarketFormValidator = ({ chainTime, }: MarketValidationDependencies) => { const timeline = timelineAsBlocks(form, chainTime).unwrap(); - console.log(timeline); + return z .object({ currency: IOCurrency, @@ -138,10 +138,13 @@ export const createMarketFormValidator = ({ */ export const useMarketCreationFormValidator = ( form: Partial, -): ReturnType => { +): ReturnType | undefined => { const { data: deadlineConstants } = useMarketDeadlineConstants(); const chainTime = useChainTime(); + return useMemo(() => { + if (!deadlineConstants || !chainTime) return undefined; + return createMarketFormValidator({ form, deadlineConstants, @@ -228,7 +231,12 @@ export const IOEndDate = z export const IOPeriodDateOption = z.object({ type: z.literal("date"), - block: z.number(), + date: z + .string() + .datetime() + .refine((date) => new Date(date) > new Date(), { + message: "End date must be in the future", + }), }); export const IOPeriodDurationOption = z.object({ diff --git a/lib/state/market-creation/util/tickers.ts b/lib/state/market-creation/util/tickers.ts index f4a70f985..0cdddb2e8 100644 --- a/lib/state/market-creation/util/tickers.ts +++ b/lib/state/market-creation/util/tickers.ts @@ -36,31 +36,35 @@ export const tickersForAnswers = ( * Generate appropriate tickers for categorical answers. */ export const createCategoricalTickers = ( - answers: string[], -): { [key: string]: string } => { - const tickers: { [key: string]: string } = {}; - const usedTickers: { [key: string]: boolean } = {}; + strings: string[], +): Record => { + const tokens: Record = {}; - for (const description of answers) { - const words = description.split(" "); + for (const str of strings) { + const words = str.split(" "); + let token = ""; - let ticker = ( - words[0].slice(0, 3) + words[words.length - 1].slice(0, 3) - ).toUpperCase(); + for (let i = 0; i < words.length; i++) { + const word = words[i]; + const truncatedWord = word.slice(0, 3); - if (usedTickers[ticker]) { - let count = 1; - let newTicker = ticker; - while (usedTickers[newTicker]) { - count++; - newTicker = ticker + String(count); + if (i === words.length - 1) { + token += truncatedWord.toUpperCase(); + } else { + token += truncatedWord; } - ticker = newTicker; } - usedTickers[ticker] = true; - tickers[description] = ticker; + tokens[str] = token; } - return tickers; + return tokens; +}; + +const input = ["foo bar", "foo bar baz", "foo bar baz qux"]; + +const output = { + "foo bar": "FOOBAR", + "foo bar baz": "FOOBAZ", + "foo bar baz qux": "FOOQUX", }; diff --git a/lib/state/util/persistent-atom.ts b/lib/state/util/persistent-atom.ts index a79b2342c..75aa48547 100644 --- a/lib/state/util/persistent-atom.ts +++ b/lib/state/util/persistent-atom.ts @@ -1,6 +1,6 @@ import { atom, getDefaultStore, createStore } from "jotai"; import { RESET, atomWithStorage } from "jotai/utils"; -import { tryCatch } from "@zeitgeistpm/utility/dist/option"; +import { tryCatch, fromNullable } from "@zeitgeistpm/utility/dist/option"; export type PersistentAtomConfig = { /** @@ -10,7 +10,7 @@ export type PersistentAtomConfig = { /** * Default value if no value is stored. */ - defaultValue: Versioned; + defaultValue: T; /** * Migrations to run on the stored value. * @note index is used as version number. @@ -45,14 +45,15 @@ export type Migration = (state: A) => B; * @returns WritableAtom(opts: PersistentAtomConfig>) => { - const parsedStorageValue = tryCatch( - () => - JSON.parse(globalThis.localStorage?.getItem(opts.key)) as Versioned, - ).unwrapOr(opts.defaultValue); + const parsedStorageValue = fromNullable( + globalThis.localStorage?.getItem(opts.key), + ) + .bind((raw) => tryCatch(() => JSON.parse(raw) as Versioned)) + .unwrapOr(opts.defaultValue); const storageAtom = atomWithStorage>( opts.key, - (parsedStorageValue ?? opts.defaultValue) as Versioned, + parsedStorageValue, ); const store = opts.store ?? getDefaultStore(); @@ -61,25 +62,27 @@ export const persistentAtom = (opts: PersistentAtomConfig>) => { const nextVersion = opts.migrations?.length ?? 0; if (nextVersion > initialVersion) { - console.group(`state-migration:${opts.key}`); + let newState = initialState; + const migrations = opts.migrations?.slice(initialVersion); - const migrations = opts.migrations.slice(initialVersion); + if (migrations) { + console.group(`state-migration:${opts.key}`); + console.info(`initial [version: ${initialVersion}]`, initialState); - console.info(`initial [version: ${initialVersion}]`, initialState); + newState = migrations.reduce((acc, migration, version) => { + const nextVersion = initialVersion + version + 1; + const nextState = { ...migration(acc), __version: nextVersion }; - const newState = migrations.reduce((acc, migration, version) => { - const nextVersion = initialVersion + version + 1; - const nextState = { ...migration(acc), __version: nextVersion }; + if (migrations.length == 1 || nextVersion !== 1) { + const step = version == migrations.length - 1 ? "final" : "next"; + console.info(`${step} [version: ${nextVersion}]`, nextState); + } - if (migrations.length == 1 || nextVersion !== 1) { - const step = version == migrations.length - 1 ? "final" : "next"; - console.info(`${step} [version: ${nextVersion}]`, nextState); - } - - return nextState; - }, initialState); + return nextState; + }, initialState); - console.groupEnd(); + console.groupEnd(); + } store.set(storageAtom, { ...newState, __version: nextVersion }); } diff --git a/lib/state/wallet.ts b/lib/state/wallet.ts index 03c21bffe..ba5e1010c 100644 --- a/lib/state/wallet.ts +++ b/lib/state/wallet.ts @@ -10,48 +10,51 @@ import { TalismanWallet } from "../wallets/talisman-wallet"; import { Wallet, WalletAccount } from "../wallets/types"; import { persistentAtom } from "./util/persistent-atom"; -export type UseWallet = WalletState & - Pick & { - /** - * The real address of the current wallet. - * Use this to read data from the blockchain or the indexer when it comes to assets and markets. - * It will be the address the activeAccount is proxying for if proxy execution is enabled. - */ - realAddress?: string; - /** - * The active account of the current wallet. - */ - activeAccount?: WalletAccount; - /** - * Whether the wallet is nova wallet. - */ - isNovaWallet: boolean; - /** - * Get the active signer for transactions. Is either the real account or the proxy account. - */ - getSigner: () => KeyringPairOrExtSigner | null; - /** - * Select a wallet. - * @param wallet the selected wallet id or instance - * @returns void - */ - selectWallet: (wallet: Wallet | string) => void; - /** - * Select an address. - * @param account the address to select - * @returns void - */ - selectAccount: (account: string) => void; - /** - * Disconnect the wallet. - * @returns void - */ - disconnectWallet: () => void; - /** - * Set if proxy execution is enabled. - */ - setProxyFor: (real: string, conf: ProxyConfig) => void; - }; +export type UseWallet = WalletState & { + /** + * The real address of the current wallet. + * Use this to read data from the blockchain or the indexer when it comes to assets and markets. + * It will be the address the activeAccount is proxying for if proxy execution is enabled. + */ + realAddress?: string; + /** + * The active account of the current wallet. + */ + activeAccount?: WalletAccount; + /** + * Whether the wallet is nova wallet. + */ + isNovaWallet: boolean; + /** + * Get the active signer for transactions. Is either the real account or the proxy account. + */ + getSigner: () => KeyringPairOrExtSigner | undefined; + /** + * Select a wallet. + * @param wallet the selected wallet id or instance + * @returns void + */ + selectWallet: (wallet: Wallet | string) => void; + /** + * Select an address. + * @param account the address to select + * @returns void + */ + selectAccount: (account: string) => void; + /** + * Disconnect the wallet. + * @returns void + */ + disconnectWallet: () => void; + /** + * Set if proxy execution is enabled. + */ + setProxyFor: (real: string, conf: ProxyConfig) => void; + /** + * Get the proxy config for an address(real). + */ + getProxyFor: (address?: string) => ProxyConfig | undefined; +}; export type ProxyConfig = { enabled: boolean; @@ -161,15 +164,15 @@ const userConfigAtom = persistentAtom({ if ( selectedAddress && - tryCatch(() => encodeAddress(selectedAddress)).isNone() + tryCatch(() => encodeAddress(selectedAddress!)).isNone() ) { console.log("Invalid address in localStorage, disconnecting wallet."); return {}; } return { - walletId, - selectedAddress, + walletId: walletId ?? undefined, + selectedAddress: selectedAddress ?? undefined, }; } @@ -195,7 +198,7 @@ export const supportedWallets = [ new TalismanWallet(), ]; -let accountsSubscriptionUnsub: VoidFunction | undefined; +let accountsSubscriptionUnsub: VoidFunction | undefined | null; /** * Enable a wallet by enabling the extension and setting the wallet atom state to connected. @@ -232,15 +235,16 @@ const enableWallet = async (walletId: Wallet | string) => { store.set(walletAtom, (state) => { return { ...state, - connected: accounts.length > 0, - accounts: accounts.map((account) => { - return { - ...account, - address: encodeAddress(account.address, 73), - }; - }), + connected: Boolean(accounts && accounts.length > 0), + accounts: + accounts?.map((account) => { + return { + ...account, + address: encodeAddress(account.address, 73), + }; + }) ?? [], errors: - accounts.length === 0 + accounts?.length === 0 ? [ { extensionName: wallet.extensionName, @@ -260,7 +264,7 @@ const enableWallet = async (walletId: Wallet | string) => { accounts: [], errors: [ { - extensionName: wallet?.extensionName, + extensionName: wallet?.extensionName ?? "unknown wallet", type: "InteractionDenied", }, ], @@ -273,8 +277,9 @@ const enableWallet = async (walletId: Wallet | string) => { /** * Enable wallet on first load if wallet id is set. */ -if (store.get(userConfigAtom).walletId) { - enableWallet(store.get(userConfigAtom).walletId); +const initialWalletId = store.get(userConfigAtom).walletId; +if (initialWalletId) { + enableWallet(initialWalletId); } /** @@ -301,7 +306,12 @@ export const useWallet = (): UseWallet => { }; const getSigner = (): KeyringPairOrExtSigner | undefined => { - if (walletState.wallet == null || !activeAccount) return; + if ( + walletState.wallet == null || + !activeAccount || + !walletState.wallet.signer + ) + return; return { address: activeAccount.address, signer: walletState.wallet.signer, @@ -331,6 +341,10 @@ export const useWallet = (): UseWallet => { }); }; + const getProxyFor = (address: string): ProxyConfig | undefined => { + return userConfig.proxyFor?.[address]; + }; + const activeAccount = useMemo(() => { const userSelectedAddress = walletState.accounts.find((acc) => { return ( @@ -365,5 +379,6 @@ export const useWallet = (): UseWallet => { disconnectWallet, isNovaWallet, setProxyFor, + getProxyFor, }; }; diff --git a/pages/settings.tsx b/pages/settings.tsx index a477a0b84..fd93095cb 100644 --- a/pages/settings.tsx +++ b/pages/settings.tsx @@ -204,7 +204,7 @@ const ProxySettings = () => { const [sdk] = useSdkv2(); const wallet = useWallet(); - const proxy = wallet.proxyFor?.[wallet.activeAccount?.address]; + const proxy = wallet.getProxyFor(wallet.activeAccount?.address); const { register, @@ -221,6 +221,7 @@ const ProxySettings = () => { }); const onSubmit = (data: ProxyConfig) => { + if (!wallet.activeAccount?.address) return; wallet.setProxyFor(wallet.activeAccount?.address, data); reset(data); };