# Rust Integration Guide

This guide provides a production-ready Rust code example for integrating with the Noderr Protocol's on-chain governance using `ethers-rs`.

> **Note:** This example focuses on creating on-chain proposals after they have been approved via off-chain voting on Snapshot.

## Smart Contract Integration: Creating a Proposal

First, ensure your `Cargo.toml` includes the necessary dependencies:

```toml
[dependencies]
ethers = { version = "2.0", features = ["abigen", "rustls"] }
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
hex = "0.4"
dotenv = "0.15"
```

Now, you can use the following code to create a proposal:

```rust
use ethers::prelude::*;
use std::sync::Arc;
use std::env;
use dotenv::dotenv;

// Generate type-safe bindings for the GovernanceManager contract.
// This is typically done in a build script (build.rs).
abigen!(GovernanceManager, "./abi/GovernanceManager.json");

// --- Enums (from GovernanceManager.sol) ---
// ProposalType: Standard(0), Emergency(1)
const PROPOSAL_TYPE_STANDARD: u8 = 0;

// Chamber: Oracle(0), Guardian(1)
const CHAMBER_ORACLE: u8 = 0;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    dotenv().ok();

    // --- Configuration ---
    let rpc_url = env::var("BASE_SEPOLIA_RPC_URL").expect("BASE_SEPOLIA_RPC_URL must be set");
    let private_key = env::var("PROPOSER_PRIVATE_KEY").expect("PROPOSER_PRIVATE_KEY must be set");
    let gov_manager_address: Address = "0x72a065E5cf055F65d4c37CBc9d9DC5314115e5d7".parse()?;
    let token_address: Address = "0x61318A5e42612f1d0B67f443E457B8E9C2F001D6".parse()?;

    // --- Client Setup ---
    let provider = Provider::<Http>::try_from(rpc_url)?;
    let chain_id = provider.get_chainid().await?.as_u64();
    let wallet = private_key.parse::<LocalWallet>()?.with_chain_id(chain_id);
    let client = Arc::new(SignerMiddleware::new(provider, wallet));

    // --- Contract Instance ---
    let governance_manager = GovernanceManager::new(gov_manager_address, client.clone());

    // --- Example: Create a proposal to transfer 1,000 NODR from the treasury ---
    let recipient_address: Address = "0x...".parse()?; // The address that will receive the grant
    let amount_to_transfer = ethers::utils::parse_ether(1000)?;

    // 1. Targets: The contract(s) to be called.
    let targets = vec![token_address];

    // 2. Values: ETH values to send with each call (usually 0).
    let values = vec![U256::zero()];

    // 3. Calldatas: The encoded function call(s).
    // A minimal ABI for the transfer function is needed here.
    let transfer_abi = r#"[{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"type":"function"}]"#;
    let contract = Contract::new(token_address, serde_json::from_str::<Abi>(transfer_abi)?, client.clone());
    let calldata = contract.method::<_, Bytes>("transfer", (recipient_address, amount_to_transfer))?.calldata().unwrap();
    let calldatas = vec![calldata];

    // 4. Description: Must include a link to the passed Snapshot proposal.
    let description = "# Grant for Community Contributor @example_user\n\nThis proposal executes the on-chain transfer of 1,000 NODR...\n\n**Snapshot Proposal:** https://snapshot.org/#/noderr.eth/proposal/0x...".to_string();

    // 5. Proposal Type: Standard (0) or Emergency (1)
    let proposal_type = PROPOSAL_TYPE_STANDARD;

    // 6. Chamber: Oracle (0) or Guardian (1)
    let chamber = CHAMBER_ORACLE;

    // --- Send the Transaction ---
    println!("Creating on-chain proposal...");
    let tx = governance_manager.propose(
        targets,
        values,
        calldatas,
        description,
        proposal_type,
        chamber,
    );

    let pending_tx = tx.send().await?;
    let receipt = pending_tx.await?.expect("Transaction failed to mine");

    println!("Transaction successful! Hash: {:?}", receipt.transaction_hash);

    // --- Extract Proposal ID from Event Logs ---
    if let Some(log) = receipt.logs.iter().find(|log| log.topics[0] == governance_manager.proposal_created_filter().topic)
    {
        let event = governance_manager.decode_event::<ProposalCreatedFilter>("ProposalCreated", log.topics.clone(), log.data.clone())?;
        println!("Successfully created on-chain Proposal ID: {}", event.proposal_id);
    } else {
        println!("Could not find ProposalCreated event in transaction receipt.");
    }

    Ok(())
}
```

And a minimal `abi/GovernanceManager.json` would look like this:

```json
[
  {
    "inputs": [
      { "internalType": "address[]", "name": "targets", "type": "address[]" },
      { "internalType": "uint256[]", "name": "values", "type": "uint256[]" },
      { "internalType": "bytes[]", "name": "calldatas", "type": "bytes[]" },
      { "internalType": "string", "name": "description", "type": "string" },
      { "internalType": "uint8", "name": "proposalType", "type": "uint8" },
      { "internalType": "uint8", "name": "chamber", "type": "uint8" }
    ],
    "name": "propose",
    "outputs": [{ "internalType": "uint256", "name": "proposalId", "type": "uint256" }],
    "stateMutability": "nonpayable",
    "type": "function"
  },
  {
    "anonymous": false,
    "inputs": [
      { "indexed": true, "internalType": "uint256", "name": "proposalId", "type": "uint256" },
      { "indexed": true, "internalType": "address", "name": "proposer", "type": "address" },
      { "indexed": false, "internalType": "address[]", "name": "targets", "type": "address[]" },
      { "indexed": false, "internalType": "uint256[]", "name": "values", "type": "uint256[]" },
      { "indexed": false, "internalType": "string[]", "name": "signatures", "type": "string[]" },
      { "indexed": false, "internalType": "bytes[]", "name": "calldatas", "type": "bytes[]" },
      { "indexed": false, "internalType": "uint256", "name": "votingStartTime", "type": "uint256" },
      { "indexed": false, "internalType": "uint256", "name": "votingEndTime", "type": "uint256" },
      { "indexed": false, "internalType": "string", "name": "description", "type": "string" },
      { "indexed": false, "internalType": "uint8", "name": "proposalType", "type": "uint8" },
      { "indexed": false, "internalType": "uint8", "name": "chamber", "type": "uint8" }
    ],
    "name": "ProposalCreated",
    "type": "event"
  }
]
```
