From 5ae6dbfc3f239db291abfd98a82953c74a019df2 Mon Sep 17 00:00:00 2001 From: Andrew Miller Date: Fri, 5 Jun 2026 10:55:30 -0400 Subject: [PATCH] getInterchain: perChain + chains options (default first anchor per chain); bump 1.5.0 transaction.getInterchain / block.getInterchain take an optional { perChain?, chains? } that maps to prime-node's ?perChain=&chains= params. Default (no options) returns one anchor per chain. Shared buildInterchainQuery helper + InterchainOptions type, exported; jest-tested. --- README.md | 18 ++++++++++++++++++ package.json | 2 +- src/block.ts | 7 +++++-- src/index.ts | 1 + src/interchain.ts | 37 +++++++++++++++++++++++++++++++++++++ src/transaction.ts | 10 ++++++++-- tests/interchain.test.ts | 27 +++++++++++++++++++++++++++ 7 files changed, 97 insertions(+), 5 deletions(-) create mode 100644 src/interchain.ts create mode 100644 tests/interchain.test.ts diff --git a/README.md b/README.md index cef2e2b..31e9925 100644 --- a/README.md +++ b/README.md @@ -145,6 +145,24 @@ await sdk.transactionType.delete('my-type'); const block = await sdk.block.get('block-id'); ``` +### Interchain trace + +`transaction.getInterchain` / `block.getInterchain` trace a prime block to the +public-chain anchors covering it. By default they return the **first anchor per +chain** (anchor proofs are chained, so the earliest per chain is the meaningful +one). Tune with options: + +```typescript +// Default: first anchor per chain (ETH, BTC, …) +const trace = await sdk.block.getInterchain('42'); + +// Up to 3 anchors per chain +await sdk.block.getInterchain('42', { perChain: 3 }); + +// All anchors, only the ETH-mainnet chain ("1"); "0" = BTC +await sdk.block.getInterchain('42', { perChain: 0, chains: ['1'] }); +``` + ### Proof Measure (public, unauthenticated) `proof-measure` is a separate, **public, unauthenticated** Dragonchain service diff --git a/package.json b/package.json index ef5609d..b2550a7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@dragonchain-inc/prime-sdk", - "version": "1.4.0", + "version": "1.5.0", "description": "Official Dragonchain Prime SDK for Node.js and TypeScript", "main": "./dist/index.js", "module": "./dist/index.mjs", diff --git a/src/block.ts b/src/block.ts index 1e7a1bc..0d646ba 100644 --- a/src/block.ts +++ b/src/block.ts @@ -3,6 +3,7 @@ */ import { DragonchainClient } from './client'; +import { InterchainOptions, buildInterchainQuery } from './interchain'; import { Block, InterchainTrace } from './types'; export class BlockClient { @@ -23,7 +24,9 @@ export class BlockClient { * Traces a block to the validator (verification) blocks that validated it and * the public-chain interchain anchors those validator blocks were bundled into. */ - async getInterchain(blockId: string): Promise { - return this.client.get(`/api/v1/block/${blockId}/interchain`); + async getInterchain(blockId: string, options?: InterchainOptions): Promise { + return this.client.get( + `/api/v1/block/${blockId}/interchain${buildInterchainQuery(options)}` + ); } } diff --git a/src/index.ts b/src/index.ts index 40e16c7..cda710c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -85,6 +85,7 @@ export { BlockClient } from './block'; export { SystemClient } from './system'; export { ProofMeasureClient, PROOF_MEASURE_DEFAULT_BASE_URL } from './proofMeasure'; export { UnauthHttpClient, UnauthClientConfig } from './unauthHttpClient'; +export { InterchainOptions, buildInterchainQuery } from './interchain'; // Default export export default DragonchainSDK; diff --git a/src/interchain.ts b/src/interchain.ts new file mode 100644 index 0000000..21f10af --- /dev/null +++ b/src/interchain.ts @@ -0,0 +1,37 @@ +/** + * Options + query builder for the interchain-trace endpoints + * (transaction.getInterchain / block.getInterchain). + * + * Anchor proofs are chained, so by default the trace returns the first anchor + * per public chain; these options change how many and which chains are returned. + */ + +export interface InterchainOptions { + /** + * Max interchain anchors returned per public chain, earliest-first. + * 1 = the first anchor per chain (the service default); 0 = all anchors. + */ + perChain?: number; + /** + * Interchain chain ids to include ("1" = ETH mainnet, "0" = BTC; testnet ids + * differ). Omit to include every chain found. + */ + chains?: string[]; +} + +/** + * Builds the "?perChain=...&chains=..." suffix for the interchain trace + * endpoints. Returns "" when no options are set (the server then applies its + * defaults: one anchor per chain, all chains). + */ +export function buildInterchainQuery(options?: InterchainOptions): string { + if (!options) return ''; + const parts: string[] = []; + if (options.perChain !== undefined) { + parts.push(`perChain=${encodeURIComponent(String(options.perChain))}`); + } + if (options.chains && options.chains.length > 0) { + parts.push(`chains=${options.chains.map(encodeURIComponent).join(',')}`); + } + return parts.length ? `?${parts.join('&')}` : ''; +} diff --git a/src/transaction.ts b/src/transaction.ts index a738e0f..472fca5 100644 --- a/src/transaction.ts +++ b/src/transaction.ts @@ -3,6 +3,7 @@ */ import { DragonchainClient } from './client'; +import { InterchainOptions, buildInterchainQuery } from './interchain'; import { ContentType, TransactionCreateRequest, @@ -56,8 +57,13 @@ export class TransactionClient { * blocks were bundled into. If the transaction is still pending (not yet in a * block) the trace's arrays are empty. */ - async getInterchain(transactionId: string): Promise { - return this.client.get(`/api/v1/transaction/${transactionId}/interchain`); + async getInterchain( + transactionId: string, + options?: InterchainOptions + ): Promise { + return this.client.get( + `/api/v1/transaction/${transactionId}/interchain${buildInterchainQuery(options)}` + ); } /** diff --git a/tests/interchain.test.ts b/tests/interchain.test.ts new file mode 100644 index 0000000..c2fc442 --- /dev/null +++ b/tests/interchain.test.ts @@ -0,0 +1,27 @@ +import { buildInterchainQuery } from '../src/interchain'; + +describe('buildInterchainQuery', () => { + it('returns empty string with no options', () => { + expect(buildInterchainQuery()).toBe(''); + expect(buildInterchainQuery({})).toBe(''); + }); + + it('encodes perChain (including 0 for all)', () => { + expect(buildInterchainQuery({ perChain: 1 })).toBe('?perChain=1'); + expect(buildInterchainQuery({ perChain: 0 })).toBe('?perChain=0'); + }); + + it('joins chains with commas', () => { + expect(buildInterchainQuery({ chains: ['1', '0'] })).toBe('?chains=1,0'); + }); + + it('combines perChain and chains', () => { + expect(buildInterchainQuery({ perChain: 2, chains: ['1', '0'] })).toBe( + '?perChain=2&chains=1,0' + ); + }); + + it('ignores an empty chains array', () => { + expect(buildInterchainQuery({ chains: [] })).toBe(''); + }); +});