All you need to sign ERC-2612/DAI permits with viem and wagmi.
- A set of functions and wagmi hooks to sign Permits
- Supports both ERC-2612 Permit and Dai non-standard permit
- Automatically manages nonce, version and token name - just pass in the contract address
- Handles USDC on Polygon PoS edge-case
npm i wagmi-permit
pnpm add wagmi-permit
bun i wagmi-permit
yarn add wagmi-permit
The auto-generated API docs can be found here
import { usePermit } from "wagmi-permit";
function PermitExample() {
const { data: walletClient } = useWalletClient();
/* No need to specify name, nonce and permit version, the hook takes care of all that automatically */
const { signPermit, signature } = usePermit({
walletClient,
ownerAddress: walletClient?.account.address,
chainId: 1,
spenderAddress: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", // vitalik.eth
contractAddress: "0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf", // usdc on mainnet
value: parseEther("10"),
deadline: BigInt(Math.floor(Date.now() / 1000) + 100_000),
});
return (
<>
<pre>{JSON.stringify(signature, null, 2)}</pre>
<button
onClick={async () => {
/* Permit signature is returned both from the action and from the hook */
const permitSignature = await signPermit?.();
console.log(permitSignature);
}}
>
Sign Permit
</button>
</>
);
}
You can override the data passed to the permit signature in the hook...
const { signPermit, signature } = usePermit({
walletClient,
ownerAddress: walletClient?.account.address,
chainId: 1,
spenderAddress: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", // vitalik.eth
contractAddress: "0xa2327a938febf5fec13bacfb16ae10ecbc4cbdcf", // usdc on mainnet
value: parseEther("10"),
deadline: BigInt(Math.floor(Date.now() / 1000) + 100_000),
/** Overrides */
nonce: 2n,
erc20Name: "Overriden Token Name",
version: "2",
});
or directly in the function
<button
onClick={async () => {
/* Permit signature is returned both from the action and from the hook */
const permitSignature = await signPermit?.({
nonce: 2n,
erc20Name: "Overriden Token Name",
version: "2",
});
console.log(permitSignature);
}}
>
Sign Permit
</button>
Sign Dai permits with the signPermitDai function returned from the hook
import { usePermit } from "wagmi-permit";
function DaiPermitExample() {
const { data: walletClient } = useWalletClient();
const { signPermitDai, signature } = usePermit({
walletClient,
ownerAddress: walletClient?.account.address,
chainId: 1,
spenderAddress: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", // vitalik.eth
contractAddress: "0x6b175474e89094c44da98b954eedeac495271d0f", // dai on mainnet
value: parseEther("10"),
deadline: BigInt(Math.floor(Date.now() / 1000) + 100_000),
});
return (
<>
<pre>{JSON.stringify(signature, null, 2)}</pre>
<button
onClick={async () => {
/* Permit signature is returned both from the action and from the hook */
const permitSignature = await signPermitDai?.();
console.log(permitSignature);
}}
>
Sign Permit
</button>
</>
);
}
You can also use just the permit signing functions, for both standard tokens and Dai
import {signPermit} from "wagmi-permit";
import {WalletClient} from "wagmi";
async function signPermitForUSDC(walletClient: WalletClient) {
const sig = await signPermit(walletClient, {
spenderAddress: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266",
ownerAddress: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8",
contractAddress: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
erc20Name: "USD Coin",
version: "2",
deadline: BigInt(Math.floor(Date.now() / 1000) + 100_000),
nonce: 0n,
chainId: 1,
value: 1_000_000_000n,
});
console.log(sig);
}
Play with an example of signing a USDC permit on Optimism Mainnet in the example app under example-app
.
git clone https://github.com/vacekj/wagmi-permit
cd wagmi-permit
cd example-app
npm i
npm run dev
Information on various tokens, their supported permit type, version and methods can be found in PERMIT.md