diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d03d4a..21a236b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,7 +40,7 @@ jobs: run: npm test - name: Build - run: npm run build + run: npx tsc -p packages/registry-client && npm run build docker: name: Docker build & smoke test diff --git a/Dockerfile b/Dockerfile index 4b87efb..76d6017 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,7 +10,7 @@ RUN npm ci COPY tsconfig.json tsconfig.build.json ./ COPY src ./src -RUN npm run build && npm prune --omit=dev +RUN npx tsc -p packages/registry-client && npm run build && npm prune --omit=dev # ── Runtime stage ─────────────────────────────────────────────────────────── FROM node:24-alpine diff --git a/packages/registry-client/dist/generated/index.d.ts b/packages/registry-client/dist/generated/index.d.ts deleted file mode 100644 index 670d9a8..0000000 --- a/packages/registry-client/dist/generated/index.d.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { Buffer } from "buffer"; -import { AssembledTransaction, Client as ContractClient, ClientOptions as ContractClientOptions, MethodOptions, Result } from "@stellar/stellar-sdk/contract"; -import type { u32, i128 } from "@stellar/stellar-sdk/contract"; -export * from "@stellar/stellar-sdk"; -export * as contract from "@stellar/stellar-sdk/contract"; -export * as rpc from "@stellar/stellar-sdk/rpc"; -export declare const Errors: { - 1: { - message: string; - }; - 2: { - message: string; - }; - 3: { - message: string; - }; - 4: { - message: string; - }; - 5: { - message: string; - }; -}; -export type DataKey = { - tag: "Resource"; - values: readonly [string]; -} | { - tag: "Count"; - values: void; -} | { - tag: "Index"; - values: readonly [u32]; -}; -export interface Resource { - creator: string; - id: string; - listed: boolean; - metadata: string; - price: i128; - /** - * Discovery labels (e.g. "dataset", "research"). Distinct from `metadata`, - * which remains the off-chain content anchor (IPFS URI, content hash, etc.). - */ - tags: Array; -} -export interface Client { - /** - * Construct and simulate a get transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Fetch a resource. Errors with `NotFound` if it does not exist. - */ - get: ({ id }: { - id: string; - }, options?: MethodOptions) => Promise>>; - /** - * Construct and simulate a list transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Paginated resource list in insertion order. `limit` is capped at 20. - */ - list: ({ start, limit }: { - start: u32; - limit: u32; - }, options?: MethodOptions) => Promise>>; - /** - * Construct and simulate a count transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Total number of resources successfully registered (monotonic; not decremented on transfer). - */ - count: (options?: MethodOptions) => Promise>; - /** - * Construct and simulate a delist transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Delist a resource (convenience method for set_listed(false)). Only the creator may call this. - */ - delist: ({ id }: { - id: string; - }, options?: MethodOptions) => Promise>>; - /** - * Construct and simulate a exists transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Whether a resource with `id` is registered. - */ - exists: ({ id }: { - id: string; - }, options?: MethodOptions) => Promise>; - /** - * Construct and simulate a register transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Register a new resource. Errors if `id` already exists or `price <= 0`. - * Requires the creator's authorization. - */ - register: ({ creator, id, price, metadata, tags, }: { - creator: string; - id: string; - price: i128; - metadata: string; - tags: Array; - }, options?: MethodOptions) => Promise>>; - /** - * Construct and simulate a set_tags transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Replace a resource's discovery tags. Only the creator may call this. - * Does not modify `metadata` (the off-chain content pointer). - */ - set_tags: ({ id, tags }: { - id: string; - tags: Array; - }, options?: MethodOptions) => Promise>>; - /** - * Construct and simulate a get_owner transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Get the owner address of a resource. Errors with `NotFound` if it does not exist. - */ - get_owner: ({ id }: { - id: string; - }, options?: MethodOptions) => Promise>>; - /** - * Construct and simulate a set_price transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Update a resource's price. Only the creator may call this. - */ - set_price: ({ id, new_price }: { - id: string; - new_price: i128; - }, options?: MethodOptions) => Promise>>; - /** - * Construct and simulate a set_listed transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Set the listing state of a resource. Only the creator may call this. - */ - set_listed: ({ id, listed }: { - id: string; - listed: boolean; - }, options?: MethodOptions) => Promise>>; - /** - * Construct and simulate a update_metadata transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Update a resource's metadata pointer. Only the creator may call this. - */ - update_metadata: ({ id, metadata }: { - id: string; - metadata: string; - }, options?: MethodOptions) => Promise>>; - /** - * Construct and simulate a transfer_ownership transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object. - * Hand ownership to a new creator. Only the current creator may call this. - */ - transfer_ownership: ({ id, new_creator }: { - id: string; - new_creator: string; - }, options?: MethodOptions) => Promise>>; -} -export declare class Client extends ContractClient { - readonly options: ContractClientOptions; - static deploy( - /** Options for initializing a Client as well as for calling a method, with extras specific to deploying. */ - options: MethodOptions & Omit & { - /** The hash of the Wasm blob, which must already be installed on-chain. */ - wasmHash: Buffer | string; - /** Salt used to generate the contract's ID. Passed through to {@link Operation.createCustomContract}. Default: random. */ - salt?: Buffer | Uint8Array; - /** The format used to decode `wasmHash`, if it's provided as a string. */ - format?: "hex" | "base64"; - }): Promise>; - constructor(options: ContractClientOptions); - readonly fromJSON: { - get: (json: string) => AssembledTransaction>; - list: (json: string) => AssembledTransaction; - count: (json: string) => AssembledTransaction; - delist: (json: string) => AssembledTransaction>; - exists: (json: string) => AssembledTransaction; - register: (json: string) => AssembledTransaction>; - set_tags: (json: string) => AssembledTransaction>; - get_owner: (json: string) => AssembledTransaction>; - set_price: (json: string) => AssembledTransaction>; - set_listed: (json: string) => AssembledTransaction>; - update_metadata: (json: string) => AssembledTransaction>; - transfer_ownership: (json: string) => AssembledTransaction>; - }; -} -//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/packages/registry-client/dist/generated/index.d.ts.map b/packages/registry-client/dist/generated/index.d.ts.map deleted file mode 100644 index 121edda..0000000 --- a/packages/registry-client/dist/generated/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/generated/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/packages/registry-client/dist/generated/index.js b/packages/registry-client/dist/generated/index.js deleted file mode 100644 index 3d71839..0000000 --- a/packages/registry-client/dist/generated/index.js +++ /dev/null @@ -1,59 +0,0 @@ -import { Buffer } from "buffer"; -import { Client as ContractClient, Spec as ContractSpec, } from "@stellar/stellar-sdk/contract"; -export * from "@stellar/stellar-sdk"; -export * as contract from "@stellar/stellar-sdk/contract"; -export * as rpc from "@stellar/stellar-sdk/rpc"; -if (typeof window !== "undefined") { - //@ts-ignore Buffer exists - window.Buffer = window.Buffer || Buffer; -} -export const Errors = { - 1: { message: "AlreadyRegistered" }, - 2: { message: "NotFound" }, - 3: { message: "InvalidPrice" }, - 4: { message: "MetadataTooLong" }, - 5: { message: "InvalidTag" }, -}; -export class Client extends ContractClient { - options; - static async deploy( - /** Options for initializing a Client as well as for calling a method, with extras specific to deploying. */ - options) { - return ContractClient.deploy(null, options); - } - constructor(options) { - super(new ContractSpec([ - "AAAAAAAAAD5GZXRjaCBhIHJlc291cmNlLiBFcnJvcnMgd2l0aCBgTm90Rm91bmRgIGlmIGl0IGRvZXMgbm90IGV4aXN0LgAAAAAAA2dldAAAAAABAAAAAAAAAAJpZAAAAAAAEAAAAAEAAAPpAAAH0AAAAAhSZXNvdXJjZQAAAAM=", - "AAAAAAAAAERQYWdpbmF0ZWQgcmVzb3VyY2UgbGlzdCBpbiBpbnNlcnRpb24gb3JkZXIuIGBsaW1pdGAgaXMgY2FwcGVkIGF0IDIwLgAAAARsaXN0AAAAAgAAAAAAAAAFc3RhcnQAAAAAAAAEAAAAAAAAAAVsaW1pdAAAAAAAAAQAAAABAAAD6gAAB9AAAAAIUmVzb3VyY2U=", - "AAAAAAAAAFtUb3RhbCBudW1iZXIgb2YgcmVzb3VyY2VzIHN1Y2Nlc3NmdWxseSByZWdpc3RlcmVkIChtb25vdG9uaWM7IG5vdCBkZWNyZW1lbnRlZCBvbiB0cmFuc2ZlcikuAAAAAAVjb3VudAAAAAAAAAAAAAABAAAABA==", - "AAAAAAAAAF1EZWxpc3QgYSByZXNvdXJjZSAoY29udmVuaWVuY2UgbWV0aG9kIGZvciBzZXRfbGlzdGVkKGZhbHNlKSkuIE9ubHkgdGhlIGNyZWF0b3IgbWF5IGNhbGwgdGhpcy4AAAAAAAAGZGVsaXN0AAAAAAABAAAAAAAAAAJpZAAAAAAAEAAAAAEAAAPpAAAD7QAAAAAAAAAD", - "AAAAAAAAACtXaGV0aGVyIGEgcmVzb3VyY2Ugd2l0aCBgaWRgIGlzIHJlZ2lzdGVyZWQuAAAAAAZleGlzdHMAAAAAAAEAAAAAAAAAAmlkAAAAAAAQAAAAAQAAAAE=", - "AAAABAAAAAAAAAAAAAAABUVycm9yAAAAAAAABQAAAAAAAAARQWxyZWFkeVJlZ2lzdGVyZWQAAAAAAAABAAAAAAAAAAhOb3RGb3VuZAAAAAIAAAAAAAAADEludmFsaWRQcmljZQAAAAMAAAAAAAAAD01ldGFkYXRhVG9vTG9uZwAAAAAEAAAAAAAAAApJbnZhbGlkVGFnAAAAAAAF", - "AAAAAAAAAG1SZWdpc3RlciBhIG5ldyByZXNvdXJjZS4gRXJyb3JzIGlmIGBpZGAgYWxyZWFkeSBleGlzdHMgb3IgYHByaWNlIDw9IDBgLgpSZXF1aXJlcyB0aGUgY3JlYXRvcidzIGF1dGhvcml6YXRpb24uAAAAAAAACHJlZ2lzdGVyAAAABQAAAAAAAAAHY3JlYXRvcgAAAAATAAAAAAAAAAJpZAAAAAAAEAAAAAAAAAAFcHJpY2UAAAAAAAALAAAAAAAAAAhtZXRhZGF0YQAAABAAAAAAAAAABHRhZ3MAAAPqAAAAEAAAAAEAAAPpAAAD7QAAAAAAAAAD", - "AAAAAAAAAIBSZXBsYWNlIGEgcmVzb3VyY2UncyBkaXNjb3ZlcnkgdGFncy4gT25seSB0aGUgY3JlYXRvciBtYXkgY2FsbCB0aGlzLgpEb2VzIG5vdCBtb2RpZnkgYG1ldGFkYXRhYCAodGhlIG9mZi1jaGFpbiBjb250ZW50IHBvaW50ZXIpLgAAAAhzZXRfdGFncwAAAAIAAAAAAAAAAmlkAAAAAAAQAAAAAAAAAAR0YWdzAAAD6gAAABAAAAABAAAD6QAAA+0AAAAAAAAAAw==", - "AAAAAAAAAFFHZXQgdGhlIG93bmVyIGFkZHJlc3Mgb2YgYSByZXNvdXJjZS4gRXJyb3JzIHdpdGggYE5vdEZvdW5kYCBpZiBpdCBkb2VzIG5vdCBleGlzdC4AAAAAAAAJZ2V0X293bmVyAAAAAAAAAQAAAAAAAAACaWQAAAAAABAAAAABAAAD6QAAABMAAAAD", - "AAAAAAAAADpVcGRhdGUgYSByZXNvdXJjZSdzIHByaWNlLiBPbmx5IHRoZSBjcmVhdG9yIG1heSBjYWxsIHRoaXMuAAAAAAAJc2V0X3ByaWNlAAAAAAAAAgAAAAAAAAACaWQAAAAAABAAAAAAAAAACW5ld19wcmljZQAAAAAAAAsAAAABAAAD6QAAA+0AAAAAAAAAAw==", - "AAAAAgAAAAAAAAAAAAAAB0RhdGFLZXkAAAAAAwAAAAEAAAAAAAAACFJlc291cmNlAAAAAQAAABAAAAAAAAAAAAAAAAVDb3VudAAAAAAAAAEAAAAAAAAABUluZGV4AAAAAAAAAQAAAAQ=", - "AAAAAAAAAERTZXQgdGhlIGxpc3Rpbmcgc3RhdGUgb2YgYSByZXNvdXJjZS4gT25seSB0aGUgY3JlYXRvciBtYXkgY2FsbCB0aGlzLgAAAApzZXRfbGlzdGVkAAAAAAACAAAAAAAAAAJpZAAAAAAAEAAAAAAAAAAGbGlzdGVkAAAAAAABAAAAAQAAA+kAAAPtAAAAAAAAAAM=", - "AAAAAQAAAAAAAAAAAAAACFJlc291cmNlAAAABgAAAAAAAAAHY3JlYXRvcgAAAAATAAAAAAAAAAJpZAAAAAAAEAAAAAAAAAAGbGlzdGVkAAAAAAABAAAAAAAAAAhtZXRhZGF0YQAAABAAAAAAAAAABXByaWNlAAAAAAAACwAAAJNEaXNjb3ZlcnkgbGFiZWxzIChlLmcuICJkYXRhc2V0IiwgInJlc2VhcmNoIikuIERpc3RpbmN0IGZyb20gYG1ldGFkYXRhYCwKd2hpY2ggcmVtYWlucyB0aGUgb2ZmLWNoYWluIGNvbnRlbnQgYW5jaG9yIChJUEZTIFVSSSwgY29udGVudCBoYXNoLCBldGMuKS4AAAAABHRhZ3MAAAPqAAAAEA==", - "AAAAAAAAAEVVcGRhdGUgYSByZXNvdXJjZSdzIG1ldGFkYXRhIHBvaW50ZXIuIE9ubHkgdGhlIGNyZWF0b3IgbWF5IGNhbGwgdGhpcy4AAAAAAAAPdXBkYXRlX21ldGFkYXRhAAAAAAIAAAAAAAAAAmlkAAAAAAAQAAAAAAAAAAhtZXRhZGF0YQAAABAAAAABAAAD6QAAA+0AAAAAAAAAAw==", - "AAAAAAAAAEhIYW5kIG93bmVyc2hpcCB0byBhIG5ldyBjcmVhdG9yLiBPbmx5IHRoZSBjdXJyZW50IGNyZWF0b3IgbWF5IGNhbGwgdGhpcy4AAAASdHJhbnNmZXJfb3duZXJzaGlwAAAAAAACAAAAAAAAAAJpZAAAAAAAEAAAAAAAAAALbmV3X2NyZWF0b3IAAAAAEwAAAAEAAAPpAAAD7QAAAAAAAAAD", - ]), options); - this.options = options; - } - fromJSON = { - get: (this.txFromJSON), - list: (this.txFromJSON), - count: (this.txFromJSON), - delist: (this.txFromJSON), - exists: (this.txFromJSON), - register: (this.txFromJSON), - set_tags: (this.txFromJSON), - get_owner: (this.txFromJSON), - set_price: (this.txFromJSON), - set_listed: (this.txFromJSON), - update_metadata: (this.txFromJSON), - transfer_ownership: (this.txFromJSON), - }; -} -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/packages/registry-client/dist/generated/index.js.map b/packages/registry-client/dist/generated/index.js.map deleted file mode 100644 index 2b871eb..0000000 --- a/packages/registry-client/dist/generated/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/generated/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,OAAO,EAEL,MAAM,IAAI,cAAc,EAIxB,IAAI,IAAI,YAAY,GACrB,MAAM,+BAA+B,CAAC;AAavC,cAAc,sBAAsB,CAAC;AACrC,OAAO,KAAK,QAAQ,MAAM,+BAA+B,CAAC;AAC1D,OAAO,KAAK,GAAG,MAAM,0BAA0B,CAAC;AAEhD,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;IAClC,0BAA0B;IAC1B,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC;AAC1C,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,CAAC,EAAE,EAAE,OAAO,EAAE,mBAAmB,EAAE;IACnC,CAAC,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE;IAC1B,CAAC,EAAE,EAAE,OAAO,EAAE,cAAc,EAAE;IAC9B,CAAC,EAAE,EAAE,OAAO,EAAE,iBAAiB,EAAE;IACjC,CAAC,EAAE,EAAE,OAAO,EAAE,YAAY,EAAE;CAC7B,CAAC;AAsIF,MAAM,OAAO,MAAO,SAAQ,cAAc;IAeZ;IAd5B,MAAM,CAAC,KAAK,CAAC,MAAM;IACjB,4GAA4G;IAC5G,OAQG;QAEH,OAAO,cAAc,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC9C,CAAC;IACD,YAA4B,OAA8B;QACxD,KAAK,CACH,IAAI,YAAY,CAAC;YACf,8KAA8K;YAC9K,8MAA8M;YAC9M,0KAA0K;YAC1K,kNAAkN;YAClN,8HAA8H;YAC9H,kNAAkN;YAClN,kVAAkV;YAClV,0RAA0R;YAC1R,kMAAkM;YAClM,0MAA0M;YAC1M,8IAA8I;YAC9I,8MAA8M;YAC9M,0YAA0Y;YAC1Y,0NAA0N;YAC1N,kOAAkO;SACnO,CAAC,EACF,OAAO,CACR,CAAC;QApBwB,YAAO,GAAP,OAAO,CAAuB;IAqB1D,CAAC;IACe,QAAQ,GAAG;QACzB,GAAG,EAAE,CAAA,IAAI,CAAC,UAA4B,CAAA;QACtC,IAAI,EAAE,CAAA,IAAI,CAAC,UAA2B,CAAA;QACtC,KAAK,EAAE,CAAA,IAAI,CAAC,UAAe,CAAA;QAC3B,MAAM,EAAE,CAAA,IAAI,CAAC,UAAwB,CAAA;QACrC,MAAM,EAAE,CAAA,IAAI,CAAC,UAAmB,CAAA;QAChC,QAAQ,EAAE,CAAA,IAAI,CAAC,UAAwB,CAAA;QACvC,QAAQ,EAAE,CAAA,IAAI,CAAC,UAAwB,CAAA;QACvC,SAAS,EAAE,CAAA,IAAI,CAAC,UAA0B,CAAA;QAC1C,SAAS,EAAE,CAAA,IAAI,CAAC,UAAwB,CAAA;QACxC,UAAU,EAAE,CAAA,IAAI,CAAC,UAAwB,CAAA;QACzC,eAAe,EAAE,CAAA,IAAI,CAAC,UAAwB,CAAA;QAC9C,kBAAkB,EAAE,CAAA,IAAI,CAAC,UAAwB,CAAA;KAClD,CAAC;CACH"} \ No newline at end of file diff --git a/packages/registry-client/dist/index.d.ts b/packages/registry-client/dist/index.d.ts deleted file mode 100644 index 6ef209f..0000000 --- a/packages/registry-client/dist/index.d.ts +++ /dev/null @@ -1,43 +0,0 @@ -/** - * @synapsvault/registry-client - * - * Thin, typed wrapper around the auto-generated Soroban TypeScript bindings - * for the `vault-registry` contract. Bindings are regenerated by running - * `pnpm contract:bindings` at the repo root. - * - * Consumers (server/, web/, mcp/) import from this package rather than the - * `generated/` folder directly so we have one place to: - * - swap RPC URLs / network passphrases - * - add ergonomic helpers (e.g. unit conversion) - * - re-export the generated types under stable names - */ -export { networks, parseStellarNetwork, resolveStellarNetwork, getNetworkPreset, type NetworkPreset, type StellarDeploymentNetwork, type ExplorerNetwork, } from "./networks.js"; -export { X402_NETWORK_IDS, normalizeX402Network, inferNetworkFromX402, inferNetworkFromRpcUrl, inferNetworkFromHorizonUrl, networkPassphraseForX402, validateNetworkConfig, applyNetworkEnvDefaults, type NetworkConfigInput, type NetworkValidationIssue, } from "./validateNetwork.js"; -export * from "./generated/index.js"; -import { Client, type Resource } from "./generated/index.js"; -/** Options for creating a {@link VaultRegistryClient}. */ -export interface RegistryClientOptions { - /** The deployed vault-registry contract ID (C…). */ - contractId: string; - /** Soroban RPC URL (e.g. https://soroban-testnet.stellar.org). */ - rpcUrl: string; - /** - * Network passphrase. Defaults to {@link Networks.TESTNET}. Pass - * {@link Networks.PUBLIC} for mainnet. - */ - networkPassphrase?: string; - /** - * Optional public key for read-only calls. Write calls require a signer - * supplied separately by the caller (see the generated Client docs). - */ - publicKey?: string; -} -/** - * Construct a {@link Client} configured against the given Soroban RPC and - * deployed contract. This is the only entry point most callers need. - */ -export declare function createRegistryClient(opts: RegistryClientOptions): Client; -/** Convenience alias so callers can write `VaultRegistryClient` if preferred. */ -export type VaultRegistryClient = Client; -export declare function listResources(client: Client, start: number, limit: number): Promise; -//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/packages/registry-client/dist/index.d.ts.map b/packages/registry-client/dist/index.d.ts.map deleted file mode 100644 index 90153a2..0000000 --- a/packages/registry-client/dist/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,EACL,QAAQ,EACR,mBAAmB,EACnB,qBAAqB,EACrB,gBAAgB,EAChB,KAAK,aAAa,EAClB,KAAK,wBAAwB,EAC7B,KAAK,eAAe,GACrB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,sBAAsB,EACtB,0BAA0B,EAC1B,wBAAwB,EACxB,qBAAqB,EACrB,uBAAuB,EACvB,KAAK,kBAAkB,EACvB,KAAK,sBAAsB,GAC5B,MAAM,sBAAsB,CAAC;AAI9B,cAAc,sBAAsB,CAAC;AAErC,OAAO,EAAE,MAAM,EAAE,KAAK,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAE7D,0DAA0D;AAC1D,MAAM,WAAW,qBAAqB;IACpC,oDAAoD;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,qBAAqB,GAAG,MAAM,CAOxE;AAED,iFAAiF;AACjF,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAEzC,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,QAAQ,EAAE,CAAC,CAGrB"} \ No newline at end of file diff --git a/packages/registry-client/dist/index.js b/packages/registry-client/dist/index.js deleted file mode 100644 index 37afc67..0000000 --- a/packages/registry-client/dist/index.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * @synapsvault/registry-client - * - * Thin, typed wrapper around the auto-generated Soroban TypeScript bindings - * for the `vault-registry` contract. Bindings are regenerated by running - * `pnpm contract:bindings` at the repo root. - * - * Consumers (server/, web/, mcp/) import from this package rather than the - * `generated/` folder directly so we have one place to: - * - swap RPC URLs / network passphrases - * - add ergonomic helpers (e.g. unit conversion) - * - re-export the generated types under stable names - */ -import { Networks } from "@stellar/stellar-sdk"; -export { networks, parseStellarNetwork, resolveStellarNetwork, getNetworkPreset, } from "./networks.js"; -export { X402_NETWORK_IDS, normalizeX402Network, inferNetworkFromX402, inferNetworkFromRpcUrl, inferNetworkFromHorizonUrl, networkPassphraseForX402, validateNetworkConfig, applyNetworkEnvDefaults, } from "./validateNetwork.js"; -// Re-export everything the bindings generator produced. The Client class, -// the Resource type, error variants, and network defaults all flow through. -export * from "./generated/index.js"; -import { Client } from "./generated/index.js"; -/** - * Construct a {@link Client} configured against the given Soroban RPC and - * deployed contract. This is the only entry point most callers need. - */ -export function createRegistryClient(opts) { - return new Client({ - contractId: opts.contractId, - rpcUrl: opts.rpcUrl, - networkPassphrase: opts.networkPassphrase ?? Networks.TESTNET, - publicKey: opts.publicKey, - }); -} -export async function listResources(client, start, limit) { - const tx = await client.list({ start, limit }); - return tx.result; -} -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/packages/registry-client/dist/index.js.map b/packages/registry-client/dist/index.js.map deleted file mode 100644 index 0035b1d..0000000 --- a/packages/registry-client/dist/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,OAAO,EACL,QAAQ,EACR,mBAAmB,EACnB,qBAAqB,EACrB,gBAAgB,GAIjB,MAAM,eAAe,CAAC;AAEvB,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,oBAAoB,EACpB,sBAAsB,EACtB,0BAA0B,EAC1B,wBAAwB,EACxB,qBAAqB,EACrB,uBAAuB,GAGxB,MAAM,sBAAsB,CAAC;AAE9B,0EAA0E;AAC1E,4EAA4E;AAC5E,cAAc,sBAAsB,CAAC;AAErC,OAAO,EAAE,MAAM,EAAiB,MAAM,sBAAsB,CAAC;AAoB7D;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAA2B;IAC9D,OAAO,IAAI,MAAM,CAAC;QAChB,UAAU,EAAE,IAAI,CAAC,UAAU;QAC3B,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,IAAI,QAAQ,CAAC,OAAO;QAC7D,SAAS,EAAE,IAAI,CAAC,SAAS;KAC1B,CAAC,CAAC;AACL,CAAC;AAKD,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAAc,EACd,KAAa,EACb,KAAa;IAEb,MAAM,EAAE,GAAG,MAAO,MAAc,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IACxD,OAAO,EAAE,CAAC,MAAoB,CAAC;AACjC,CAAC"} \ No newline at end of file diff --git a/packages/registry-client/dist/networks.d.ts b/packages/registry-client/dist/networks.d.ts deleted file mode 100644 index f846f91..0000000 --- a/packages/registry-client/dist/networks.d.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** Deployment target: Stellar testnet or mainnet (pubnet). */ -export type StellarDeploymentNetwork = "testnet" | "mainnet"; -/** Stellar Expert explorer segment for a deployment network. */ -export type ExplorerNetwork = "testnet" | "public"; -/** - * Canonical network constants for SynapsVault deployments. - * Operators select `testnet` or `mainnet` via STELLAR_NETWORK and may override - * individual fields with env vars. - */ -export interface NetworkPreset { - stellarNetwork: StellarDeploymentNetwork; - /** x402 network identifier passed to paywalls and signers. */ - x402Network: string; - networkPassphrase: string; - sorobanRpcUrl: string; - horizonUrl: string; - explorerNetwork: ExplorerNetwork; - /** Soroban USDC Stellar Asset Contract (SEP-41) used by x402. */ - usdcSacContractId: string; - /** Classic USDC issuer for trustline setup scripts. */ - usdcClassicIssuer: string; - /** - * Known vault-registry deployment for this network, if any. - * Mainnet operators deploy their own contract and set VAULT_REGISTRY_CONTRACT_ID. - */ - defaultRegistryContractId: string | null; -} -export declare const networks: Record; -/** - * Parse STELLAR_NETWORK (or alias) into a deployment network id. - * Returns undefined when the value is missing or unrecognized. - */ -export declare function parseStellarNetwork(value: string | undefined): StellarDeploymentNetwork | undefined; -/** Resolve deployment network, defaulting to testnet. */ -export declare function resolveStellarNetwork(value: string | undefined): StellarDeploymentNetwork; -/** Look up the preset for a deployment network. */ -export declare function getNetworkPreset(network: StellarDeploymentNetwork): NetworkPreset; -//# sourceMappingURL=networks.d.ts.map \ No newline at end of file diff --git a/packages/registry-client/dist/networks.d.ts.map b/packages/registry-client/dist/networks.d.ts.map deleted file mode 100644 index 1879472..0000000 --- a/packages/registry-client/dist/networks.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"networks.d.ts","sourceRoot":"","sources":["../src/networks.ts"],"names":[],"mappings":"AAEA,8DAA8D;AAC9D,MAAM,MAAM,wBAAwB,GAAG,SAAS,GAAG,SAAS,CAAC;AAE7D,gEAAgE;AAChE,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEnD;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,cAAc,EAAE,wBAAwB,CAAC;IACzC,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,eAAe,CAAC;IACjC,iEAAiE;IACjE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,uDAAuD;IACvD,iBAAiB,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,yBAAyB,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1C;AAED,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,wBAAwB,EAAE,aAAa,CAuBpE,CAAC;AAUF;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,wBAAwB,GAAG,SAAS,CAGtC;AAED,yDAAyD;AACzD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,wBAAwB,CAEzF;AAED,mDAAmD;AACnD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,wBAAwB,GAAG,aAAa,CAEjF"} \ No newline at end of file diff --git a/packages/registry-client/dist/networks.js b/packages/registry-client/dist/networks.js deleted file mode 100644 index 469d82b..0000000 --- a/packages/registry-client/dist/networks.js +++ /dev/null @@ -1,50 +0,0 @@ -import { Networks } from "@stellar/stellar-sdk"; -export const networks = { - testnet: { - stellarNetwork: "testnet", - x402Network: "stellar:testnet", - networkPassphrase: Networks.TESTNET, - sorobanRpcUrl: "https://soroban-testnet.stellar.org", - horizonUrl: "https://horizon-testnet.stellar.org", - explorerNetwork: "testnet", - usdcSacContractId: "CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA", - usdcClassicIssuer: "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5", - defaultRegistryContractId: "CDQKUIADLO5S5WEHEUTTXX2M45WAHVRU2PBEBD6ZGDKMOP5A72FJ3OD4", - }, - mainnet: { - stellarNetwork: "mainnet", - x402Network: "stellar:pubnet", - networkPassphrase: Networks.PUBLIC, - sorobanRpcUrl: "https://soroban.stellar.org", - horizonUrl: "https://horizon.stellar.org", - explorerNetwork: "public", - usdcSacContractId: "CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75", - usdcClassicIssuer: "GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN", - defaultRegistryContractId: null, - }, -}; -/** Accepted aliases for STELLAR_NETWORK env var. */ -const STELLAR_NETWORK_ALIASES = { - testnet: "testnet", - mainnet: "mainnet", - pubnet: "mainnet", - public: "mainnet", -}; -/** - * Parse STELLAR_NETWORK (or alias) into a deployment network id. - * Returns undefined when the value is missing or unrecognized. - */ -export function parseStellarNetwork(value) { - if (!value) - return undefined; - return STELLAR_NETWORK_ALIASES[value.trim().toLowerCase()]; -} -/** Resolve deployment network, defaulting to testnet. */ -export function resolveStellarNetwork(value) { - return parseStellarNetwork(value) ?? "testnet"; -} -/** Look up the preset for a deployment network. */ -export function getNetworkPreset(network) { - return networks[network]; -} -//# sourceMappingURL=networks.js.map \ No newline at end of file diff --git a/packages/registry-client/dist/networks.js.map b/packages/registry-client/dist/networks.js.map deleted file mode 100644 index 4bcf32b..0000000 --- a/packages/registry-client/dist/networks.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"networks.js","sourceRoot":"","sources":["../src/networks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAgChD,MAAM,CAAC,MAAM,QAAQ,GAAoD;IACvE,OAAO,EAAE;QACP,cAAc,EAAE,SAAS;QACzB,WAAW,EAAE,iBAAiB;QAC9B,iBAAiB,EAAE,QAAQ,CAAC,OAAO;QACnC,aAAa,EAAE,qCAAqC;QACpD,UAAU,EAAE,qCAAqC;QACjD,eAAe,EAAE,SAAS;QAC1B,iBAAiB,EAAE,0DAA0D;QAC7E,iBAAiB,EAAE,0DAA0D;QAC7E,yBAAyB,EAAE,0DAA0D;KACtF;IACD,OAAO,EAAE;QACP,cAAc,EAAE,SAAS;QACzB,WAAW,EAAE,gBAAgB;QAC7B,iBAAiB,EAAE,QAAQ,CAAC,MAAM;QAClC,aAAa,EAAE,6BAA6B;QAC5C,UAAU,EAAE,6BAA6B;QACzC,eAAe,EAAE,QAAQ;QACzB,iBAAiB,EAAE,0DAA0D;QAC7E,iBAAiB,EAAE,0DAA0D;QAC7E,yBAAyB,EAAE,IAAI;KAChC;CACF,CAAC;AAEF,oDAAoD;AACpD,MAAM,uBAAuB,GAA6C;IACxE,OAAO,EAAE,SAAS;IAClB,OAAO,EAAE,SAAS;IAClB,MAAM,EAAE,SAAS;IACjB,MAAM,EAAE,SAAS;CAClB,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAAyB;IAEzB,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,OAAO,uBAAuB,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,CAAC;AAC7D,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,qBAAqB,CAAC,KAAyB;IAC7D,OAAO,mBAAmB,CAAC,KAAK,CAAC,IAAI,SAAS,CAAC;AACjD,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,gBAAgB,CAAC,OAAiC;IAChE,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC;AAC3B,CAAC"} \ No newline at end of file diff --git a/packages/registry-client/dist/validateNetwork.d.ts b/packages/registry-client/dist/validateNetwork.d.ts deleted file mode 100644 index 466f056..0000000 --- a/packages/registry-client/dist/validateNetwork.d.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { type StellarDeploymentNetwork } from "./networks.js"; -/** x402 network strings accepted by SynapsVault. */ -export declare const X402_NETWORK_IDS: { - readonly testnet: "stellar:testnet"; - readonly mainnet: "stellar:pubnet"; -}; -export interface NetworkConfigInput { - stellarNetwork: StellarDeploymentNetwork; - x402Network: string; - sorobanRpcUrl: string; - horizonUrl?: string; - usdcSacContractId?: string; - registryContractId?: string; -} -export interface NetworkValidationIssue { - field: string; - message: string; -} -/** Normalize x402 network id (accept stellar:mainnet as alias for stellar:pubnet). */ -export declare function normalizeX402Network(network: string): string; -/** Infer deployment network from an x402 network string. */ -export declare function inferNetworkFromX402(network: string): StellarDeploymentNetwork | undefined; -/** Infer deployment network from a Soroban RPC URL hostname. */ -export declare function inferNetworkFromRpcUrl(rpcUrl: string): StellarDeploymentNetwork | undefined; -/** Infer deployment network from a Horizon URL hostname. */ -export declare function inferNetworkFromHorizonUrl(horizonUrl: string): StellarDeploymentNetwork | undefined; -/** Stellar network passphrase for the given x402 network id. */ -export declare function networkPassphraseForX402(x402Network: string): string; -/** - * Validate that network-related settings are internally consistent. - * Returns a list of issues; an empty list means the config is valid. - */ -export declare function validateNetworkConfig(input: NetworkConfigInput): NetworkValidationIssue[]; -/** Apply network preset defaults to env vars that were not explicitly set. */ -export declare function applyNetworkEnvDefaults(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv; -//# sourceMappingURL=validateNetwork.d.ts.map \ No newline at end of file diff --git a/packages/registry-client/dist/validateNetwork.d.ts.map b/packages/registry-client/dist/validateNetwork.d.ts.map deleted file mode 100644 index 4ac8780..0000000 --- a/packages/registry-client/dist/validateNetwork.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"validateNetwork.d.ts","sourceRoot":"","sources":["../src/validateNetwork.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,wBAAwB,EAE9B,MAAM,eAAe,CAAC;AAEvB,oDAAoD;AACpD,eAAO,MAAM,gBAAgB;;;CAGnB,CAAC;AAKX,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,wBAAwB,CAAC;IACzC,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,sFAAsF;AACtF,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAI5D;AAED,4DAA4D;AAC5D,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,wBAAwB,GAAG,SAAS,CAK1F;AAED,gEAAgE;AAChE,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,wBAAwB,GAAG,SAAS,CAS3F;AAED,4DAA4D;AAC5D,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,MAAM,GACjB,wBAAwB,GAAG,SAAS,CAStC;AAED,gEAAgE;AAChE,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAQpE;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,kBAAkB,GAAG,sBAAsB,EAAE,CA6EzF;AAED,8EAA8E;AAC9E,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAWjF"} \ No newline at end of file diff --git a/packages/registry-client/dist/validateNetwork.js b/packages/registry-client/dist/validateNetwork.js deleted file mode 100644 index 1fbe9fe..0000000 --- a/packages/registry-client/dist/validateNetwork.js +++ /dev/null @@ -1,149 +0,0 @@ -import { getNetworkPreset, resolveStellarNetwork, } from "./networks.js"; -/** x402 network strings accepted by SynapsVault. */ -export const X402_NETWORK_IDS = { - testnet: "stellar:testnet", - mainnet: "stellar:pubnet", -}; -/** Aliases that map to the canonical mainnet x402 id. */ -const MAINNET_X402_ALIASES = new Set(["stellar:pubnet", "stellar:mainnet"]); -/** Normalize x402 network id (accept stellar:mainnet as alias for stellar:pubnet). */ -export function normalizeX402Network(network) { - const trimmed = network.trim(); - if (MAINNET_X402_ALIASES.has(trimmed)) - return X402_NETWORK_IDS.mainnet; - return trimmed; -} -/** Infer deployment network from an x402 network string. */ -export function inferNetworkFromX402(network) { - const normalized = normalizeX402Network(network); - if (normalized === X402_NETWORK_IDS.testnet) - return "testnet"; - if (normalized === X402_NETWORK_IDS.mainnet) - return "mainnet"; - return undefined; -} -/** Infer deployment network from a Soroban RPC URL hostname. */ -export function inferNetworkFromRpcUrl(rpcUrl) { - try { - const host = new URL(rpcUrl).hostname.toLowerCase(); - if (host.includes("testnet")) - return "testnet"; - if (host === "soroban.stellar.org") - return "mainnet"; - } - catch { - // invalid URL handled elsewhere - } - return undefined; -} -/** Infer deployment network from a Horizon URL hostname. */ -export function inferNetworkFromHorizonUrl(horizonUrl) { - try { - const host = new URL(horizonUrl).hostname.toLowerCase(); - if (host.includes("testnet")) - return "testnet"; - if (host === "horizon.stellar.org") - return "mainnet"; - } - catch { - // invalid URL handled elsewhere - } - return undefined; -} -/** Stellar network passphrase for the given x402 network id. */ -export function networkPassphraseForX402(x402Network) { - const inferred = inferNetworkFromX402(x402Network); - if (!inferred) { - throw new Error(`Unsupported NETWORK value "${x402Network}". Use stellar:testnet or stellar:pubnet.`); - } - return getNetworkPreset(inferred).networkPassphrase; -} -/** - * Validate that network-related settings are internally consistent. - * Returns a list of issues; an empty list means the config is valid. - */ -export function validateNetworkConfig(input) { - const preset = getNetworkPreset(input.stellarNetwork); - const issues = []; - const normalizedX402 = normalizeX402Network(input.x402Network); - const x402Inferred = inferNetworkFromX402(normalizedX402); - if (!x402Inferred) { - issues.push({ - field: "NETWORK", - message: `Unsupported x402 network "${input.x402Network}". Use stellar:testnet or stellar:pubnet.`, - }); - } - else if (x402Inferred !== input.stellarNetwork) { - issues.push({ - field: "NETWORK", - message: `NETWORK=${input.x402Network} does not match STELLAR_NETWORK=${input.stellarNetwork}.`, - }); - } - else if (normalizedX402 !== preset.x402Network) { - issues.push({ - field: "NETWORK", - message: `NETWORK should be ${preset.x402Network} for STELLAR_NETWORK=${input.stellarNetwork}.`, - }); - } - const rpcInferred = inferNetworkFromRpcUrl(input.sorobanRpcUrl); - if (!rpcInferred) { - issues.push({ - field: "SOROBAN_RPC_URL", - message: `Cannot infer network from SOROBAN_RPC_URL="${input.sorobanRpcUrl}".`, - }); - } - else if (rpcInferred !== input.stellarNetwork) { - issues.push({ - field: "SOROBAN_RPC_URL", - message: `SOROBAN_RPC_URL points to ${rpcInferred} but STELLAR_NETWORK=${input.stellarNetwork}.`, - }); - } - if (input.horizonUrl) { - const horizonInferred = inferNetworkFromHorizonUrl(input.horizonUrl); - if (!horizonInferred) { - issues.push({ - field: "HORIZON_URL", - message: `Cannot infer network from HORIZON_URL="${input.horizonUrl}".`, - }); - } - else if (horizonInferred !== input.stellarNetwork) { - issues.push({ - field: "HORIZON_URL", - message: `HORIZON_URL points to ${horizonInferred} but STELLAR_NETWORK=${input.stellarNetwork}.`, - }); - } - } - if (input.usdcSacContractId && input.usdcSacContractId !== preset.usdcSacContractId) { - issues.push({ - field: "USDC_CONTRACT_ID", - message: `USDC_CONTRACT_ID does not match the canonical ${input.stellarNetwork} USDC SAC.`, - }); - } - if (input.registryContractId && - preset.defaultRegistryContractId && - input.registryContractId !== preset.defaultRegistryContractId && - input.stellarNetwork === "testnet") { - // Custom testnet contract IDs are allowed — only warn when clearly cross-network. - const otherPreset = getNetworkPreset(input.stellarNetwork === "testnet" ? "mainnet" : "testnet"); - if (otherPreset.defaultRegistryContractId === input.registryContractId) { - issues.push({ - field: "VAULT_REGISTRY_CONTRACT_ID", - message: `Registry contract ID belongs to ${otherPreset.stellarNetwork}, not ${input.stellarNetwork}.`, - }); - } - } - return issues; -} -/** Apply network preset defaults to env vars that were not explicitly set. */ -export function applyNetworkEnvDefaults(env) { - const stellarNetwork = resolveStellarNetwork(env.STELLAR_NETWORK); - const preset = getNetworkPreset(stellarNetwork); - return { - ...env, - STELLAR_NETWORK: stellarNetwork, - NETWORK: env.NETWORK ?? preset.x402Network, - SOROBAN_RPC_URL: env.SOROBAN_RPC_URL ?? preset.sorobanRpcUrl, - USDC_CONTRACT_ID: env.USDC_CONTRACT_ID ?? preset.usdcSacContractId, - }; -} -//# sourceMappingURL=validateNetwork.js.map \ No newline at end of file diff --git a/packages/registry-client/dist/validateNetwork.js.map b/packages/registry-client/dist/validateNetwork.js.map deleted file mode 100644 index 0d9c2cf..0000000 --- a/packages/registry-client/dist/validateNetwork.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"validateNetwork.js","sourceRoot":"","sources":["../src/validateNetwork.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/scripts/e2e-test.ts b/scripts/e2e-test.ts index ea3fe5b..396fe61 100644 --- a/scripts/e2e-test.ts +++ b/scripts/e2e-test.ts @@ -176,8 +176,8 @@ async function getOrCreateTestWallet(): Promise { const keypair = Keypair.random(); const publicKey = keypair.publicKey(); info(`generated fresh test wallet: ${publicKey}`); - info(` secret: ${keypair.secret()}`); - info(` save these to .env as E2E_PUBLIC_KEY / E2E_SECRET_KEY to reuse`); + info(" secret key not printed; generate your own keypair and set E2E_SECRET_KEY in .env to reuse"); + info(" save the public key to .env as E2E_PUBLIC_KEY to reuse"); info("funding via Friendbot…"); const res = await fetch(`https://friendbot.stellar.org?addr=${publicKey}`); diff --git a/src/lib/idempotency.inProgressTtl.test.ts b/src/lib/idempotency.inProgressTtl.test.ts new file mode 100644 index 0000000..b630741 --- /dev/null +++ b/src/lib/idempotency.inProgressTtl.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTtlCache } from "./ttlCache.js"; +import { IDEMPOTENCY_IN_PROGRESS_TTL_MS } from "./idempotency.js"; + +describe("in-progress idempotency TTL", () => { + it("expires abandoned in-progress records well before the full TTL", () => { + let t = 0; + const cache = createTtlCache<{ inProgress: boolean }>({ + defaultTtlMs: 24 * 3600_000, + cacheName: "test", + now: () => t, + } as never); + cache.set("k", { inProgress: true }, IDEMPOTENCY_IN_PROGRESS_TTL_MS); + t = IDEMPOTENCY_IN_PROGRESS_TTL_MS + 1; + expect(cache.get("k")).toBeUndefined(); + vi.restoreAllMocks(); + }); +}); diff --git a/src/lib/idempotency.ts b/src/lib/idempotency.ts index 777cd57..22d6c25 100644 --- a/src/lib/idempotency.ts +++ b/src/lib/idempotency.ts @@ -29,6 +29,9 @@ const defaultStore = createTtlCache({ cacheName: "idempotency", }); +/** Abandoned in-progress markers expire after twice the request timeout. */ +export const IDEMPOTENCY_IN_PROGRESS_TTL_MS = config.REQUEST_TIMEOUT_MS * 2; + // Shared (Postgres-backed) store installed at boot so retries that land on a // different replica see the original result (#71). When unset (tests, local // scripts) the in-memory store above is used. diff --git a/src/lib/rateLimit/stores.redisBoot.test.ts b/src/lib/rateLimit/stores.redisBoot.test.ts new file mode 100644 index 0000000..2f4dd8a --- /dev/null +++ b/src/lib/rateLimit/stores.redisBoot.test.ts @@ -0,0 +1,10 @@ +import { describe, expect, it } from "vitest"; +import { RedisSlidingWindowStore } from "./stores.js"; + +describe("RedisSlidingWindowStore with unreachable Redis", () => { + it("does not leave an unhandled rejection and consume rejects cleanly", async () => { + const store = new RedisSlidingWindowStore("redis://127.0.0.1:1"); + await expect(store.consume("k", 5, 1000)).rejects.toThrow(); + await store.close(); + }); +}); diff --git a/src/lib/rateLimit/stores.ts b/src/lib/rateLimit/stores.ts index 99a6b1e..05f18e9 100644 --- a/src/lib/rateLimit/stores.ts +++ b/src/lib/rateLimit/stores.ts @@ -1,4 +1,5 @@ import { randomBytes } from "node:crypto"; +import { rootLogger } from "../logger.js"; import type { RateLimitConsumeResult, RateLimitStore } from "./types.js"; function buildBlockedResult( @@ -108,18 +109,70 @@ export class MemorySlidingWindowStore implements RateLimitStore { */ export class RedisSlidingWindowStore implements RateLimitStore { private client: import("redis").RedisClientType | null = null; - private readonly connectPromise: Promise; + private connectPromise: Promise; + private fallbackLogged = false; + private reconnectAttempt = 0; + private reconnectTimer: NodeJS.Timeout | null = null; + private closed = false; constructor(private readonly redisUrl: string) { - this.connectPromise = this.init(); + this.connectPromise = this.connectSafely(); + } + + /** Connect without ever rejecting; log the failure once and retry with backoff. */ + private async connectSafely(): Promise { + try { + await this.init(); + this.reconnectAttempt = 0; + this.fallbackLogged = false; + } catch (err) { + if (!this.fallbackLogged) { + this.fallbackLogged = true; + rootLogger.warn( + { event: "rate_limit_redis_unavailable", err }, + "Redis rate-limit store unavailable; requests will be rejected by the store until it reconnects", + ); + } + try { + await this.client?.disconnect(); + } catch { + // ignore + } + this.client = null; + this.scheduleReconnect(); + } + } + + private scheduleReconnect(): void { + if (this.closed || this.reconnectTimer) return; + const delay = Math.min(30_000, 500 * 2 ** this.reconnectAttempt++); + this.reconnectTimer = setTimeout(() => { + this.reconnectTimer = null; + this.connectPromise = this.connectSafely(); + }, delay); + this.reconnectTimer.unref(); } private async init(): Promise { const { createClient } = await import("redis"); - this.client = createClient({ url: this.redisUrl }); + this.client = createClient({ + url: this.redisUrl, + socket: { + connectTimeout: 5_000, + // Give up quickly so connect() rejects and our backoff loop takes over. + reconnectStrategy: (retries: number) => + retries >= 2 ? new Error("Redis unreachable") : 200 * (retries + 1), + }, + }); this.client.on("error", () => { // Connection errors are handled per consume call; avoid crashing the process. }); + this.client.on("end", () => { + if (!this.closed && this.client) { + this.client = null; + this.scheduleReconnect(); + } + }); await this.client.connect(); } @@ -171,6 +224,8 @@ export class RedisSlidingWindowStore implements RateLimitStore { } async close(): Promise { + this.closed = true; + if (this.reconnectTimer) clearTimeout(this.reconnectTimer); await this.connectPromise; if (this.client?.isOpen) { await this.client.quit(); diff --git a/src/routes/resources.ts b/src/routes/resources.ts index 051acef..64f9c93 100644 --- a/src/routes/resources.ts +++ b/src/routes/resources.ts @@ -34,6 +34,7 @@ import { and, eq, inArray } from "drizzle-orm"; import { config } from "../config.js"; import { getLogger } from "../lib/logger.js"; import { publishIpRateLimit, publishWalletRateLimit } from "../middleware/rateLimiters.js"; +import { IDEMPOTENCY_IN_PROGRESS_TTL_MS } from "../lib/idempotency.js"; import { getIdempotencyStore, idempotencyCacheKey } from "../lib/idempotency.js"; import { parseIdempotencyKey } from "../lib/idempotencyKey.js"; import { decodeNewCreator, decodeSetPrice } from "../services/signedTxDecode.js"; @@ -100,7 +101,7 @@ router.post( res.status(existing.result.status).json(existing.result.body); return; } - await store.set(scopedKey, { inProgress: true }); + await store.set(scopedKey, { inProgress: true }, IDEMPOTENCY_IN_PROGRESS_TTL_MS); } // Records the final result under the idempotency key (when keyed), then responds.