Here is the setup:
I have a ProxyFactory, which has stores the address of the implementation, It creates a proxyAdmin, then it has a deploy function, which creates proxies for the logic contract.
The logic contract has a function, which needs to verify that the object calling it, is of the same kind: aka something of the like:
IERC165Upgradeable(_dst).supportsInterface(type(IRemix).interfaceId)
If I deploy the logic contract manually, this works just fine, however when I use a proxy, this results in:
Error: VM Exception while processing transaction: reverted with reason string 'Address: low-level delegate call failed'
When I call the proxy functions from "outside" (node.js, ethers.js) it all works fine, and I don't have any error. my Mocha tests are passing fine as well. But when the proxy calls a proxy, then it becomes problematic.
Code to reproduce
Proxy 1 calls:
IRemix(parent).requestDerivative(authors, address(this))
This comes from this function:
/// @dev Initialize a split sum to check that sum of all splits is 10000 (100%)
/// @return _senderIsAuthor if the sender is an author
function _splitSum() internal returns (bool _senderIsAuthor) {
uint256 _sum = 0;
_senderIsAuthor = false;
for (uint256 _i = 0; _i < authors.length; _i++) {
// Add splits for authors
if (msg.sender == authors[_i]) {
_senderIsAuthor = true;
}
splitAddresses.push(authors[_i]); // Add split address for each author
splits[authors[_i]] = authorSplits[_i]; // Add split amount for each author
_sum += authorSplits[_i]; // Add splits to working sum
_mintBadge(authors[_i]); // Mint a badge to each author
}
for (uint256 _j = 0; _j < parents.length; _j++) {
// Add splits for parent tokens
splitAddresses.push(parents[_j]); // Add split address for each parent token
splits[parents[_j]] = parentSplits[_j]; // Add split amount for each parent token
_sum += parentSplits[_j]; // Add splits to working sum
// Request derivatives from each specified parent
require(
IRemix(parents[_j]).requestDerivative(authors, address(this))
);
}
// Ensure valid split total
require(_sum == 10000, "INVALID_SPLIT_SUM");
return _senderIsAuthor;
}
Proxy 2 receives:
/// @dev Request derivative by a child token contract
/// @param _authors Address of authors to check for validity of holding a RMX
/// @param _dst Address of the Remix contract to send the derivative token to
/// @return _derivativeSuccess Success for the request
function requestDerivative(address[] memory _authors, address _dst) external override isDeployed returns (bool _derivativeSuccess) {
// Verifies that the address is a Remix contract
// WHERE I GET THE PROBLEM
require(
IERC165Upgradeable(_dst).supportsInterface(type(IRemix).interfaceId),
"INVALID_REMIX_CONTRACT"
);
// IRRELEVANT AS THIS WORKS FINE
require(msg.sender == _dst, "DERIVATIVE_RIGHTS_MUST_BE_ASKED_BY_DST");
// License check
bool _hasLicense = false;
for (uint256 _i = 0; _i < _authors.length; _i++) {
if (remixLicenseActive(_authors[_i])) {
_hasLicense = true;
}
}
require(_hasLicense, "MISSING_AUTHOR_LICENSE"); // TODO_ONE_DAY: Is this safe? Should require pre approval of minter?
// Mint derivative
_mintDerivative(_dst);
emit DerivativeIssued(_dst);
return true;
}
execution reverts with message:
eth_estimateGas
Contract call: <UnrecognizedContract>
From: 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266
To: 0xc6e7df5e7b4f2a278906862b61205850344d4e7d
Value: 0 ETH
Error: VM Exception while processing transaction: reverted with reason string 'Address: low-level delegate call failed'
at <UnrecognizedContract>.constructor (unknown)
at <UnrecognizedContract>.<unknown> (0xc6e7df5e7b4f2a278906862b61205850344d4e7d)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async EthModule._estimateGasAction (/home/xqua/Documents/Work/Alien/Alias/app/node_modules/hardhat/src/internal/hardhat-network/provider/modules/eth.ts:431:7)
at async HardhatNetworkProvider._sendWithLogging (/home/xqua/Documents/Work/Alien/Alias/app/node_modules/hardhat/src/internal/hardhat-network/provider/provider.ts:144:22)
at async HardhatNetworkProvider.request (/home/xqua/Documents/Work/Alien/Alias/app/node_modules/hardhat/src/internal/hardhat-network/provider/provider.ts:121:18)
at async JsonRpcHandler._handleRequest (/home/xqua/Documents/Work/Alien/Alias/app/node_modules/hardhat/src/internal/hardhat-network/jsonrpc/handler.ts:191:20)
at async JsonRpcHandler._handleSingleRequest (/home/xqua/Documents/Work/Alien/Alias/app/node_modules/hardhat/src/internal/hardhat-network/jsonrpc/handler.ts:152:17)
at async Server.JsonRpcHandler.handleHttp (/home/xqua/Documents/Work/Alien/Alias/app/node_modules/hardhat/src/internal/hardhat-network/jsonrpc/handler.ts:52:21)
Environment
Hardhat, solidity 8.4 or 8.20, tried on hardhat chain and ganache
What I tried
So I tried to do .call
as well, same issue. I've tried numerous functions in the contract and none are working as well. It is quite bizarre because proxy 1 succeeds at calling the first proxy (or so it seems to me???) but then proxy 2 can't read proxy 1 data.
I need to check that proxy 1 is really who they say they are as this could be abused otherwise by injecting false authors in the call.