stylus_tools/core/project/
contract.rs1use std::path::PathBuf;
5
6use alloy::{
7 primitives::{Address, TxHash, B256, U256},
8 providers::{Provider, WalletProvider},
9};
10use cargo_metadata::{semver::Version, Package, TargetKind};
11
12use crate::{
13 core::{
14 build::{build_contract, BuildConfig, BuildError},
15 check::{check_contract, CheckConfig, CheckError},
16 deployment::{deploy, DeploymentConfig, DeploymentError},
17 manifest,
18 reflection::ReflectionConfig,
19 verification::{self, VerificationStatus},
20 },
21 error::decode_contract_error,
22 ops,
23 precompiles::{self, ArbWasm::ArbWasmErrors},
24 utils::toolchain::get_toolchain_channel,
25};
26
27#[derive(Debug)]
28pub struct Contract {
29 pub package: Package,
31
32 stable: bool,
34
35 name: String,
37 version: Version,
38}
39
40impl Contract {
41 pub fn is_contract(package: &Package) -> bool {
42 if let Some(stylus_manifest_path) = package
43 .manifest_path
44 .parent()
45 .map(|p| p.join(manifest::FILENAME))
46 {
47 stylus_manifest_path.exists()
48 } else {
49 false
50 }
51 }
52
53 pub fn stable(&self) -> bool {
54 self.stable
55 }
56
57 pub fn name(&self) -> &str {
58 &self.name
59 }
60
61 pub fn version(&self) -> &Version {
62 &self.version
63 }
64
65 pub async fn exists(codehash: B256, provider: &impl Provider) -> Result<bool, ContractError> {
67 let arbwasm = precompiles::arb_wasm(provider);
68 match arbwasm.codehashVersion(codehash).call().await {
69 Ok(_) => Ok(true),
70 Err(e) => {
71 let errs = decode_contract_error(e)?;
72 use ArbWasmErrors as A;
73 match errs {
74 A::ProgramNotActivated(_)
75 | A::ProgramNeedsUpgrade(_)
76 | A::ProgramExpired(_) => Ok(false),
77 _ => Err(ContractError::UnexpectedArbWasmError),
78 }
79 }
80 }
81 }
82
83 pub fn build(&self, config: &BuildConfig) -> Result<PathBuf, BuildError> {
84 build_contract(self, config)
85 }
86
87 pub async fn check(
88 &self,
89 address: Option<Address>,
90 config: &CheckConfig,
91 provider: &impl Provider,
92 ) -> Result<ContractStatus, CheckError> {
93 check_contract(self, address, config, provider).await
94 }
95
96 pub async fn deploy(
97 &self,
98 config: &DeploymentConfig,
99 provider: &(impl Provider + WalletProvider),
100 ) -> Result<(), DeploymentError> {
101 deploy(self, config, provider).await
102 }
103
104 pub fn export_abi(&self, config: &ReflectionConfig) -> eyre::Result<()> {
105 ops::export_abi(self.package.name.as_ref(), config)
106 }
107
108 pub async fn verify(
109 &self,
110 tx_hash: TxHash,
111 skip_clean: bool,
112 provider: &impl Provider,
113 ) -> eyre::Result<VerificationStatus> {
114 let status = verification::verify(self, tx_hash, skip_clean, provider).await?;
115 Ok(status)
116 }
117
118 pub fn print_constructor(&self, config: &ReflectionConfig) -> eyre::Result<()> {
119 ops::print_constructor(self.package.name.as_ref(), config)
120 }
121}
122
123impl TryFrom<&Package> for Contract {
124 type Error = ContractError;
125
126 fn try_from(package: &Package) -> Result<Self, Self::Error> {
127 let toolchain_channel = get_toolchain_channel(package)?;
128 let stable = !toolchain_channel.contains("nightly");
129 let version = package.version.clone();
130 let name = package
133 .targets
134 .iter()
135 .find_map(|t| t.kind.contains(&TargetKind::Lib).then(|| t.name.clone()))
136 .unwrap_or_else(|| package.name.to_string());
138 Ok(Self {
139 package: package.clone(),
140 stable,
141 version,
142 name,
143 })
144 }
145}
146
147#[derive(Debug, thiserror::Error)]
148pub enum ContractError {
149 #[error("{0}")]
150 ContractDecode(#[from] crate::error::ContractDecodeError),
151 #[error("{0}")]
152 Toolchain(#[from] crate::utils::toolchain::ToolchainError),
153
154 #[error("unexpected ArbWasm error")]
155 UnexpectedArbWasmError,
156}
157
158#[derive(Debug)]
159pub enum ContractStatus {
160 Active { code: Vec<u8> },
162 Ready { code: Vec<u8>, fee: U256 },
164}
165
166impl ContractStatus {
167 pub fn code(&self) -> &[u8] {
168 match self {
169 Self::Active { code } => code,
170 Self::Ready { code, .. } => code,
171 }
172 }
173
174 pub fn suggest_fee(&self) -> U256 {
175 match self {
176 Self::Active { .. } => U256::ZERO,
177 Self::Ready { fee, .. } => *fee,
178 }
179 }
180}