
Creating Seamless Web3 Wallet Connections in Your DApp Using Wagmi Hooks
Table of ContentsIntroductionWhat is WagmiComponent StructureMainWalletConnectionWalletConnectionModalUserAccountDetailMain Config HooksWalletConnection ComponentWalletConnectionModal ComponentRabby WalletConclusionIntroductionA view of the Web3 wallet connection implementation we'll be discussingFor a Web3 application, a wallet connection is essential. It's the equivalent of a user signing in with a username and password, except using a wallet as a form of identification. By connec...
Behind the Scenes: Creating the AirSwap Member Dashboard App
Table of ContentsIntroductionWhat is the AirSwap Voter Rewards App?App Features I DevelopedMentorshipCode ReviewsTypeScriptWagmi LibraryCustom React ComponentsVariable NamingRegressionsLaunch DayPutting Apps into ProductionConclusionIntroductionIn this post, I'll share my experience and the key lessons I learned while building the AirSwap Member Dashboard app. This app provides users with the ability to stake AST tokens, vote on proposals that influence the AirSwap protocol, and claim re...

How to Build an Accordion Component
I've been actively involved in the frontend development of the new AirSwap Member Dashboard app. AirSwap currently leverages a platform named Activate for user stake management. However, within a month, Activate will be phased out, prompting the development of a new app for $AST token stakers. A notable feature of this app is the Accordion component, which I'll discuss here, illustrating its creation with React, Radix, and TailwindCSS.What is Radix-UI?Radix-UI logoRadix-UI is an ope...
Web Developer @ AirSwap // Building DeFi // React, TypeScript, Tailwind, Frontend Developer.



Creating Seamless Web3 Wallet Connections in Your DApp Using Wagmi Hooks
Table of ContentsIntroductionWhat is WagmiComponent StructureMainWalletConnectionWalletConnectionModalUserAccountDetailMain Config HooksWalletConnection ComponentWalletConnectionModal ComponentRabby WalletConclusionIntroductionA view of the Web3 wallet connection implementation we'll be discussingFor a Web3 application, a wallet connection is essential. It's the equivalent of a user signing in with a username and password, except using a wallet as a form of identification. By connec...
Behind the Scenes: Creating the AirSwap Member Dashboard App
Table of ContentsIntroductionWhat is the AirSwap Voter Rewards App?App Features I DevelopedMentorshipCode ReviewsTypeScriptWagmi LibraryCustom React ComponentsVariable NamingRegressionsLaunch DayPutting Apps into ProductionConclusionIntroductionIn this post, I'll share my experience and the key lessons I learned while building the AirSwap Member Dashboard app. This app provides users with the ability to stake AST tokens, vote on proposals that influence the AirSwap protocol, and claim re...

How to Build an Accordion Component
I've been actively involved in the frontend development of the new AirSwap Member Dashboard app. AirSwap currently leverages a platform named Activate for user stake management. However, within a month, Activate will be phased out, prompting the development of a new app for $AST token stakers. A notable feature of this app is the Accordion component, which I'll discuss here, illustrating its creation with React, Radix, and TailwindCSS.What is Radix-UI?Radix-UI logoRadix-UI is an ope...
Web Developer @ AirSwap // Building DeFi // React, TypeScript, Tailwind, Frontend Developer.
Share Dialog
Share Dialog

Subscribe to starrdev

Subscribe to starrdev
<100 subscribers
<100 subscribers
Intro
Video Demo
Tech stack used
The Staking flow - how it works
Components and functions used
How to build a modal with React
Zustand store
Wagmi hooks
Controlling variables
Transaction Tracker
Action Buttons
Summary
Conclusion
This article is a technical write-up about the token staking feature for the AirSwap Member Dashboard app. The majority of my time working on this app was spent on this feature.
Users can stake tokens into the AirSwap Staking smart contract, which gives them access to voting on proposals, then claim rewards from the AirSwap Pool smart contract. This article will detail the logic on how I built the staking feature.
https://vimeo.com/manage/videos/877284150
The app is built using React, TypeScript, and TailwindCSS. All smart contract interactions use a library called Wagmi. Here’s a brief overview:
React is a popular JavaScript library for building user interfaces. Docs.
TypeScript is a superset of JavaScript that adds static typing to the language. Docs.
TailwindCSS is a CSS framework that allows you to write CSS classes directly into HTML, or JSX in the case of React. Some love it, some hate. I’m among those who love it. Docs.
Wagmi: Wagmi is a collection of React hooks that make it simple to interact with Ethereum and EVM blockchains. Docs.

If you hold AirSwap Token, (AST), you can use this app to stake your tokens. Here’s the flow of how staking works:
Approve: To stake tokens, first you have to approve the AirSwap Staking contract to spend your AST tokens. This is done by calling the approve function, and is a feature in most smart contracts.
Stake: After you’ve approved the spending of your token, you can call the stake function. When you stake your tokens, you’ll receive sAST tokens in exchange for locking up your AST tokens in the smart contract. sAST is kind of like an IOU for AST tokens that you receive from the smart contract. It’s an IOU, but your sAST balance is also used to calculate your voting power.
Unstake: Calling the unstake function lets you unlock your tokens from the smart contract. Note that when you stake AST tokens, your tokens unlock linearly over time.
This section covers various components and functions in the Staking feature. Every function won’t be described here, but feel free to view all the code on GitHub if you’re curious.

The Modal component in the app utilizes the HTML dialog element. The dialog element is a modern, elegant way to create a modal. It comes with built-in JavaScript methods, such as showModal and close.
To create a modal, use the JSX tag <dialog />, and pass it a ref. For the ref, we can utilize the useRef hook. useRef is a React hook that persists a value between renders. Since the value persists, it means that our modal can remain open (or closed) when we want it to.
Here’s a code sample of the Modal component:
import { useKeyboardEvent } from "@react-hookz/web";
import { useEffect, useRef } from "react";
export const Modal = ({
// omitted code
isClosable = true,
onCloseRequest,
}: {
// omitted code
isClosable?: boolean;
onCloseRequest: () => void;
}) => {
const modalRef = useRef<HTMLDialogElement>(null);
useKeyboardEvent("Escape", () => {
if (!isClosable) return;
onCloseRequest && onCloseRequest();
modalRef.current?.close();
});
useEffect(() => {
if (modalRef.current && !modalRef.current.hasAttribute("open")) {
modalRef.current.showModal();
}
}, [modalRef]);
return (
<dialog ref={modalRef} >
// omitted code
</dialog>
);
};
In the code above, first we create a ref object called modalRef, which gets passed into the dialog. We also have a hook called useKeyboardEvent, which gets returned from the react-hookz library. useKeyboardEvent returns a callback function that closes the modal when the user presses the “escape” key.
Note that close is a method which gets called on modalRef. Since the dialog node has a ref of modalRef, the ref object now can access the JavaScript functions that are associated with the HTML dialog element, and pass it along to the dialog.

The useEffect hook checks whether the dialog is open. The Modal component opens when a user clicks on a button in another component, but I’ve omitted that code from this blog post. Opening the Modal component would give it an attribute of open.

Zustand is a lightweight alternative to Redux. We chose Zustand over Redux because the app is relatively small in size. Redux would’ve been overkill for our needs on this app.
We store values in the Zustand store so these values persist across component re-renders and various stages of the staking cycle. It’s possible to use React “prop-drilling” to pass values from parent to children components, but using Zustand produces cleaner code.
Another cool feature of Zustand is that it comes with persist middleware. This middleware makes it easy to “persist” data into local storage. This middleware isn’t used for the staking modal, but some other features in the app utilize it.

In the Staking feature, there are main 4 custom hooks which call Wagmi hooks:
useApproveAst.ts - calls smart contract “write” function to approve the spending of AST token
useAstAllowance.ts - calls a smart contract “read” function to return the amount of AST tokens held in a connected wallet
useStakeAst.ts - calls smart contract “write” function to stake AST
useUnstakeSast.ts - calls smart contract “write” function to unstake AST
These hooks are mostly similar, so for brevity I’ll only cover useStakeAst in detail.

This hook returns a function that users can use to stake tokens. It calls 3 Wagmi hooks:
usePrepareContractWrite. This hook prepares an object called config. This object later gets passed into useContractWrite, which is explained below. usePrepareContractWrite accepts several arguments:
Address - the smart contract address of the contract you want to use.
ABI - an interface of the smart contract.
Function - the function on the ABI you want to call. In our case, stake.
Arguments - an optional array of arguments to be passed into the function want to call.
Enabled - an optional boolean value. If false, the function will not run. Using this can optimize the performance of the app. For this purpose, variable called canStake was used. canStake is a boolean value that checks that the transaction type is “stake”, that needsApproval is false, and that the input the user entered is valid.
useContractWrite - This hook is used to call smart contract functions. It returns several values that were utilized:
write - this is the function that writes to the blockchain when it gets called. When a user clicks the “stake” button, this “write” function gets called.


Several variables used in the main StakingModal component are used to control the flow of certain hooks. For example, the following boolean variables: needsApproval, canStake, and canUnstake. If needsApproval is true, the hook useApproveAst will be enabled, and the hook useStakeAst will be disabled. For canStake to be true, needsApproval must be false.
Here are a few code snippets with explanations below:
const needsApproval =
txType === TxType.STAKE &&
Number(astAllowance) < Number(stakingAmountFormatted) * 10 ** 4 &&
validNumberInput;
const canStake =
txType === TxType.STAKE && !needsApproval && validNumberInput;
const {
writeAsync: approveAst,
data: dataApproveAst,
reset: resetApproveAst,
isLoading: approvalAwaitingSignature,
} = useApproveAst({
stakingAmountFormatted: Number(stakingAmountFormatted) || 0,
enabled: needsApproval,
});
Check if a user can approve tokens or not:
needsApproval is a boolean value with a few checks. The user must toggle the “stake” option, the user’s allowance must be less than the amount they entered to stake, and the number inputted to the form must be valid.
Transaction Type
Let’s look at the following code: txType === TxType.STAKE. The txType variable is stored in the Zustand store. (Code is not shown above). The value of txType changes when you click on the STAKE/UNSTAKE toggle.

TxType is a TypeScript enum with the following shape:
export enum TxType {
STAKE = "stake",
UNSTAKE = "unstake",
}
Check if a user can stake or not:
canStake checks that the user has toggled the “stake” option, that needsApproval is false, and that a valid number has been inputted into the staking form.
Loading transaction variable:

const txIsLoading =
approvalAwaitingSignature ||
stakeAwaitingSignature ||
unstakeAwaitingSignature ||
txStatus === "loading";
This variable is a boolean that changes depending on the current transaction status. This value gets passed into the Modal component as a prop called isClosable. If isClosable is true, the close button in the modal will be disabled.
<Modal
className="w-full max-w-none xs:max-w-[360px] text-white"
heading={modalLoadingStateHeadlines}
isClosable={!txIsLoading}
onCloseRequest={() => setShowStakingModal(false)}
>
Above is a code snippet that shows the Modal component accepting txIsLoading as a prop for the isClosable variable

We want to disable the close button when a transaction is processing because closing the modal will make a user confused about the status of an active blockchain transaction. It’s bad UX when this happens. Blockchain users usually want a status update on their transaction and will often look at their screen until they see a “success” (or “failed”) confirmation of a completed transaction.

This component was designed to be as generic as possible and to display the current state of transaction data. It’s used for the staking feature, as well as the claims feature (which will not be covered in this post).
The tracker takes in several props, including a transaction hash. This is a hash that gets passed into the Wagmi hook useWaitForTransaction, which returns the status of a transaction. The status is either: idle, loading, successful, or failed. The values of these status variables are used to determine which text and images to display in the transaction tracker. This provides users with feedback on their current stage in the staking and unstaking process.
The staking modal has 1 main button to handle user actions. These actions include: approve, stake, unstaking. There’s also a toggle where users can switch between staking and unstaking.
actionButtonsObject is a function that takes in 4 arguments and returns an object. The return object takes the shape of ActionButton, which returns button labels and callback functions. 3 of these arguments are functions that were returned from Wagmi hooks.
type ActionButton = {
afterSuccess: { label: string; callback: () => void };
afterFailure: { label: string; callback: () => void };
};
Here are some examples of how the button would change:
After successfully calling the “approve” function, the button label will read “Continue”, and clicking on it would call the function resetApproveAst, which resets the status of the function. At this point, the user is permitted to stake tokens.
After a failed attempt at calling the "approve" function, the button label will read “Try again”, and clicking on it would also call resetApproveAst. The status of the approval function will reset, but the user will still be required to approve token spending before staking is allowed.


The staking modal component also has a function called actionButtonLogic. This checks the status of various staking actions and returns a value based on which staking action is true. The return value of actionButtonLogic gets passed as props into the TransactionTracker component. The value that gets passed into the Transaction Tracker is what the user will see on the button.
The following code snippet shows how the TransactionTracker component is called in the StakingModal component.
<TransactionTracker
actionButtons={actionButtonLogic()}
successContent={
<span>
You successfully {verb}{" "}
<span className="text-white">{stakingAmountFormatted} AST</span>
</span>
}
failureContent={"Your transaction has failed"}
signatureExplainer={
isApproval
? "To stake AST you will first need to approve the token spend."
: undefined
}
txHash={currentTransactionHash}
/>
signatureExplainer is a prop in TransactionTracker. It pops up when a user has initiated a transaction, but has not yet signed the transaction in his or her wallet (for example MetaMask). The explainer is used to tell the user the next steps in the Staking flow.

Here’s a video demo of the staking flow in action:
This article covers many of the functions that make up the staking modal, but there are several details I skipped over. Dive into the code on GitHub to see the rest of it. Here’s a high-level recap of how the staking modal works:
First, a user must approve the smart contract to spend AST tokens. In the StakingModal component, the boolean variable needsApproval checks whether or not a user can approve.
If needsApproval is true, the custom hook useApproveAst is enabled. Now, when a user clicks the “Approve” button in the TransactionTracker component, useApproveAst returns a callback function that writes the "approve" transaction to the blockchain.
After successfully approving, needsApproval should be set to false. Now the boolean value canStake determines whether a user can stake tokens or not.
If canStake is true, the custom hook useStakeAst will return the Wagmi callback function stakeAst (the original function name returned from Wagmi is writeAsync, but I renamed it in the staking Modal component). This process is similar to the token spending approval process, and unstaking tokens is similar to the token staking process.
useApproveAst, useStakeAst, useUnstakeSast all return objects called data, which contain a transaction hash (or undefined). These hooks also return the status of transactions: "idle", "loading", "success", or
The AirSwap Member Dashboard has more features than Staking, but I want to keep this blog post specific and won’t go into the other features today.

Developing this Staking feature was one of my most challenging projects. I did countless refactors and put in many hours of work to make sure it came out as expected. I was fortunate to work alongside a more senior developer on this app. The guidance and mentorship were priceless.
Here’s an incomplete list of some of my learning lessons from building this feature:
It’s almost impossible to refactor code too much. Continuously refactoring code can seem tedious, but in the end, it makes you a more skilled developer.
Often the cause of bugs is having too much code, rather than too little. If there are bugs that are hard to fix, it can help to remove code until there aren’t any bugs. Once you reach the point of having no bugs, add in code until you can isolate exactly what was causing the original bugs.
Working with a mentor (or mentee) is priceless. Even if you’re building stuff that works, it’s nice to get other perspectives and hear of ways to improve your code.
Conducting code reviews for others can enhance your learning by providing insights into their coding structure.
I take pride in the work I've accomplished on this app, and I hope this blog post effectively conveys my enthusiasm during its development. Feel free to explore the app and test the staking feature. If you're interested, drop me a message for some Goerli AST to try it out. While there are areas for optimization, I am open to any feedback you may have
If you’ve read this far, I’d like to thank you for your attention.
GitHub: https://github.com/airswap/airswap-voter-rewards.
Demo: http://dao.airswap.eth.limo.
Intro
Video Demo
Tech stack used
The Staking flow - how it works
Components and functions used
How to build a modal with React
Zustand store
Wagmi hooks
Controlling variables
Transaction Tracker
Action Buttons
Summary
Conclusion
This article is a technical write-up about the token staking feature for the AirSwap Member Dashboard app. The majority of my time working on this app was spent on this feature.
Users can stake tokens into the AirSwap Staking smart contract, which gives them access to voting on proposals, then claim rewards from the AirSwap Pool smart contract. This article will detail the logic on how I built the staking feature.
https://vimeo.com/manage/videos/877284150
The app is built using React, TypeScript, and TailwindCSS. All smart contract interactions use a library called Wagmi. Here’s a brief overview:
React is a popular JavaScript library for building user interfaces. Docs.
TypeScript is a superset of JavaScript that adds static typing to the language. Docs.
TailwindCSS is a CSS framework that allows you to write CSS classes directly into HTML, or JSX in the case of React. Some love it, some hate. I’m among those who love it. Docs.
Wagmi: Wagmi is a collection of React hooks that make it simple to interact with Ethereum and EVM blockchains. Docs.

If you hold AirSwap Token, (AST), you can use this app to stake your tokens. Here’s the flow of how staking works:
Approve: To stake tokens, first you have to approve the AirSwap Staking contract to spend your AST tokens. This is done by calling the approve function, and is a feature in most smart contracts.
Stake: After you’ve approved the spending of your token, you can call the stake function. When you stake your tokens, you’ll receive sAST tokens in exchange for locking up your AST tokens in the smart contract. sAST is kind of like an IOU for AST tokens that you receive from the smart contract. It’s an IOU, but your sAST balance is also used to calculate your voting power.
Unstake: Calling the unstake function lets you unlock your tokens from the smart contract. Note that when you stake AST tokens, your tokens unlock linearly over time.
This section covers various components and functions in the Staking feature. Every function won’t be described here, but feel free to view all the code on GitHub if you’re curious.

The Modal component in the app utilizes the HTML dialog element. The dialog element is a modern, elegant way to create a modal. It comes with built-in JavaScript methods, such as showModal and close.
To create a modal, use the JSX tag <dialog />, and pass it a ref. For the ref, we can utilize the useRef hook. useRef is a React hook that persists a value between renders. Since the value persists, it means that our modal can remain open (or closed) when we want it to.
Here’s a code sample of the Modal component:
import { useKeyboardEvent } from "@react-hookz/web";
import { useEffect, useRef } from "react";
export const Modal = ({
// omitted code
isClosable = true,
onCloseRequest,
}: {
// omitted code
isClosable?: boolean;
onCloseRequest: () => void;
}) => {
const modalRef = useRef<HTMLDialogElement>(null);
useKeyboardEvent("Escape", () => {
if (!isClosable) return;
onCloseRequest && onCloseRequest();
modalRef.current?.close();
});
useEffect(() => {
if (modalRef.current && !modalRef.current.hasAttribute("open")) {
modalRef.current.showModal();
}
}, [modalRef]);
return (
<dialog ref={modalRef} >
// omitted code
</dialog>
);
};
In the code above, first we create a ref object called modalRef, which gets passed into the dialog. We also have a hook called useKeyboardEvent, which gets returned from the react-hookz library. useKeyboardEvent returns a callback function that closes the modal when the user presses the “escape” key.
Note that close is a method which gets called on modalRef. Since the dialog node has a ref of modalRef, the ref object now can access the JavaScript functions that are associated with the HTML dialog element, and pass it along to the dialog.

The useEffect hook checks whether the dialog is open. The Modal component opens when a user clicks on a button in another component, but I’ve omitted that code from this blog post. Opening the Modal component would give it an attribute of open.

Zustand is a lightweight alternative to Redux. We chose Zustand over Redux because the app is relatively small in size. Redux would’ve been overkill for our needs on this app.
We store values in the Zustand store so these values persist across component re-renders and various stages of the staking cycle. It’s possible to use React “prop-drilling” to pass values from parent to children components, but using Zustand produces cleaner code.
Another cool feature of Zustand is that it comes with persist middleware. This middleware makes it easy to “persist” data into local storage. This middleware isn’t used for the staking modal, but some other features in the app utilize it.

In the Staking feature, there are main 4 custom hooks which call Wagmi hooks:
useApproveAst.ts - calls smart contract “write” function to approve the spending of AST token
useAstAllowance.ts - calls a smart contract “read” function to return the amount of AST tokens held in a connected wallet
useStakeAst.ts - calls smart contract “write” function to stake AST
useUnstakeSast.ts - calls smart contract “write” function to unstake AST
These hooks are mostly similar, so for brevity I’ll only cover useStakeAst in detail.

This hook returns a function that users can use to stake tokens. It calls 3 Wagmi hooks:
usePrepareContractWrite. This hook prepares an object called config. This object later gets passed into useContractWrite, which is explained below. usePrepareContractWrite accepts several arguments:
Address - the smart contract address of the contract you want to use.
ABI - an interface of the smart contract.
Function - the function on the ABI you want to call. In our case, stake.
Arguments - an optional array of arguments to be passed into the function want to call.
Enabled - an optional boolean value. If false, the function will not run. Using this can optimize the performance of the app. For this purpose, variable called canStake was used. canStake is a boolean value that checks that the transaction type is “stake”, that needsApproval is false, and that the input the user entered is valid.
useContractWrite - This hook is used to call smart contract functions. It returns several values that were utilized:
write - this is the function that writes to the blockchain when it gets called. When a user clicks the “stake” button, this “write” function gets called.


Several variables used in the main StakingModal component are used to control the flow of certain hooks. For example, the following boolean variables: needsApproval, canStake, and canUnstake. If needsApproval is true, the hook useApproveAst will be enabled, and the hook useStakeAst will be disabled. For canStake to be true, needsApproval must be false.
Here are a few code snippets with explanations below:
const needsApproval =
txType === TxType.STAKE &&
Number(astAllowance) < Number(stakingAmountFormatted) * 10 ** 4 &&
validNumberInput;
const canStake =
txType === TxType.STAKE && !needsApproval && validNumberInput;
const {
writeAsync: approveAst,
data: dataApproveAst,
reset: resetApproveAst,
isLoading: approvalAwaitingSignature,
} = useApproveAst({
stakingAmountFormatted: Number(stakingAmountFormatted) || 0,
enabled: needsApproval,
});
Check if a user can approve tokens or not:
needsApproval is a boolean value with a few checks. The user must toggle the “stake” option, the user’s allowance must be less than the amount they entered to stake, and the number inputted to the form must be valid.
Transaction Type
Let’s look at the following code: txType === TxType.STAKE. The txType variable is stored in the Zustand store. (Code is not shown above). The value of txType changes when you click on the STAKE/UNSTAKE toggle.

TxType is a TypeScript enum with the following shape:
export enum TxType {
STAKE = "stake",
UNSTAKE = "unstake",
}
Check if a user can stake or not:
canStake checks that the user has toggled the “stake” option, that needsApproval is false, and that a valid number has been inputted into the staking form.
Loading transaction variable:

const txIsLoading =
approvalAwaitingSignature ||
stakeAwaitingSignature ||
unstakeAwaitingSignature ||
txStatus === "loading";
This variable is a boolean that changes depending on the current transaction status. This value gets passed into the Modal component as a prop called isClosable. If isClosable is true, the close button in the modal will be disabled.
<Modal
className="w-full max-w-none xs:max-w-[360px] text-white"
heading={modalLoadingStateHeadlines}
isClosable={!txIsLoading}
onCloseRequest={() => setShowStakingModal(false)}
>
Above is a code snippet that shows the Modal component accepting txIsLoading as a prop for the isClosable variable

We want to disable the close button when a transaction is processing because closing the modal will make a user confused about the status of an active blockchain transaction. It’s bad UX when this happens. Blockchain users usually want a status update on their transaction and will often look at their screen until they see a “success” (or “failed”) confirmation of a completed transaction.

This component was designed to be as generic as possible and to display the current state of transaction data. It’s used for the staking feature, as well as the claims feature (which will not be covered in this post).
The tracker takes in several props, including a transaction hash. This is a hash that gets passed into the Wagmi hook useWaitForTransaction, which returns the status of a transaction. The status is either: idle, loading, successful, or failed. The values of these status variables are used to determine which text and images to display in the transaction tracker. This provides users with feedback on their current stage in the staking and unstaking process.
The staking modal has 1 main button to handle user actions. These actions include: approve, stake, unstaking. There’s also a toggle where users can switch between staking and unstaking.
actionButtonsObject is a function that takes in 4 arguments and returns an object. The return object takes the shape of ActionButton, which returns button labels and callback functions. 3 of these arguments are functions that were returned from Wagmi hooks.
type ActionButton = {
afterSuccess: { label: string; callback: () => void };
afterFailure: { label: string; callback: () => void };
};
Here are some examples of how the button would change:
After successfully calling the “approve” function, the button label will read “Continue”, and clicking on it would call the function resetApproveAst, which resets the status of the function. At this point, the user is permitted to stake tokens.
After a failed attempt at calling the "approve" function, the button label will read “Try again”, and clicking on it would also call resetApproveAst. The status of the approval function will reset, but the user will still be required to approve token spending before staking is allowed.


The staking modal component also has a function called actionButtonLogic. This checks the status of various staking actions and returns a value based on which staking action is true. The return value of actionButtonLogic gets passed as props into the TransactionTracker component. The value that gets passed into the Transaction Tracker is what the user will see on the button.
The following code snippet shows how the TransactionTracker component is called in the StakingModal component.
<TransactionTracker
actionButtons={actionButtonLogic()}
successContent={
<span>
You successfully {verb}{" "}
<span className="text-white">{stakingAmountFormatted} AST</span>
</span>
}
failureContent={"Your transaction has failed"}
signatureExplainer={
isApproval
? "To stake AST you will first need to approve the token spend."
: undefined
}
txHash={currentTransactionHash}
/>
signatureExplainer is a prop in TransactionTracker. It pops up when a user has initiated a transaction, but has not yet signed the transaction in his or her wallet (for example MetaMask). The explainer is used to tell the user the next steps in the Staking flow.

Here’s a video demo of the staking flow in action:
This article covers many of the functions that make up the staking modal, but there are several details I skipped over. Dive into the code on GitHub to see the rest of it. Here’s a high-level recap of how the staking modal works:
First, a user must approve the smart contract to spend AST tokens. In the StakingModal component, the boolean variable needsApproval checks whether or not a user can approve.
If needsApproval is true, the custom hook useApproveAst is enabled. Now, when a user clicks the “Approve” button in the TransactionTracker component, useApproveAst returns a callback function that writes the "approve" transaction to the blockchain.
After successfully approving, needsApproval should be set to false. Now the boolean value canStake determines whether a user can stake tokens or not.
If canStake is true, the custom hook useStakeAst will return the Wagmi callback function stakeAst (the original function name returned from Wagmi is writeAsync, but I renamed it in the staking Modal component). This process is similar to the token spending approval process, and unstaking tokens is similar to the token staking process.
useApproveAst, useStakeAst, useUnstakeSast all return objects called data, which contain a transaction hash (or undefined). These hooks also return the status of transactions: "idle", "loading", "success", or
The AirSwap Member Dashboard has more features than Staking, but I want to keep this blog post specific and won’t go into the other features today.

Developing this Staking feature was one of my most challenging projects. I did countless refactors and put in many hours of work to make sure it came out as expected. I was fortunate to work alongside a more senior developer on this app. The guidance and mentorship were priceless.
Here’s an incomplete list of some of my learning lessons from building this feature:
It’s almost impossible to refactor code too much. Continuously refactoring code can seem tedious, but in the end, it makes you a more skilled developer.
Often the cause of bugs is having too much code, rather than too little. If there are bugs that are hard to fix, it can help to remove code until there aren’t any bugs. Once you reach the point of having no bugs, add in code until you can isolate exactly what was causing the original bugs.
Working with a mentor (or mentee) is priceless. Even if you’re building stuff that works, it’s nice to get other perspectives and hear of ways to improve your code.
Conducting code reviews for others can enhance your learning by providing insights into their coding structure.
I take pride in the work I've accomplished on this app, and I hope this blog post effectively conveys my enthusiasm during its development. Feel free to explore the app and test the staking feature. If you're interested, drop me a message for some Goerli AST to try it out. While there are areas for optimization, I am open to any feedback you may have
If you’ve read this far, I’d like to thank you for your attention.
GitHub: https://github.com/airswap/airswap-voter-rewards.
Demo: http://dao.airswap.eth.limo.
data - this object returns data about the transaction after write gets called. It contains useful info such as the transaction hash.
reset- After a transaction completes, the status of the transaction will persist until it changes, or the user refreshes the page. The code will look like this: status === 'success'. Calling reset will reset the transaction status and prevent UX pitfalls.
useWaitForTransaction - we use this hook primarily to get the status of a transaction, and its transaction hash.
status can either be: “error”, “idle”, “success”, or “loading. These values change during the course of a transaction’s lifetime.
data is a return object that contains a transaction hash. We use the transaction has to link users to an etherscan link with their transaction data.
"failed"These status variables and transaction hashes are passed into the TransactionTracker component, and give users feedback on their transactions.
data - this object returns data about the transaction after write gets called. It contains useful info such as the transaction hash.
reset- After a transaction completes, the status of the transaction will persist until it changes, or the user refreshes the page. The code will look like this: status === 'success'. Calling reset will reset the transaction status and prevent UX pitfalls.
useWaitForTransaction - we use this hook primarily to get the status of a transaction, and its transaction hash.
status can either be: “error”, “idle”, “success”, or “loading. These values change during the course of a transaction’s lifetime.
data is a return object that contains a transaction hash. We use the transaction has to link users to an etherscan link with their transaction data.
"failed"These status variables and transaction hashes are passed into the TransactionTracker component, and give users feedback on their transactions.
No activity yet