I'm trying to upgrade my UUPS contract using the upgradeTo method provided by UUPSUpgradable.
I'm kinda new to this, from what I understood the UUPS contract is both a proxy and implementation contract, as such, i should be able to directly call upgradeTo on it.
The thing is, when i try to run the Truffle migration script, i get the Function must be called through delegatecall error. This is thrown by the onlyProxy modifier inside the UUPSUpgradable contract:
modifier onlyProxy() {
require(address(this) != __self, "Function must be called through delegatecall");
require(_getImplementation() == __self, "Function must be called through active proxy");
_;
}
This is my migration script for the upgrade:
Here CryptoWay is the Proxy contract instance and CryptoWayV2 is the new Implementation.
module.exports = async function (deployer) {
const CryptoWay = artifacts.require('CryptoWay');
const CryptoWayV2 = artifacts.require('CryptoWayV2');
// deploy new contract instance
await deployer.deploy(CryptoWayV2);
const contractV2 = await CryptoWayV2.deployed();
// Get the existing contract instance
const contract = await CryptoWay.deployed();
// Perform the upgrade when needed by replacing NEW_ADDRESS with the address of the new implementation contract
await contract.upgradeTo(contractV2.address);
console.log('-- UPGRADE COMPLETE --');
console.log('Existing contract address: ', contract.address);
};
Here's the contract:
pragma solidity ^0.8.0;
//SPDX-License-Identifier: GL3
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
/**
* @title CryptoWay
* @dev This upgradable contract creates payments and allows the owner to withdraw funds.
*/
contract CryptoWay is
Initializable,
OwnableUpgradeable,
UUPSUpgradeable
{
event Payment(
string paymentId,
address customerAddress,
uint finalAmount,
string currency,
string coinName
);
event Withdraw(address indexed owner, uint amount);
uint public innerBalance;
/**
* @dev the initializer works as a constructor for upgradable contracts, this gets executed only once during the initial deployment
*/
function initialize() initializer public {
innerBalance = 0;
__Ownable_init();
__UUPSUpgradeable_init();
}
/**
* @param paymentId the id of the payment
* @param currency currency type (fiat/crypto)
* @param coinName ISO code of the coin that is being used
* @dev allows external users or other contracts to initiate payments, the payment amount is then transferred to the owner.
*/
function triggerPayment (
string memory paymentId,
string memory currency,
string memory coinName
) external payable {
require(msg.value > 0, "Payment amount must be greater than zero");
emit Payment(paymentId, msg.sender, msg.value, currency, coinName);
payable(owner()).transfer(msg.value);
}
/**
* @dev gets automatically called when a transaction to this smart contract is made without explicitly calling any function
*/
receive() external payable {
innerBalance += msg.value;
}
/**
* @dev transfers all the smart contract balance to the owner address
*/
function withdrawAll() external onlyOwner {
require(innerBalance > 0, "Inner balance must be greater than zero");
payable(owner()).transfer(innerBalance);
innerBalance = 0;
emit Withdraw(msg.sender, innerBalance);
}
/**
* @param amount amount that needs to be withdrawed
* @dev transfer the chosen smart contract balance to the owner address
*/
function withdraw(uint amount) external onlyOwner {
require(amount <= innerBalance, "Withdrawal amount exceeds inner balance");
innerBalance -= amount;
payable(owner()).transfer(amount);
emit Withdraw(msg.sender, amount);
}
/**
* @param newOwner address of the new owner
* @dev changes the owner of the contract based on a new address, can only be called by the current owner
*/
function changeOwner(address newOwner) external onlyOwner {
transferOwnership(newOwner);
}
/**
* @param newImplementation address of the new implementation contract
* @dev upgrades the contract by changing the implementation contract to newImplementation
*/
function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}
}