Write to Contract
The useWriteContract
Composable allows you to mutate data on a smart contract, from a payable
or nonpayable
(write) function. These types of functions require gas to be executed, hence a transaction is broadcasted in order to change the state.
In the guide below, we will teach you how to implement a "Mint NFT" form that takes in a dynamic argument (token ID) using Wagmi. The example below builds on the Connect Wallet guide and uses the useWriteContract & useWaitForTransaction composables.
If you have already completed the Sending Transactions guide, this guide will look very similar! That's because writing to a contract internally broadcasts & sends a transaction.
Example
Feel free to check out the example before moving on:
Steps
1. Connect Wallet
Follow the Connect Wallet guide guide to get this set up.
2. Create a new component
Create your MintNft
component that will contain the Mint NFT logic.
<script setup lang="ts">
</script>
<template>
<form>
<input name="tokenId" placeholder="69420" required />
<button type="submit">Mint</button>
</form>
</template>
3. Add a form handler
Next, we will need to add a handler to the form that will send the transaction when the user hits "Mint". This will be a basic handler in this step.
<script setup lang="ts">
function submit(event: Event) {
const formData = new FormData(e.target as HTMLFormElement)
const tokenId = formData.get('tokenId') as string
}
</script>
<template
<form>
<form @submit.prevent="submit">
<input name="tokenId" placeholder="69420" required />
<button type="submit">Mint</button>
</form>
</template>
4. Hook up the useWriteContract
Composable
Now that we have the form handler, we can hook up the useWriteContract
Composable to send the transaction.
<script setup lang="ts">
import { useWriteContract } from 'wagmi'
import { abi } from './abi'
const { data: hash, writeContract } = useWriteContract()
function submit(event: Event) {
const formData = new FormData(e.target as HTMLFormElement)
const tokenId = formData.get('tokenId') as string
writeContract({
address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
abi,
functionName: 'mint',
args: [BigInt(tokenId)],
})
}
</script>
<template
<form @submit.prevent="submit">
<input name="tokenId" placeholder="69420" required />
<button type="submit">Mint</button>
<div v-if="hash">Transaction Hash: {{ hash }}</div>
</form>
</template>
export const abi = [
{
name: 'mint',
type: 'function',
stateMutability: 'nonpayable',
inputs: [{ internalType: 'uint32', name: 'tokenId', type: 'uint32' }],
outputs: [],
},
] as const
5. Add loading state (optional)
We can optionally add a loading state to the "Mint" button while we are waiting confirmation from the user's wallet.
<script setup lang="ts">
import { useWriteContract } from 'wagmi'
import { abi } from './abi'
const {
data: hash,
isPending,
writeContract
} = useWriteContract()
function submit(event: Event) {
const formData = new FormData(e.target as HTMLFormElement)
const tokenId = formData.get('tokenId') as string
writeContract({
address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
abi,
functionName: 'mint',
args: [BigInt(tokenId)],
})
}
</script>
<template
<form @submit.prevent="submit">
<input name="tokenId" placeholder="69420" required />
<button :disabled="isPending" type="submit">
<span v-if="isPending">Sending...</span>
<span v-else>Send</span>
</button>
<div v-if="hash">Transaction Hash: {{ hash }}</div>
</form>
</template>
export const abi = [
{
name: 'mint',
type: 'function',
stateMutability: 'nonpayable',
inputs: [{ internalType: 'uint32', name: 'tokenId', type: 'uint32' }],
outputs: [],
},
] as const
6. Wait for transaction receipt (optional)
We can also display the transaction confirmation status to the user by using the useWaitForTransactionReceipt
Composable.
<script setup lang="ts">
import {
useWaitForTransactionReceipt,
useWriteContract
} from 'wagmi'
import { abi } from './abi'
const {
data: hash,
isPending,
writeContract
} = useWriteContract()
function submit(event: Event) {
const formData = new FormData(e.target as HTMLFormElement)
const tokenId = formData.get('tokenId') as string
writeContract({
address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
abi,
functionName: 'mint',
args: [BigInt(tokenId)],
})
}
const { isLoading: isConfirming, isSuccess: isConfirmed } =
useWaitForTransactionReceipt({
hash,
})
</script>
<template
<form @submit.prevent="submit">
<input name="tokenId" placeholder="69420" required />
<button :disabled="isPending" type="submit">
<span v-if="isPending">Sending...</span>
<span v-else>Send</span>
</button>
<div v-if="hash">Transaction Hash: {{ hash }}</div>
<div v-if="isConfirming">Waiting for confirmation...</div>
<div v-if="isConfirmed">Transaction Confirmed!</div>
</form>
</template>
export const abi = [
{
name: 'mint',
type: 'function',
stateMutability: 'nonpayable',
inputs: [{ internalType: 'uint32', name: 'tokenId', type: 'uint32' }],
outputs: [],
},
] as const
7. Handle errors (optional)
If the user rejects the transaction, or the user does not have enough funds to cover the transaction, we can display an error message to the user.
<script setup lang="ts">
import {
useWaitForTransactionReceipt,
useWriteContract
} from 'wagmi'
import { abi } from './abi'
const {
data: hash,
error,
isPending,
writeContract
} = useWriteContract()
function submit(event: Event) {
const formData = new FormData(e.target as HTMLFormElement)
const tokenId = formData.get('tokenId') as string
writeContract({
address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
abi,
functionName: 'mint',
args: [BigInt(tokenId)],
})
}
const { isLoading: isConfirming, isSuccess: isConfirmed } =
useWaitForTransactionReceipt({
hash,
})
</script>
<template
<form @submit.prevent="submit">
<input name="tokenId" placeholder="69420" required />
<button :disabled="isPending" type="submit">
<span v-if="isPending">Sending...</span>
<span v-else>Send</span>
</button>
<div v-if="hash">Transaction Hash: {{ hash }}</div>
<div v-if="isConfirming">Waiting for confirmation...</div>
<div v-if="isConfirmed">Transaction Confirmed!</div>
<div v-if="error">
Error: {{ (error as BaseError).shortMessage || error.message }}
</div>
</form>
</template>
export const abi = [
{
name: 'mint',
type: 'function',
stateMutability: 'nonpayable',
inputs: [{ internalType: 'uint32', name: 'tokenId', type: 'uint32' }],
outputs: [],
},
] as const
8. Wire it up!
Finally, we can wire up our Send Transaction component to our application's entrypoint.
<script setup lang="ts">
import { useAccount } from '@wagmi/vue';
import Account from './Account.vue';
import Connect from './Connect.vue';
import MintNft from './MintNft.vue';
const { isConnected } = useAccount();
</script>
<template>
<Account v-if="isConnected" />
<Connect v-else />
<MintNft v-if="isConnected" />
</template>
<script setup lang="ts">
import {
useWaitForTransactionReceipt,
useWriteContract
} from 'wagmi'
import { abi } from './abi'
const {
data: hash,
error,
isPending,
writeContract
} = useWriteContract()
function submit(event: Event) {
const formData = new FormData(e.target as HTMLFormElement)
const tokenId = formData.get('tokenId') as string
writeContract({
address: '0xFBA3912Ca04dd458c843e2EE08967fC04f3579c2',
abi,
functionName: 'mint',
args: [BigInt(tokenId)],
})
}
const { isLoading: isConfirming, isSuccess: isConfirmed } =
useWaitForTransactionReceipt({
hash,
})
</script>
<template
<form @submit.prevent="submit">
<input name="tokenId" placeholder="69420" required />
<button :disabled="isPending" type="submit">
<span v-if="isPending">Sending...</span>
<span v-else>Send</span>
</button>
<div v-if="hash">Transaction Hash: {{ hash }}</div>
<div v-if="isConfirming">Waiting for confirmation...</div>
<div v-if="isConfirmed">Transaction Confirmed!</div>
<div v-if="error">
Error: {{ (error as BaseError).shortMessage || error.message }}
</div>
</form>
</template>
export const abi = [
{
name: 'mint',
type: 'function',
stateMutability: 'nonpayable',
inputs: [{ internalType: 'uint32', name: 'tokenId', type: 'uint32' }],
outputs: [],
},
] as const