# TypeScript SDK Guide
###### tags: `FuelJourney`
## Overview
If you know JavaScript, you can effectively learn how to build fullstack dapps (decentralized applications) on Fuel with Sway.
In this guide, you will learn how to build a full-stack dapp on Fuel from scratch.
Let's take a look at what you'll be building!
## Introduction
In the Sway guide, you learned how to write a smart contract for the sway-farm game. Now, you need a frontend for your game that can interact with your contract.
Below is a sneak-peak of what the end result should look like!
**[insert screenshot of the game]
## Trying the reference app
To try running the game locally, follow the following steps below:
1. Clone the [sway-farm]() repository and move into the root folder:
```bash
git clone https://github.com/FuelLabs/sway-farm
cd sway-farm
```
2. Move into the frontend folder to install dependencies and run the app locally:
```bash
cd frontend
npm install
npm start
```
Awesome! You can now test out the app and play around with it locally to understand what you be learning to implement and how it should look like in the end.
Now, let's go ahead and get started with building the app yourself!
## Prerequistes
This guide assumes that you have the following tools setup:
- [Fuel toolchain](https://github.com/FuelLabs/fuelup)
- [Sway VSCode extension](https://marketplace.visualstudio.com/items?itemName=FuelLabs.sway-vscode-plugin)
Additionally, this guide assumes that you have an existing Sway smart contract that you can build a frontend for. Head over to the [Sway Guide]() to write your first Sway smart contract!
Additional resources that may be helpful:
- [Fuel 101 Guide]()
- [Fuel Discourse](https://forum.fuel.network/)
## Getting Started
- Initialising a React app using `create-react-app`
- Installing `fuels-ts` SDK dependencies
- Installing Fuel wallet
```bash
npm install fuels @fuel-wallet/sdk
```
## TS SDK Basics
### WalletLocked
**(https://fuellabs.github.io/fuels-ts/packages/fuel-ts-wallet/classes/WalletLocked.html)**
### BN
**(https://fuellabs.github.io/fuels-ts/packages/fuel-ts-providers/classes/internal-BN.html)**
## Fuel Wallet SDK Basics
**(https://wallet.fuel.network/docs/how-to-use/)**
### Fuel
If you've correctly installed the Fuel wallet extension, the wallet SDK will be injected automatically on the window object on the property fuel. To access it, you can use window.fuel
```javascript
window.fuel.connect();
```
### Request connection
First, you need to request a connection with the wallet, which will authorize your application to execute other actions. You can do this by accessing fuel.connect().
```javascript
const isConnected = await fuel.connect();
console.debug("Connection response", isConnected);
```
The` connect()` method returns a promise. If you prefer to do it in an async way, you can use `fuel.on('connection', () => void)` to listen for changes in the connection.
### Get current account
You can also get the current account being used in the wallet using window.fuel.currentAccount().
```javascript
const currentAccount = await fuel.currentAccount();
console.debug("Current Account ", currentAccount);
```
## Building your frontend
### Initialising your React app
1. Create a new folder called `frontend`.
2. Initialise the React app using the following command:
`npx create-react-app frontend --template typescript`
3. Generate contract types using the following command:
`npx fuelchain --target=fuels --out-dir=./src/contracts ../sway-farm/out/debug/*-abi.json`
You should be able to find a new folder `sway-farm/frontend/src/contracts`. This folder was auto-generated by our `fuelchain` command, these files abstract the work we would need to do to create a contract instance and generate a complete TypeScript interface to the contract making easy to develop.
### Writing wallet hooks
1. `useFuel`
```javascript=
import { useState, useEffect } from 'react';
import { Fuel } from '@fuel-wallet/sdk';
const globalWindow: Window & {
fuel: Fuel;
} = typeof window !== 'undefined' ? window as any : ({} as any);
export function useFuel() {
const [error, setError] = useState('');
const [isLoading, setLoading] = useState(true);
const [fuel, setFuel] = useState<Fuel>(
globalWindow.fuel
);
useEffect(() => {
const timeout = setTimeout(() => {
if (globalWindow.fuel) {
setFuel(globalWindow.fuel);
} else {
setError('Fuel Wallet not detected on the window!');
}
setLoading(false);
}, 500);
return () => clearTimeout(timeout);
}, []);
return [fuel, error, isLoading] as const;
}
```
2. `useIsConnected`
```javascript=
import { useEffect, useState } from 'react';
import { useFuel } from './useFuel';
export function useIsConnected() {
const [fuel] = useFuel();
const [isConnected, setIsConnected] = useState(false);
useEffect(() => {
async function main() {
try {
const accounts = await fuel.accounts();
setIsConnected(Boolean(accounts.length));
} catch (err) {
setIsConnected(false);
}
}
if (fuel) {
main();
}
fuel?.on('connection', main);
return () => {
fuel?.off('connection', main);
};
}, [fuel]);
return isConnected;
}
```
### Primary Game Functions
The player performs several actions throughout the game such as:
- **Creating a new player**
- **Buying seeds**
- **Planting seeds to grow new plants**
- **Harvesting plants**
- **Selling harvested items**
- **Leveling-up**
Let's go ahead and implement some functions to let the player perform the above primary functions in the game.
#### Creating New Player
To beging the game session, let's first write a function that creates a new player.
```javascript
interface NewPlayerProps {
contract: ContractAbi | null;
setUpdateNum: Dispatch<SetStateAction<number>>;
updateNum: number;
}
export default function NewPlayer({ contract, setUpdateNum, updateNum }: NewPlayerProps){
const [status, setStatus] = useState<'error' | 'loading' | 'none'>('none');
async function handleNewPlayer(){
if (contract !== null) {
try {
setStatus('loading')
await contract.functions.new_player()
.txParams({ variableOutputs: 1 })
.call();
setStatus('none')
setUpdateNum(updateNum + 1)
} catch(err){
console.log("Error:", err)
setStatus('error')
}
} else {
console.log("ERROR: contract missing");
setStatus('error')
}
}
```
#### Buying seeds
Once the game has started, a player needs to buy seeds. Let's write a function in `./Components/BuySeeds.tsx` to enable this functionality.
```javascript
interface BuySeedsProps {
contract: ContractAbi | null;
setUpdateNum: Dispatch<SetStateAction<number>>;
updateNum: number;
}
// const CONTRACT_ID = "0xcae2ec3ca9f6fc2a6c604f1c5cec7a8052e9379dc066acc651ea56515ddeca6e"
export default function BuySeeds({ contract, setUpdateNum, updateNum }: BuySeedsProps) {
const [amount, setAmount] = useState<string>("0");
const [status, setStatus] = useState<'error' | 'none' | `loading`>('none');
// let price = bn.parseUnits('0.00000075');
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
if (contract !== null) {
try {
setStatus('loading')
let realAmount = parseInt(amount) / 1_000_000_000;
let inputAmount = bn.parseUnits(realAmount.toFixed(9).toString());
let seedType: FoodTypeInput = { tomatoes: [] };
await contract.functions
// .buy_seeds(seedType, inputAmount)
.buy_seeds_free(seedType, inputAmount)
// .callParams({
// forward: [price, CONTRACT_ID],
// })
.call();
setUpdateNum(updateNum + 1)
setStatus('none')
} catch (err) {
console.log("Error:", err)
setStatus('error')
}
} else {
console.log("ERROR: contract missing");
setStatus('error')
}
}
```
Note that the `BuySeeds` functions makes use of the `buy_seeds` from your smart contract which is a payable function. This means that in order to call the function, a wallet must also send some tokens along with the call.
#### Planting seeds
Now that the player has bought seeds using their wallet, it is time to plant them! Let's write a function in `./Components/PlantSeeds.tsx` to plant the seeds.
```javascript
interface PlantSeedsProps {
contract: ContractAbi | null;
setUpdateNum: Dispatch<SetStateAction<number>>;
updateNum: number;
}
export default function PlantSeeds({ contract, setUpdateNum, updateNum }: PlantSeedsProps) {
const [amount, setAmount] = useState<string>("0");
const [status, setStatus] = useState<'error' | 'none' | 'loading'>('none');
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
if (contract !== null) {
try {
setStatus('loading')
let realAmount = parseInt(amount) / 1_000_000_000;
let inputAmount = bn.parseUnits(realAmount.toFixed(9).toString());
console.log("inputAmount:", inputAmount);
let seedType: FoodTypeInput = { tomatoes: [] };
await contract.functions.plant_seeds(seedType, inputAmount).call();
setUpdateNum(updateNum + 1);
setStatus('none');
} catch (err) {
console.log("Error!!", err);
setStatus('error');
}
} else {
console.log("ERROR: contract missing");
setStatus('error')
}
}
```
#### Harvesting Items
Awesome! The player can now harvest their plants once they're ready. Let's write a function in `./Components/Harvest.tsx` to collect the grown plants.
```javascript
interface HarvestProps {
contract: ContractAbi | null;
index: number;
setUpdateNum: Dispatch<SetStateAction<number>>;
updateNum: number;
}
export default function Harvest({ contract, index, setUpdateNum, updateNum }: HarvestProps) {
const [status, setStatus] = useState<'error' | 'none' | 'loading'>('none');
async function harvestItem() {
if (contract !== null) {
try {
setStatus('loading')
await contract.functions.harvest(index.toString()).call()
setUpdateNum(updateNum + 1)
setStatus('none')
} catch (err) {
console.log("Error:", err)
setStatus('error')
}
} else {
console.log("ERROR: contract missing");
setStatus('error')
}
}
```
#### Selling Items
After harvesting the plants, the player can sell them as items. Let's write a function in `./Components/SellItem.tsx` that is called when the player wishes to sell harvested items.
```javascript
interface SellItemProps {
contract: ContractAbi | null;
setUpdateNum: Dispatch<SetStateAction<number>>;
updateNum: number;
}
export default function SellItem({ contract, setUpdateNum, updateNum }: SellItemProps) {
const [amount, setAmount] = useState<string>("0");
const [status, setStatus] = useState<'error' | 'none' | 'loading'>('none');
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
if (contract !== null) {
try {
setStatus('loading')
let realAmount = parseInt(amount) / 1_000_000_000;
let inputAmount = bn.parseUnits(realAmount.toFixed(9).toString());
let seedType: FoodTypeInput = { tomatoes: [] };
await contract.functions.sell_item(seedType, inputAmount).call()
setUpdateNum(updateNum + 1);
setStatus('none')
} catch (err) {
console.log("Error:", err)
setStatus('error')
}
} else {
console.log("ERROR: contract missing");
setStatus('error')
}
}
```
Note that the `SellItem` function makes use of the `sell_item` function from your Sway contract. Just like `buy_seeds`, `sell_item` is also a payable function that transfers coins to the seller when called.
#### Levelling Up
Now that the player has bought seeds, planted them, harvested the plants, and sold items from their harvest, you need to write a function to level-up the player.
The function `levelUp` checks whether a player has enough experience or not to proceed to the next level based on the response from `getCanLevelUp`. If the player is eligible, the async function `handleLevelUp` should increment the player's level.
```javascript
interface LevelUpProps {
contract: ContractAbi | null;
setUpdateNum: Dispatch<SetStateAction<number>>;
updateNum: number;
}
export default function LevelUp({ contract, setUpdateNum, updateNum }: LevelUpProps) {
const [status, setStatus] = useState<'error' | 'loading' | 'none'>('none');
const [canLevelUp, setCanLevelUp] = useState<boolean>();
useEffect(() => {
async function getCanLevelUp() {
if (contract && contract.wallet) {
try {
let address: AddressInput = { value: contract.wallet.address.toB256() }
let id: IdentityInput = { Address: address };
let { value } = await contract.functions.can_level_up(id).get();
setCanLevelUp(value)
} catch (err) {
console.log("Error:", err)
}
}
}
getCanLevelUp();
}, [contract, updateNum])
async function handleLevelUp() {
if (contract !== null) {
try {
setStatus('loading')
await contract.functions.level_up().call();
setUpdateNum(updateNum + 1);
setStatus('none')
} catch (err) {
console.log("Error:", err)
setStatus('error')
}
} else {
console.log("ERROR: contract missing");
setStatus('error')
}
}
```
### Other functions to display game results
Apart from the primary functions of the game, you also need to implement functions to display game results such as:
- Amount of seeds bought by the player
- Amount of seeds planted by the player
- Number of items harvested by the player
- Amount of coins owned by the player
Let's go ahead and write the functions to be able to display the above properties in the game.
#### ShowSeeds
Once a player buys some seeds, they should be able to check the amount of seeds they have available to plant.
The ShowSeeds function should return the number of seeds bought by the player.
```javascript
interface ShowSeedsProps {
contract: ContractAbi | null;
setUpdateNum: Dispatch<SetStateAction<number>>;
updateNum: number;
}
export default function ShowSeeds({ contract, setUpdateNum, updateNum }: ShowSeedsProps) {
const [seeds, setSeeds] = useState<number>(0);
useEffect(() => {
async function getSeeds() {
if (contract && contract.wallet) {
try {
let address: AddressInput = { value: contract.wallet.address.toB256() }
let id: IdentityInput = { Address: address };
let seedType: FoodTypeInput = { tomatoes: [] };
let { value } = await contract.functions.get_seed_amount(id, seedType).get();
let num = parseFloat(value.format()) * 1_000_000_000
setSeeds(num)
} catch (err) {
console.log("Error:", err)
}
}
}
getSeeds();
}, [contract, updateNum])
```
#### ShowPlantedSeeds
Now you need a function that lets the player check the amount of seeds planted.
Let's go ahead and implement this functionality using the `ShowPlantedSeeds` function.
```javascript
interface ShowItemsProps {
contract: ContractAbi | null;
setUpdateNum: Dispatch<SetStateAction<number>>;
updateNum: number;
}
export default function ShowItems({ contract, setUpdateNum, updateNum }: ShowItemsProps) {
const [seeds, setSeeds] = useState<number>(0);
useEffect(() => {
async function getItems() {
if (contract && contract.wallet) {
try {
let address: AddressInput = { value: contract.wallet.address.toB256() }
let id: IdentityInput = { Address: address };
let seedType: FoodTypeInput = { tomatoes: [] };
let { value } = await contract.functions.get_item_amount(id, seedType).get();
let num = parseFloat(value.format()) * 1_000_000_000
setSeeds(num)
} catch (err) {
console.log("Error:", err)
}
}
}
getItems();
}, [contract, updateNum])
```
#### ShowItems
To be able to sell the items on the market, the user should be able to check the inventory at all times. The inventory should show the currently available items that the player can sell.
Let's write a function that returns the remaining items in the player's inventory!
```javascript
interface ShowItemsProps {
contract: ContractAbi | null;
setUpdateNum: Dispatch<SetStateAction<number>>;
updateNum: number;
}
export default function ShowItems({ contract, setUpdateNum, updateNum }: ShowItemsProps) {
const [seeds, setSeeds] = useState<number>(0);
useEffect(() => {
async function getItems() {
if (contract && contract.wallet) {
try {
let address: AddressInput = { value: contract.wallet.address.toB256() }
let id: IdentityInput = { Address: address };
let seedType: FoodTypeInput = { tomatoes: [] };
let { value } = await contract.functions.get_item_amount(id, seedType).get();
let num = parseFloat(value.format()) * 1_000_000_000
setSeeds(num)
} catch (err) {
console.log("Error:", err)
}
}
}
getItems();
}, [contract, updateNum])
```
#### ShowCoins
The player should be able to check their wallet balance at a certain point in the game. To enable this, let's write a function that checks and displays the amount of coins in the player's wallet.
```javascript
interface ShowCoinsProps {
updateNum: number;
}
const CONTRACT_ID = "0xcae2ec3ca9f6fc2a6c604f1c5cec7a8052e9379dc066acc651ea56515ddeca6e"
export default function ShowCoins({ updateNum }: ShowCoinsProps){
const [balance, setBalance] = useState<BN>();
const [Fuel] = useFuel();
useEffect(() => {
async function getAccounts() {
const currentAccount = await Fuel.currentAccount();
const wallet = await Fuel.getWallet(currentAccount)
const walletBalance = await wallet.getBalance(CONTRACT_ID);
setBalance(walletBalance);
}
if (Fuel) getAccounts();
}, [Fuel, updateNum]);
```
The `ShowCoins` functions makes use of the `fuel.currentAccount()`, `fuel.getWallet()`, and `fuel.getBalance()` methods from the `Fuel-wallet/sdk` to retrieve the balance of the player's current wallet account.
### Game Components
Now that you have written all your functions, refer the example game repository to understand what your Sway Farm game components should look like in the end.
**(https://github.com/sarahschwartz/sway-farm/tree/main/frontend/src/components)**
### Modifying main `App.tsx` file
1. Import hooks and components
```javascript
import { useState, useEffect, useMemo } from "react";
import { useIsConnected } from "./hooks/useIsConnected";
import { useFuel } from "./hooks/useFuel";
import { WalletLocked } from "fuels";
import { ContractAbi__factory } from "./contracts"
import Game from "./components/Game";
import { Heading, Button, Link } from "@fuel-ui/react";
```
2. Add the contract id of the Sway smart contract you have deployed in your `App.tsx` file
```javascript
const CONTRACT_ID = "0xd715df667608312fe9f2e57f8e92d2dd0f5e23db94d8a27f63c3c5c74c01da77"
```
3. In the `App` function, define state variables for `accounts`, `provider`, and `active` as follows:
```javascript
function App() {
const [account, setAccount] = useState<string>();
const [wallet, setWallet] = useState<WalletLocked>();
```
4. Define `fuel` and `isConnected` using the wallet hooks you wrote earlier:
```javascript
const isConnected = useIsConnected();
const [Fuel] = useFuel();
```
5. Get `accounts`
```javascript
useEffect(() => {
async function getAccounts() {
const currentAccount = await Fuel.currentAccount();
const tempWallet = await Fuel.getWallet(currentAccount)
setAccount(currentAccount);
setWallet(tempWallet)
}
if (Fuel) getAccounts();
}, [Fuel, isConnected]);
```
6. Connect contract instance to the deployed contract address using the given wallet as described below:
```javascript
const contract = useMemo(() => {
if (Fuel && account && wallet) {
// Connects out Contract instance to the deployed contract
// address using the given wallet.
const contract = ContractAbi__factory.connect(CONTRACT_ID, wallet);
return contract;
}
return null;
}, [Fuel, account, wallet]);
```
8. Add a `header` for your app as follows:
```javascript
return (
<div className="App">
<header>
<Heading as= "h1" fontColor="gray5">Sway Farm</Heading>
</header>
```
9. Add a button as follows:
```javascript
{Fuel ? (
<div>
{isConnected ? (
<Game contract={contract} />
) : (
<div>
<Button onPress={() => Fuel.connect()}>Connect Wallet</Button>
</div>
)}
</div>
)
}
```
After making the final changes, here's how your `App.tsx` file should look:
```javascript=
import { useState, useEffect, useMemo } from "react";
import { useIsConnected } from "./hooks/useIsConnected";
import { useFuel } from "./hooks/useFuel";
import { WalletLocked } from "fuels";
import { ContractAbi__factory } from "./contracts"
import Game from "./components/Game";
import { Heading, Button, Link } from "@fuel-ui/react";
import "./App.css";
const CONTRACT_ID = "0xcae2ec3ca9f6fc2a6c604f1c5cec7a8052e9379dc066acc651ea56515ddeca6e"
function App() {
const [account, setAccount] = useState<string>();
const [wallet, setWallet] = useState<WalletLocked>();
const isConnected = useIsConnected();
const [Fuel] = useFuel();
useEffect(() => {
async function getAccounts() {
const currentAccount = await Fuel.currentAccount();
const tempWallet = await Fuel.getWallet(currentAccount)
setAccount(currentAccount);
setWallet(tempWallet)
}
if (Fuel) getAccounts();
}, [Fuel, isConnected]);
const contract = useMemo(() => {
if (Fuel && account && wallet) {
// Connects out Contract instance to the deployed contract
// address using the given wallet.
const contract = ContractAbi__factory.connect(CONTRACT_ID, wallet);
return contract;
}
return null;
}, [Fuel, account, wallet]);
return (
<div className="App">
<header>
<Heading as= "h1" fontColor="gray5">Sway Farm</Heading>
</header>
{Fuel ? (
<div>
{isConnected ? (
<Game contract={contract} />
) : (
<div>
<Button onPress={() => Fuel.connect()}>Connect Wallet</Button>
</div>
)}
</div>
) : (
<div>
Download the{" "}
<Link
target="_blank"
rel="noopener noreferrer"
href="https://wallet.fuel.network/"
>
Fuel Wallet
</Link>{" "}
to play the game.
</div>
)}
</div>
);
}
export default App;
```
## Running your app
Voila! Your Fuel full-stack dapp is ready to play around with. Run the following command to spin up a development build.
```bash
npm install
npm start
```