Skip to content

Commit

Permalink
Merge 4be6c95 into 450d13e
Browse files Browse the repository at this point in the history
  • Loading branch information
kayagokalp committed May 28, 2024
2 parents 450d13e + 4be6c95 commit 946beec
Show file tree
Hide file tree
Showing 7 changed files with 257 additions and 35 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

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

17 changes: 17 additions & 0 deletions forc-pkg/src/manifest/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,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 @@ -242,6 +243,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 @@ -619,6 +631,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 @@ -2126,6 +2127,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
1 change: 1 addition & 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.60.0", path = "../../forc" }
forc-pkg = { version = "0.60.0", path = "../../forc-pkg" }
Expand Down
72 changes: 68 additions & 4 deletions forc-plugins/forc-client/src/op/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,35 @@ use crate::{
util::{
gas::get_estimated_max_fee,
node_url::get_node_url,
pkg::built_pkgs,
tx::{TransactionBuilderExt, WalletSelectionMode, TX_SUBMIT_TIMEOUT_MS},
pkg::{built_pkgs, create_proxy_contract},
tx::{
check_and_create_wallet_at_default_path, first_user_account, TransactionBuilderExt,
WalletSelectionMode, TX_SUBMIT_TIMEOUT_MS,
},
},
};
use anyhow::{bail, Context, Result};
use colored::Colorize;
use forc_pkg::manifest::GenericManifestFile;
use forc_pkg::{self as pkg, PackageManifestFile};
use forc_tracing::println_warning;
use forc_util::default_output_directory;
use forc_wallet::utils::default_wallet_path;
use fuel_core_client::client::types::TransactionStatus;
use fuel_core_client::client::FuelClient;
use fuel_crypto::fuel_types::ChainId;
use fuel_tx::{Output, Salt, TransactionBuilder};
use fuel_vm::prelude::*;
use fuels_accounts::provider::Provider;
use fuels_core::types::bech32::Bech32Address;
use futures::FutureExt;
use pkg::{manifest::build_profile::ExperimentalFlags, BuildProfile, BuiltPackage};
use pkg::{manifest::build_profile::ExperimentalFlags, BuildOpts, BuildProfile, BuiltPackage};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use std::{
collections::BTreeMap,
path::{Path, PathBuf},
};
use std::{sync::Arc, time::Duration};
use sway_core::language::parsed::TreeType;
use sway_core::BuildTarget;
use tracing::info;
Expand Down Expand Up @@ -113,6 +119,27 @@ fn validate_and_parse_salts<'a>(
Ok(contract_salt_map)
}

/// Build a proxy contract owned by the deployer.
/// First creates the contract project at the current dir. The source code for the proxy contract is updated
/// with 'owner_adr'.
pub fn build_proxy_contract(
owner_addr: &str,
impl_contract_id: &str,
pkg_name: &str,
build_opts: &BuildOpts,
) -> Result<Arc<BuiltPackage>> {
let proxy_contract_dir = create_proxy_contract(owner_addr, impl_contract_id, pkg_name)?;
let mut build_opts = build_opts.clone();
let proxy_contract_dir_str = format!("{}", proxy_contract_dir.clone().display());
build_opts.pkg.path = Some(proxy_contract_dir_str);
let built_pkgs = built_pkgs(&proxy_contract_dir, &build_opts)?;
let built_pkg = built_pkgs
.first()
.cloned()
.ok_or_else(|| anyhow::anyhow!("could not get proxy contract"))?;
Ok(built_pkg)
}

/// Builds and deploys contract(s). If the given path corresponds to a workspace, all deployable members
/// will be built and deployed.
///
Expand Down Expand Up @@ -176,6 +203,7 @@ pub async fn deploy(command: cmd::Deploy) -> Result<Vec<DeployedContract>> {
None
};

let mut owner_account_address: Option<Bech32Address> = None;
for pkg in built_pkgs {
if pkg
.descriptor
Expand All @@ -197,8 +225,44 @@ pub async fn deploy(command: cmd::Deploy) -> Result<Vec<DeployedContract>> {
bail!("Both `--salt` and `--default-salt` were specified: must choose one")
}
};
println!(
" {} contract: {}",
"Deploying".bold().green(),
&pkg.descriptor.name
);
let contract_id =
deploy_pkg(&command, &pkg.descriptor.manifest_file, &pkg, salt).await?;
let proxy = &pkg.descriptor.manifest_file.proxy();
if let Some(proxy) = proxy {
if proxy.enabled {
println!(" {} proxy contract", "Creating".bold().green());
let user_addr = if let Some(owner_address) = &owner_account_address {
anyhow::Ok(owner_address.clone())
} else {
// Check if the wallet exists and if not create it at the default path.
let default_path = default_wallet_path();
check_and_create_wallet_at_default_path(&default_path)?;
let account = first_user_account(&default_wallet_path())?;
owner_account_address = Some(account.clone());
Ok(account)
}?;
let user_addr_hex: fuels_core::types::Address = user_addr.into();
let user_addr = format!("0x{}", user_addr_hex);
let pkg_name = pkg.descriptor.manifest_file.project_name();
let contract_addr = format!("0x{}", contract_id.id);
let proxy_contract =
build_proxy_contract(&user_addr, &contract_addr, pkg_name, &build_opts)?;
println!(" {} proxy contract", "Deploying".bold().green());
deploy_pkg(
&command,
&pkg.descriptor.manifest_file,
&proxy_contract,
salt,
)
.await?;
}
}

contract_ids.push(contract_id);
}
}
Expand Down
110 changes: 110 additions & 0 deletions forc-plugins/forc-client/src/util/pkg.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,119 @@
use anyhow::Result;
use forc_pkg::manifest::GenericManifestFile;
use forc_pkg::{self as pkg, manifest::ManifestFile, BuildOpts, BuildPlan};
use forc_util::user_forc_directory;
use pkg::{build_with_options, BuiltPackage};
use std::io::Write;
use std::path::PathBuf;
use std::{collections::HashMap, path::Path, sync::Arc};
use sway_utils::{MAIN_ENTRY, MANIFEST_FILE_NAME, SRC_DIR};

/// The name of the folder that forc generated proxy contract project will reside at.
pub const PROXY_CONTRACT_FOLDER_NAME: &str = ".generated_proxy_contracts";
/// Forc.toml for the default proxy contract that 'generate_proxy_contract_src()' returns.
pub const PROXY_CONTRACT_FORC_TOML: &str = r#"
[project]
authors = ["Fuel Labs <contact@fuel.sh>"]
entry = "main.sw"
license = "Apache-2.0"
name = "proxy_contract"
[dependencies]
standards = { git = "https://github.com/FuelLabs/sway-standards/" }
"#;

/// Generates source code for proxy contract owner set to the given 'addr'.
pub(crate) fn generate_proxy_contract_src(addr: &str, impl_contract_id: &str) -> String {
format!(
r#"
contract;
use std::execution::run_external;
use standards::src5::{{AccessError, SRC5, State}};
use standards::src14::SRC14;
/// The owner of this contract at deployment.
const INITIAL_OWNER: Identity = Identity::Address(Address::from({addr}));
// use sha256("storage_SRC14") as base to avoid collisions
#[namespace(SRC14)]
storage {{
// target is at sha256("storage_SRC14_0")
target: ContractId = ContractId::from({impl_contract_id}),
owner: State = State::Initialized(INITIAL_OWNER),
}}
impl SRC5 for Contract {{
#[storage(read)]
fn owner() -> State {{
storage.owner.read()
}}
}}
impl SRC14 for Contract {{
#[storage(write)]
fn set_proxy_target(new_target: ContractId) {{
only_owner();
storage.target.write(new_target);
}}
}}
#[fallback]
#[storage(read)]
fn fallback() {{
// pass through any other method call to the target
run_external(storage.target.read())
}}
#[storage(read)]
fn only_owner() {{
require(
storage
.owner
.read() == State::Initialized(msg_sender().unwrap()),
AccessError::NotOwner,
);
}}
"#
)
}

/// Creates a proxy contract project at the given path, adds a forc.toml and source file.
pub(crate) fn create_proxy_contract(
addr: &str,
impl_contract_id: &str,
pkg_name: &str,
) -> Result<PathBuf> {
// Create the proxy contract folder.
let proxy_contract_dir = user_forc_directory()
.join(PROXY_CONTRACT_FOLDER_NAME)
.join(pkg_name);
std::fs::create_dir_all(&proxy_contract_dir)?;

// Create the Forc.toml
let mut f = std::fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(proxy_contract_dir.join(MANIFEST_FILE_NAME))?;
write!(f, "{}", PROXY_CONTRACT_FORC_TOML)?;

// Create the src folder
std::fs::create_dir_all(proxy_contract_dir.join(SRC_DIR))?;

// Create main.sw
let mut f = std::fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(proxy_contract_dir.join(SRC_DIR).join(MAIN_ENTRY))?;

let contract_str = generate_proxy_contract_src(addr, impl_contract_id);
write!(f, "{}", contract_str)?;
Ok(proxy_contract_dir)
}
pub(crate) fn built_pkgs(path: &Path, build_opts: &BuildOpts) -> Result<Vec<Arc<BuiltPackage>>> {
let manifest_file = ManifestFile::from_dir(path)?;
let lock_path = manifest_file.lock_path()?;
Expand Down
Loading

0 comments on commit 946beec

Please sign in to comment.