> ## Documentation Index
> Fetch the complete documentation index at: https://astral-6ef288be-claude-ascii-globe-mouse-interaction-bupya.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# EAS Resolvers

> Gate smart contract actions with location-based policies

<Warning>
  **Research Preview** — Under active development.
</Warning>

# EAS Resolvers

EAS Resolvers **unlock location-based smart contracts**. They allow you to execute arbitrary logic whenever an attestation is created — turning geospatial policy attestations into triggers for onchain actions.

<Info>
  For background on the Ethereum Attestation Service, see the [EAS documentation](https://docs.attest.org/).
</Info>

## What is a Resolver?

In the Ethereum Attestation Service, a **resolver** is a smart contract that gets called whenever an attestation is made against a specific schema. This enables:

* **Validation**: Accept or reject attestations based on custom logic
* **Side Effects**: Execute actions atomically with attestation creation
* **Composability**: Combine attestations with any onchain logic

## The Pattern

```mermaid theme={null}
sequenceDiagram
    participant User
    participant SDK as Astral SDK
    participant Engine as Geospatial Policy Engine
    participant EAS as EAS Contracts
    participant Resolver as Your Resolver

    User->>SDK: compute.within(...)
    SDK->>Engine: Request computation
    Engine-->>SDK: Signed Policy Attestation
    SDK->>EAS: Submit delegated attestation
    EAS->>Resolver: onAttest() callback
    Resolver->>Resolver: Verify + Execute logic
    Resolver-->>EAS: return true/false
```

## Basic Resolver Contract

<Note>
  **Code snippets need testing** — Verify against actual implementation before use.
</Note>

<Info>
  **About the Astral signer**: The `astralSigner` address is the key that signs policy attestations inside the TEE. Signer management (multisig, key rotation, etc.) is on the roadmap. See [Security](/resources/security) for more details.
</Info>

```solidity theme={null}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@eas/contracts/resolver/SchemaResolver.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract LocationGatedAction is SchemaResolver, Ownable {
    address public astralSigner;

    constructor(IEAS eas, address _astralSigner) SchemaResolver(eas) {
        astralSigner = _astralSigner;
    }

    function onAttest(
        Attestation calldata attestation,
        uint256 /* value */
    ) internal override returns (bool) {
        // 1. Verify from Astral
        require(attestation.attester == astralSigner, "Not from Astral");

        // 2. Decode policy result (BooleanPolicyAttestation)
        (
            bool result,
            bytes32[] memory inputRefs,
            uint64 timestamp,
            string memory operation
        ) = abi.decode(
            attestation.data,
            (bool, bytes32[], uint64, string)
        );

        // 3. Execute business logic
        require(result, "Policy check failed");
        _executeAction(attestation.recipient);

        return true;
    }

    function onRevoke(Attestation calldata, uint256)
        internal pure override returns (bool)
    {
        return false;  // Don't allow revocation
    }

    function _executeAction(address recipient) internal virtual {
        // Override in child contracts
    }

    // Owner-controlled signer update for key rotation
    function updateAstralSigner(address newSigner) external onlyOwner {
        astralSigner = newSigner;
    }
}
```

## Common Patterns

### NFT Minting

```solidity theme={null}
contract LocationNFT is LocationGatedAction, ERC721 {
    mapping(address => bool) public hasMinted;
    uint256 public nextTokenId = 1;

    function _executeAction(address recipient) internal override {
        require(!hasMinted[recipient], "Already minted");
        hasMinted[recipient] = true;
        _mint(recipient, nextTokenId++);
    }
}
```

### Token Distribution

```solidity theme={null}
contract LocationAirdrop is LocationGatedAction {
    IERC20 public token;
    uint256 public amount;

    function _executeAction(address recipient) internal override {
        token.transfer(recipient, amount);
    }
}
```

### Access Control

```solidity theme={null}
contract LocationGate is LocationGatedAction {
    mapping(address => bool) public hasAccess;

    function _executeAction(address recipient) internal override {
        hasAccess[recipient] = true;
    }

    modifier onlyVerified() {
        require(hasAccess[msg.sender], "Location not verified");
        _;
    }
}
```

### Numeric Policies (Distance-Based)

Use numeric policy attestations (distance, area, length) for more sophisticated logic like [spatial demurrage](https://www.johnx.co/notes/spatial-demurrage) — where transfer fees vary based on distance from a target location.

```solidity theme={null}
// Decode numeric policy attestation
(
    uint256 distanceCm,
    string memory units,
    bytes32[] memory inputRefs,
    uint64 timestamp,
    string memory operation
) = abi.decode(attestation.data, (uint256, string, bytes32[], uint64, string));

// Apply distance-based fee (example: 0.1% per km from target)
uint256 distanceKm = distanceCm / 100000;
uint256 fee = (amount * distanceKm) / 1000;
```

## Decoding Attestation Data

### Boolean Policies

```solidity theme={null}
(
    bool result,
    bytes32[] memory inputRefs,
    uint64 timestamp,
    string memory operation
) = abi.decode(
    attestation.data,
    (bool, bytes32[], uint64, string)
);
```

### Numeric Policies

```solidity theme={null}
(
    uint256 result,        // Scaled integer (centimeters)
    string memory units,   // "meters" or "square_meters"
    bytes32[] memory inputRefs,
    uint64 timestamp,
    string memory operation
) = abi.decode(
    attestation.data,
    (uint256, string, bytes32[], uint64, string)
);

// Convert back to meters
uint256 meters = result / 100;
```

## Verification Best Practices

<AccordionGroup>
  <Accordion title="Verify Attester" icon="shield-check">
    Always check `attestation.attester == astralSigner`:

    ```solidity theme={null}
    require(attestation.attester == astralSigner, "Not from Astral");
    ```
  </Accordion>

  <Accordion title="Check Timestamp" icon="clock">
    Prevent replay of old attestations:

    ```solidity theme={null}
    require(timestamp > block.timestamp - 1 hours, "Attestation too old");
    ```
  </Accordion>

  <Accordion title="Verify Input References" icon="link">
    Ensure the right locations were checked:

    ```solidity theme={null}
    require(inputRefs[1] == EXPECTED_LANDMARK_UID, "Wrong location");
    ```
  </Accordion>

  <Accordion title="Track Used Attestations" icon="list-check">
    Prevent reuse of attestations:

    ```solidity theme={null}
    mapping(bytes32 => bool) public usedAttestations;

    function onAttest(...) {
        bytes32 attUid = keccak256(abi.encode(attestation));
        require(!usedAttestations[attUid], "Already used");
        usedAttestations[attUid] = true;
    }
    ```
  </Accordion>
</AccordionGroup>

## Registering Your Schema

```typescript theme={null}
import { SchemaRegistry } from '@ethereum-attestation-service/eas-sdk';

const schemaRegistry = new SchemaRegistry(SCHEMA_REGISTRY_ADDRESS);

// Boolean policy schema
const boolSchema = "bool result,bytes32[] inputRefs,uint64 timestamp,string operation";

const tx = await schemaRegistry.connect(signer).register({
  schema: boolSchema,
  resolverAddress: yourResolver.address,
  revocable: false
});

const receipt = await tx.wait();
const schemaUID = receipt.logs[0].args.uid;
```

## Key Rotation

Resolver contracts should support updating the Astral signer address. For the research preview, we recommend a simple owner-controlled approach:

```solidity theme={null}
function updateAstralSigner(address newSigner) external onlyOwner {
    emit SignerUpdated(astralSigner, newSigner);
    astralSigner = newSigner;
}
```

<Note>
  For production deployments, you may want the owner to be a multisig. We plan to provide more graceful key rotation mechanisms in future releases.
</Note>

<Card title="Next: Verifiable Computation" icon="microchip" href="/concepts/verifiable-computation">
  Learn how EigenCompute provides trust
</Card>
