> For the complete documentation index, see [llms.txt](https://docs.bitplanet.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.bitplanet.ai/developer-guides/module-interaction.md).

# Module Interaction

## Architecture Overview

The Bitplanet architecture bridges two distinct execution environments: the BitSDK layer (native blockchain modules) and the EVM layer (Ethereum-compatible smart contracts). This dual-layer design enables seamless interaction between Bitplanet's custom modules and EVM-based applications.

**Key Components:**

**BitSDK Layer:**

* **x/brahma** manages AI token economics, processing tensor contributions and distributing rewards
* **x/precompile** acts as the bridge, monitoring EVM events and triggering SDK actions via hooks
* **x/gov** handles governance proposals for core allocations and parameter updates
* **BitEVM Module** provides the EVM execution environment

**EVM Contract Layer:**

* **GemCoreNFT** (UUPS upgradeable) orchestrates the NFT ecosystem, managing Core and Gem tokens
* **MintableERC1155** contracts (Beacon proxies) represent individual gem collections with upgrade capability
* **ERC20Adapter** contracts bridge ERC1155 NFTs to fungible ERC20 tokens (10^19 scaling factor)
* **Precompiled Contracts** expose BitSDK functionality (staking, governance, etc.) to EVM contracts

**Flow Highlights:**

1. **Event-Driven Architecture:** EVM events trigger BitSDK state changes via the precompile module's PostTxProcessing hook
2. **Bidirectional Communication:** x/brahma can call EVM contracts (for minting), while EVM events trigger SDK messages (for governance)
3. **Governance Integration:** Core allocations require governance approval, creating a trustless permission system
4. **Modular Upgradeability:** UUPS pattern for GemCoreNFT, Beacon pattern for token contracts enable non-disruptive upgrades

**Architecture Diagram:**

{% @mermaid/diagram content="graph TB
subgraph CosmosSDK\["BitSDK Layer"]
brahma\["x/brahma<br/>(AI Token Economics)"]
precompile\["x/precompile<br/>(EVM Bridge)"]
gov\["x/gov<br/>(Governance)"]

```
    brahma <--"Interactions"--> precompile
    precompile <--"Interactions"--> gov

    brahma --"EVM Calls"--> evmModule["BitEVM Module"]
    precompile --"Event Hooks"--> evmModule
end

evmModule --"Bridge"--> EVMLayer

subgraph EVMLayer["EVM Contract Layer"]
    subgraph Contracts["Smart Contracts"]
        gemCore["GemCoreNFT<br/>(Upgradeable)<br/>Master Contract"]
        erc1155["MintableERC1155<br/>Beacon Proxies"]
        erc20["ERC20Adapter<br/>Token Bridge"]

        gemCore <--"Creates/Manages"--> erc1155
        erc1155 <--"Converts"--> erc20
        gemCore <--"Controls"--> erc20
    end

    precompiles["Precompiled Contracts<br/>(Staking, Gov, Bank,<br/>Distribution, ICS20, etc.)"]
end" %}
```

## EVM Hook Mechanism

**Implementation Location:** `x/precompile/keeper/evm_hooks.go`

The precompile module implements the `EvmHooks` interface to process EVM events and trigger BitSDK state changes.

### 1. Event Detection (PostTxProcessing Hook)

When an EVM transaction completes, the precompile module scans emitted events:

```go
// File: x/precompile/keeper/evm_hooks.go
func (h Hooks) PostTxProcessing(ctx sdk.Context, sender common.Address,
                                msg core.Message, receipt *ethtypes.Receipt) error {
    params := h.k.GetParams(ctx)

    // Only process if transaction has a recipient contract
    if msg.To != nil {
        // Check for Governance precompile events
        if containsPrecompileContract(receipt, params.GovPrecompile) {
            h.govPrecompile(ctx, receipt, params.GovPrecompile)
        }

        // Check for Brahma precompile events
        if containsPrecompileContract(receipt, params.BrahmaPrecompile) {
            if err := h.brahmaPrecompile(ctx, receipt, msg, params.BrahmaPrecompile); err != nil {
                return err
            }
        }

        // Check for Staking precompile events
        if containsPrecompileContract(receipt, params.StakingPrecompile) {
            h.stakingPrecompile(ctx, receipt, params.StakingPrecompile)
        }
    }

    return nil
}
```

**Hook Registration:**

* Hooks are registered in the app setup (`evmd/app.go`)
* Multiple precompile handlers can be chained
* Each handler processes specific contract events

### 2. Event Processing

Different events trigger different BitSDK actions:

**CoreGrantApplicationEvent:**

```go
// Automatically creates governance proposal
func (h Hooks) handleCoreGrantApplicationEvent(ctx sdk.Context, log *ethtypes.Log) error {
    proposerAddress := common.BytesToAddress(log.Topics[1].Bytes())
	proposer := sdk.AccAddress(proposerAddress.Bytes())

    grantAmount := math.NewIntFromBigInt(log.Topics[2].Big())

    // Message only contains Authority, Requester, CoreAllocation
    msg := &brahmaTypes.MsgCoreGrantApplication{
        Authority:      "ethm10d07y265gmmuvt4z0w9aw880jnsr700jpva843",
        Requester:      proposer.String(),
        CoreAllocation: grantAmount.Uint64(),
        // Note: Title, Description, CreatedAtHeight NOT set here
    }

    // Title/summary passed to SubmitProposal, not in message
    title := "Core Grant Application"
    summary := "Request for Core grant allocation"
    messages := []sdk.Msg{msg}
    metadata := "Proposal Metadata"

    _, err := h.k.govKeeper.SubmitProposal(ctx.Context(), messages, metadata, title, summary, proposer, true)
    return err
}
```

**Note:** The `MsgCoreGrantApplication` created by the hook only populates `Authority`, `Requester`, and `CoreAllocation`. The `Title`, `Description`, and `CreatedAtHeight` fields are NOT set in the hook—they're passed separately to `SubmitProposal` or must be set when manually creating proposals.

**OreRequestEvent:**

```go
// Processes tensor contribution immediately
func (h Hooks) handleOreRequestEvent(ctx sdk.Context, log *ethtypes.Log, msg core.Message) error {
    addr := common.HexToAddress(log.Topics[1].Hex())

    var event OreRequestEvent
    err := BrahmaContract.UnpackIntoInterface(&event, "OreRequestEvent", log.Data)
    if err != nil {
        return err
    }

    // Submit MsgRequestForCore directly
    return h.RequestForCore(ctx, event.Name, addr, event.Values, msg.From)
}
```

### 3. BeginBlock Reward Distribution

Every block, the Brahma module siphons inflation and distributes rewards:

```go
func (k *Keeper) BeginBlock(ctx sdk.Context) error {
    const baseDenom = "bplcoin"

    // 1. Get reward split parameters (all return math.LegacyDec)
    creatorSplit, _ := k.GetCreatorSplit(ctx)
    applicationSplit, _ := k.GetApplicationSplit(ctx)
    contributorSplit, _ := k.GetContributorSplit(ctx)
    siphonPercentage, _ := k.GetSiphonPercentage(ctx)

    // 2. Calculate AI reward proportions based on market cap
    aiToSplitOfRewards := k.SplitInflationPerAI(ctx)  // returns map[string]math.LegacyDec

    // 3. Get distribution module account and balance
    distributionModuleAccount := k.accountKeeper.GetModuleAccount(ctx, distributiontypes.ModuleName)
    if distributionModuleAccount == nil {
        return nil  // No module account
    }

    // Get all balances and extract the base denomination amount
    distributionBalances := k.bankKeeper.GetAllBalances(ctx, distributionModuleAccount.GetAddress())
    distributionBalance := distributionBalances.AmountOf(baseDenom)

    if !distributionBalance.IsPositive() {
        return nil  // No tokens to distribute
    }

    // 4. Siphon tokens: Convert to Dec, multiply, truncate back to Int
    toSiphon := math.LegacyNewDecFromInt(distributionBalance).Mul(siphonPercentage).TruncateInt()

    if !toSiphon.IsPositive() {
        return nil
    }

    // Get brahma module account
    brahmaModuleAccount := k.accountKeeper.GetModuleAccount(ctx, types.ModuleName)
    if brahmaModuleAccount == nil {
        return nil
    }

    // Transfer siphoned tokens
    siphonedCoins := sdk.NewCoins(sdk.NewCoin(baseDenom, toSiphon))
    err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, distributiontypes.ModuleName,
                                                      brahmaModuleAccount.GetAddress(), siphonedCoins)
    if err != nil {
        return err
    }

    // 5. Allocate to each AI's participants
    for aiID, proportion := range aiToSplitOfRewards {
        ai, found := k.GetAI(ctx, aiID)
        if !found {
            continue
        }

        // Convert siphoned Int to Dec, multiply by proportion
        toAllocateForAI := math.LegacyNewDecFromInt(toSiphon).Mul(proportion)

        // Allocate using Dec amounts (methods accept math.LegacyDec)
        k.AllocateToCreator(ctx, toAllocateForAI.Mul(creatorSplit), ai)
        k.AllocateToApplication(ctx, toAllocateForAI.Mul(applicationSplit), ai)
        k.AllocateToContributors(ctx, toAllocateForAI.Mul(contributorSplit), ai)
    }

    return nil
}
```

**Critical implementation details:**

* Must fetch module account first: `GetModuleAccount(ctx, distributiontypes.ModuleName)`
* Use `GetAllBalances()` then `.AmountOf(baseDenom)` (not `GetBalance()`)
* Check for nil module accounts and zero balances before proceeding
* `math.LegacyNewDecFromInt()` converts `sdk.Int` → `math.LegacyDec` for multiplication
* `.TruncateInt()` converts `math.LegacyDec` → `sdk.Int` after multiplication
* This pattern prevents precision loss during percentage calculations

## Complete Workflow Example

**Scenario: Core Grant Application → Approval → Minting**

1. **User submits grant application via smart contract:**

   ```solidity
   brahmaPrecompile.CoreGrantApplication(1000000); // Request 1M cores
   ```
2. **Precompile hook detects event and creates governance proposal:**

   ```
   CoreGrantApplicationEvent emitted
   → PostTxProcessing hook triggered
   → MsgCoreGrantApplication created (with title, description, height)
   → Governance proposal submitted automatically
   ```
3. **Governance voting and execution:**

   ```
   Validators vote on proposal
   → Proposal passes
   → MsgCoreGrantApplication executed
   → CoreGrantApplication stored in state (keyed by requester address)
   ```
4. **User contributes AI tensors:**

   ```solidity
   brahmaPrecompile.submitOreRequest("my-ai", [addr], [contributions]);
   ```
5. **Ore minting and Core NFT distribution:**

   ```
   OreRequestEvent emitted
   → handleOreRequestEvent processes contributions
   → CalculateOresToMint returns map[address]amount
   → IssueCores batches and calls GemCoreNFT.mintCore()
   → Users can claim Core NFTs via claimCore()
   ```
6. **Automated reward distribution each block:**

   ```
   BeginBlock executes
   → SplitInflationPerAI calculates proportions
   → Rewards distributed to creator, Application, contributors
   → Claimable balances updated
   ```

## Next Steps

* Review module APIs for detailed transaction types in [Module API](/developer-guides/module-api.md)
* Explore smart contract implementations in [Smart Contract Integration](/developer-guides/smart-contract.md)
* Learn about security patterns in [Security Considerations](/developer-guides/security-considerations.md)
* Begin development with [Quick Start](/developer-guides/quick-start.md)
