In this guide, we will create a simple website that interacts with your deployed Move smart contract. The website will allow users to increment the counter and display its current value using Move transactions and Viem SDK.
npx create-next-app@latest my-move-dapp --typescript
cd my-move-dapp
npm install viem @aptos-labs/ts-sdk @mysten/bcs
Create a new file src/app/Counter.tsx
:
'use client';
import { useEffect, useState } from 'react';
import { counterPayload, getAccount, publicClient, walletClient } from '@/config';
import { bcs } from '@mysten/bcs';
const MoveCounter = bcs.struct('Counter', {
value: bcs.u64(),
});
export default function Counter() {
const [counter, setCounter] = useState(0);
const fetchCounter = async () => {
const callResponse = await publicClient().call({
to: await getAccount(),
data: await counterPayload('get'),
});
if (!callResponse.data) throw Error('No data found');
if (typeof callResponse.data == 'string') throw Error('Data is not an array of bytes');
// The returned data is a vector of results with mostly a single result.
// Each result is a tuple of output data bytes followed by the serialized Move type layout.
// The following code extracts the output bytes from inside this complex returned data structure.
const output = new Uint8Array(
bcs.vector(bcs.tuple([bcs.vector(bcs.u8())])).parse(new Uint8Array(callResponse.data))[0][0],
);
const counter = MoveCounter.parse(output);
setCounter(parseInt(counter.value));
};
const incrementCounter = async () => {
const hash = await walletClient().sendTransaction({
account: await getAccount(),
to: await getAccount(),
data: await counterPayload('increment'),
});
await publicClient().waitForTransactionReceipt({ hash });
fetchCounter();
};
useEffect(() => {
fetchCounter();
}, []);
return (
<div className="text-center m-24">
<h1 className="py-6">Counter: {counter}</h1>
<button className="bg-blue-700 rounded-lg px-5 py-2.5" type="button" onClick={incrementCounter}>
Increment
</button>
</div>
);
}
Replace the contents of src/app/page.tsx
with:
import Counter from "./Counter";
export default function Home() {
return (
<div>
<h1>Move Counter DApp</h1>
<Counter />
</div>
);
}
Create a new file config.ts
to define methods for creating a transaction payload and sending transactions using the Viem SDK.
import { AccountAddress, EntryFunction, TransactionPayloadEntryFunction } from '@aptos-labs/ts-sdk';
import { createPublicClient, createWalletClient, custom, defineChain } from 'viem';
import { publicActionsL2, walletActionsL2 } from 'viem/op-stack';
export const devnet = defineChain({
id: 42069,
sourceId: 42069,
name: 'Moved',
nativeCurrency: {
decimals: 18,
name: 'Ether',
symbol: 'ETH',
},
rpcUrls: {
default: {
http: ['https://devnet.moved.network'],
},
},
});
export const getAccount = async () => {
const [account] = await window.ethereum!.request({
method: 'eth_requestAccounts',
});
return account;
};
export const getMoveAccount = async () => {
const account = await getAccount();
const moveAccount = account.slice(0, 2) + '000000000000000000000000' + account.slice(2);
return moveAccount;
};
export const publicClient = () =>
createPublicClient({
chain: devnet,
transport: custom(window.ethereum!),
}).extend(publicActionsL2());
export const walletClient = () =>
createWalletClient({
chain: devnet,
transport: custom(window.ethereum!),
}).extend(walletActionsL2());
export const counterPayload = async (method: string) => {
const moveAccount = await getMoveAccount();
const entryFunction = EntryFunction.build(
`${moveAccount}::counter`,
method,
[],
[AccountAddress.fromString(moveAccount)],
);
const transactionPayload = new TransactionPayloadEntryFunction(entryFunction);
return transactionPayload.bcsToHex().toString() as `0x${string}`;
};
Start the development server:
npm run dev
Open http://localhost:3000
in your browser to interact with the contract.
You've successfully created a simple Next.js website to interact with your Move smart contract. Users can now send transactions to increment the counter and retrieve its value. In the next section, we will explore deploying this website for public access.