Skip to content

Testnet DX feedback from building a lending dApp on Arc: USYC stub, oracle gap, gasless/EVM docs #107

Open

Description

@osr21

Sharing constructive feedback after building ArcLend, an Aave-style lending/borrowing protocol on Arc Testnet (chain 5042002). Four things slowed us down that small docs/infra fixes would solve for other DeFi builders.

1. The listed USYC token is a non-functional stub on testnet

eth_getCode(0xe9185F0c5F296Ed1797AaE4238D26CCaBEadb86C) returns only 183 bytes and exposes just owner() — no ERC-20 transfer or allowlist logic. By contrast USDC (0x3600000000000000000000000000000000000000) and EURC (0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a) are full 1798-byte precompiles. Since USYC is listed as a supported asset, any protocol wiring a USYC market hits a dead end.

Suggestion: deploy a functional USYC test contract (even a mock with the allowlist interface), or mark it "not yet available" in the contract-addresses docs with an ETA.

2. No documented price-oracle path for testnet

Chainlink and Pyth don't list Arc testnet feeds, yet any lending/perps/RWA app needs prices for non-stable assets (e.g. cirBTC, which the arc-defi-lending-and-borrowing sample uses as collateral). Today builders must run a private mock oracle, which makes liquidation/health-factor testing unrealistic.

Suggestion: publish a canonical testnet feed (a Circle-run AggregatorV3-compatible aggregator, or a documented partner like RedStone/Stork/Chronicle).

3. Document EIP-3009 support + the EIP-712 domain for the native stablecoins

Because USDC is the gas token, gasless "sign-then-relay" UX is a flagship Arc use case. Building a gasless supply router, we needed the native USDC/EURC EIP-3009 (receiveWithAuthorization) status and the exact EIP-712 domain (name/version/verifyingContract) to produce valid off-chain signatures. We resolved it via ERC-5267 eip712Domain() with a fallback, but it was guesswork.

Suggestion: document EIP-3009/EIP-2612 support and the canonical domain for the precompiled stablecoins.

4. Document the canonical EVM hardfork / evmVersion

Contracts importing OpenZeppelin 5.1+ (EIP712/ECDSA) silently fail to compile under solc's default shanghai because they emit mcopy; setting evmVersion: "cancun" fixes it, and Cancun opcodes do execute on Arc. There's an open arc-node issue referencing Prague support, so a one-line note in the Hardhat/Foundry setup docs stating the current canonical evmVersion would save real debugging time.


Happy to provide repro details or open PRs against the docs. Thanks for building Arc — the USDC-as-gas model is genuinely great to build on.

Activity

  1. osr21 commented on Jun 19, 2026

    @osr21
    Author

    Adding: exact EIP-712 domain values for USDC and EURC + memo precompile for labeling

    Great writeup. Adding two more concrete findings from Arc Swap that are directly relevant to point 3 (EIP-2612/EIP-3009 domain documentation):

    Exact EIP-712 domain for USDC and EURC on Arc Testnet

    Both USDC and EURC use version "2", which is not obvious and not documented. Using version "1" produces an invalid signature that reverts without a helpful error:

    // USDC — Arc Testnet
    const usdcDomain = {
      name: "USDC",
      version: "2",           // ← must be "2", not "1"
      chainId: 5042002n,
      verifyingContract: "0x3600000000000000000000000000000000000000",
    };
    
    // EURC — Arc Testnet
    const eurcDomain = {
      name: "EURC",
      version: "2",           // ← same — must be "2"
      chainId: 5042002n,
      verifyingContract: "0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a",
    };

    You can verify programmatically via eip712Domain() (ERC-5267) on either contract — but having it documented saves builders from having to discover it empirically.

    Memo precompile for labeling on-chain operations (Arc v0.7.2)

    Relevant to any DApp wanting audit trails for lending actions (supply, borrow, repay, liquidate): Arc v0.7.2 activated a memo precompile at 0x5294E9927c3306DcBaDb03fe70b92e01cCede505 (confirmed live on testnet as of 2026-06-18). It accepts arbitrary UTF-8 bytes and writes them as an on-chain label attached to the transaction:

    // Fire-and-forget after every lending action — non-fatal if it fails
    walletClient.writeContract({
      address: "0x5294E9927c3306DcBaDb03fe70b92e01cCede505",
      abi: [{ name: "memo", type: "function", stateMutability: "nonpayable",
              inputs: [{ name: "data", type: "bytes" }], outputs: [] }],
      functionName: "memo",
      args: [toHex(Buffer.from(`ArcLend: supply ${amount} USDC @ block ${blockNumber}`, "utf8"))],
      gas: 100_000n,
    }).catch(() => {}); // non-fatal

    This makes liquidation events and supply/borrow actions human-readable on Arcscan without needing a custom indexer.

  2. osr21 commented on Jul 2, 2026

    @osr21
    Author

    Oracle gap update — Stork live on Arc Testnet; adapter errors + evmVersion follow-up

    Circling back on items #2 and #4 from the original issue with findings from deploying the oracle infrastructure for ArcLend.


    Item #2: Stork is now live on Arc Testnet (but feed data not yet published)

    As of July 1 2026 (Chainlink Scale announcement), five oracle providers are now listed in the Arc docs: Chainlink, Chronicle, Pyth, RedStone, and Stork.

    Stork Verifier on Arc Testnet: 0xacC0a0cF13571d30B4b8637996F5D6D774d4fd62
    Interface: getTemporalNumericValueUnsafeV1(bytes32 feedId) → {uint64 timestampNs, int192 quantizedValue} (1e18-scaled USD price)

    We deployed a full adapter stack for ArcLend:

    Contract Address Purpose
    StorkChainlinkAdapter (USDC/USD) 0x704CBB1d1ff44Ec569b402ee08D2e14a7bC43bc2 Wraps Stork → AggregatorV3Interface
    StorkChainlinkAdapter (EURC/USD) 0xD1ba670BBA1E445eA8633489935294d939291957 Same for EURC
    ChainlinkAdapter 0x30c4C225F3441151d85F0410EdF7848F3ec6A3A1 Aggregates feeds → IPriceOracle for the pool
    LiquidationUpkeep 0x046c32ee269550D4d0B7f649D2790124Ff99F1aa Chainlink ACE-compatible liquidation automation

    Errors found during integration:

    1. All Stork feed IDs revert — getTemporalNumericValueUnsafeV1 returns execution reverted for every encoding tried: keccak256("USDCUSD"), keccak256("USDC/USD"), ASCII right-padded bytes32, keccak256("BTCUSD"), keccak256("ETHUSD"). The verifier IS deployed (342-byte proxy) but publishers have not yet pushed data to Arc Testnet — timing issue, not a feed ID encoding bug.

    2. Feed ID encoding is keccak256 — bytes32 id = keccak256(abi.encodePacked("USDCUSD")), not ASCII right-padded. Worth documenting officially alongside the verifier address.

    3. Hot-swap works without protocol redeploy — LendingPool.setOracle(adapter) activates new price feeds atomically. No migration needed when Stork goes live.

    Current state: Protocol runs on MockPriceOracle with admin-set prices. The adapters are deployed and wired — a single setOracle() call activates live prices once publishers push data.


    Item #4: evmVersion: "cancun" confirmed (note on PR #151)

    PR #151 documents --evm-version paris for Foundry. That's correct for contracts without OZ 5.1+. However OZ ≥ 5.1 requires cancun — EIP712 and ECDSA emit the mcopy opcode (EIP-5656), absent in shanghai/paris. Solc 0.8.24 defaults to shanghai and fails silently.

    Any contract inheriting EIP712, ECDSA, or ERC4626 from OZ ≥ 5.1 (gasless routers, permit vaults, etc.):

    • Hardhat: evmVersion: "cancun" in hardhat.config.js
    • Foundry: --evm-version cancun in forge commands

    Arc Testnet executes Cancun opcodes correctly.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions