Starknet-React: React Integration
In the starknet ecosystem, several tools are available for front-end development. The most notable are:
-
starknet-react (documentation): A collection of React hooks tailored for Starknet, inspired by wagmi and powered by starknet.js.
-
starknet.js: This JavaScript library facilitates interactions with Starknet contracts, akin to web3.js for Ethereum.
Developed by the Apibara team, Starknet React is an open-source suite of React providers and hooks specifically for Starknet.
Integrating Starknet React
The fastest way to get started using Starknet React is by using the create-starknet
Command Line Interface (CLI). The tool will guide you through setting up your Starknet application:
npm init starknet
Or, if you want to do it manually you will need to add the following dependencies to your project:
npm install @starknet-react/chains @starknet-react/core starknet get-starknet-core
Starknet.js is an SDK designed to simplify interactions with Starknet. Conversely, get-starknet specializes in wallet connection management.
Wrap your app in the StarknetConfig
component to configure and provide a React Context. This component lets you specify wallet connection options for users through its connectors prop.
export default function App({ children }) {
const chains = [goerli, mainnet];
const provider = publicProvider();
const { connectors } = useInjectedConnectors({
// Show these connectors if the user has no connector installed.
recommended: [argent(), braavos()],
// Hide recommended connectors if the user has any connector installed.
includeRecommended: "onlyIfNoConnectors",
// Randomize the order of the connectors.
order: "random",
});
return (
<StarknetConfig chains={chains} provider={provider} connectors={connectors}>
{children}
</StarknetConfig>
);
}
Establishing Connection and Managing Account
After defining the connectors in the config
, you can use a hook to access them. This enables users to connect their wallets.
export default function Component() {
const { connect, connectors } = useConnect();
return (
<ul>
{connectors.map((connector) => (
<li key={connector.id}>
<button onClick={() => connect({ connector })}>
{connector.name}
</button>
</li>
))}
</ul>
);
}
Now, observe the disconnect
function that terminates the connection when
invoked:
const { disconnect } = useDisconnect();
return <button onClick={() => disconnect()}>Disconnect</button>;
Once connected, the useAccount
hook provides access to the connected account, giving insights into the connection's current state.
const { address, isConnected, isReconnecting, account } = useAccount();
return <div>{isConnected ? <p>Hello, {address}</p> : <Connect />}</div>;
State values like isConnected
and isReconnecting
update automatically, easing UI updates. This is particularly useful for asynchronous processes, removing the need for manual state management in your components.
Once connected, signing messages is easy with the account value from the useAccount
hook. For a smoother experience, you can also use the useSignTypedData
hook.
const { data, isPending, signTypedData } = useSignTypedData(exampleData);
return (
<button
onClick={() => signTypedData(exampleData)}
disabled={!account}
>
{isPending ? <p>Waiting for wallet...</p> : <p>Sign Message</p>}
</button>
);
Starknet React supports signing an array of BigNumberish
values or an object. When signing an object, ensure the data adheres to the EIP712 type. For detailed guidance on signing, see the Starknet.js documentation: here.
Displaying StarkName
Once an account is connected, the useStarkName
hook retrieves the StarkName
of the account. Linked to Starknet.id, it allows for displaying the user address in a user-friendly manner.
const { data, isLoading, isError } = useStarkName({ address });
if (isLoading)
return <span>Loading...</span>;
if (isError)
return <span>Error fetching name...</span>;
return <span>StarkName: {data}</span>;
This hook provides additional information: error, status, fetchStatus, isSuccess, isError, isPending, isFetching, isLoading. These details offer precise insights into the current process.
Fetching Address from StarkName
To retrieve an address
from a StarkName
, use the useAddressFromStarkName
hook.
const { data, isLoading, isError } = useAddressFromStarkName({
name: "vitalik.stark",
});
if (isLoading)
return <span>Loading...</span>;
if (isError)
return <span>Error fetching address...</span>;
return <span>address: {data}</span>;
If the provided name does not have an associated address, it will return
0x0
Navigating the Network
Starknet React provides developers with tools for network interactions, including hooks like useBlock for retrieving the latest block:
const { data, isLoading, isError } = useBlock({
refetchInterval: 10_000,
blockIdentifier: "latest" as BlockNumber,
});
if (isLoading)
return <span>Loading...</span>;
if (isError || !data)
return <span>Error...</span>;
return <span>Hash: {data.block_hash}</span>;
Here, refetchInterval
sets the data refresh rate. Starknet React uses react-query for state and query management. Other hooks like useContractRead
and useWaitForTransaction
are also available for interval-based updates.
The useStarknet hook gives direct access to the ProviderInterface:
const { provider } = useProvider()
// library.getClassByHash(...)
// library.getTransaction(...)
Tracking Wallet changes
For a better dApp user experience, tracking wallet changes is crucial. This includes account changes, connections, disconnections, and network switches. Reload balances on account changes, or reset your dApp's state on network changes. Use useAccount
and useNetwork
for this.
useNetwork
provides the current network chain:
const { chain: { id, name } } = useNetwork();
return (
<>
<p>Connected chain: {name}</p>
<p>Connected chain id: {id}</p>
</>
)
This hook also offers blockExplorer, testnet for detailed network information.
Monitor user interactions with account and network using the useEffect
hook:
const { chain } = useNetwork();
const { address } = useAccount();
useEffect(() => {
if(address) {
// Do some work when the user changes the account on the wallet
// Like reloading the balances
}else{
// Do some work when the user disconnects the wallet
// Like reseting the state of your dApp
}
}, [address]);
useEffect(() => {
// Do some work when the user changes the network on the wallet
// Like reseting the state of your dApp
}, [chain]);
Contract Interactions
Read Functions
Starknet React introduces useContractRead
, similar to wagmi, for read operations on contracts. These operations are independent of the user's connection status and don't require a signer.
const { data, isError, isLoading, error } = useContractRead({
functionName: "balanceOf",
args: [address as string],
abi,
address: testAddress,
watch: true,
});
if (isLoading)
return <div>Loading ...</div>;
if (isError || !data)
return <div>{error?.message}</div>;
return <div>{parseFloat(data.balance.low)}n</div>;
For ERC20 operations, the useBalance
hook simplifies retrieving balances without needing an ABI.
const { isLoading, isError, error, data } = useBalance({
address,
watch: true,
});
if (isLoading) return <div>Loading ...</div>;
if (isError || !data) return <div>{error?.message}</div>;
return (
<div>
{data.value.toString()}
{data.symbol}
</div>
);
Write Functions
The useContractWrite
hook, unlike wagmi, benefits from Starknet's native support for multicall transactions. This improves user experience by facilitating multiple transactions without individual approvals.
const calls = useMemo(() => {
if (!address || !contract) return [];
// return a single object for single transaction,
// or an array of objects for multicall**
return contract.populateTransaction["transfer"]!(address, { low: 1, high: 0 });
}, [contract, address]);
const {
writeAsync,
data,
isPending,
} = useContractWrite({
calls,
});
return (
<>
<button onClick={() => writeAsync()}>Transfer</button>
<p>status: {isPending && <div>Submitting...</div>}</p>
<p>hash: {data?.transaction_hash}</p>
</>
);
This setup starts with the populateTransaction
utility, followed by executing the transaction through writeAsync
. The hook also provides transaction status and hash.
A Single Contract Instance
For cases where a single contract instance is more than apecifying the contract address and ABI in each hook., use the useContract
hook:
const { contract } = useContract({
address: CONTRACT_ADDRESS,
abi: abi_erc20,
});
// Call functions directly on contract
// contract.transfer(...);
// contract.balanceOf(...);
Tracking Transactions
UseWaitForTransaction
tracks transaction states with a transaction hash, reducing network requests through caching.
const { isLoading, isError, error, data } = useWaitForTransaction({
hash: transaction,
watch: true,
});
if (isLoading) return <div>Loading ...</div>;
if (isError || !data) return <div>{error?.message}</div>;
return <div>{data.status?.length}</div>;
Explore all available hooks in Starknet React's documentation: https://starknet-react.com/hooks/.
Conclusion
The Starknet React library provides a range of React hooks and providers specifically designed for Starknet and the Starknet.js SDK. These tools enable developers to create applications on the Starknet network.