1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
use std::{
collections::HashMap,
fmt::Display,
path::{Path, PathBuf},
};
use serde::{Deserialize, Serialize};
use crate::{Config, Target};
/// Hex representation of a byte array (as used by functions in this
/// module that construct a path from an address)
pub fn hex(bytes: &[u8]) -> String {
hex::encode(bytes)
}
/// Fully qualified contracts name.
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[non_exhaustive]
pub struct ContractFQN {
/// Name of the file where the contract is defined.
pub file: PathBuf,
/// Contract name.
pub name: String,
}
impl Display for ContractFQN {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{}", self.file.display(), self.name)
}
}
impl ContractFQN {
/// Constructor for [`ContractFQN`].
///
/// # Arguments
///
/// - `file` Relative path to constructor
/// - `name` Constructor name
///
/// # Panics
///
/// This function panics if `file` is not relative.
pub fn new(file: PathBuf, name: String) -> Self {
assert!(file.is_relative());
ContractFQN { file, name }
}
}
/// Well-known paths for a Cubist project.
#[non_exhaustive]
#[derive(Clone)]
pub struct Paths {
/// The project directory, i.e., the directory that contains the Cubist config file.
pub project_dir: PathBuf,
/// Build directory: where Cubist generates per-chain projects.
pub build_dir: PathBuf,
/// Deploy directory: where Cubist saves deployment receipts.
pub deploy_dir: PathBuf,
/// Per target chain well-known paths.
pub per_target: HashMap<Target, TargetPaths>,
}
/// Well-known paths for a target chain project (generated by Cubist).
#[non_exhaustive]
#[derive(Clone)]
pub struct TargetPaths {
/// Build root directory of this chain-specific project.
pub build_root: PathBuf,
/// Deploy root directory of this chain-specific project.
pub deploy_root: PathBuf,
/// Manifest file that Cubist generates for various bookkeeping purposes.
pub manifest: PathBuf,
/// Manifest file containing Axelar contract addresses (when using axelar).
pub axelar_manifest: PathBuf,
/// Root directory for all generated contract source files.
pub contracts: PathBuf,
/// Directory where the compiler generates compiled artifacts.
pub compiler_artifacts: PathBuf,
/// Directory where the compiler saves its build manifest.
pub compiler_build_infos: PathBuf,
/// Cache file used by the compiler.
pub compiler_cache: PathBuf,
}
impl Paths {
/// Constructor for [`Paths`].
pub fn new(cfg: &Config) -> Self {
let project_dir = cfg.project_dir();
let build_dir = cfg.build_dir();
assert!(build_dir.is_absolute());
let deploy_dir = cfg.deploy_dir().join(&cfg.current_network_profile);
assert!(deploy_dir.is_absolute());
let per_target = cfg
.targets()
.map(|t| (t, TargetPaths::new(build_dir.join(t), deploy_dir.join(t))))
.collect();
Paths {
project_dir,
build_dir,
deploy_dir,
per_target,
}
}
/// Try to find a [`TargetPaths`] instance associated with a given target.
pub fn try_for_target(&self, t: Target) -> Option<&TargetPaths> {
self.per_target.get(&t)
}
/// Try to find a [`TargetPaths`] instance associated with a given target and PANIC if you can't.
pub fn for_target(&self, t: Target) -> &TargetPaths {
self.try_for_target(t)
.unwrap_or_else(|| panic!("Paths not found for target '{:?}'", t))
}
/// Directory where cubist deployment manifests are written.
///
/// Path: {deploy_dir}/cubist-deploy
pub fn deployment_manifest_dir(&self) -> PathBuf {
self.deploy_dir.with_file_name("cubist-deploy")
}
/// Destination for a contract deployment manifest file generated
/// by Cubist. This manifest file contains contract's deployed
/// address as well as the deployed addresses of all of its shims.
///
/// Path: {deploy_dir}/cubist-deploy/{contract_name}-{address}.json
pub fn for_deployment_manifest(&self, contract: &ContractFQN, address: &[u8; 20]) -> PathBuf {
self.deployment_manifest_dir()
.join(format!("{}-{}.json", contract.name, hex(address)))
}
/// Full path to the file indicating that a bridge has been
/// created for a given contract (as specified by its deployment
/// manifest file)
///
/// Path: {deploy_dir}/cubist-deploy/{contract_name}-{address}.bridged
pub fn notify_contract_bridged(&self, contract: &ContractFQN, address: &[u8; 20]) -> PathBuf {
Self::bridged_signal_for_manifest_file(&self.for_deployment_manifest(contract, address))
}
/// Full path to the file indicating that a bridge has been
/// created for a given deployment manifest file.
///
/// Path: {deploy_dir}/cubist-deploy/{contract_name}-{address}.bridged
pub fn bridged_signal_for_manifest_file(deployment_manifest: &Path) -> PathBuf {
deployment_manifest.with_extension("bridged")
}
}
impl TargetPaths {
/// Constructor for [`TargetPaths`].
///
/// # Panics
///
/// If `build_root` or `deploy_root` is not an absolute path.
pub fn new(build_root: PathBuf, deploy_root: PathBuf) -> Self {
assert!(build_root.is_absolute());
TargetPaths {
contracts: build_root.join("contracts"),
manifest: build_root.join("contracts").join("cubist-manifest.json"),
compiler_artifacts: build_root.join("artifacts"),
compiler_build_infos: build_root.join("build_infos"),
compiler_cache: build_root.join("cache"),
axelar_manifest: deploy_root.join("axelar.json"),
build_root,
deploy_root,
}
}
/// Directory where deployment receipts are stored for a given contract.
///
/// Path: {deploy_dir}/{network_profile}/{target}/{contract_file}/{contract_name}
pub fn deployment_receipts_dir(&self, contract: &ContractFQN) -> PathBuf {
self.deploy_root.join(&contract.file).join(&contract.name)
}
/// Destination for a contract deployment receipt file.
///
/// Path: {deploy_dir}/{network_profile}/{target}/{contract_file}/{contract_name}/{address}.json
pub fn for_deployment_receipt(&self, contract: &ContractFQN, address: &[u8; 20]) -> PathBuf {
self.deployment_receipts_dir(contract)
.join(hex(address))
.with_extension("json")
}
/// Destination for a generated contract source file given the relative path of an original
/// source file (relative to the configured contracts source root directory). Note that we
/// generate one file per source file, no matter how many contracts are defined in it.
///
/// Path: {build_dir}/{target}/{contract_file}
pub fn for_contract(&self, source_relative_path: &Path) -> PathBuf {
assert!(source_relative_path.is_relative());
self.contracts.join(source_relative_path)
}
/// Destination for a bridge file corresponding to a contract source file. Note that we
/// generate one bridge file per source file, no matter how many contracts are defined in it.
///
/// Path: {build_dir}/{target}/{contract_file_stem}.bridge.json
pub fn for_bridge(&self, source_relative_path: &Path) -> PathBuf {
self.for_contract(source_relative_path)
.with_extension("bridge.json")
}
}