{"circuit_id":"coinbase_attestation","display_name":"Coinbase KYC","description":"Prove KYC attestation from Coinbase without revealing identity","e2e_encryption":{"enabled":false,"description":"This deployment provides no hardware TEE attestation. HTTPS protects transport; the prover receives proof inputs.","sdk_usage":"generateProof checks the actual challenge for teePublicKey and encrypts when available."},"local_mcp_server":{"recommended":true,"npm_package":"@zkproofport-ai/mcp","version":"0.3.0","install":"npm install -g @zkproofport-ai/mcp@latest","readme":"https://www.npmjs.com/package/@zkproofport-ai/mcp","required_arguments":["circuit"],"generate_proof":{"circuit":"coinbase_kyc","scope":"myapp:membership"}},"sdk":{"package":"@zkproofport-ai/sdk","repository":"https://github.com/zkproofport/proofport-ai","install":"npm install @zkproofport-ai/sdk@latest ethers","description":"Use generateProof to prepare witnesses, request a challenge, pay using the explicit payment wallet, and encrypt when the deployment supplies a TEE key.","quick_start":"import { createConfig, generateProof } from '@zkproofport-ai/sdk';\n// Existing signer and explicitly selected funded payment wallet stay in local code.\nconst result = await generateProof(\n  createConfig({ baseUrl: 'https://ai.zkproofport.app' }),\n  { attestation: existingAttestationSigner, payment: existingPaymentWallet },\n  { circuit: 'coinbase_kyc', scope: 'myapp:membership' },\n);","cli":"PROOFPORT_URL=https://ai.zkproofport.app zkproofport-prove coinbase_kyc --scope myapp:membership --silent","payment_selection":"Load PAYMENT_PRIVATE_KEY for pay_with=key, or select an existing Circle/CDP payment wallet. The attestation signer is not a payment fallback."},"constants":{"eas":{"graphql_endpoint":"https://base.easscan.org/graphql","schema_id":"0xf8b05c79f090979bf4a80270aba232dff11a10d9ca55c4f88de95317970f0de9","chain_id":8453},"authorized_signers":["0x952f32128AF084422539C4Ff96df5C525322E564","0x8844591D47F17bcA6F5dF8f6B64F4a739F1C0080","0x88fe64ea2e121f49bb77abea6c0a45e93638c3c5","0x44ace9abb148e8412ac4492e9a1ae6bd88226803"],"contracts":{"coinbase_attester":"0x357458739F90461b99789350868CD7CF330Dd7EE","function_selector":"0x56feed5e","verifier_address":"0xf7ded73e7a7fc8fb030c35c5a88d40abe6865382","chain_id":8453},"payment":{"required":true,"recipient":"0xc5B29033e63A986b601Fe430806A2C9735F2ea97","amount":"100000","asset":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","network":"base","currency":"USDC","decimals":6,"source":"The first configured offer is shown here for compatibility. Use the live 402 accepts list to select a network."},"rpc":{"eas_rpc_url":"https://mainnet.base.org","eas_rpc_chain":"Base Mainnet (8453)","verification_rpc_url":"https://base.gateway.tenderly.co"},"x402":{"protocol":"x402 v2; use the selected live offer","chains":[{"network":"eip155:8453","network_name":"base","asset":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913","decimals":6,"settlement":"facilitator","eip712_domain":{"name":"USD Coin","version":"2","chainId":8453,"verifyingContract":"0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"}},{"network":"eip155:1","network_name":"ethereum","asset":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48","decimals":6,"settlement":"payee","eip712_domain":{"name":"USD Coin","version":"2","chainId":1,"verifyingContract":"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"}}],"single_step_flow":"POST /api/v1/prove → 402 PAYMENT-REQUIRED and accepts → sign the selected offer → retry with PAYMENT-SIGNATURE. Return X-Payment-Nonce when the challenge supplied one.","nonce_details":"X-Payment-Nonce is single-use and circuit-bound; required with X-Payment-TX. Signed authorization payments also have their own replay protection.","nanopayments":"For arc-testnet-nano, use @circle-fin/x402-batching. Circle Agent Wallet backing EOA signs GatewayWalletBatched; Gateway settles against deposited balance. USDC in the wallet and deposited Gateway balance are separate."},"verification":{"verifier_address":"0xf7ded73e7a7fc8fb030c35c5a88d40abe6865382","chain_id":8453,"chain_name":"Base","rpc_url":"https://base.gateway.tenderly.co","function_signature":"verify(bytes proof, bytes32[] publicInputs) external view returns (bool)","input_format":"Keep proof and publicInputs separate; split the publicInputs hex blob into bytes32 words."}},"formulas":{"signal_hash":{"description":"Binds the proof to a specific user address, scope string, and circuit. Used as the message the user signs.","formula":"signal_hash = keccak256(abi.encodePacked(userAddress, scope, circuitId))","ethers_v6_code":"ethers.keccak256(ethers.solidityPacked(['address', 'string', 'string'], [userAddress, scope, 'coinbase_attestation']))"},"scope_bytes":{"description":"The keccak256 hash of the scope string. Scopes partition nullifiers so the same user can prove once per scope.","formula":"scope_bytes = keccak256(toUtf8Bytes(scope))","ethers_v6_code":"ethers.keccak256(ethers.toUtf8Bytes(scope))"},"nullifier":{"description":"Prevents double-proving for the same user + scope. Deterministic from address, signal_hash, and scope_bytes.","formula":"nullifier = keccak256(keccak256(address_bytes || signal_hash_bytes) || scope_bytes_bytes)","ethers_v6_code":"const userSecret = ethers.keccak256(ethers.concat([ethers.getBytes(userAddress), ethers.getBytes(signal_hash)]));\nconst nullifier = ethers.keccak256(ethers.concat([ethers.getBytes(userSecret), ethers.getBytes(scope_bytes)]));"},"merkle_leaf":{"description":"Each leaf in the authorized-signers Merkle tree is the keccak256 hash of the checksum-encoded address bytes.","formula":"leaf = keccak256(address_bytes_20)","ethers_v6_code":"ethers.keccak256(ethers.getBytes(ethers.getAddress(signerAddress)))"},"merkle_tree":{"description":"Binary Merkle tree built bottom-up. Each internal node = keccak256(left_child || right_child). If a layer has an odd number of nodes, the last node is duplicated as its own sibling. The proof is the list of sibling hashes from leaf to root.","formula":"node = keccak256(left || right); if odd count, right = left for last pair","ethers_v6_code":"ethers.keccak256(ethers.concat([ethers.getBytes(left), ethers.getBytes(right)]))"}},"input_schema":{"fields":[{"name":"signal_hash","type":"hex string","bytes":32,"description":"keccak256(abi.encodePacked(userAddress, scope, circuitId)) -- binds proof to user + scope + circuit","how_to_obtain":"Computed from user address, scope, and circuit ID"},{"name":"nullifier","type":"hex string","bytes":32,"description":"Double-proof prevention hash derived from user address, signal_hash, and scope_bytes","how_to_obtain":"Computed from user address, signal_hash, and scope"},{"name":"scope_bytes","type":"hex string","bytes":32,"description":"keccak256(toUtf8Bytes(scope)) -- hashed scope string","how_to_obtain":"Computed from user address, signal_hash, and scope"},{"name":"merkle_root","type":"hex string","bytes":32,"description":"Root of the Merkle tree over authorized Coinbase attester addresses","how_to_obtain":"Built from authorized attester addresses"},{"name":"user_address","type":"hex string","bytes":20,"description":"The Ethereum address of the user requesting the proof (checksummed or lowercase)","how_to_obtain":"User wallet address"},{"name":"signature","type":"hex string","bytes":65,"description":"User personal_sign signature over signal_hash (r + s + v)","how_to_obtain":"User signs signal_hash via personal_sign"},{"name":"user_pubkey_x","type":"hex string","bytes":32,"description":"X coordinate of the user uncompressed secp256k1 public key","how_to_obtain":"Recovered from user signature"},{"name":"user_pubkey_y","type":"hex string","bytes":32,"description":"Y coordinate of the user uncompressed secp256k1 public key","how_to_obtain":"Recovered from user signature"},{"name":"raw_transaction","type":"hex string","bytes":null,"description":"Signed RLP-encoded attestation transaction bytes (EIP-1559, 12 fields, variable length)","how_to_obtain":"Fetched from EAS attestation transaction"},{"name":"tx_length","type":"number","bytes":null,"description":"Byte length of raw_transaction","how_to_obtain":"Fetched from EAS attestation transaction"},{"name":"coinbase_attester_pubkey_x","type":"hex string","bytes":32,"description":"X coordinate of the Coinbase attester uncompressed secp256k1 public key","how_to_obtain":"Recovered from attestation transaction signature"},{"name":"coinbase_attester_pubkey_y","type":"hex string","bytes":32,"description":"Y coordinate of the Coinbase attester uncompressed secp256k1 public key","how_to_obtain":"Recovered from attestation transaction signature"},{"name":"merkle_proof","type":"hex string[]","bytes":32,"description":"Array of sibling hashes from leaf to root in the authorized-signers Merkle tree","how_to_obtain":"Built from authorized attester addresses"},{"name":"leaf_index","type":"number","bytes":null,"description":"Index of the attester in the AUTHORIZED_SIGNERS array (0-based)","how_to_obtain":"Built from authorized attester addresses"},{"name":"depth","type":"number","bytes":null,"description":"Depth of the Merkle proof (number of sibling hashes)","how_to_obtain":"Built from authorized attester addresses"}]},"endpoints":{"prove":{"method":"POST","url":"https://ai.zkproofport.app/api/v1/prove","content_type":"application/json","flow":"x402 single-step: POST with {circuit} → 402 with nonce and live offers → pay when required → retry with {circuit, inputs} over HTTPS and payment headers","timeout_hint":"10-30 seconds","description":"Settles the selected payment and generates the proof. Encrypt with teePublicKey when the challenge supplies it; otherwise the prover receives the witness over HTTPS."},"guide":{"method":"GET","url":"https://ai.zkproofport.app/api/v1/guide/coinbase_attestation","description":"Returns this guide as JSON (the document you are reading)"}}}