PE

permit2-allowance-flow

Handles secure ERC20/Permit2 allowance workflows, including reset-to-zero logic.

Install

mkdir -p .claude/skills/permit2-allowance-flow && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/14459" && unzip -o skill.zip -d .claude/skills/permit2-allowance-flow && rm skill.zip

Installs to .claude/skills/permit2-allowance-flow

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Complete approval flow with reset-to-zero pattern. Use when implementing full ERC20 and Permit2 approval workflow.
114 chars✓ has a “when” trigger
Advanced

Key capabilities

  • Ensure ERC20 allowance for Permit2.
  • Handle reset-to-zero pattern for tokens requiring it.
  • Ensure Permit2 allowance for a specific spender.
  • Write contract transactions for approval.
  • Wait for transaction receipts.

How it works

This skill ensures spending limits by checking current ERC20 and Permit2 allowances, and if insufficient, it executes approval transactions, including a reset-to-zero pattern for specific tokens, and waits for transaction receipts.

Inputs & outputs

You give it
Token address, spender address, amount needed, writeContractAsync function, publicClient, and user address.
You get back
Confirmed ERC20 and Permit2 allowances, or executed approval transactions on the blockchain.

When to use permit2-allowance-flow

  • Implement token approvals
  • Manage Permit2 allowances
  • Handle ERC20 transfer flows

About this skill

Permit2 Allowance Transfer - Complete Flow

Full implementation with reset-to-zero pattern for tokens that require it.

Constants

const PERMIT2_ADDRESS = '0x000000000022D473030F116dDEE9F6B43aC78BA3'

const PERMIT2_APPROVE_ABI = [
  {
    inputs: [
      { name: 'token', type: 'address' },
      { name: 'spender', type: 'address' },
      { name: 'amount', type: 'uint160' },
      { name: 'expiration', type: 'uint48' }
    ],
    name: 'approve',
    outputs: [],
    stateMutability: 'nonpayable',
    type: 'function'
  }
] as const

Complete Approval Function

async function ensureSpendingLimits(
  tokenAddress: string,
  spenderAddress: string,
  amountNeeded: bigint,
  writeContractAsync: (config: WriteContractParameters) => Promise<Hash>,
  publicClient: PublicClient,
  address: string
) {
  // Step 1: Ensure ERC20 → Permit2 allowance
  const currentErc20Allowance = await publicClient.readContract({
    address: tokenAddress,
    abi: erc20Abi,
    functionName: 'allowance',
    args: [address, PERMIT2_ADDRESS]
  })

  if (currentErc20Allowance < amountNeeded) {
    // Some tokens (like USDT) require resetting to 0 first
    try {
      const hash = await writeContractAsync({
        address: tokenAddress,
        abi: erc20Abi,
        functionName: 'approve',
        args: [PERMIT2_ADDRESS, amountNeeded]
      })
      await publicClient.waitForTransactionReceipt({ hash })
    } catch {
      // Reset to 0, then approve
      const hash0 = await writeContractAsync({
        address: tokenAddress,
        abi: erc20Abi,
        functionName: 'approve',
        args: [PERMIT2_ADDRESS, 0n]
      })
      await publicClient.waitForTransactionReceipt({ hash: hash0 })

      const hash1 = await writeContractAsync({
        address: tokenAddress,
        abi: erc20Abi,
        functionName: 'approve',
        args: [PERMIT2_ADDRESS, amountNeeded]
      })
      await publicClient.waitForTransactionReceipt({ hash: hash1 })
    }
  }

  // Step 2: Ensure Permit2 → Spender allowance
  const currentPermit2Allowance = await publicClient.readContract({
    address: PERMIT2_ADDRESS,
    abi: [{
      inputs: [
        { name: 'owner', type: 'address' },
        { name: 'token', type: 'address' },
        { name: 'spender', type: 'address' }
      ],
      name: 'allowance',
      outputs: [
        { name: 'amount', type: 'uint160' },
        { name: 'expiration', type: 'uint48' },
        { name: 'nonce', type: 'uint48' }
      ],
      stateMutability: 'view',
      type: 'function'
    }],
    functionName: 'allowance',
    args: [address, tokenAddress, spenderAddress]
  })

  if (currentPermit2Allowance[0] < amountNeeded) {
    const threeDaysSecs = 3n * 24n * 60n * 60n
    const expiration = BigInt(Math.floor(Date.now() / 1000)) + threeDaysSecs

    const hash = await writeContractAsync({
      address: PERMIT2_ADDRESS,
      abi: PERMIT2_APPROVE_ABI,
      functionName: 'approve',
      args: [tokenAddress, spenderAddress, amountNeeded, expiration]
    })
    await publicClient.waitForTransactionReceipt({ hash })
  }
}

When not to use it

  • When the user needs to implement a custom approval flow without the reset-to-zero pattern.
  • When the user wants to bypass the Permit2 allowance mechanism.
  • When the user needs to manage tokens that do not require a reset-to-zero pattern.

Limitations

  • The skill specifically implements the reset-to-zero pattern for ERC20 tokens.
  • It relies on the `PERMIT2_ADDRESS` constant.
  • It sets a fixed expiration of three days for Permit2 allowances.

How it compares

This skill provides a complete and reliable approval flow for ERC20 and Permit2 tokens, including a specific reset-to-zero pattern for problematic tokens, which is more complete than a basic approval transaction.

Compared to similar skills

permit2-allowance-flow side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
permit2-allowance-flow (this skill)04moNo flagsAdvanced
tier-entitlements16moNo flagsIntermediate
qruiq-google-auth04moReviewIntermediate
dev-supabase02moReviewIntermediate

Try saying

Example prompts that trigger this skill in your AI assistant.

Search skills

Search the agent skills registry