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

docs: proxy contract cookbook #3253

Draft
wants to merge 23 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
73b1bb1
docs: cookbook for manually deploying and upgrading a proxy
danielbate Oct 5, 2024
191054d
Merge branch 'master' of https://github.com/FuelLabs/fuels-ts into db…
danielbate Oct 9, 2024
0e795f7
chore: add missing test groups
danielbate Oct 9, 2024
36c0e24
chore: changeset
danielbate Oct 9, 2024
238fd5b
chore: update changeset
danielbate Oct 9, 2024
5459232
chore: forc format
danielbate Oct 9, 2024
8a79ee2
Merge branch 'db/chore/manual-proxy-contracts' of https://github.com/…
danielbate Oct 9, 2024
360b704
chore: update doc
danielbate Oct 9, 2024
d7c0b0c
chore: update doc
danielbate Oct 9, 2024
445c93c
chore: update doc
danielbate Oct 9, 2024
914bc01
Merge branch 'master' into db/chore/manual-proxy-contracts
danielbate Oct 9, 2024
67d9433
Merge branch 'master' into db/chore/manual-proxy-contracts
Torres-ssf Oct 11, 2024
d0ff3df
Merge branch 'master' of https://github.com/FuelLabs/fuels-ts into db…
danielbate Oct 15, 2024
8f9c67c
docs: use src 14 commit hash for doc
danielbate Oct 15, 2024
38a125f
chore: migrate to v2 snippet
danielbate Oct 17, 2024
a17bb8e
chore: restore v1 snippets files
danielbate Oct 17, 2024
dea7aea
chore: fix snippet path
danielbate Oct 17, 2024
93b0472
chore: fix test region
danielbate Oct 17, 2024
2290e74
multilning doc comments
Torres-ssf Oct 17, 2024
b966a26
moving snippet to another place
Torres-ssf Oct 17, 2024
e1c676a
Merge branch 'master' into db/chore/manual-proxy-contracts
Torres-ssf Oct 21, 2024
4c5f549
Merge branch 'master' into db/chore/manual-proxy-contracts
Torres-ssf Oct 22, 2024
3eccadc
Merge branch 'master' into db/chore/manual-proxy-contracts
Torres-ssf Oct 29, 2024
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: 5 additions & 0 deletions .changeset/friendly-cooks-appear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@internal/benchmarks": patch
danielbate marked this conversation as resolved.
Show resolved Hide resolved
---

docs: proxy contract cookbook
105 changes: 105 additions & 0 deletions apps/docs-snippets/src/guide/contracts/proxy-contracts.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { Provider, Wallet } from 'fuels';
import { launchTestNode } from 'fuels/test-utils';

import {
Counter,
CounterFactory,
CounterV2,
CounterV2Factory,
ProxyContract,
ProxyContractFactory,
} from '../../../test/typegen';

/**
* @group node
* @group browser
*/
describe('Proxy Contracts', () => {
it('deploys and upgrades a contracts using a proxy', async () => {
using launched = await launchTestNode();

const {
provider: testProvider,
wallets: [testWallet],
} = launched;
const providerUrl = testProvider.url;
const WALLET_PVT_KEY = testWallet.privateKey;

// #region proxy-2
// #import { Provider, Wallet };
// #context import { WALLET_PVT_KEY } from 'path/to/my/env/file';
// #context import { CounterFactory, Counter, ProxyFactory, CounterV2Factory } from 'path/to/typegen/outputs';

const provider = await Provider.create(providerUrl);
const wallet = Wallet.fromPrivateKey(WALLET_PVT_KEY, provider);

const counterContractFactory = new CounterFactory(wallet);
const { waitForResult: waitForCounterContract } = await counterContractFactory.deploy();
const { contract: counterContract } = await waitForCounterContract();
// #endregion proxy-2

// #region proxy-3
// It is important to pass the pass all storage slots to the proxy in order to initialize the storage slots.
danielbate marked this conversation as resolved.
Show resolved Hide resolved
const storageSlots = Counter.storageSlots.concat(ProxyContract.storageSlots);

// These configurables are specific to our recommended SRC14 compliant contract. They must be passed on deploy
// and then `initialize_proxy` must be called to setup the proxy contract.
const configurableConstants = {
INITIAL_TARGET: { bits: counterContract.id.toB256() },
INITIAL_OWNER: { Initialized: { Address: { bits: wallet.address.toB256() } } },
};

const proxyContractFactory = new ProxyContractFactory(wallet);
const { waitForResult: waitForProxyContract } = await proxyContractFactory.deploy({
storageSlots,
configurableConstants,
});
const { contract: proxyContract } = await waitForProxyContract();

const { waitForResult: waitForProxyInit } = await proxyContract.functions
.initialize_proxy()
.call();
await waitForProxyInit();
// #endregion proxy-3

// #region proxy-4
// Make sure to use only the contract ID of the proxy when instantiating the contract
// as this will remain static even with future upgrades.
const initialContract = new Counter(proxyContract.id, wallet);

const { waitForResult: waitForIncrement } = await initialContract.functions
.increment_counter(1)
.call();
await waitForIncrement();

const { value: count } = await initialContract.functions.get_count().get();
// #endregion proxy-4

// #region proxy-6
const { waitForResult: waitForCounterContractV2 } = await CounterV2Factory.deploy(wallet);
const { contract: counterContractV2 } = await waitForCounterContractV2();

const { waitForResult: waitForUpdateTarget } = await proxyContract.functions
.set_proxy_target({ bits: counterContractV2.id.toB256() })
.call();

await waitForUpdateTarget();
// #endregion proxy-6

// #region proxy-7
// Again, we are instantiating the contract with the same proxy ID but using a new contract instance.
const upgradedContract = new CounterV2(proxyContract.id, wallet);
const { waitForResult: waitForSecondIncrement } = await upgradedContract.functions
.increment_counter(1)
.call();
await waitForSecondIncrement();

const { value: increments } = await upgradedContract.functions.get_increments().get();
const { value: secondCount } = await upgradedContract.functions.get_count().get();
// #endregion proxy-7

expect(count.toNumber()).toBe(1);
expect(secondCount.toNumber()).toBe(2);
expect(increments.toNumber()).toBe(1);
});
});
2 changes: 2 additions & 0 deletions apps/docs-snippets/test/fixtures/forc-projects/Forc.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[workspace]
members = [
"counter",
"counter-v2",
"echo-enum",
"liquidity-pool",
"log-values",
Expand Down Expand Up @@ -34,4 +35,5 @@ members = [
"input-output-types",
"bytecode-input",
"configurable-pin",
"proxy-contract",
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[project]
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
license = "Apache-2.0"
name = "counter-v2"

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// #region proxy-5
contract;

abi Counter {
#[storage(read)]
fn get_count() -> u64;

#[storage(read)]
fn get_increments() -> u64;

#[storage(write, read)]
fn increment_counter(amount: u64) -> u64;

#[storage(write, read)]
fn decrement_counter(amount: u64) -> u64;
}

storage {
counter: u64 = 0,
increments: u64 = 0,
}

impl Counter for Contract {
#[storage(read)]
fn get_count() -> u64 {
storage.counter.try_read().unwrap_or(0)
}

#[storage(read)]
fn get_increments() -> u64 {
storage.increments.try_read().unwrap_or(0)
}

#[storage(write, read)]
fn increment_counter(amount: u64) -> u64 {
let current = storage.counter.try_read().unwrap_or(0);
storage.counter.write(current + amount);

let current_iteration: u64 = storage.increments.try_read().unwrap_or(0);
storage.increments.write(current_iteration + 1);

storage.counter.read()
}

#[storage(write, read)]
fn decrement_counter(amount: u64) -> u64 {
let current = storage.counter.read();
storage.counter.write(current - amount);
storage.counter.read()
}
}
// #endregion proxy-5
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// #region proxy-1
contract;

abi Counter {
Expand All @@ -18,20 +19,21 @@ storage {
impl Counter for Contract {
#[storage(read)]
fn get_count() -> u64 {
storage.counter.read()
storage.counter.try_read().unwrap_or(0)
}

#[storage(write, read)]
fn increment_counter(amount: u64) -> u64 {
let current = storage.counter.read();
let current = storage.counter.try_read().unwrap_or(0);
storage.counter.write(current + amount);
storage.counter.read()
}

#[storage(write, read)]
fn decrement_counter(amount: u64) -> u64 {
let current = storage.counter.read();
let current = storage.counter.try_read().unwrap_or(0);
storage.counter.write(current - amount);
storage.counter.read()
}
}
// #endregion proxy-1
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[project]
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
license = "Apache-2.0"
name = "proxy-contract"

[dependencies]
standards = { git = "https://github.com/FuelLabs/sway-standards", tag = "v0.6.0" }
sway_libs = { git = "https://github.com/FuelLabs/sway-libs", tag = "v0.24.0" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
library;

use standards::src5::State;

abi OwnedProxy {
#[storage(write)]
fn initialize_proxy();

#[storage(write)]
fn set_proxy_owner(new_proxy_owner: State);
}
Loading
Loading