I am developping a solidity contract with those inheritances:
ERC20
ERC20FlashMint
VRFConsumerBaseV2Plus
AutomationCompatibleInterface
pragma solidity ^0.8.24;
import "@chainlink/contracts/node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@chainlink/contracts/src/v0.8/vrf/dev/VRFConsumerBaseV2Plus.sol";
import "@chainlink/contracts/src/v0.8/vrf/dev/interfaces/IVRFCoordinatorV2Plus.sol";
import "@chainlink/contracts/src/v0.8/automation/interfaces/AutomationCompatibleInterface.sol";
contract ExampleV1 is ERC20, ERC20FlashMint, VRFConsumerBaseV2Plus, AutomationCompatibleInterface {
...
}
I also created a javascript Test file to be able to deploy and simulate all the functions calls of the contract.
I would like to be able to create a new contract ExampleV2 but i struggle to add UUPS capability of Openzeppelin.
import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
If i try to add them into the contract ExampleV1, i get a lot of conflicts telling me that functions exist more than once.
Can someone help me understand how to create a proxy contract with UUPS that will help me upgrade the ExampleV1 to ExampleV2 and also add the code in Javascript to be able to have a complete test in Hardhat deploying first the ExampleV1 and then the ExampleV2.
Thank you for your help.
If needed more information. I would be happy to add details.
Expected result would be:
Develop ExampleV1 (upgradeable)
In the javascript test code:
deploy ExampleV1
call ExampleV1 functions
Develop ExampleV2 (upgrdeable)
In the javascript test code:
deploy ExampleV1
call ExampleV1 functions
In the same address deploy ExampleV2
call ExampleV2 functions
Amxx
June 25, 2024, 1:16pm
2
Hello @Mehdi_El_Filahi
Is your V1 already deployed? If yes, can you share the address?
The first thing that comes to mind is that there is no process to upgrade from a non-upgradeable to an upgradeable contract. Upgradeability requires the use of a proxy, and the non upgradeable contracts are not designed to work with a proxy.
If you are in a situation where you deployed a non-upgradeable contract in the past, and you now want to upgrade it ... that is just not possible.
Amxx
June 25, 2024, 1:17pm
3
This is not how ethereum works. You just cannot do that.
Hi Amxx,
For the moment we did not deploy on a testnet but only tested with Hardhat because we are stil in development.
I will show an example of the solidity contract that we try to change to be upgradeable.
@Amxx here is an example of contract similar to ours that i would like to convert as upgradeable.
If you would guide me of help me it would be really reallt great.
Tank you.
This is an example from Patrick Collins (https://github.com/PatrickAlphaC/hardhat-smartcontract-lottery-fcc/raw/62463b07955caa5cc68d027a7668d5a1372f795c/contracts/Raffle.sol ):
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
import "@chainlink/contracts/src/v0.8/interfaces/VRFCoordinatorV2Interface.sol";
import "@chainlink/contracts/src/v0.8/vrf/VRFConsumerBaseV2.sol";
// In the video, we use the following import statement, but newer versions of the Chainlink
// contracts have a different file structure.
// import "@chainlink/contracts/src/v0.8/VRFConsumerBaseV2.sol";
import "@chainlink/contracts/src/v0.8/automation/interfaces/AutomationCompatibleInterface.sol";
import "hardhat/console.sol";
/* Errors */
error Raffle__UpkeepNotNeeded(uint256 currentBalance, uint256 numPlayers, uint256 raffleState);
error Raffle__TransferFailed();
error Raffle__SendMoreToEnterRaffle();
error Raffle__RaffleNotOpen();
/**
* @title A sample Raffle Contract
* @author Patrick Collins
* @notice This contract is for creating a sample raffle contract
* @dev This implements the Chainlink VRF Version 2
*/
contract Raffle is VRFConsumerBaseV2, AutomationCompatibleInterface {
/* Type declarations */
enum RaffleState {
OPEN,
CALCULATING
}
/* State variables */
// Chainlink VRF Variables
VRFCoordinatorV2Interface private immutable i_vrfCoordinator;
uint64 private immutable i_subscriptionId;
bytes32 private immutable i_gasLane;
uint32 private immutable i_callbackGasLimit;
uint16 private constant REQUEST_CONFIRMATIONS = 3;
uint32 private constant NUM_WORDS = 1;
// Lottery Variables
uint256 private immutable i_interval;
uint256 private immutable i_entranceFee;
uint256 private s_lastTimeStamp;
address private s_recentWinner;
address payable[] private s_players;
RaffleState private s_raffleState;
/* Events */
event RequestedRaffleWinner(uint256 indexed requestId);
event RaffleEnter(address indexed player);
event WinnerPicked(address indexed player);
/* Functions */
constructor(
address vrfCoordinatorV2,
uint64 subscriptionId,
bytes32 gasLane, // keyHash
uint256 interval,
uint256 entranceFee,
uint32 callbackGasLimit
) VRFConsumerBaseV2(vrfCoordinatorV2) {
i_vrfCoordinator = VRFCoordinatorV2Interface(vrfCoordinatorV2);
i_gasLane = gasLane;
i_interval = interval;
i_subscriptionId = subscriptionId;
i_entranceFee = entranceFee;
s_raffleState = RaffleState.OPEN;
s_lastTimeStamp = block.timestamp;
i_callbackGasLimit = callbackGasLimit;
}
function enterRaffle() public payable {
// require(msg.value >= i_entranceFee, "Not enough value sent");
// require(s_raffleState == RaffleState.OPEN, "Raffle is not open");
if (msg.value < i_entranceFee) {
revert Raffle__SendMoreToEnterRaffle();
}
if (s_raffleState != RaffleState.OPEN) {
revert Raffle__RaffleNotOpen();
}
s_players.push(payable(msg.sender));
// Emit an event when we update a dynamic array or mapping
// Named events with the function name reversed
emit RaffleEnter(msg.sender);
}
/**
* @dev This is the function that the Chainlink Keeper nodes call
* they look for `upkeepNeeded` to return True.
* the following should be true for this to return true:
* 1. The time interval has passed between raffle runs.
* 2. The lottery is open.
* 3. The contract has ETH.
* 4. Implicity, your subscription is funded with LINK.
*/
function checkUpkeep(bytes memory /* checkData */ )
public
view
override
returns (bool upkeepNeeded, bytes memory /* performData */ )
{
bool isOpen = RaffleState.OPEN == s_raffleState;
bool timePassed = ((block.timestamp - s_lastTimeStamp) > i_interval);
bool hasPlayers = s_players.length > 0;
bool hasBalance = address(this).balance > 0;
upkeepNeeded = (timePassed && isOpen && hasBalance && hasPlayers);
return (upkeepNeeded, "0x0"); // can we comment this out?
}
/**
* @dev Once `checkUpkeep` is returning `true`, this function is called
* and it kicks off a Chainlink VRF call to get a random winner.
*/
function performUpkeep(bytes calldata /* performData */ ) external override {
(bool upkeepNeeded,) = checkUpkeep("");
// require(upkeepNeeded, "Upkeep not needed");
if (!upkeepNeeded) {
revert Raffle__UpkeepNotNeeded(address(this).balance, s_players.length, uint256(s_raffleState));
}
s_raffleState = RaffleState.CALCULATING;
uint256 requestId = i_vrfCoordinator.requestRandomWords(
i_gasLane, i_subscriptionId, REQUEST_CONFIRMATIONS, i_callbackGasLimit, NUM_WORDS
);
// Quiz... is this redundant?
emit RequestedRaffleWinner(requestId);
}
/**
* @dev This is the function that Chainlink VRF node
* calls to send the money to the random winner.
*/
function fulfillRandomWords(uint256, /* requestId */ uint256[] memory randomWords) internal override {
// s_players size 10
// randomNumber 202
// 202 % 10 ? what's doesn't divide evenly into 202?
// 20 * 10 = 200
// 2
// 202 % 10 = 2
uint256 indexOfWinner = randomWords[0] % s_players.length;
address payable recentWinner = s_players[indexOfWinner];
s_recentWinner = recentWinner;
s_players = new address payable[](0);
s_raffleState = RaffleState.OPEN;
s_lastTimeStamp = block.timestamp;
(bool success,) = recentWinner.call{value: address(this).balance}("");
// require(success, "Transfer failed");
if (!success) {
revert Raffle__TransferFailed();
}
emit WinnerPicked(recentWinner);
}
/**
* Getter Functions
*/
function getRaffleState() public view returns (RaffleState) {
return s_raffleState;
}
function getNumWords() public pure returns (uint256) {
return NUM_WORDS;
}
function getRequestConfirmations() public pure returns (uint256) {
return REQUEST_CONFIRMATIONS;
}
function getRecentWinner() public view returns (address) {
return s_recentWinner;
}
function getPlayer(uint256 index) public view returns (address) {
return s_players[index];
}
function getLastTimeStamp() public view returns (uint256) {
return s_lastTimeStamp;
}
function getInterval() public view returns (uint256) {
return i_interval;
}
function getEntranceFee() public view returns (uint256) {
return i_entranceFee;
}
function getNumberOfPlayers() public view returns (uint256) {
return s_players.length;
}
}
Amxx
June 26, 2024, 8:02am
6
@Amxx i already tried with this documentation and it is two weeks i m stuck with many errors in the contract and struggled to find example explaining how to create unit tests in javascripts.
Amxx
June 27, 2024, 8:49am
8
If the documentation is not enough, then I'm not sure what to tell you. At that point you need a tutorial/class more than you need technical support.
The first thing I see is that your ExampleV1 is not usiong the upgradeable contracts
contract ExampleV1 is ERC20, ERC20FlashMint, VRFConsumerBaseV2Plus, AutomationCompatibleInterface {
Which is probably not a goot start.
If you want an example of contracts that went through an upgrade, you can check the vesting wallets in the forta repository .