diff --git a/packages/core/src/hooks/useAccount.ts b/packages/core/src/hooks/useAccount.ts index 502bc9aa..b4e91cbb 100644 --- a/packages/core/src/hooks/useAccount.ts +++ b/packages/core/src/hooks/useAccount.ts @@ -3,7 +3,6 @@ import { AccountInterface } from "starknet"; import { Connector } from "~/connectors"; import { useStarknetAccount } from "~/context/account"; -import { useStarknet } from "~/context/starknet"; import { useConnect } from "./useConnect"; diff --git a/packages/core/src/hooks/useBalance.test.ts b/packages/core/src/hooks/useBalance.test.ts index 71d205da..6e7b34eb 100644 --- a/packages/core/src/hooks/useBalance.test.ts +++ b/packages/core/src/hooks/useBalance.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { account } from "../../test/devnet"; +import { accounts } from "../../test/devnet"; import { renderHook, waitFor } from "../../test/react"; import { useBalance } from "./useBalance"; @@ -28,7 +28,7 @@ describe("useBalance", () => { // Some issue with the RPC provider. it.skip("returns the balance", async () => { const { result } = renderHook(() => - useBalance({ address: account.address }), + useBalance({ address: accounts.goerli[0].address }), ); await waitFor(() => { diff --git a/packages/core/src/hooks/useBalance.ts b/packages/core/src/hooks/useBalance.ts index 8b9660f4..a271801a 100644 --- a/packages/core/src/hooks/useBalance.ts +++ b/packages/core/src/hooks/useBalance.ts @@ -10,9 +10,9 @@ import { import { z } from "zod"; import { UseQueryProps, UseQueryResult, useQuery } from "~/query"; + import { useContract } from "./useContract"; import { useInvalidateOnBlock } from "./useInvalidateOnBlock"; - import { useNetwork } from "./useNetwork"; export type Balance = { diff --git a/packages/core/src/hooks/useContract.test.ts b/packages/core/src/hooks/useContract.test.ts index 797c2659..62ee1876 100644 --- a/packages/core/src/hooks/useContract.test.ts +++ b/packages/core/src/hooks/useContract.test.ts @@ -64,7 +64,7 @@ describe("useContract", () => { "type": "function", }, ], - "address": "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", + "address": "0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7", "callData": CallData { "abi": [ { diff --git a/packages/core/src/hooks/useContractRead.test.ts b/packages/core/src/hooks/useContractRead.test.ts new file mode 100644 index 00000000..12ac38ae --- /dev/null +++ b/packages/core/src/hooks/useContractRead.test.ts @@ -0,0 +1,61 @@ +import { Call, Contract } from "starknet"; +import { describe, expect, it } from "vitest"; +import { accounts, defaultConnector, tokenAddress } from "../../test/devnet"; +import { act, renderHook, waitFor } from "../../test/react"; + +import { useContractRead } from "./useContractRead"; + +const abi = [ + { + members: [ + { + name: "low", + offset: 0, + type: "felt", + }, + { + name: "high", + offset: 1, + type: "felt", + }, + ], + name: "Uint256", + size: 2, + type: "struct", + }, + { + name: "balanceOf", + type: "function", + inputs: [ + { + name: "account", + type: "felt", + }, + ], + outputs: [ + { + name: "balance", + type: "Uint256", + }, + ], + stateMutability: "view", + }, +]; + +describe("useContractRead", () => { + it.skip("returns the contract read result", async () => { + const { result } = renderHook(() => + useContractRead({ + functionName: "balanceOf", + args: [accounts.goerli[0].address], + abi, + address: tokenAddress, + watch: true, + }), + ); + + await waitFor(() => { + expect(result.current.isSuccess).toBeTruthy(); + }); + }); +}); diff --git a/packages/core/src/hooks/useContractRead.ts b/packages/core/src/hooks/useContractRead.ts index e20e6f69..7eec68eb 100644 --- a/packages/core/src/hooks/useContractRead.ts +++ b/packages/core/src/hooks/useContractRead.ts @@ -59,6 +59,7 @@ export function useContractRead({ parseArgs, parseResult, watch = false, + enabled: enabled_ = true, ...props }: UseContractReadProps): UseContractReadResult { const { chain } = useNetwork(); @@ -70,8 +71,8 @@ export function useContractRead({ ); const enabled = useMemo( - () => Boolean(props.enabled && contract && functionName && args), - [props.enabled, contract, functionName, args], + () => Boolean(enabled_ && contract && functionName && args), + [enabled_, contract, functionName, args], ); useInvalidateOnBlock({ diff --git a/packages/core/src/hooks/useContractWrite.test.ts b/packages/core/src/hooks/useContractWrite.test.ts new file mode 100644 index 00000000..a7c8fa8f --- /dev/null +++ b/packages/core/src/hooks/useContractWrite.test.ts @@ -0,0 +1,91 @@ +import { Call, Contract } from "starknet"; +import { describe, expect, it } from "vitest"; +import { accounts, defaultConnector, tokenAddress } from "../../test/devnet"; +import { act, renderHook, waitFor } from "../../test/react"; +import { useConnect } from "./useConnect"; + +import { useContractWrite } from "./useContractWrite"; + +const abi = [ + { + members: [ + { + name: "low", + offset: 0, + type: "felt", + }, + { + name: "high", + offset: 1, + type: "felt", + }, + ], + name: "Uint256", + size: 2, + type: "struct", + }, + { + inputs: [ + { + name: "recipient", + type: "felt", + }, + { + name: "amount", + type: "Uint256", + }, + ], + name: "transfer", + outputs: [ + { + name: "success", + type: "felt", + }, + ], + type: "function", + }, +]; + +function useTestHook({ calls }: { calls: Call[] }) { + const writeResult = useContractWrite({ + calls, + }); + const { connectAsync, connector } = useConnect(); + return { + ...writeResult, + connectAsync, + connector, + }; +} + +describe("useContractWrite", () => { + it.skip("returns the transaction hash in the response", async () => { + const contract = new Contract(abi, tokenAddress); + const { result } = renderHook(() => + useTestHook({ + calls: [ + contract.populateTransaction.transfer(accounts.goerli[0].address, { + low: 1, + high: 0, + }), + ], + }), + ); + + await act(async () => { + await result.current.connectAsync({ connector: defaultConnector }); + }); + + await waitFor(() => { + expect(result.current.connector).toBeDefined(); + }); + + await act(async () => { + await result.current.writeAsync(); + }); + + await waitFor(() => { + // expect(result.current).toMatchInlineSnapshot(``); + }); + }); +}); diff --git a/packages/core/src/hooks/useContractWrite.ts b/packages/core/src/hooks/useContractWrite.ts index ee619d00..b3a46e0b 100644 --- a/packages/core/src/hooks/useContractWrite.ts +++ b/packages/core/src/hooks/useContractWrite.ts @@ -1,3 +1,4 @@ +import { useCallback } from "react"; import { Abi, AccountInterface, @@ -23,7 +24,7 @@ export type ContractWriteVariables = { export type UseContractWriteProps = ContractWriteVariables & UseMutationProps; -type MutationResult = UseMutationResult< +export type MutationResult = UseMutationResult< InvokeFunctionResponse, Error, ContractWriteVariables @@ -69,13 +70,39 @@ export function useContractWrite({ variables, } = useMutation({ mutationKey: mutationKey({ account, calls, abis, options }), - mutationFn: mutationFn({ account, calls, abis, options }), + mutationFn: mutationFn({ account }), ...props, }); + const write = useCallback( + (args?: ContractWriteVariables) => { + return mutate({ + ...(args ?? { + calls, + abis, + options, + }), + }); + }, + [mutate, calls, abis, options], + ); + + const writeAsync = useCallback( + (args?: ContractWriteVariables) => { + return mutateAsync({ + ...(args ?? { + calls, + abis, + options, + }), + }); + }, + [mutateAsync, calls, abis, options], + ); + return { - write: mutate, - writeAsync: mutateAsync, + write, + writeAsync, data, error, isError, @@ -105,16 +132,10 @@ function mutationKey({ function mutationFn({ account, - calls, - abis, - options, }: { account?: AccountInterface; - calls?: Call[]; - abis?: Abi[]; - options?: InvocationsDetails; }) { - return async function () { + return async function ({ calls, abis, options }: ContractWriteVariables) { if (!account) throw new Error("account is required"); if (!calls || calls.length === 0) throw new Error("calls are required"); return await account?.execute(calls, abis, options); diff --git a/packages/core/src/hooks/useSign.ts b/packages/core/src/hooks/useSign.ts index 22fec294..b337876e 100644 --- a/packages/core/src/hooks/useSign.ts +++ b/packages/core/src/hooks/useSign.ts @@ -4,7 +4,7 @@ import { AccountInterface, Signature, TypedData } from "starknet"; import { UseMutationProps, UseMutationResult, useMutation } from "~/query"; import { useAccount } from "./useAccount"; -type Variables = { account?: AccountInterface } & Partial; +type Variables = Partial; type MutationResult = UseMutationResult; @@ -45,32 +45,34 @@ export function useSignTypedData({ variables, } = useMutation({ mutationKey: mutationKey({ domain, types, message, primaryType }), - mutationFn: mutateFn, + mutationFn: mutateFn({ account }), ...props, }); const signTypedData = useCallback( (args?: Partial) => - mutate({ - domain: args?.domain ?? domain, - types: args?.types ?? types, - message: args?.message ?? message, - primaryType: args?.primaryType ?? primaryType, - account, - }), - [mutate, account, domain, types, message, primaryType], + mutate( + args ?? { + domain, + types, + message, + primaryType, + }, + ), + [mutate, domain, types, message, primaryType], ); const signTypedDataAsync = useCallback( (args?: Partial) => - mutateAsync({ - domain: args?.domain ?? domain, - types: args?.types ?? types, - message: args?.message ?? message, - primaryType: args?.primaryType ?? primaryType, - account, - }), - [mutateAsync, account, domain, types, message, primaryType], + mutateAsync( + args ?? { + domain, + types, + message, + primaryType, + }, + ), + [mutateAsync, domain, types, message, primaryType], ); return { @@ -106,17 +108,18 @@ function mutationKey({ ] as const; } -function mutateFn({ - account, - domain, - types, - message, - primaryType, -}: Variables): Promise { - if (!account) throw new Error("account is required"); - if (!domain) throw new Error("domain is required"); - if (!types) throw new Error("types is required"); - if (!message) throw new Error("message is required"); - if (!primaryType) throw new Error("primaryType is required"); - return account.signMessage({ domain, types, message, primaryType }); +function mutateFn({ account }: { account?: AccountInterface }) { + return function ({ + domain, + types, + message, + primaryType, + }: Variables): Promise { + if (!account) throw new Error("account is required"); + if (!domain) throw new Error("domain is required"); + if (!types) throw new Error("types is required"); + if (!message) throw new Error("message is required"); + if (!primaryType) throw new Error("primaryType is required"); + return account.signMessage({ domain, types, message, primaryType }); + }; } diff --git a/packages/core/src/hooks/useStarkAddress.ts b/packages/core/src/hooks/useStarkAddress.ts index 7bc73e8f..5c43da36 100644 --- a/packages/core/src/hooks/useStarkAddress.ts +++ b/packages/core/src/hooks/useStarkAddress.ts @@ -42,14 +42,12 @@ export type UseStarkAddressResult = UseQueryResult; export function useStarkAddress({ name, contract, + enabled: enabled_ = true, ...props }: UseStarkAddressProps): UseStarkAddressResult { const { provider } = useProvider(); - const enabled = useMemo( - () => Boolean(props.enabled && name), - [props.enabled, name], - ); + const enabled = useMemo(() => Boolean(enabled_ && name), [enabled_, name]); return useQuery({ queryKey: queryKey({ name, contract }), diff --git a/packages/core/src/hooks/useStarkName.ts b/packages/core/src/hooks/useStarkName.ts index e46fab9a..943cce91 100644 --- a/packages/core/src/hooks/useStarkName.ts +++ b/packages/core/src/hooks/useStarkName.ts @@ -61,13 +61,14 @@ export type StarkNameResult = UseQueryResult; export function useStarkName({ address, contract, + enabled: enabled_ = true, ...props }: StarkNameArgs): StarkNameResult { const { provider } = useProvider(); const enabled = useMemo( - () => Boolean(props.enabled && address), - [props.enabled, address], + () => Boolean(enabled_ && address), + [enabled_, address], ); return useQuery({ diff --git a/packages/core/test/devnet.ts b/packages/core/test/devnet.ts index 9df0afb5..b7cd5411 100644 --- a/packages/core/test/devnet.ts +++ b/packages/core/test/devnet.ts @@ -6,7 +6,7 @@ import { MockConnector } from "../src/connectors"; const provider = new RpcProvider({ nodeUrl: devnet.rpcUrls.public.http[0] }); export const tokenAddress = - "0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"; + "0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7"; const devnetAccounts = [ { diff --git a/website/components/demos/accounts.tsx b/website/components/demos/accounts.tsx index 0790ef3e..a17f9d2e 100644 --- a/website/components/demos/accounts.tsx +++ b/website/components/demos/accounts.tsx @@ -154,7 +154,7 @@ function FundBurnerAccount({ address }: { address?: string }) { : [], }); const fundAccount = useCallback(async () => { - await writeAsync({}); + await writeAsync(); setFundedAddress(address); }, [setFundedAddress, address, writeAsync]); @@ -292,7 +292,7 @@ function InitializeBurnerAccount() {

{data ? null : (