Skip to main content
Research Preview — The SDK is under development.

Compute Module

The ComputeModule provides verifiable geospatial operations that run in a trusted execution environment. Each operation returns a signed delegated attestation that can be submitted to EAS.
import { AstralSDK } from '@decentralized-geo/astral-sdk';

const astral = new AstralSDK({
  chainId: 84532,
  signer: wallet,
  apiUrl: 'https://api.astral.global'
});

// Access via astral.compute
astral.compute.distance(from, to, options);
astral.compute.area(geometry, options);
astral.compute.length(geometry, options);
astral.compute.contains(container, containee, options);
astral.compute.within(geometry, target, radius, options);
astral.compute.intersects(a, b, options);
astral.compute.submit(delegatedAttestation);
astral.compute.estimate(delegatedAttestation);
astral.compute.health();

Input Types

All compute methods accept flexible input types:
type Input =
  | string                    // Onchain attestation UID
  | GeoJSON.Geometry          // Raw GeoJSON geometry
  | GeoJSON.Feature           // GeoJSON Feature (geometry extracted)
  | { uid: string }           // Onchain reference
  | { uid: string, uri: string }  // Offchain reference

Examples

// Using attestation UID
const result = await astral.compute.distance(
  '0x1234...abcd',  // UID
  '0x5678...efgh',  // UID
  options
);

// Using raw GeoJSON
const result = await astral.compute.distance(
  { type: 'Point', coordinates: [2.2945, 48.8584] },
  { type: 'Point', coordinates: [-0.1276, 51.5074] },
  options
);

// Mixing UIDs and GeoJSON
const result = await astral.compute.contains(
  boundaryUID,  // Polygon UID
  { type: 'Point', coordinates: [lon, lat] },  // Raw point
  options
);
Raw GeoJSON is not verified for authenticity. The inputRefs will contain a keccak256 hash of the geometry rather than a UID.

Compute Options

All methods require options:
interface ComputeOptions {
  schema: string;       // Required: EAS schema UID for the result
  recipient?: string;   // Optional: recipient address (defaults to zero address)
}

Schema Requirements

Use the correct schema type - Compute operations encode data differently than Location attestations. Using the wrong schema UID causes a data/schema mismatch where the attestation is stored but cannot be decoded correctly.
OperationRequired Schema Type
distance, area, lengthNumericPolicyAttestation
contains, within, intersectsBooleanPolicyAttestation
// CORRECT: Boolean operation with Boolean schema
await compute.within(point, target, 500, {
  schema: BOOLEAN_POLICY_SCHEMA_UID  // bool result, bytes32[] inputRefs, ...
});

// WRONG: Boolean operation with Location schema
await compute.within(point, target, 500, {
  schema: LOCATION_SCHEMA_UID  // bytes geometry, string geometryType, ...
});
// The attestation is created but data is unreadable!
See Schemas for schema definitions and default UIDs.

Numeric Operations

distance()

Calculate the geodesic distance between two geometries.
astral.compute.distance(
  from: Input,
  to: Input,
  options: ComputeOptions
): Promise<NumericComputeResult>

Example

const result = await astral.compute.distance(
  userLocationUID,
  landmarkUID,
  { schema: SCHEMA_UID }
);

console.log(result.result);  // 523.45
console.log(result.units);   // 'meters'
console.log(result.operation);  // 'distance'

// Submit onchain
await astral.compute.submit(result.delegatedAttestation);

area()

Calculate the area of a polygon.
astral.compute.area(
  geometry: Input,
  options: ComputeOptions
): Promise<NumericComputeResult>

Example

const result = await astral.compute.area(
  propertyBoundaryUID,
  { schema: SCHEMA_UID }
);

console.log(result.result);  // 5432.10
console.log(result.units);   // 'square_meters'

length()

Calculate the length of a LineString.
astral.compute.length(
  geometry: Input,
  options: ComputeOptions
): Promise<NumericComputeResult>

Example

const result = await astral.compute.length(
  routeUID,
  { schema: SCHEMA_UID }
);

console.log(result.result);  // 2345.67
console.log(result.units);   // 'meters'

Boolean Operations

contains()

Check if a container geometry contains another geometry.
astral.compute.contains(
  container: Input,
  containee: Input,
  options: ComputeOptions
): Promise<BooleanComputeResult>

Example

const result = await astral.compute.contains(
  geofencePolygonUID,
  userLocationUID,
  { schema: SCHEMA_UID }
);

if (result.result) {
  console.log('User is inside the geofence!');
  await astral.compute.submit(result.delegatedAttestation);
}

within()

Check if a geometry is within a specified radius of a target.
astral.compute.within(
  geometry: Input,
  target: Input,
  radius: number,  // meters
  options: ComputeOptions
): Promise<BooleanComputeResult>

Example

const result = await astral.compute.within(
  userLocationUID,
  landmarkUID,
  500,  // 500 meters
  { schema: SCHEMA_UID, recipient: userAddress }
);

if (result.result) {
  console.log('User is within 500m of the landmark!');
  // Submit to trigger resolver action
  await astral.compute.submit(result.delegatedAttestation);
}

intersects()

Check if two geometries intersect (overlap).
astral.compute.intersects(
  a: Input,
  b: Input,
  options: ComputeOptions
): Promise<BooleanComputeResult>

Example

const result = await astral.compute.intersects(
  territory1UID,
  territory2UID,
  { schema: SCHEMA_UID }
);

console.log('Territories overlap:', result.result);

Submission Methods

submit()

Submit a delegated attestation to EAS. You pay gas; Astral remains the attester.
astral.compute.submit(
  attestation: DelegatedAttestation
): Promise<AttestationResult>

Example

// Compute first
const result = await astral.compute.within(
  userLocationUID,
  landmarkUID,
  500,
  { schema: SCHEMA_UID }
);

// Submit if the check passed
if (result.result) {
  const submission = await astral.compute.submit(result.delegatedAttestation);
  console.log('Attestation UID:', submission.uid);
}

estimate()

Estimate gas for attestation submission.
astral.compute.estimate(
  attestation: DelegatedAttestation
): Promise<bigint>

Example

const gas = await astral.compute.estimate(result.delegatedAttestation);
console.log('Estimated gas:', gas.toString());

// Proceed if acceptable
if (gas < 200000n) {
  await astral.compute.submit(result.delegatedAttestation);
}

health()

Check the compute service health status.
astral.compute.health(): Promise<HealthStatus>

Example

const status = await astral.compute.health();
console.log('Service status:', status.status);
console.log('Database status:', status.database);

Return Types

NumericComputeResult

interface NumericComputeResult {
  result: number;           // The computed value
  units: string;            // 'meters' or 'square_meters'
  operation: string;        // 'distance', 'area', or 'length'
  timestamp: number;        // Unix timestamp
  inputRefs: string[];      // UIDs or hashes of inputs
  attestation: {
    schema: string;
    attester: string;
    recipient: string;
    data: string;
    signature: string;
  };
  delegatedAttestation: {
    message: DelegatedAttestationMessage;
    signature: { v: number; r: string; s: string };
    attester: string;
  };
}

BooleanComputeResult

interface BooleanComputeResult {
  result: boolean;          // The computed boolean
  operation: string;        // 'contains', 'within', or 'intersects'
  timestamp: number;        // Unix timestamp
  inputRefs: string[];      // UIDs or hashes of inputs
  attestation: {
    schema: string;
    attester: string;
    recipient: string;
    data: string;
    signature: string;
  };
  delegatedAttestation: {
    message: DelegatedAttestationMessage;
    signature: { v: number; r: string; s: string };
    attester: string;
  };
}

DelegatedAttestation

interface DelegatedAttestation {
  message: {
    schema: string;
    recipient: string;
    expirationTime: bigint;
    revocable: boolean;
    refUID: string;
    data: string;
    value: bigint;
    nonce: bigint;
    deadline: bigint;
  };
  signature: {
    v: number;
    r: string;
    s: string;
  };
  attester: string;
}

Complete Example

import { AstralSDK } from '@decentralized-geo/astral-sdk';
import { ethers } from 'ethers';

// Setup
const provider = new ethers.JsonRpcProvider('https://sepolia.base.org');
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);

const astral = new AstralSDK({
  chainId: 84532,
  signer: wallet,
  apiUrl: 'https://api.astral.global'
});

// Create a location attestation
const location = await astral.location.onchain.create({
  location: { type: 'Point', coordinates: [2.2951, 48.8580] },
  memo: 'Near Eiffel Tower'
});

// Check proximity to landmark
const EIFFEL_TOWER_UID = '0x...';  // Pre-registered landmark
const SCHEMA_UID = '0x...';       // Your resolver schema

const result = await astral.compute.within(
  location.uid,
  EIFFEL_TOWER_UID,
  500,  // 500 meters
  { schema: SCHEMA_UID, recipient: wallet.address }
);

console.log('Within 500m:', result.result);
console.log('Input refs:', result.inputRefs);

// Submit to trigger resolver
if (result.result) {
  const gas = await astral.compute.estimate(result.delegatedAttestation);
  console.log('Estimated gas:', gas.toString());

  const submission = await astral.compute.submit(result.delegatedAttestation);
  console.log('Attestation submitted:', submission.uid);
}

How Delegated Attestations Work

StepWhoDoes What
1DeveloperCalls compute.within() or other method
2Astral APIRuns computation in TEE, signs attestation
3DeveloperReceives signed delegatedAttestation
4DeveloperCalls compute.submit() (pays gas)
5EASVerifies signature, records Astral as attester
6ResolverChecks attestation.attester == astralSigner
This pattern means:
  • Astral does not need to pay gas for every computation
  • Developers control when/whether to submit onchain
  • Smart contracts can verify attestations came from Astral

Signature Expiry

Delegated attestation signatures have a deadline:
const result = await astral.compute.within(uid1, uid2, 500, options);

// Check deadline (Unix timestamp)
const deadline = result.delegatedAttestation.message.deadline;
const now = BigInt(Math.floor(Date.now() / 1000));

if (now < deadline) {
  await astral.compute.submit(result.delegatedAttestation);
} else {
  // Signature expired - request new computation
  const newResult = await astral.compute.within(uid1, uid2, 500, options);
  await astral.compute.submit(newResult.delegatedAttestation);
}

Next: EAS Integration

Learn how the SDK integrates with EAS