From 602771405cf56a56570f6215a85fffc4610f8cf1 Mon Sep 17 00:00:00 2001 From: ja88a Date: Fri, 15 May 2026 13:00:35 +0200 Subject: [PATCH 1/8] feat: intermediary fee types (re-apply after #525 revert) Re-applies the FeeSplit / FeeRecipient / intermediary fee surface that was originally merged in #520 and reverted in #525 to ship 17.81.x cleanly. Refs CORE-213. Pairs with lifi-backend PR #8187 on the consumer side. --- src/api.ts | 7 +++++ src/step.ts | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/src/api.ts b/src/api.ts index 28c433ec..d9f113d8 100644 --- a/src/api.ts +++ b/src/api.ts @@ -164,6 +164,10 @@ export interface RouteOptions extends RouteOptionsBase { /** Should contain the identifier of the integrator. Usually, it's dApp/company name. */ integrator?: string + /** Optional intermediary identifier for multi-party fee splitting. + * Requires a registered integrator with a `fee` and a configured intermediary share on the backend. */ + intermediary?: string + /** Integrators can set a wallet address as a referrer to track them */ referrer?: string @@ -368,6 +372,9 @@ export interface QuoteRequest extends ToolConfiguration, TimingStrings { order?: Order slippage?: number | string integrator?: string + /** Optional intermediary identifier for multi-party fee splitting. + * Requires a registered integrator with a `fee` and a configured intermediary share on the backend. */ + intermediary?: string referrer?: string fee?: number | string diff --git a/src/step.ts b/src/step.ts index 7fa05e31..d4968630 100644 --- a/src/step.ts +++ b/src/step.ts @@ -6,6 +6,54 @@ import type { } from './api.js' import type { Token } from './tokens/index.js' +/** + * How a fee was calculated / the role of the recipient in the split. + * + * - `FIXED` — a fixed LI.FI cut applied alongside the integrator's own fee. + * - `SHARED` — a single shared pool divided between LI.FI and the integrator. + * - `DYNAMIC` — fee resolved at quote time from configured rules (e.g. dynamic + * stablecoin pricing). + * - `INTERMEDIARY` — a third-party recipient that carves a configured share + * from the integrator pool (e.g. a widget or aggregator). + * - `DISTRIBUTION` — a partner-specified extra recipient added in parallel to + * the integrator/intermediary split via the `distributionFees` request param. + * + * The union is expected to grow over time. When pattern-matching with a + * `switch`, always include a `default` branch so adding a new value remains + * a non-breaking minor for consumers. + */ +export type FeeSplitType = + | 'FIXED' + | 'SHARED' + | 'DYNAMIC' + | 'INTERMEDIARY' + | 'DISTRIBUTION' + +export interface FeeRecipient { + /** + * Recipient identifier. Polymorphic — interpret in conjunction with `type`: + * - `'lifi'` for the LI.FI platform recipient, + * - the integrator id (e.g. `'jumper'`) for the integrator recipient, + * - the intermediary id (e.g. `'mesh'`) when `type === 'INTERMEDIARY'`, + * - the recipient wallet address when `type === 'DISTRIBUTION'`. + * + * Do not display this value directly in user-facing UI without + * `type`-aware formatting — for `DISTRIBUTION` rows the value is a + * raw hex address. + */ + name: string + /** Absolute fee amount, in source token base units (string-encoded). */ + fee: string + /** + * Fee calculation type. Absent when not statically determinable (e.g. + * an integrator entry whose `feeType` could not be resolved from the + * matching planned fee cost). Consumers should null-check before reading. + */ + type?: FeeSplitType + /** Recipient wallet address on the source chain. */ + walletAddress?: string +} + export interface FeeCost { name: string description: string @@ -14,6 +62,33 @@ export interface FeeCost { amount: string amountUSD: string included: boolean + feeSplit?: { + /** LI.FI's slice of THIS `FeeCost.amount`. */ + lifiFee: string + /** + * The integrator's slice of THIS `FeeCost.amount` — the integrator's + * portion only, NOT including any intermediary or distribution amounts. + */ + integratorFee: string + /** + * The intermediary's slice of THIS `FeeCost.amount` when an intermediary + * participates in the split. Absent otherwise. + */ + intermediaryFee?: string + /** + * Per-recipient breakdown of THIS `FeeCost.amount`. Source of truth for + * new consumers — the aggregate fields above are disjoint slices kept + * for backward compatibility with 2-recipient consumers. + * + * Note: partner-specified distribution recipients are emitted as + * separate `FeeCost` entries (one per receiver, `name: "Fee Forward"`) + * at quote/route estimation time. At status-response time the same + * recipients may appear merged on the integrator's `FeeCost` for + * compactness. Iterate `estimate.feeCosts[]` to discover all + * recipients across both shapes. + */ + recipients?: FeeRecipient[] + } } export interface GasCost { @@ -101,6 +176,9 @@ export interface StepBase { tool: StepTool toolDetails: StepToolDetails integrator?: string + /** Intermediary identifier set when the route was generated with a multi-party fee split. + * Threaded through to /stepTransaction so the same split can be reproduced at tx-generation time. */ + intermediary?: string referrer?: string action: Action estimate?: Estimate From 3003094d89aaff3fe9a40b5197c55574c23f4dc5 Mon Sep 17 00:00:00 2001 From: ja88a Date: Fri, 15 May 2026 13:00:55 +0200 Subject: [PATCH 2/8] chore(release): 17.83.0-alpha.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f165a054..b7564e2a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lifi/types", - "version": "17.82.1", + "version": "17.83.0-alpha.0", "description": "Types for the LI.FI stack", "keywords": [ "sdk", From f588f4142dadf2aad8ad5823ffc62ab2497975a9 Mon Sep 17 00:00:00 2001 From: ja88a Date: Fri, 15 May 2026 18:40:42 +0200 Subject: [PATCH 3/8] feat: add DistributionFee type to RouteOptions and QuoteRequest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Promotes the partner-provided distributionFees request parameter to the public type surface alongside intermediary. RouteOptions and QuoteRequest both gain distributionFees?: DistributionFee[]; QuoteToAmountRequest inherits via Omit. JSDoc documents per-entry constraints (decimal percentage, EVM receiver, sum cap, ~10 entry hard cap) without exporting numeric constants — limits stay backend policy, not part of the SemVer contract. --- src/api.ts | 51 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/src/api.ts b/src/api.ts index d9f113d8..6307b6aa 100644 --- a/src/api.ts +++ b/src/api.ts @@ -160,6 +160,36 @@ export interface RouteOptionsBase { executionType?: ExecutionType } +/** + * A single entry in the `distributionFees` request parameter. + * + * `distributionFees` lets a verified integrator carve additional on-chain + * recipients out of their integrator-fee pool, in parallel with the + * optional `intermediary` carve. Each entry produces an extra fee transfer + * to `receiver` proportional to `percentage` of the source amount, executed + * via the LI.FI FeeForwarder contract. Partner-provided receivers are + * sanction-screened at quote time (warn-log on failure, see backend logs). + * + * Constraints (enforced server-side, subject to change without a types + * major bump — read the current values from the API error responses if + * you need them programmatically): + * - `percentage` is a decimal proportion ( `0.001` = 0.1% ), exclusive of 0 + * and bounded by a per-request cap. The aggregate fee (integrator `fee` + * plus all `distributionFees[].percentage`) is also capped by the + * backend; exceeding either limit yields a `ValidationError`. + * - At most ~10 entries per request. + * - `receiver` must be a non-zero EVM address (`0x` followed by 40 hex + * characters). Non-EVM receivers are not yet supported. + * + * Only meaningful when `integrator` is set and has an active fee agreement. + */ +export interface DistributionFee { + /** Fraction of the swap input taken as a distribution fee (e.g. `0.001` = 0.1%). */ + percentage: number + /** On-chain EVM recipient address; must be a non-zero `0x`-prefixed hex address. */ + receiver: string +} + export interface RouteOptions extends RouteOptionsBase { /** Should contain the identifier of the integrator. Usually, it's dApp/company name. */ integrator?: string @@ -168,6 +198,16 @@ export interface RouteOptions extends RouteOptionsBase { * Requires a registered integrator with a `fee` and a configured intermediary share on the backend. */ intermediary?: string + /** + * Partner-provided fee distribution recipients. Each entry adds an + * independent on-chain transfer to `receiver` in proportion to + * `percentage` of the swap input, in parallel with the integrator and + * (optional) intermediary carves. Requires a verified `integrator` with + * an active fee agreement. + * @see {@link DistributionFee} for per-entry constraints. + */ + distributionFees?: DistributionFee[] + /** Integrators can set a wallet address as a referrer to track them */ referrer?: string @@ -375,6 +415,8 @@ export interface QuoteRequest extends ToolConfiguration, TimingStrings { /** Optional intermediary identifier for multi-party fee splitting. * Requires a registered integrator with a `fee` and a configured intermediary share on the backend. */ intermediary?: string + /** @see {@link RouteOptions.distributionFees} and {@link DistributionFee} */ + distributionFees?: DistributionFee[] referrer?: string fee?: number | string @@ -413,8 +455,10 @@ export interface QuoteRequest extends ToolConfiguration, TimingStrings { insurance?: boolean } -export interface QuoteToAmountRequest - extends Omit { +export interface QuoteToAmountRequest extends Omit< + QuoteRequest, + 'fromAmount' | 'fromAmountForGas' | 'insurance' +> { toAmount: string } @@ -857,8 +901,7 @@ export type TransferSummary = { totalReceivedAmount: number } -export interface TransferSummariesResponse - extends PaginatedResponse {} +export interface TransferSummariesResponse extends PaginatedResponse {} export interface GetStepRequest { stepId: string From 50f7819dbaaa0bed51a45c7ab1423d8789edc946 Mon Sep 17 00:00:00 2001 From: ja88a Date: Fri, 15 May 2026 18:40:52 +0200 Subject: [PATCH 4/8] chore(release): 17.83.0-alpha.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b7564e2a..85402faf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lifi/types", - "version": "17.83.0-alpha.0", + "version": "17.83.0-alpha.1", "description": "Types for the LI.FI stack", "keywords": [ "sdk", From 67a026c56054a09f4a35f86d41b7f85bff45a4bb Mon Sep 17 00:00:00 2001 From: ja88a Date: Mon, 18 May 2026 21:39:08 +0200 Subject: [PATCH 5/8] refactor: extract FeeSplit as a named public interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `FeeCost.feeSplit` was an inline anonymous shape; expose it as the named `FeeSplit` interface so downstream consumers (lifi-backend, lifi-sdk) can import it directly instead of writing `NonNullable`. Pure factoring — shape, JSDoc semantics, and field set are unchanged. `FeeRecipient` JSDoc also reflowed to single-line blocks for consistency. --- src/step.ts | 71 ++++++++++++++++++++++++++--------------------------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/src/step.ts b/src/step.ts index d4968630..1c6fe792 100644 --- a/src/step.ts +++ b/src/step.ts @@ -30,8 +30,7 @@ export type FeeSplitType = | 'DISTRIBUTION' export interface FeeRecipient { - /** - * Recipient identifier. Polymorphic — interpret in conjunction with `type`: + /** Recipient identifier. Polymorphic — interpret in conjunction with `type`: * - `'lifi'` for the LI.FI platform recipient, * - the integrator id (e.g. `'jumper'`) for the integrator recipient, * - the intermediary id (e.g. `'mesh'`) when `type === 'INTERMEDIARY'`, @@ -39,21 +38,46 @@ export interface FeeRecipient { * * Do not display this value directly in user-facing UI without * `type`-aware formatting — for `DISTRIBUTION` rows the value is a - * raw hex address. - */ + * raw hex address. */ name: string - /** Absolute fee amount, in source token base units (string-encoded). */ + + /** Absolute fee amount, a big number in source token base units (string-encoded). */ fee: string - /** - * Fee calculation type. Absent when not statically determinable (e.g. + + /** Fee calculation type. Absent when not statically determinable (e.g. * an integrator entry whose `feeType` could not be resolved from the - * matching planned fee cost). Consumers should null-check before reading. - */ + * matching planned fee cost). Consumers should null-check before reading. */ type?: FeeSplitType + /** Recipient wallet address on the source chain. */ walletAddress?: string } +export interface FeeSplit { + /** LI.FI's slice of THIS `FeeCost.amount`. */ + lifiFee: string + + /** The integrator's slice of THIS `FeeCost.amount` — the integrator's + * portion only, NOT including any intermediary or distribution amounts. */ + integratorFee: string + + /** The intermediary's slice of THIS `FeeCost.amount` when an intermediary + * participates in the split. Absent otherwise. */ + intermediaryFee?: string + + /** Per-recipient breakdown of THIS `FeeCost.amount`. Source of truth for + * new consumers — the aggregate fields above are disjoint slices kept + * for backward compatibility with 2-recipient consumers. + * + * Note: partner-specified distribution recipients are emitted as + * separate `FeeCost` entries (one per receiver, `name: "Fee Forward"`) + * at quote/route estimation time. At status-response time the same + * recipients may appear merged on the integrator's `FeeCost` for + * compactness. Iterate `estimate.feeCosts[]` to discover all + * recipients across both shapes. */ + recipients?: FeeRecipient[] +} + export interface FeeCost { name: string description: string @@ -62,33 +86,8 @@ export interface FeeCost { amount: string amountUSD: string included: boolean - feeSplit?: { - /** LI.FI's slice of THIS `FeeCost.amount`. */ - lifiFee: string - /** - * The integrator's slice of THIS `FeeCost.amount` — the integrator's - * portion only, NOT including any intermediary or distribution amounts. - */ - integratorFee: string - /** - * The intermediary's slice of THIS `FeeCost.amount` when an intermediary - * participates in the split. Absent otherwise. - */ - intermediaryFee?: string - /** - * Per-recipient breakdown of THIS `FeeCost.amount`. Source of truth for - * new consumers — the aggregate fields above are disjoint slices kept - * for backward compatibility with 2-recipient consumers. - * - * Note: partner-specified distribution recipients are emitted as - * separate `FeeCost` entries (one per receiver, `name: "Fee Forward"`) - * at quote/route estimation time. At status-response time the same - * recipients may appear merged on the integrator's `FeeCost` for - * compactness. Iterate `estimate.feeCosts[]` to discover all - * recipients across both shapes. - */ - recipients?: FeeRecipient[] - } + + feeSplit?: FeeSplit } export interface GasCost { From bfd7f99b4b2c701bc551421ac7c4ae4da6067d15 Mon Sep 17 00:00:00 2001 From: ja88a Date: Mon, 18 May 2026 21:39:18 +0200 Subject: [PATCH 6/8] chore(release): 17.83.0-alpha.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 85402faf..d2a5cba8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lifi/types", - "version": "17.83.0-alpha.1", + "version": "17.83.0-alpha.2", "description": "Types for the LI.FI stack", "keywords": [ "sdk", From e01e8305e8fae97d71e075e597f97210fdd71bff Mon Sep 17 00:00:00 2001 From: ja88a Date: Thu, 28 May 2026 22:39:46 +0200 Subject: [PATCH 7/8] chore(release): 17.83.0-alpha.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d2a5cba8..a461e7c8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lifi/types", - "version": "17.83.0-alpha.2", + "version": "17.83.0-alpha.3", "description": "Types for the LI.FI stack", "keywords": [ "sdk", From 53b848175cb75aaed1d9a7f34590fc58e10cefd6 Mon Sep 17 00:00:00 2001 From: ja88a Date: Wed, 10 Jun 2026 11:43:48 +0200 Subject: [PATCH 8/8] chore(release): 17.84.0-alpha.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 60592ad1..0892c22c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@lifi/types", - "version": "17.83.0", + "version": "17.84.0-alpha.0", "description": "Types for the LI.FI stack", "keywords": [ "sdk",