Skip to content

Commit

Permalink
chore: get envio contracts generating locally
Browse files Browse the repository at this point in the history
  • Loading branch information
drewstone committed Dec 20, 2024
1 parent a924939 commit 36d861a
Show file tree
Hide file tree
Showing 11 changed files with 495 additions and 530 deletions.
401 changes: 10 additions & 391 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ wiremock = "0.6.2"
fake = "3.0.1"
rand = "0.8.5"
regex = "1.11.1"
envio = { git = "https://github.com/enviodev/hyperindex", branch = "main" }
ethers = { version = "2.0.14" }
anyhow = "1.0.93"
enigo = "0.3.0"
rexpect = "0.6.0"
bollard = "0.16.1"
futures = "0.3.31"
testcontainers = "0.20.0"
chrono = "0.4.39"

[dev-dependencies]
k8s-openapi = { version = "0.23.0", features = ["v1_31"] }
Expand Down
24 changes: 24 additions & 0 deletions src/envio_utils/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,30 @@ pub enum ContractSource {
},
}

impl ContractSource {
pub fn is_explorer(&self) -> bool {
matches!(self, ContractSource::Explorer { .. })
}

pub fn is_abi(&self) -> bool {
matches!(self, ContractSource::Abi { .. })
}

pub fn get_abi(&self) -> Option<String> {
match self {
ContractSource::Abi { abi, .. } => abi.clone(),
_ => None,
}
}

pub fn get_url(&self) -> Option<String> {
match self {
ContractSource::Abi { url, .. } => url.clone(),
_ => None,
}
}
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct ContractDeployment {
pub network_id: String,
Expand Down
58 changes: 58 additions & 0 deletions src/envio_utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,61 @@ pub mod project;

pub use config::*;
pub use project::*;

pub const CHAIN_LIST: [&str; 55] = [
"amoy",
"arbitrum-nova",
"arbitrum-one",
"arbitrum-sepolia",
"aurora",
"avalanche",
"b2-testnet",
"base",
"base-sepolia",
"blast",
"blast-sepolia",
"boba",
"bsc",
"bsc-testnet",
"celo",
"citrea-testnet",
"crab",
"ethereum-mainnet",
"fantom",
"flare",
"fuji",
"galadriel-devnet",
"gnosis",
"gnosis-chiado",
"goerli",
"harmony",
"holesky",
"kroma",
"linea",
"lisk",
"lukso",
"lukso-testnet",
"manta",
"mantle",
"metis",
"mode",
"moonbase-alpha",
"moonbeam",
"morph",
"morph-testnet",
"neon-evm",
"opbnb",
"optimism",
"optimism-sepolia",
"polygon",
"polygon-zkevm",
"rsk",
"saakuru",
"scroll",
"sepolia",
"shimmer-evm",
"tangle",
"unichain-sepolia",
"zeta",
"zora",
];
85 changes: 75 additions & 10 deletions src/envio_utils/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ use std::path::PathBuf;
use thiserror::Error;
use tokio::process::{Child, Command};

use crate::network::SUPPORTED_NETWORKS;

use super::config::{ContractConfig, ContractSource};
use super::docker::EnvioDocker;

Expand Down Expand Up @@ -164,14 +166,19 @@ impl EnvioManager {
let mut current_contract_idx = 0;
let mut current_deployment_idx = 0;

let mut success = false;

loop {
match Self::handle_envio_prompts(
&mut session,
&project_dir_clone,
&contracts,
&mut current_contract_idx,
&mut current_deployment_idx,
) {
&mut success,
)
.await
{
Ok(true) => {
// If we're finished, kill the process directly instead of trying to exit cleanly
println!("Project template ready");
Expand All @@ -186,8 +193,13 @@ impl EnvioManager {
EnvioError::RexpectError(rexpect::error::Error::Io(err))
if err.raw_os_error() == Some(5) =>
{
println!("Gracefully handling I/O error");
break;
if success {
break;
} else {
return Err(EnvioError::ProcessFailed(
"Envio process exited unexpectedly".to_string(),
));
}
}
_ => return Err(e),
},
Expand All @@ -214,9 +226,21 @@ impl EnvioManager {
}
}
println!("Envio process completed, verifying project setup...");
let current_dir = std::env::current_dir()?;
println!("Current dir: {:?}", current_dir);

// Since we're already in the project directory, just use current_dir
let project_dir = current_dir;

println!("Project dir: {:?}", project_dir);
println!("Project dir exists: {:?}", project_dir.exists());

// Verify the project directory structure
// Use current directory for config file check
let config_path = project_dir.join("config.yaml");
println!("Config path: {:?}", config_path);
println!("Config path exists: {:?}", config_path.exists());
println!("Config path is file: {:?}", config_path.is_file());

if !config_path.exists() {
return Err(EnvioError::InvalidState(
"Project initialization failed: config.yaml not created".into(),
Expand All @@ -231,12 +255,13 @@ impl EnvioManager {
})
}

fn handle_envio_prompts(
async fn handle_envio_prompts(
session: &mut rexpect::session::PtySession,
project_dir: &PathBuf,
contracts: &[ContractConfig],
current_contract_idx: &mut usize,
current_deployment_idx: &mut usize,
success: &mut bool,
) -> Result<bool, EnvioError> {
let mut prompt = String::new();
loop {
Expand Down Expand Up @@ -298,14 +323,49 @@ impl EnvioManager {
}
s if s.contains("What is the path to your json abi file?") => {
let contract = &contracts[*current_contract_idx];
let abi_path = project_dir
.join("abis")
.join(format!("{}_abi.json", contract.name));
println!("ABI path: {:?}", abi_path);
session.send(abi_path.to_str().unwrap_or_default())?;
let abi_path = format!("./abis/{}_abi.json", contract.name);

session.send(&abi_path)?;
session.flush()?;
session.send_control('m')?;
}
s if s.contains("Would you like to import from a block explorer or a local abi") => {
println!("Handling block explorer vs local ABI prompt");
let contract = &contracts[*current_contract_idx];

if contract.source.is_explorer() {
// For block explorer, just hit enter
session.send_control('m')?;
} else {
// For local ABI, arrow down and hit enter
session.send("\x1B[B")?; // Down arrow
session.send_control('m')?;
}
}
s if s.contains("Which blockchain would you like to import a contract from?") => {
println!("Handling blockchain selection");
let contract = &contracts[*current_contract_idx];
let network_id: u64 = (&contract.deployments[*current_deployment_idx].network_id).parse().unwrap_or_default();
// Get the network info from definitions
let network_info = crate::network::definitions::SUPPORTED_NETWORKS
.get(&network_id)
.expect("Network ID not found in supported networks");

// Convert network name to lowercase and convert spaces to hyphens
let network_name = network_info.name.to_lowercase().replace(' ', "-");

// Find index in CHAIN_LIST
let chain_idx = crate::envio_utils::CHAIN_LIST
.iter()
.position(|&x| x == network_name)
.expect("Network not found in chain list");

// Send down arrow key chain_idx times
for _ in 0..chain_idx {
session.send("\x1B[B")?; // Down arrow
}
session.send_control('m')?;
}
s if s.contains("Choose network:") || s.contains("<Enter Network Id>") => {
println!("Handling network selection");
session.send_control('m')?;
Expand Down Expand Up @@ -385,6 +445,7 @@ impl EnvioManager {
}
s if s.contains("You can always visit 'https://envio.dev/app/api-tokens' and add a token later to your .env file.") => {
println!("Handling final prompt");
*success = true;
session.send_control('m')?;
return Ok(true)
}
Expand Down Expand Up @@ -482,5 +543,9 @@ mod tests {

// Clean up
manager.stop_docker().await.unwrap();

// Clean up project directory
std::fs::remove_dir_all(&project.dir).unwrap();
assert!(!project.dir.exists(), "Project directory should be removed");
}
}
34 changes: 21 additions & 13 deletions src/jobs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,37 +160,45 @@ mod tests {
use std::path::PathBuf;

#[tokio::test]
async fn test_spawn_multi_contract_indexer() {
async fn test_spawn_multi_contract_indexer_local() {
// Setup test environment
let context = ServiceContext::new(
StdGadgetConfiguration::default(),
PathBuf::from("/tmp/test"),
);
let context = ServiceContext::new(StdGadgetConfiguration::default(), PathBuf::from("."));

// Create test contracts configuration using test utils
let contracts = vec![
create_test_contract("Greeter", "1"), // Ethereum mainnet
create_test_contract("OptimismGreeter", "10"), // Optimism
create_test_explorer_contract("ArbitrumToken", "42161"), // Arbitrum
];

let config = IndexerConfig::new("multi_network_test".to_string(), contracts);
let params = SpawnIndexerParams { config };
let params_bytes = serde_json::to_vec(&params).unwrap();

// Test local indexer spawn
let result = spawn_indexer_local(params_bytes.clone(), context.clone())
.await
.unwrap();
let result = spawn_indexer_local(params_bytes, context).await.unwrap();
let result: SpawnIndexerResult = serde_json::from_slice(&result).unwrap();
assert!(result.id.contains("multi_network_test"));
}

// Test kubernetes indexer spawn (requires mock k8s client)
let mut context_k8s = context.clone();
context_k8s.deployment_mode = DeploymentMode::Kubernetes;
#[tokio::test]
async fn test_kube_spawn_multi_contract_indexer() {
// Setup test environment
let mut context =
ServiceContext::new(StdGadgetConfiguration::default(), PathBuf::from("."));
context.deployment_mode = DeploymentMode::Kubernetes;
// Setup mock k8s client here...

let result = spawn_indexer_kube(params_bytes, context_k8s).await.unwrap();
// Create test contracts configuration using test utils
let contracts = vec![
create_test_contract("Greeter", "1"), // Ethereum mainnet
create_test_contract("OptimismGreeter", "10"), // Optimism
];

let config = IndexerConfig::new("multi_network_test".to_string(), contracts);
let params = SpawnIndexerParams { config };
let params_bytes = serde_json::to_vec(&params).unwrap();

let result = spawn_indexer_kube(params_bytes, context).await.unwrap();
let results: Vec<EnvioIndexer> = serde_json::from_slice(&result).unwrap();

assert_eq!(results.len(), 3); // One for each contract
Expand Down
13 changes: 8 additions & 5 deletions src/kubernetes/envio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,21 +133,24 @@ mod tests {
let deployment = create_envio_deployment_config(&spec, "default");
let env = &deployment.container.env;

// Verify environment variables
// Verify environment variables with actual addresses from test_utils
assert!(env.iter().any(|(k, v)| k == "BLOCKCHAIN" && v == "1"));
assert!(env.iter().any(|(k, v)| k == "BLOCKCHAIN_1" && v == "10"));
assert!(env
.iter()
.any(|(k, v)| k == "CONTRACT_ADDRESS" && v == "0x123"));
.any(|(k, v)| k == "CONTRACT_ADDRESS"
&& v == "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"));
assert!(env
.iter()
.any(|(k, v)| k == "CONTRACT_ADDRESS_1" && v == "0x456"));
.any(|(k, v)| k == "CONTRACT_ADDRESS_1"
&& v == "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"));
assert!(env
.iter()
.any(|(k, v)| k == "PROXY_ADDRESS_1" && v == "0x789"));
.any(|(k, v)| k == "PROXY_ADDRESS_1"
&& v == "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"));
assert!(env
.iter()
.any(|(k, v)| k == "OPTIMISM_API_KEY" && v == "test_key"));
.any(|(k, v)| k == "OPTIMISM_VERIFIED_CONTRACT_API_TOKEN" && v == "test_key"));
assert!(env.iter().any(|(k, v)| k == "NUM_CONTRACTS" && v == "2"));
}
}
3 changes: 0 additions & 3 deletions src/network/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@ macro_rules! define_networks {
};
}

// Define all supported networks
// ... existing code up to define_networks! macro ...

define_networks! {
42161 => {
name: "Arbitrum",
Expand Down
Loading

0 comments on commit 36d861a

Please sign in to comment.