Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgradeable contracts using proxies. #39

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions rfcs/0013-contract-proxies.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
- Feature Name: `contract_proxies`
- Start Date: 2024-05-21
- RFC PR: [FuelLabs/sway-rfcs#0013](https://github.com/FuelLabs/sway-rfcs/pull/39)

# Summary

[summary]: #summary

This proposal introduces a feature to simplify the creation of upgradeable contracts in Sway by enhancing the Forc.toml manifest file.

# Motivation

[motivation]: #motivation

Sway developers want to upgrade their smart contract code post-deployment and need tooling support to facilitate this process. They seek a straightforward method, such as adding a configuration to the Forc.toml file, to enable and manage contract upgradeability seamlessly.

# Guide-level explanation

[guide-level-explanation]: #guide-level-explanation

### Use of the Forc.toml for an upgradeable contact.
This proposal enhances the Forc.toml file to simplify creating upgradeable contracts. A new `[proxy]` table indicates to `forc-deploy` that this contract is upgradable. During the initial deployment, `forc-deploy` stores the proxy contract's address in the `address` field, streamlining future updates and ensuring the address is stored safely with the project.

```toml
[project]
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
license = "Apache-2.0"
name = "implementation"

[proxy]
enabled = true # Undecided if we really need this flag yet.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think removing this makes sense, as absence or existence of [proxy] table can be used to deduce this

Copy link
Member Author

@JoshuaBatty JoshuaBatty May 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess setting this to false would allow you to deploy the contract for testing purposes before generating and committing to the proxy contract.

Users could always comment out [proxy] but this might make that more explicit?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm you mean to test the implementation contract right? if so sounds good to me. Another option to infer the enabled = true if the table is there, and for testing users can use enabled = false or we can explicitly require the field to be there.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fwiw I think having them comment it out for that behavior (or run a specific flag) looks more correct.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if address = # is valid TOML.

Some blockchains make things like this explicit by pointing to well-known values like addresss = 0x0000000000000000000000000000000000000000. I personally don't like this because you have to count the number of 0 for no good reason and is also error-prone.

Maybe an enum here or a shorter value as a placeholder could make sense:

address = 0x0
address = ""

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the proxy will only ever be a single value, would it make sense to move this to the project table?

address = # this field gets automatically added and filled in during deployment
```

## Workflow

The deployment process takes care of generating proxy bytecode, deploying contracts, and updating addresses automatically. The workflow from forc’s perspective could look something like the following:

1. **Forc Deploy**:
- Forc deploy checks if the **`proxy.enabled`** field is set to **`true`** in **`Forc.toml`**.
- If enabled, it checks the **`proxy.address`** field.
- If **`proxy.address`** is empty, it deploys a new proxy contract, updates the **`proxy.address`** field with the deployed proxy's address, and deploys the implementation contract.
- If **`proxy.address`** is not empty, it skips deploying a new proxy and proceeds to update the existing proxy's storage to point to the new implementation contract.
2. **Initial Deployment**:
- Generate the proxy contract bytecode.
- Deploy the implementation contract.
- Deploy the proxy contract.
- Update the proxy contract's storage to point to the implementation contract's address.
- Store the proxy contract's address in **`Forc.toml`**.
3. **Subsequent Deployments**:
- Check if a proxy address already exists.
- Check storage layout consistency between the proxy and the new implementation contract.
- Check for breaking changes in the ABI.
- If storage layout is consistent and no breaking changes are found in the ABI, deploy the new implementation contract.
- Update the existing proxy contract to point to the new implementation contract's address.

```mermaid
graph TD
A[Check Forc.toml] --> B{Proxy Enabled?}
B -- No --> C[Deploy Main Contract Normally]
B -- Yes --> D{Proxy Address Exists?}
D -- No --> E[Generate Proxy Contract Bytecode]
E --> F[Deploy Implementation Contract]
F --> G[Deploy Proxy Contract]
G --> H[Update Proxy Storage with Implementation Address]
H --> I[Store Proxy Address in Forc.toml]
D -- Yes --> J[Check Storage Layout Consistency]
J -- Inconsistent --> X[Abort Deployment]
J -- Consistent --> K[Check for ABI Breaking Changes]
K -- Breaking Changes --> Y[Abort Deployment]
K -- No Breaking Changes --> L[Deploy New Implementation Contract]
L --> M[Update Existing Proxy Storage with New Implementation Address]
```

### **Usability Features**

- **Simplicity**: The system is simple for developers to use. They only need to enable the proxy in **`Forc.toml`**, and the rest is handled automatically.
- **Automation**: Automatically managing the proxy address in **`Forc.toml`** reduces the risk of errors and ensures consistency.
- **Deployment Flexibility**: The system handles both initial deployments and subsequent updates seamlessly.

### **Security Considerations**

- **Address Verification**: Ensure the address stored in **`Forc.toml`** is accurate and not tampered with. Use checksums or other verification methods if needed.
- **State Management**: Ensure the proxy contract's storage is correctly updated to point to the new implementation contract to prevent any potential issues with state consistency.
- **Access Control**: Implement proper access control in the proxy contract to restrict who can update the implementation address to prevent unauthorized changes.
- **Storage Layout Consistency**: Ensure that the storage layout between the proxy and the implementation contracts is identical.

### **Example Contract Syntax**
@IGI-111 to fill out.
Copy link
Member

@sdankel sdankel Aug 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we fill this in?



### **Compiler Features**
* A function that generates the smallest possible byte code for the proxy contract that can be used by `forc-deploy`
Loading