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 const5. 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 const6. 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 const7. 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 const8. 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