> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kontor.network/llms.txt
> Use this file to discover all available pages before exploring further.

# Cross-Contract Calls

Sigil contracts can call other contracts with full type safety. The framework provides two macros for importing contract interfaces: `interface!` for dynamic addresses and `import!` for fixed addresses.

## The interface! Macro

Import another contract's interface without specifying its address:

```rust theme={null}
interface!(name = "token", path = "token/wit");
```

**What it does:**

* Reads the WIT file from the specified path
* Generates type-safe Rust bindings
* Creates functions you can call from your contract

## The import! Macro

Import a specific contract instance at a known address:

```rust theme={null}
import!(
    name = "token",
    mod_name = "my_token",
    height = 100,
    tx_index = 0,
    path = "token/wit"
);
```

**Parameters:**

* `name` - Contract name
* `mod_name` - Rust module name (avoid conflicts)
* `height` - Block height where contract was deployed
* `tx_index` - Transaction index in that block
* `path` - Path to WIT file

The macro generates functions similar to `interface!` but with the contract address fixed at compile time.

### When to Use Each

**Use `interface!` for dynamic addresses** (passed as parameters):

```rust theme={null}
fn deposit(ctx: &ProcContext, token: ContractAddress, amount: Integer) -> Result<(), Error> {
    token::transfer(&token, ctx.signer(), &ctx.contract_signer().to_string(), amount)?;
    // ...
}
```

**Use `import!` for fixed addresses** (known at compile time):

```rust theme={null}
// In tests or contracts with known dependencies
import!(
    name = "arith",
    mod_name = "arith_v1",
    height = 1,
    tx_index = 0,
    path = "arith/wit"
);

fn calculate(ctx: &ProcContext, x: u64) -> u64 {
    arith_v1::eval(ctx.signer(), x, arith_v1::Op::Id).value
}
```

## Standard Interfaces

The `interface!` macro enables **standards** similar to Ethereum's ERC-20: define an interface once, and contracts can work with any implementation of that interface.

### Defining a Token Standard

Extract common token functions into a standalone WIT file that any token can implement:

```wit theme={null}
// erc20-standard.wit
package erc20:standard;

interface erc20 {
    use kontor:built-in/context.{view-context, proc-context};
    use kontor:built-in/error.{error};
    use kontor:built-in/numbers.{integer};

    total-supply: func(ctx: borrow<view-context>) -> integer;
    balance-of: func(ctx: borrow<view-context>, account: string) -> integer;
    transfer: func(ctx: borrow<proc-context>, to: string, amount: integer) -> result<_, error>;
    approve: func(ctx: borrow<proc-context>, spender: string, amount: integer) -> result<_, error>;
    allowance: func(ctx: borrow<view-context>, owner: string, spender: string) -> integer;
    transfer-from: func(ctx: borrow<proc-context>, from: string, to: string, amount: integer) -> result<_, error>;
}
```

### Using Standards with Dynamic Addresses

Contracts accept any contract address that implements the standard interface:

```rust theme={null}
contract!(name = "dex");

// Import the standard interface
interface!(name = "erc20", path = "../erc20-standard.wit");

impl Guest for Dex {
    fn swap(
        ctx: &ProcContext,
        token: ContractAddress,  // User can pass any token
        amount: Integer,
    ) -> Result<Integer, Error> {
        // Compiler ensures this call is valid for the interface
        // Runtime error if token doesn't actually implement erc20
        erc20::transfer(&token, ctx.signer(), &ctx.contract_signer().to_string(), amount)?;
        Ok(amount)
    }
}
```

The [AMM example](/docs/sigil/examples/amm) demonstrates this pattern, accepting any token address and calling its transfer functions.

### How This Differs from Ethereum

**Ethereum's ERC-20:** A social convention where contracts implement functions named `transfer`, `balanceOf`, `approve`, etc. No compile-time or runtime verification that a contract actually implements the standard—you just call functions and hope they exist.

**Sigil's `interface!`:** The WIT interface provides strong typing in your contract code:

* The compiler ensures your contract makes valid calls to the interface
* The compiler ensures implementations export all required functions with correct signatures
* The runtime validates that the target contract matches the interface before executing calls
* Interface mismatches surface as clear runtime errors, not undefined behavior

## Making Cross-Contract Calls

### Basic Example

From the shared-account contract:

```rust theme={null}
use stdlib::*;

contract!(name = "shared-account");

// Import token interface dynamically
interface!(name = "token", path = "token/wit");

impl Guest for SharedAccount {
    fn deposit(
        ctx: &ProcContext,
        token: ContractAddress,  // Dynamic token address
        account_id: String,
        n: Integer,
    ) -> Result<(), Error> {
        // Call token contract's transfer function
        token::transfer(&token, ctx.signer(), &ctx.contract_signer().to_string(), n)?;

        // Update local state
        let account = ctx.model().accounts().get(account_id).ok_or(unknown_error())?;
        account.update_balance(|b| b + n);

        Ok(())
    }

    fn token_balance(
        _ctx: &ViewContext,
        token: ContractAddress,
        holder: String,
    ) -> Option<Integer> {
        // Call token contract's balance function (view)
        token::balance(&token, &holder)
    }
}
```

## ContractAddress Type

```rust theme={null}
pub struct ContractAddress {
    pub name: String,
    pub height: s64,
    pub tx_index: s64,
}
```

**Creating addresses:**

```rust theme={null}
let addr = ContractAddress {
    name: "token".to_string(),
    height: 100,
    tx_index: 0,
};
```

## Call Context and Signers

### The Signer Parameter

When making cross-contract calls, you choose whose authority to use:

**Execute as the current user:**

```rust theme={null}
fn transfer_from_caller(ctx: &ProcContext, token: ContractAddress, to: String, n: Integer) -> Result<(), Error> {
    // Transfer from caller's account
    token::transfer(&token, ctx.signer(), &to, n)
}
```

**Execute as the contract itself:**

```rust theme={null}
fn transfer_from_contract(ctx: &ProcContext, token: ContractAddress, to: String, n: Integer) -> Result<(), Error> {
    // Transfer from contract's own account
    token::transfer(&token, ctx.contract_signer(), &to, n)
}
```

**When to use contract\_signer():**

* Contract holds tokens on behalf of users
* Contract acts as custodian
* Escrow patterns

## Error Propagation

Errors from cross-contract calls propagate automatically with `?`:

```rust theme={null}
fn fib_of_sub(
    ctx: &ProcContext,
    arith_address: ContractAddress,
    x: String,
    y: String,
) -> Result<u64, Error> {
    // If arith::checked_sub returns an error, it propagates
    let n = arith::checked_sub(&arith_address, &x, &y)?;
    Ok(Self::fib(ctx, arith_address, n))
}
```

**Cross-contract errors roll back all storage changes** in the entire call chain.

## Re-entrancy Protection

The runtime prevents re-entrancy automatically:

```
Contract A calls Contract B
Contract B tries to call Contract A
→ Error: "reentrancy prevented"
```

**Example:**

```rust theme={null}
// arith contract calls fib contract
// fib contract calls back to arith -> OK (different direction)

// But if arith calls fib, and fib calls back to arith:
// -> Runtime error: "reentrancy prevented"
```

**Why this matters:**

* Prevents common exploit patterns
* Simplifies reasoning about contract behavior
* No need for re-entrancy guards

## Multi-Contract Interaction Example

AMM (Automated Market Maker) calling Token contract:

```rust theme={null}
contract!(name = "amm");
interface!(name = "token_dyn", path = "token/wit");

fn create(
    ctx: &ProcContext,
    pair: TokenPair,
    amount_a: Integer,
    amount_b: Integer,
    fee_bps: Integer,
) -> Result<Integer, Error> {
    // Transfer tokens from user to AMM contract
    let custodian = ctx.contract_signer().to_string();

    token_dyn::transfer(&pair.a, ctx.signer(), &custodian, amount_a)?;
    token_dyn::transfer(&pair.b, ctx.signer(), &custodian, amount_b)?;

    // Create pool
    let lp_shares = (amount_a * amount_b).sqrt()?;
    
    // Store pool data in AMM's storage
    // ...

    Ok(lp_shares)
}
```

**Pattern:**

1. Call external contracts first (get tokens)
2. Validate received amounts
3. Update local state
4. Return results

## The foreign::call Low-Level API

For advanced use cases, you can make dynamic calls:

```rust theme={null}
use stdlib::*;

fn fallback(ctx: &FallContext, expr: String) -> String {
    if let Some(addr) = ctx.view_context().model().contract_address() {
        // Dynamic call to any contract
        foreign::call(ctx.signer(), &addr, &expr)
    } else {
        "".to_string()
    }
}
```

**Signature:**

```rust theme={null}
pub fn call(
    signer: Option<&Signer>,
    contract_address: &ContractAddress,
    expr: &str
) -> String
```

**When to use:**

* Proxy contracts
* Generic delegation patterns
* When you need to construct calls dynamically
* Fallback handlers

**Most contracts should use `interface!` or `import!` instead** for type safety.

## Best Practices

**1. Use interface! for flexibility**

```rust theme={null}
// Good: Accept any token
fn deposit(ctx: &ProcContext, token: ContractAddress, n: Integer) -> Result<(), Error> {
    token::transfer(&token, ctx.signer(), &ctx.contract_signer().to_string(), n)
}
```

**2. Validate before external calls (CEI pattern)**

```rust theme={null}
fn withdraw(ctx: &ProcContext, token: ContractAddress, n: Integer) -> Result<(), Error> {
    // Check: Verify authorization and balance
    let account = ctx.model().account();
    if account.balance() < n {
        return Err(insufficient_balance_error());
    }

    // Effect: Update local state first
    account.set_balance(account.balance() - n);

    // Interaction: Make external call last
    token::transfer(&token, ctx.contract_signer(), &ctx.signer().to_string(), n)?;

    Ok(())
}
```

**3. Handle cross-contract errors**

```rust theme={null}
// Propagate errors
token::transfer(&addr, signer, to, amount)?;

// Or handle explicitly
match token::transfer(&addr, signer, to, amount) {
    Ok(()) => { /* success */ },
    Err(e) => {
        // Log or handle error
        return Err(Error::Message(format!("Transfer failed: {}", e)));
    }
}
```

**4. Document contract dependencies**

```rust theme={null}
// At top of contract
//! This contract requires a token contract implementing:
//! - transfer(to: string, n: integer) -> result<_, error>
//! - balance(acc: string) -> option<integer>
```
