Skip to content

docs: add ERC-4626 vault example with Multi-Agent Orchestrator integration#183

Open
consumeobeydie wants to merge 3 commits into
circlefin:mainfrom
consumeobeydie:docs/erc4626-vault-example
Open

docs: add ERC-4626 vault example with Multi-Agent Orchestrator integration#183
consumeobeydie wants to merge 3 commits into
circlefin:mainfrom
consumeobeydie:docs/erc4626-vault-example

Conversation

@consumeobeydie

Copy link
Copy Markdown

Summary

This PR adds an example showing how to build an ERC-4626 tokenized vault for native USDC on Arc Testnet, including an agent-yield deposit path that integrates with the Multi-Agent Orchestrator example from this series.

What's included

  • ArcUSDCVault.sol built on OpenZeppelin v5.6.1's ERC4626 base contract
  • Owner-injected yield mechanism (depositYield) that increases share value for all holders
  • Agent-tagged deposit function (depositForAgent) for orchestrator integration
  • 13 passing tests covering deposit, withdraw, redeem, yield distribution, and access control

Live Verification

Deployed and tested with a real deposit on Arc Testnet:

Full implementation

https://github.com/consumeobeydie/arc-vault

Related PRs

This is the 10th example added to this series.

consumeobeydie and others added 3 commits June 20, 2026 18:28
@osr21

osr21 commented Jun 21, 2026

Copy link
Copy Markdown

Great additions — both examples cover territory that isn't well-documented yet for Arc Testnet. A few things worth flagging:


🔴 Critical — evmVersion: paris required for Arc Testnet deployment

OpenZeppelin v5.6.1 requires Solidity ^0.8.20. Solidity 0.8.20+ compiles with Shanghai EVM by default, which introduces the PUSH0 opcode (EIP-3855). Arc Testnet does not support PUSH0 — it runs at Paris EVM level.

Without explicitly setting evmVersion = "paris" in your foundry config, the compiled bytecode will contain PUSH0 and the deployment will silently fail or revert on Arc. The example should include:

# foundry.toml
[profile.default]
evm_version = "paris"

Or if using a per-contract pragma:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
// compile with: forge build --evm-version paris

The forge tests pass locally because they run against the default EVM (Shanghai/Cancun), not Arc. This only surfaces at deployment time. Can confirm — hit this exact issue deploying ERC-4337 Paymaster and GasRelayer contracts across Arc, Base Sepolia, and Fuji. It's also documented in the Arc EVM differences page but easy to miss when starting from an OZ template.


⚠️ Minor — missing zero-address guard in depositForAgent

deposit(amount, agent) from the OZ ERC4626 base doesn't validate receiver != address(0). Shares minted to the zero address are permanently locked. A simple guard prevents this:

function depositForAgent(address agent, uint256 missionId, uint256 amount)
    external
    returns (uint256 shares)
{
    require(agent != address(0), "ArcUSDCVault: zero address agent");
    shares = deposit(amount, agent);
    emit AgentYieldDeposited(agent, missionId, amount, shares);
}

📝 Share ratio note — worth a line of explanation

The PR body says "5 USDC deposited, 5,000,000 avUSDC shares minted (1:1 first-deposit ratio)" — technically correct since USDC has 6 decimals (5 USDC = 5_000_000 in raw units), but users will see "5,000,000 avUSDC" in their wallet and it's not obvious why. One sentence in the example explaining that share amounts track USDC's 6-decimal representation would save confusion.


✅ Transaction memo documentation is excellent

The selector derivation via bytecode inspection (0xc3b2c4f8) and the cast call --from pitfall note are both the kind of detail that only comes from actually testing it. The correlation ID angle is particularly useful for financial reconciliation — CCTP bridge flows would benefit from memos to track cross-chain transfers end-to-end.

One addition that might help: a note that the Memo contract's callWithMemo preserves msg.sender via the CallFrom precompile, so from the target contract's perspective the call looks like it originated directly from the caller — which means no contract modifications are needed for existing deployments.


For reference: built a CCTP V2 bridge + ERC-4626-style yield vaults on Arc Testnet recently (arc-relay-bridge — deployed vault at 0xFf8f0Fc1Cca51ddE49ee1E7083D36CC99Af01665, Arc domain 26). The paris EVM constraint and the USDC decimals handling are the two things that trip up Arc deployments most consistently in my experience.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants