# Invoking from Solidity

This guide shows how to invoke Somnia Agents from smart contracts. Your contracts can call agents and receive responses via callbacks.

## Generate Code from the Web App

The easiest way to get started is to use the code generator:

1. Visit <https://agents.somnia.network>
2. Select an agent from the list
3. Click on a method to expand it
4. Select the "Solidity" tab to see generated code for that method
5. Copy the generated code into your project

The generator creates a complete contract with:

* Platform interface definitions
* Agent method encoding
* Callback handling
* Proper error handling

> **Tip:** The "TypeScript" tab generates code for invoking agents from JavaScript/TypeScript — it sends transactions to the platform contract and listens for response events.

## How It Works

{% @mermaid/diagram content="sequenceDiagram
participant Contract as Your Contract
participant Platform as SomniaAgents Contract
participant Committee
participant Validators as Validator Network

```
Contract->>Platform: createRequest{value: deposit}(agentId, callback, selector, payload)
Platform-->>Validators: RequestCreated(..., perAgentBudget, subcommittee)
Validators->>Platform: submitResponse (result, receipt, success, executionCost)
Platform-->>Validators: gas refund (operations reserve)
Platform->>Platform: Reach consensus
Platform->>Contract: handleResponse(requestId, responses[], status, details)
Platform->>Committee: deposit(subcommittee, [median × subSize])
Platform->>Contract: Send rebate (unused budget)
Platform-->>Contract: RequestFinalized event" %}
```

1. Your contract calls `createRequest()` with the agent ID, encoded payload, and a deposit.
2. The platform splits the deposit into an **operations reserve** (`minPerAgentDeposit × subSize`, funds gas refunds / callback / upkeep) and an **agent reward pot** (the rest). `perAgentBudget = rewardPot / subSize` is emitted in `RequestCreated` so runners know their cap.
3. Elected validators execute the agent and submit responses; each submission is gas-refunded from the operations reserve.
4. Once consensus is reached, the platform calls your callback, then pays every elected subcommittee member the **median** of the reported `executionCost` values via the committee.
5. Any unused funds are rebated to the requester.

See [Gas Fees](/agents/invoking-agents/gas-fees.md) for a full fund-flow diagram and details on sizing `msg.value`.

## Platform Interface

```solidity
enum ConsensusType { Majority, Threshold }
enum ResponseStatus {
    None,       // 0 - Default zero value (uninitialized storage)
    Pending,    // 1 - Awaiting responses
    Success,    // 2 - Consensus reached normally
    Failed,     // 3 - Validators reported failure
    TimedOut    // 4 - Request timed out
}

struct Response {
    address validator;
    bytes result;
    ResponseStatus status;
    uint256 receipt;
    uint256 timestamp;
    uint256 executionCost;
}

struct Request {
    uint256 id;
    address requester;
    address callbackAddress;
    bytes4 callbackSelector;
    address[] subcommittee;
    Response[] responses;
    uint256 responseCount;
    uint256 failureCount;
    uint256 threshold;
    uint256 createdAt;
    uint256 deadline;
    ResponseStatus status;
    ConsensusType consensusType;
    uint256 remainingBudget;   // escrow remaining at any point in the lifecycle
    uint256 perAgentBudget;    // max each elected member can claim (set at creation)
}

interface IAgentRequester {
    // Events
    event RequestCreated(uint256 indexed requestId, uint256 indexed agentId, uint256 perAgentBudget, bytes payload, address[] subcommittee);
    event RequestFinalized(uint256 indexed requestId, ResponseStatus status);
    event SubcommitteePaid(uint256 indexed requestId, uint256 totalPaid, uint256 perMember);
    event CommitteeDepositFailed(uint256 indexed requestId, uint256 attemptedAmount);

    // Request Creation (payable - sends budget upfront)
    function createRequest(
        uint256 agentId,
        address callbackAddress,
        bytes4 callbackSelector,
        bytes calldata payload
    ) external payable returns (uint256 requestId);

    function createAdvancedRequest(
        uint256 agentId,
        address callbackAddress,
        bytes4 callbackSelector,
        bytes calldata payload,
        uint256 subcommitteeSize,
        uint256 threshold,
        ConsensusType consensusType,
        uint256 timeout
    ) external payable returns (uint256 requestId);

    // Query Functions
    function getRequest(uint256 requestId) external view returns (Request memory);
    function hasRequest(uint256 requestId) external view returns (bool);

    // Deposit helpers — query the exact msg.value required
    function getRequestDeposit() external view returns (uint256);
    function getAdvancedRequestDeposit(uint256 subcommitteeSize) external view returns (uint256);
}

interface IAgentRequesterHandler {
    function handleResponse(
        uint256 requestId,
        Response[] memory responses,
        ResponseStatus status,
        Request memory details
    ) external;
}
```

**`SomniaAgents` / `AgentRequester` Addresses:**

| Network                    | Address                                      |
| -------------------------- | -------------------------------------------- |
| Mainnet (chain ID `5031`)  | `0x5E5205CF39E766118C01636bED000A54D93163E6` |
| Testnet (chain ID `50312`) | `0x037Bb9C718F3f7fe5eCBDB0b600D607b52706776` |

The examples below use the testnet address; swap in the mainnet address for production.

## Basic Example

```solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

enum ConsensusType { Majority, Threshold }
enum ResponseStatus {
    None,       // 0 - Default zero value (uninitialized storage)
    Pending,    // 1 - Awaiting responses
    Success,    // 2 - Consensus reached normally
    Failed,     // 3 - Validators reported failure
    TimedOut    // 4 - Request timed out
}

struct Response {
    address validator;
    bytes result;
    ResponseStatus status;
    uint256 receipt;
    uint256 timestamp;
    uint256 executionCost;
}

struct Request {
    uint256 id;
    address requester;
    address callbackAddress;
    bytes4 callbackSelector;
    address[] subcommittee;
    Response[] responses;
    uint256 responseCount;
    uint256 failureCount;
    uint256 threshold;
    uint256 createdAt;
    uint256 deadline;
    ResponseStatus status;
    ConsensusType consensusType;
    uint256 remainingBudget;   // escrow remaining at any point in the lifecycle
    uint256 perAgentBudget;    // max each elected member can claim (set at creation)
}

interface IAgentRequester {
    function createRequest(
        uint256 agentId,
        address callbackAddress,
        bytes4 callbackSelector,
        bytes calldata payload
    ) external payable returns (uint256 requestId);

    function getRequestDeposit() external view returns (uint256);
}

interface IJsonApiAgent {
    function fetchUint(string calldata url, string calldata selector, uint8 decimals)
        external returns (uint256);
}

contract PriceOracle {
    IAgentRequester public platform =
        IAgentRequester(0x037Bb9C718F3f7fe5eCBDB0b600D607b52706776);

    uint256 public constant JSON_API_AGENT_ID = 12345678901234567890; // Replace with actual agent ID from the web app
    uint256 public constant SUBCOMMITTEE_SIZE = 3;             // matches the platform default
    uint256 public constant JSON_FETCH_COST_PER_AGENT = 0.03 ether; // see Gas Fees → Current Per-Agent Prices

    uint256 public latestPrice;
    mapping(uint256 => bool) public pendingRequests;

    event PriceRequested(uint256 indexed requestId);
    event PriceReceived(uint256 indexed requestId, uint256 price);

    function requestBitcoinPrice() external payable returns (uint256 requestId) {
        // Encode the agent function call
        bytes memory payload = abi.encodeWithSelector(
            IJsonApiAgent.fetchUint.selector,
            "https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd",
            "bitcoin.usd",
            uint8(8)
        );

        // Safe deposit: contract floor + per-agent execution reward.
        // Floor only would satisfy the contract but runners would skip the
        // request because perAgentBudget < scheduledExecutionCost.
        uint256 reserve = platform.getRequestDeposit();                       // = minPerAgentDeposit × subSize
        uint256 reward  = JSON_FETCH_COST_PER_AGENT * SUBCOMMITTEE_SIZE;
        uint256 deposit = reserve + reward;
        requestId = platform.createRequest{value: deposit}(
            JSON_API_AGENT_ID,
            address(this),
            this.handleResponse.selector,
            payload
        );
        pendingRequests[requestId] = true;

        emit PriceRequested(requestId);
    }

    // Callback function - called by the platform when consensus is reached
    function handleResponse(
        uint256 requestId,
        Response[] memory responses,
        ResponseStatus status,
        Request memory details
    ) external {
        require(msg.sender == address(platform), "Only platform can call");
        require(pendingRequests[requestId], "Unknown request");

        delete pendingRequests[requestId];

        if (status == ResponseStatus.Success && responses.length > 0) {
            latestPrice = abi.decode(responses[0].result, (uint256));
            emit PriceReceived(requestId, latestPrice);
        }
    }

    // Allow receiving rebates
    receive() external payable {}
}
```

## Callback Function Signature

Your callback function must match this signature:

```solidity
function handleResponse(
    uint256 requestId,
    Response[] memory responses,
    ResponseStatus status,
    Request memory details
) external;
```

* `requestId`: The ID returned from `createRequest()`
* `responses`: Array of `Response` structs from validators (each contains `.result` bytes, `.status`, `.validator`, etc.)
* `status`: The overall request status — one of `Success` (2), `Failed` (3), or `TimedOut` (4)
* `details`: The full `Request` struct with metadata (subcommittee, threshold, deadline, etc.)

You can name the function anything, but the parameter types must match. The selector you pass to `createRequest` must match your function.

### Response Status

| Status     | Value | Meaning                                                 |
| ---------- | ----- | ------------------------------------------------------- |
| `None`     | 0     | Default zero value (uninitialized storage)              |
| `Pending`  | 1     | Awaiting responses                                      |
| `Success`  | 2     | Validators reached consensus                            |
| `Failed`   | 3     | Validators reported failure (success became impossible) |
| `TimedOut` | 4     | Request timed out before consensus was reached          |

## Decoding Responses

The `responses` array contains `Response` structs from validators. Each response has a `.result` field with ABI-encoded bytes and a `.status` field. The callback receives **all** responses submitted up to finalization. For `Majority` consensus, at least `threshold` responses will have identical `.result` bytes (the consensus value). For `Threshold` consensus, results may differ.

```solidity
// Decode from the first successful response
if (status == ResponseStatus.Success && responses.length > 0) {
    // Single uint256
    uint256 value = abi.decode(responses[0].result, (uint256));

    // Single string
    string memory text = abi.decode(responses[0].result, (string));

    // Multiple values
    (uint256 value, string memory message) = abi.decode(responses[0].result, (uint256, string));
}

// Iterate all responses (useful for Threshold consensus)
for (uint256 i = 0; i < responses.length; i++) {
    if (responses[i].status == ResponseStatus.Success) {
        uint256 value = abi.decode(responses[i].result, (uint256));
        // Process each validator's result...
    }
}
```

## Multiple Callbacks

Use different callbacks for different response types:

```solidity
contract MultiAgentConsumer {
    IAgentRequester public platform =
        IAgentRequester(0x037Bb9C718F3f7fe5eCBDB0b600D607b52706776);

    // Request with uint256 response
    function requestPrice() external payable {
        bytes memory payload = abi.encodeWithSelector(...);

        platform.createRequest{value: msg.value}(
            PRICE_AGENT_ID,
            address(this),
            this.handlePriceResponse.selector,
            payload
        );
    }

    // Request with string response
    function requestText() external payable {
        bytes memory payload = abi.encodeWithSelector(...);

        platform.createRequest{value: msg.value}(
            TEXT_AGENT_ID,
            address(this),
            this.handleTextResponse.selector,
            payload
        );
    }

    function handlePriceResponse(
        uint256 requestId,
        Response[] memory responses,
        ResponseStatus status,
        Request memory details
    ) external {
        require(msg.sender == address(platform), "Only platform");
        if (status == ResponseStatus.Success && responses.length > 0) {
            uint256 price = abi.decode(responses[0].result, (uint256));
            // Process price...
        }
    }

    function handleTextResponse(
        uint256 requestId,
        Response[] memory responses,
        ResponseStatus status,
        Request memory details
    ) external {
        require(msg.sender == address(platform), "Only platform");
        if (status == ResponseStatus.Success && responses.length > 0) {
            string memory text = abi.decode(responses[0].result, (string));
            // Process text...
        }
    }

    receive() external payable {}
}
```

## Error Handling

Handle all response statuses gracefully:

```solidity
function handleResponse(
    uint256 requestId,
    Response[] memory responses,
    ResponseStatus status,
    Request memory details
) external {
    require(msg.sender == address(platform), "Only platform");

    if (status == ResponseStatus.Failed) {
        emit AgentFailed(requestId);
        // Validators reported execution failure
        return;
    }

    if (status == ResponseStatus.TimedOut) {
        emit AgentTimedOut(requestId);
        // Request timed out - maybe retry
        return;
    }

    // Success
    if (responses.length == 0) {
        emit EmptyResponse(requestId);
        return;
    }

    // Decode and process response...
}
```

## Security Considerations

1. **Always verify the caller**: Check that `msg.sender` is the platform contract
2. **Track pending requests**: Use a mapping to validate incoming callbacks
3. **Handle all statuses**: Check `status` — requests can succeed, fail, or time out
4. **Validate responses length**: Check `responses.length` before decoding
5. **Accept rebates**: Implement `receive()` so your contract can receive rebated funds

## Next Steps

* [Gas Fees](/agents/invoking-agents/gas-fees.md) — Understand costs


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.somnia.network/agents/invoking-agents/from-solidity.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
