Skip to main content
Research preview — The SDK is under active 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://staging-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(input);
astral.compute.estimate(attestation);
astral.compute.health();

Input types

All compute methods accept flexible input types:
type Input =
  | string                         // Onchain attestation UID
  | GeoJSON.Geometry               // Raw GeoJSON geometry
  | { uid: string }                // Onchain reference
  | { uid: string; uri: string }   // Offchain reference
  | { verifiedProof: VerifiedLocationProof }  // Verified proof input

Examples

// Using attestation UID
const result = await astral.compute.distance(
  '0x1234...abcd',
  '0x5678...efgh',
  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,
  { type: 'Point', coordinates: [lon, lat] },
  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 spatial 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
});

// WRONG: Boolean operation with location schema
await compute.within(point, target, 500, {
  schema: LOCATION_SCHEMA_UID
});
// 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>
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'

area()

Calculate the area of a polygon.
astral.compute.area(
  geometry: Input,
  options: ComputeOptions
): Promise<NumericComputeResult>
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>
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>
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>
const result = await astral.compute.within(
  userLocationUID,
  landmarkUID,
  500,
  { schema: SCHEMA_UID, recipient: userAddress }
);

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

intersects()

Check if two geometries intersect.
astral.compute.intersects(
  a: Input,
  b: Input,
  options: ComputeOptions
): Promise<BooleanComputeResult>
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. Accepts two input formats:
// Format 1: DelegatedAttestation directly
astral.compute.submit(
  attestation: DelegatedAttestation
): Promise<AttestationResult>

// Format 2: Object with both attestation types
astral.compute.submit(
  input: {
    attestation: AttestationObject;
    delegatedAttestation: DelegatedAttestationObject;
  }
): Promise<AttestationResult>
const result = await astral.compute.within(
  userLocationUID, landmarkUID, 500, { schema: SCHEMA_UID }
);

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>
const gas = await astral.compute.estimate(result.delegatedAttestation);
console.log('Estimated gas:', gas.toString());

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

health()

Check the compute service health status.
astral.compute.health(): Promise<HealthStatus>
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;
  units: string;               // 'meters' or 'square_meters'
  operation: string;           // 'distance', 'area', or 'length'
  timestamp: number;
  inputRefs: string[];         // UIDs or hashes of inputs
  attestation: AttestationObject;
  delegatedAttestation: DelegatedAttestationObject;
  proofInputs?: ProofInputContext[];  // When using VerifiedLocationProof inputs
}

BooleanComputeResult

interface BooleanComputeResult {
  result: boolean;
  operation: string;           // 'contains', 'within', or 'intersects'
  timestamp: number;
  inputRefs: string[];
  attestation: AttestationObject;
  delegatedAttestation: DelegatedAttestationObject;
  proofInputs?: ProofInputContext[];
}

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;
}

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);

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);
}

Complete example

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

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://staging-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 a landmark
const LANDMARK_UID = '0x...';
const SCHEMA_UID = '0x...';

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

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

// Estimate gas and submit if check passed
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);
}

Next: EAS integration

Learn how the SDK integrates with EAS