@starknet-react/core
Version:
Starknet React is a collection of React hooks for Starknet.
126 lines (114 loc) • 3.59 kB
text/typescript
import type { Address } from "@starknet-react/chains";
import type { Abi } from "abi-wan-kanabi";
import type {
ContractFunctions,
ContractFunctionsPopulateTransaction,
ExtractAbiFunction,
ExtractAbiFunctionNames,
ExtractArgs,
FunctionRet,
} from "abi-wan-kanabi/kanabi";
import { useMemo } from "react";
import {
type Call,
type CallOptions,
Contract,
type ProviderInterface,
} from "starknet";
import { useStarknet } from "../context/starknet";
// did this because "Omit" wont work directly over an abstract class
type Contract_ = {
[K in keyof Contract as K extends "populate" | "populateTransaction" | "call"
? never
: K]: Contract[K];
};
type ArgsArray_<
TAbi extends Abi,
TFunctionName extends ExtractAbiFunctionNames<TAbi>,
> = ExtractArgs<TAbi, ExtractAbiFunction<TAbi, TFunctionName>>;
// with reference to 'abi-wan-kanabi' but with args accepting only array type
type TypedContractActions_<TAbi extends Abi> = {
call<TFunctionName extends ExtractAbiFunctionNames<TAbi>>(
method: TFunctionName,
args?: ArgsArray_<TAbi, TFunctionName>,
options?: CallOptions,
): Promise<FunctionRet<TAbi, TFunctionName>>;
populate<TFunctionName extends ExtractAbiFunctionNames<TAbi>>(
method: TFunctionName,
args?: ArgsArray_<TAbi, TFunctionName>,
): Call;
populateTransaction: ContractFunctionsPopulateTransaction<TAbi>;
/**
* Returns same contract but typed and with the applied options for the next call.
* @note if using `contract.withOptions(...).methodName()`, you must NOT pass options again in the `methodName(...argsOfMethodOnly)` as an argument.
* @important You must apply the options again for each call
*/
withOptions(options: CallOptions): StarknetTypedContract<TAbi>;
};
type TypedContract_<TAbi extends Abi> = TypedContractActions_<TAbi> &
ContractFunctions<TAbi>;
export type StarknetTypedContract<TAbi extends Abi> = TypedContract_<TAbi> &
Contract_;
/** Arguments for `useContract`. */
export interface UseContractArgs<TAbi extends Abi> {
/** The contract abi
* @remarks
*
* You must pass ABI as a const
*
* @example
* abi: [
* {
* type: "function",
* name: "fn_simple_array",
* inputs: [
* {
* name: "arg",
* type: "core::array::Array::<core::integer::u8>",
* },
* ],
* outputs: [],
* state_mutability: "view",
* }
* ] as const
*
*/
abi?: TAbi;
/** The contract address. */
address?: Address;
/** The provider, by default it will be the current one. */
provider?: ProviderInterface | null;
}
/** Value returned from `useContract`. */
export interface UseContractResult<TAbi extends Abi> {
/** The contract. */
contract?: StarknetTypedContract<TAbi>;
}
/**
* Hook to bind a `Contract` instance.
*
* @remarks
*
* - The returned contract is a starknet.js `Contract` object.
* - Must pass `abi` as const for strict type safety
*
*/
export function useContract<TAbi extends Abi>({
abi,
address,
provider: providedProvider,
}: UseContractArgs<TAbi>): UseContractResult<TAbi> {
const { provider: currentProvider } = useStarknet();
const contract = useMemo(() => {
const provider = providedProvider ? providedProvider : currentProvider;
if (abi && address && provider) {
return new Contract({
abi,
address,
providerOrAccount: provider,
}).typedv2(abi) as StarknetTypedContract<TAbi>;
}
return undefined;
}, [abi, address, providedProvider, currentProvider]);
return { contract };
}