Skip to content

Commit

Permalink
core: add useDeployAccount hook and demo
Browse files Browse the repository at this point in the history
  • Loading branch information
fracek committed Sep 27, 2023
1 parent c058c73 commit 792a27a
Show file tree
Hide file tree
Showing 26 changed files with 1,443 additions and 105 deletions.
5 changes: 5 additions & 0 deletions .changeset/thirty-paws-dance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@starknet-react/core": patch
---

Add useDeployAccount hook
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
"lint": "turbo run lint --parallel",
"lint:fix": "turbo run lint:fix --parallel",
"format": "turbo run format --parallel",
"typedoc": "turbo run typedoc --filter @starknet-react/*",
"clean": "turbo run clean --parallel",
"release": "pnpm build && pnpm changeset publish",
"postinstall": "pnpm run prepare",
Expand All @@ -33,7 +32,7 @@
"pretty-quick": "^3.1.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"starknet": "5.18.0",
"starknet": "5.19.0",
"turbo": "^1.10.14",
"typescript": "^4.9.5"
},
Expand Down
5 changes: 2 additions & 3 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,18 @@
"README.md"
],
"scripts": {
"build": "tsup && typedoc",
"build": "tsup",
"clean": "rimraf dist",
"lint": "biome check .",
"lint:fix": "pnpm lint --apply",
"format": "biome format . --write",
"typedoc": "typedoc",
"test": "vitest",
"test:typecheck": "vitest typecheck"
},
"peerDependencies": {
"get-starknet-core": "^3.2.0",
"react": "^18.0",
"starknet": "^5.18.0"
"starknet": "^5.19.0"
},
"dependencies": {
"@starknet-react/chains": "workspace:^",
Expand Down
26 changes: 26 additions & 0 deletions packages/core/src/context/account.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React, { useContext } from "react";

import { AccountInterface } from "starknet";

const AccountContext = React.createContext<AccountInterface | undefined>(
undefined,
);

export function useStarknetAccount() {
const account = useContext(AccountContext);
return { account };
}

export function AccountProvider({
account,
children,
}: {
account?: AccountInterface;
children: React.ReactNode;
}) {
return (
<AccountContext.Provider value={account}>
{children}
</AccountContext.Provider>
);
}
2 changes: 2 additions & 0 deletions packages/core/src/context/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import React from "react";

import { StarknetProvider, StarknetProviderProps } from "./starknet";

export { AccountProvider as OverrideAccount } from "./account";

export type StarknetConfigProps = StarknetProviderProps;

export function StarknetConfig({ children, ...config }: StarknetConfigProps) {
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/context/starknet.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { devnet } from "@starknet-react/chains";
import { describe, expect, it } from "vitest";
import { address, connector } from "../../test/devnet";
import { connector } from "../../test/devnet";
import { act, renderHook } from "../../test/react";

import { useStarknet } from "./starknet";
Expand All @@ -18,12 +18,12 @@ describe("StarknetProvider", () => {
it("connects to a connector", async () => {
const { result } = renderHook(() => useStarknet());

expect(result.current.account).toBeUndefined();
expect(result.current.connector).toBeUndefined();

await act(async () => {
await result.current.connect({ connector: connector });
});

expect(result.current.account?.address).toEqual(address);
expect(result.current.connector).toBeDefined();
});
});
11 changes: 5 additions & 6 deletions packages/core/src/context/starknet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,10 @@ import { AccountInterface, ProviderInterface, RpcProvider } from "starknet";
import { Connector } from "~/connectors";
import { ConnectorNotFoundError } from "~/errors";
import { ChainProviderFactory } from "~/providers";
import { AccountProvider } from "./account";

/** State of the Starknet context. */
export interface StarknetState {
/** Connected account. */
account?: AccountInterface;
/** Connected connector. */
connector?: Connector;
/** Connect the given connector. */
Expand Down Expand Up @@ -138,7 +137,7 @@ function useStarknetManager({
providers,
connectors = [],
autoConnect = false,
}: UseStarknetManagerProps): StarknetState {
}: UseStarknetManagerProps): StarknetState & { account?: AccountInterface } {
const initialChain = chains[0];
if (initialChain === undefined) {
throw new Error("Must provide at least one chain.");
Expand Down Expand Up @@ -241,9 +240,9 @@ function useStarknetManager({
}, []);

return {
account: state.currentAccount,
provider: state.currentProvider,
chain: state.currentChain,
account: state.currentAccount,
connector: state.currentConnector,
connect,
disconnect,
Expand Down Expand Up @@ -277,7 +276,7 @@ export function StarknetProvider({
queryClient,
children,
}: StarknetProviderProps): JSX.Element {
const state = useStarknetManager({
const { account, ...state } = useStarknetManager({
chains,
providers,
connectors,
Expand All @@ -287,7 +286,7 @@ export function StarknetProvider({
return (
<QueryClientProvider client={queryClient ?? new QueryClient()}>
<StarknetContext.Provider value={state}>
{children}
<AccountProvider account={account}>{children}</AccountProvider>
</StarknetContext.Provider>
</QueryClientProvider>
);
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export * from "./useContract";
export * from "./useContractFactory";
export * from "./useContractRead";
export * from "./useContractWrite";
export * from "./useDeployAccount";
export * from "./useDisconnect";
export * from "./useInvalidateOnBlock";
export * from "./useNetwork";
Expand Down
17 changes: 16 additions & 1 deletion packages/core/src/hooks/useAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useCallback, useEffect, useState } from "react";
import { AccountInterface } from "starknet";

import { Connector } from "~/connectors";
import { useStarknetAccount } from "~/context/account";
import { useStarknet } from "~/context/starknet";

import { useConnect } from "./useConnect";
Expand Down Expand Up @@ -68,7 +69,7 @@ export function useAccount({
onConnect,
onDisconnect,
}: UseAccountProps = {}): UseAccountResult {
const { account: connectedAccount } = useStarknet();
const { account: connectedAccount } = useStarknetAccount();
const { connectors } = useConnect();
const [state, setState] = useState<UseAccountResult>({
status: "disconnected",
Expand All @@ -87,6 +88,7 @@ export function useAccount({
isReconnecting: false,
});
}

for (const connector of connectors) {
if (!connector.available()) continue;
const connAccount = await connector.account();
Expand All @@ -107,6 +109,19 @@ export function useAccount({
});
}
}

// If we get here, we're not connected to any connector.
// This can happen if it's an arcade account.
setState({
connector: undefined,
account: connectedAccount,
address: connectedAccount.address,
status: "connected",
isConnected: true,
isConnecting: false,
isDisconnected: false,
isReconnecting: false,
});
}, [
setState,
connectedAccount,
Expand Down
5 changes: 3 additions & 2 deletions packages/core/src/hooks/useBalance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export function useBalance({
token,
address,
watch = false,
enabled: enabled_ = true,
...props
}: UseBalanceProps) {
const { chain } = useNetwork();
Expand All @@ -56,8 +57,8 @@ export function useBalance({
);

const enabled = useMemo(
() => Boolean(props.enabled && contract && address),
[chain, contract, address],
() => Boolean(enabled_ && contract && address),
[enabled_, contract, address],
);

useInvalidateOnBlock({
Expand Down
48 changes: 37 additions & 11 deletions packages/core/src/hooks/useContractWrite.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
import { AccountInterface, Call, InvokeFunctionResponse } from "starknet";
import {
Abi,
AccountInterface,
Call,
InvocationsDetails,
InvokeFunctionResponse,
} from "starknet";

import { UseMutationProps, UseMutationResult, useMutation } from "~/query";

import { useAccount } from "./useAccount";

/** Arguments for `useContractWrite`. */
export type Variables = {
export type ContractWriteVariables = {
/** List of smart contract calls to execute. */
calls?: Call[];
/** Contract ABIs for better displaying. */
abis?: Abi[];
/** Transaction options. */
options?: InvocationsDetails;
};

export type UseContractWriteProps = Variables &
UseMutationProps<InvokeFunctionResponse, Error, Variables>;
export type UseContractWriteProps = ContractWriteVariables &
UseMutationProps<InvokeFunctionResponse, Error, ContractWriteVariables>;

type MutationResult = UseMutationResult<
InvokeFunctionResponse,
Error,
Variables
ContractWriteVariables
>;

export type UseContractWriteResult = Omit<
Expand All @@ -39,6 +49,8 @@ export type UseContractWriteResult = Omit<
*/
export function useContractWrite({
calls,
abis,
options,
...props
}: UseContractWriteProps): UseContractWriteResult {
const { account } = useAccount();
Expand All @@ -56,8 +68,8 @@ export function useContractWrite({
status,
variables,
} = useMutation({
mutationKey: mutationKey({ account, calls }),
mutationFn: mutationFn({ account, calls }),
mutationKey: mutationKey({ account, calls, abis, options }),
mutationFn: mutationFn({ account, calls, abis, options }),
...props,
});

Expand All @@ -80,17 +92,31 @@ export function useContractWrite({
function mutationKey({
account,
calls,
}: { account?: AccountInterface; calls?: Call[] }) {
return [{ entity: "contractWrite", account, calls }] as const;
abis,
options,
}: {
account?: AccountInterface;
calls?: Call[];
abis?: Abi[];
options?: InvocationsDetails;
}) {
return [{ entity: "contractWrite", account, calls, abis, options }] as const;
}

function mutationFn({
account,
calls,
}: { account?: AccountInterface; calls?: Call[] }) {
abis,
options,
}: {
account?: AccountInterface;
calls?: Call[];
abis?: Abi[];
options?: InvocationsDetails;
}) {
return async function () {
if (!account) throw new Error("account is required");
if (!calls || calls.length === 0) throw new Error("calls are required");
return account?.execute(calls);
return await account?.execute(calls, abis, options);
};
}
Loading

0 comments on commit 792a27a

Please sign in to comment.