Payments
This guide will walk you through the process of creating a payment flow from the perspective of
interacting with a contract with EIP-5792 (the useSendCalls
Hook)
to submit a bundle of contract calls to Porto.
Steps
Connect Account
Follow the Onboard & Discover Accounts guide to get this set up.
Create BuyNow
Component
We will add a simple "Buy Now" button that will trigger the payment flow.
BuyNow.tsx
import * as React from 'react'
export function BuyNow() {
return (
<form>
<button type="submit">Buy Now</button>
</form>
)
}
Hook up useSendCalls
Next, we will add the useSendCalls
hook to submit a batch of contract calls.
- For the first call, we will request for the user to allow us to spend
10 EXP
(a payment hold), - For the second call, we will mint the NFT which will also debit
10 EXP
from the user's account.
BuyNow.tsx
import * as React from 'react'
import { useSendCalls } from 'wagmi'
import { parseEther } from 'viem'
import { expConfig, nftConfig } from './abi'
export function BuyNow() {
const { sendCalls } = useSendCalls()
async function submit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault()
sendCalls({
calls: [
{
abi: expConfig.abi,
args: [nftConfig.address, parseEther('10')],
functionName: 'approve',
to: expConfig.address
},
{
abi: nftConfig.abi,
functionName: 'mint',
to: nftConfig.address
}
]
})
}
return (
<form>
<form onSubmit={submit}>
<button type="submit">Buy Now</button>
</form>
)
}
Add Pending State
We will also display the pending state to the user while we are waiting for them to approve the request.
BuyNow.tsx
import * as React from 'react'
import { useSendCalls } from 'wagmi'
import { parseEther } from 'viem'
import { expConfig, nftConfig } from './abi'
export function BuyNow() {
const { isPending, sendCalls } = useSendCalls()
async function submit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault()
sendCalls({
calls: [
{
abi: expConfig.abi,
args: [nftConfig.address, parseEther('10')],
functionName: 'approve',
to: expConfig.address
},
{
abi: nftConfig.abi,
functionName: 'mint',
to: nftConfig.address
}
]
})
}
return (
<form onSubmit={submit}>
<button
disabled={isPending}
type="submit"
>
Buy now
{isPending ? 'Check prompt' : 'Buy Now'}
</button>
</form>
)
}
Hook up useWaitForCallsStatus
Now that we have the calls submitted, we can hook up the useWaitForCallsStatus
hook to wait for the calls to be confirmed,
and show a "Completing purchase" message to the user.
BuyNow.tsx
import * as React from 'react'
import { useSendCalls } from 'wagmi'
import { useSendCalls, useWaitForCallsStatus } from 'wagmi'
import { parseEther }from 'viem'
import { expConfig, nftConfig } from './abi'
export function BuyNow() {
const {
data,
isPending,
sendCalls
} = useSendCalls()
const { isLoading: isConfirming } = useWaitForCallsStatus({
id: data?.id,
})
async function submit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault()
sendCalls({
calls: [
{
abi: expConfig.abi,
args: [nftConfig.address, parseEther('10')],
functionName: 'approve',
to: expConfig.address
},
{
abi: nftConfig.abi,
functionName: 'mint',
to: nftConfig.address
}
]
})
}
return (
<form onSubmit={submit}>
<button disabled={isPending || isConfirming} type="submit">
{isPending ? ( {
'Check prompt' {
) : isConfirming ? (
'Completing purchase'
) : (
'Buy Now'
)}
</button>
</form>
)
}
Display Success State
BuyNow.tsx
import * as React from 'react'
import { parseEther }from 'viem'
import { useSendCalls, useWaitForCallsStatus } from 'wagmi'
import { expConfig, nftConfig } from './abi'
export function BuyNow() {
const { data, isPending, sendCalls } = useSendCalls()
const {
isLoading: isConfirming,
isSuccess: isConfirmed,
} = useWaitForCallsStatus({
id: data?.id,
})
async function submit(event: React.FormEvent<HTMLFormElement>) {
event.preventDefault()
sendCalls({
calls: [
{
abi: expConfig.abi,
args: [nftConfig.address, parseEther('10')],
functionName: 'approve',
to: expConfig.address
},
{
abi: nftConfig.abi,
functionName: 'mint',
to: nftConfig.address
}
]
})
}
if (isConfirmed)
return (
<div>
<img alt="Running Sneaker" src="/sneaker.png" />
<div>Purchase complete!</div>
</div>
)
return (
<form onSubmit={submit}>
<button disabled={isPending || isConfirming} type="submit">
{isPending ? (
'Check prompt'
) : isConfirming ? (
'Completing purchase'
) : (
'Buy Now'
)}
</button>
</form>
)
}