From b271305fa0dca83ab72e3c8d149b52eee548ec6d Mon Sep 17 00:00:00 2001 From: Patrick Bennett <170563+pbennett@users.noreply.github.com> Date: Tue, 27 Oct 2026 18:29:38 -0400 Subject: [PATCH] feat(client): stop auto-disabling Humble and add Protocol.Humble The router routes through Humble pools again: the swap-direction bug that made Humble swaps fail (and got it disabled in July 2025) is fixed, and its quotes are verified against mainnet simulation. fetchQuote no longer adds Humble to disabledProtocols; only Tinyman v1 stays auto-disabled (unless _allowNonComposableSwaps). Callers can still exclude it with disabledProtocols: [Protocol.Humble]. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_011w8febSoFfFhV7xSwAR5oc --- packages/haystack/src/client.ts | 7 +++---- packages/haystack/src/constants.ts | 3 ++- packages/haystack/tests/client.test.ts | 13 +++++-------- packages/haystack/tests/constants.test.ts | 13 +++++++------ 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/packages/haystack/src/client.ts b/packages/haystack/src/client.ts index bf4925c..5dd84ad 100644 --- a/packages/haystack/src/client.ts +++ b/packages/haystack/src/client.ts @@ -167,11 +167,10 @@ export class RouterClient { ) } - // Conditionally include deprecated protocols in disabled list - // If _allowNonComposableSwaps is true, only exclude Tinyman v1 from auto-disabled list - // Otherwise, always include all deprecated protocols (Tinyman v1, Humble) + // Always disable the deprecated protocols (Tinyman v1), unless + // _allowNonComposableSwaps opts into Tinyman v1 const allDisabledProtocols = _allowNonComposableSwaps - ? [...new Set(['Humble', ...disabledProtocols])] + ? [...new Set(disabledProtocols)] : [...new Set([...DEPRECATED_PROTOCOLS, ...disabledProtocols])] let includeOptIn = optIn diff --git a/packages/haystack/src/constants.ts b/packages/haystack/src/constants.ts index 52753a6..df40d0e 100644 --- a/packages/haystack/src/constants.ts +++ b/packages/haystack/src/constants.ts @@ -3,6 +3,7 @@ */ export enum Protocol { TinymanV2 = 'TinymanV2', + Humble = 'Humble', Algofi = 'Algofi', Algomint = 'Algomint', Pact = 'Pact', @@ -15,7 +16,7 @@ export enum Protocol { * * @internal */ -export const DEPRECATED_PROTOCOLS = ['Humble', 'Tinyman'] as const +export const DEPRECATED_PROTOCOLS = ['Tinyman'] as const /** Default Algod node URI for mainnet */ export const DEFAULT_ALGOD_URI = 'https://mainnet-api.4160.nodely.dev/' diff --git a/packages/haystack/tests/client.test.ts b/packages/haystack/tests/client.test.ts index ec7ad6f..f2c3b94 100644 --- a/packages/haystack/tests/client.test.ts +++ b/packages/haystack/tests/client.test.ts @@ -163,9 +163,7 @@ describe('RouterClient', () => { expect(callUrl).toContain('toASAID=31566704') expect(callUrl).toContain('amount=1000000') expect(callUrl).toContain('type=fixed-output') - expect(callUrl).toContain( - 'disabledProtocols=Humble%2CTinyman%2CTinymanV2', - ) + expect(callUrl).toContain('disabledProtocols=Tinyman%2CTinymanV2') expect(callUrl).toContain('maxGroupSize=12') expect(callUrl).toContain('maxDepth=3') expect(callUrl).toContain('optIn=true') @@ -190,8 +188,8 @@ describe('RouterClient', () => { }) const callUrl = mockRequest.mock.calls?.[0]?.[0] as string - expect(callUrl).toContain('Humble') - expect(callUrl).toContain('Tinyman') + expect(callUrl).toContain('disabledProtocols=Tinyman&') + expect(callUrl).not.toContain('Humble') }) it('should use custom apiBaseUrl when provided', async () => { @@ -540,8 +538,7 @@ describe('RouterClient', () => { const callUrl = mockRequest.mock.calls?.[0]?.[0] as string // Should NOT include Tinyman when _allowNonComposableSwaps is true expect(callUrl).not.toContain('Tinyman') - // Should still include Humble - expect(callUrl).toContain('Humble') + expect(callUrl).not.toContain('Humble') }) it('should include deprecated protocols by default when _allowNonComposableSwaps is false', async () => { @@ -566,7 +563,7 @@ describe('RouterClient', () => { const callUrl = mockRequest.mock.calls?.[0]?.[0] as string // Should include deprecated protocols expect(callUrl).toContain('Tinyman') - expect(callUrl).toContain('Humble') + expect(callUrl).not.toContain('Humble') }) it('should throw error when _allowNonComposableSwaps is used with optIn: true', async () => { diff --git a/packages/haystack/tests/constants.test.ts b/packages/haystack/tests/constants.test.ts index 441ece1..3ac977b 100644 --- a/packages/haystack/tests/constants.test.ts +++ b/packages/haystack/tests/constants.test.ts @@ -18,6 +18,7 @@ describe('constants', () => { describe('Protocol enum', () => { it('should have expected protocol values', () => { expect(Protocol.TinymanV2).toBe('TinymanV2') + expect(Protocol.Humble).toBe('Humble') expect(Protocol.Algofi).toBe('Algofi') expect(Protocol.Algomint).toBe('Algomint') expect(Protocol.Pact).toBe('Pact') @@ -25,20 +26,20 @@ describe('constants', () => { expect(Protocol.TAlgo).toBe('TAlgo') }) - it('should have exactly 6 protocols', () => { + it('should have exactly 7 protocols', () => { const protocols = Object.values(Protocol) - expect(protocols).toHaveLength(6) + expect(protocols).toHaveLength(7) }) }) describe('DEPRECATED_PROTOCOLS', () => { - it('should include Humble and Tinyman', () => { - expect(DEPRECATED_PROTOCOLS).toContain('Humble') + it('should include Tinyman but not Humble', () => { expect(DEPRECATED_PROTOCOLS).toContain('Tinyman') + expect(DEPRECATED_PROTOCOLS).not.toContain('Humble') }) - it('should have exactly 2 deprecated protocols', () => { - expect(DEPRECATED_PROTOCOLS).toHaveLength(2) + it('should have exactly 1 deprecated protocol', () => { + expect(DEPRECATED_PROTOCOLS).toHaveLength(1) }) it('should be a readonly array', () => {