UNPKG

@neardefi/shade-agent-js

Version:

A library for creating Shade Agent agents in JavaScript and TypeScript

245 lines (238 loc) 10.6 kB
import { Provider } from '@near-js/providers'; import { SerializedReturnValue, BlockReference, TxExecutionStatus } from '@near-js/types'; interface TcbInfo { mrtd: string; rtmr0: string; rtmr1: string; rtmr2: string; rtmr3: string; os_image_hash: string; compose_hash: string; device_id: string; app_compose: string; event_log: EventLog[]; } interface EventLog { imr: number; event_type: number; digest: string; event: string; event_payload: string; } interface DstackAttestationForContract { quote: number[]; collateral: { pck_crl_issuer_chain: string; root_ca_crl: string; pck_crl: string; tcb_info_issuer_chain: string; tcb_info: string; tcb_info_signature: string; qe_identity_issuer_chain: string; qe_identity: string; qe_identity_signature: string; }; tcb_info: TcbInfo; } interface Measurements { /** MRTD (Measurement of Root of Trust for Data) - identifies the virtual firmware. */ mrtd: string; /** RTMR0 (Runtime Measurement Register 0) - typically measures the bootloader, virtual firmware data, and configuration. */ rtmr0: string; /** RTMR1 (Runtime Measurement Register 1) - typically measures the OS kernel, boot parameters, and initrd (initial ramdisk). */ rtmr1: string; /** RTMR2 (Runtime Measurement Register 2) - typically measures the OS application. */ rtmr2: string; } interface FullMeasurements { /** Expected RTMRs (Runtime Measurement Registers). */ rtmrs: Measurements; /** Expected digest for the key-provider event. */ key_provider_event_digest: string; /** Expected app_compose hash payload. */ app_compose_hash_payload: string; } /** * Configuration object for creating a ShadeClient instance */ interface ShadeConfig { /** Network ID ('testnet' or 'mainnet'), defaults to 'testnet' if not provided */ networkId?: "testnet" | "mainnet"; /** The NEAR contract ID for the agent contract */ agentContractId?: string; /** Sponsor account configuration for funding the agent account */ sponsor?: { /** The sponsor's NEAR account ID */ accountId: string; /** The sponsor's private key */ privateKey: string; }; /** Custom NEAR RPC provider. If not provided, a default provider will be created based on networkId */ rpc?: Provider; /** Number of keys to use for the agent (1-100), defaults to 1 if not provided */ numKeys?: number; /** Derivation path for deterministic key generation for local testing (needs to be a randomly unique string)*/ derivationPath?: string; } declare class ShadeClient { private config; private dstackClient; private agentAccountId; private agentPrivateKeys; private currentKeyIndex; private keysDerivedWithRandom; private keysChecked; private constructor(); /** * Creates a new ShadeClient instance asynchronously * @param config - Configuration object for the Shade client (see ShadeConfig interface for details) * @returns Promise that resolves to a ShadeClient instance * @throws Error if configuration is invalid, network ID mismatch, or key generation fails */ static create(config: ShadeConfig): Promise<ShadeClient>; /** * Gets the NEAR account ID of the agent * @returns The agent's account ID */ accountId(): string; /** * Gets the NEAR balance of the agent account in human readable format (e.g. 1 = one NEAR) * @returns Promise that resolves to the account balance in NEAR tokens, if the agent account does not exist, returns 0 * @throws Error if network request fails */ balance(): Promise<number>; /** * Registers the agent in the agent contract. * * @param params * @param params.deposit Attached deposit in yoctoNEAR when storage is required or when `forceDeposit` is `true` (defaults to `5000000000000000000000` — 0.005 NEAR) * @param params.forceDeposit If `true`, always attach `deposit` (or the default) and skip `get_agent`. If `false`, attach no deposit and skip `get_agent`. If omitted, use `get_agent` to decide. * @returns Promise that resolves to true if registration was successful * @throws Error if agentContractId is not configured, if fetching attestation fails, or if the contract call fails */ register(params?: { deposit?: bigint | string | number; forceDeposit?: boolean; }): Promise<boolean>; /** * Call a view function on the agent contract and return the result * @param params * @param params.methodName The method that will be called * @param params.args Arguments as a valid JSON Object * @param params.blockQuery (optional) Block reference for the query * @returns A promise that resolves with the result of the view function call * @throws Error if agentContractId is not configured or if RPC call fails */ view<T extends SerializedReturnValue>(params: { methodName: string; args: Record<string, unknown>; blockQuery?: BlockReference; }): Promise<T>; /** * Call a function on the agent contract and return the result * @param params * @param params.methodName The method that will be called * @param params.args Arguments, either as a valid JSON Object or a raw Uint8Array * @param params.deposit (optional) Amount of NEAR Tokens to attach to the call * @param params.gas (optional) Amount of GAS to use attach to the call * @param params.waitUntil (optional) Transaction finality to wait for * @returns A promise that resolves with the result of the contract function call * @throws Error if agentContractId is not configured, if key derivation fails, or if transaction fails */ call<T extends SerializedReturnValue>(params: { methodName: string; args: Uint8Array | Record<string, any>; deposit?: bigint | string | number; gas?: bigint | string | number; waitUntil?: TxExecutionStatus; }): Promise<T>; /** * Gets the TEE attestation for the agent in contract format (ready to be sent to the contract) * @returns Promise that resolves to the contract-formatted attestation object * @throws Error if fetching quote collateral fails (network errors, HTTP errors, timeouts) */ getAttestation(): Promise<DstackAttestationForContract>; /** * Funds the agent account with NEAR tokens from the sponsor account * @param fundAmount - Amount of NEAR tokens to transfer to the agent account in human readable format (e.g. 1 = one NEAR) * @returns Promise that resolves when funding is complete * @throws Error if sponsor is not configured or if transfer fails after retries */ fund(fundAmount: number): Promise<void>; /** * Gets the agent's private keys (use with caution) * @param params - Must pass `{ acknowledgeRisk: true }` * @returns Array of private key strings */ getPrivateKeys(params: { acknowledgeRisk: true; }): string[]; /** * Checks if the agent is whitelisted for local mode * @returns Promise that resolves to true if the agent is whitelisted, false if the agent is not whitelisted, or null if the agent contract requires TEE * @throws Error if agentContractId is not configured or if view call fails */ isWhitelisted(): Promise<boolean | null>; } /** * Error / sanitisation utilities. The single trust boundary for secret leak * prevention in shade-agent-js: every catch in the package should rethrow * via `toThrowable(e)` so that sensitive fields and recognised secret value * patterns are redacted before the error escapes. * * Fail-closed rule: if any value can't be safely processed (hostile getter, * throwing Proxy, deep-redact crash, …), it is replaced with the * `[unsanitisable]` placeholder string so operators can see that * something was there but couldn't be sanitised. The unsanitised * original is NEVER propagated. The only exception is the final * `toThrowable` boundary, which must return a real Error — there it * falls back to a generic "An error occurred" message. * * File layout — six sections, top to bottom: * 1. Redaction policy — which field names + value patterns count as secrets. * 2. Redactor engine — the mutable deep-redact instance + extension API. * 3. Sanitiser — walks any value, redacts secrets, marks unsanitisables. * 4. Public throw API — toThrowable + genericError. * 5. Retry — defaultRetryable + withRetry + helpers. * 6. Safe key parsing — safeParseKeyPair / safeParseSigner. */ /** Shape of a value-pattern entry: a regex to test, plus a replacer function. */ interface StringTest { pattern: RegExp; replacer: (value: string, pattern: RegExp) => string; } /** * Extend the redaction config at runtime. After this returns, subsequent * sanitize/toThrowable calls also redact the new field names and apply * the new value patterns. Mutation is process-global — call once at boot. */ declare function addSensitive(opts: { keys?: string[]; patterns?: StringTest[]; }): void; /** * Sanitise any value. Type-routed: * - primitives (number, boolean, bigint, symbol, null, undefined) * → returned as-is (nothing to redact) * - string → value-pattern redaction applied * - Error → routed through `sanitizeError` * - object → deep-redact walks string keys; symbol-keyed properties are dropped * * Marks on uncertainty: if the value is so exotic the redactor can't * process it (hostile Proxy, throwing prototype trap), returns the * `"[unsanitisable]"` placeholder string instead of the original value. * The unsanitised value is never propagated. */ declare function sanitize(value: unknown): unknown; /** * Take any thrown value, return a safe Error to rethrow. * * try { ... } catch (e) { throw toThrowable(e); } * * Never throws, even on circular structures, BigInt * values, hostile Proxies, or other exotic shapes. If sanitisation * dropped the input entirely, returns `new Error("An error occurred")` * rather than echoing anything potentially unsafe. */ declare function toThrowable(error: unknown): Error; export { type DstackAttestationForContract, type FullMeasurements, type Measurements, ShadeClient, type ShadeConfig, addSensitive, sanitize, toThrowable };