From 44ca784155989e9717860e5b9b8493ed63620363 Mon Sep 17 00:00:00 2001 From: Nick Barry Date: Mon, 17 Mar 2025 20:16:06 -0600 Subject: [PATCH 1/5] add receive function to Proxy --- contracts/proxy/IProxy.sol | 2 ++ contracts/proxy/Proxy.sol | 4 ++++ contracts/proxy/_Proxy.sol | 7 +++++++ contracts/proxy/diamond/ISolidstateDiamondProxy.sol | 4 +--- contracts/proxy/diamond/SolidstateDiamondProxy.sol | 2 -- 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/contracts/proxy/IProxy.sol b/contracts/proxy/IProxy.sol index 6e757f4a7..6770f4428 100644 --- a/contracts/proxy/IProxy.sol +++ b/contracts/proxy/IProxy.sol @@ -6,4 +6,6 @@ import { _IProxy } from './_IProxy.sol'; interface IProxy is _IProxy { fallback() external payable; + + receive() external payable; } diff --git a/contracts/proxy/Proxy.sol b/contracts/proxy/Proxy.sol index 4f5eaeb94..58fd5c074 100644 --- a/contracts/proxy/Proxy.sol +++ b/contracts/proxy/Proxy.sol @@ -16,4 +16,8 @@ abstract contract Proxy is IProxy, _Proxy { fallback() external payable { _fallback(); } + + receive() external payable { + _receive(); + } } diff --git a/contracts/proxy/_Proxy.sol b/contracts/proxy/_Proxy.sol index 74ecd3592..e5d952776 100644 --- a/contracts/proxy/_Proxy.sol +++ b/contracts/proxy/_Proxy.sol @@ -73,4 +73,11 @@ abstract contract _Proxy is _IProxy { revert Proxy__ImplementationIsNotContract(); } + + /** + * @notice received ether is forwarded to the fallback function + */ + function _receive() internal virtual { + _fallback(); + } } diff --git a/contracts/proxy/diamond/ISolidstateDiamondProxy.sol b/contracts/proxy/diamond/ISolidstateDiamondProxy.sol index 986c094ac..ede24a014 100644 --- a/contracts/proxy/diamond/ISolidstateDiamondProxy.sol +++ b/contracts/proxy/diamond/ISolidstateDiamondProxy.sol @@ -16,6 +16,4 @@ interface ISolidstateDiamondProxy is IDiamondProxyReadable, IDiamondProxyWritable, ISafeOwnable -{ - receive() external payable; -} +{} diff --git a/contracts/proxy/diamond/SolidstateDiamondProxy.sol b/contracts/proxy/diamond/SolidstateDiamondProxy.sol index 2eeb5278a..755a8d977 100644 --- a/contracts/proxy/diamond/SolidstateDiamondProxy.sol +++ b/contracts/proxy/diamond/SolidstateDiamondProxy.sol @@ -99,8 +99,6 @@ abstract contract SolidstateDiamondProxy is _setOwner(msg.sender); } - receive() external payable {} - function _transferOwnership( address account ) From db9f6ef8cfaa4c6317235ad64b5f3dd4f9791e4b Mon Sep 17 00:00:00 2001 From: Nick Barry Date: Mon, 17 Mar 2025 20:31:09 -0600 Subject: [PATCH 2/5] add pending receive tests --- spec/proxy/Proxy.behavior.ts | 4 ++++ spec/proxy/diamond/DiamondProxy.behavior.ts | 4 ++++ spec/proxy/diamond/fallback/DiamondProxyFallback.behavior.ts | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/spec/proxy/Proxy.behavior.ts b/spec/proxy/Proxy.behavior.ts index d340a4aa2..a69a311fa 100644 --- a/spec/proxy/Proxy.behavior.ts +++ b/spec/proxy/Proxy.behavior.ts @@ -37,5 +37,9 @@ export function describeBehaviorOfProxy( ).not.to.be.reverted; }); }); + + describe('receive()', () => { + it('forwards value to implementation'); + }); }); } diff --git a/spec/proxy/diamond/DiamondProxy.behavior.ts b/spec/proxy/diamond/DiamondProxy.behavior.ts index d750b1f5e..664de1b16 100644 --- a/spec/proxy/diamond/DiamondProxy.behavior.ts +++ b/spec/proxy/diamond/DiamondProxy.behavior.ts @@ -55,5 +55,9 @@ export function describeBehaviorOfDiamondProxy( }); }); }); + + describe('receive()', () => { + it('todo'); + }); }); } diff --git a/spec/proxy/diamond/fallback/DiamondProxyFallback.behavior.ts b/spec/proxy/diamond/fallback/DiamondProxyFallback.behavior.ts index ad3fbed7e..d589abea5 100644 --- a/spec/proxy/diamond/fallback/DiamondProxyFallback.behavior.ts +++ b/spec/proxy/diamond/fallback/DiamondProxyFallback.behavior.ts @@ -37,6 +37,10 @@ export function describeBehaviorOfDiamondProxyFallback( it('forwards data without matching selector to fallback contract'); }); + describe('receive()', () => { + it('forwards value to fallback contract'); + }); + describe('#getFallbackAddress()', () => { it('returns the fallback address', async () => { expect(await instance.getFallbackAddress.staticCall()).to.equal( From e5485e9b98bfd08074380347c7a1b26cffbbc3d7 Mon Sep 17 00:00:00 2001 From: Nick Barry Date: Fri, 21 Mar 2025 23:01:41 -0600 Subject: [PATCH 3/5] fix UpgradeableProxy behavior test implementationFunction argument --- test/proxy/upgradeable/UpgradeableProxy.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/proxy/upgradeable/UpgradeableProxy.ts b/test/proxy/upgradeable/UpgradeableProxy.ts index 39ed0246d..78f9da49a 100644 --- a/test/proxy/upgradeable/UpgradeableProxy.ts +++ b/test/proxy/upgradeable/UpgradeableProxy.ts @@ -1,7 +1,7 @@ import { SignerWithAddress } from '@nomicfoundation/hardhat-ethers/signers'; import { describeBehaviorOfUpgradeableProxy } from '@solidstate/spec'; import { - $Ownable__factory, + $SafeOwnable__factory, $UpgradeableProxy, $UpgradeableProxy__factory, } from '@solidstate/typechain-types'; @@ -20,7 +20,7 @@ describe('UpgradeableProxy', () => { beforeEach(async () => { const [deployer] = await ethers.getSigners(); - const implementationInstance = await new $Ownable__factory( + const implementationInstance = await new $SafeOwnable__factory( deployer, ).deploy(); @@ -35,7 +35,7 @@ describe('UpgradeableProxy', () => { describeBehaviorOfUpgradeableProxy(async () => instance, { getOwner: async () => owner, getNonOwner: async () => nonOwner, - implementationFunction: 'owner()', + implementationFunction: 'nomineeOwner()', implementationFunctionArgs: [], }); From 2074e7d114c6c824401ca4ede67db467eecfb84c Mon Sep 17 00:00:00 2001 From: Nick Barry Date: Fri, 21 Mar 2025 23:16:23 -0600 Subject: [PATCH 4/5] remove SolidstateDiamondProxy receive test --- .../proxy/diamond/SolidstateDiamondProxy.behavior.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/spec/proxy/diamond/SolidstateDiamondProxy.behavior.ts b/spec/proxy/diamond/SolidstateDiamondProxy.behavior.ts index 813fe1d60..ea1b6be40 100644 --- a/spec/proxy/diamond/SolidstateDiamondProxy.behavior.ts +++ b/spec/proxy/diamond/SolidstateDiamondProxy.behavior.ts @@ -78,18 +78,6 @@ export function describeBehaviorOfSolidstateDiamondProxy( describeBehaviorOfSafeOwnable(deploy, args, skips); - describe('receive()', () => { - it('accepts ether transfer', async () => { - const [signer] = await ethers.getSigners(); - const value = 1; - const to = await instance.getAddress(); - - await expect(() => - signer.sendTransaction({ to, value }), - ).to.changeEtherBalance(instance, value); - }); - }); - describe('#diamondCut((address,enum,bytes4[])[],address,bytes)', () => { const selectors: string[] = []; const abi: string[] = []; From e34353babf8a2a876fd93c711a419722f54e2061 Mon Sep 17 00:00:00 2001 From: Nick Barry Date: Fri, 21 Mar 2025 23:36:18 -0600 Subject: [PATCH 5/5] clean up fallback and receive tests --- spec/proxy/Proxy.behavior.ts | 12 +++++++- spec/proxy/diamond/DiamondProxy.behavior.ts | 4 ++- .../fallback/DiamondProxyFallback.behavior.ts | 28 +++++++++++++++++-- test/proxy/diamond/SolidstateDiamondProxy.ts | 2 +- 4 files changed, 40 insertions(+), 6 deletions(-) diff --git a/spec/proxy/Proxy.behavior.ts b/spec/proxy/Proxy.behavior.ts index a69a311fa..05050ea29 100644 --- a/spec/proxy/Proxy.behavior.ts +++ b/spec/proxy/Proxy.behavior.ts @@ -39,7 +39,17 @@ export function describeBehaviorOfProxy( }); describe('receive()', () => { - it('forwards value to implementation'); + it('forwards value to implementation via delegatecall', async () => { + // TODO: receive tests pass because hardhat-exposed functions used as implementations are payable + const [signer] = await ethers.getSigners(); + + await expect( + signer.sendTransaction({ + to: await instance.getAddress(), + value: 1n, + }), + ).not.to.be.reverted; + }); }); }); } diff --git a/spec/proxy/diamond/DiamondProxy.behavior.ts b/spec/proxy/diamond/DiamondProxy.behavior.ts index 664de1b16..ed6fa77aa 100644 --- a/spec/proxy/diamond/DiamondProxy.behavior.ts +++ b/spec/proxy/diamond/DiamondProxy.behavior.ts @@ -1,5 +1,5 @@ import { describeFilter } from '@solidstate/library'; -import { ProxyBehaviorArgs } from '@solidstate/spec'; +import { describeBehaviorOfProxy, ProxyBehaviorArgs } from '@solidstate/spec'; import { IDiamondProxy } from '@solidstate/typechain-types'; import { expect } from 'chai'; import { ethers } from 'hardhat'; @@ -20,6 +20,8 @@ export function describeBehaviorOfDiamondProxy( instance = await deploy(); }); + describeBehaviorOfProxy(deploy, args, skips); + describe('fallback()', () => { it('forwards data with matching selector call to facet', async () => { expect(instance.interface.hasFunction(args.implementationFunction)).to diff --git a/spec/proxy/diamond/fallback/DiamondProxyFallback.behavior.ts b/spec/proxy/diamond/fallback/DiamondProxyFallback.behavior.ts index d589abea5..f6a9848ca 100644 --- a/spec/proxy/diamond/fallback/DiamondProxyFallback.behavior.ts +++ b/spec/proxy/diamond/fallback/DiamondProxyFallback.behavior.ts @@ -31,14 +31,36 @@ export function describeBehaviorOfDiamondProxyFallback( nonOwner = await args.getNonOwner(); }); - describeBehaviorOfDiamondProxy(async () => instance, args, skips); + describeBehaviorOfDiamondProxy(async () => instance, args, [ + 'receive()', + ...(skips ?? []), + ]); describe('fallback()', () => { - it('forwards data without matching selector to fallback contract'); + it('forwards data without matching selector to fallback contract', async () => { + await expect( + owner.sendTransaction({ + to: await instance.getAddress(), + }), + ).to.be.revertedWithCustomError( + instance, + 'Proxy__ImplementationIsNotContract', + ); + }); }); describe('receive()', () => { - it('forwards value to fallback contract'); + it('forwards value to fallback contract', async () => { + await expect( + owner.sendTransaction({ + to: await instance.getAddress(), + value: 1n, + }), + ).to.be.revertedWithCustomError( + instance, + 'Proxy__ImplementationIsNotContract', + ); + }); }); describe('#getFallbackAddress()', () => { diff --git a/test/proxy/diamond/SolidstateDiamondProxy.ts b/test/proxy/diamond/SolidstateDiamondProxy.ts index 8a7dfd311..76ae0128e 100644 --- a/test/proxy/diamond/SolidstateDiamondProxy.ts +++ b/test/proxy/diamond/SolidstateDiamondProxy.ts @@ -54,6 +54,6 @@ describe('SolidstateDiamondProxy', () => { fallbackAddress: ethers.ZeroAddress, immutableSelectors, }, - ['fallback()'], + ['fallback()', 'receive()'], ); });