2

I have the following code for a Factory that creates instances of a contract that is Upgradeable. I understand that, in order to achieve this, one of the options I have is to use TransparentUpgradeableProxy (actually, when I first deployed using Hardhat -and it worked- this is what it was done under the hood):

SmartWalletFactory.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;

import "../multiwrap/SmartWallet.sol";
import "../proxy/SmartWalletProxy.sol";

contract SmartWalletFactory {
    mapping(uint256 => address) private smartWallets;
    SmartWalletProxy immutable proxy;

    constructor(address _initialImpl) {
        proxy = new SmartWalletProxy(_initialImpl);
    }

    function createSmartWallet(
        uint256 _smartWalletId,
        address _defaultAdmin,
        string memory _name,
        string memory _symbol,
        string memory _contractURI,
        address[] memory _trustedForwarders,
        address _royaltyRecipient,
        uint256 _royaltyBps
      ) public {
        TransparentUpgradeableProxy smartWallet = new TransparentUpgradeableProxy(address(proxy), address(0), abi.encodeWithSelector(SmartWallet(payable(address(0))).initialize.selector, _defaultAdmin, _name, _symbol, _contractURI, _trustedForwarders, _royaltyRecipient, _royaltyBps));
        smartWallets[_smartWalletId] = address(smartWallet);
    }
} 

And this is the code for the proxy:

SmartWalletProxy.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.8;

import "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
import "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol";

contract SmartWalletProxy is Ownable {
    TransparentUpgradeableProxy immutable proxy;
    address public smartWalletImplementation;

    constructor(address _initialImpl) {
        proxy = new TransparentUpgradeableProxy(_initialImpl, address(0), "0x");
        smartWalletImplementation = _initialImpl;
        transferOwnership(tx.origin);
    }

    function upgrade(address _newImpl) public onlyOwner {
        proxy.upgradeTo(_newImpl);
        smartWalletImplementation = _newImpl;
    }

}

So I first deploy SmartWallet as my implementation contract and then I want to deploy the SmartWalletFactory using the address of SmartWallet as input.

The problem I am having is that I am getting the following error (using Remix):

Gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending?
Internal JSON-RPC error.
{
"code": 3,
"message": "execution reverted: Address: low-level delegate call failed",
"data": "0x08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000027416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c656400000000000000000000000000000000000000000000000000"
}

Does anybody know what this error could be?

4 Answers 4

0

I know this might not be the answer you are looking for, but I would STRONGLY advise you consider using the UUPS design pattern for an upgradable smart contract implementation. It's similar to the transparent proxy pattern, except the upgrade is triggered via the logic contract rather than from the proxy contract.

UUPS proxy pattern tutorial

The advantages are many. For example, there is no possibility of solidity function collision or any exploit based on that. Plus Hardhat && OpenZepplein both offer full library support for it.

In my personal opinion, TransparentUpgradeableProxy is outdated. UUPS && Dimond pattern are the only two worth considering.

7
  • Thank you, now I am using UUPS proxy. Anyway, I am getting the same error. But I do think this proxy is better than the one I was using before. Commented Dec 29, 2022 at 2:29
  • It would be nice if you can point me to an actual Factory or Clone implementation of an upgreadeable smart contract that uses this kind of proxy. I found none looking online. Thank you. Commented Dec 29, 2022 at 2:35
  • Alright, but if you are getting the same error, I would like to see your code, and I try to fix it. Would be possible for you to share the code?
    – Sky
    Commented Dec 29, 2022 at 7:08
  • This is what I have at the moment: gist.github.com/MatiFalcone/c0f6c5c82a59d8628160a5a9e372ccc4 It is based in this solution that works, but with a Token contract which is not as complex as the one I have: github.com/MatiFalcone/ContractFactoryUpgradeable Right now problem is that when I want to createSmartWallet from the Factory contract, the Factory has not the right permissions to mint the NFT. I don't know how to fix this since I first deploy the Smart Wallet and then using that I deploy the Factory. Is not possible to know the address of the Factory beforehand. Commented Dec 29, 2022 at 7:22
  • Alright, I understand. Is this urgent for you? Because I am on hollidays now, but I could sit down and give you an answer in a few days. That alright by you?
    – Sky
    Commented Dec 29, 2022 at 12:34
3

The second parameter of TransparentUpgradeableProxy sets the proxy admin address. When initializing proxy in the first line of the constructor of SmartWalletProxy.sol, you're specifying the zero address as the admin of your proxy. Perhaps try address(this) instead of address(0).

1
  • I have tried this and I am getting the same error. Commented Dec 25, 2022 at 20:58
1

In order to use the transparent proxy pattern, you have to operate the proxy using a proxyAdmin. When any interaction with your proxy contract is made, the proxy delegates the call to whichever contract you have provided a proxy for.

So, the clue here is the error line "message": "execution reverted: Address: low-level delegate call failed". Just reading this, I would assume that you haven't set up your proxyAdmin or that you are calling the contract from a non-admin address.

Read the docs here from OpenZeppelin

4
  • I have replaced that line but I am getting the same error. Commented Dec 29, 2022 at 2:30
  • You say that now you’re using UUPS - is that the case?
    – immaxkent
    Commented Dec 29, 2022 at 9:18
  • Yes, that's correct. Commented Dec 30, 2022 at 0:02
  • Try passing msg.sender as the address argument in the constructor (address(this) won' work as that would represent the contract itself being it's own admin, and the delegate call would throw an error. Does it help?
    – immaxkent
    Commented Dec 30, 2022 at 16:57
1

as you're facing issue with upgradeable contracts. I would suggest you to use UUPS EIP-1967 contract on remix. Remix gives you full support of UUPS. Just you need to use openzeppelin wizard to get useful UUPS inheritences and use it in remix.

1
  • Thank you, now I am using UUPS proxy. Anyway, I am getting the same error. But I do think this proxy is better than the one I was using before. Commented Dec 29, 2022 at 2:29

Not the answer you're looking for? Browse other questions tagged or ask your own question.