Ethereum: Upgrading Smart Contracts with New Fields: How to Safely Modify Storage Layout

Here’s the article:

Upgrading Smart Contracts with New Fields: How to Safely Modify Storage Layout

As a developer working on Ethereum-based smart contracts, you’re likely familiar with the concept of upgrading your codebase to incorporate new features. In this article, we’ll explore how to safely upgrade your existing smart contract using upgradable smart contract patterns and introduce a new struct called GovernorCountingSimpleStorage.

Background

Ethereum: Upgrading Smart Contracts with New Fields: How to Safely Modify Storage Layout

In recent times, developers have been experimenting with upgradable smart contracts, which use special properties like upgradable() and removable() to allow for more flexibility in the code. These upgrades can improve scalability, performance, and security of your contract. One common approach is to create new structs that modify the existing storage layout.

GovernorCountingSimpleStorage Upgrade

Let’s assume you have a struct named GovernorCountingSimpleStorage with existing fields:

// GovernorCountingSimpleStorage.sol

struct GovernorCountingSimpleStorage {

uint256 _governors: uint256;

}

To upgrade this contract, we need to create a new struct that modifies the storage layout. We can achieve this by adding new fields that are not already defined in the original struct.

// GovernorCountingSimpleStorageUpgrade.sol

pragma solidity ^0.8.0;

struct GovernorCountingSimpleStorage {

uint256 _governors;

uint256[] governors; // New field: governorCountingSimpleStorage

}

constructor() public {

_governors = 10; // Initial value for the governorCountingSimpleStorage array

}

In this updated version, we’ve added a new field governorCountingSimpleStorage that stores an array of uint256 values. This new field is not part of the original struct and has a different name.

Safely Modifying Storage Layout

To safely modify the storage layout, you should always follow these guidelines:

  • Avoid mutating the struct fields directly: Instead, use the new struct to create a temporary array and then update it.

  • Use the upgradable() function: To make your upgrade work, you need to call upgradable() on the original struct before modifying its fields.

Let’s demonstrate this with an example:

// GovernorCountingSimpleStorageUpgrade.sol

pragma solidity ^0.8.0;

struct GovernorCountingSimpleStorage {

uint256 _governors;

}

contract GovernorCountingSimpleStorageUpgrade is UpgradableContract {

function getGovernor() public view returns (uint256) {

return _governors;

}

}

function upgradable() internal pure virtual non payable {

// Call upgradable to reset the governor count

uint256[] memory governors = new uint256[](10);

_governors = 0; // Reset the governor count

// Create a temporary array and update it

GovernorCountingSimpleStorage storage temp = GovernorCountingSimpleStorage(_governors, governors);

// Update the stored value

temp._governors += 1;

// Call upgradable again to persist the changes

}

Conclusion

Upgrading your smart contract using new structs is a powerful way to improve its performance and security. By modifying the storage layout, you can create more flexible contracts that can adapt to changing requirements.

When upgrading, be sure to follow these guidelines:

  • Avoid mutating the struct fields directly.

  • Use the upgradable() function to make your upgrade work.

  • Call upgradable() before and after modifying the original struct’s fields.

By following these best practices, you’ll be able to safely upgrade your smart contract using upgradable structs, unlocking new possibilities for scalability, performance, and security in your Ethereum-based applications.