# Get started on Starknet dapps Starknet is a Layer 2 Validity Rollup - basically it aims to make Ethereum more scalable and affordable, while making the most of Ethereum's security. You can read more about it here (links to smart people here) If you've been following Starknet's progress, you're probably aware that there has been _a lot_ of change recently. Cairo 1 was released, and that means frontend tools need updating to keep up. ![blazingly fast](https://media2.giphy.com/media/v1.Y2lkPTc5MGI3NjExMmYxMjg4MGFhMmI1OTA0NjFlM2FmMzZlN2Y0MTdjY2UzYTZmMDczNyZlcD12MV9pbnRlcm5hbF9naWZzX2dpZklkJmN0PWc/3oz8xtBx06mcZWoNJm/giphy.gif) *keeping up with blazingly fast changes* ## The good news If you have experience building dapps but are new to Starknet, the good news is that you can apply plenty of that knowledge here! If you're familiar with Starknet, the good news (in my opinion) is that the changes are moving in the right direction. The bad news is that we still have to deal with BN.js. Sorry. ## Getting started The basic tools you can use to get started are [Starknet.js](https://www.starknetjs.com/) and [get-starknet](https://github.com/starknet-io/get-starknet). Starknet.js is an essential low level lib, equivalent to ethers.js, whereas get-starknet handles connecting to wallets. You should definitely read Starknet.js' docs for a more in-depth understanding. With these tools, there are basically 3 main concepts to know on the frontend: ![](https://hackmd.io/_uploads/Byz0vx2E3.png) ### Account We can generally think of the account as the "end user" of a dapp, and some user interaction will be involved to gain access to it. Think of a dapp where the user connects their browser extension wallet (such as [ArgentX](https://www.argent.xyz/argent-x/) or [Braavos](https://braavos.app/)) - if the user accepts the connection, that gives us access to the account and signer, which can sign transactions and messages. Unlike Ethereum, where user accounts are Externally Owned Accounts, Starknet [**accounts are contracts**](https://docs.starknet.io/documentation/architecture_and_concepts/Account_Abstraction/introduction/). This might not impact your dapp's frontend, but you should definitely be aware of this difference. ```ts async function connectWallet() { const starknet = await connect(); console.log(starknet.account); starknet.account.signMessage(...) } ``` The snippet above uses the `connect` function provided by `get-starknet` to establish a connection to the user wallet. ### Provider This one is pretty simple. The provider allows you to interact with the Starknet network. You can think of it as a "read" connection to the blockchain, with methods such as `getBlock` or `getChainId`. Just like in Ethereum, you can use a default provider, or use services like Infura or Alchemy, both of which support Starknet. ```ts export const provider = new Provider({ sequencer: { network: "goerli-alpha", }, /* rpc: { nodeUrl: INFURA_ENDPOINT } */ }); const block = await provider.getBlock("latest"); // <- Get latest block console.log(block.block_number); ``` ### Contracts Of course, your frontend will likely be interacting with deployed contracts. For each contract, there should be a counterpart on the frontend. To create these instances, you will need the contract's address and ABI, and either a provider or signer. ```ts const contract = new Contract( abi_erc20, contractAddress, starknet.account ); contract.balanceOf(starknet.account.address) ``` If you create a contract instance with a provider, you'll be limited to calling read functions on the contract - only with a signer can you change the state of the blockchain. ## Units If you have previous experience with web3, you know dealing with units can be tricky, and Starknet is no exception. Once again, the docs are very useful here, especially [this section on data transformation](https://www.starknetjs.com/docs/guides/define_call_message/). Very often you will need to convert Cairo structs (such as Uint256) that are returned from contracts into numbers: ```ts // Uint256 shape: // { // type: 'struct', // low: Uint256.low, // high: Uint256.high // // } const balance = await contract.balanceOf(address); // <- uint256 const asBN = uint256.uint256ToBN(uint256); // <- uint256 into BN const asString = asBN.toString() //<- BN into string ``` And vice versa: ```ts const amount = 1; const amountFormatted = { type: "struct", ...uint256.bnToUint256(amount), }; ``` If we put it all together, calling a `transfer` function on a contract looks like this: ```ts const tx = await contract.transfer(recipientAddress, amountFormatted); //or: const tx = await contract.invoke("transfer", [to, amountFormatted]); console.log(`Tx hash: ${tx.transaction_hash}`); ``` There are other helpful utils, besides `bnToUint256` and `uint256ToBN`, provided by Starknet.js. We now have a solid foundation to build a Starknet dapp! If you have a background in regular Ethereum apps, you'll notice how similar the experience is, but you're probably also wondering if there are more high-level tools out there that we can use. Well, there are! ## starknet-react [starknet-react](https://github.com/apibara/starknet-react) is a collection of React providers and hooks for StarkNet inspired by [wagmi](https://wagmi.sh/). If you've used wagmi before, you should have a pretty good idea of what this looks like, as it aims to have a very similar API. First, you need to wrap your app in a `StarknetConfig` component. This allows for some configuration and provides a context to the underlying app to be able to use the shared data and hooks: ```ts const connectors = [ new InjectedConnector({ options: { id: "braavos" } }), new InjectedConnector({ options: { id: "argentX" } }), ]; return ( <StarknetConfig connectors={connectors} // defaultProvider={...} // autoConnect={false} > <App /> </StarknetConfig> ) ``` Then, you can use the hooks to access the connectors you defined: ```ts export default function Connect() { const { connect, connectors } = useConnectors(); return ( <div> {connectors.map((connector) => ( <button onClick={() => connect(connector)} key={connector.id()} > Connect {connector.id()} </button> ))} </div> ); } ``` Once you're connected, you have access to the connected account through the `useAccount` hook. This also providdes the current state of connection: ```ts const { address, isConnected, isReconnecting, account } = useAccount(); return ( <div> {isConnected ? ( <p>Hello, {address}</p> ) : ( <Connect /> )} </div> ); ``` Getting data from the provider can also be done through hooks. The `refetchInterval` prop is especially useful for easily keeping the UI updated: ```ts const { data } = useBlock({ refetchInterval: 10_000, blockIdentifier: "latest", }); return ( <p>Current block: {data?.block_number}<p> ) ``` Finally, starknet-react also provides hooks for interacting with contracts. For read functions, just like in wagmi there is a `useContractRead` function: ```ts const { data: balance, isLoading, isError, isSuccess } = useContractRead({ abi: abi_erc20, address: CONTRACT_ADDRESS, functionName: "balanceOf", args: [address], // watch: true <- refresh at every block }); ``` For write functions though, things are slightly different to wagmi. Due to Starknet's architecture, accounts can natively support multicall transactions. In practice, this means an improved user experience when executing multiple transactions, as you won't have to individually approve each transaction. Therefore it makes sense that starknet-react should make the most of this feature in it's hooks, and this is what it looks like: ```ts const calls = useMemo(() => { if (!amount || !to) return; // format the amount const amountFormatted = { type: "struct", ...uint256.bnToUint256(parseFixed(amount, 18).toString()), } as const; // compile the calldata to send const calldata = stark.compileCalldata({ recipient: to, amount: amountFormatted, }); // return a single object for single transaction, array of objects // for multicall return { contractAddress: CONTRACT_ADDRESS, entrypoint: "transfer", calldata, }; }, [address, to, amount]); const { write, isLoading, data } = useContractWrite({ calls, }); // trigger the transaction write(); ``` Basically, we compile the calldata to be executed and pass that to the `useContractWrite` hook, which returns a `write` function we can use to actually trigger it. Using `useContractRead` and `useContractWrite` might not a good fit for your use case - you might want to work with a single instance of the contract, instead of continually specifying its address and ABI in individual hooks. This is also possible throught the `useContract` hook: ```ts const { contract } = useContract({ address: CONTRACT_ADDRESS, abi: abi_erc20, }); // contract.transfer(...); // contract.balanceOf(...); ``` There are a bunch of other hooks available, which you can learn more about in [starknet-react's docs](https://apibara.github.io/starknet-react/). ### Typescript One area where starknet frontend tooling is still lacking, compared to Ethereum, is in type safety. Thanks to [abitype](https://github.com/wagmi-dev/abitype), libs like wagmi or viem are capable of providing strongly typed contract interfaces based on the ABI you provide. ```ts const contractRead = useContractRead({ address, abi: abi_erc20, functionName: 'balanceOf', args: [42], // <- Type error! }) ``` In this example of wagmi code you would get an error, because the `balanceOf` function expects a string, not a number. You can also get autocomplete for the available functions on a given contract, all based on its ABI. This extra type-safety really improves the developer experience and helps prevent bugs. Though this isn't yet possible in Starknet, there is work being made on equivalent libs, such as [abi-wan-kanabi](https://github.com/keep-starknet-strange/abi-wan-kanabi/). ## Final thoughts With these tools, you've got what you need to build a frontend users can connect to that interacts with Starknet contracts. Hopefully this has been a helpful intro to building starknet frontends. As you can see, there's a lot going on, but there's also a lot of effort in making it easy to start building in Starknet if you're coming from an Ethereum background. Thanks for reading!