@insumermodel/mppx-condition-gate
June 1, 2026 · View on GitHub
Condition-based access for mppx routes. One signed call between request and charge gives free access to wallets that meet your conditions; everyone else falls through to the normal paid path. Six condition types: token balance, NFT ownership, EAS attestation, Farcaster ID, plus ratio_to_amount (hold >= N x a spend amount) and ratio_to_supply (hold >= a fraction of token supply). No RPC management.
Migrating from
@insumermodel/mppx-token-gate? This is the v2 successor. See Migration below.
How it works
mppx embeds the payer's identity in every payment credential as a DID string (credential.source: "did:pkh:eip155:8453:0xABC..."). conditionGate reads that address, calls InsumerAPI to evaluate the configured conditions, and short-circuits the payment flow for wallets that pass.
- Request arrives with a payment credential
conditionGateextracts the payer address fromcredential.source- InsumerAPI evaluates the conditions and returns an ECDSA P-256 signed attestation
- Pass → free receipt returned (
reference: "condition-gate:free:{attestationId}") - Fail → delegates to the original
verify(normal payment proceeds)
The signed attestation is verifiable offline via JWKS. The adapter does not re-sign or wrap the result; the signature on the attestation is the one InsumerAPI produced.
Install
npm install @insumermodel/mppx-condition-gate
Usage
import { Mppx, tempo } from 'mppx/server'
import { conditionGate } from '@insumermodel/mppx-condition-gate'
const tempoCharge = tempo({
currency: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
recipient: '0xYourAddress',
})
const gatedCharge = conditionGate(tempoCharge, {
apiKey: process.env.INSUMER_API_KEY,
conditions: [{
type: 'nft_ownership',
contractAddress: '0xYourNFT',
chainId: 8453, // Base
}],
})
const mppx = Mppx.create({ methods: [gatedCharge] })
Works with any framework (Hono, Express, Elysia, Next.js) and any payment method (tempo, stripe). The adapter wraps Method.Server, so no middleware changes needed.
Condition types
Mix any of the six in a single call. matchMode: 'any' (default) passes when any one is met; matchMode: 'all' requires all of them.
Token balance
{
type: 'token_balance',
contractAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
chainId: 1,
threshold: 1000,
decimals: 6,
label: 'USDC >= 1000',
}
NFT ownership
{
type: 'nft_ownership',
contractAddress: '0xYourNFT',
chainId: 8453,
label: 'Holds the access NFT',
}
EAS attestation (compliance template)
{
type: 'eas_attestation',
template: 'coinbase_verified_account',
chainId: 8453,
label: 'Coinbase KYC verified',
}
Available templates: coinbase_verified_account, coinbase_verified_country, coinbase_one, gitcoin_passport_score, gitcoin_passport_active. See GET /v1/compliance/templates.
EAS attestation (raw schema)
{
type: 'eas_attestation',
schemaId: '0xf8b05c79f090979bf4a80270aba232dff11a10d9ca55c4f88de95317970f0de9',
attester: '0x357458739F90461b99789350868CD7CF330Dd7EE',
indexer: '0x2c7eE1E5f416dfF40054c27A62f7B357C4E8619C',
chainId: 8453,
label: 'Custom EAS attestation',
}
Farcaster ID
{
type: 'farcaster_id',
label: 'Has a Farcaster account',
}
Always evaluated on Optimism (chain 10). Passes if the wallet has any FID registered.
Ratio to amount
Self-scaling spend rule: passes when the wallet holds at least multiple times a per-request amount. RPC EVM chains only.
{
type: 'ratio_to_amount',
contractAddress: 'native', // or an ERC-20 address
chainId: 8453,
multiple: 10,
amount: 250,
label: 'Holds >= 10x the 250-unit spend',
}
Ratio to supply
Share-of-supply rule: passes when the wallet holds at least minFraction of the token's on-chain total supply. For project/governance tokens, not stablecoins. RPC EVM chains, ERC-20 contracts only.
{
type: 'ratio_to_supply',
contractAddress: '0x1f9840a85d5aF5bf1D1762F925BdADdC4201F984', // UNI
chainId: 1,
minFraction: 0.005, // 0.5% of supply
label: 'Holds >= 0.5% of UNI supply',
}
API key and credits
Each call to /v1/attest consumes one or more attestation credits. New keys ship with 10 free credits, enough to wire up the integration end-to-end before paying anything.
Two ways to provision a key.
Email signup (human-managed):
curl -X POST https://api.insumermodel.com/v1/keys/create \
-H "Content-Type: application/json" \
-d '{"email":"you@example.com","appName":"my-app","tier":"free"}'
On-chain (autonomous agent bootstrap): send USDC, USDT, or BTC to the platform wallet, then call POST /v1/keys/buy with the transaction hash. The transaction sender wallet is the identity, the payment is the auth — no email, no human in the loop.
Either way, set INSUMER_API_KEY as an environment variable in your runtime.
Top up an existing key on-chain via POST /v1/credits/buy. Accepted: USDC or USDT on any major EVM chain, USDC on Solana, or BTC on Bitcoin. See the credits endpoint for transaction format.
Pricing model: the wallet holder pays nothing at the gated route. The operator running the gate pays per attestation call out of the key's credit balance. Cost per attestation depends on tier and condition mix.
Options
| Option | Type | Default | Description |
|---|---|---|---|
apiKey | string | env.INSUMER_API_KEY | InsumerAPI key |
conditions | Condition[] | required | One or more conditions to evaluate |
matchMode | 'any' | 'all' | 'any' | Wallet must satisfy any or all conditions |
cacheTtlSeconds | number | 300 | In-memory cache TTL |
jwt | boolean | false | Request ES256 JWT alongside raw attestation |
apiBaseUrl | string | https://api.insumermodel.com | API base URL override |
Supported chains
30 EVM chains (Ethereum, Base, Polygon, Arbitrum, Optimism, BNB, Avalanche, and 23 more) + Solana + XRPL + Bitcoin. EAS conditions evaluate on EVM chains only. Farcaster always on Optimism.
Distinguishing free vs paid access
const receipt = Receipt.fromResponse(response)
if (receipt.reference.startsWith('condition-gate:free:')) {
const attestationId = receipt.reference.replace('condition-gate:free:', '')
// Free access. Attestation ID is retrievable via /v1/attestations/{id}
} else {
// Paid access
}
Fail-open behavior
If the attestation API is unreachable, the adapter falls through to the original payment method. Wallets that would have qualified for free access pay normally; everyone else is unaffected.
Migrating from mppx-token-gate
@insumermodel/mppx-token-gate (v1) is deprecated. Migration to v2:
- npm install @insumermodel/mppx-token-gate
+ npm install @insumermodel/mppx-condition-gate
- import { tokenGate } from '@insumermodel/mppx-token-gate'
+ import { conditionGate } from '@insumermodel/mppx-condition-gate'
- const gated = tokenGate(server, { ... })
+ const gated = conditionGate(server, { ... })
- if (receipt.reference.startsWith('token-gate:free:'))
+ if (receipt.reference.startsWith('condition-gate:free:'))
The condition shapes for token_balance and nft_ownership are unchanged. eas_attestation and farcaster_id are new in v2.
The parsXrplDid typo from v1 is fixed: use parseXrplDid. The cache helper renamed: clearTokenGateCache is now clearConditionGateCache.
License
MIT