Proxy Contract

Upgradeable BRC20 Contracts on Brise Chain

What are Upgradeable Contracts?

Smart contracts in EVM are designed to be immutable. Once you create them there is no way to modify them, effectively acting as an unbreakable contract among participants.What do I do if I want to expand the functionality of my contracts? What if there is a bug in the contract that leads to a loss of funds? What if a vulnerability in the Solidity compiler is discovered? Here’s what you’d need to do to fix a bug in a contract you cannot upgrade:

  • Deploy a new version of the contract

  • Manually migrate all state from the old one contract to the new one (which can be very expensive in terms of gas fees!)

  • Update all contracts that interacted with the old contract to use the address of the new one

  • Reach out to all your users and convince them to start using the new deployment (and handle both contracts being used simultaneously, as users are slow to migrate)

There are several approaches that allow us to make some changes to smart contracts.

Separate logic and data

By using this approach, data will be read from a designated data contract directly. This is a rather common approach that is also used outside of Solidity. One of the main disadvantages of this approach is that you cannot change the interface of contracts external to the entire system, and you cannot add or remove functions.

Delegatecall Proxy

delegatecall opcode was implemented in EIP-7. It is possible to delegate execution to other contract, but execution context stays the same. As with delegatecall, the msg.sender will remain that of the caller of the proxy contract. One of the main disadvantages of this approach is that contract code of the proxy will not reflect the state that it stores.

Writing Upgradeable BRC20 Contracts

It’s worth mentioning that these restrictions have their roots in how the Ethereum VM works, and apply to all projects that work with upgradeable contracts, not just OpenZeppelin Upgrades.

Initializers

You can use your Solidity contracts in the OpenZeppelin Upgrades without any modifications, except for their constructors. Due to a requirement of the proxy-based upgradeability system, no constructors can be used in upgradeable contracts. To learn about the reasons behind this restriction, head to Proxies.

This means that, when using a contract with the OpenZeppelin Upgrades, you need to change its constructor into a regular function, typically named initialize, where you run all the setup logic:

OpenZeppelin Upgrades provides an Initializable base contract that has an initializer modifier to prevent a contract from being initialized multiple times: https://github.com/Bitgert/brise-chain/canonical-upgradeable-BRC20/blob/47ed7a710e6e86bdc85f2118bf63fc892e3b7716/contracts/BRC20TokenImplementation.sol#L37

BRC20 contract initializes the token’s name, symbol, and decimals in its constructor. You should not use these contracts in your BRC20 Upgrades contract. , make sure to use the upgradableBRC20implementation that has been modified to use initializers instead of constructors. https://github.com/Bitgert/brise-chain-genesis-contract/blob/42922472b43397fbca9d0c84c7f72fbfaf39efc3/contracts/BRC20_template/BRC20Token.template#L351

Using Truffle

Setting up the Environment

We will begin by creating a new npm project:

Then,

Installation

We will install Truffle.

When running Truffle select the option to “Create a truffle-config.js”

Create upgradeable contract

This example token has a fixed supply that is minted to the deployer of the contract.

https://github.com/Bitgert/brise-chain/canonical-upgradeable-BRC20/blob/master/contracts/BRC20TokenImplementation.sol

Test the contract locally

To test upgradeable contracts we should create unit tests for the implementation contract, along with creating higher level tests for testing interaction via the proxy.

Transfer Control

You can change the proxy owner to another address.

Transfer Owner

You can change the BRC20 token owner to another address.

Deploy on Testnet

Create the following 2_BRC20.js script in the migrations directory.

You can first deploy our contract to a local test (such as ganache-cli) and manually interact with it, then deploy your contract to a public test network.

We can interact with our contract using the Truffle console.

Note: any secrets such as mnemonics or brisescan.com keys should not be committed to version control.

Run truffle migrate with the BRISE testnet to deploy. We can see our implementation contract 'BRC20TokenImplementation' and the 'BRC20TokenFactory' being deployed.

Create a new version of our implementation

After a period of time, we decide that we want to add functionality to our contract. In this guide we will add an whitelist function.

Create the new implementation, BRC20_V2.sol in your contracts directory with the following Solidity code.

Test the upgrade locally

To test our upgrade we should create unit tests for the new implementation contract, along with creating higher level tests for testing interaction via the proxy, checking that state is maintained across upgrades.

We will create unit tests for the new implementation contract. We can add to the unit tests we already created to ensure high coverage.

Create uograde.test.js in your test directory with the following JavaScript.

Last updated