Testing proxy giving error

Hi, first of all thank openzeppelin team for help for clearing my doubt on proxy contract set up. I successfully deployed and can call the logic contract's methods through proxy. But testing this giving issue. My test script for checking is the proxy working properly or not is :slight_smile:

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

import "forge-std/Test.sol";
import "../src/Game.sol";
import "../src/MainProxy.sol";

contract ProxyTest is Test {
    address me = vm.addr(0x1);
    Game game;
    MainProxy mainProxy;
    
   
    function setUp() public {
        game = new Game();
        console.log("game address:", address(game));
        mainProxy = new MainProxy(address(game), abi.encodeWithSignature("initialize()", 0.001 ether));
        console.log("mainProxy address:", address(mainProxy));
    }

    function test_proxy() public {
        vm.startPrank(address(me));
        mainProxy.monsterId();
    }
}

This is not complete script, but the mainProxy.monsterId(); is giving error:

[{
	"resource": "/home/abinash/Desktop/Game/test/ProxyTest.t.sol",
	"owner": "solidity-language-server",
	"code": "9582",
	"severity": 8,
	"message": "Member \"monsterId\" not found or not visible after argument-dependent lookup in contract MainProxy.",
	"source": "solidity",
	"startLineNumber": 23,
	"startColumn": 9,
	"endLineNumber": 23,
	"endColumn": 28
}]

When i calling the monsterId through my real proxy address by cast send it is giving correct ans.
Why is this happening in case of test??

This error-message:

Tells you that MainProxy does not implement any public or external function named monsterId.

Sir i understood the error message but why it is giving the error in testing when i can execute this function with my EOA.

Because you are not trying to execute a function here, you are trying to compile a contract.
And in order for the compiler to able to compile the contract, that function needs to be visible in it.

The issue is that as far as Solidity is concerned mainProxy is an instance of MainProxy, which does not have a monsterId function, and this causes a compilation error.

You can call this method with your EOA because you know that it will answer to the Game interface. You can express this in Solidity by casting mainProxy as an instance of Game. You should do something like this:

contract ProxyTest is Test {
    address me = vm.addr(0x1);
    Game game;
    MainProxy mainProxy;
    
   
    function setUp() public {
        address gameImplementation = address(new Game());
        console.log("game implementation address:", gameImplementation);
        mainProxy = new MainProxy(gameImplementation, abi.encodeWithSignature("initialize()", 0.001 ether));
        console.log("mainProxy address:", address(mainProxy));
        game = Game(gameImplementation);
    }

    function test_proxy() public {
        vm.startPrank(address(me));
        game.monsterId();
    }
}

Here gameImplementation is new game instance, then we are doing this again: game = Game(gameImplementation);. I have 2 doubts.

  1. Is address gameImplementation = address(new Game()); game = Game(gameImplementation) and Game game = new Game() same thing? If no, then how it is different?

  2. From your code the addresses are:

game implementation address: 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f
mainProxy address: 0x2e234DAe75C793f67A35089C9d99245E1C58470b
game address: 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f

Where we can see game and gameImplementation address is same. So can we do Game game = new Game()?? We will get same address.
And Here mainProxy is the wrapper of logic contract, as per rule we have to call the logic contract through proxy contract. If we do game.monsterId() then we are not actually calling the function through proxy, we are calling it through logic contract instance, am i right?? To test the upgrade functionality working properly or not we will have to call the methods of logic contract from proxy which we are not doing in your code. I am confused, please clear. Thanks.

Sorry, my bad. I wrote that wrong. What I meant was:

         mainProxy = new MainProxy(gameImplementation, abi.encodeWithSignature("initialize()", 0.001 ether));
         console.log("mainProxy address:", address(mainProxy));
-        game = Game(gameImplementation);
+        game = Game(mainProxy);

So that you interact with the proxy through the Game interface.

So calling through mainProxy and game of game = Game(mainProxy) is same thing, right?

It's the same thing in terms of what function call is made, but in one case the Solidity code will compile and in the other it won't, because mainProxy.monsterId() is not recognized as a valid function call.

One thing still making me confused that, u said You can call this method with your EOA because you know that it will answer to the Game interface - here what we know or expect doesn't matter, the thing matter is how the upgradable functionality works, it works by calling the logic contract through proxy, but why we could not do the same thing in test. Just this portion making me confused. It will be very helpful if u can clear it. Btw my test worked. Thanks.

Because in the test, before executing the function, you first need to compile the contract into byte-code, and the compiler won't let you do that.

Whereas, when you execute the transaction with an EOA, the byte-code already exists (deployed on the network), so you don't need to go through the compiler.

What matters is what the compiler knows! And in this case, it doesn't know "how the upgradeable functionality works" unless you cast the contract address to the Game.