Category: Ecosystem / Development
Author: Boreal-Proof (@aeosproof)
GitHub Pull Request: ensdomains/ens-contracts#577
Status: Open / Ready for Review
---
## Executive Summary
We have submitted:**Pull Request [#577](https://github.com/ensdomains/ens-contracts/pull/577)** to `ensdomains/ens-contracts` to address a compatibility limitation affecting Smart Contract Wallets (e.g., Gnosis Safe, Argent,ERC-4337 Smart Accounts) during .eth name registrations and renewals.
Currently, `ETHRegistrarController.sol` and `BulkRenewal.sol` rely on Solidity's built-in `.transfer()` to return excess ETH refunds. Because `.transfer()` forwards a fixed stipend of only **2,300 gas**, transactions initiatedby smart contract accounts that perform event logging or state updates in their receive() functions consistently revert with Out of Gas (OOG).
PR #577 replaces `.transfer()` with modern `.call{value: ...}("")` across the affected contracts, accompanied by reproducible Foundry unit tests. We are submitting this proposal to:
1. Solicit feedback and review from the ENS core development team.
2. Request ecosystem grant / retroactive public goods consideration from the ENS Foundation for this core infrastructure improvement.
---
## Background & Motivation
When users register or renew names via `ETHRegistrarController`, they frequently provide extra ETH (`msg.value > totalPrice`) to account for oracle price fluctuations or slippage during commitment-reveal intervals.
When surplus ETH is refunded:
```solidity
if (msg.value > totalPrice)
payable(msg.sender).transfer(msg.value - totalPrice);
The Problem:
• .transfer() forwards a hardcoded 2,300 gas stipend.
• Following EIP-1884 (gas repricing) and the standard adoption of Smart Contract Wallets (Gnosis Safe multi-sigs, DAOs, ERC-4337 bundler targets), almost all smart accounts execute logic (e.g., emit ExecutionSuccess, state
bookkeeping, or module delegation) in their receive() or fallback() hooks that consumes significantly more than 2,300 gas.
• As a result, DAOs and institutional multi-sig vaults attempting to register names with a buffer revert, creating a broken user experience for Ethereum’s most security-conscious participants.
• Furthermore, in ETHRegistrarController.sol, the owner withdrawal function withdraw() uses .transfer(address(this).balance). If the contract owner is a timelock or multisig requiring >2,300 gas, withdrawals can be permanently
locked out.
──────
Technical Specification & Pull Request #577
PR #577 introduces a drop-in update to replace .transfer() with low-level .call and boolean verification:
Affected Files:
- contracts/ethregistrar/ETHRegistrarController.sol (lines 344, 371, 376):
• register(): Surplus refund updated to .call.
• renew(): Surplus refund updated to .call.
• withdraw(): Owner balance withdrawal updated to .call. - contracts/ethregistrar/BulkRenewal.sol (line 73):
• renewAll(): Excess refund updated to .call. - contracts/ethregistrar/StaticBulkRenewal.sol (line 54):
• renewAll(): Excess refund updated to .call.
Security & Reentrancy Analysis (Checks-Effects-Interactions):
In all instances, the external .call is executed strictly after:
• All state mutations are finalized (resolver configuration, reverse record bitwise checks, expiry updates).
• All canonical events are emitted (emit NameRegistered, emit NameRenewed).
• Execution follows the strict Checks-Effects-Interactions pattern, mitigating reentrancy attack vectors.
──────
Verification & Unit Testing
To ensure 100% test reproducibility, PR #577 includes a dedicated Foundry test suite in test/SmartWalletRefundTest.t.sol:
contract SmartWalletRefundTest is Test {
// Validates that legacy .transfer reverts on Smart Accounts with active receive() hooks
function test_legacyTransferRevertsForSmartWallet() public;
// Validates that modern .call successfully delivers 100% of the surplus refund
function test_modernCallSucceedsForSmartWallet() public;
}
Test Results:
forge test --match-contract SmartWalletRefundTest
[PASS] test_legacyTransferRevertsForSmartWallet() (gas: 50297)
[PASS] test_modernCallSucceedsForSmartWallet() (gas: 71964)
Suite result: ok. 2 passed; 0 failed; 0 skipped
──────
Funding Request & Community Value
Account Abstraction and institutional multi-sig adoption are core pillars of Ethereum’s roadmap. Ensuring that ENS registration controllers natively support smart contract accounts without artificial 2,300 gas reverts is essential
public infrastructure.
We respectfully request:
- Core Review: Review and merge of PR #577 fix(ethregistrar): replace .transfer() with .call() to support smart contract wallets by aeosproof · Pull Request #577 · ensdomains/ens-contracts · GitHub into staging.
- Retroactive Grant / Public Goods Funding: Consideration from the ENS Foundation / DAO for a developer grant ($3,000 – $7,500 USDC) to cover the research, engineering, formalization, and test suite implementation of this
compatibility patch.
We look forward to collaborating with the ENS team and community on this implementation!