

Dave
4.5K posts

@I_amDavve
Research Analyst || Driving growth through insights & strategy




Week 9 Update for Beni This week had a lot of thought processes about the people who would actually use Beni. Right now, the audience for agentic trading is still relatively small. It's an emerging narrative, and that naturally makes onboarding kind of a constrain. The SDK is not available as an npm package. Right now, anyone who wants to use it has to interact with the GitHub repository. So I thought, what if deployment happens from a dashboard. Someone should be able to connect a wallet, choose their spending limits without needing a terminal. The other thing I've been thinking about is the MCP server. I built it so AI agents can interact with Beni directly. I think a better approach is meeting users where they already are. Instead of expecting people to build around Beni, I want Beni to fit naturally into the tools and frameworks they're already using. I plan to finish shipping these improvements this new week, then testing and feedback will be made open at scale. #gimbalabs #pieceofpie #hackathon @gimbalabs


I've been spending the last couple of weeks writing tutorials for the Midnight Eclipse Content Program.
@MidnightNtwrk is a privacy focused blockchain that lets you write zero knowledge smart contracts in a language called Compact. The bounty program pays developers to write documentation for it, including tutorials, guides, and security breakdowns.
This post is a summary of four submissions that have gone through the review process. Most of them had feedback applied and are. I'm writing this partly to document what I built, and partly because the individual articles are dense and technical. This is the friendlier version of what each one is actually about.
If you're building on Midnight or just curious about ZK development, hopefully at least one of these is useful to you.
.
1. Why ownPublicKey() can't be trusted for access control
When you write a smart contract on most blockchains, the standard way to protect an admin function is to check who's calling it, something like "only the owner can call this." On Ethereum, you check msg.sender. On Midnight, the function that looks equivalent is ownPublicKey().
But ownPublicKey() doesn't work the way you'd expect in a zero knowledge context.
Here's why. In a ZK proof, the prover is the one who generates the proof on their own machine before submitting it to the chain. The prover chooses what values to supply as private inputs. ownPublicKey() compiles down to an unconstrained private_input in the ZK circuit, meaning the prover can put anything there. They can read the owner's public key from the on chain ledger, which is public state, plug it into their local proof as the value for ownPublicKey(), generate a valid proof, and submit it. The chain accepts the proof because the math checks out. There's no on chain identity verification happening.
This affects every contract that gates a privileged function behind an ownPublicKey() check. It also affects OpenZeppelin's Ownable.compact, which uses the same pattern.
The correct approach is to store a commitment on chain rather than a public key. The admin knows a secret. When they deploy the contract, they store persistentHash(["admin:v1", secret]) on the ledger. When they want to call an admin function, they supply the secret as a private witness and the circuit checks that it hashes to the stored commitment. Nobody can fake this without knowing the secret because the secret itself never goes on chain.
I built a proof of concept exploit for this against Midnight's own example bulletin board contract and wrote a full breakdown, covering the four steps of the attack, the correct commitment based pattern, and a note on how to migrate existing contracts.
The reviewer asked me to add a "valid uses of ownPublicKey()" section, since there are patterns where it's fine to use, and to pin the compiler version. Both done.
Read the full article: dev.to/iamharrie/comp…
.
2. Why your Midnight deploy fails before it even starts
I was calling balanceUnboundTransaction, the SDK function that figures out the fees and token movements for a transaction, and it was returning wrong results, and incorrect balances. The transaction would then fail downstream.
The root cause was that the wallet hadn't finished syncing yet.
The Midnight wallet is not just one wallet. It's three. There's a shielded wallet for private tokens using ZK proofs, an unshielded wallet for public token balances, and a DUST wallet specifically for paying network fees. Each one syncs independently from the chain, and each one has a different shape for its sync state in the SDK.
The state object for the shielded wallet is at state.shielded.state.progress. For the unshielded wallet, it's at state.unshielded.progress. For the DUST wallet, it's at state.dust.state.progress. They all nest differently. If you call .isStrictlyComplete() on the wrong path, you get undefined back instead of false or true. Your sync check passes and you proceed with an incomplete wallet.
There's also a known issue with the DUST wallet specifically. On idle chains, like a local devnet with no recent activity, isStrictlyComplete() hangs and never resolves. The workaround is to wrap the sync check in a timeout and catch the timeout as a best effort completion.
The safe pattern is RxJS based: wallet.state().pipe(filter(s => s.isSynced), take(1), timeout(60_000)). I spent time pulling the actual syncWallet function out of Midnight's CLI tutorial code and walking through what each RxJS operator is doing because the "why throttleTime?" and "why each: instead of a total timeout?" questions kept coming up when I was reading it.
The reviewer asked me to remove some duplicated text, which was a paste error on my end, soften a claim I'd made about the DUST bug being "documented" when it was actually just something I observed on a local devnet, and run through the style guide. All done.
Read the full article: dev.to/iamharrie/unde…
.
3. Working with Maps and Merkle Trees in Compact
Compact has two main data structures for managing on chain state: Map and MerkleTree. They serve different purposes and the choice between them matters. Using the wrong one produces contracts that are either wasteful or broken.
Maps are the simpler one. A Map

Challenge 8 is LIVE 🔴 📣 Calling all writers. We’re opening a long-form writing challenge focused on real Cardano products. Write. Publish. Get featured. 💰Earn up to 3,600 ADA in rewards (300 ADA per winning article). 🔒This challenge is exclusive to Frenchie Founders by AdaLink NFT holders with access to our members channels. Submit your topic in Discord today. Good luck 🚀









Week 3 Update for Beni This week was a big step forward. The SDK is no longer running on placeholders. It now connects directly to real compiled Aiken validators, which means createAgentWallet() can actually deploy wallets onchain. One of the hardest parts was handling the thread token system. Every agent wallet now mints its own unique NFT during creation using a one time minting policy tied to a seed UTxO. The validator checks for that NFT on every transaction, making it impossible to fake wallet state with a forged datum. Everything is enforced directly by the ledger, not just the app layer. Here’s what is fully working now: → createAgentWallet now handles seeding, minting, and locking in a single transaction → agentSpend reads live onchain state, checks guardrails, and updates the rolling 24 hour spending window → ownerAction and freezeWallet are live with owner co sign protection enforced by the validator → CI is fully green with aiken check, aiken build, TypeScript compilation, and 14 passing unit tests. Next up is the Approvals UI. Right now, if an agent tries to spend above the allowed per transaction limit, it simply fails. The next version will instead queue that request and let the owner approve it directly from their Cardano wallet. That approval flow will eventually be the real UX advantage for Beni. #Gimbalabs #Pieceofpie #Hackathon @gimbalabs

