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

Add proxy_owner() function to the SRC-14 standard #107

Merged
merged 4 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
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
5 changes: 2 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Description of the upcoming release here.

### Added

- Something new here 1
- [#107](https://github.com/FuelLabs/sway-standards/pull/107) Adds the `proxy_owner()` function to the SRC-14 standard.
- Something new here 2

### Changed
Expand All @@ -21,8 +21,7 @@ Description of the upcoming release here.

### Fixed

- Some fix here 1
- Some fix here 2
- [#107](https://github.com/FuelLabs/sway-standards/pull/107) resolves the conflict when SRC-5's `owner()` function is used in both the proxy and target contract in the SRC-14 standard.

#### Breaking

Expand Down
19 changes: 16 additions & 3 deletions docs/src/src-14-simple-upgradeable-proxies.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ The following functions MUST be implemented by a proxy contract to follow the SR
If a valid call is made to this function it MUST change the target address of the proxy to `new_target`.
This method SHOULD implement access controls such that the target can only be changed by a user that possesses the right permissions (typically the proxy owner).

### Optional Public Functions

The following functions are RECOMMENDED to be implemented by a proxy contract to follow the SRC-14 standard:

#### `fn proxy_owner() -> State;`

This function SHALL return the current state of ownership for the proxy contract where the `State` is either `Uninitialized`, `Initialized`, or `Revoked`. `State` is defined in the [SRC-5; Ownership Standard](./src-5-ownership.md).

## Rationale

This standard is meant to provide simple upgradeability, it is deliberately minimalistic and does not provide the level of functionality of diamonds.
Expand All @@ -57,15 +65,20 @@ As it is the first attempt to standardize proxy implementation, we do not consid
## Security Considerations

Permissioning proxy target changes is the primary consideration here.
This standard is not opinionated about means of achieving this, use of [SRC-5](./src-5-ownership.md) is recommended.
Use of the [SRC-5; Ownership Standard](./src-5-ownership.md) is discouraged. If both the target and proxy contracts implement the [SRC-5](./src-5-ownership.md) standard, the `owner()` function in the target contract is unreachable through the proxy contract. Use of the `proxy_owner()` function in the proxy contract should be used instead.

## Example ABI

```sway
abi SRC14 {
#[storage(write)]
#[storage(read, write)]
IGI-111 marked this conversation as resolved.
Show resolved Hide resolved
fn set_proxy_target(new_target: ContractId);
}

abi SRC14Extension {
#[storage(read)]
fn proxy_owner() -> State;
}
```

## Example Implementation
Expand All @@ -80,7 +93,7 @@ Example of a minimal SRC-14 implementation with no access control.

### Owned Proxy

Example of a SRC-14 implementation that also implements [SRC-5](./src-5-ownership.md).
Example of a SRC-14 implementation that also implements `proxy_owner()`.

```sway
{{#include ../../examples/src14-simple-proxy/owned/src/owned.sw}}
Expand Down
5 changes: 2 additions & 3 deletions examples/src14-simple-proxy/minimal/src/minimal.sw
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
contract;

use std::execution::run_external;
use std::constants::ZERO_B256;
use standards::src14::SRC14;

// use sha256("storage_SRC14") as base to avoid collisions
#[namespace(SRC14)]
storage {
// target is at sha256("storage_SRC14_0")
target: ContractId = ContractId::from(ZERO_B256),
target: ContractId = ContractId::zero(),
}

impl SRC14 for Contract {
#[storage(write)]
#[storage(read, write)]
fn set_proxy_target(new_target: ContractId) {
storage.target.write(new_target);
}
Expand Down
25 changes: 12 additions & 13 deletions examples/src14-simple-proxy/owned/src/owned.sw
Original file line number Diff line number Diff line change
@@ -1,36 +1,35 @@
contract;

use std::execution::run_external;
use std::constants::ZERO_B256;
use standards::src5::{AccessError, SRC5, State};
use standards::src14::SRC14;
use standards::src5::{AccessError, State};
use standards::src14::{SRC14, SRC14Extension};

/// The owner of this contract at deployment.
const INITIAL_OWNER: Identity = Identity::Address(Address::from(ZERO_B256));
const INITIAL_OWNER: Identity = Identity::Address(Address::zero());

// use sha256("storage_SRC14") as base to avoid collisions
#[namespace(SRC14)]
storage {
// target is at sha256("storage_SRC14_0")
target: ContractId = ContractId::from(ZERO_B256),
target: ContractId = ContractId::zero(),
owner: State = State::Initialized(INITIAL_OWNER),
}

impl SRC5 for Contract {
#[storage(read)]
fn owner() -> State {
storage.owner.read()
}
}

impl SRC14 for Contract {
#[storage(write)]
#[storage(read, write)]
fn set_proxy_target(new_target: ContractId) {
only_owner();
storage.target.write(new_target);
}
}

impl SRC14Extension for Contract {
#[storage(read)]
fn proxy_owner() -> State {
storage.owner.read()
}
}

#[fallback]
#[storage(read)]
fn fallback() {
Expand Down
26 changes: 25 additions & 1 deletion standards/src/src14.sw
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
library;

use ::src5::State;

abi SRC14 {
/// Change the target address of a proxy contract.
///
Expand All @@ -18,10 +20,32 @@ abi SRC14 {
/// contract_abi.set_proxy_target(new_target);
/// }
/// ```
#[storage(write)]
#[storage(read, write)]
fn set_proxy_target(new_target: ContractId);
}

abi SRC14Extension {
/// Returns the owner of the proxy contract.
///
/// # Returns
///
/// * [State] - Represents the state of ownership for this contract.
///
/// # Examples
///
/// ```sway
/// fn foo() {
/// match owner() {
/// State::Uninitalized => log("The ownership is uninitalized"),
/// State::Initialized(owner) => log("The ownership is initalized"),
/// State::Revoked => log("The ownership is revoked"),
/// }
/// }
/// ```
#[storage(read)]
fn proxy_owner() -> State;
}

/// The standard storage slot to store proxy target address.
///
/// Value is `sha256("storage_SRC14_0")`.
Expand Down
Loading