@hyperlane-xyz/sdk
Version:
The official SDK for the Hyperlane Network
66 lines • 2.43 kB
JavaScript
import { assert, rootLogger } from '@hyperlane-xyz/utils';
import { z } from 'zod';
const DEFAULT_PREDICATE_API_URL = 'https://api.predicate.io/v2/attestation';
export const PredicateAttestationSchema = z.object({
uuid: z.string(),
expiration: z.number(),
attester: z.string(),
signature: z.string(),
});
const PredicateAttestationResponseSchema = z.object({
policy_id: z.string(),
policy_name: z.string(),
verification_hash: z.string(),
is_compliant: z.boolean(),
attestation: PredicateAttestationSchema,
});
export class PredicateApiClient {
logger = rootLogger.child({ module: 'PredicateApiClient' });
baseUrl;
apiKey;
constructor(apiKey, baseUrl = DEFAULT_PREDICATE_API_URL) {
assert(apiKey, 'Predicate API key is required');
this.apiKey = apiKey;
this.baseUrl = baseUrl;
}
async fetchAttestation(request) {
this.logger.debug('Fetching attestation', {
url: this.baseUrl,
request,
});
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 30_000);
let response;
try {
response = await fetch(this.baseUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': this.apiKey,
},
body: JSON.stringify(request),
signal: controller.signal,
});
}
finally {
clearTimeout(timeoutId);
}
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Predicate API error (${response.status}): ${errorText}`);
}
const result = PredicateAttestationResponseSchema.parse(await response.json());
if (!result.is_compliant) {
throw new Error(`Transaction not compliant: policy=${result.policy_id}, hash=${result.verification_hash}`);
}
const now = Math.floor(Date.now() / 1000);
if (result.attestation.expiration <= now) {
throw new Error(`Attestation already expired (expiration=${result.attestation.expiration}, now=${now})`);
}
this.logger.debug('Attestation received', {
uuid: result.attestation.uuid,
});
return result;
}
}
//# sourceMappingURL=PredicateApiClient.js.map