Summary
A Clarity 4 contract deployed after SIP-033/034 activation (Bitcoin block 923,222) cannot execute contract-call? to a contract deployed before the Clarity 4 epoch. Every call — including plain read-only calls with no as-contract? — fails at the VM level with ContractCallExpectName before any contract logic executes. The VM cannot resolve the target contract reference when the call originates from a Clarity 4 contract.
This is not an authorization error, not an as-contract? issue, and not a parameter mismatch. The target contract is fully functional when called directly from a wallet.
How to Reproduce
Deploy two identical minimal contracts to mainnet — one as Clarity 2, one as Clarity 4:
;; Deploy this exact code twice:
;; Once as Clarity 2 (contract name: test-crossepoch-c2)
;; Once as Clarity 4 (contract name: test-crossepoch-c4)
(define-public (test-read (name (buff 48)))
(ok (contract-call?
'SP2QEZ06AGJ3RKJPBV14SY1V5BBFNAW33D96YPGZF.BNS-V2
get-bns-info
name
0x627463)))
Call test-read on both contracts with any name registered in the .btc namespace (e.g. 0x74657374 for “test”, or any known registered name — the name buffer should contain only the name, not the namespace).
Expected behavior: Both return the BNS info tuple for the name.
Predicted behavior based on mainnet evidence: The Clarity 2 contract succeeds. The Clarity 4 contract fails with ContractCallExpectName at the VM level — no contract logic executes.
The target function (get-bns-info) is a define-read-only on BNS-V2. It takes a name buffer and a namespace buffer and returns a map lookup. There is no authorization check, no write, no asset transfer. The failure is purely in cross-epoch contract reference resolution.
Evidence from Mainnet
Three transactions demonstrate the issue in a real-world scenario where a Clarity 4 manager contract calls BNS-V2 (a pre-Clarity 4 contract):
Transaction 1 — Write call with as-contract?: Clarity 4 contract calls BNS-V2.name-claim-fast via as-contract?. Result: ContractCallExpectName, write count 0. Initial hypothesis was as-contract? compatibility.
Transaction 2 — Read-only call, NO as-contract?: Same Clarity 4 contract calls BNS-V2.get-bns-info via plain contract-call?. Result: ContractCallExpectName. This eliminates as-contract? as the cause — the VM fails on a plain contract-call? to a read-only function with no authorization checks.
Transaction 3 — Direct wallet call to BNS-V2: Same wallet calls BNS-V2.name-claim-fast directly (no intermediary contract). Result: (err u102) — a proper contract-level error (ERR-NOT-AUTHORIZED). BNS-V2 executes correctly, evaluates its logic, and returns a meaningful error. The issue is exclusively in cross-epoch contract-call resolution.
Transaction hashes available on request.
Root Cause Analysis
The Clarity VM appears unable to resolve contract principal references when contract-call? originates from a Clarity 4 contract and targets a contract deployed before the Clarity 4 epoch. The error ContractCallExpectName fires before any contract logic is evaluated — this is a VM-level name resolution failure, not a contract execution error.
Key observations:
-
The target contract (BNS-V2) works correctly when called directly from a wallet (Transaction 3)
-
The failure occurs on plain
contract-call?—as-contract?is not required to trigger it (Transaction 2) -
The failure occurs on read-only functions with no authorization checks — this is not a permissions issue
-
The calling contract compiles and deploys without errors — the contract reference is syntactically valid
Impact
Any Clarity 4 → pre-Clarity 4 interaction is potentially broken
This affects any Clarity 4 contract that needs to call any pre-Clarity 4 contract. The most severe cases are managed namespaces on BNS-V2, because:
-
BNS-V2 requires all managed namespace operations to come from the
namespace-managercontract viacontract-callerchecks -
If the manager contract was deployed as Clarity 4, it cannot call any BNS-V2 function
-
The
mng-manager-transferfunction (to transfer the manager role to a working contract) also requirescontract-caller == manager— creating a circular lockout -
BNS-V2 has no deployer override, no emergency function, and no alternative authorization path for launched namespaces
This results in a permanent, irrecoverable lockout of the namespace. No names can be minted, transferred, burned, listed, or managed. The manager cannot be transferred to a new contract. There is no on-chain escape route.
Broader ecosystem risk
Any DeFi protocol, DAO, or application that deployed a Clarity 4 contract as a manager or controller for a pre-Clarity 4 system is potentially affected. The bug is silent until the first cross-epoch contract-call? is attempted.
Relevant Precedent
SIP-023: The Stacks 2.2 epoch introduced a bug that broke trait semantics in contract-calls, deviating from the behavior established in SIP-015 / Stacks 2.1. SIP-023 was ratified as an emergency fix, creating the Stacks 2.3 epoch to restore correct trait argument handling. That bug similarly affected cross-epoch contract interoperability and required a consensus-level fix.
Requested Resolution
-
VM fix: Patch the Clarity VM to correctly resolve contract references when
contract-call?crosses epoch boundaries -
State intervention for affected namespaces: For BNS-V2 managed namespaces that are permanently locked due to this bug, reassign the
namespace-managerto thenamespace-importprincipal (or a designated proxy contract) via coordinated state modification, so operators can recover control once the VM is patched