-
Notifications
You must be signed in to change notification settings - Fork 2
/
contract_deploy.rs
160 lines (132 loc) · 5.15 KB
/
contract_deploy.rs
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
use ethers::contract::Contract;
use ethers::prelude::{
BlockNumber, ConfigurableArtifacts, ContractFactory, LocalWallet, Project,
ProjectCompileOutput, ProjectPathsConfig, Signer, SignerMiddleware, U256,
};
use ethers::utils::Ganache;
use ethers_providers::{Middleware, Provider};
use ethers_solc::Artifact;
use eyre::Result;
use eyre::{eyre, ContextCompat};
use hex::ToHex;
use std::path::PathBuf;
use std::time::Duration;
pub type SignerDeployedContract<T> = Contract<SignerMiddleware<Provider<T>, LocalWallet>>;
#[tokio::main]
async fn main() -> Result<()> {
// Spawn a ganache instance
let mnemonic = "gas monster ski craft below illegal discover limit dog bundle bus artefact";
let ganache = Ganache::new().mnemonic(mnemonic).spawn();
println!("HTTP Endpoint: {}", ganache.endpoint());
// Get the first wallet managed by ganache
let wallet: LocalWallet = ganache.keys()[0].clone().into();
let first_address = wallet.address();
println!(
"Wallet first address: {}",
first_address.encode_hex::<String>()
);
// A provider is an Ethereum JsonRPC client
let provider = Provider::try_from(ganache.endpoint())?.interval(Duration::from_millis(10));
let chain_id = provider.get_chainid().await?.as_u64();
println!("Ganache started with chain_id {chain_id}");
// Compile solidity project
let project = compile("examples/").await?;
// Print compiled project information
print_project(project.clone()).await?;
let balance = provider.get_balance(wallet.address(), None).await?;
println!(
"Wallet first address {} balance: {}",
wallet.address().encode_hex::<String>(),
balance
);
let contract_name = "BUSDImplementation";
// Find the contract to be deployed
let contract = project
.find(contract_name)
.context("Contract not found")?
.clone();
// We'll create a transaction which will include code for deploying the contract
// Get ABI and contract byte, these are required for contract deployment
let (abi, bytecode, _) = contract.into_parts();
let abi = abi.context("Missing abi from contract")?;
let bytecode = bytecode.context("Missing bytecode from contract")?;
// Create signer client
let wallet = wallet.with_chain_id(chain_id);
let client = SignerMiddleware::new(provider.clone(), wallet).into();
// Deploy contract
let factory = ContractFactory::new(abi.clone(), bytecode, client);
// Our contract don't need any constructor arguments, so we can use an empty tuple
let mut deployer = factory.deploy(())?;
let block = provider
.clone()
.get_block(BlockNumber::Latest)
.await?
.context("Failed to get latest block")?;
// Set a reasonable gas price to prevent our contract from being rejected by EVM
let gas_price = block
.next_block_base_fee()
.context("Failed to get the next block base fee")?;
deployer.tx.set_gas_price::<U256>(gas_price);
// We can also manually set the gas limit
// let gas_limit = block.gas_limit;
// deployer.tx.set_gas::<U256>(gas_limit);
// Create transaction and send
let contract = deployer.clone().legacy().send().await?;
println!(
"BUSDImpl contract address {}",
contract.address().encode_hex::<String>()
);
Ok(())
}
pub async fn compile(root: &str) -> Result<ProjectCompileOutput<ConfigurableArtifacts>> {
// Create path from string and check if the path exists
let root = PathBuf::from(root);
if !root.exists() {
return Err(eyre!("Project root {root:?} does not exists!"));
}
// Configure `root` as our project root
let paths = ProjectPathsConfig::builder()
.root(&root)
.sources(&root)
.build()?;
// Create a solc ProjectBuilder instance for compilation
let project = Project::builder()
.paths(paths)
.set_auto_detect(true) // auto detect solc version from solidity source code
.no_artifacts()
.build()?;
// Compile project
let output = project.compile()?;
// Check for compilation errors
if output.has_compiler_errors() {
Err(eyre!(
"Compiling solidity project failed: {:?}",
output.output().errors
))
} else {
Ok(output.clone())
}
}
pub async fn print_project(project: ProjectCompileOutput<ConfigurableArtifacts>) -> Result<()> {
let artifacts = project.into_artifacts();
for (id, artifact) in artifacts {
let name = id.name;
let abi = artifact.abi.context("No ABI found for artificat {name}")?;
println!("{}", "=".repeat(80));
println!("CONTRACT: {:?}", name);
let contract = &abi.abi;
let functions = contract.functions();
let functions = functions.cloned();
let constructor = contract.constructor();
if let Some(constructor) = constructor {
let args = &constructor.inputs;
println!("CONSTRUCTOR args: {args:?}");
}
for func in functions {
let name = &func.name;
let params = &func.inputs;
println!("FUNCTION {name} {params:?}");
}
}
Ok(())
}