Hello!
While deploying an implementation contract, the following function retrieves data stored in memory:
// Retrieve the array that contains the entire number of Items set for the contract
function getUpdatedItems()
public
returns (uint256[] memory, ItemsLibrary.Item[] memory)
{
idCounter.reset();
for (uint256 i = 0; i < itemsCount; i++) {
if (idToItem[i].itemState != ItemsLibrary.ItemState.deleted) {
idCounter.increment();
}
}
uint256 indexesNumber = idCounter.current();
uint256[] memory _itemsIndexesArray = new uint256[](indexesNumber);
ItemLibrary.Items[] memory _items = new ItemsLibrary.Item[](
indexesNumber
);
uint8 k;
for (uint8 j = 0; j < itemCount; j++) {
if (idToItem[j].itemState != ItemLibrary.ItemState.deleted) {
_items[k].var1 = idToItem[j].var1;
_items[k].var2 = idToItem[j].var2;
_itemIndexesArray[k] = j;
k++;
}
}
return (_itemIndexesArray, _items);
}
If I deploy this and call the function within the contract I get both memory arrays. However, if I call it on a contract that was deployed as a proxy being my code the implementation the transaction goes through but it doesn't store the memory arrays. The console shows that the input is the same 0x.. on both scenarios.
I notice though that on the first scenario the "decoded input" is "{}" while on the latter is "-" so I don't understand where is the proxy taking the memory variable to, how is it decoding (or not decoding) the input or simply why is it unable to build the function.
Any help will be much appreciated!
Environment
Remix
The proxy-implementation pattern runs the logic in the implementation contract in the context of the proxy contract. What you observed is very likely caused by a lack of valid idToItem
array in the proxy contract, i.e., the idToItem
array is not initialized.
Hello @maxareo ! It's actually not an array but a mapping and its actually initialized in the implementation contract before the initialize
function:
contract MyImplementation is
Initializable,
AccessControlUpgradeable,
PausableUpgradeable,
UUPSUpgradeable
{
using ItemLibrary for *;
using CountersUpgradeable for CountersUpgradeable.Counter;
CountersUpgradeable.Counter private idCounter;
mapping(uint256 => ItemLibrary.Item) public idToItem;
so how can I initialize it (the mapping) in the context of the proxy contract? Thanks for the guidance!
Do you understand what this sentence means? It means the logic is in the implementation contract, but the state variables are retrieved from the storage in the proxy contract. Therefore, in the storage of the proxy contract, either a mapping or an array must be initialized.
I understand what you try to say but my question is the same...how do you do it in the proxy contract? Maybe the steps Im following don't work. This is what Im doing:
-
Deploy MyImplementation.sol.
-
Deploy ProxyFactory.sol with MyImplementation.sol ' address as a MyImplementationTemplate address variable that is used to make the proxy contract.
-
Initialize ProxyFactory.sol.
-
Call the CreateProxyContract
function in ProxyFactory.sol and get the address the proxy was deployed to. This already initializes the proxy.
-
Use the "at address" button in remix using the address I got from creating the proxy and selecting a MyImplementation type of contract . IS THIS RIGHT TO TEST THE CORRECT BEHAVIOUR OF THE PROXY?
-
Call the getUpdatedItems function on the proxy address --->Returns nothing...
What am I doing wrong from the steps?
The proxy-implementation pattern takes very careful designs especially for the storage layout in both contracts. That means the storage layout in the proxy contract must match that in the implementation contract and vice versa. Have you considered this?
Yes, see here how the proxy is initialized:
function initialize() public initializer {
...
MyImplementationTemplate = 0x...;
}
function createTargetContract(uint8 _Number, string memory _Name) // same as required by the initializer function in the implementation contract
external
payable
onlyRole(DEFAULT_ADMIN_ROLE)
returns (address)
{
address clone = ClonesUpgradeable.clone(MyImplementationTemplate);
ERC1967Proxy proxy = new ERC1967Proxy(
clone,
abi.encodeWithSelector(
TargetContract(address(0)).initialize.selector,
_Number,
_Name
)
);
contractsToOrganizer[proxy] = msg.sender;
targetContractArray.push(proxy);
emit targetContractDeployed(address(proxy));
return address(proxy);
}
Do you know example repos that implement the proxy pattern so maybe I can check?
You may want to take a look at EIP-1967.