<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: zk_nd3r</title>
    <description>The latest articles on DEV Community by zk_nd3r (@zknd3r).</description>
    <link>https://dev.to/zknd3r</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3866560%2F4b858a42-881c-4a24-911a-8a976060996c.jpeg</url>
      <title>DEV Community: zk_nd3r</title>
      <link>https://dev.to/zknd3r</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zknd3r"/>
    <language>en</language>
    <item>
      <title>The Agent Custody Problem</title>
      <dc:creator>zk_nd3r</dc:creator>
      <pubDate>Wed, 08 Apr 2026 01:27:38 +0000</pubDate>
      <link>https://dev.to/zknd3r/the-agent-custody-problem-5dac</link>
      <guid>https://dev.to/zknd3r/the-agent-custody-problem-5dac</guid>
      <description>&lt;p&gt;AI agents are handling real money.  Trading bots.  Treasury managers.  Procurement agents.  Settlement systems.  They need wallets.&lt;/p&gt;

&lt;p&gt;Three products launched in the last 30 days.  Trust Wallet Agent Kit covers 25+ chains.  Coinbase AgentKit introduced the x402 protocol for agent payments.  Human.tech shipped Agentic WaaP at WalletCon.  All of them have the same problem.&lt;/p&gt;

&lt;p&gt;The agent holds a hot key.  Or the platform holds it for them.  Either way,  one compromise drains everything.&lt;/p&gt;

&lt;p&gt;This is the agent custody problem: how do you give an agent the ability to transact without giving it the ability to steal?&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it matters now
&lt;/h2&gt;

&lt;p&gt;250,000 agents transact on-chain daily.  550+ agent projects.  $4.3B combined market cap.  The x402 payment standard,  co-founded by Coinbase and Cloudflare,  has processed $10M+ on Solana alone.  This is not a research problem.  This is a production problem.&lt;/p&gt;

&lt;p&gt;Every one of these agents holds keys.  Every one of those keys is a single point of failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three requirements
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Non-drainable wallets.&lt;/strong&gt;  The signing key must never exist as a whole.  Not at creation.  Not at signing.  Not at recovery.  This is a cryptographic guarantee,  not a trust assumption.  Split-key architectures where the agent holds one share and a second party holds another.  No single compromise drains the wallet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Enforceable spend policy.&lt;/strong&gt;  Rate limits in API middleware are not policy.  A compromised agent ignores client-side checks.  Policy must live on-chain,  in a smart contract that the agent cannot bypass.  Maximum daily spend.  Approved addresses.  Position size limits.  Enforced by consensus,  not by the agent's own code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Verifiable history.&lt;/strong&gt;  Every action the agent takes must be provable after the fact.  But "provable" does not mean "public."  A trading agent that publishes every position to a public ledger gets front-run.  A treasury agent that exposes every payment gets targeted.  You need attestation that proves what happened without revealing the details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why existing solutions fail
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Coinbase AgentKit.&lt;/strong&gt;  Custodial.  Coinbase holds the key,  or the agent holds a hot key with an emergency admin freeze.  Better than nothing.  Still a single point of compromise at the custody layer.  No split-key signing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trust Wallet TWAK.&lt;/strong&gt;  25+ chains.  220M user base.  Standard wallet model.  The agent gets full key access.  User-defined permissions exist,  but they are client-side.  A compromised agent has the key.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Human.tech WaaP.&lt;/strong&gt;  Two-party computation between user device and secure enclave.  Permission tokens for spending caps.  Closest to real agent custody.  But every transaction is visible on-chain.  A trading agent using WaaP broadcasts its entire strategy to the world.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lit Protocol.&lt;/strong&gt;  Threshold MPC with a network of nodes.  N-of-M signing.  Weaker trust model than 2PC because you trust a threshold of nodes rather than a cryptographic split.  No attestation layer.  No privacy chain.  7,000 agent wallets created.  No way to prove what those agents did without exposing it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fireblocks.&lt;/strong&gt;  Institutional MPC.  Closed source.  $699/month minimum.  Solves custody for hedge funds,  not for autonomous agents that need programmable policy and verifiable history.&lt;/p&gt;

&lt;p&gt;Every one of these products solves part of the problem.  None solves all three requirements together.&lt;/p&gt;

&lt;h2&gt;
  
  
  What agent custody actually requires
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;2PC-MPC signing.&lt;/strong&gt;  The key is generated in two shares.  One share stays with the agent operator.  One share stays with the custody network.  At signing time,  both parties compute their part.  The full key never exists,  not in memory,  not in transit,  not in any backup.  This is not threshold signing where enough colluding nodes reconstruct the key.  This is a two-party protocol where reconstruction is cryptographically impossible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On-chain policy.&lt;/strong&gt;  Sui Move contracts that enforce spend limits,  approved counterparties,  and position constraints.  The agent submits a transaction request.  The policy contract checks it.  If it violates the rules,  the transaction never gets signed.  No API middleware.  No client-side checks.  On-chain,  deterministic,  auditable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Privacy attestation.&lt;/strong&gt;  Zcash shielded memos.  Every agent action gets attested to a Merkle tree anchored on Zcash mainnet.  The attestation proves the action happened.  The shielded memo hides the details.  An auditor can verify the proof.  A competitor cannot see the position.  BLAKE2b verification via EIP-152 on Ethereum at 712 gas per hash.  The same proof root,  checkable on multiple chains.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cross-chain verification.&lt;/strong&gt;  One proof system.  Multiple verification surfaces.  The same attestation root registered on Ethereum,  Arbitrum,  Base,  Hyperliquid,  NEAR,  and Sui.  An agent custody system that only works on one chain is not an agent custody system.  Agents operate across chains.  Their custody must follow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Born shielded
&lt;/h2&gt;

&lt;p&gt;We built this.  Born shielded,  traded shielded,  settled shielded.&lt;/p&gt;

&lt;p&gt;The key never existed whole.  The policy lives on Sui.  The attestation lives on Zcash.  The verification lives on seven chains.  FROST threshold signing for multi-party attestation.  EIP-152 verification on Ethereum mainnet.  54 npm exports.  2 Rust crates.  Mainnet proven.&lt;/p&gt;

&lt;p&gt;The agent custody problem is not a feature request.  It is a category.  The products that solve it will handle the money that agents move.  The products that do not will be the ones that get drained.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @frontiercompute/zcash-ika
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;zk-nd3r builds agent custody infrastructure at frontiercompute.cash  (&lt;a href="https://github.com/Frontier-Compute" rel="noopener noreferrer"&gt;https://github.com/Frontier-Compute&lt;/a&gt;).&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>blockchain</category>
      <category>security</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Verifying Zcash Proofs on Ethereum with EIP-152</title>
      <dc:creator>zk_nd3r</dc:creator>
      <pubDate>Tue, 07 Apr 2026 21:39:46 +0000</pubDate>
      <link>https://dev.to/zknd3r/verifying-zcash-proofs-on-ethereum-with-eip-152-4h80</link>
      <guid>https://dev.to/zknd3r/verifying-zcash-proofs-on-ethereum-with-eip-152-4h80</guid>
      <description>&lt;p&gt;Ethereum has a precompile that almost nobody knows about.  It lives at address &lt;code&gt;0x09&lt;/code&gt;,  it computes the BLAKE2b compression function,  and it was put there specifically so you can verify Zcash on Ethereum.  This is the story of how we use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What EIP-152 Is
&lt;/h2&gt;

&lt;p&gt;EIP-152 landed in the Istanbul hard fork (December 2019).  It exposes the BLAKE2b &lt;code&gt;F&lt;/code&gt; compression function as a precompiled contract at address &lt;code&gt;0x09&lt;/code&gt;.  Cost: 1 gas per round.  A standard BLAKE2b call runs 12 rounds,  so 12 gas total.&lt;/p&gt;

&lt;p&gt;BLAKE2b is the hash function underpinning Zcash's Sapling and NU5 Merkle trees.  Without this precompile,  computing BLAKE2b in Solidity costs around 200,000 gas.  With it: 712 gas for a full hash.  That is a 280x reduction.&lt;/p&gt;

&lt;p&gt;The EIP was proposed by Tjaden Hess and others from the Ethereum Foundation and was motivated by one thing: enabling Zcash light client verification on Ethereum without absurd gas costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why It Matters
&lt;/h2&gt;

&lt;p&gt;Cross chain proof verification needs hash functions.  Zcash uses BLAKE2b with personalization strings for domain separation.  Each tree level,  each protocol context uses a different personalization.  For example,  &lt;code&gt;ZcashPedersenHash&lt;/code&gt; for note commitment trees and &lt;code&gt;ZTxIdHeadersHash&lt;/code&gt; for transaction IDs.&lt;/p&gt;

&lt;p&gt;If you cannot compute these hashes cheaply on chain,  you cannot verify Zcash state on Ethereum.  Period.  The precompile makes it possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 213 Byte Input Format
&lt;/h2&gt;

&lt;p&gt;The precompile expects exactly 213 bytes,  packed tight:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;rounds&lt;/strong&gt; (4 bytes): number of rounds,  12 for BLAKE2b&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;h&lt;/strong&gt; (64 bytes): state vector,  8 x uint64 little endian&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;m&lt;/strong&gt; (128 bytes): message block&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;t[0]&lt;/strong&gt; (8 bytes): offset counter low,  uint64 little endian&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;t[1]&lt;/strong&gt; (8 bytes): offset counter high,  uint64 little endian&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;f&lt;/strong&gt; (1 byte): final block flag,  0 or 1&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Total: 213 bytes in,  64 bytes out.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Precompile Call
&lt;/h2&gt;

&lt;p&gt;Here is how ZAP1Verifier.sol calls it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function blake2b(
    uint32 rounds,
    bytes memory h,
    bytes memory m,
    uint64 t0,
    uint64 t1,
    bool isFinal
) internal view returns (bytes memory) {
    bytes memory input = abi.encodePacked(
        bytes4(rounds),
        h,
        m,
        bytes8(t0),
        bytes8(t1),
        isFinal ? bytes1(0x01) : bytes1(0x00)
    );

    (bool ok, bytes memory out) = address(0x09).staticcall(input);
    require(ok, "BLAKE2b precompile failed");
    return out;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;staticcall&lt;/code&gt; to &lt;code&gt;0x09&lt;/code&gt;.  No ABI encoding.  No function selector.  Raw bytes in,  raw bytes out.  The precompile handles the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Personalization Strings
&lt;/h2&gt;

&lt;p&gt;Zcash domain separation works by XOR ing a 16 byte personalization string into the initial state vector (bytes 32 to 47 of &lt;code&gt;h&lt;/code&gt;).  Each protocol context gets its own string.  When verifying a Sapling note commitment Merkle path,  you set the personalization to the appropriate Zcash constant before each compression call.&lt;/p&gt;

&lt;p&gt;This is not optional.  Wrong personalization means wrong hash means failed verification.  The precompile does not enforce personalization;  your contract must set &lt;code&gt;h&lt;/code&gt; correctly before calling &lt;code&gt;0x09&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gas Reality
&lt;/h2&gt;

&lt;p&gt;A Merkle proof for a Zcash Sapling tree is 32 levels deep.  Each level needs one BLAKE2b compression.  With the precompile,  that is roughly 32 x 712 = ~22,800 gas for the hashing.  In pure Solidity: 32 x 200,000 = 6.4M gas.  One would blow past block gas limits.&lt;/p&gt;

&lt;p&gt;The precompile does not just save money.  It makes the verification possible at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Live on Mainnet
&lt;/h2&gt;

&lt;p&gt;ZAP1Verifier is deployed on ETH mainnet at &lt;a href="https://etherscan.io/address/0x12db453A7181E369cc5C64A332e3808e807057C1" rel="noopener noreferrer"&gt;0x12db453A7181E369cc5C64A332e3808e807057C1&lt;/a&gt;.  It verifies Zcash Sapling Merkle proofs using the EIP-152 precompile.  The same contract is live on Arbitrum,  Base,  Hyperliquid,  and Sepolia.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://github.com/Frontier-Compute/zap1-verify-sol" rel="noopener noreferrer"&gt;github.com/Frontier-Compute/zap1-verify-sol&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;The precompile has been on Ethereum since 2019.  Almost nobody uses it.  We do.&lt;/p&gt;

</description>
      <category>zcash</category>
      <category>ethereum</category>
      <category>solidity</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
