Documentation Index
Fetch the complete documentation index at: https://mintlify.com/zkp2p/zkp2p-contracts/llms.txt
Use this file to discover all available pages before exploring further.
This guide covers common issues you may encounter when integrating with or developing on zkp2p-v2-contracts, along with their solutions.
Deployment Issues
Contract Deployment Fails
Symptoms: Deployment script fails with error or timeout
Common Causes:
-
Missing Environment Variables
# Check .env file has all required keys
ALCHEMY_API_KEY=your_key_here
BASE_DEPLOY_PRIVATE_KEY=your_private_key
BASESCAN_API_KEY=your_basescan_key
-
Insufficient Gas
- Solution: Increase gas limit in
hardhat.config.ts
- Check current network gas prices
-
Network Connection Issues
- Verify RPC endpoint is accessible
- Try alternative RPC provider (Alchemy, Infura)
-
Nonce Mismatch
# Reset nonce by deleting deployment cache
rm -rf deployments/{network}/.migrations.json
Deployment Scripts Skip Unexpectedly
Symptoms: Some deployment scripts don’t execute
Solution: Check the func.skip condition in deployment files. Many scripts are frozen in production:
func.skip = async (hre: HardhatRuntimeEnvironment): Promise<boolean> => {
const network = hre.network.name;
if (network === "localhost" || network === "hardhat") {
return false;
}
return true; // Frozen for production networks
};
For production deployments, payment methods are now managed by 15_configure_v2_payment_methods.ts.
Contract Verification Fails
Symptoms: Etherscan/Basescan verification returns error
Solutions:
-
Check API Key:
# Verify Basescan API key is set
echo $BASESCAN_API_KEY
-
Wait for Block Confirmations:
# Wait 5-10 blocks before verifying
yarn etherscan:base
-
Manual Verification:
- Use Basescan GUI with flattened contract
- Ensure compiler version matches
hardhat.config.ts
Payment Verification Issues
Payment Proof Verification Fails
Symptoms: fulfillIntent() reverts with verification error
Common Errors:
“UPV: Invalid payment method”
Cause: Payment method not registered with UnifiedPaymentVerifier
Solution:
// Check if payment method is registered
const isRegistered = await unifiedVerifier.isPaymentMethod(paymentMethodHash);
if (!isRegistered) {
// Register payment method (owner only)
await unifiedVerifier.addPaymentMethod(paymentMethodHash);
}
See contracts/unifiedVerifier/UnifiedPaymentVerifier.sol:142
”UPV: Invalid attestation”
Cause: EIP-712 signature verification failed
Solutions:
-
Check Witness Signer:
// Ensure correct witness signed the attestation
const expectedWitness = await attestationVerifier.witness();
-
Verify Domain Separator:
// Domain must match verifier contract
const domainSeparator = await unifiedVerifier.DOMAIN_SEPARATOR();
-
Check Data Hash Integrity:
// dataHash must match keccak256(data)
const computedHash = ethers.utils.keccak256(attestation.data);
assert(computedHash === attestation.dataHash);
See contracts/unifiedVerifier/UnifiedPaymentVerifier.sol:183-214
”UPV: Snapshot hash mismatch”
Cause: Intent snapshot doesn’t match on-chain intent
Solution:
Ensure all intent fields match:
// Verify intent snapshot
const intent = await orchestrator.getIntent(intentHash);
// All these must match:
assert(snapshot.intentHash === intentHash);
assert(snapshot.payeeDetails === intent.payeeId);
assert(snapshot.amount === intent.amount);
assert(snapshot.paymentMethod === intent.paymentMethod);
assert(snapshot.fiatCurrency === intent.fiatCurrency);
assert(snapshot.conversionRate === intent.conversionRate);
assert(snapshot.signalTimestamp === intent.timestamp);
See contracts/unifiedVerifier/UnifiedPaymentVerifier.sol:220-234
”UPV: Snapshot timestamp buffer exceeds maximum”
Cause: Timestamp buffer exceeds 48-hour maximum
Solution:
const MAX_TIMESTAMP_BUFFER_MS = 48 * 60 * 60 * 1000; // 48 hours
assert(snapshot.timestampBuffer <= MAX_TIMESTAMP_BUFFER_MS);
See contracts/unifiedVerifier/UnifiedPaymentVerifier.sol:31
”UPV: Data hash mismatch”
Cause: Signed data hash doesn’t match actual data
Solution:
// Recompute data hash
const dataHash = ethers.utils.keccak256(attestation.data);
// Update attestation
attestation.dataHash = dataHash;
See contracts/unifiedVerifier/UnifiedPaymentVerifier.sol:202-205
Nullifier Already Used
Symptoms: Transaction reverts with nullifier error
Cause: Payment ID has already been used (double-spend prevention)
Explanation:
Nullifiers are created as: keccak256(abi.encodePacked(paymentMethod, paymentId))
Each payment can only be used once across the entire protocol.
Solution:
- If legitimate retry: Use a different payment (new payment ID)
- If testing: Deploy fresh contracts or use different payment IDs
- In production: This is expected behavior - find the original intent that used this payment
See contracts/unifiedVerifier/UnifiedPaymentVerifier.sol:242-245
Payment Method Not Found in Registry
Symptoms: signalIntent() reverts with payment method error
Cause: Payment method not registered in PaymentVerifierRegistry
Solution:
// Add payment method to registry (owner only)
await paymentVerifierRegistry.addPaymentMethod(
paymentMethodHash,
unifiedVerifierAddress,
[Currency.USD, Currency.EUR] // Supported currencies
);
Intent Lifecycle Issues
Cannot Signal Intent
Symptoms: signalIntent() reverts
Common Causes:
Insufficient Deposit Liquidity
// Check available deposit amount
const deposit = await escrow.getDeposit(depositId);
const available = deposit.availableAmount;
if (intentAmount > available) {
// Reduce intent amount or choose different deposit
}
Amount Below Minimum
// Check minimum amount for payment method
const minAmount = await escrow.getMinAmount(depositId, paymentMethodHash);
if (intentAmount < minAmount) {
// Increase intent amount
}
Deposit Expired
// Check deposit expiry
const deposit = await escrow.getDeposit(depositId);
const currentTime = Math.floor(Date.now() / 1000);
if (currentTime > deposit.expiryTimestamp) {
// Deposit has expired, choose another
}
Invalid Gating Signature
// If orchestrator requires gating signature
const gatingSignature = await generateGatingServiceSignature(
gatingServiceWallet,
intentOwner,
escrowAddress,
depositId,
// ... other params
);
await orchestrator.signalIntent({
// ... intent params
}, gatingSignature); // Include signature
Cannot Fulfill Intent
Symptoms: fulfillIntent() reverts
Common Causes:
Intent Expired
// Check intent expiry
const intent = await orchestrator.getIntent(intentHash);
const currentTime = Math.floor(Date.now() / 1000);
if (currentTime > intent.expiryTimestamp) {
// Intent has expired, must cancel instead
await orchestrator.cancelIntent(intentHash);
}
Wrong Intent State
// Intent must be in SIGNALED state
const intent = await orchestrator.getIntent(intentHash);
const IntentState = {
NONE: 0,
SIGNALED: 1,
FULFILLED: 2,
CANCELLED: 3
};
assert(intent.state === IntentState.SIGNALED);
Payment Verification Failed
See Payment Verification Issues section above.
Cannot Cancel Intent
Symptoms: cancelIntent() reverts
Cause: Intent not yet expired or already fulfilled
Solution:
const intent = await orchestrator.getIntent(intentHash);
const currentTime = Math.floor(Date.now() / 1000);
if (currentTime <= intent.expiryTimestamp) {
// Wait until expiry before canceling
const waitTime = intent.expiryTimestamp - currentTime;
console.log(`Wait ${waitTime} seconds before canceling`);
}
Testing Issues
Tests Fail with Timeout
Symptoms: Tests hang or timeout
Solutions:
-
Increase Timeout:
// In test file
this.timeout(120000); // 2 minutes
-
Use Fast Tests:
yarn test:fast # Skips slow integration tests
-
Run Specific Suite:
yarn test test/unifiedVerifier/unifiedPaymentVerifier.spec.ts
Mock Contract Issues
Symptoms: Mock contracts behave unexpectedly
Solution: Check mock configuration in test setup
// Example: Configure mock USDC
usdcToken = await deployer.deployUSDCMock(
ethers.utils.parseUnits("1000000", 6),
"USDC",
"USDC"
);
// Transfer to test accounts
await usdcToken.transfer(maker.address, depositAmount);
await usdcToken.connect(maker.wallet).approve(escrow.address, depositAmount);
Foundry Tests Fail
Symptoms: yarn test:forge fails
Solutions:
-
Update Foundry:
-
Clean Cache:
-
Check Remappings:
# Verify remappings in foundry.toml
cat foundry.toml
Integration Issues
TypeScript Type Errors
Symptoms: TypeScript compilation errors with contract types
Solution: Regenerate Typechain bindings
# Full rebuild
yarn build
# Or just regenerate types
yarn compile
yarn typechain
Types are in typechain/ directory.
Contract Address Not Found
Symptoms: Cannot find deployed contract address
Solution: Check deployment artifacts
import { getDeployedContractAddress } from "./deployments/helpers";
const orchestratorAddress = getDeployedContractAddress(
network,
"Orchestrator"
);
Deployment artifacts are in deployments/{network}/.
Event Parsing Fails
Symptoms: Cannot parse contract events
Solution: Use correct event filter and parsing
// Listen for IntentSignaled event
const filter = orchestrator.filters.IntentSignaled();
const events = await orchestrator.queryFilter(filter, fromBlock, toBlock);
events.forEach(event => {
const { intentHash, escrow, depositId } = event.args;
console.log(`Intent ${intentHash} signaled`);
});
Gas Estimation Fails
Symptoms: Transaction reverts during gas estimation
Solutions:
-
Simulate Transaction:
// Use callStatic to simulate
try {
await orchestrator.callStatic.fulfillIntent({
intentHash,
paymentProof,
data
});
} catch (error) {
console.error("Simulation failed:", error);
}
-
Manual Gas Limit:
await orchestrator.fulfillIntent(
{ intentHash, paymentProof, data },
{ gasLimit: 500000 }
);
Registry Permission Issues
”Caller is not owner”
Symptoms: Registry modification reverts with ownership error
Cause: Only owner can modify registries
Solution:
// Check current owner
const owner = await paymentVerifierRegistry.owner();
// Transfer ownership if needed (current owner only)
await paymentVerifierRegistry.transferOwnership(newOwner);
“Caller does not have write permission”
Symptoms: Cannot write to NullifierRegistry
Cause: UnifiedPaymentVerifier needs write permission
Solution:
// Grant write permission (owner only)
await nullifierRegistry.addWritePermission(unifiedVerifierAddress);
See deployment script 01_deploy_unified_verifier.ts for setup example.
Network-Specific Issues
Base Network Issues
High Gas Prices
Solution: Wait for lower gas prices or increase gas budget
// Check current gas price
const gasPrice = await ethers.provider.getGasPrice();
console.log(`Current gas price: ${ethers.utils.formatUnits(gasPrice, "gwei")} gwei`);
RPC Rate Limiting
Solution: Use dedicated RPC provider or implement retry logic
// Exponential backoff retry
const retry = async (fn, retries = 3) => {
for (let i = 0; i < retries; i++) {
try {
return await fn();
} catch (error) {
if (i === retries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i)));
}
}
};
Base Sepolia (Testnet) Issues
Insufficient Test ETH
Solution: Get test ETH from Base Sepolia faucet
Test USDC Not Available
Solution: Deploy mock USDC for testing
const usdcMock = await deployer.deployUSDCMock(
ethers.utils.parseUnits("1000000", 6),
"USDC",
"USDC"
);
Build & Compilation Issues
Compilation Fails
Symptoms: yarn compile fails with Solidity errors
Solutions:
-
Clean Build:
-
Check Solidity Version:
- Contracts require Solidity ^0.8.18
- Verify
hardhat.config.ts compiler version
-
Missing Dependencies:
# Reinstall dependencies
rm -rf node_modules
yarn install
Module Not Found
Symptoms: Import errors in TypeScript files
Solution: Check module aliases in tsconfig.json
{
"compilerOptions": {
"paths": {
"@utils/*": ["./utils/*"],
"@typechain/*": ["./typechain/*"]
}
}
}
Slow RPC Responses
Solutions:
-
Use Protocol Viewer: Batch queries instead of individual calls
// Instead of multiple calls
const deposit1 = await escrow.getDeposit(id1);
const deposit2 = await escrow.getDeposit(id2);
// Use Protocol Viewer batching
const deposits = await protocolViewer.getDeposits(
escrowAddress,
[id1, id2]
);
-
Cache Results: Store frequently accessed data locally
-
Use Multicall: Batch multiple read operations
High Transaction Costs
Solutions:
- Optimize Data: Minimize data in transactions
- Use Relayers: For gasless transactions (configure RelayerRegistry)
- Batch Operations: Combine multiple actions when possible
Getting More Help
If you’re still experiencing issues:
- Check Test Files: See
test/ for working examples
- Review Deployment Scripts: See
deploy/ for configuration examples
- Read Contract Comments: Contracts have extensive NatSpec documentation
- Check GitHub Issues: zkp2p-v2-contracts issues
- Ask the Community: Visit zkp2p.xyz for community links
Useful References