@zkp2p/sdk 0.9.0 → 0.10.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -80,7 +80,7 @@ const client = new Zkp2pClient({
80
80
  - `indexerUrl` and `baseApiUrl`: override defaults when you are targeting custom deployments
81
81
  - `timeouts`: `{ api?: number }` — API timeout in milliseconds (default 15000)
82
82
 
83
- **No external API key is required.** `createDeposit`, `registerPayeeDetails`, `getQuote`, `getTakerTier`, `signalIntent`, and the rest of the public SDK flows work without `apiKey` or `authorizationToken`. When `baseApiUrl` is configured, `signalIntent()` can auto-fetch a gating service signature from curator `/v3/intent/sign` without auth. Quote responses include resolved maker payee details (`offchainId`, `telegramUsername`, `metadata`) when curator has them.
83
+ **No external API key is required.** `createDeposit`, `registerPayeeDetails`, `getQuote`, `signalIntent`, and the rest of the public SDK flows work without `apiKey` or `authorizationToken`. When `baseApiUrl` is configured, `signalIntent()` can auto-fetch a gating service signature from curator `/v3/intent/sign` without auth. Quote responses include resolved maker payee details (`offchainId`, `telegramUsername`, `metadata`) when curator has them.
84
84
 
85
85
  Indexer defaults by environment:
86
86
 
@@ -94,16 +94,18 @@ Indexer defaults by environment:
94
94
  | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
95
95
  | Deposits | `createDeposit`, `addFunds`, `removeFunds`, `withdrawDeposit`, `ensureAllowance`, `setAcceptingIntents`, `setIntentRange`, `setCurrencyMinRate`, `setRetainOnEmpty` |
96
96
  | Payment methods and currencies | `addPaymentMethods`, `removePaymentMethod`, `setPaymentMethodActive`, `addCurrencies`, `removeCurrency`, `deactivateCurrency`, `pruneExpiredIntents` |
97
- | Intents | `signalIntent`, `fulfillIntent`, `cancelIntent`, `releaseFundsToPayer`, `getFulfillIntentInputs` |
97
+ | Intents | `signalIntent`, `fulfillIntent`, `cancelIntent`, `releaseFundsToPayer`, `getFulfillIntentInputs`, including explicit staging OrchestratorV3 routing |
98
98
  | Prepared transactions | `client.prepareCreateDeposit(...)`, `client.signalIntent.prepare(...)`, `client.fulfillIntent.prepare(...)`, `client.setVaultFee.prepare(...)`, and equivalent prepare flows across the rest of the prepareable write surface |
99
- | Payee and quote APIs | `registerPayeeDetails`, `resolvePayeeHash`, `getQuote`, `getQuotesBestByPlatform`, `getTakerTier` |
99
+ | Payee and quote APIs | `registerPayeeDetails`, `resolvePayeeHash`, `getQuote`, `getQuotesBestByPlatform` |
100
100
  | Seller automated release | `uploadSellerCredential`, `getSellerCredentialStatus`, `verifySellerPayment` |
101
101
  | Delegation and hooks | `setDelegate`, `removeDelegate`, `setRateManager`, `clearRateManager`, `setDepositRateManager`, `clearDepositRateManager`, `setDepositPreIntentHook`, `setDepositWhitelistHook` |
102
102
  | Vault / DRM | `createRateManager`, `setVaultMinRate`, `setVaultMinRatesBatch`, `setVaultFee`, `setVaultConfig`, `getDepositRateManager`, `getManagerFee`, `getEffectiveRate` |
103
+ | Taker staking | `ensureStakeAllowance`, `depositStake`, `depositStakeFor`, partial withdrawal and full-exit methods, stake-owner permission methods, direct StakeVault reads, and exact risk-capacity math |
104
+ | V3 settlement operations | `releaseMaturedPositions`, `reconcileSettlement(s)`, `reconcileCancellation(s)`, OrchestratorV3 intent prepares, and exported settlement event ABIs |
103
105
  | Oracle config | `setOracleRateConfig`, `setOracleRateConfigBatch`, `removeOracleRateConfig`, `updateCurrencyConfigBatch`, `deactivateCurrenciesBatch`, `supportsInlineOracleRateConfig`, `validateOracleFeedsOnChain` |
104
106
  | RPC reads | `getDeposits`, `getDeposit`, `getDepositsById`, `getIntents`, `getIntent`, `getPvDepositById`, `getPvDepositsFromIds`, `getPvAccountDeposits`, `getPvAccountIntents`, `getPvIntent` |
105
107
  | Indexer | `client.indexer.getDeposits`, `getDepositsWithRelations`, `getDepositById`, `getDepositsByIds`, `getDepositsByIdsWithRelations`, `getDepositsByPayeeHash`, `getIntentsForDeposits`, `getOwnerIntents`, `getIntentsByRateManager`, `getIntentByHash`, `getExpiredIntents`, `getFulfilledIntentEvents`, `getIntentFulfillmentAmounts`, `getFulfillmentAndPayment`, `getDepositFundActivities`, `getMakerFundActivities`, `getDepositDailySnapshots`, `getProfitSnapshotsByDeposits`, `getRateManagers`, `getRateManagerDetail`, `getRateManagerDelegations`, `getDelegationForDeposit`, `getManagerDailySnapshots`, `getManualRateUpdates`, `getOracleConfigUpdates`, `query` |
106
- | React hooks | `@zkp2p/sdk/react` exports hooks for deposits, intents, delegation, vaults, payment methods, and taker tier |
108
+ | React hooks | `@zkp2p/sdk/react` exports hooks for deposits, intents, delegation, vaults, and payment methods |
107
109
  | Attribution | ERC-8021 helpers like `sendTransactionWithAttribution`, `encodeWithAttribution`, and `txOverrides.referrer` support |
108
110
 
109
111
  ## Extension Metadata Bridge
@@ -180,7 +182,7 @@ console.log(result.depositDetails);
180
182
 
181
183
  If you do not pass `payeeDetailsHashes`, `createDeposit()` can register the payee details for you. If you want to pre-register or reuse hashes across deposits, use `registerPayeeDetails()` first.
182
184
 
183
- ## Payee Registration, Quotes, and Taker Tier
185
+ ## Payee Registration and Quotes
184
186
 
185
187
  ```ts
186
188
  import { resolvePaymentMethodHash } from '@zkp2p/sdk';
@@ -212,18 +214,81 @@ const quote = await client.getQuote({
212
214
  isExactFiat: true,
213
215
  });
214
216
 
215
- const takerTier = await client.getTakerTier({
216
- owner: '0xBuyer',
217
- chainId: 8453,
218
- });
219
-
220
217
  const payeeHash = await client.resolvePayeeHash(
221
218
  42n,
222
219
  resolvePaymentMethodHash('wise', { env: 'production' }),
223
220
  );
224
221
  ```
225
222
 
226
- `getQuote()` returns available liquidity plus payee details when authenticated. Use `getQuotesBestByPlatform()` to fetch the best quote per supported payment platform in a single call (handy for cross-platform comparison UIs). `getTakerTier()` returns limits, cooldown data, Peer Pay volume progression, and `volumeBreakdown` rows for taker UX.
223
+ `getQuote()` returns available liquidity plus payee details when authenticated. Use `getQuotesBestByPlatform()` to fetch the best quote per supported payment platform in a single call (handy for cross-platform comparison UIs). Quotes do not reserve or filter by OrchestratorV3 stake capacity; fetch the configured Curator environment's capacity view separately and treat on-chain admission as final.
224
+
225
+ ## Taker Stake & Capacity
226
+
227
+ ```ts
228
+ const amount = 250_000000n;
229
+ const approval = await client.ensureStakeAllowance({ amount });
230
+ // Wait for approval.hash to confirm when hadAllowance is false.
231
+
232
+ await client.depositStake({ amount });
233
+ // Or prepare for a smart account/relayer:
234
+ const prepared = await client.depositStakeFor.prepare({
235
+ taker: delegatedTakerAddress,
236
+ amount,
237
+ });
238
+
239
+ const stake = await client.getTakerStake({ owner: takerAddress, chainId: 8453 });
240
+ // stake.responseObject.balances: stakedUsdc, pendingWithdrawalUsdc, eligibleUsdc,
241
+ // reservedUsdc (locked), freeUsdc — each { raw, formatted } in 6-decimal base units.
242
+
243
+ const capacity = await client.getTakerCapacity({ owner: takerAddress, chainId: 8453 });
244
+ // capacity.responseObject.platforms[]: bondedTakingCapacity, freeTakes, griefing and
245
+ // chargeback curve terms, warnings; operationalGates carries pause state.
246
+
247
+ const onchain = await client.getStakeVaultState({
248
+ staker: stakeOwnerAddress,
249
+ taker: takerAddress,
250
+ });
251
+ ```
252
+
253
+ Stake writes expose direct and `.prepare()` variants: `depositStake`,
254
+ `depositStakeFor`, `requestStakeWithdrawal`, `cancelStakeWithdrawal`,
255
+ `withdrawRequestedStake`, `requestExit`, `cancelExit`, `withdrawStake`,
256
+ `setStakeDelegationEnabled`, `setAllowedStakeOwner`, and `clearStakeOwner`.
257
+ StakeVault is currently staging-only; SDK contract resolution fails closed in
258
+ production/preproduction until a deployment exists.
259
+
260
+ The same staging deployment exposes OrchestratorV3 and RiskManager through
261
+ `getDeployedAddresses()`, `getOrchestratorV3Contract()`, and
262
+ `getRiskManagerContract()`. Route an intent explicitly by passing
263
+ `orchestratorAddress: client.getDeployedAddresses().orchestratorV3`.
264
+ OrchestratorV3 signals are ungated: the SDK encodes an empty gating signature
265
+ and zero expiration without calling the curator signing endpoint. Existing
266
+ OrchestratorV2 signaling remains gated.
267
+
268
+ Keeper writes are direct and prepareable:
269
+
270
+ ```ts
271
+ const sweep = await client.releaseMaturedPositions.prepare({
272
+ intentHashes: [intentHash],
273
+ });
274
+ const settlement = await client.reconcileSettlement.prepare({ intentHash });
275
+ const cancellations = await client.reconcileCancellations.prepare({
276
+ intentHashes: [intentHash],
277
+ });
278
+ ```
279
+
280
+ For receipt classification, import `RISK_POSITION_SETTLED_EVENT_ABI` and
281
+ `DEFERRED_PAYOUT_RECORDED_EVENT_ABI` and pass them to viem's
282
+ `decodeEventLog()`.
283
+
284
+ `getTakerStake()` and `getTakerCapacity()` wrap curator
285
+ `GET /v2/taker/stake` and `GET /v2/taker/capacity`, the hard replacement for
286
+ the removed `/v2/taker/tier` endpoint. Platform capacities are alternative
287
+ views of one shared stake-owner portfolio — never sum them, and never filter
288
+ quotes by capacity. Free takes are separate whole intents (at most
289
+ `amountPerTakeUsdc` each) and cannot be combined with bonded capacity.
290
+ Capacity can change whenever any authorized relayer uses the shared stake, so
291
+ refetch immediately before signaling; the contract stays authoritative.
227
292
 
228
293
  For table-style liquidity UIs, the low-level `apiGetOrderbookTable()` adapter exposes curator's paginated public/private orderbook rows and row-level privacy metadata.
229
294
 
@@ -626,14 +691,12 @@ import {
626
691
  useSignalIntent,
627
692
  useCreateVault,
628
693
  useVaultDelegation,
629
- useGetTakerTier,
630
694
  } from '@zkp2p/sdk/react';
631
695
 
632
696
  const createDeposit = useCreateDeposit({ client });
633
697
  const signalIntent = useSignalIntent({ client });
634
698
  const createVault = useCreateVault({ client, sendTransaction });
635
699
  const vaultDelegation = useVaultDelegation({ client, sendTransaction, sendBatch });
636
- const takerTier = useGetTakerTier({ client, owner, chainId, autoFetch: true });
637
700
  ```
638
701
 
639
702
  Hook groups:
@@ -643,7 +706,7 @@ Hook groups:
643
706
  - Intent lifecycle: `useSignalIntent`, `useFulfillIntent`, `useReleaseFundsToPayer`, `usePruneExpiredIntents`
644
707
  - Delegation: `useSetDelegate`, `useRemoveDelegate`
645
708
  - Vault / DRM: `useCreateVault`, `useVaultDelegation`, `useSetVaultFee`, `useSetVaultMinRate`, `useSetVaultConfig`
646
- - Taker tier: `useGetTakerTier`, plus `getTierDisplayInfo()` and `getNextTierCap()` helpers
709
+ - Taker stake and capacity: `useGetTakerStake`, `useGetTakerCapacity`
647
710
 
648
711
  `useVaultDelegation()` is the batching-oriented hook. It returns `delegateDeposit`, `delegateDeposits`, `clearDelegation`, and `clearDelegations`.
649
712
 
@@ -651,15 +714,23 @@ Hook groups:
651
714
 
652
715
  ```ts
653
716
  import {
717
+ DEFERRED_PAYOUT_RECORDED_EVENT_ABI,
654
718
  getContracts,
719
+ getOrchestratorV3Contract,
655
720
  getPaymentMethodsCatalog,
656
721
  getRateManagerContracts,
722
+ getRiskManagerContract,
723
+ getStakeVaultContract,
724
+ RISK_POSITION_SETTLED_EVENT_ABI,
657
725
  resolveFiatCurrencyBytes32,
658
726
  resolvePaymentMethodHash,
659
727
  } from '@zkp2p/sdk';
660
728
 
661
729
  const contracts = getContracts(8453, 'production');
662
730
  const rateManagerContracts = getRateManagerContracts(8453, 'production');
731
+ const stakeVault = getStakeVaultContract(8453, 'staging');
732
+ const orchestratorV3 = getOrchestratorV3Contract(8453, 'staging');
733
+ const riskManager = getRiskManagerContract(8453, 'staging');
663
734
  const paymentMethods = getPaymentMethodsCatalog(8453, 'production');
664
735
  const usdHash = resolveFiatCurrencyBytes32('USD');
665
736
  const wiseHash = resolvePaymentMethodHash('wise', { env: 'production' });
@@ -1,39 +1,3 @@
1
- // src/takerTiers.ts
2
- var TAKER_TIER_SCHEDULE = [
3
- { tier: "PLATINUM", perIntentCapUsd: 5e3, minVolumeUsd: 1e5, maxVolumeUsd: Infinity },
4
- { tier: "PRO", perIntentCapUsd: 2500, minVolumeUsd: 5e4, maxVolumeUsd: 99999 },
5
- { tier: "PLUS", perIntentCapUsd: 1e3, minVolumeUsd: 1e4, maxVolumeUsd: 49999 },
6
- { tier: "PEER", perIntentCapUsd: 250, minVolumeUsd: 1e3, maxVolumeUsd: 9999 },
7
- { tier: "PEASANT", perIntentCapUsd: 100, minVolumeUsd: 0, maxVolumeUsd: 999 }
8
- ];
9
- var TAKER_TIER_ORDER = [
10
- "PEASANT",
11
- "PEER",
12
- "PLUS",
13
- "PRO",
14
- "PLATINUM"
15
- ];
16
- var TAKER_TIER_CAPS = Object.fromEntries(
17
- TAKER_TIER_SCHEDULE.map((entry) => [entry.tier, entry.perIntentCapUsd])
18
- );
19
- var TAKER_TIER_FEE_DISCOUNT_BPS = {
20
- PEASANT: 0,
21
- PEER: 5,
22
- PLUS: 10,
23
- PRO: 20,
24
- PLATINUM: 30
25
- };
26
- function getTakerTierFeeDiscountBps(tier) {
27
- if (!tier) return 0;
28
- return TAKER_TIER_FEE_DISCOUNT_BPS[tier] ?? 0;
29
- }
30
- function getNextTakerTier(currentTier) {
31
- if (!currentTier) return null;
32
- const index = TAKER_TIER_ORDER.indexOf(currentTier);
33
- if (index === -1 || index >= TAKER_TIER_ORDER.length - 1) return null;
34
- return TAKER_TIER_ORDER[index + 1];
35
- }
36
-
37
1
  // src/react/hooks/vaultUtils.ts
38
2
  var ZERO_ADDRESS = "0x0000000000000000000000000000000000000000";
39
3
  var ZERO_RATE_MANAGER_ID = "0x0000000000000000000000000000000000000000000000000000000000000000";
@@ -106,6 +70,6 @@ var prepareSetDelegationTx = async (client, params, referrer) => {
106
70
  });
107
71
  };
108
72
 
109
- export { TAKER_TIER_CAPS, TAKER_TIER_FEE_DISCOUNT_BPS, TAKER_TIER_ORDER, TAKER_TIER_SCHEDULE, ZERO_ADDRESS, ZERO_RATE_MANAGER_ID, asErrorMessage, assertAtomicDelegationSwitchSupport, assertDelegationMethodSupport, classifyDelegationState, getDelegationRoute, getNextTakerTier, getTakerTierFeeDiscountBps, isZeroRateManagerId, normalizeRateManagerId, normalizeRegistry, prepareClearDelegationTx, prepareSetDelegationTx };
110
- //# sourceMappingURL=chunk-EIOXBVXP.mjs.map
111
- //# sourceMappingURL=chunk-EIOXBVXP.mjs.map
73
+ export { ZERO_ADDRESS, ZERO_RATE_MANAGER_ID, asErrorMessage, assertAtomicDelegationSwitchSupport, assertDelegationMethodSupport, classifyDelegationState, getDelegationRoute, isZeroRateManagerId, normalizeRateManagerId, normalizeRegistry, prepareClearDelegationTx, prepareSetDelegationTx };
74
+ //# sourceMappingURL=chunk-LPJE2MN7.mjs.map
75
+ //# sourceMappingURL=chunk-LPJE2MN7.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react/hooks/vaultUtils.ts"],"names":[],"mappings":"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","file":"chunk-LPJE2MN7.mjs","sourcesContent":["import type { Address, Hex } from 'viem';\nimport type { PreparedTransaction } from '../../types/prepared';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\nexport const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' as const;\nexport const ZERO_RATE_MANAGER_ID =\n '0x0000000000000000000000000000000000000000000000000000000000000000' as const;\n\n// ---------------------------------------------------------------------------\n// Normalizers\n// ---------------------------------------------------------------------------\n\nexport const normalizeRateManagerId = (value?: string | null): string =>\n (value ?? ZERO_RATE_MANAGER_ID).toLowerCase();\n\nexport const normalizeRegistry = (value?: string | null): string =>\n (value ?? ZERO_ADDRESS).toLowerCase();\n\nexport const isZeroRateManagerId = (value?: string | null): boolean => {\n if (!value) return true;\n const normalized = value.toLowerCase();\n return normalized === ZERO_ADDRESS || normalized === ZERO_RATE_MANAGER_ID;\n};\n\n// ---------------------------------------------------------------------------\n// Error helper\n// ---------------------------------------------------------------------------\n\nexport const asErrorMessage = (error: unknown): string => {\n if (!error) return 'Unknown error';\n if (error instanceof Error) return error.message;\n if (typeof error === 'string') return error;\n\n const anyErr = error as Record<string, unknown>;\n if (typeof anyErr.shortMessage === 'string') return anyErr.shortMessage;\n if (typeof anyErr.details === 'string') return anyErr.details;\n if (typeof anyErr.message === 'string') return anyErr.message;\n\n try {\n return JSON.stringify(error);\n } catch {\n return String(error);\n }\n};\n\n// ---------------------------------------------------------------------------\n// Delegation state classification\n// ---------------------------------------------------------------------------\n\nexport type DelegationState = 'delegated_here' | 'delegated_elsewhere' | 'not_delegated';\n\n/**\n * Classify whether a deposit is delegated to the target vault, delegated\n * elsewhere, or not delegated at all.\n *\n * @param currentRateManagerId The deposit's current rateManagerId (from indexer)\n * @param currentRegistry The deposit's current rateManagerAddress / registry (from indexer)\n * @param targetRateManagerId The vault's rateManagerId we're comparing against\n * @param targetRegistry The vault's registry address we're comparing against\n */\nexport const classifyDelegationState = (\n currentRateManagerId: string | null | undefined,\n currentRegistry: string | null | undefined,\n targetRateManagerId: string,\n targetRegistry: string | null | undefined,\n): DelegationState => {\n if (isZeroRateManagerId(currentRateManagerId)) {\n return 'not_delegated';\n }\n\n const normalizedCurrent = normalizeRateManagerId(currentRateManagerId);\n const normalizedTarget = targetRateManagerId.toLowerCase();\n const normalizedCurrentRegistry = normalizeRegistry(currentRegistry);\n const normalizedTargetRegistry = targetRegistry ? targetRegistry.toLowerCase() : '';\n\n const idsMatch = normalizedCurrent === normalizedTarget;\n const registryMatches = normalizedTargetRegistry\n ? normalizedCurrentRegistry === normalizedTargetRegistry\n : true;\n\n if (idsMatch && registryMatches) {\n return 'delegated_here';\n }\n\n return 'delegated_elsewhere';\n};\n\nexport const assertAtomicDelegationSwitchSupport = (\n requiresSwitch: boolean,\n canBatch: boolean,\n): void => {\n if (!requiresSwitch || canBatch) return;\n throw new Error(\n 'Switching delegation targets requires smart-account batching. Clear the existing delegation first.',\n );\n};\n\n// ---------------------------------------------------------------------------\n// Contract routing\n// ---------------------------------------------------------------------------\n\nexport type DelegationRoute = 'v2';\n\nexport const getDelegationRoute = (_client: unknown, _escrow: Address): DelegationRoute => 'v2';\n\ntype DelegationMethodSupportClient = Partial<\n Record<'setRateManager' | 'clearRateManager', { prepare?: unknown }>\n>;\n\nexport const assertDelegationMethodSupport = (\n client: DelegationMethodSupportClient,\n _route: DelegationRoute,\n action: 'set' | 'clear',\n): void => {\n const method = action === 'set' ? 'setRateManager' : 'clearRateManager';\n if (client?.[method]?.prepare) return;\n throw new Error(`Delegation requires SDK ${method} support`);\n};\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport interface DelegationDepositTarget {\n compositeDepositId: string;\n escrow: Address;\n depositId: bigint;\n currentRateManagerId?: string | null;\n currentRateManagerRegistry?: string | null;\n}\n\nexport interface BatchResult {\n hashes: string[];\n failed: Array<{ compositeDepositId: string; error: string }>;\n}\n\n/**\n * Callback the consumer provides for sending a single prepared transaction.\n * Returns the transaction hash.\n */\nexport type SendTransactionFn = (tx: { to: Address; data: Hex; value?: bigint }) => Promise<string>;\n\n/**\n * Optional callback for batching multiple calls in a single user operation\n * (e.g. via a smart account). If not provided, transactions are sent\n * sequentially via sendTransaction.\n */\nexport type SendBatchFn = (\n txs: Array<{ to: Address; data: Hex; value?: bigint }>,\n) => Promise<string>;\n\n// ---------------------------------------------------------------------------\n// Prepare helpers (used internally by hooks)\n// ---------------------------------------------------------------------------\n\nexport const prepareClearDelegationTx = async (\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n client: any,\n params: { escrow: Address; depositId: bigint },\n referrer?: string | string[],\n): Promise<PreparedTransaction> => {\n const route = getDelegationRoute(client, params.escrow);\n assertDelegationMethodSupport(client, route, 'clear');\n return client.clearRateManager.prepare({\n escrowAddress: params.escrow,\n depositId: params.depositId,\n ...(referrer ? { txOverrides: { referrer } } : {}),\n });\n};\n\nexport const prepareSetDelegationTx = async (\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n client: any,\n params: {\n escrow: Address;\n depositId: bigint;\n registry: Address;\n rateManagerId: Hex;\n },\n referrer?: string | string[],\n): Promise<PreparedTransaction> => {\n const route = getDelegationRoute(client, params.escrow);\n assertDelegationMethodSupport(client, route, 'set');\n return client.setRateManager.prepare({\n escrowAddress: params.escrow,\n depositId: params.depositId,\n rateManagerAddress: params.registry,\n rateManagerId: params.rateManagerId,\n ...(referrer ? { txOverrides: { referrer } } : {}),\n });\n};\n"]}