Skip to Content
Supra Layer 1SupraMVMGetting StartedCreate a dApp with StarKey

Create a dApp with StarKey

Create your first Dapp using StarKey wallet, the official wallet of Supra. For the full window.starkey.supra provider API, see the StarKey developer docs .

Setup the project

Initialize the frontend

npx create-react-app supra-dapp --template typescript
cd supra-dapp

Install the TypeScript SDK

npm install supra-ts-sdk@devnet

The devnet, testnet and mainnet npm dist-tags point to the release that matches each network. This guide uses Testnet; see the TypeScript SDK page for the dist-tags.

Start building

Open the App.tsx file and update it

Remove the template code and replace it with the code below. This will give us a fresh foundation to work with.

function App() { return <div></div>; } export default App;

Import dependencies

We’ll be utilizing the react library along with useful functions from the supra-ts-sdk.

import React, { useEffect, useState } from 'react'; import { HexString, BCS, SupraClient, Network } from 'supra-ts-sdk';

Initialize the supraProvider and state hooks

supraProvider - used to interact with StarKey wallet.

isStarKeyInstalled - boolean value of StarKey extension installation status

accounts - Array of connected accounts

networkData - Chain ID for the connected network

supra - the SDK client, created once at module scope and reused. Building a new client per transaction would open a fresh RPC connection each time.

import React, {useEffect, useState} from "react"; import { HexString, BCS, SupraClient, Network } from "supra-ts-sdk"; const supra = new SupraClient({ network: Network.TESTNET }); function App() { let supraProvider: any = typeof window !== "undefined" && (window as any)?.starkey?.supra; const [isStarkeyInstalled, setIsStarkeyInstalled] = useState<boolean>(!!supraProvider); const [accounts, setAccounts] = useState<string[]>([]); const [networkData, setNetworkData] = useState<any>(); return <div></div>; }

All code from this step forward will be included within the App() function.

Check for the injected StarKey object

The following code checks for the injected StarKey object every 500ms for up to 5 seconds. If found, the function will update the StarKey installation status and obtain the connected accounts. See Detecting the Provider  in the StarKey docs.

const checkForStarkey = () => { const intervalId = setInterval(() => { if ((window as any)?.starkey) { supraProvider = (window as any)?.starkey.supra; clearInterval(intervalId); setIsStarkeyInstalled(true); updateAccounts().then(); } }, 500); setTimeout(() => { clearInterval(intervalId); }, 5000); }; useEffect(() => { checkForStarkey(); }, []);

Get the connected accounts

The following code obtains the connected accounts (if any) and stores them.

const updateAccounts = async () => { if (supraProvider) { try { const response_acc = await supraProvider.account(); if (response_acc.length > 0) { setAccounts(response_acc); } else { setAccounts([]); } } catch (e) { setAccounts([]); } getNetworkData().then(); } };

Get the connected network data

The following code obtains the network data (chain id) of the connected wallet.

const getNetworkData = async () => { if (supraProvider) { const data = await supraProvider.getChainId(); console.log(data); if (data) { setNetworkData(data) } } };

Initialize additional hooks

The following code will assist in maintaining updated network/account data while also handling events  emitted by the injected StarKey object.

useEffect(() => { if (supraProvider) { supraProvider.on("accountChanged", (acc: string[]) => { setAccounts(acc); }); supraProvider.on("networkChanged", (data: any) => { setNetworkData(data); }); supraProvider.on("disconnect", () => { resetWalletData(); }); } }, [supraProvider]); useEffect(() => { if (accounts) { getNetworkData().then(); } }, [accounts]);

Handle wallet connections

The following code will handle the wallet connections , triggered through a button click (which we will setup later) or an emitted event from the StarKey object.

When a wallet is connected, the accounts must be updated. When a wallet is disconnected, we must clear the stored accounts and network data.

const connectWallet = async () => { const response = await supraProvider.connect(); updateAccounts().then(); }; const disconnectWallet = async () => { if (supraProvider) { await supraProvider.disconnect(); } resetWalletData(); }; const resetWalletData = ()=>{ setAccounts([]); setNetworkData({}); }

Switch between mainnet and testnet

The following code will be triggered through a button click to prompt the user to switch between mainnet and testnet

const switchToTestnet = async (id:string) => { if (supraProvider) { await supraProvider.changeNetwork({chainId:"6"}); await getNetworkData() } }; const switchToMainnet = async () => { if (supraProvider) { await supraProvider.changeNetwork({chainId:"8"}); await getNetworkData() } };

Send a transaction

The following code builds a raw transaction that calls the transfer function of the supra_account module, then hands the serialized bytes to supraProvider.sendTransaction so the connected wallet signs and submits it.

It will transfer 1 supra to the address 0x8de4158b48633d853186d5fc790718e5821d7d3c4855e06bcd97b105389a7d0f .

The transaction is built with the SDK rather than StarKey’s deprecated createRawTransactionData. The SDK needs the sender’s real sequence number, so the function fetches it with supra.account.getAccountInfo first.

rawTxnObject makes no request, so it does not estimate the max gas: without maxGas it uses 20,000 units, and the node requires the sender’s SUPRA balance to cover max gas × gas unit price (20 SUPRA at the default gas unit price). A transfer uses far fewer units, so the code sets maxGas. The wallet signs the transaction, so the SDK’s chain ID check before signing does not apply; the chainId passed to sendTransaction must be that of the connected network.

New recipient addresses: Supra addresses may not have on-chain account state before their first transaction. See Sending Tokens to New Accounts.

const sendRawTransaction = async () => { const { sequence_number } = await supra.account.getAccountInfo({ accountAddress: accounts[0], }); /** OptionalTransactionPayloadArgs { maxGas?: bigint; gasUnitPrice?: bigint; txExpiryTime?: bigint; }*/ const rawTxn = supra.transaction.build.rawTxnObject({ senderAddress: accounts[0], senderSequenceNumber: sequence_number, function: "0x1::supra_account::transfer", functionTypeArgs: [], functionArgs: [ new HexString("0x8de4158b48633d853186d5fc790718e5821d7d3c4855e06bcd97b105389a7d0f").toUint8Array(), BCS.bcsSerializeUint64(100000000) ], optionalTransactionPayloadArgs: { maxGas: BigInt(5000), txExpiryTime: BigInt(Math.ceil(Date.now() / 1000) + 30) //30 seconds } }); const txHash = await supraProvider.sendTransaction({ data: HexString.fromUint8Array(rawTxn.toBytes()).noPrefix(), from: accounts[0], to: "", value: "", chainId: "6", }); console.log("Transaction hash: ", txHash); };

Create the buttons displayed on the frontend

The following code renders buttons on the frontend that will interact with the above functions.

return <div> <button onClick={connectWallet}>Connect Wallet</button> <button onClick={disconnectWallet}>Disconnect Wallet</button> <p>Connected Account(s): {JSON.stringify(accounts)}</p> <p>Network Data: {JSON.stringify(networkData)}</p> <button onClick={switchToTestnet}>Switch to Testnet</button> <button onClick={switchToMainnet}>Switch to Mainnet</button> <br></br> <button onClick={sendRawTransaction}>Send Test Transaction</button> </div>;

That’s it! Run your project

npm start

Final code for App.tsx

import React, {useEffect, useState} from "react"; import { HexString, BCS, SupraClient, Network } from "supra-ts-sdk"; const supra = new SupraClient({ network: Network.TESTNET }); function App() { let supraProvider: any = typeof window !== "undefined" && (window as any)?.starkey?.supra; const [isStarkeyInstalled, setIsStarkeyInstalled] = useState<boolean>(!!supraProvider); const [accounts, setAccounts] = useState<string[]>([]); const [networkData, setNetworkData] = useState<any>(); const checkForStarkey = () => { const intervalId = setInterval(() => { if ((window as any)?.starkey) { supraProvider = (window as any)?.starkey.supra; clearInterval(intervalId); setIsStarkeyInstalled(true); updateAccounts().then(); } }, 500); setTimeout(() => { clearInterval(intervalId); }, 5000); }; useEffect(() => { checkForStarkey(); }, []); const getNetworkData = async () => { if (supraProvider) { const data = await supraProvider.getChainId(); console.log(data); if (data) { setNetworkData(data) } } }; const updateAccounts = async () => { if (supraProvider) { try { const response_acc = await supraProvider.account(); if (response_acc.length > 0) { setAccounts(response_acc); } else { setAccounts([]); } } catch (e) { setAccounts([]); } getNetworkData().then(); } }; useEffect(() => { if (supraProvider) { supraProvider.on("accountChanged", (acc: string[]) => { setAccounts(acc); }); supraProvider.on("networkChanged", (data: any) => { setNetworkData(data); }); supraProvider.on("disconnect", () => { resetWalletData(); }); } }, [supraProvider]); useEffect(() => { if (accounts) { getNetworkData().then(); } }, [accounts]); const resetWalletData = ()=>{ setAccounts([]); setNetworkData({}); } const connectWallet = async () => { const response = await supraProvider.connect(); updateAccounts().then(); }; const disconnectWallet = async () => { if (supraProvider) { await supraProvider.disconnect(); } resetWalletData(); }; const switchToTestnet = async () => { if (supraProvider) { await supraProvider.changeNetwork({chainId:"6"}); await getNetworkData() } }; const switchToMainnet = async () => { if (supraProvider) { await supraProvider.changeNetwork({chainId:"8"}); await getNetworkData() } }; const sendRawTransaction = async () => { const { sequence_number } = await supra.account.getAccountInfo({ accountAddress: accounts[0], }); const rawTxn = supra.transaction.build.rawTxnObject({ senderAddress: accounts[0], senderSequenceNumber: sequence_number, function: "0x1::supra_account::transfer", functionTypeArgs: [], functionArgs: [ new HexString("0x8de4158b48633d853186d5fc790718e5821d7d3c4855e06bcd97b105389a7d0f").toUint8Array(), BCS.bcsSerializeUint64(100000000) ], optionalTransactionPayloadArgs: { maxGas: BigInt(5000), txExpiryTime: BigInt(Math.ceil(Date.now() / 1000) + 30) //30 seconds } }); const txHash = await supraProvider.sendTransaction({ data: HexString.fromUint8Array(rawTxn.toBytes()).noPrefix(), from: accounts[0], to: "", value: "", chainId: "6", }); console.log("Transaction hash: ", txHash); }; return <div> <button onClick={connectWallet}>Connect Wallet</button> <button onClick={disconnectWallet}>Disconnect Wallet</button> <p>Connected Account(s): {JSON.stringify(accounts)}</p> <p>Network Data: {JSON.stringify(networkData)}</p> <button onClick={switchToTestnet}>Switch to Testnet</button> <button onClick={switchToMainnet}>Switch to Mainnet</button> <br></br> <button onClick={sendRawTransaction}>Send Test Transaction</button> </div>; } export default App;
Last updated on