# Python Integration Guide

This guide provides a production-ready Python code example for integrating with the Noderr Protocol's on-chain governance.

> **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

```python
import os
from web3 import Web3
from web3.middleware import geth_poa_middleware

# --- Configuration ---
# Infura or other RPC provider URL for Base Sepolia
RPC_URL = os.environ.get("BASE_SEPOLIA_RPC_URL", "https://sepolia.base.org")

# Private key of an account with the PROPOSER_ROLE in GovernanceManager
# In production, use a hardware wallet, a multi-sig, or a secure key management service.
PROPOSER_PRIVATE_KEY = os.environ.get("PROPOSER_PRIVATE_KEY")

# Deployed contract addresses on Base Sepolia
# Source: noderr-protocol/config/testnet-contracts.json
GOVERNANCE_MANAGER_ADDRESS = "0x72a065E5cf055F65d4c37CBc9d9DC5314115e5d7"
NODERR_TOKEN_ADDRESS = "0x61318A5e42612f1d0B67f443E457B8E9C2F001D6"

# --- Enums (from GovernanceManager.sol) ---
# ProposalType: Standard(0), Emergency(1)
PROPOSAL_TYPE_STANDARD = 0
PROPOSAL_TYPE_EMERGENCY = 1

# Chamber: Oracle(0), Guardian(1)
CHAMBER_ORACLE = 0
CHAMBER_GUARDIAN = 1

# --- Connect to the Blockchain ---
w3 = Web3(Web3.HTTPProvider(RPC_URL))
# Required for interacting with PoA networks like Base Sepolia
w3.middleware_onion.inject(geth_poa_middleware, layer=0)

# --- Load Contract ABIs ---
# It's best practice to store ABIs in separate JSON files.
# This ABI is validated against the actual GovernanceManager.sol source.
GOVERNANCE_MANAGER_ABI = '''
[
  {
    "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" }],
    "name": "ProposalCreated",
    "type": "event"
  }
]
'''
NODERR_TOKEN_ABI = '''
[
  {
    "constant": false,
    "inputs": [
      { "name": "_to", "type": "address" },
      { "name": "_value", "type": "uint256" }
    ],
    "name": "transfer",
    "outputs": [{ "name": "", "type": "bool" }],
    "type": "function"
  }
]
'''

# --- Initialize Contracts ---
governance_manager = w3.eth.contract(address=GOVERNANCE_MANAGER_ADDRESS, abi=GOVERNANCE_MANAGER_ABI)
noderr_token = w3.eth.contract(address=NODERR_TOKEN_ADDRESS, abi=NODERR_TOKEN_ABI)

# --- Proposer Account Setup ---
proposer_account = w3.eth.account.from_key(PROPOSER_PRIVATE_KEY)
w3.eth.default_account = proposer_account.address

def create_onchain_proposal(targets, values, calldatas, description, proposal_type, chamber):
    """
    Creates a new on-chain governance proposal.
    """
    print(f"Creating proposal: {description[:50]}...")

    try:
        # Build the transaction
        tx = governance_manager.functions.propose(
            targets,
            values,
            calldatas,
            description,
            proposal_type,
            chamber
        ).build_transaction({
            "from": proposer_account.address,
            "nonce": w3.eth.get_transaction_count(proposer_account.address),
            "gas": 2000000,  # Set a generous gas limit; estimate gas for production
        })

        # Sign the transaction
        signed_tx = w3.eth.account.sign_transaction(tx, private_key=PROPOSER_PRIVATE_KEY)

        # Send the transaction
        tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
        print(f"Transaction sent! Hash: {tx_hash.hex()}")

        # Wait for the transaction to be mined
        receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
        print("Transaction confirmed!")

        # Extract the proposal ID from the event logs
        proposal_created_event = governance_manager.events.ProposalCreated().process_receipt(receipt)
        if not proposal_created_event:
            print("Could not find ProposalCreated event in transaction receipt.")
            return None
            
        proposal_id = proposal_created_event[0]["args"]["proposalId"]
        print(f"Successfully created on-chain Proposal ID: {proposal_id}")

        return receipt

    except Exception as e:
        print(f"An error occurred: {e}")
        return None

if __name__ == "__main__":
    # --- Example: Create a proposal to transfer 1,000 NODR from the treasury ---

    recipient_address = "0x..."  # The address that will receive the grant
    amount_to_transfer = w3.to_wei(1000, "ether")

    # 1. Targets: The contract(s) to be called.
    targets = [NODERR_TOKEN_ADDRESS]

    # 2. Values: ETH values to send with each call (usually 0).
    values = [0]

    # 3. Calldatas: The encoded function call(s).
    transfer_calldata = noderr_token.encodeABI(
        fn_name="transfer",
        args=[recipient_address, amount_to_transfer]
    )
    calldatas = [transfer_calldata]

    # 4. Description: Must include a link to the passed Snapshot proposal.
    description = (
        "# Grant for Community Contributor @example_user\n\n"
        "This proposal executes the on-chain transfer of 1,000 NODR...\n\n"
        "**Snapshot Proposal:** https://snapshot.org/#/noderr.eth/proposal/0x..."
    )
    
    # 5. Proposal Type: Standard (0) or Emergency (1)
    proposal_type = PROPOSAL_TYPE_STANDARD
    
    # 6. Chamber: Oracle (0) or Guardian (1)
    chamber = CHAMBER_ORACLE

    # Create the on-chain proposal
    create_onchain_proposal(targets, values, calldatas, description, proposal_type, chamber)
```
