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

feat: create and deploy a reference proxy contract for contracts with [proxy] enabled #6069

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ fuel-vm = "0.52.0"
# Dependencies from the `fuels-rs` repository:
fuels-core = "0.64.0"
fuels-accounts = "0.64.0"
fuels = "0.64.0"

# Dependencies from the `forc-wallet` repository:
forc-wallet = "0.8.1"
Expand Down
115 changes: 101 additions & 14 deletions docs/book/src/forc/plugins/forc_client/index.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,76 @@
# `forc-client`

Forc plugin for interacting with a Fuel node.
Forc plugin for interacting with a Fuel node. Since transactions are going to require some gas, you need to sign them with an account that has enough tokens to pay for them.

## Initializing the wallet and adding accounts
We offer multiple ways to sign the transaction:

If you don't have an initialized wallet or any account for your wallet you won't be able to sign transactions.
1. Sign the transaction via your local wallet using forc-client which integrates with our CLI wallet, forc-wallet.
2. Use the default signer to deploy to a local node
3. Use forc-wallet to manually sign transactions, and copy the signed transaction back to forc-client.

To create a wallet you can use `forc wallet new`. It will ask you to choose a password to encrypt your wallet. After the initialization is done you will have your mnemonic phrase.
The easiest and recommended way to interact with deployed networks such as our testnets is option 1, using forc-client to sign your transactions which reads your default forc-wallet vault. For interacting with local node, we recommend using the second option, which leads forc-client to sign transactions with the a private key that comes pre-funded in local environments.

After you have created a wallet, you can derive a new account by running `forc wallet account new`. It will ask your password to decrypt the wallet before deriving an account.
## Option 1: Sign transactions via forc-client using your local forc-wallet vault

## Signing transactions using `forc-wallet` CLI
If you used forc-wallet before, a vault which securely holds your private key is created and written to your file-system in a password encrypted format. forc-client is compatible with forc-wallet such that it can read that vault by asking you your password and use your account to sign transactions.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
If you used forc-wallet before, a vault which securely holds your private key is created and written to your file-system in a password encrypted format. forc-client is compatible with forc-wallet such that it can read that vault by asking you your password and use your account to sign transactions.
If you've used `forc-wallet` before, you'll already have a secure, password-protected vault holding your private key written to your file-system. `forc-client` is compatible with `forc-wallet` such that it can read that vault by asking you your password and use your account to sign transactions.


To submit the transactions created by `forc deploy` or `forc run`, you need to sign them first (unless you are using a client without UTXO validation). To sign a transaction you can use `forc-wallet` CLI. This section is going to walk you through the whole signing process.
Example:

By default `fuel-core` runs without UTXO validation, this allows you to send invalid inputs to emulate different conditions.
```console
> forc deploy

If you want to run `fuel-core` with UTXO validation, you can pass `--utxo-validation` to `fuel-core run`.
Building /Users/test/test-projects/test-contract
Finished release [optimized + fuel] target(s) in 11.39s

To install `forc-wallet` please refer to `forc-wallet`'s [GitHub repo](https://github.com/FuelLabs/forc-wallet#forc-wallet).
Please provide the password of your encrypted wallet vault at "/Users/ceylinbormali/.fuel/wallets/.wallet":
Deploying contract: impl-contract

1. Construct the transaction by using either `forc deploy` or `forc run`. To do so simply run `forc deploy` or `forc run` with your desired parameters. For a list of parameters please refer to the [forc-deploy](./forc_deploy.md) or [forc-run](./forc_run.md) section of the book. Once you run either command you will be asked the address of the wallet you are going to be signing with. After the address is given the transaction will be generated and you will be given a transaction ID. At this point CLI will actively wait for you to insert the signature.
---------------------------------------------------------------------------
Account 0: fuel12pls73y9hnqdqthvduy2x44x48zt8s50pkerf32kq26f2afeqdwq6rj9ar

Asset ID : f8f8b6283d7fa5b672b530cbb84fcccb4ff8dc40f8176ef4544ddb1f1952ad07
Amount : 2197245
---------------------------------------------------------------------------

Please provide the index of account to use for signing: 0
Do you agree to sign this transaction with fuel12pls73y9hnqdqthvduy2x44x48zt8s50pkerf32kq26f2afeqdwq6rj9ar? [y/N]: y


Contract impl-contract Deployed!
Copy link
Member

Choose a reason for hiding this comment

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

Rather than Contract contract_name Deployed! I think it should match the style of the Deploying text.

Deployed contract_name

Where Deployed is in bold-green

image


Network: https://testnet.fuel.network
Contract ID: 0x94b712901f04332682d14c998a5fc5a078ed15321438f46d58d0383200cde43d
Deployed in block 00114d4d
```

As it can be seen from the example, forc-client asks for your password to decrypt the forc-wallet vault, and list your accounts so that you can select the one you want to fund the transaction with.

## Option 2: Using default signer

If you are not interacting with a deployed network, such as testnets, your local fuel-core environment can be structured such that it funds an account by default. Using `--default-signer` flag with forc-client binaries (run, deploy) will instruct forc-client to sign transactions with this pre-funded account. Which makes it a useful command while working against a local node.

Example:

```console
> forc deploy --default-signer

Building /Users/test/test-projects/test-contract
Finished release [optimized + fuel] target(s) in 11.40s
Deploying contract: impl-contract


Contract impl-contract Deployed!

Network: http://127.0.0.1:4000
Contract ID: 0xf9fb08ef18ce226954270d6d4f67677d484b8782a5892b3d436572b405407544
Deployed in block 00000001
```

## Option 3: Manually signing through forc-wallet (Deprecated)

This option is for creating the transaction first, signing it manually and supplying the signed transaction back to forc-client. Since it requires multiple steps, it is more error-prone and not recommended for general use case. Also this will be deprecated soon.

1. Construct the transaction by using either `forc deploy` or `forc run`. To do so simply run `forc deploy --manual-sign` or `forc run --manual-sign` with your desired parameters. For a list of parameters please refer to the [forc-deploy](./forc_deploy.md) or [forc-run](./forc_run.md) section of the book. Once you run either command you will be asked the address of the wallet you are going to be signing with. After the address is given the transaction will be generated and you will be given a transaction ID. At this point CLI will actively wait for you to insert the signature.
2. Take the transaction ID generated in the first step and sign it with `forc wallet sign --account <account_index> tx-id <transaction_id>`. This will generate a signature.
3. Take the signature generated in the second step and provide it to `forc-deploy` (or `forc-run`). Once the signature is provided, the signed transaction will be submitted.

Expand Down Expand Up @@ -56,7 +106,7 @@ By default `--default-signer` flag would sign your transactions with the followi

## Interacting with the testnet

To interact with the latest testnet, use the `--testnet` flag. When this flag is passed, transactions created by `forc-deploy` will be sent to the `beta-4` testnet.
To interact with the latest testnet, use the `--testnet` flag. When this flag is passed, transactions created by `forc-deploy` will be sent to the latest `testnet`.

```sh
forc-deploy --testnet
Expand All @@ -68,10 +118,10 @@ It is also possible to pass the exact node URL while using `forc-deploy` or `for
forc-deploy --node-url https://beta-3.fuel.network
```

Another alternative is the `--target` option, which provides useful aliases to all targets. For example if you want to deploy to `beta-3` you can use:
Another alternative is the `--target` option, which provides useful aliases to all targets. For example if you want to deploy to `beta-5` you can use:

```sh
forc-deploy --target beta-3
forc-deploy --target beta-5
```

Since deploying and running projects on the testnet cost gas, you will need coins to pay for them. You can get some using the [testnet faucet](https://faucet-testnet.fuel.network/).
Expand All @@ -91,3 +141,40 @@ forc-deploy saves the details of each deployment in the `out/deployments` folder
"deployed_block_id": "0x915c6f372252be6bc54bd70df6362dae9bf750ba652bf5582d9b31c7023ca6cf"
}
```

## Proxy Contracts

`forc-deploy` supports deploying proxy contracts automatically if it is enabled in the `Forc.toml` of the contract.

```TOML
[project]
name = "test_contract"
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
license = "Apache-2.0"
implicit-std = false

[proxy]
enabled = true
```

If there is no `address` field present under the proxy table, like the example above, forc automatically creates a proxy contract based on [SRC-14](https://github.com/FuelLabs/sway-standards/blob/master/docs/src/src-14-simple-upgradeable-proxies.md) implementation from [sway-standards](https://github.com/FuelLabs/sway-standards). After generating the proxy contract, the target is set to the current contract, and owner of the proxy is set to the account that is signing the transaction for deployment.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
If there is no `address` field present under the proxy table, like the example above, forc automatically creates a proxy contract based on [SRC-14](https://github.com/FuelLabs/sway-standards/blob/master/docs/src/src-14-simple-upgradeable-proxies.md) implementation from [sway-standards](https://github.com/FuelLabs/sway-standards). After generating the proxy contract, the target is set to the current contract, and owner of the proxy is set to the account that is signing the transaction for deployment.
If there is no `address` field present under the proxy table, like the example above, `forc` will automatically create a proxy contract based on the [SRC-14](https://github.com/FuelLabs/sway-standards/blob/master/docs/src/src-14-simple-upgradeable-proxies.md) implementation from [sway-standards](https://github.com/FuelLabs/sway-standards). After generating and deploying the proxy contract, the target is set to the current contract, and owner of the proxy is set to the account that is signing the transaction for deployment.


This means that if you simply enable proxy in the `Forc.toml`, forc will automatically deploy a proxy contract for you and you do not need to make anything manually. After deploying the proxy contract the `address` of it is added into the `address` field of the proxy table.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
This means that if you simply enable proxy in the `Forc.toml`, forc will automatically deploy a proxy contract for you and you do not need to make anything manually. After deploying the proxy contract the `address` of it is added into the `address` field of the proxy table.
This means that if you simply enable proxy in the `Forc.toml`, forc will automatically deploy a proxy contract for you and you do not need to do anything manually aside from signing the deployment transactions for the proxy contract. After deploying the proxy contract, the its address is added into the `address` field of the proxy table.


If you want to update the target of an [SRC-14](https://github.com/FuelLabs/sway-standards/blob/master/docs/src/src-14-simple-upgradeable-proxies.md) compliant proxy contract rather than deploying a new one, simply add its `address` in the `address` field, like the following example:

```TOML
[project]
name = "test_contract"
authors = ["Fuel Labs <[email protected]>"]
entry = "main.sw"
license = "Apache-2.0"
implicit-std = false

[proxy]
enabled = true
address = "0xd8c4b07a0d1be57b228f4c18ba7bca0c8655eb6e9d695f14080f2cf4fc7cd946" # example proxy contract address
```

If an `address` is present, forc calls into that contract to update its target instead of deploying a new contract. Since a new proxy deployment adds its own `address` into the `Forc.toml` automatically, you can simply enable the proxy once and after the initial deployment, forc will keep updating the target accordingly for each new deployment of the same contract.
Copy link
Member

@sdankel sdankel Jun 29, 2024

Choose a reason for hiding this comment

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

Suggested change
If an `address` is present, forc calls into that contract to update its target instead of deploying a new contract. Since a new proxy deployment adds its own `address` into the `Forc.toml` automatically, you can simply enable the proxy once and after the initial deployment, forc will keep updating the target accordingly for each new deployment of the same contract.
If an `address` is present, `forc` calls into that contract to update its `target` instead of deploying a new contract. Since a new proxy deployment adds its own `address` into the `Forc.toml` automatically, you can simply enable the proxy once and after the initial deployment, `forc` will keep updating the target accordingly for each new deployment of the same contract.

17 changes: 17 additions & 0 deletions forc-pkg/src/manifest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ pub struct PackageManifest {
pub build_target: Option<BTreeMap<String, BuildTarget>>,
build_profile: Option<BTreeMap<String, BuildProfile>>,
pub contract_dependencies: Option<BTreeMap<String, ContractDependency>>,
pub proxy: Option<Proxy>,
}

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -273,6 +274,17 @@ pub struct DependencyDetails {
pub(crate) ipfs: Option<String>,
}

/// Describes the details around proxy contract.
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub struct Proxy {
pub enabled: bool,
/// Points to the proxy contract to be updated with the new contract id.
/// If there is a value for this field, forc will try to update the proxy contract's storage
/// field such that it points to current contract's deployed instance.
pub address: Option<String>,
}

impl DependencyDetails {
/// Checks if dependency details reserved for a specific dependency type used without the main
/// detail for that type.
Expand Down Expand Up @@ -650,6 +662,11 @@ impl PackageManifest {
.and_then(|patches| patches.get(patch_name))
}

/// Retrieve the proxy table for the package.
pub fn proxy(&self) -> Option<&Proxy> {
self.proxy.as_ref()
}

/// Check for the `core` and `std` packages under `[dependencies]`. If both are missing, add
/// `std` implicitly.
///
Expand Down
6 changes: 5 additions & 1 deletion forc-pkg/src/pkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ pub struct MinifyOpts {
type ContractIdConst = String;

/// The set of options provided to the `build` functions.
#[derive(Default)]
#[derive(Default, Clone)]
pub struct BuildOpts {
pub pkg: PkgOpts,
pub print: PrintOpts,
Expand Down Expand Up @@ -315,6 +315,7 @@ pub struct BuildOpts {
}

/// The set of options to filter type of projects to build in a workspace.
#[derive(Clone)]
pub struct MemberFilter {
pub build_contracts: bool,
pub build_scripts: bool,
Expand Down Expand Up @@ -2132,6 +2133,9 @@ pub fn build_with_options(build_options: &BuildOpts) -> Result<Built> {
.as_ref()
.map_or_else(|| current_dir, PathBuf::from);

let building = ansi_term::Colour::Green.bold().paint("Building");
info!(" {} {}", building, path.display());

let build_plan = BuildPlan::from_build_opts(build_options)?;
let graph = build_plan.graph();
let manifest_map = build_plan.manifest_map();
Expand Down
3 changes: 3 additions & 0 deletions forc-plugins/forc-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ anyhow = "1"
async-trait = "0.1.58"
chrono = { version = "0.4", default-features = false, features = ["std"] }
clap = { version = "4.5.4", features = ["derive", "env"] }
colored = "2.0.0"
devault = "0.1"
forc = { version = "0.61.0", path = "../../forc" }
forc-pkg = { version = "0.61.0", path = "../../forc-pkg" }
Expand All @@ -26,6 +27,7 @@ fuel-core-types = { workspace = true }
fuel-crypto = { workspace = true }
fuel-tx = { workspace = true, features = ["test-helpers"] }
fuel-vm = { workspace = true }
fuels = { workspace = true }
fuels-accounts = { workspace = true }
fuels-core = { workspace = true }
futures = "0.3"
Expand All @@ -38,6 +40,7 @@ sway-core = { version = "0.61.0", path = "../../sway-core" }
sway-types = { version = "0.61.0", path = "../../sway-types" }
sway-utils = { version = "0.61.0", path = "../../sway-utils" }
tokio = { version = "1.8", features = ["macros", "rt-multi-thread", "process"] }
toml_edit = "0.21.1"
tracing = "0.1"

[[bin]]
Expand Down
3 changes: 0 additions & 3 deletions forc-plugins/forc-client/src/cmd/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,6 @@ pub struct Command {
pub unsigned: bool,
/// Set the key to be used for signing.
pub signing_key: Option<SecretKey>,
/// Sign the deployment transaction manually.
Copy link
Member

Choose a reason for hiding this comment

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

Why is this removed?

#[clap(long)]
pub manual_signing: bool,
/// Override storage slot initialization.
///
/// By default, storage slots are initialized with the values defined in the storage block in
Expand Down
3 changes: 0 additions & 3 deletions forc-plugins/forc-client/src/cmd/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,6 @@ pub struct Command {
pub unsigned: bool,
/// Set the key to be used for signing.
pub signing_key: Option<SecretKey>,
/// Sign the deployment transaction manually.
#[clap(long)]
pub manual_signing: bool,
/// Arguments to pass into main function with forc run.
#[clap(long)]
pub args: Option<Vec<String>>,
Expand Down
Loading
Loading