@attestprotocol/stellar-sdk
Version:
Stellar implementation of the Attest Protocol SDK
1,438 lines (1,433 loc) • 48.2 kB
JavaScript
import { Address, scValToNative, rpc, Networks } from '@stellar/stellar-sdk';
import { createErrorResponse, createAttestProtocolError, AttestProtocolErrorType, createSuccessResponse, AttestProtocolBase } from '@attestprotocol/core';
export { AttestProtocolErrorType, createAttestProtocolError, createErrorResponse, createSuccessResponse } from '@attestprotocol/core';
import { networks, Client } from '@attestprotocol/stellar/dist/bindings/src/protocol';
export { Client as ProtocolClient, networks as ProtocolNetworks } from '@attestprotocol/stellar/dist/bindings/src/protocol';
import { networks as networks$1, Client as Client$1 } from '@attestprotocol/stellar/dist/bindings/src/authority';
export { Client as AuthorityClient, networks as AuthorityNetworks } from '@attestprotocol/stellar/dist/bindings/src/authority';
var __defProp = Object.defineProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __esm = (fn, res) => function __init() {
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
};
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
// src/common/schema-utils.ts
async function generateSchemaUid(schemaDefinition, authority, resolver) {
let dataToHash = schemaDefinition + authority;
if (resolver) {
dataToHash += resolver;
}
const encoder = new TextEncoder();
const data = encoder.encode(dataToHash);
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
}
async function generateIdFromSchema(schema, authority) {
const auth = authority || "";
return generateSchemaUid(schema.content, auth, schema.resolver);
}
function formatSchemaUid(uid) {
if (uid.length !== 64) {
return uid;
}
return `${uid.slice(0, 8)}-${uid.slice(8, 16)}-${uid.slice(16, 24)}-${uid.slice(24, 32)}-${uid.slice(32)}`;
}
function parseFormattedUid(formattedUid) {
return formattedUid.replace(/-/g, "");
}
var init_schema_utils = __esm({
"src/common/schema-utils.ts"() {
}
});
var StellarDataType, SchemaValidationError, StellarSchemaEncoder, StellarSchemaRegistry;
var init_schema_encoder = __esm({
"src/common/schema-encoder.ts"() {
StellarDataType = /* @__PURE__ */ ((StellarDataType2) => {
StellarDataType2["STRING"] = "string";
StellarDataType2["BOOL"] = "bool";
StellarDataType2["U32"] = "u32";
StellarDataType2["U64"] = "u64";
StellarDataType2["I32"] = "i32";
StellarDataType2["I64"] = "i64";
StellarDataType2["I128"] = "i128";
StellarDataType2["ADDRESS"] = "address";
StellarDataType2["BYTES"] = "bytes";
StellarDataType2["SYMBOL"] = "symbol";
StellarDataType2["ARRAY"] = "array";
StellarDataType2["OPTION"] = "option";
StellarDataType2["MAP"] = "map";
StellarDataType2["TIMESTAMP"] = "timestamp";
StellarDataType2["AMOUNT"] = "amount";
return StellarDataType2;
})(StellarDataType || {});
SchemaValidationError = class extends Error {
constructor(message, field) {
super(message);
this.field = field;
this.name = "SchemaValidationError";
}
};
StellarSchemaEncoder = class _StellarSchemaEncoder {
constructor(schema) {
this.validateSchema(schema);
this.schema = schema;
}
/**
* Get the schema definition
*/
getSchema() {
return { ...this.schema };
}
/**
* Generate a unique hash for this schema
*/
getSchemaHash() {
const schemaString = JSON.stringify({
name: this.schema.name,
version: this.schema.version,
fields: this.schema.fields.map((f) => ({ name: f.name, type: f.type, optional: f.optional }))
});
const encoder = new TextEncoder();
const data = encoder.encode(schemaString);
return Array.from(new Uint8Array(data.slice(0, 32))).map((b) => b.toString(16).padStart(2, "0")).join("");
}
/**
* Encode attestation data according to the schema
*/
async encodeData(data) {
this.validateData(data);
const encodedData = JSON.stringify(this.processDataForEncoding(data));
const schemaHash = this.getSchemaHash();
return {
schemaHash,
encodedData,
decodedData: { ...data },
schema: this.getSchema()
};
}
/**
* Decode attestation data from encoded format
*/
decodeData(encodedData) {
try {
const parsed = JSON.parse(encodedData);
return this.processDataForDecoding(parsed);
} catch (error) {
throw new SchemaValidationError(`Failed to decode data: ${error}`);
}
}
/**
* Validate data against the schema
*/
validateData(data) {
for (const field of this.schema.fields) {
if (!field.optional && !(field.name in data)) {
throw new SchemaValidationError(`Required field '${field.name}' is missing`, field.name);
}
}
for (const [key, value] of Object.entries(data)) {
const field = this.schema.fields.find((f) => f.name === key);
if (!field) {
throw new SchemaValidationError(`Unknown field '${key}'`, key);
}
this.validateFieldValue(field, value);
}
}
/**
* Generate default values for a schema
*/
generateDefaults() {
const defaults = {};
for (const field of this.schema.fields) {
if (field.optional) continue;
defaults[field.name] = this.getDefaultValue(field.type);
}
return defaults;
}
/**
* Convert schema to JSON Schema format for external compatibility
*/
toJSONSchema() {
const properties = {};
const required = [];
for (const field of this.schema.fields) {
properties[field.name] = {
type: this.stellarTypeToJSONSchemaType(field.type),
description: field.description
};
if (field.validation) {
Object.assign(properties[field.name], field.validation);
}
if (!field.optional) {
required.push(field.name);
}
}
return {
$schema: "https://json-schema.org/draft/2020-12/schema",
type: "object",
title: this.schema.name,
description: this.schema.description,
version: this.schema.version,
properties,
required,
additionalProperties: false
};
}
/**
* Create a schema encoder from JSON Schema
*/
static fromJSONSchema(jsonSchema) {
const fields = [];
for (const [name, prop] of Object.entries(jsonSchema.properties || {})) {
const property = prop;
fields.push({
name,
type: _StellarSchemaEncoder.jsonSchemaTypeToStellarType(property.type),
optional: !jsonSchema.required?.includes(name),
description: property.description,
validation: {
min: property.minimum,
max: property.maximum,
pattern: property.pattern,
enum: property.enum
}
});
}
const schema = {
name: jsonSchema.title || "Untitled Schema",
version: jsonSchema.version || "1.0.0",
description: jsonSchema.description || "",
fields
};
return new _StellarSchemaEncoder(schema);
}
/**
* Validate schema definition
*/
validateSchema(schema) {
if (!schema.name || typeof schema.name !== "string") {
throw new SchemaValidationError("Schema must have a valid name");
}
if (!schema.version || typeof schema.version !== "string") {
throw new SchemaValidationError("Schema must have a valid version");
}
if (!schema.fields || !Array.isArray(schema.fields) || schema.fields.length === 0) {
throw new SchemaValidationError("Schema must have at least one field");
}
const fieldNames = /* @__PURE__ */ new Set();
for (const field of schema.fields) {
if (!field.name || typeof field.name !== "string") {
throw new SchemaValidationError("Each field must have a valid name");
}
if (fieldNames.has(field.name)) {
throw new SchemaValidationError(`Duplicate field name: ${field.name}`);
}
fieldNames.add(field.name);
if (!this.isValidStellarType(field.type)) {
throw new SchemaValidationError(`Invalid type '${field.type}' for field '${field.name}'`);
}
}
}
/**
* Validate individual field value
*/
validateFieldValue(field, value) {
if (value === null || value === void 0) {
if (!field.optional) {
throw new SchemaValidationError(`Field '${field.name}' cannot be null`, field.name);
}
return;
}
switch (field.type) {
case "string" /* STRING */:
case "symbol" /* SYMBOL */:
if (typeof value !== "string") {
throw new SchemaValidationError(`Field '${field.name}' must be a string`, field.name);
}
break;
case "bool" /* BOOL */:
if (typeof value !== "boolean") {
throw new SchemaValidationError(`Field '${field.name}' must be a boolean`, field.name);
}
break;
case "u32" /* U32 */:
case "u64" /* U64 */:
case "i32" /* I32 */:
case "i64" /* I64 */:
case "i128" /* I128 */:
case "amount" /* AMOUNT */:
if (typeof value !== "number" && typeof value !== "bigint") {
throw new SchemaValidationError(`Field '${field.name}' must be a number`, field.name);
}
break;
case "address" /* ADDRESS */:
if (typeof value !== "string" || !this.isValidStellarAddress(value)) {
throw new SchemaValidationError(`Field '${field.name}' must be a valid Stellar address`, field.name);
}
break;
case "timestamp" /* TIMESTAMP */:
if (typeof value !== "number" && typeof value !== "string") {
throw new SchemaValidationError(`Field '${field.name}' must be a timestamp`, field.name);
}
break;
}
if (field.validation) {
if (field.validation.enum && !field.validation.enum.includes(value)) {
throw new SchemaValidationError(
`Field '${field.name}' must be one of: ${field.validation.enum.join(", ")}`,
field.name
);
}
if (typeof value === "string" && field.validation.pattern) {
if (!new RegExp(field.validation.pattern).test(value)) {
throw new SchemaValidationError(`Field '${field.name}' does not match pattern`, field.name);
}
}
if (typeof value === "number") {
if (field.validation.min !== void 0 && value < field.validation.min) {
throw new SchemaValidationError(`Field '${field.name}' is below minimum value`, field.name);
}
if (field.validation.max !== void 0 && value > field.validation.max) {
throw new SchemaValidationError(`Field '${field.name}' exceeds maximum value`, field.name);
}
}
}
}
/**
* Process data for encoding (type conversions, etc.)
*/
processDataForEncoding(data) {
const processed = {};
for (const [key, value] of Object.entries(data)) {
const field = this.schema.fields.find((f) => f.name === key);
if (!field) continue;
switch (field.type) {
case "address" /* ADDRESS */:
processed[key] = typeof value === "string" ? value : value.toString();
break;
case "timestamp" /* TIMESTAMP */:
processed[key] = typeof value === "string" ? new Date(value).getTime() : value;
break;
case "i128" /* I128 */:
case "amount" /* AMOUNT */:
processed[key] = typeof value === "bigint" ? value.toString() : value;
break;
default:
processed[key] = value;
}
}
return processed;
}
/**
* Process data for decoding (reverse of encoding)
*/
processDataForDecoding(data) {
const processed = {};
for (const [key, value] of Object.entries(data)) {
const field = this.schema.fields.find((f) => f.name === key);
if (!field) {
processed[key] = value;
continue;
}
switch (field.type) {
case "i128" /* I128 */:
case "amount" /* AMOUNT */:
processed[key] = typeof value === "string" && /^\d+$/.test(value) ? BigInt(value) : value;
break;
default:
processed[key] = value;
}
}
return processed;
}
/**
* Get default value for a type
*/
getDefaultValue(type) {
switch (type) {
case "string" /* STRING */:
case "symbol" /* SYMBOL */:
return "";
case "bool" /* BOOL */:
return false;
case "u32" /* U32 */:
case "u64" /* U64 */:
case "i32" /* I32 */:
case "i64" /* I64 */:
case "amount" /* AMOUNT */:
return 0;
case "i128" /* I128 */:
return BigInt(0);
case "address" /* ADDRESS */:
return "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF";
case "timestamp" /* TIMESTAMP */:
return Date.now();
case "bytes" /* BYTES */:
return new Uint8Array(0);
case "array" /* ARRAY */:
return [];
case "map" /* MAP */:
return {};
default:
return null;
}
}
/**
* Check if a type is valid Stellar type
*/
isValidStellarType(type) {
return Object.values(StellarDataType).includes(type) || type.startsWith("array<") || type.startsWith("option<") || type.startsWith("map<");
}
/**
* Convert Stellar type to JSON Schema type
*/
stellarTypeToJSONSchemaType(stellarType) {
switch (stellarType) {
case "string" /* STRING */:
case "symbol" /* SYMBOL */:
case "address" /* ADDRESS */:
return "string";
case "bool" /* BOOL */:
return "boolean";
case "u32" /* U32 */:
case "u64" /* U64 */:
case "i32" /* I32 */:
case "i64" /* I64 */:
case "i128" /* I128 */:
case "amount" /* AMOUNT */:
case "timestamp" /* TIMESTAMP */:
return "number";
case "array" /* ARRAY */:
return "array";
case "map" /* MAP */:
return "object";
default:
return "string";
}
}
/**
* Convert JSON Schema type to Stellar type
*/
static jsonSchemaTypeToStellarType(jsonType) {
switch (jsonType) {
case "string":
return "string" /* STRING */;
case "boolean":
return "bool" /* BOOL */;
case "number":
case "integer":
return "i64" /* I64 */;
case "array":
return "array" /* ARRAY */;
case "object":
return "map" /* MAP */;
default:
return "string" /* STRING */;
}
}
/**
* Validate Stellar address format
*/
isValidStellarAddress(address) {
try {
Address.fromString(address);
return true;
} catch {
return false;
}
}
};
StellarSchemaRegistry = class {
/**
* Register a schema encoder
*/
static register(name, encoder) {
this.schemas.set(name, encoder);
}
/**
* Get a registered schema encoder
*/
static get(name) {
return this.schemas.get(name);
}
/**
* List all registered schema names
*/
static list() {
return Array.from(this.schemas.keys());
}
/**
* Initialize with common schemas
*/
static initializeDefaults() {
this.register("identity-verification", new StellarSchemaEncoder({
name: "Identity Verification",
version: "1.0.0",
description: "Standard identity verification attestation",
fields: [
{ name: "fullName", type: "string" /* STRING */, description: "Legal full name" },
{ name: "dateOfBirth", type: "timestamp" /* TIMESTAMP */, description: "Date of birth" },
{ name: "nationality", type: "string" /* STRING */, description: "Nationality" },
{ name: "documentType", type: "string" /* STRING */, validation: { enum: ["passport", "drivers_license", "national_id"] } },
{ name: "verificationLevel", type: "string" /* STRING */, validation: { enum: ["basic", "enhanced", "premium"] } },
{ name: "verifiedBy", type: "address" /* ADDRESS */, description: "Verifying authority address" }
]
}));
this.register("academic-credential", new StellarSchemaEncoder({
name: "Academic Credential",
version: "1.0.0",
description: "University degree or academic achievement",
fields: [
{ name: "studentName", type: "string" /* STRING */, description: "Name of the student" },
{ name: "institution", type: "string" /* STRING */, description: "Educational institution" },
{ name: "degree", type: "string" /* STRING */, description: "Type of degree" },
{ name: "fieldOfStudy", type: "string" /* STRING */, description: "Major or field" },
{ name: "graduationDate", type: "timestamp" /* TIMESTAMP */, description: "Graduation date" },
{ name: "gpa", type: "u32" /* U32 */, optional: true, validation: { min: 0, max: 400 } },
// GPA * 100
{ name: "honors", type: "string" /* STRING */, optional: true, validation: { enum: ["summa_cum_laude", "magna_cum_laude", "cum_laude", "none"] } }
]
}));
this.register("professional-certification", new StellarSchemaEncoder({
name: "Professional Certification",
version: "1.0.0",
description: "Professional certification or license",
fields: [
{ name: "holderName", type: "string" /* STRING */, description: "Certification holder name" },
{ name: "certificationName", type: "string" /* STRING */, description: "Name of certification" },
{ name: "issuingOrganization", type: "string" /* STRING */, description: "Issuing organization" },
{ name: "certificationNumber", type: "string" /* STRING */, description: "Certification number" },
{ name: "issueDate", type: "timestamp" /* TIMESTAMP */, description: "Issue date" },
{ name: "expirationDate", type: "timestamp" /* TIMESTAMP */, optional: true, description: "Expiration date" },
{ name: "level", type: "string" /* STRING */, validation: { enum: ["entry", "associate", "professional", "expert", "master"] } }
]
}));
}
};
StellarSchemaRegistry.schemas = /* @__PURE__ */ new Map();
StellarSchemaRegistry.initializeDefaults();
}
});
// src/common/index.ts
var common_exports = {};
__export(common_exports, {
SchemaValidationError: () => SchemaValidationError,
StellarDataType: () => StellarDataType,
StellarSchemaEncoder: () => StellarSchemaEncoder,
StellarSchemaRegistry: () => StellarSchemaRegistry,
formatSchemaUid: () => formatSchemaUid,
generateIdFromSchema: () => generateIdFromSchema,
generateSchemaUid: () => generateSchemaUid,
parseFormattedUid: () => parseFormattedUid
});
var init_common = __esm({
"src/common/index.ts"() {
init_schema_utils();
init_schema_encoder();
}
});
var StellarSchemaService = class {
constructor(config, protocolClient) {
this.protocolClient = protocolClient;
this.publicKey = config.publicKey;
}
/**
* Create a new schema on the Stellar network
*/
async createSchema(config) {
try {
const validationError = this.validateSchemaDefinition(config);
if (validationError) return createErrorResponse(validationError);
const caller = this.publicKey;
const schemaDefinition = config.content;
const resolver = config.resolver || null;
const revocable = config.revocable ?? true;
const tx = await this.protocolClient.register({
caller,
schema_definition: schemaDefinition,
resolver,
revocable
});
const result = await tx.signAndSend();
if (!result.returnValue) {
throw createAttestProtocolError(
AttestProtocolErrorType.NETWORK_ERROR,
"Failed to get schema UID from transaction"
);
}
const uid = scValToNative(result.returnValue).toString("hex");
return createSuccessResponse({
uid,
definition: config.content,
authority: caller,
revocable: config.revocable ?? true,
resolver: config.resolver || null,
levy: config.levy || null
});
} catch (error) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NETWORK_ERROR,
error.message || "Failed to create schema"
)
);
}
}
/**
* Fetch a schema by its UID
*/
async fetchSchemaById(id) {
try {
if (!/^[0-9a-fA-F]{64}$/.test(id)) {
throw createAttestProtocolError(
AttestProtocolErrorType.VALIDATION_ERROR,
"Invalid schema UID format. Expected a 64-character hex string."
);
}
const schemaUid = Buffer.from(id, "hex");
return createSuccessResponse(null);
} catch (error) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NETWORK_ERROR,
error.message || "Failed to fetch schema"
)
);
}
}
/**
* Generate a deterministic ID from schema definition
*/
async generateIdFromSchema(schema) {
try {
const { generateIdFromSchema: generateId } = await Promise.resolve().then(() => (init_common(), common_exports));
const uid = await generateId(schema, this.publicKey);
return createSuccessResponse(uid);
} catch (error) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.VALIDATION_ERROR,
error.message || "Failed to generate schema ID"
)
);
}
}
/**
* List schemas by issuer
*/
async listSchemasByIssuer(params) {
try {
const emptyResponse = {
items: [],
total: 0,
limit: params.limit ?? 10,
offset: params.offset ?? 0,
hasMore: false
};
return createSuccessResponse(emptyResponse);
} catch (error) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NETWORK_ERROR,
error.message || "Failed to list schemas"
)
);
}
}
/**
* Validate schema definition
*/
validateSchemaDefinition(config) {
if (!config.name || config.name.trim() === "") {
return createAttestProtocolError(
AttestProtocolErrorType.VALIDATION_ERROR,
"Schema name is required"
);
}
if (!config.content || config.content.trim() === "") {
return createAttestProtocolError(
AttestProtocolErrorType.VALIDATION_ERROR,
"Schema content is required"
);
}
if (config.resolver) {
try {
Address.fromString(config.resolver);
} catch {
return createAttestProtocolError(
AttestProtocolErrorType.VALIDATION_ERROR,
"Invalid resolver address format"
);
}
}
return null;
}
};
var StellarAttestationService = class {
constructor(config, protocolClient) {
this.protocolClient = protocolClient;
this.publicKey = config.publicKey;
}
/**
* Issue a new attestation on the Stellar network
*/
async issueAttestation(config) {
try {
const validationError = this.validateAttestationDefinition(config);
if (validationError) return createErrorResponse(validationError);
if (!/^[0-9a-fA-F]{64}$/.test(config.schemaUid)) {
throw createAttestProtocolError(
AttestProtocolErrorType.VALIDATION_ERROR,
"Invalid schema UID format. Must be a 64-character hex string."
);
}
const caller = this.publicKey;
const schemaUid = Buffer.from(config.schemaUid, "hex");
const subject = config.subject;
const value = config.data;
const reference = config.reference || null;
const tx = await this.protocolClient.attest({
caller,
schema_uid: schemaUid,
subject,
value,
reference
});
const result = await tx.signAndSend();
const timestamp = Date.now();
return createSuccessResponse({
uid: result.transactionHash || Date.now().toString(),
schemaUid: config.schemaUid,
subject: config.subject,
attester: this.publicKey,
data: config.data,
timestamp,
expirationTime: config.expirationTime || null,
revocationTime: null,
revoked: false,
reference: config.reference || null
});
} catch (error) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NETWORK_ERROR,
error.message || "Failed to issue attestation"
)
);
}
}
/**
* Fetch an attestation by its ID
*/
async fetchAttestationById(id) {
try {
return createSuccessResponse(null);
} catch (error) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NETWORK_ERROR,
error.message || "Failed to fetch attestation"
)
);
}
}
/**
* Get an attestation by schema UID, subject, and reference
*/
async getAttestation(schemaUid, subject, reference) {
try {
if (!/^[0-9a-fA-F]{64}$/.test(schemaUid)) {
throw createAttestProtocolError(
AttestProtocolErrorType.VALIDATION_ERROR,
"Invalid schema UID format. Must be a 64-character hex string."
);
}
const schemaUidBuffer = Buffer.from(schemaUid, "hex");
const tx = await this.protocolClient.get_attestation({
schema_uid: schemaUidBuffer,
subject,
reference: reference || null
});
const result = await tx.simulate();
if (!result.result?.returnValue) {
return createSuccessResponse(null);
}
const attestationRecord = scValToNative(result.result.returnValue);
return createSuccessResponse({
uid: `${schemaUid}-${subject}-${reference || "default"}`,
schemaUid,
subject,
attester: this.publicKey,
// This should come from the contract
data: attestationRecord.value,
timestamp: Date.now(),
// This should come from the contract
expirationTime: null,
revocationTime: null,
revoked: attestationRecord.revoked || false,
reference: attestationRecord.reference || null
});
} catch (error) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NETWORK_ERROR,
error.message || "Failed to get attestation"
)
);
}
}
/**
* List attestations by wallet address
*/
async listAttestationsByWallet(params) {
try {
const emptyResponse = {
items: [],
total: 0,
limit: params.limit ?? 10,
offset: params.offset ?? 0,
hasMore: false
};
return createSuccessResponse(emptyResponse);
} catch (error) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NETWORK_ERROR,
error.message || "Failed to list attestations by wallet"
)
);
}
}
/**
* List attestations by schema UID
*/
async listAttestationsBySchema(params) {
try {
const emptyResponse = {
items: [],
total: 0,
limit: params.limit ?? 10,
offset: params.offset ?? 0,
hasMore: false
};
return createSuccessResponse(emptyResponse);
} catch (error) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NETWORK_ERROR,
error.message || "Failed to list attestations by schema"
)
);
}
}
/**
* Revoke an attestation
*/
async revokeAttestation(config) {
try {
const validationError = this.validateRevocationDefinition(config);
if (validationError) return createErrorResponse(validationError);
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NOT_FOUND_ERROR,
"Revocation by attestation UID not yet implemented. Use revokeAttestationByComponents instead."
)
);
} catch (error) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NETWORK_ERROR,
error.message || "Failed to revoke attestation"
)
);
}
}
/**
* Revoke an attestation by its components (Stellar-specific)
*/
async revokeAttestationByComponents(schemaUid, subject, reference) {
try {
if (!/^[0-9a-fA-F]{64}$/.test(schemaUid)) {
throw createAttestProtocolError(
AttestProtocolErrorType.VALIDATION_ERROR,
"Invalid schema UID format. Must be a 64-character hex string."
);
}
const caller = this.publicKey;
const schemaUidBuffer = Buffer.from(schemaUid, "hex");
const tx = await this.protocolClient.revoke_attestation({
caller,
schema_uid: schemaUidBuffer,
subject,
reference: reference || null
});
await tx.signAndSend();
return createSuccessResponse(void 0);
} catch (error) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NETWORK_ERROR,
error.message || "Failed to revoke attestation"
)
);
}
}
/**
* Attest by delegation (not implemented in current Stellar contracts)
*/
async attestByDelegation(config) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NOT_FOUND_ERROR,
"Delegation not implemented in Stellar contracts"
)
);
}
/**
* Revoke by delegation (not implemented in current Stellar contracts)
*/
async revokeByDelegation(config) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NOT_FOUND_ERROR,
"Delegation not implemented in Stellar contracts"
)
);
}
/**
* Validate attestation definition
*/
validateAttestationDefinition(config) {
if (!config.schemaUid || config.schemaUid.trim() === "") {
return createAttestProtocolError(
AttestProtocolErrorType.VALIDATION_ERROR,
"Schema UID is required"
);
}
if (!config.subject || config.subject.trim() === "") {
return createAttestProtocolError(
AttestProtocolErrorType.VALIDATION_ERROR,
"Subject is required"
);
}
try {
Address.fromString(config.subject);
} catch {
return createAttestProtocolError(
AttestProtocolErrorType.VALIDATION_ERROR,
"Invalid subject address format"
);
}
if (!config.data || config.data.trim() === "") {
return createAttestProtocolError(
AttestProtocolErrorType.VALIDATION_ERROR,
"Attestation data is required"
);
}
return null;
}
/**
* Validate revocation definition
*/
validateRevocationDefinition(config) {
if (!config.attestationUid || config.attestationUid.trim() === "") {
return createAttestProtocolError(
AttestProtocolErrorType.VALIDATION_ERROR,
"Attestation UID is required"
);
}
return null;
}
};
var StellarAuthorityService = class {
constructor(config, authorityClient) {
this.authorityClient = authorityClient;
this.publicKey = config.publicKey;
}
/**
* Initialize the authority contract
*/
async initialize(admin, tokenContractId) {
try {
const tx = await this.authorityClient.initialize({
admin,
token_contract_id: tokenContractId
});
await tx.signAndSend();
return createSuccessResponse(void 0);
} catch (error) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NETWORK_ERROR,
error.message || "Failed to initialize authority contract"
)
);
}
}
/**
* Register an authority (admin function)
*/
async adminRegisterAuthority(authToReg, metadata) {
try {
const tx = await this.authorityClient.admin_register_authority({
admin: this.publicKey,
auth_to_reg: authToReg,
metadata
});
await tx.signAndSend();
return createSuccessResponse(void 0);
} catch (error) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NETWORK_ERROR,
error.message || "Failed to register authority"
)
);
}
}
/**
* Register an authority (public function with fees)
*/
async registerAuthority(authorityToReg, metadata) {
try {
const tx = await this.authorityClient.register_authority({
caller: this.publicKey,
authority_to_reg: authorityToReg,
metadata
});
await tx.signAndSend();
return createSuccessResponse(void 0);
} catch (error) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NETWORK_ERROR,
error.message || "Failed to register authority"
)
);
}
}
/**
* Check if an address is an authority
*/
async isAuthority(authority) {
try {
const tx = await this.authorityClient.is_authority({ authority });
const result = await tx.simulate();
if (!result.result?.returnValue) {
return createSuccessResponse(false);
}
const isAuth = scValToNative(result.result.returnValue);
return createSuccessResponse(isAuth);
} catch (error) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NETWORK_ERROR,
error.message || "Failed to check authority status"
)
);
}
}
/**
* Register schema rules (admin function)
*/
async adminRegisterSchema(schemaUid, rules) {
try {
const tx = await this.authorityClient.admin_register_schema({
admin: this.publicKey,
schema_uid: schemaUid,
rules
});
await tx.signAndSend();
return createSuccessResponse(void 0);
} catch (error) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NETWORK_ERROR,
error.message || "Failed to register schema rules"
)
);
}
}
/**
* Set schema levy (admin function)
*/
async adminSetSchemaLevy(schemaUid, levyAmount, levyRecipient) {
try {
const tx = await this.authorityClient.admin_set_schema_levy({
admin: this.publicKey,
schema_uid: schemaUid,
levy_amount: levyAmount,
levy_recipient: levyRecipient
});
await tx.signAndSend();
return createSuccessResponse(void 0);
} catch (error) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NETWORK_ERROR,
error.message || "Failed to set schema levy"
)
);
}
}
/**
* Set registration fee (admin function)
*/
async adminSetRegistrationFee(feeAmount, tokenId) {
try {
const tx = await this.authorityClient.admin_set_registration_fee({
admin: this.publicKey,
fee_amount: feeAmount,
token_id: tokenId
});
await tx.signAndSend();
return createSuccessResponse(void 0);
} catch (error) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NETWORK_ERROR,
error.message || "Failed to set registration fee"
)
);
}
}
/**
* Process attestation through authority contract
*/
async attest(attestation) {
try {
const tx = await this.authorityClient.attest({ attestation });
const result = await tx.signAndSend();
if (!result.returnValue) {
return createSuccessResponse(false);
}
const success = scValToNative(result.returnValue);
return createSuccessResponse(success);
} catch (error) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NETWORK_ERROR,
error.message || "Failed to process attestation"
)
);
}
}
/**
* Process revocation through authority contract
*/
async revoke(attestation) {
try {
const tx = await this.authorityClient.revoke({ attestation });
const result = await tx.signAndSend();
if (!result.returnValue) {
return createSuccessResponse(false);
}
const success = scValToNative(result.returnValue);
return createSuccessResponse(success);
} catch (error) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NETWORK_ERROR,
error.message || "Failed to process revocation"
)
);
}
}
/**
* Withdraw collected levies
*/
async withdrawLevies() {
try {
const tx = await this.authorityClient.withdraw_levies({
caller: this.publicKey
});
await tx.signAndSend();
return createSuccessResponse(void 0);
} catch (error) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NETWORK_ERROR,
error.message || "Failed to withdraw levies"
)
);
}
}
/**
* Get schema rules
*/
async getSchemaRules(schemaUid) {
try {
const tx = await this.authorityClient.get_schema_rules({ schema_uid: schemaUid });
const result = await tx.simulate();
if (!result.result?.returnValue) {
return createSuccessResponse(null);
}
const rules = scValToNative(result.result.returnValue);
return createSuccessResponse(rules);
} catch (error) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NETWORK_ERROR,
error.message || "Failed to get schema rules"
)
);
}
}
/**
* Get collected levies for an authority
*/
async getCollectedLevies(authority) {
try {
const tx = await this.authorityClient.get_collected_levies({ authority });
const result = await tx.simulate();
if (!result.result?.returnValue) {
return createSuccessResponse(BigInt(0));
}
const levies = scValToNative(result.result.returnValue);
return createSuccessResponse(BigInt(levies));
} catch (error) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NETWORK_ERROR,
error.message || "Failed to get collected levies"
)
);
}
}
/**
* Get token ID
*/
async getTokenId() {
try {
const tx = await this.authorityClient.get_token_id();
const result = await tx.simulate();
if (!result.result?.returnValue) {
throw new Error("No token ID returned");
}
const tokenId = scValToNative(result.result.returnValue);
return createSuccessResponse(tokenId);
} catch (error) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NETWORK_ERROR,
error.message || "Failed to get token ID"
)
);
}
}
/**
* Get admin address
*/
async getAdminAddress() {
try {
const tx = await this.authorityClient.get_admin_address();
const result = await tx.simulate();
if (!result.result?.returnValue) {
throw new Error("No admin address returned");
}
const admin = scValToNative(result.result.returnValue);
return createSuccessResponse(admin);
} catch (error) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NETWORK_ERROR,
error.message || "Failed to get admin address"
)
);
}
}
/**
* Fetch authority information
*/
async fetchAuthority(id) {
try {
const isAuthResult = await this.isAuthority(id);
if (isAuthResult.error || !isAuthResult.data) {
return createSuccessResponse(null);
}
return createSuccessResponse({
id,
isVerified: true,
metadata: "Authority metadata"
// This should come from contract storage
});
} catch (error) {
return createErrorResponse(
createAttestProtocolError(
AttestProtocolErrorType.NETWORK_ERROR,
error.message || "Failed to fetch authority"
)
);
}
}
};
var StellarAttestProtocol = class extends AttestProtocolBase {
/**
* Creates a new instance of the Stellar Attest SDK
* @param config SDK configuration options
*/
constructor(config) {
super(config);
this.config = config;
const defaultUrl = "https://soroban-testnet.stellar.org";
this.server = new rpc.Server(config.url ?? defaultUrl, {
allowHttp: config.allowHttp ?? (config.url ?? defaultUrl).startsWith("http://")
});
this.networkPassphrase = config.networkPassphrase ?? Networks.TESTNET;
const clientOptions = {
networkPassphrase: this.networkPassphrase,
rpcUrl: config.url ?? defaultUrl,
allowHttp: config.allowHttp ?? (config.url ?? defaultUrl).startsWith("http://")
};
const protocolContractId = config.contractAddresses?.protocol ?? networks.testnet.contractId;
const authorityContractId = config.contractAddresses?.authority ?? networks$1.testnet.contractId;
this.protocolClient = new Client({
...clientOptions,
contractId: protocolContractId
});
this.authorityClient = new Client$1({
...clientOptions,
contractId: authorityContractId
});
this.schemaService = new StellarSchemaService(config, this.protocolClient);
this.attestationService = new StellarAttestationService(config, this.protocolClient);
this.authorityService = new StellarAuthorityService(config, this.authorityClient);
}
getDefaultNetworkUrl() {
return "https://soroban-testnet.stellar.org";
}
/**
* Initialize the protocol contract by setting the admin
*/
async initialize() {
return this.safeExecute(
async () => {
const tx = await this.protocolClient.initialize({
admin: this.config.publicKey
});
await tx.signAndSend();
},
() => {
this.initialized = true;
}
);
}
// Authority Management - Delegate to AuthorityService
async registerAuthority() {
const initError = this.ensureInitialized();
if (initError) return createErrorResponse(initError);
return createSuccessResponse(this.config.publicKey);
}
async fetchAuthority(id) {
const initError = this.ensureInitialized();
if (initError) return createErrorResponse(initError);
return this.authorityService.fetchAuthority(id);
}
async isIssuerAnAuthority(issuer) {
const initError = this.ensureInitialized();
if (initError) return createErrorResponse(initError);
return this.authorityService.isAuthority(issuer);
}
// Schema Management - Delegate to SchemaService
async createSchema(config) {
const initError = this.ensureInitialized();
if (initError) return createErrorResponse(initError);
return this.schemaService.createSchema(config);
}
async fetchSchemaById(id) {
const initError = this.ensureInitialized();
if (initError) return createErrorResponse(initError);
return this.schemaService.fetchSchemaById(id);
}
async generateIdFromSchema(schema) {
return this.schemaService.generateIdFromSchema(schema);
}
async listSchemasByIssuer(params) {
const initError = this.ensureInitialized();
if (initError) return createErrorResponse(initError);
return this.schemaService.listSchemasByIssuer(params);
}
// Attestation Management - Delegate to AttestationService
async issueAttestation(config) {
const initError = this.ensureInitialized();
if (initError) return createErrorResponse(initError);
return this.attestationService.issueAttestation(config);
}
async fetchAttestationById(id) {
const initError = this.ensureInitialized();
if (initError) return createErrorResponse(initError);
return this.attestationService.fetchAttestationById(id);
}
async listAttestationsByWallet(params) {
const initError = this.ensureInitialized();
if (initError) return createErrorResponse(initError);
return this.attestationService.listAttestationsByWallet(params);
}
async listAttestationsBySchema(params) {
const initError = this.ensureInitialized();
if (initError) return createErrorResponse(initError);
return this.attestationService.listAttestationsBySchema(params);
}
async revokeAttestation(config) {
const initError = this.ensureInitialized();
if (initError) return createErrorResponse(initError);
return this.attestationService.revokeAttestation(config);
}
// Delegation (not implemented in current Stellar contracts)
async attestByDelegation(config) {
return this.attestationService.attestByDelegation(config);
}
async revokeByDelegation(config) {
return this.attestationService.revokeByDelegation(config);
}
// Stellar-specific helper methods
/**
* Get the underlying protocol contract client
*/
getProtocolClient() {
return this.protocolClient;
}
/**
* Get the underlying authority contract client
*/
getAuthorityClient() {
return this.authorityClient;
}
/**
* Get the schema service for direct access
*/
getSchemaService() {
return this.schemaService;
}
/**
* Get the attestation service for direct access
*/
getAttestationService() {
return this.attestationService;
}
/**
* Get the authority service for direct access
*/
getAuthorityService() {
return this.authorityService;
}
/**
* Get an attestation by schema UID, subject, and reference
*/
async getAttestation(schemaUid, subject, reference) {
const initError = this.ensureInitialized();
if (initError) return createErrorResponse(initError);
return this.attestationService.getAttestation(schemaUid, subject, reference);
}
};
// src/index.ts
init_schema_encoder();
init_common();
var index_default = StellarAttestProtocol;
export { SchemaValidationError, StellarAttestProtocol, StellarAttestationService, StellarAuthorityService, StellarDataType, StellarSchemaEncoder, StellarSchemaRegistry, StellarSchemaService, common_exports as common, index_default as default };
//# sourceMappingURL=index.mjs.map
//# sourceMappingURL=index.mjs.map