UNPKG

attest.so

Version:

<div align="center">

358 lines (316 loc) 12.1 kB
import { AttestSDKBase } from './base' import { AttestationConfig, AttestSDKResponse, RevokeAttestationConfig, SchemaConfig, StellarConfig, StellarFetchAuthorityResult, StellarFetchSchemaResult, StellarFetchAttestationResult, SolanaRevokeAttestationConfig, StellarAttestationConfig } from './types' import * as StellarSdk from '@stellar/stellar-sdk'; /** * Stellar implementation of the Attest SDK */ export class StellarAttestSDK extends AttestSDKBase { private server: StellarSdk.Horizon.Server private keypair: StellarSdk.Keypair private networkPassphrase: string private ATTEST_CONTRACT = "CAF5SWYR7B7V5FYUXTGYXCRUNRQEIWEUZRDCARNMX456LRD64RX76BNN" private AUTHORITY_CONTRACT = "CBORL365DYHJLIOUXR7GU6VIKXQ4X7DJTV6SR366O6ZWCF7C5PO2XESQ" /** * Creates a new instance of the Stellar Attest SDK * @param config SDK configuration options */ constructor(config: StellarConfig) { super() // Initialize Stellar SDK this.server = new StellarSdk.Horizon.Server(config.url ?? 'https://horizon-testnet.stellar.org') this.keypair = StellarSdk.Keypair.fromSecret(config.secretKey) this.networkPassphrase = config.networkPassphrase ?? StellarSdk.Networks.TESTNET } /** * Initialize the SDK by setting the admin of the contract * Maps to initialize(env: Env, admin: Address) in the Stellar contract */ async initialize(): Promise<AttestSDKResponse<void>> { try { // Create contract instance const contract = new StellarSdk.Contract(this.contractId) // Call initialize to set admin // const operation = contract.call("initialize", [ // new StellarSdk.Address(this.keypair.publicKey()).toString() // ]) // // Execute transaction // await this.buildAndSubmitTransaction([operation]) return { data: undefined } } catch (error) { return { error } } } /** * Retrieves the authority record for the current wallet * Note: The Stellar contract doesn't have a direct method for this * @returns The authority record or null if not found */ async fetchAuthority(): Promise<AttestSDKResponse<StellarFetchAuthorityResult | null>> { try { // In Stellar contract, authority is implicit with the caller's address // There's no separate authority record storage like in Solana // We return the current keypair information return { data: { address: this.keypair.publicKey(), metadata: "Default authority metadata" } } } catch (error) { return { error } } } /** * Registers the current wallet as an authority * Note: In Stellar, there's no explicit registration needed beyond initialization * @returns The authority address */ async registerAuthority(): Promise<AttestSDKResponse<string>> { try { // In Stellar's contract, there's no separate registration needed // The initialize method sets the admin, and all other operations work based on caller address // We're just returning the public key here return { data: this.keypair.publicKey() } } catch (error) { return { error } } } /** * Fetches a schema by its UID * Maps to get_schema in the Stellar contract (internal method) * @param schemaUID The schema UID to fetch * @returns The schema or null if not found */ async fetchSchema(schemaUID: string): Promise<AttestSDKResponse<StellarFetchSchemaResult | null>> { try { // Create a contract instance const contract = new StellarSdk.Contract(this.contractId) // Currently there's no direct method to fetch a schema in the public interface // This would need to be added to the contract for a complete implementation // For now we return a simulated result return { data: { uid: schemaUID, definition: "Sample schema definition", authority: this.keypair.publicKey(), revocable: true, resolver: null } } } catch (error) { return { error } } } /** * Creates a new schema * Maps to register(env, caller, schema_definition, resolver, revocable) in the Stellar contract * @param config Schema configuration * @returns The UID of the created schema */ async createSchema(config: SchemaConfig): Promise<AttestSDKResponse<string>> { try { // Create a contract instance const contract = new StellarSdk.Contract(this.contractId) // Build the transaction to create a schema // const operation = contract.call("register", [ // new StellarSdk.Address(this.keypair.publicKey()).toString(), // config.schemaContent, // config.resolverAddress ? new StellarSdk.Address(config.resolverAddress.toString()).toString() : null, // config.revocable ?? true // ]) // Execute the transaction // const transaction = await this.buildAndSubmitTransaction([operation]) // In a real implementation, we would extract the schema UID from the transaction result // For now, we'll use a deterministic method to generate it based on inputs const schemaUID = this.generateSchemaUID(config.schemaName, this.keypair.publicKey()) return { data: schemaUID } } catch (error) { return { error } } } /** * Fetches an attestation by its identifiers * Maps to get_attestation(env, schema_uid, subject, reference) in the Stellar contract * @param attestation The attestation ID (encoded combination of schema_uid, subject, reference) * @returns The attestation or null if not found */ async fetchAttestation( attestation: string ): Promise<AttestSDKResponse<StellarFetchAttestationResult | null>> { try { // Create a contract instance const contract = new StellarSdk.Contract(this.contractId) // Parse the attestation parameter to extract components const [schemaUID, subject, reference] = this.parseAttestationId(attestation) // Call the get_attestation method // const operation = contract.call("get_attestation", [ // schemaUID, // new StellarSdk.Address(subject).toString(), // reference // ]) // In a real implementation, we would execute this and parse the result // For now, return simulated data return { data: { schemaUid: schemaUID, subject, value: "Sample attestation value", reference, revoked: false } } } catch (error) { return { error } } } /** * Creates a new attestation * Maps to attest(env, caller, schema_uid, subject, value, reference) in the Stellar contract * @param config Attestation configuration * @returns The ID of the created attestation */ async attest(config: StellarAttestationConfig): Promise<AttestSDKResponse<string>> { try { // Create a contract instance const contract = new StellarSdk.Contract(this.contractId) // Build the transaction to create an attestation // const operation = contract.call("attest", [ // new StellarSdk.Address(this.keypair.publicKey()).toString(), // config.schemaData.toString(), // new StellarSdk.Address(config.accounts.recipient.toString()).toString(), // config.data, // config.refUID ? config.refUID.toString() : null // ]) // // Execute the transaction // const transaction = await this.buildAndSubmitTransaction([operation]) // Generate the attestation ID - in practice this would be derived from contract data const attestationId = this.generateAttestationId( config.schemaData.toString(), config.accounts.recipient.toString(), config.refUID?.toString() ?? null ) return { data: attestationId } } catch (error) { return { error } } } /** * Revokes an attestation * Maps to revoke_attestation(env, caller, schema_uid, subject, reference) in the Stellar contract * @param props Revocation configuration * @returns The ID of the revoked attestation */ async revokeAttestation( props: RevokeAttestationConfig ): Promise<AttestSDKResponse<string>> { try { // Create a contract instance const contract = new StellarSdk.Contract(this.contractId) // Build the transaction to revoke the attestation // const operation = contract.call("revoke_attestation", [ // new StellarSdk.Address(this.keypair.publicKey()).toString(), // props.schemaUID.toString(), // new StellarSdk.Address(props.recipient.toString()).toString(), // props.reference || null // ]) // Execute the transaction // const transaction = await this.buildAndSubmitTransaction([operation]) // Generate the attestation ID const attestationId = this.generateAttestationId( props.schemaUID.toString(), props.recipient.toString(), props.reference || null ) return { data: attestationId } } catch (error) { return { error } } } /** * Helper method to build and submit a transaction * @param operations Transaction operations * @returns Transaction result */ private async buildAndSubmitTransaction(operations: StellarSdk.Operation[]): Promise<any> { try { // Load the account const account = await this.server.loadAccount(this.keypair.publicKey()) // Build the transaction const transaction = new StellarSdk.TransactionBuilder(account, { fee: StellarSdk.BASE_FEE, networkPassphrase: this.networkPassphrase }) // Add operations // operations.forEach(operation => { // transaction.addOperation(operation) // }) // Finalize and sign const builtTx = transaction.setTimeout(30).build() builtTx.sign(this.keypair) // Submit const result = await this.server.submitTransaction(builtTx) return result } catch (error) { throw error } } /** * Generates a schema UID (this is a simplified simulation) * @param schemaName Schema name * @param authority Authority address * @returns Generated schema UID */ private generateSchemaUID(schemaName: string, authority: string): string { // This is a simplified way to generate schema UID // In practice, this would use proper cryptographic hashing similar to the Stellar contract return Buffer.from(`schema:${schemaName}:${authority}`).toString('hex') } /** * Generates an attestation ID (this is a simplified simulation) * @param schemaUID Schema UID * @param subject Subject address * @param reference Optional reference string * @returns Generated attestation ID */ private generateAttestationId(schemaUID: string, subject: string, reference: string | null): string { // This is a simplified way to generate attestation ID // In practice, this would use proper cryptographic hashing return Buffer.from(`attestation:${schemaUID}:${subject}:${reference || ''}`).toString('hex') } /** * Parses an attestation ID into its components * @param attestationId Attestation ID to parse * @returns [schemaUID, subject, reference] */ private parseAttestationId(attestationId: string): [string, string, string | null] { // This is a simplified parsing implementation // In production, this would properly decode the attestation ID // For demonstration, we'll assume the attestation ID follows our encoding pattern try { const decoded = Buffer.from(attestationId, 'hex').toString() const parts = decoded.split(':') if (parts.length >= 4) { return [parts[1], parts[2], parts[3] || null] } } catch (error) { // If parsing fails, use default values } // Default fallback values return [ 'schema_uid_placeholder', 'subject_placeholder', null ] } }