# Source: https://docs.near.org/ai/shade-agents/tutorials/ai-dao/dao-agent-contract.md

---
id: dao-agent-contract
title: DAO Agent Contract
sidebar_label: DAO Agent Contract
description: "Learn about the key parts of the agent contract as part of the Verifiable AI DAO Shade Agent tutorial, including how to create a custom agent contract and create a yield and resume-based Shade Agent."
---




On this page, you'll look at the DAO smart contract that uses the yield and resume pattern to enable the Shade Agent to vote on proposals within a `single transaction` flow.

The AI DAO contract is a fork of the [default agent contract](https://github.com/NearDeFi/shade-agent-js/tree/main/contracts/sandbox), modified to remove the `request_signature` function and implement DAO-specific functionality. This page focuses on the DAO-specific code, as agent registration follows the default contract.

---

## Contract Structure

The DAO agent contract extends the default contract with additional state:
- The DAO's manifesto
- A map of pending proposals
- A map of finalized proposals
- The current proposal ID

```
pub struct Contract {
    pub owner_id: AccountId,
    pub approved_codehashes: IterableSet<String>,
    pub worker_by_account_id: IterableMap<AccountId, Worker>,
    pub manifesto: Manifesto,
    pub pending_proposals: IterableMap<u32, ProposalRequest>,
    pub finalized_proposals: IterableMap<u32, FinalizedProposal>,
    pub current_proposal_id: u32,
}

```

### Manifesto

The manifesto consists of two components: the `manifesto text` that defines the DAO's decision-making principles and a `hash` of the manifesto for verifying that the agent uses the correct manifesto when voting.

```
pub struct Manifesto {
    pub manifesto_text: String,
    pub manifesto_hash: String,
}

```

The manifesto and its hash are initialized as empty strings.

### Pending Proposals

This stores all proposals that are awaiting a vote from the agent. Each proposal request includes the `proposal text` and `yield ID` - a unique identifier for each yielded promise (each active pending proposal request).

```
pub struct ProposalRequest {
    pub yield_id: CryptoHash,
    pub proposal_text: String,
}

```

The map is initialized as empty.

### Finalized Proposals

This stores all proposals that the agent has voted on. Each finalized proposal contains the `proposal text`, `proposal result` (enum of `Approved` or `Rejected`), and `reasoning` for the vote. The result and reasoning are provided by the agent.

<Tabs groupId="code-tabs">
  <TabItem value="finalized-proposal" label="FinalizedProposal">

    ```
pub struct FinalizedProposal {
    pub proposal_text: String,
    pub proposal_result: ProposalResult,
    pub reasoning: String,
}

```

  </TabItem>
  <TabItem value="proposal-result" label="ProposalResult">

    ```
pub enum ProposalResult {
    Approved,
    Rejected,
}

```
  
  </TabItem>
</Tabs>

The map is initialized as empty.

### Current Proposal ID

The current proposal ID is an integer identifier that increments with each proposal request and is used to identify different proposals. If a proposal is not voted on by the agent, then the proposal ID will still increment, leading to `non-consecutive proposal IDs` within the finalized proposals map. Note that the proposal ID is different to the yield ID.

---

## Setting the Manifesto

The contract provides a function to set the manifesto, which only the contract `owner` can call. The owner provides the manifesto text, which is `hashed` and stored along with the text in the contract's state. In production, the owner would typically be a `multisig` contract.

```
    pub fn set_manifesto(&mut self, manifesto_text: String) {
        self.require_owner();

        require!(
            manifesto_text.len() <= 10000,
            "Manifesto text needs to be under 10,000 characters"
        );

        self.manifesto = Manifesto {
            manifesto_text: manifesto_text.clone(),
            manifesto_hash: hash(manifesto_text),
        };
    }

```

---

## Creating a Proposal

When a user calls `create_proposal` with the proposal text, the function creates a `yielded promise`. The promise will call the specified function,`return_external_response`, with the arguments of `proposal_id` and `proposal_text` when the promise resolves. The promise resolves when the agent produces a valid response or the promise times out - after 200 blocks (~2 minutes).

```
        let yielded_promise = env::promise_yield_create(
            "return_external_response", // Function to call when the promise is resumed
            &json!({ "proposal_id": proposal_id, "proposal_text": proposal_text })
                .to_string()
                .into_bytes(),
            RETURN_EXTERNAL_RESPONSE_GAS,
            GasWeight::default(),
            YIELD_REGISTER,
        );

```

The function then reads the `yield ID` from the `register` for the created promise. The `yield ID` is a unique hash identifier that ensures responses are matched to the correct pending proposal. The yield ID is generated by the `register`, which takes an integer identifier that specifies which register is being used, since there is just one yield-resume register here, it's set to zero.

```
        let yield_id: CryptoHash = env::read_register(YIELD_REGISTER)
            .expect("read_register failed")
            .try_into()
            .expect("conversion to CryptoHash failed");

```

A new proposal request is created and inserted into the pending proposals map, allowing the agent to fetch proposals that it needs to respond to.

```
        let proposal_request = ProposalRequest {
            yield_id,
            proposal_text: proposal_text.clone(),
        };

        self.pending_proposals.insert(proposal_id, proposal_request);

```

Lastly, the function `returns the yielded promise` making it ready to be resumed.

```
        env::promise_return(yielded_promise)
    }
```

---

## Agent Response and Validation

Once the agent makes its decision, it calls the `agent_vote` function. This function checks whether the response is valid and resumes the yielded promise if so.

The agent responds with the `yield ID` for the promise it intends to resume, the `proposal ID` it's voting on, the `hash of the proposal`, the `hash of the manifesto`, the `vote`, and the `reasoning` behind the vote.

<Tabs groupId="code-tabs">
  <TabItem value="args" label="Args">

    ```
    pub fn agent_vote(&mut self, yield_id: CryptoHash, response: AiResponse) {
        // Comment this out for local development
```

  </TabItem>
  <TabItem value="ai-response" label="AiResponse">

    ```
pub struct AiResponse {
    proposal_id: u32,
    proposal_hash: String,
    manifesto_hash: String,
    vote: ProposalResult,
    reasoning: String,
}

```
  
  </TabItem>
</Tabs>

Most importantly, the function checks if the caller is a `valid registered agent`, ensuring the DAO only makes decisions through the expected process (that's defined by the verifiable agent).

```
        self.require_approved_codehash();

```

The function verifies that the `manifesto hash` and `proposal hash` submitted by the agent match those stored in the contract. This verification ensures the agent used the correct manifesto and proposal when voting, `removing trust in the RPC` used to fetch proposals and manifesto data. Otherwise, there could be a bug in the RPC causing it to fetch the wrong details and the RPC or a malicious intermediary could intentionally provide the wrong details to try to corrupt the vote.

```
        require!(
            response.manifesto_hash == self.manifesto.manifesto_hash,
            "Manifesto hash mismatch"
        );

        // Verify the proposal exists and hash matches
        let pending_proposal = self
            .pending_proposals
            .get(&response.proposal_id)
            .expect("Proposal not found or already processed");

        require!(
            response.proposal_hash == hash(pending_proposal.proposal_text.clone()),
            "Proposal hash mismatch"
        );

```

If any of these checks fail then the function panics and the promise is not be resumed (it could be resumed later with valid arguments before timeout). If all checks pass, then the function resumes the promise with the specified yield ID and the agent's response as an argument.

```
        env::promise_yield_resume(&yield_id, &serde_json::to_vec(&response).unwrap());
    }
```

---

### Proposal Finalization

When the yielded promise resolves (either resumed or timed out), the `return_external_response` function is called. This function is private and can only be called by the yielded promise, not by external accounts.

The function receives arguments from both when the promise was created and when it was resumed.

```
    #[private]
    pub fn return_external_response(
        &mut self,
        proposal_id: u32,
        proposal_text: String,
        #[callback_result] response: Result<AiResponse, PromiseError>,
    ) -> PromiseOrValue<DaoResponse> {
        self.pending_proposals.remove(&proposal_id);
```

The function first removes the proposal being responded to from the pending proposals map, regardless of whether the promise was resumed or timed out.

```
        self.pending_proposals.remove(&proposal_id);

```

If the response is valid, i.e. the yielded promise was successfully resumed, the proposal and the result are added to the map of finalized proposals, and a response is returned to the caller within the same transaction that the proposal was submitted in.

```
        match response {
            Ok(ai_response) => {
                // Add to finalized proposals and return the decision
                let finalized_proposal = FinalizedProposal {
                    proposal_text,
                    proposal_result: ai_response.vote.clone(),
                    reasoning: ai_response.reasoning.clone(),
                };
                self.finalized_proposals
                    .insert(proposal_id, finalized_proposal);

                PromiseOrValue::Value(DaoResponse {
                    vote: ai_response.vote,
                    reasoning: ai_response.reasoning,
                })
            }
            Err(_) => {
```

If the response is invalid, i.e. the yielded promise timed out, the function returns a promise to call `fail_on_timeout`, which panics and produces a failed [receipt](../../../../protocol/transaction-execution) in a separate block to provide a clear error to the user (the return_external_response receipt is still successful).

<Tabs groupId="code-tabs">
  <TabItem value="promise" label="Promise">

    ```
            Err(_) => {
                // Make a call to fail_on_timeout to cause a failed receipt
                let promise = Promise::new(env::current_account_id()).function_call(
                    "fail_on_timeout".to_string(),
                    vec![],
                    NearToken::from_near(0),
                    FAIL_ON_TIMEOUT_GAS,
                );
                PromiseOrValue::Promise(promise.as_return())
            }
        }
```

  </TabItem>
  <TabItem value="panic-function" label="Panic function">

    ```
    #[private]
    pub fn fail_on_timeout(&self) {
        env::panic_str("Proposal request timed out");
    }

```
  
  </TabItem>
</Tabs>

:::tip
Visit the [yield and resume section](../../../../smart-contracts/anatomy/yield-resume.md) of the docs for a deeper look into this pattern.
:::

---

## View Functions

The contract exposes [view functions](https://github.com/NearDeFi/verifiable-ai-dao/blob/main/contract/src/dao.rs#L188-L223) to retrieve the manifesto text, pending proposals, and finalized proposals.

---

## Next steps

Now that you understand the DAO agent contract implementation, continue to the [agent page](./dao-agent.md) to learn about the verifiable agent that queries the contract for pending requests and casts a vote using an LLM.