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

# Call other actors

> Call another actor and use its answer, send it a message, decide who may call you, and send events for others to watch

An actor doesn't have to do everything alone. It can ask another actor for an answer, tell it that something happened, or leave a record for anyone to follow, and which one you want depends on whether you need something back.

Use `call` when you need the answer before going on, such as reading a price or checking a balance. The other actor's handler runs now, inside your call, and you get its return value. Use `send` to tell another actor something happened: a message reaches it a little later, and you get nothing back. Use `runtime.emit_event` to let apps and people follow what the actor does: an entry goes into your actor's event log, with no other actor involved.

This page builds three actors: a price list, a ledger, and a shop that asks one for a price and tells the other about each sale.

First, [install the CLI](https://docs.cowboy.inc/getting-started/install.md). The last step deploys, so it needs a [funded wallet](https://docs.cowboy.inc/build/deploy.md#wallets).

## 1. Create the project

A project can hold several actors. Create a project for the shop, then add the other two:

```bash theme={"system"}
cowboy init shop --dir shop
cd shop
cowboy init ledger
cowboy init prices
rm tests/test_ledger.py tests/test_prices.py
```

```text Output theme={"system"}
✓ Project initialized.
…
Yeehaw! Project ready: /path/to/shop

Check  cowboy check prices
```

## 2. Write the shop

Replace `actors/shop/main.py` with:

```python theme={"system"}
from cowboy_sdk import (
    OWNER,
    Actor,
    ActorCallError,
    Address,
    call,
    callable_by,
    deferred,
    public,
    runtime,
    send,
)


class ShopActor(Actor):
    @public
    def init(self, payload: bytes | None = None) -> None:
        self.storage["prices"] = None
        self.storage["ledger"] = None
        self.storage["orders"] = []

    @callable_by(OWNER)
    def connect(self, peers: object) -> None:
        if not isinstance(peers, dict):
            raise ValueError('send {"prices": address, "ledger": address}')
        self.storage["prices"] = Address(peers["prices"]).to_hex()
        self.storage["ledger"] = Address(peers["ledger"]).to_hex()

    @public
    @deferred
    def order(self, item: object) -> int:
        try:
            price = call(self.storage["prices"], "price", item, cycles_limit=50_000)
        except ActorCallError:
            raise ValueError("the price list did not answer") from None
        if not isinstance(price, int):
            raise ValueError(f"no price for {item!r}")
        orders = self.storage["orders"]
        orders.append({"by": self.sender.to_hex(), "item": item, "price": price})
        self.storage["orders"] = orders
        send(self.storage["ledger"], {"item": item, "price": price})
        runtime.emit_event("order.placed", {"item": item, "price": price})
        return price
```

### Calls

`call(address, handler, args, cycles_limit=...)` runs another actor's handler and returns what it returns. `args` arrives at the other handler as its payload, and `cycles_limit` caps its work. The other actor sees yours as `self.sender`, and it can't call back into your actor during the call.

If it raises, `call` raises `ActorCallError`, and your whole call is undone unless you catch it.

### Messages

`send(address, payload)` sends a message and returns at once. It reaches the other actor's `handle_message` handler a little later, as its own call, with your actor as `self.sender`. If the other actor fails, your call still goes through. A handler that sends must be `@deferred`, as `order` is.

A sent message is delivered once, and only if your call goes through. Your actor pays for delivery, and if it can't pay when the message is due, the message is dropped.

### Events

`runtime.emit_event(name, value)` adds an entry to the actor's event log, which anyone can read with `cowboy actor logs <actor>`.

## 3. Write the ledger

The ledger takes sales only from the shop it trusts. Replace `actors/ledger/main.py` with:

```python theme={"system"}
from cowboy_sdk import OWNER, Actor, Address, PermissionDeniedError, callable_by, public


class LedgerActor(Actor):
    @public
    def init(self, payload: bytes | None = None) -> None:
        self.storage["shop"] = None
        self.storage["total"] = 0

    @callable_by(OWNER)
    def trust(self, shop: object) -> None:
        self.storage["shop"] = Address(shop).to_hex()

    @public
    def handle_message(self, entry: object) -> None:
        if self.sender.to_hex() != self.storage["shop"]:
            raise PermissionDeniedError("only the shop may record sales")
        self.storage["total"] = self.storage["total"] + entry["price"]
```

### Who may call you

A decorator can't hold an address you learn later, such as the shop's here. Instead, make the handler `@public` and check `self.sender` against the stored address first thing, as `handle_message` does. A check that raises undoes the call. [Keep an actor safe](https://docs.cowboy.inc/build/sdk/safety.md) covers the rest.

## 4. Write the price list

The price list answers `price` for anyone, and only its owner sets prices. Replace `actors/prices/main.py` with:

```python theme={"system"}
from cowboy_sdk import OWNER, Actor, callable_by, public


class PricesActor(Actor):
    @public
    def init(self, payload: bytes | None = None) -> None:
        self.storage["prices"] = {}

    @callable_by(OWNER)
    def set_price(self, entry: object) -> None:
        if not isinstance(entry, dict) or not isinstance(entry.get("price"), int):
            raise ValueError('send {"item": name, "price": whole number}')
        prices = self.storage["prices"]
        prices[str(entry["item"])] = entry["price"]
        self.storage["prices"] = prices

    @public
    def price(self, item: object) -> object:
        return self.storage["prices"].get(item)
```

## 5. Test them

The tests don't need the other actors running: `CallMock` stands in for the price list, and `mock_host.get_send_log()` lists the messages the shop sent. Replace `tests/test_shop.py` with:

```python theme={"system"}
import pytest
from cowboy_sdk import Address, PermissionDeniedError, codec, mock_host
from cowboy_sdk.testing import CallMock, SimulatedChain

from actors.ledger.main import LedgerActor
from actors.prices.main import PricesActor
from actors.shop.main import ShopActor

PRICES = b"\x44" * 20
LEDGER = b"\x55" * 20
SHOP = b"\x66" * 20


def open_shop() -> tuple[ShopActor, SimulatedChain, CallMock]:
    shop = ShopActor()
    chain = SimulatedChain(shop)
    mock_host.seed_actor_owner(mock_host.context()["sender"])
    chain.call(shop.init)
    peers = {"prices": "0x" + PRICES.hex(), "ledger": "0x" + LEDGER.hex()}
    chain.call(shop.connect, codec.encode(peers))
    prices = CallMock(default=codec.encode(None))
    prices.when(method="price", args=codec.encode("coffee")).respond(codec.encode(3))
    mock_host.set_call_handler(prices)
    return shop, chain, prices


def test_an_order_asks_the_price_and_tells_the_ledger() -> None:
    shop, chain, prices = open_shop()
    assert codec.decode(chain.call(shop.order, codec.encode("coffee"))) == 3
    target, method, _args, cycles, _access = prices.calls[-1]
    assert (target, method, cycles) == (PRICES, "price", 50_000)
    sent = mock_host.get_send_log()[-1]
    assert sent["target"] == LEDGER
    assert codec.decode(sent["payload"]) == {"item": "coffee", "price": 3}


def test_an_unknown_item_changes_nothing() -> None:
    shop, chain, _prices = open_shop()
    before = chain.snapshot()
    with pytest.raises(ValueError):
        chain.call(shop.order, codec.encode("tea"))
    assert chain.snapshot() == before


def test_the_ledger_takes_sales_only_from_the_shop() -> None:
    ledger = LedgerActor()
    chain = SimulatedChain(ledger)
    mock_host.seed_actor_owner(mock_host.context()["sender"])
    chain.call(ledger.init)
    chain.call(ledger.trust, codec.encode(Address.from_bytes(SHOP).to_hex()))
    chain.set_sender(SHOP)
    chain.call(ledger.handle_message, codec.encode({"item": "coffee", "price": 3}))
    assert codec.decode(chain.state("total")) == 3
    chain.set_sender(b"\x77" * 20)
    with pytest.raises(PermissionDeniedError):
        chain.call(ledger.handle_message, codec.encode({"item": "coffee", "price": 3}))


def test_the_price_list_answers_anyone() -> None:
    prices = PricesActor()
    chain = SimulatedChain(prices)
    mock_host.seed_actor_owner(mock_host.context()["sender"])
    chain.call(prices.init)
    chain.call(prices.set_price, codec.encode({"item": "coffee", "price": 3}))
    chain.set_sender(SHOP)
    assert codec.decode(chain.call(prices.price, codec.encode("coffee"))) == 3
    assert chain.call(prices.price, codec.encode("tea")) is None
    with pytest.raises(PermissionDeniedError):
        chain.call(prices.set_price, codec.encode({"item": "coffee", "price": 0}))
```

```bash theme={"system"}
cowboy check
cowboy test
```

```text Output theme={"system"}
✓ Checked ledger
  …
✓ Checked prices
  …
✓ Checked shop
  …
tests/test_shop.py ....                                                  [100%]
============================== 4 passed in 0.12s ===============================
```

## 6. Connect them

Deploy all three, set a price, then tell the shop and the ledger about each other. Each `deploy` prints the actor's address:

```bash theme={"system"}
cowboy env use cloud
cowboy deploy prices
cowboy deploy shop
cowboy deploy ledger
```

```text Output theme={"system"}
  🤠 Yeehaw! prices is live on cloud

  Actor    0x2858…c20c
  …
  🤠 Yeehaw! shop is live on cloud

  Actor    0x2858…07ee
  …
  🤠 Yeehaw! ledger is live on cloud

  Actor    0x2858…b3a6
```

```bash theme={"system"}
cowboy actor call prices --handler set_price \
  --payload-json '{"item": "coffee", "price": 3}'
cowboy actor call shop --handler connect --payload-json \
  '{"prices": "<prices actor address>", "ledger": "<ledger actor address>"}'
cowboy actor call ledger --handler trust --payload-json '"<shop actor address>"'
```

```text Output theme={"system"}
✓ Transaction submitted
→ Waiting for the receipt (up to 120s)
✓ Transaction executed in block 69436 (1.7s)
```

## 7. Place an order

```bash theme={"system"}
cowboy actor call shop --handler order --payload-json '"coffee"'
```

```text Output theme={"system"}
✓ Transaction submitted
→ Waiting for the receipt (up to 120s)
✓ Transaction executed in block 69436 (1.7s)
```

```bash theme={"system"}
cowboy actor logs shop
```

```text Output theme={"system"}
Fetching event logs for actor: 2858…07ee
Actor Event Log (3 entries):
  [0] height=83768 tx=0x7baf…941b topic=order.placed data={"item":"coffee","price":3}
  [1] height=83768 tx=0x7baf…941b topic=deferred_tx.created data=…
  [2] height=83769 tx=0x5ba4…a0be topic=deferred_tx.executed data=…
```

```bash theme={"system"}
cowboy actor state ledger
```

```text Output theme={"system"}
{
  "__INITIALIZED__": 1,
  "shop": "0x91ab…07ee",
  "total": 3
}
```

`logs` shows `order.placed` with the item and its price. The message reaches the ledger a little after the call, so give it a moment: `state` then shows `total` at 3.

## Next

* [Keep an actor safe](https://docs.cowboy.inc/build/sdk/safety.md)
* [SDK reference: `call`](https://docs.cowboy.inc/developers/sdk-reference#call)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.