ByAUJay
Which Custodial Services Support Dual-Control MPC Plus On-Chain Audits, and How to Verify Attestation Data Programmatically?
Dual-control MPC is becoming essential for anyone serious about crypto security in the institutional space. But it's not just about safety--decision-makers are also looking for on-chain auditability that they (and their users) can check out without having to wait for PDF reports. This guide highlights the custodians that are currently blending dual-control MPC with verifiable on-chain attestations. Plus, we’ll walk you through how to verify those attestations in your code.
Quick definitions you can act on (and skip the fluff)
- Dual-control MPC: This is all about keeping things secure by splitting responsibilities through a policy, often called “four-eyes.” What this means in practice is that you need 2 to 5 independent approvers to follow the rules before you can finish any signing process. Companies like Fireblocks and BitGo offer this as a kind of policy or quorum engine, while Cobo and Qredo provide options for co-managed roles and approvers. You can check out more details here.
- On-chain audits: These are pretty cool--real-time proofs of reserves that get published on a public chain, like Chainlink PoR feeds. This means protocols and users can check things out in a trust-minimized way as things happen. You’ll usually find this in use with wrapped BTC, stablecoins, and tokenized real-world assets. Just a heads-up: a lot of these PoR feeds depend on self-attested addresses or third-party reporters. So, while verifying is technically possible, it also leans heavily on governance and process. For more info, dive into this link: chain.link.
Who actually supports both today?
Here’s a list of providers that (a) utilize dual-control MPC with enterprise policy/quorum controls and (b) have actual on-chain audit integrations you can check out on the mainnet, or they offer the tools and documentation you need to publish audits programmatically.
1) BitGo -- Dual‑control MPC with production on‑chain PoR (WBTC, others)
- Dual-control MPC: BitGo's got your back with MPC/TSS support and policy-based approvals, using a 2-of-3 key setup. This means you can set approval rules tailored to your wallet or enterprise needs. Check it out here.
- On-chain audits are live: BitGo was the trailblazer for on-chain audits, specifically for WBTC, thanks to Chainlink's Proof of Reserve (PoR). You can actually view the reserves through Chainlink feeds that keep an eye on BitGo’s BTC custodian addresses and see how they stack up against the minted supply. Dive into the details here.
- More PoR building blocks: If you're looking to build out your own Proof of Reserve workflows, BitGo's got the documentation and APIs ready to help you list wallets and addresses while publishing balances for auditors or oracles. For exchanges, funds, or issuers using BitGo, you can set up verifiable PoR pipelines through their REST endpoints, along with a Merkleized snapshot of your liabilities. Learn more here.
- Concrete feeds you can tap into:
- You can easily browse WBTC/WBTC.e PoR feeds on Chainlink’s data site (check out the Avalanche WBTC.e PoR; make sure to note the “self-attested address” disclosure). Have a look here.
- There are feeds for other BitGo-custodied wrapped BTC variants as well (like bgbtc-por). Don't forget to check the “Data Source” and “Reporter” details. Find it here.
Bottom line: If you're looking for a smooth dual-control MPC setup along with an already functioning PoR that includes public feeds and vendor-updated docs, BitGo is your best bet right now. Check it out here: (developers.bitgo.com)
2) Coinbase (for cbBTC PoR) -- On‑chain audits present, but custody isn’t MPC
- Coinbase has rolled out a Chainlink Proof of Reserve (PoR) feed for cbBTC on Base/Ethereum, which boosts transparency around on-chain reserves. Even though Coinbase's institutional custody setup relies on hardware security modules (HSMs) and segregation controls, it doesn’t fully satisfy the “dual-control MPC” requirement for every asset since it doesn't use multi-party computation (MPC). So, think of Coinbase as a great example of a solid PoR implementation rather than an MPC provider. You can check out more details here: (data.chain.link)
3) Fireblocks -- Dual‑control MPC with SGX‑hardened policy engine; PoR requires integration
- Dual‑control MPC: Fireblocks uses Multi-Party Computation (MPC) across SGX enclaves to keep things secure. It follows a “four-eyes” principle for workflows, thanks to an approval quorum and a nifty policy engine. This means there’s an admin quorum for configuration, specific approval groups for each domain, and detailed rules for transactions. Check it out here.
- On‑chain audits: Now, Fireblocks doesn’t directly publish Proof of Reserve (PoR) feeds. What clients usually do is hook up Chainlink PoR or bring in an auditor to handle those on-chain attestations. You can either send addresses via API or let an auditor or PoR oracle take care of reading them. Learn more here.
When you’re using Fireblocks as your signing stack for an issued or wrapped asset, you can easily set up your mint logic to pull data from a Chainlink Proof of Reserve (PoR) feed. This lets you programmatically control the issuance process (check out the guide and code below). For more details, take a look at this post: (blog.chain.link).
4) Cobo -- Co‑managed dual‑control MPC; bring‑your‑own PoR
- Dual-control MPC: With Cobo's co-managed custody, they split the MPC key shares for you (the default is 2-out-of-3), giving specific roles like admin, spender, and approver. Plus, they support individual wallet risk policies and have TEE-backed secure authentication through Cobo Guard. Check it out here: (docs.cobo.com).
- On-chain audits: There isn’t a default Proof of Reserves (PoR) feed, so you’ll need to either integrate Chainlink PoR or team up with an auditor to get those reserves published on-chain. More info can be found at (chain.link).
5) Qredo -- Decentralized MPC with on‑chain governance… but PoR for external assets is not native
- Dual-control MPC: Qredo has this cool distributed MPC setup that makes sure approvals happen through on-chain governance, all neatly logged on the Qredo Network. They've got roles, whitelists, and multi-level policies that are top-notch. (qredo.com)
- On-chain audits: The governance happens on-chain (thanks to the Qredo chain), but when it comes to reserve attestations for assets on L1/L2s, they do need an external oracle or auditor to make it happen. (chain.link)
Bottom‑line mapping (January 7, 2026)
- Dual-control MPC + production on-chain PoR for at least one widely used asset: BitGo (think WBTC). Check it out here.
- Dual-control + publishable PoR with some extra effort: Fireblocks, Cobo, Qredo (you can integrate Chainlink PoR or use an auditor API). More details can be found here.
- Strong on-chain PoR without MPC custody: Coinbase for cbBTC. Dive into the specifics here.
Just a heads up about the PoR caveat: Chainlink's PoR is kind of the go-to standard for on-chain reserves. That said, keep in mind that the suppliers and reporters can differ (we're talking self-attested addresses versus third-party reporters). It's super important to check out the feed metadata and make sure your trust model is solid based on that. (chain.link)
How to verify on‑chain attestation data programmatically
Here are some tried-and-true patterns that you can easily integrate into your stacks right now.
A) Solidity: Gate mint/redemption with Chainlink PoR
Check out Chainlink’s Aggregator-style interface to match the reported reserves of a PoR feed against your token’s totalSupply before you start any mints or redemptions. Here's an example featuring cbBTC on Base or WBTC.e on Avalanche; just swap out the feed address according to the chain you’re targeting.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
contract MintWithPoR is ERC20Burnable {
AggregatorV3Interface public porFeed; // Chainlink PoR feed
uint8 public immutable porDecimals;
constructor(address _porFeed) ERC20("MyWrappedBTC", "mwBTC") {
porFeed = AggregatorV3Interface(_porFeed);
porDecimals = porFeed.decimals();
}
function reserves() public view returns (uint256) {
(, int256 answer,,,) = porFeed.latestRoundData();
require(answer > 0, "invalid PoR");
// PoR answer reports satoshis or BTC depending on feed; normalize as needed
return uint256(answer);
}
function mint(address to, uint256 amount) external {
// Example policy: require reserves >= new total supply post-mint
uint256 newSupply = totalSupply() + amount;
require(reserves() >= newSupply, "PoR undercollateralized");
_mint(to, amount);
}
}
- Check out the feed addresses on data.chain.link and make sure to confirm the type of reporter/source. For example, cbBTC on Base shows cbbtc‑por, while WBTC.e on Avalanche gives you wbtce‑por along with a note about a “self‑attested address.” Keep that in mind for your governance decisions.
- Dive into Chainlink’s PoR docs for some handy info on “circuit breaker” mint controls and different architectural patterns. You can find that over at blog.chain.link.
B) Node.js/ethers: Read PoR, compare to token supply off‑chain
You can use this if you're running a minting backend, managing an exchange risk engine, or operating a monitoring bot.
import { ethers } from "ethers";
import erc20 from "@openzeppelin/contracts/build/contracts/ERC20.json" assert { type: "json" };
const RPC = process.env.RPC_URL; // e.g., Base mainnet
const provider = new ethers.JsonRpcProvider(RPC);
// Example: cbBTC PoR feed on Base
const POR_FEED = "0x0F8E...2519"; // cbbtc-por (verify latest address on data.chain.link)
const TOKEN = "0xYourToken"; // token you want to check (e.g., cbBTC or your wrapped asset)
// Chainlink Aggregator ABI (subset)
const AGG_ABI = [
"function latestRoundData() view returns (uint80,int256,uint256,uint256,uint80)",
"function decimals() view returns (uint8)"
];
async function main() {
const feed = new ethers.Contract(POR_FEED, AGG_ABI, provider);
const token = new ethers.Contract(TOKEN, erc20.abi, provider);
const [ , ans ] = await feed.latestRoundData();
if (ans <= 0n) throw new Error("Invalid PoR reading");
const por = ans; // normalize to your token's units
const supply = await token.totalSupply();
if (por < supply) {
console.error("Under-collateralized! POR < totalSupply");
process.exit(2);
} else {
console.log("Collateralization OK");
}
}
main().catch(console.error);
- Swap out POR_FEED with your desired PoR feed (like, say, cbBTC on Base). Don’t forget to check the “Reporter” and “Data Source” sections to see if it’s self‑attested or coming from third‑party reporters. (data.chain.link)
C) BitGo: Build a PoR snapshot from custody addresses (REST) and publish it on‑chain
If you’re using BitGo for custody but haven’t set up a Chainlink feed just yet, here’s what you can do:
- List your addresses and wallets using the BitGo REST API (it’s a good idea to stick with custody wallets for your reserves).
- Share the balances and addresses with an auditor or use them for your own Merkle tree.
- Set up an oracle, like Chainlink, to pull this data and refresh an on-chain feed. (developers.bitgo.com)
BitGo's Guide to Listing Wallets and Extracting Data for PoR
To help you with your Proof of Reserves (PoR) process, here's a handy rundown from BitGo on how to list your wallets and pull out the necessary data.
Step 1: Listing Wallets
First things first, you’ll want to get a list of all the wallets associated with your account. You can do this using the BitGo API. Just make a simple GET request like this:
curl -X GET "https://api.bitgo.com/api/v2/wallets" \
-H "Authorization: Bearer {access_token}"
Replace {access_token} with your actual access token. This will give you a JSON response containing all your wallets.
Step 2: Extracting Wallet Data
Once you have the list of wallets, the next step is digging deeper into each one to gather specific data for your PoR. You’ll want to retrieve details such as balances, transaction history, and other relevant info. Here's how to do that:
For each wallet, make another GET request like the following:
curl -X GET "https://api.bitgo.com/api/v2/wallet/{wallet_id}" \
-H "Authorization: Bearer {access_token}"
Just replace {wallet_id} with the ID of the wallet you want to check out. This request will return detailed data about the wallet.
Additional Tips
- Make sure to handle the API responses patiently. Sometimes, you might hit rate limits or need to deal with errors.
- Keep your access token secure; treat it like a password.
- If you’re working with multiple wallets, consider automating the process to save time.
By following these steps, you’ll be well on your way to gathering the required data for your PoR efficiently. Happy coding!
# Example outline (use BitGo SDK or REST)
# 1) List wallets and select reserve set
curl -H "Authorization: Bearer $BITGO_TOKEN" \
"https://app.bitgo.com/api/v2/wallets"
# 2) For each wallet ID, list addresses and balances
curl -H "Authorization: Bearer $BITGO_TOKEN" \
"https://app.bitgo.com/api/v2/btc/wallet/<WALLET_ID>/addresses?limit=500"
From this point, you can:
- Export balances into a signed JSON manifest,
- Create a Merkle root based on customer balances (liabilities), and
- Publish both the asset manifest and the liabilities root either on-chain or through an auditor’s API that a Chainlink External Adapter can access. (developers.bitgo.com)
D) Verifying Merkle proofs for exchange/fund PoR (client‑side)
If your auditor creates a Merkle leaf for every user, your clients will be able to check inclusion on their own:
import crypto from "crypto";
function hash(x) { return crypto.createHash("sha256").update(x).digest(); }
/**
* Verify a Merkle proof for a leaf (balance commitment).
* @param {Buffer} leaf
* @param {Buffer[]} proof - list of sibling hashes from leaf->root
* @param {Buffer} root
* @returns {boolean}
*/
function verifyMerkle(leaf, proof, root) {
return proof.reduce((acc, sib) => {
const [a,b] = Buffer.compare(acc, sib) < 0 ? [acc, sib] : [sib, acc];
return hash(Buffer.concat([a, b]));
}, hash(leaf)).equals(root);
}
If you're using Chainlink PoR for the assets side, it's a good idea to provide users with a Merkle proof for the liabilities side too. This gives a stronger assurance of solvency (you want to make sure assets are greater than or equal to liabilities). Make sure to sync your auditor’s API with how often the feed gets updated and include a URL for the signed report right in the on-chain feed metadata. Check it out at (chain.link).
E) If a vendor exposes enclave attestation (SGX/DCAP), verify quotes server‑side
Some MPC providers lock down their policies or approvals within Trusted Execution Environments (TEEs) like Intel SGX. If your vendor offers remote attestation quotes, you can check them against Intel's DCAP flow to tie policy execution to verified measurements (MRENCLAVE/MRSIGNER). Here’s a quick rundown of the high-level steps (DCAP):
- Gather the ECDSA quote from the enclave.
- Forward it to an attestation verification service (either a local DCAP verifier or a cloud PCCS) to check the TCB status and measurements.
- Cross-check the measurements against an allowlist; if the attestation fails or the firmware is outdated, reject the requests. (intel.com)
Just a heads up: Fireblocks mentions that it uses SGX to beef up its policy engine and MPC flow. However, keep in mind that most providers don’t usually disclose raw quotes to clients. If your threat model hinges on needing runtime attestation checks, make sure to negotiate this in your contract. (fireblocks.com)
- Before you dive into accepting WBTC as collateral in a lending market, make sure to double-check the WBTC reserves. You can do this by checking the WBTC PoR feed and comparing it to WBTC.totalSupply(). If you notice any significant discrepancies, either cap the loan-to-value (LTV) or put a hold on new borrows. (businesswire.com)
- If you're looking to launch a wrapped BTC product with Fireblocks as the signer, first get those reserve addresses out there using BitGo-like APIs or custodian exports. Then, set up a Chainlink External Adapter to read your signed reserve manifest. Finally, make sure minting is gated through the PoR feed in your ERC-20 contract. (chain.link)
- To keep an eye on cbBTC health on Base, you can set up a little bot that checks cbbtc-por every N blocks. If reserves fall below the circulating supply, it should notify your ops team, and your protocol will automatically switch “pause” on the relevant markets. (data.chain.link)
Best emerging practices (what we see working in 2025-2026)
- When it comes to PoR feed metadata, think of it as part of your trust boundary. Don’t just glance at “answer()”--take a moment to see if the feed is self-attested, uses a wallet address manager, or has a third-party reporter (and make sure to note who that is). It’s also a good idea to set up alerts for any changes in reporters. (data.chain.link)
- Merge asset-side Proof of Reserve (PoR) with liabilities-side Merkle proofs. Chainlink feeds typically provide data on reserves; when you combine that with a user-verifiable Merkle inclusion proof for liabilities, you create a way stronger solvency claim than just using PoR on its own. (chain.link)
3) Implement Mint Circuit Breakers in Code
Make sure to set up mint circuit breakers directly in your code. You can use Chainlink Automation or your own keeper system to halt minting and burning whenever the Proof of Reserves (PoR) dips below the total supply. This way, you won't have to wait for governance to notice the issue. It really helps to tighten the gap between under-collateralization and the actions taken to remedy it. For more details, check out this Chainlink blog post.
- Don't put all your eggs in the PoR basket; make sure to audit the process. There’s been chatter in the press and from researchers that PoR is only as reliable as the inputs and governance behind it (think about the difference between self-attestation and data sourced from auditors). Make it a point to have documented data paths and sign-offs, and don’t forget to version your “attestation policy” right along with your code. (coindesk.com)
- Make sure to lock change management behind dual control as well. Approval quorums shouldn't just be for transfers; they should also safeguard things like policy edits, whitelists, adding new signers, and changes to feed addresses. Both Fireblocks and BitGo provide options for admin quorum and policy-change approvals--so make sure to take advantage of those! (developers.fireblocks.com)
- If you're relying on TEEs, make sure to request attestation evidence. It’s a good idea to either require or test out periodic enclave quote verification with DCAP. If your vendor can’t provide quotes, then ask for SOC2 mapping to their enclave controls and ensure there’s at least one annual third-party review of the enclave configurations. (intel.com)
Brief vendor‑by‑vendor implementation notes
- BitGo
- Implement BitGo's policies to ensure you have a “four‑eyes” approval for each asset or amount. Plus, make use of REST to export your reserve addresses on a daily basis. If you're issuing a wrapped asset, don’t forget to link up a Chainlink PoR feed, allowing reporters to access your signed JSON manifest that includes your addresses and balances. (bitgo.com)
- Fireblocks
- Start by setting up your Admin Quorum and Approval Groups, then go ahead and lay out your transaction policies based on asset, amount, and destination. If you're diving into Proof of Reserves (PoR), make sure to publish your reserve addresses and team up with an auditor or use a Chainlink External Adapter. You should anticipate SGX-hardened policy execution, but don't hesitate to negotiate if you need some raw quotes. (developers.fireblocks.com)
- Cobo
- Use co-managed MPC (with a default setup of 2-of-3) that includes approver roles, plus Cobo Guard for safe authentication. For Proof of Reserve, just export your addresses through the API and send them over to an oracle. Check out the details here: (docs.cobo.com)
- Qredo
- Boasting a solid decentralized MPC and on-chain governance for all your approval needs. If you're looking for transparency when it comes to externally issued tokens, you can’t go wrong by pairing Qredo with Chainlink PoR and a liabilities Merkle audit. Check it out here: (qredo.com)
What to ask vendors before you commit
- MPC specifics: When it comes to multi-party computation, you’ll want to know things like the threshold (for example, 2‑of‑3 or 3‑of‑5), who’s got what shares, and where those trusted execution environments (TEEs) or hardware security modules (HSMs) are located. BitGo and Cobo lay out their TSS/key-share models pretty well in their documentation. Check it out here.
- Policy coverage: You should be clear on which changes need an admin quorum, what requires mobile approvals, API-only approvals, expirations, and who’s got the power to veto. Fireblocks does a great job of detailing these controls in their docs. Dive into the details here.
- PoR posture: Do they have a live Proof of Reserve (PoR) feed? If not, you’ll want to find out who’s supplying data to Chainlink (is it self-attestation or an auditor?), what the heartbeat/deviation thresholds are, and how they’ll prove liabilities. Make sure to review the feed pages and any disclaimers, plus request a runbook for incident handling. Take a look at all that here.
- TEE attestation: If they’re claiming enclave protection, can they provide DCAP/IAS artifacts or at least auditor attestations that link enclave measurements to builds? You can explore more about this on Intel’s site here.
The 30‑day rollout plan we recommend
- Week 1: Start by choosing your custodian and the PoR path. If you're in a hurry and need “MPC + PoR now,” go with BitGo for products similar to WBTC. If you can take your time, consider options like Fireblocks, Cobo, or Qredo for MPC, and plan on integrating Chainlink for PoR later. (businesswire.com)
- Week 2: Set up your dual-control policies and admin quorums. Also, don’t forget to enable address whitelists and size-based approvals to keep things secure. (developers.fireblocks.com)
- Week 3: Time to establish your PoR. You can do this by either using an existing feed like cbBTC or by making your reserves available to an auditor or a feed. Plus, write a straightforward mint gate contract (see the code above). (data.chain.link)
- Week 4: Add Merkle proofs for liabilities to your user portals. It’s also a smart move to conduct failover drills for any reporter outages and under-collateralization circuit breakers. (chain.link)
Key links and references (for your RFP and engineers)
- Check out BitGo's MPC/TSS, policies, and PoR developer docs right here: (developers.bitgo.com)
- Fireblocks has some pretty neat stuff on approval quorums, a policy engine, and their SGX-backed setup. Dive into it here: (developers.fireblocks.com)
- If you’re interested in Cobo’s co-managed MPC roles and their key-share model, this guide is for you: (docs.cobo.com)
- Want an overview of Chainlink's PoR? Check out their developer guidance: (chain.link)
- Here are some live PoR examples to keep an eye on: WBTC.e (Avalanche), cbBTC (Base), and the FBTC feed (Ethereum). Don't forget to look at the “Reporter” and “Data Source” sections! (data.chain.link)
- Be sure to read up on PoR caveats in the press. They can really help you bolster your governance docs! (coindesk.com)
- And for verifying enclave quotes, check out Intel's SGX attestation (DCAP) guide: (intel.com)
Final take
- Looking for a dual-control MPC solution that you can trust, complete with solid public on-chain audits? BitGo is hands down the best option that ticks all the boxes. Check it out here: (businesswire.com)
- If your go-to platforms are Fireblocks, Cobo, or Qredo for operations, think about treating Proof of Reserve (PoR) as a separate module. Implement Chainlink PoR along with a liabilities Merkle proof, and then set up circuit breakers to connect those feeds. Your users--and auditors--are going to appreciate it! Learn more here: (chain.link)
7Block Labs is here to help you design the policy engine, set up a PoR pipeline, and get those Solidity guards and monitoring bots up and running in less than a month.
Like what you're reading? Let's build together.
Get a free 30-minute consultation with our engineering team.
Related Posts
ByAUJay
Building 'Private Social Networks' with Onchain Keys
Creating Private Social Networks with Onchain Keys
ByAUJay
Tokenizing Intellectual Property for AI Models: A Simple Guide
## How to Tokenize “Intellectual Property” for AI Models ### Summary: A lot of AI teams struggle to show what their models have been trained on or what licenses they comply with. With the EU AI Act set to kick in by 2026 and new publisher standards like RSL 1.0 making things more transparent, it's becoming more crucial than ever to get this right.
ByAUJay
Creating 'Meme-Utility' Hybrids on Solana: A Simple Guide
## How to Create “Meme‑Utility” Hybrids on Solana Dive into this handy guide on how to blend Solana’s Token‑2022 extensions, Actions/Blinks, Jito bundles, and ZK compression. We’ll show you how to launch a meme coin that’s not just fun but also packs a punch with real utility, slashes distribution costs, and gets you a solid go-to-market strategy.

