diff --git a/src/runtime/http/express-router-impl.ts b/src/runtime/http/express-router-impl.ts index ef7e082..3d4b2b6 100644 --- a/src/runtime/http/express-router-impl.ts +++ b/src/runtime/http/express-router-impl.ts @@ -22,6 +22,25 @@ import { extractClientIdentifier } from './client-identifier.ts'; const SEP10_AUTH_OPERATION_SUFFIX = ' auth'; +/** + * Result of looking up an account's configured signers via an injectable + * SEP-10 account lookup. Distinguishes not-found from upstream failures. + */ +export type Sep10AccountLookupResult = + | { + status: 'found'; + account: { + signers: Array<{ key: string; weight: number }>; + thresholds: { low: number; medium: number; high: number }; + }; + } + | { status: 'not_found' } + | { status: 'unavailable'; reason?: string }; + +export interface Sep10AccountLookup { + lookup(accountId: string): Promise; +} + /** * Stable issuer for bearer access tokens minted by the anchor API. * Tokens issued by a different service must not be accepted here, even when diff --git a/src/runtime/http/express-router.ts b/src/runtime/http/express-router.ts index 18f098a..e3d8133 100644 --- a/src/runtime/http/express-router.ts +++ b/src/runtime/http/express-router.ts @@ -1,7 +1,11 @@ import type { AnchorConfig } from '@/core/config.ts'; import { errorHandler } from '../../utils/error-handler.ts'; import { InMemoryRateLimiter, type RateLimitRule } from './rate-limiter.ts'; -import type { DatabaseAdapter, WebhookProcessor } from '@/runtime/interfaces.ts'; +import type { + AccountLookup, + DatabaseAdapter, + WebhookProcessor, +} from '@/runtime/interfaces.ts'; import type { RouteDefinition } from '@/types/foundation.ts'; import type { AnchorPluginHooks } from '@/types/plugin.ts'; import { Keypair } from '@stellar/stellar-sdk'; @@ -25,6 +29,7 @@ interface RouterDependencies { depositRequestHooks?: NonNullable[]; sep10ChallengeHooks?: NonNullable[]; pluginRoutes?: Array<{ pluginId: string; route: RouteDefinition }>; + accountLookup?: AccountLookup; } export class AnchorExpressRouter { @@ -65,6 +70,7 @@ export class AnchorExpressRouter { rateLimiter: new InMemoryRateLimiter(), rateRules, pluginRoutes, + accountLookup: dependencies.accountLookup, }; } diff --git a/src/runtime/interfaces.ts b/src/runtime/interfaces.ts index 61b2611..3b7a845 100644 --- a/src/runtime/interfaces.ts +++ b/src/runtime/interfaces.ts @@ -45,6 +45,30 @@ export interface AccountSigner { weight: number; } +/** Signers and thresholds for a Stellar account, as reported by an account lookup source. */ +export interface AccountSignerConfiguration { + account: string; + signers: AccountSigner[]; + thresholds: { + low: number; + medium: number; + high: number; + }; +} + +export type AccountLookupResult = + | { outcome: 'found'; account: AccountSignerConfiguration } + | { outcome: 'not_found'; account: string }; + +/** + * Injectable boundary for loading account signers and thresholds. + * Implementations must throw on upstream failure (network, provider outage); + * an unknown account must be reported as `not_found`, never as an exception. + */ +export interface AccountLookup { + lookupAccount(account: string): Promise; +} + export interface AuthChallengeRecord { id: string; account: string; diff --git a/src/services/stellar-signer.ts b/src/services/stellar-signer.ts index b20054a..a7a9f88 100644 --- a/src/services/stellar-signer.ts +++ b/src/services/stellar-signer.ts @@ -5,6 +5,32 @@ export interface StellarTransactionSigner { signTransaction(transactionXdr: string, networkPassphrase: string): Promise; } +export interface StellarAccountSigner { + key: string; + weight: number; +} + +export interface StellarAccountThresholds { + low: number; + medium: number; + high: number; +} + +export interface StellarAccountRecord { + accountId: string; + signers: StellarAccountSigner[]; + thresholds: StellarAccountThresholds; +} + +export type StellarAccountLookupResult = + | { status: 'found'; account: StellarAccountRecord } + | { status: 'not_found' } + | { status: 'upstream_error'; message: string }; + +export interface StellarAccountLookup { + lookupAccount(accountId: string): Promise; +} + export class LocalKeypairSigner implements StellarTransactionSigner { readonly #keypair: Keypair;