@totalis/hip4-client 0.76.0 → 0.78.0

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/CHANGELOG.md CHANGED
@@ -3,6 +3,60 @@
3
3
  `@totalis/hip4-client` follows [semantic versioning](https://semver.org).
4
4
  The contract version the generated types come from is `sdkVersion` in `./version`.
5
5
 
6
+ ## 0.78.0
7
+
8
+ ### Added
9
+
10
+ - `listComboFeed` (`GET /v1/feed/combos`): every Combo opened by a trader
11
+ whose profile is public, newest first, each with the trader's `username`,
12
+ `avatar_seed` and `picture_url`, paged with `cursor` and `limit` (TT-2615).
13
+ Contract `206.0.0-public-trader-reads`.
14
+ - `listTraderPositions` answers `singles` on its first page: the HIP-4 outcome
15
+ sides the trader holds on HyperCore, each with `shares`, `average_price` and
16
+ `opened_at`, or null while they cannot be known.
17
+
18
+ ### Changed
19
+
20
+ - `getTrader`, `listTraderTrades`, `listTraderPositions` and `getTraderPnl`
21
+ answer signed out too.
22
+
23
+ ## 0.77.0
24
+
25
+ `openPosition` chooses its quote last and moves on to the next quote when an
26
+ accept is refused, declined or times out (TT-2578, TT-2574). Makers replace
27
+ quotes as they renew them, and an accept that took seconds to sign often found
28
+ its quote gone (`QUOTE_REPLACED`). Additive: 0.76 code runs unchanged, but
29
+ `openPosition` now sends up to `maxAttempts` accepts where it sent one or two.
30
+
31
+ ### Added
32
+
33
+ - `prepareEntryFunding({ stake, core, cash, sourceDex, hypercoreNonce })`
34
+ plans and signs an entry's funding (the wallet's USDC authorization and any
35
+ bridge from Core) before a quote is chosen. `signEntry({ quote, prepared })`
36
+ then signs only the quote, so the accept can go straight out. A prepared
37
+ funding is for one accept.
38
+ - `SignedEntry.refused`: the code of an accept the venue refused outright.
39
+ `recoverAcceptance` answers such a record `NOT_ADMITTED` from the record
40
+ alone, without reading the acceptance or cancelling the RFQ.
41
+ - `openPosition` options `minPayout`, `refreshMs` (default 10 s) and
42
+ `maxAttempts` (default 4).
43
+
44
+ ### Changed
45
+
46
+ - `openPosition` signs the funding first and chooses the quote after, then
47
+ signs it and sends the accept at once. Signing the funding counts against
48
+ `collectMs`.
49
+ - On `QUOTE_REPLACED` or `QUOTE_EXPIRED`, `openPosition` hands `onSigned` the
50
+ record marked `refused`, then prepares the funding again and signs the best
51
+ other quote paying at least `minPayout` (left out: the first chosen quote's
52
+ payout). It keeps going until an accept is taken, `refreshMs` passes or
53
+ `maxAttempts` accepts were sent, and throws the last refusal. Before, it
54
+ retried once and took any payout.
55
+ - A `DECLINED` or `TIMED_OUT` accept is followed the same way, within the
56
+ same `refreshMs` and `maxAttempts`: the maker's answer admitted nothing and
57
+ the RFQ stays open. With no other quote at or above `minPayout` in time,
58
+ `openPosition` returns that answer, as before.
59
+
6
60
  ## 0.76.0
7
61
 
8
62
  Vault agents (TT-2554), one Combo leg per question (TT-2605) and no legs on
package/README.md CHANGED
@@ -66,8 +66,9 @@ if (opened.operationId) {
66
66
  totalis.close();
67
67
  ```
68
68
 
69
- `openPosition` creates the RFQ, takes the best quote, signs the acceptance and funds any shortfall from the
70
- wallet. Only `COMMITTED` means the trade happened. A decided position settles and pays out on its own.
69
+ `openPosition` creates the RFQ, signs the funding for any shortfall from the wallet, then takes the best quote
70
+ and signs the acceptance. If the maker replaced the quote first, declined it or didn't answer, it signs the best
71
+ other quote paying at least the first one's payout (`minPayout`) and tries again. Only `COMMITTED` means the trade happened. A decided position settles and pays out on its own.
71
72
 
72
73
  ## Make markets
73
74
 
package/dist/client.d.ts CHANGED
@@ -5,7 +5,7 @@ export type { components, operations, paths } from "./generated/client.js";
5
5
  export type { AccessToken, ApiClientOptions, PublicEnvironment } from "./base.js";
6
6
  /** The typed HTTP client for the `client` audience. `createTotalis` adds the flows. */
7
7
  export declare function createClient(environment: PublicEnvironment, options?: ApiClientOptions): import("openapi-fetch").Client<paths, `${string}/${string}`>;
8
- export { bestBid, bestQuote, createTotalis, EntryError, entryFailure, follow, type Acceptance, type AcceptanceResult, type Amount, type BridgeState, type CashoutBid, type Chooser, type Deployment, type DepositAccepted, type EntryCash, type EntryFailure, type EntryQuote, type CoreExit, type LegacySignedEntry, type Leg, type NotAdmitted, type RecordListener, type Operation, type Quote, type ResponseOf, type SellBackQuote, type SignedAcceptance, type SignedCashout, type SignedCoreExit, type SignedDeposit, type SignedEntry, type SignedReturnToCore, type Totalis, type TotalisOptions, type TransferQuote, type WalletCall, type Withdrawal, } from "./totalis.js";
8
+ export { bestBid, bestQuote, createTotalis, EntryError, entryFailure, follow, type Acceptance, type AcceptanceResult, type Amount, type BridgeState, type CashoutBid, type Chooser, type Deployment, type DepositAccepted, type EntryCash, type EntryFailure, type EntryQuote, type CoreExit, type LegacySignedEntry, type Leg, type PreparedEntryFunding, type NotAdmitted, type RecordListener, type Operation, type Quote, type ResponseOf, type SellBackQuote, type SignedAcceptance, type SignedCashout, type SignedCoreExit, type SignedDeposit, type SignedEntry, type SignedReturnToCore, type Totalis, type TotalisOptions, type TransferQuote, type WalletCall, type Withdrawal, } from "./totalis.js";
9
9
  export { bridgeAction, bridgeGasBuffer, classifyHypercoreAnswer, hyperevmUsdcBridge, hyperliquidExchange, hyperliquidNetwork, hypercoreNonce, hypercoreSignature, sendAssetTypedData, settleHypercore, signSendAsset, submitHypercore, usdcText, type BridgedCash, type HypercoreAnswer, type HypercoreSignature, type HypercoreTransport, type HyperliquidNetwork, type SendAssetAction, type SignedSendAsset, } from "./hypercore.js";
10
10
  export { ApiError, envelope, NetworkError, randomBytes32, retryingFetch, sendOnce, unwrap, uuidv7, type Fetch, type Repaired, type RetryPolicy } from "./request.js";
11
11
  export { items, pages, type Page, type PageQuery } from "./pagination.js";
@@ -39,6 +39,7 @@ export type paths = {
39
39
  readonly "/v1/traders/{username}/trades": Pick<SharedPaths["/v1/traders/{username}/trades"], "parameters" | "get">;
40
40
  readonly "/v1/traders/{username}/positions": Pick<SharedPaths["/v1/traders/{username}/positions"], "parameters" | "get">;
41
41
  readonly "/v1/traders/{username}/pnl": Pick<SharedPaths["/v1/traders/{username}/pnl"], "parameters" | "get">;
42
+ readonly "/v1/feed/combos": Pick<SharedPaths["/v1/feed/combos"], "parameters" | "get">;
42
43
  readonly "/v1/me/follows": Pick<SharedPaths["/v1/me/follows"], "parameters" | "get">;
43
44
  readonly "/v1/me/follows/{username}": Pick<SharedPaths["/v1/me/follows/{username}"], "parameters" | "put" | "delete">;
44
45
  readonly "/v1/me/copy-settings": Pick<SharedPaths["/v1/me/copy-settings"], "parameters" | "get" | "put">;
@@ -103,4 +104,4 @@ export type paths = {
103
104
  readonly "/v1/makers/{maker_id}/exposure": Pick<SharedPaths["/v1/makers/{maker_id}/exposure"], "parameters" | "get">;
104
105
  readonly "/v1/makers/{maker_id}/performance": Pick<SharedPaths["/v1/makers/{maker_id}/performance"], "parameters" | "get">;
105
106
  };
106
- export type operations = Pick<SharedOperations, "abandonCoreExit" | "acceptQuote" | "activeCoreExits" | "admitOwnMaker" | "admitPartnerWallet" | "bindEmbeddedWallet" | "cancelRfq" | "confirmComboCopyAgent" | "confirmCoreCopyAgent" | "createCashout" | "createComment" | "createCoreExit" | "createDeposit" | "createRfq" | "createRunOffClaim" | "createWalletChallenge" | "createWalletSession" | "createWithdrawal" | "deleteComment" | "editComment" | "endWalletSession" | "followTrader" | "getAcceptance" | "getAccountActivity" | "getAccountPnl" | "getAccountPosition" | "getBalances" | "getCopyAgents" | "getCopyBudget" | "getCopySettings" | "getCopySummary" | "getCoreExit" | "getDeployment" | "getHypercoreBalance" | "getHypercorePortfolio" | "getHypercoreSettlementCredits" | "getIdentity" | "getMakerActivity" | "getMakerCapital" | "getMakerExposure" | "getMakerPerformance" | "getMakerPosition" | "getMakerQuotes" | "getMakerTelemetry" | "getMakerVaultFundingTransaction" | "getMakerWithdrawal" | "getMarket" | "getMarketDataConfig" | "getMarketDefinitions" | "getMarketHistory" | "getMarketRecurringRound" | "getMarketReferenceCloses" | "getMarketScoreTimeline" | "getMarketStats" | "getOperation" | "getOwnMaker" | "getPartnerReport" | "getPerpMarks" | "getProfitLeaderboard" | "getQuestion" | "getQuestionRecurringRound" | "getQuestionScoreTimeline" | "getRfq" | "getSeries" | "getSuggestedCombo" | "getTaxonomy" | "getTrader" | "getTraderPnl" | "getUsernameAvailability" | "getVaultAgents" | "getVolumeLeaderboard" | "getWithdrawal" | "grantComboCopyAgent" | "grantVaultAgent" | "likeComment" | "listAccountActivity" | "listAccountPositions" | "listComboCopies" | "listCommentReplies" | "listComments" | "listControlledMakers" | "listCopiedFills" | "listCopyTrades" | "listDiscovery" | "listFollows" | "listMakerActivity" | "listMakerPositions" | "listMakerQuoteHistory" | "listMarkets" | "listOperations" | "listQuotes" | "listTraderPositions" | "listTraderTrades" | "reactComment" | "recordAcquisition" | "refreshPicture" | "requestComboCopyAgent" | "requestCoreCopyAgent" | "revokeComboCopyAgent" | "searchCatalog" | "setAvatar" | "setCopyAllocation" | "setCopySettings" | "setPublicProfile" | "setUsername" | "stream" | "streamMarkets" | "switchOffComboCopyAgent" | "switchOffCoreCopyAgent" | "unfollowTrader" | "unlikeComment" | "unreactComment">;
107
+ export type operations = Pick<SharedOperations, "abandonCoreExit" | "acceptQuote" | "activeCoreExits" | "admitOwnMaker" | "admitPartnerWallet" | "bindEmbeddedWallet" | "cancelRfq" | "confirmComboCopyAgent" | "confirmCoreCopyAgent" | "createCashout" | "createComment" | "createCoreExit" | "createDeposit" | "createRfq" | "createRunOffClaim" | "createWalletChallenge" | "createWalletSession" | "createWithdrawal" | "deleteComment" | "editComment" | "endWalletSession" | "followTrader" | "getAcceptance" | "getAccountActivity" | "getAccountPnl" | "getAccountPosition" | "getBalances" | "getCopyAgents" | "getCopyBudget" | "getCopySettings" | "getCopySummary" | "getCoreExit" | "getDeployment" | "getHypercoreBalance" | "getHypercorePortfolio" | "getHypercoreSettlementCredits" | "getIdentity" | "getMakerActivity" | "getMakerCapital" | "getMakerExposure" | "getMakerPerformance" | "getMakerPosition" | "getMakerQuotes" | "getMakerTelemetry" | "getMakerVaultFundingTransaction" | "getMakerWithdrawal" | "getMarket" | "getMarketDataConfig" | "getMarketDefinitions" | "getMarketHistory" | "getMarketRecurringRound" | "getMarketReferenceCloses" | "getMarketScoreTimeline" | "getMarketStats" | "getOperation" | "getOwnMaker" | "getPartnerReport" | "getPerpMarks" | "getProfitLeaderboard" | "getQuestion" | "getQuestionRecurringRound" | "getQuestionScoreTimeline" | "getRfq" | "getSeries" | "getSuggestedCombo" | "getTaxonomy" | "getTrader" | "getTraderPnl" | "getUsernameAvailability" | "getVaultAgents" | "getVolumeLeaderboard" | "getWithdrawal" | "grantComboCopyAgent" | "grantVaultAgent" | "likeComment" | "listAccountActivity" | "listAccountPositions" | "listComboCopies" | "listComboFeed" | "listCommentReplies" | "listComments" | "listControlledMakers" | "listCopiedFills" | "listCopyTrades" | "listDiscovery" | "listFollows" | "listMakerActivity" | "listMakerPositions" | "listMakerQuoteHistory" | "listMarkets" | "listOperations" | "listQuotes" | "listTraderPositions" | "listTraderTrades" | "reactComment" | "recordAcquisition" | "refreshPicture" | "requestComboCopyAgent" | "requestCoreCopyAgent" | "revokeComboCopyAgent" | "searchCatalog" | "setAvatar" | "setCopyAllocation" | "setCopySettings" | "setPublicProfile" | "setUsername" | "stream" | "streamMarkets" | "switchOffComboCopyAgent" | "switchOffCoreCopyAgent" | "unfollowTrader" | "unlikeComment" | "unreactComment">;
@@ -407,8 +407,8 @@ export declare const operationErrors: {
407
407
  readonly getSeries: readonly ["INVALID_REQUEST", "NOT_FOUND", "MARKET_DATA_UNAVAILABLE", "INVALID_QUERY"];
408
408
  readonly getSuggestedCombo: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "DEPENDENCY_STALE", "RATE_LIMITED", "INVALID_QUERY"];
409
409
  readonly getTaxonomy: readonly ["INVALID_QUERY", "MARKET_DATA_UNAVAILABLE", "UNSUPPORTED_BROWSE_CONTRACT", "BROWSE_UNAVAILABLE"];
410
- readonly getTrader: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "RATE_LIMITED", "INVALID_QUERY", "ORIGIN_REQUIRED", "NOT_FOUND", "SOURCE_UNAVAILABLE", "CONTRACT_UNAVAILABLE"];
411
- readonly getTraderPnl: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "RATE_LIMITED", "INVALID_QUERY", "ORIGIN_REQUIRED", "NOT_FOUND", "SOURCE_UNAVAILABLE", "CONTRACT_UNAVAILABLE"];
410
+ readonly getTrader: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "RATE_LIMITED", "INVALID_QUERY", "NOT_FOUND", "SOURCE_UNAVAILABLE"];
411
+ readonly getTraderPnl: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "RATE_LIMITED", "INVALID_QUERY", "NOT_FOUND", "SOURCE_UNAVAILABLE", "CONTRACT_UNAVAILABLE"];
412
412
  readonly getUsernameAvailability: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "DEPENDENCY_STALE", "RATE_LIMITED", "INVALID_QUERY", "IDENTITY_NOT_FOUND", "ORIGIN_REQUIRED"];
413
413
  readonly getVaultAgents: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "RATE_LIMITED", "ORIGIN_REQUIRED", "IDENTITY_NOT_FOUND", "INVALID_QUERY", "DEPENDENCY_STALE"];
414
414
  readonly getVolumeLeaderboard: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "DEPENDENCY_STALE", "RATE_LIMITED", "INVALID_QUERY", "CONTRACT_UNAVAILABLE"];
@@ -419,13 +419,14 @@ export declare const operationErrors: {
419
419
  readonly listAccountActivity: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "DEPENDENCY_STALE", "RATE_LIMITED", "INVALID_QUERY", "ORIGIN_REQUIRED", "NOT_FOUND", "SOURCE_UNAVAILABLE", "CONTRACT_UNAVAILABLE", "CURSOR_EXPIRED"];
420
420
  readonly listAccountPositions: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "DEPENDENCY_STALE", "RATE_LIMITED", "INVALID_QUERY", "ORIGIN_REQUIRED", "NOT_FOUND", "SOURCE_UNAVAILABLE", "CONTRACT_UNAVAILABLE", "CURSOR_EXPIRED"];
421
421
  readonly listComboCopies: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "RATE_LIMITED", "ORIGIN_REQUIRED", "IDENTITY_NOT_FOUND", "INVALID_QUERY", "CONTRACT_UNAVAILABLE"];
422
+ readonly listComboFeed: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "RATE_LIMITED", "INVALID_QUERY", "SOURCE_UNAVAILABLE"];
422
423
  readonly listCommentReplies: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "DEPENDENCY_STALE", "RATE_LIMITED", "INVALID_QUERY", "INVALID_CURSOR", "NOT_FOUND"];
423
424
  readonly listComments: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "DEPENDENCY_STALE", "RATE_LIMITED", "INVALID_QUERY", "INVALID_CURSOR", "SOURCE_UNAVAILABLE", "MARKET_DATA_UNAVAILABLE"];
424
425
  readonly listControlledMakers: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "DEPENDENCY_STALE", "RATE_LIMITED", "ORIGIN_REQUIRED", "INVALID_QUERY", "INVALID_CURSOR"];
425
426
  readonly listCopiedFills: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "RATE_LIMITED", "ORIGIN_REQUIRED", "IDENTITY_NOT_FOUND", "INVALID_QUERY", "CONTRACT_UNAVAILABLE"];
426
427
  readonly listCopyTrades: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "RATE_LIMITED", "ORIGIN_REQUIRED", "IDENTITY_NOT_FOUND", "INVALID_QUERY", "CONTRACT_UNAVAILABLE"];
427
428
  readonly listDiscovery: readonly ["INVALID_QUERY", "INVALID_CURSOR", "STALE_CURSOR", "MARKET_DATA_UNAVAILABLE", "UNSUPPORTED_BROWSE_CONTRACT", "BROWSE_UNAVAILABLE"];
428
- readonly listFollows: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "RATE_LIMITED", "ORIGIN_REQUIRED", "IDENTITY_NOT_FOUND", "INVALID_QUERY", "CONTRACT_UNAVAILABLE"];
429
+ readonly listFollows: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "RATE_LIMITED", "ORIGIN_REQUIRED", "IDENTITY_NOT_FOUND", "INVALID_QUERY"];
429
430
  readonly listMakerAcceptances: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "DEPENDENCY_STALE", "RATE_LIMITED", "INVALID_QUERY", "INVALID_CURSOR", "NOT_FOUND"];
430
431
  readonly listMakerActivity: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "DEPENDENCY_STALE", "RATE_LIMITED", "INVALID_QUERY", "ORIGIN_REQUIRED", "NOT_FOUND", "SOURCE_UNAVAILABLE", "CONTRACT_UNAVAILABLE", "CURSOR_EXPIRED"];
431
432
  readonly listMakerApiKeys: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "DEPENDENCY_STALE", "RATE_LIMITED", "INVALID_REQUEST", "NOT_FOUND"];
@@ -437,8 +438,8 @@ export declare const operationErrors: {
437
438
  readonly listPersonalApiKeys: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "DEPENDENCY_STALE", "RATE_LIMITED", "INVALID_REQUEST", "NOT_FOUND"];
438
439
  readonly listQuotes: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "DEPENDENCY_STALE", "RATE_LIMITED", "INVALID_REQUEST", "NOT_FOUND"];
439
440
  readonly listRfqs: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "DEPENDENCY_STALE", "RATE_LIMITED", "INVALID_QUERY", "INVALID_CURSOR"];
440
- readonly listTraderPositions: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "RATE_LIMITED", "INVALID_QUERY", "ORIGIN_REQUIRED", "NOT_FOUND", "SOURCE_UNAVAILABLE", "CONTRACT_UNAVAILABLE"];
441
- readonly listTraderTrades: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "RATE_LIMITED", "INVALID_QUERY", "ORIGIN_REQUIRED", "NOT_FOUND", "SOURCE_UNAVAILABLE", "CONTRACT_UNAVAILABLE"];
441
+ readonly listTraderPositions: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "RATE_LIMITED", "INVALID_QUERY", "NOT_FOUND", "SOURCE_UNAVAILABLE"];
442
+ readonly listTraderTrades: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "RATE_LIMITED", "INVALID_QUERY", "NOT_FOUND", "SOURCE_UNAVAILABLE"];
442
443
  readonly live: readonly [];
443
444
  readonly publishMakerTelemetry: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "DEPENDENCY_STALE", "RATE_LIMITED", "INVALID_QUERY", "NOT_FOUND", "CONTRACT_UNAVAILABLE", "TELEMETRY_CONFLICT", "INVALID_REQUEST"];
444
445
  readonly reactComment: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "DEPENDENCY_STALE", "RATE_LIMITED", "ORIGIN_REQUIRED", "IDENTITY_NOT_FOUND", "NOT_FOUND", "INVALID_IDEMPOTENCY_KEY"];
@@ -460,14 +461,14 @@ export declare const operationErrors: {
460
461
  readonly setAvatar: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "DEPENDENCY_STALE", "RATE_LIMITED", "INVALID_REQUEST", "INVALID_IDEMPOTENCY_KEY", "IDEMPOTENCY_KEY_REUSED", "IDENTITY_NOT_FOUND", "ORIGIN_REQUIRED"];
461
462
  readonly setCopyAllocation: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "RATE_LIMITED", "ORIGIN_REQUIRED", "IDENTITY_NOT_FOUND", "DEPENDENCY_STALE", "INVALID_REQUEST", "INVALID_IDEMPOTENCY_KEY", "IDEMPOTENCY_KEY_REUSED", "CONTRACT_UNAVAILABLE"];
462
463
  readonly setCopySettings: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "RATE_LIMITED", "ORIGIN_REQUIRED", "IDENTITY_NOT_FOUND", "INVALID_REQUEST", "INVALID_IDEMPOTENCY_KEY", "IDEMPOTENCY_KEY_REUSED", "CONTRACT_UNAVAILABLE"];
463
- readonly setPublicProfile: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "RATE_LIMITED", "INVALID_REQUEST", "INVALID_IDEMPOTENCY_KEY", "IDEMPOTENCY_KEY_REUSED", "IDENTITY_NOT_FOUND", "USERNAME_REQUIRED", "ORIGIN_REQUIRED", "CONTRACT_UNAVAILABLE"];
464
+ readonly setPublicProfile: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "RATE_LIMITED", "INVALID_REQUEST", "INVALID_IDEMPOTENCY_KEY", "IDEMPOTENCY_KEY_REUSED", "IDENTITY_NOT_FOUND", "USERNAME_REQUIRED", "ORIGIN_REQUIRED"];
464
465
  readonly setUsername: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "DEPENDENCY_STALE", "RATE_LIMITED", "INVALID_REQUEST", "INVALID_IDEMPOTENCY_KEY", "IDEMPOTENCY_KEY_REUSED", "IDENTITY_NOT_FOUND", "USERNAME_NOT_ALLOWED", "USERNAME_TAKEN", "ORIGIN_REQUIRED"];
465
466
  readonly stream: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "DEPENDENCY_STALE", "RATE_LIMITED", "SUBPROTOCOL_REQUIRED", "CONNECTION_LIMIT", "CONNECTION_DRAIN", "INVALID_REQUEST", "NOT_FOUND", "ORIGIN_REQUIRED"];
466
467
  readonly streamMarkets: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "DEPENDENCY_STALE", "RATE_LIMITED", "SUBPROTOCOL_REQUIRED", "CONNECTION_LIMIT", "CONNECTION_DRAIN", "ORIGIN_NOT_ALLOWED", "INVALID_QUERY"];
467
468
  readonly submitMakerSelfFundedTransaction: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "DEPENDENCY_STALE", "RATE_LIMITED", "INVALID_REQUEST", "INVALID_IDEMPOTENCY_KEY", "IDEMPOTENCY_KEY_REUSED", "STATE_CONFLICT", "FINANCIAL_ACTIONS_DISABLED", "INVALID_SIGNATURE", "CONTRACT_SIGNATURE_OVERLOADED", "NOT_FOUND"];
468
469
  readonly switchOffComboCopyAgent: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "RATE_LIMITED", "ORIGIN_REQUIRED", "IDENTITY_NOT_FOUND", "CONTRACT_UNAVAILABLE", "INVALID_IDEMPOTENCY_KEY", "IDEMPOTENCY_KEY_REUSED"];
469
470
  readonly switchOffCoreCopyAgent: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "RATE_LIMITED", "ORIGIN_REQUIRED", "IDENTITY_NOT_FOUND", "CONTRACT_UNAVAILABLE", "INVALID_IDEMPOTENCY_KEY", "IDEMPOTENCY_KEY_REUSED"];
470
- readonly unfollowTrader: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "RATE_LIMITED", "ORIGIN_REQUIRED", "IDENTITY_NOT_FOUND", "INVALID_IDEMPOTENCY_KEY", "INVALID_REQUEST", "IDEMPOTENCY_KEY_REUSED", "CONTRACT_UNAVAILABLE"];
471
+ readonly unfollowTrader: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "RATE_LIMITED", "ORIGIN_REQUIRED", "IDENTITY_NOT_FOUND", "INVALID_IDEMPOTENCY_KEY", "INVALID_REQUEST", "IDEMPOTENCY_KEY_REUSED"];
471
472
  readonly unlikeComment: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "DEPENDENCY_STALE", "RATE_LIMITED", "ORIGIN_REQUIRED", "IDENTITY_NOT_FOUND", "NOT_FOUND", "INVALID_IDEMPOTENCY_KEY"];
472
473
  readonly unreactComment: readonly ["UNAUTHENTICATED", "FORBIDDEN", "AUTHORIZATION_UNAVAILABLE", "DEPENDENCY_STALE", "RATE_LIMITED", "ORIGIN_REQUIRED", "IDENTITY_NOT_FOUND", "NOT_FOUND", "INVALID_IDEMPOTENCY_KEY"];
473
474
  };
@@ -1171,10 +1171,8 @@ export const operationErrors = {
1171
1171
  "AUTHORIZATION_UNAVAILABLE",
1172
1172
  "RATE_LIMITED",
1173
1173
  "INVALID_QUERY",
1174
- "ORIGIN_REQUIRED",
1175
1174
  "NOT_FOUND",
1176
- "SOURCE_UNAVAILABLE",
1177
- "CONTRACT_UNAVAILABLE"
1175
+ "SOURCE_UNAVAILABLE"
1178
1176
  ],
1179
1177
  "getTraderPnl": [
1180
1178
  "UNAUTHENTICATED",
@@ -1182,7 +1180,6 @@ export const operationErrors = {
1182
1180
  "AUTHORIZATION_UNAVAILABLE",
1183
1181
  "RATE_LIMITED",
1184
1182
  "INVALID_QUERY",
1185
- "ORIGIN_REQUIRED",
1186
1183
  "NOT_FOUND",
1187
1184
  "SOURCE_UNAVAILABLE",
1188
1185
  "CONTRACT_UNAVAILABLE"
@@ -1300,6 +1297,14 @@ export const operationErrors = {
1300
1297
  "INVALID_QUERY",
1301
1298
  "CONTRACT_UNAVAILABLE"
1302
1299
  ],
1300
+ "listComboFeed": [
1301
+ "UNAUTHENTICATED",
1302
+ "FORBIDDEN",
1303
+ "AUTHORIZATION_UNAVAILABLE",
1304
+ "RATE_LIMITED",
1305
+ "INVALID_QUERY",
1306
+ "SOURCE_UNAVAILABLE"
1307
+ ],
1303
1308
  "listCommentReplies": [
1304
1309
  "UNAUTHENTICATED",
1305
1310
  "FORBIDDEN",
@@ -1366,8 +1371,7 @@ export const operationErrors = {
1366
1371
  "RATE_LIMITED",
1367
1372
  "ORIGIN_REQUIRED",
1368
1373
  "IDENTITY_NOT_FOUND",
1369
- "INVALID_QUERY",
1370
- "CONTRACT_UNAVAILABLE"
1374
+ "INVALID_QUERY"
1371
1375
  ],
1372
1376
  "listMakerAcceptances": [
1373
1377
  "UNAUTHENTICATED",
@@ -1483,10 +1487,8 @@ export const operationErrors = {
1483
1487
  "AUTHORIZATION_UNAVAILABLE",
1484
1488
  "RATE_LIMITED",
1485
1489
  "INVALID_QUERY",
1486
- "ORIGIN_REQUIRED",
1487
1490
  "NOT_FOUND",
1488
- "SOURCE_UNAVAILABLE",
1489
- "CONTRACT_UNAVAILABLE"
1491
+ "SOURCE_UNAVAILABLE"
1490
1492
  ],
1491
1493
  "listTraderTrades": [
1492
1494
  "UNAUTHENTICATED",
@@ -1494,10 +1496,8 @@ export const operationErrors = {
1494
1496
  "AUTHORIZATION_UNAVAILABLE",
1495
1497
  "RATE_LIMITED",
1496
1498
  "INVALID_QUERY",
1497
- "ORIGIN_REQUIRED",
1498
1499
  "NOT_FOUND",
1499
- "SOURCE_UNAVAILABLE",
1500
- "CONTRACT_UNAVAILABLE"
1500
+ "SOURCE_UNAVAILABLE"
1501
1501
  ],
1502
1502
  "live": [],
1503
1503
  "publishMakerTelemetry": [
@@ -1728,8 +1728,7 @@ export const operationErrors = {
1728
1728
  "IDEMPOTENCY_KEY_REUSED",
1729
1729
  "IDENTITY_NOT_FOUND",
1730
1730
  "USERNAME_REQUIRED",
1731
- "ORIGIN_REQUIRED",
1732
- "CONTRACT_UNAVAILABLE"
1731
+ "ORIGIN_REQUIRED"
1733
1732
  ],
1734
1733
  "setUsername": [
1735
1734
  "UNAUTHENTICATED",
@@ -1816,8 +1815,7 @@ export const operationErrors = {
1816
1815
  "IDENTITY_NOT_FOUND",
1817
1816
  "INVALID_IDEMPOTENCY_KEY",
1818
1817
  "INVALID_REQUEST",
1819
- "IDEMPOTENCY_KEY_REUSED",
1820
- "CONTRACT_UNAVAILABLE"
1818
+ "IDEMPOTENCY_KEY_REUSED"
1821
1819
  ],
1822
1820
  "unlikeComment": [
1823
1821
  "UNAUTHENTICATED",
@@ -725,7 +725,7 @@ export type paths = {
725
725
  readonly get?: never;
726
726
  /**
727
727
  * Turn the public trader profile on or off
728
- * @description Turns the authenticated account's public trader profile on (public: true) or off. It starts on, and an account without a username can turn it off before picking one; nothing names the profile until then. While it is on, any signed-in participant reads GET /v1/traders/{username}: the username, avatar, account wallet, volume, trades, open Combos and P&L, and the leaderboards mark the account public_profile. Its HyperCore fills and tickets are public on chain anyway; what turning it on discloses is that the wallet belongs to the username. Turning it on needs a username (409 USERNAME_REQUIRED); public_since keeps the first time it went on until it is turned off. Call it after POST /v1/me/embedded-wallet; before that it is 404 IDENTITY_NOT_FOUND. Asking for the current state again returns 204, so a retry is safe. Idempotency-Keys order these requests: the other state under the account's newest key, or under an older key, is 409 IDEMPOTENCY_KEY_REUSED, so a delayed retry never undoes a newer request. A key stamped more than 5 minutes ahead of server time is 400 INVALID_REQUEST.
728
+ * @description Turns the authenticated account's public trader profile on (public: true) or off. It starts on, and an account without a username can turn it off before picking one; nothing names the profile until then. While it is on, anyone, signed in or not, reads GET /v1/traders/{username}: the username, avatar, account wallet, volume, trades, open Combos and singles and P&L, and GET /v1/feed/combos lists the Combos it opens, and the leaderboards mark the account public_profile. Its HyperCore fills and tickets are public on chain anyway; what turning it on discloses is that the wallet belongs to the username. Turning it on needs a username (409 USERNAME_REQUIRED); public_since keeps the first time it went on until it is turned off. Call it after POST /v1/me/embedded-wallet; before that it is 404 IDENTITY_NOT_FOUND. Asking for the current state again returns 204, so a retry is safe. Idempotency-Keys order these requests: the other state under the account's newest key, or under an older key, is 409 IDEMPOTENCY_KEY_REUSED, so a delayed retry never undoes a newer request. A key stamped more than 5 minutes ahead of server time is 400 INVALID_REQUEST.
729
729
  */
730
730
  readonly put: operations["setPublicProfile"];
731
731
  readonly post?: never;
@@ -744,7 +744,7 @@ export type paths = {
744
744
  };
745
745
  /**
746
746
  * Get a public trader profile
747
- * @description A trader's public profile, while they have turned it on with PUT /v1/me/public-profile: their username, avatar, the account wallet they trade from, which clients read HyperCore holdings for from Hyperliquid, their follower count, whether the reader follows them, and their all-time volume, counted as GET /v1/leaderboard/volume counts it. Only a signed-in participant session reads it: anonymous calls and API keys are refused. A username with no public profile, whether private, unknown or held by an account that is not ACTIVE, is 404 NOT_FOUND.
747
+ * @description A trader's public profile, while they have turned it on with PUT /v1/me/public-profile: their username, avatar, the account wallet they trade from, which clients read HyperCore holdings for from Hyperliquid, their follower count, whether the reader follows them, and their all-time volume, counted as GET /v1/leaderboard/volume counts it. Anyone reads it, signed out or with any credential, from any origin; a supplied credential must still authenticate. A signed-out answer is the same for everyone and is cached for 30 seconds. A username with no public profile, whether private, unknown or held by an account that is not ACTIVE, is 404 NOT_FOUND, signed in or not.
748
748
  */
749
749
  readonly get: operations["getTrader"];
750
750
  readonly put?: never;
@@ -764,7 +764,7 @@ export type paths = {
764
764
  };
765
765
  /**
766
766
  * List a public trader's trades
767
- * @description A public trader's trades, newest first: each HyperCore fill GET /v1/leaderboard/volume counts as theirs (on their Totalis embedded wallet, or an order the app placed on another linked wallet), each Combo ticket a wallet of theirs opened, when it did (a ticket passed to them on is not their entry), and each sold back from a wallet of theirs, when it was. Poll the first page for new trades; ids are stable. The cursor never expires. Only a signed-in participant session reads it: anonymous calls and API keys are refused. A username with no public profile, whether private, unknown or held by an account that is not ACTIVE, is 404 NOT_FOUND.
767
+ * @description A public trader's trades, newest first: each HyperCore fill GET /v1/leaderboard/volume counts as theirs (on their Totalis embedded wallet, or an order the app placed on another linked wallet), each Combo ticket a wallet of theirs opened, when it did (a ticket passed to them on is not their entry), and each sold back from a wallet of theirs, when it was. Poll the first page for new trades; ids are stable. The cursor never expires. Anyone reads it, signed out or with any credential, from any origin; a supplied credential must still authenticate. A signed-out answer is the same for everyone and is cached for 30 seconds. A username with no public profile, whether private, unknown or held by an account that is not ACTIVE, is 404 NOT_FOUND, signed in or not.
768
768
  */
769
769
  readonly get: operations["listTraderTrades"];
770
770
  readonly put?: never;
@@ -783,8 +783,8 @@ export type paths = {
783
783
  readonly cookie?: never;
784
784
  };
785
785
  /**
786
- * List a public trader's open Combos
787
- * @description A public trader's open Combo tickets, newest first by opening, including tickets passed to them. Their HyperCore holdings are on Hyperliquid: read them for the profile's account wallet. Only a signed-in participant session reads it: anonymous calls and API keys are refused. A username with no public profile, whether private, unknown or held by an account that is not ACTIVE, is 404 NOT_FOUND.
786
+ * List a public trader's open Combos and singles
787
+ * @description A public trader's open Combo tickets, newest first by opening, including tickets passed to them, and on the first page (no cursor) their singles: the HIP-4 outcome sides their account wallet holds on HyperCore, replayed from its whole fill history in the account index. singles is null while that history cannot prove them: off Mainnet, while the index is behind or missing history, after an outcome token moved by transfer, or while the read fails. A later page leaves singles out. Anyone reads it, signed out or with any credential, from any origin; a supplied credential must still authenticate. A signed-out answer is the same for everyone and is cached for 30 seconds. A username with no public profile, whether private, unknown or held by an account that is not ACTIVE, is 404 NOT_FOUND, signed in or not.
788
788
  */
789
789
  readonly get: operations["listTraderPositions"];
790
790
  readonly put?: never;
@@ -804,7 +804,7 @@ export type paths = {
804
804
  };
805
805
  /**
806
806
  * Get a public trader's P&L series
807
- * @description A public trader's cumulative trading P&L over one range: what GET /v1/me/pnl shows them for their account wallet. Only a signed-in participant session reads it: anonymous calls and API keys are refused. A username with no public profile, whether private, unknown or held by an account that is not ACTIVE, is 404 NOT_FOUND.
807
+ * @description A public trader's cumulative trading P&L over one range: what GET /v1/me/pnl shows them for their account wallet. Anyone reads it, signed out or with any credential, from any origin; a supplied credential must still authenticate. A signed-out answer is the same for everyone and is cached for 30 seconds. A username with no public profile, whether private, unknown or held by an account that is not ACTIVE, is 404 NOT_FOUND, signed in or not.
808
808
  */
809
809
  readonly get: operations["getTraderPnl"];
810
810
  readonly put?: never;
@@ -815,6 +815,26 @@ export type paths = {
815
815
  readonly patch?: never;
816
816
  readonly trace?: never;
817
817
  };
818
+ readonly "/v1/feed/combos": {
819
+ readonly parameters: {
820
+ readonly query?: never;
821
+ readonly header?: never;
822
+ readonly path?: never;
823
+ readonly cookie?: never;
824
+ };
825
+ /**
826
+ * List the Combos public traders opened
827
+ * @description Every Combo ticket opened by a trader whose profile is public (PUT /v1/me/public-profile), newest first by opening, a page at a time, with the trader's username and avatar. A ticket counts for the participant whose linked wallet opened it, whoever holds it now; a profile turned off leaves the feed, and one turned on brings its Combos back. A page reads a bounded run of tickets, so it may hold fewer than limit, even none, while has_more is true. Poll the first page for new Combos; the cursor never expires. Anyone reads it, signed out or with any credential, from any origin; a supplied credential must still authenticate. A signed-out answer is the same for everyone and is cached for 30 seconds.
828
+ */
829
+ readonly get: operations["listComboFeed"];
830
+ readonly put?: never;
831
+ readonly post?: never;
832
+ readonly delete?: never;
833
+ readonly options?: never;
834
+ readonly head?: never;
835
+ readonly patch?: never;
836
+ readonly trace?: never;
837
+ };
818
838
  readonly "/v1/me/follows": {
819
839
  readonly parameters: {
820
840
  readonly query?: never;
@@ -845,7 +865,7 @@ export type paths = {
845
865
  readonly get?: never;
846
866
  /**
847
867
  * Follow a trader or set a follow's copy settings
848
- * @description Follows the trader whose public profile has this username, and answers the follow's copy settings. A follow alone copies nothing: it puts the trader's trades in the account's feed. Send {} to follow: a new follow takes the defaults (automatic copying off, FIXED 10 USDC a trade up to 100 USDC, 5 open positions, 100 bps slippage, exits mirrored) and an existing one keeps its settings. Send both core and combos to replace the follow's settings, following first if needed. Automatic copying also needs the product's copy agent, and stops while the account is paused (PUT /v1/me/copy-settings) or the trader's profile is off. Idempotency-Keys order every write to a follow, unfollows included: a request under a key no newer than the follow's latest succeeds only if the follow is already as it asks (followed, with these settings if it sends any), so a retry is safe; anything else is 409 IDEMPOTENCY_KEY_REUSED, so a delayed retry never undoes a newer follow, settings change or unfollow. A key stamped more than 5 minutes ahead of server time is 400 INVALID_REQUEST. A username with no public profile is 404 NOT_FOUND; the account's own username is 422 FOLLOW_SELF; a 101st follow is 422 FOLLOW_LIMIT_REACHED.
868
+ * @description Follows the trader whose public profile has this username, and answers the follow's copy settings. A follow alone copies nothing: it puts the trader's trades in the account's feed. Send {} to follow: a new follow takes the defaults (automatic copying off, FIXED 10 USDC a trade up to 100 USDC, 5 open positions, 100 bps slippage, exits mirrored) and an existing one keeps its settings. Send both core and combos to replace the follow's settings, following first if needed. Automatic copying also needs the product's copy agent, and stops while the account is paused (PUT /v1/me/copy-settings) or the trader's profile is off. Idempotency-Keys order every write to a follow, unfollows included: a request under a key no newer than the follow's latest succeeds only if the follow is already as it asks (followed, with these settings if it sends any), so a retry is safe; anything else is 409 IDEMPOTENCY_KEY_REUSED, so a delayed retry never undoes a newer follow, settings change or unfollow. A key stamped more than 5 minutes ahead of server time is 400 INVALID_REQUEST. A username with no public profile is 404 NOT_FOUND; the account's own username is 422 FOLLOW_SELF; a 101st follow is 422 FOLLOW_LIMIT_REACHED. Where copy trading is not available, a plain follow ({}) still works but a body with copy settings is 503 CONTRACT_UNAVAILABLE.
849
869
  */
850
870
  readonly put: operations["followTrader"];
851
871
  readonly post?: never;
@@ -2663,6 +2683,35 @@ export type components = {
2663
2683
  };
2664
2684
  /** TraderCombo */
2665
2685
  readonly TraderCombo: components["schemas"]["TraderComboFields"];
2686
+ /**
2687
+ * TraderSingle
2688
+ * @description A HIP-4 outcome side a trader's account wallet holds on HyperCore.
2689
+ */
2690
+ readonly TraderSingle: {
2691
+ /** @description HIP-4 outcome ID held, as a decimal string. */
2692
+ readonly outcome_id: components["schemas"]["UInt32String"];
2693
+ /** @description Outcome side held. */
2694
+ readonly side: components["schemas"]["Side"];
2695
+ /** @description Shares held, an exact decimal string with no trailing zeros. */
2696
+ readonly shares: string;
2697
+ /** @description What each held share cost on average in USDC, fees included: buys add their cost and a sale removes its shares' share of it. Rounded half away from zero to 6 decimals, no trailing zeros. Absent when the holding's fills brought cash in rather than paid it out. */
2698
+ readonly average_price?: string;
2699
+ /** @description HyperCore block time of the fill that last opened the holding from none. */
2700
+ readonly opened_at: components["schemas"]["$defs-Timestamp"];
2701
+ };
2702
+ /**
2703
+ * ComboFeedItem
2704
+ * @description A Combo a public trader opened.
2705
+ */
2706
+ readonly ComboFeedItem: {
2707
+ readonly username: components["schemas"]["Username"];
2708
+ /** @description The seed clients draw the trader's generated avatar from, as for GET /v1/traders/{username}. */
2709
+ readonly avatar_seed: components["schemas"]["AvatarSeed"];
2710
+ /** @description The trader's X picture, shown in place of the generated avatar; absent when there is none. */
2711
+ readonly picture_url?: components["schemas"]["XPictureUrl"];
2712
+ /** @description HyperEVM block time of the ticket's opening. */
2713
+ readonly opened_at: components["schemas"]["$defs-Timestamp"];
2714
+ } & components["schemas"]["TraderComboFields"];
2666
2715
  /** @description One live, unsigned quote on an RFQ, told apart by `kind`. List quotes, the account snapshot's quotes and `QUOTE_CREATED` carry the same item. */
2667
2716
  readonly Quote: components["schemas"]["EntryQuote"] | components["schemas"]["SellBackQuote"] | components["schemas"]["TransferQuote"];
2668
2717
  /** @description A maker's offer to open a position on an entry RFQ. */
@@ -7755,7 +7804,8 @@ export interface operations {
7755
7804
  /** @description The profile */
7756
7805
  readonly 200: {
7757
7806
  headers: {
7758
- readonly "Cache-Control"?: "no-store";
7807
+ /** @description public, max-age=30 for a signed-out read, which the edge may share; no-store for one with a credential. */
7808
+ readonly "Cache-Control"?: "public, max-age=30" | "no-store";
7759
7809
  readonly [name: string]: unknown;
7760
7810
  };
7761
7811
  content: {
@@ -7820,7 +7870,8 @@ export interface operations {
7820
7870
  /** @description A page of trades */
7821
7871
  readonly 200: {
7822
7872
  headers: {
7823
- readonly "Cache-Control"?: "no-store";
7873
+ /** @description public, max-age=30 for a signed-out read, which the edge may share; no-store for one with a credential. */
7874
+ readonly "Cache-Control"?: "public, max-age=30" | "no-store";
7824
7875
  readonly [name: string]: unknown;
7825
7876
  };
7826
7877
  content: {
@@ -7895,7 +7946,8 @@ export interface operations {
7895
7946
  /** @description A page of open Combos */
7896
7947
  readonly 200: {
7897
7948
  headers: {
7898
- readonly "Cache-Control"?: "no-store";
7949
+ /** @description public, max-age=30 for a signed-out read, which the edge may share; no-store for one with a credential. */
7950
+ readonly "Cache-Control"?: "public, max-age=30" | "no-store";
7899
7951
  readonly [name: string]: unknown;
7900
7952
  };
7901
7953
  content: {
@@ -7907,6 +7959,8 @@ export interface operations {
7907
7959
  } & components["schemas"]["TraderComboFields"])[];
7908
7960
  readonly next_cursor: components["schemas"]["NextCursor"];
7909
7961
  readonly has_more: boolean;
7962
+ /** @description The trader's open singles, newest opened first; null when they cannot be known. Only on the first page. */
7963
+ readonly singles?: null | readonly components["schemas"]["TraderSingle"][];
7910
7964
  };
7911
7965
  };
7912
7966
  };
@@ -7936,7 +7990,8 @@ export interface operations {
7936
7990
  /** @description The trader's P&L series */
7937
7991
  readonly 200: {
7938
7992
  headers: {
7939
- readonly "Cache-Control"?: "no-store";
7993
+ /** @description public, max-age=30 for a signed-out read, which the edge may share; no-store for one with a credential. */
7994
+ readonly "Cache-Control"?: "public, max-age=30" | "no-store";
7940
7995
  readonly [name: string]: unknown;
7941
7996
  };
7942
7997
  content: {
@@ -7951,6 +8006,49 @@ export interface operations {
7951
8006
  readonly 503: components["responses"]["Unavailable"];
7952
8007
  };
7953
8008
  };
8009
+ readonly listComboFeed: {
8010
+ readonly parameters: {
8011
+ readonly query?: {
8012
+ /**
8013
+ * @description Omit for the first page; then send the previous response's next_cursor unchanged.
8014
+ * @example eyJ2IjoxLCJzIjoiMTIzNDUifQ
8015
+ */
8016
+ readonly cursor?: components["parameters"]["Cursor"];
8017
+ /**
8018
+ * @description Maximum items per page, 1 to 200, default 50. A page may hold fewer, even none, while has_more is true.
8019
+ * @example 50
8020
+ */
8021
+ readonly limit?: components["parameters"]["Limit"];
8022
+ };
8023
+ readonly header?: never;
8024
+ readonly path?: never;
8025
+ readonly cookie?: never;
8026
+ };
8027
+ readonly requestBody?: never;
8028
+ readonly responses: {
8029
+ /** @description A page of public traders' Combos */
8030
+ readonly 200: {
8031
+ headers: {
8032
+ /** @description public, max-age=30 for a signed-out read, which the edge may share; no-store for one with a credential. */
8033
+ readonly "Cache-Control"?: "public, max-age=30" | "no-store";
8034
+ readonly [name: string]: unknown;
8035
+ };
8036
+ content: {
8037
+ readonly "application/json": {
8038
+ /** @description Combos, newest first by opening. */
8039
+ readonly items: readonly components["schemas"]["ComboFeedItem"][];
8040
+ readonly next_cursor: components["schemas"]["NextCursor"];
8041
+ readonly has_more: boolean;
8042
+ };
8043
+ };
8044
+ };
8045
+ readonly 400: components["responses"]["Error"];
8046
+ readonly 401: components["responses"]["Error"];
8047
+ readonly 403: components["responses"]["Error"];
8048
+ readonly 429: components["responses"]["RateLimited"];
8049
+ readonly 503: components["responses"]["Unavailable"];
8050
+ };
8051
+ };
7954
8052
  readonly listFollows: {
7955
8053
  readonly parameters: {
7956
8054
  readonly query?: never;
@@ -1,5 +1,5 @@
1
- export declare const sdkVersion = "205.0.0-device-vault-agents";
2
- export declare const openapiSha256 = "93141e0bf50949fc37ebceef7c64555eb50a70f69c71c89c8fdc12780e0dd168";
1
+ export declare const sdkVersion = "206.0.0-public-trader-reads";
2
+ export declare const openapiSha256 = "1c4c1c9a685d200b19fc047a9ff25240c7a7ba37520bd6a9dc2ea6b78b696e64";
3
3
  export declare const realtimeSha256 = "1cac740e108e84ad1979f6ccc71089efd840404cb72d1612a86edc02769637e3";
4
4
  export declare const jsonSchemaSha256: {
5
5
  readonly "account.schema.json": "5106e2d53abef190990eff113f6692dd27df5848439cf5a80a42c11e89f2bef6";
@@ -18,7 +18,7 @@ export declare const jsonSchemaSha256: {
18
18
  readonly "realtime.schema.json": "1cac740e108e84ad1979f6ccc71089efd840404cb72d1612a86edc02769637e3";
19
19
  };
20
20
  export declare const operations: {
21
- readonly client: readonly ["abandonCoreExit", "acceptQuote", "activeCoreExits", "admitOwnMaker", "admitPartnerWallet", "bindEmbeddedWallet", "cancelRfq", "confirmComboCopyAgent", "confirmCoreCopyAgent", "createCashout", "createComment", "createCoreExit", "createDeposit", "createRfq", "createRunOffClaim", "createWalletChallenge", "createWalletSession", "createWithdrawal", "deleteComment", "editComment", "endWalletSession", "followTrader", "getAcceptance", "getAccountActivity", "getAccountPnl", "getAccountPosition", "getBalances", "getCopyAgents", "getCopyBudget", "getCopySettings", "getCopySummary", "getCoreExit", "getDeployment", "getHypercoreBalance", "getHypercorePortfolio", "getHypercoreSettlementCredits", "getIdentity", "getMakerActivity", "getMakerCapital", "getMakerExposure", "getMakerPerformance", "getMakerPosition", "getMakerQuotes", "getMakerTelemetry", "getMakerVaultFundingTransaction", "getMakerWithdrawal", "getMarket", "getMarketDataConfig", "getMarketDefinitions", "getMarketHistory", "getMarketRecurringRound", "getMarketReferenceCloses", "getMarketScoreTimeline", "getMarketStats", "getOperation", "getOwnMaker", "getPartnerReport", "getPerpMarks", "getProfitLeaderboard", "getQuestion", "getQuestionRecurringRound", "getQuestionScoreTimeline", "getRfq", "getSeries", "getSuggestedCombo", "getTaxonomy", "getTrader", "getTraderPnl", "getUsernameAvailability", "getVaultAgents", "getVolumeLeaderboard", "getWithdrawal", "grantComboCopyAgent", "grantVaultAgent", "likeComment", "listAccountActivity", "listAccountPositions", "listComboCopies", "listCommentReplies", "listComments", "listControlledMakers", "listCopiedFills", "listCopyTrades", "listDiscovery", "listFollows", "listMakerActivity", "listMakerPositions", "listMakerQuoteHistory", "listMarkets", "listOperations", "listQuotes", "listTraderPositions", "listTraderTrades", "reactComment", "recordAcquisition", "refreshPicture", "requestComboCopyAgent", "requestCoreCopyAgent", "revokeComboCopyAgent", "searchCatalog", "setAvatar", "setCopyAllocation", "setCopySettings", "setPublicProfile", "setUsername", "stream", "streamMarkets", "switchOffComboCopyAgent", "switchOffCoreCopyAgent", "unfollowTrader", "unlikeComment", "unreactComment"];
21
+ readonly client: readonly ["abandonCoreExit", "acceptQuote", "activeCoreExits", "admitOwnMaker", "admitPartnerWallet", "bindEmbeddedWallet", "cancelRfq", "confirmComboCopyAgent", "confirmCoreCopyAgent", "createCashout", "createComment", "createCoreExit", "createDeposit", "createRfq", "createRunOffClaim", "createWalletChallenge", "createWalletSession", "createWithdrawal", "deleteComment", "editComment", "endWalletSession", "followTrader", "getAcceptance", "getAccountActivity", "getAccountPnl", "getAccountPosition", "getBalances", "getCopyAgents", "getCopyBudget", "getCopySettings", "getCopySummary", "getCoreExit", "getDeployment", "getHypercoreBalance", "getHypercorePortfolio", "getHypercoreSettlementCredits", "getIdentity", "getMakerActivity", "getMakerCapital", "getMakerExposure", "getMakerPerformance", "getMakerPosition", "getMakerQuotes", "getMakerTelemetry", "getMakerVaultFundingTransaction", "getMakerWithdrawal", "getMarket", "getMarketDataConfig", "getMarketDefinitions", "getMarketHistory", "getMarketRecurringRound", "getMarketReferenceCloses", "getMarketScoreTimeline", "getMarketStats", "getOperation", "getOwnMaker", "getPartnerReport", "getPerpMarks", "getProfitLeaderboard", "getQuestion", "getQuestionRecurringRound", "getQuestionScoreTimeline", "getRfq", "getSeries", "getSuggestedCombo", "getTaxonomy", "getTrader", "getTraderPnl", "getUsernameAvailability", "getVaultAgents", "getVolumeLeaderboard", "getWithdrawal", "grantComboCopyAgent", "grantVaultAgent", "likeComment", "listAccountActivity", "listAccountPositions", "listComboCopies", "listComboFeed", "listCommentReplies", "listComments", "listControlledMakers", "listCopiedFills", "listCopyTrades", "listDiscovery", "listFollows", "listMakerActivity", "listMakerPositions", "listMakerQuoteHistory", "listMarkets", "listOperations", "listQuotes", "listTraderPositions", "listTraderTrades", "reactComment", "recordAcquisition", "refreshPicture", "requestComboCopyAgent", "requestCoreCopyAgent", "revokeComboCopyAgent", "searchCatalog", "setAvatar", "setCopyAllocation", "setCopySettings", "setPublicProfile", "setUsername", "stream", "streamMarkets", "switchOffComboCopyAgent", "switchOffCoreCopyAgent", "unfollowTrader", "unlikeComment", "unreactComment"];
22
22
  readonly maker: readonly ["cancelQuote", "confirmQuote", "createCollateralReduction", "createQuote", "getAccountActivity", "getAccountPosition", "getCollateralReduction", "getDeployment", "getMakerActivity", "getMakerCapital", "getMakerExposure", "getMakerPerformance", "getMakerPosition", "getMakerQuotes", "getMakerTelemetry", "getMakerTelemetryPublication", "getMakerVaultFundingTransaction", "getMakerWithdrawal", "getMarket", "getMarketHistory", "getMarketRecurringRound", "getMarketReferenceCloses", "getOperation", "getQuestion", "getQuestionRecurringRound", "getRfq", "getSeries", "getTaxonomy", "listAccountActivity", "listAccountPositions", "listDiscovery", "listMakerAcceptances", "listMakerActivity", "listMakerPositions", "listMakerQuoteHistory", "listMarkets", "listOperations", "listRfqs", "publishMakerTelemetry", "releaseQuoteLease", "renewQuoteLease", "searchCatalog", "stream", "submitMakerSelfFundedTransaction"];
23
23
  readonly operator: readonly ["live", "ready"];
24
24
  };
@@ -1,6 +1,6 @@
1
1
  // Generated by scripts/generate.mjs; do not edit.
2
- export const sdkVersion = "205.0.0-device-vault-agents";
3
- export const openapiSha256 = "93141e0bf50949fc37ebceef7c64555eb50a70f69c71c89c8fdc12780e0dd168";
2
+ export const sdkVersion = "206.0.0-public-trader-reads";
3
+ export const openapiSha256 = "1c4c1c9a685d200b19fc047a9ff25240c7a7ba37520bd6a9dc2ea6b78b696e64";
4
4
  export const realtimeSha256 = "1cac740e108e84ad1979f6ccc71089efd840404cb72d1612a86edc02769637e3";
5
5
  export const jsonSchemaSha256 = {
6
6
  "account.schema.json": "5106e2d53abef190990eff113f6692dd27df5848439cf5a80a42c11e89f2bef6",
@@ -98,6 +98,7 @@ export const operations = {
98
98
  "listAccountActivity",
99
99
  "listAccountPositions",
100
100
  "listComboCopies",
101
+ "listComboFeed",
101
102
  "listCommentReplies",
102
103
  "listComments",
103
104
  "listControlledMakers",
package/dist/totalis.d.ts CHANGED
@@ -102,6 +102,27 @@ export type SignedEntry = RecordScope & {
102
102
  readonly command: AcceptCommand;
103
103
  readonly bridge?: SignedSendAsset;
104
104
  readonly bridgeState?: BridgeState;
105
+ /**
106
+ * Set when the venue refused this accept outright (`QUOTE_REPLACED`,
107
+ * `QUOTE_EXPIRED`): nothing was admitted, so `recoverAcceptance` answers
108
+ * `NOT_ADMITTED` without reading or cancelling the RFQ, which a later
109
+ * attempt may be using.
110
+ */
111
+ readonly refused?: string;
112
+ };
113
+ /**
114
+ * An entry's funding, signed before its quote is chosen
115
+ * (`prepareEntryFunding`). Pass it to `signEntry` as `prepared`, so only the
116
+ * quote is left to sign once it is chosen. It funds one accept: prepare again
117
+ * for another.
118
+ */
119
+ export type PreparedEntryFunding = {
120
+ readonly stake: string;
121
+ readonly account: Hex;
122
+ readonly chainId: string;
123
+ readonly vault: Hex;
124
+ readonly funding?: Funding;
125
+ readonly bridge?: SignedSendAsset;
105
126
  };
106
127
  /** An entry record from SDK 0.63 or older, which sent its bridge before the accept: `recoverAcceptance` reads it and sends nothing, and `sendAcceptance` refuses it. */
107
128
  export type LegacySignedEntry = Omit<SignedEntry, "version" | "bridgeState">;
@@ -246,7 +267,7 @@ export declare function createTotalis(options: TotalisOptions): {
246
267
  readonly legs: readonly Leg[];
247
268
  readonly stake: Amount;
248
269
  readonly choose?: Chooser<EntryQuote>;
249
- /** How long quotes may keep arriving after the first one `choose` would take, before it picks; 0 takes it at once. */
270
+ /** How long quotes may keep arriving after the first one `choose` would take, before it picks; 0 takes it at once. Time spent signing the funding counts. */
250
271
  readonly collectMs?: number;
251
272
  /** `"wallet"` (default) plans the shortfall from the wallet and `core`; `"none"` never funds; or pass a funding command. */
252
273
  readonly funding?: "wallet" | "none" | Funding;
@@ -254,9 +275,9 @@ export declare function createTotalis(options: TotalisOptions): {
254
275
  readonly core?: Amount | null;
255
276
  readonly sourceDex?: "spot" | "";
256
277
  readonly transport?: HypercoreTransport;
257
- /** The cash to plan from (see `signEntry`); left out, the balances are read while the quotes arrive. */
278
+ /** The cash to plan from (see `signEntry`); left out, the balances are read while the RFQ is created, and again for each refresh. */
258
279
  readonly cash?: EntryCash;
259
- /** Awaited with the signed record before anything is sent, then with each change to its bridge (`onRecord`): persist it to `recoverAcceptance` after a restart. */
280
+ /** Awaited with the signed record before anything is sent, then with each change to its bridge (`onRecord`) or its refusal: persist it to `recoverAcceptance` after a restart. */
260
281
  readonly onSigned?: RecordListener;
261
282
  /** Automatic-quote intent identity, as the Totalis app sends it. */
262
283
  readonly intent?: {
@@ -264,6 +285,12 @@ export declare function createTotalis(options: TotalisOptions): {
264
285
  readonly intent_generation: string;
265
286
  };
266
287
  readonly quoteTimeoutMs?: number;
288
+ /** The least payout a quote may pay, in USDC base units. Left out, it is the first chosen quote's: a refresh never takes less. */
289
+ readonly minPayout?: Amount;
290
+ /** How long after the first quote is chosen a refused, declined or timed-out accept may still be followed by another (default 10 s). */
291
+ readonly refreshMs?: number;
292
+ /** The most accepts sent, the first included (default 4); 1 never refreshes. */
293
+ readonly maxAttempts?: number;
267
294
  readonly signal?: AbortSignal;
268
295
  }) => Promise<{
269
296
  entry: {
@@ -297,6 +324,14 @@ export declare function createTotalis(options: TotalisOptions): {
297
324
  readonly quoteTimeoutMs?: number;
298
325
  readonly signal?: AbortSignal;
299
326
  }) => Promise<AcceptanceResult>;
327
+ prepareEntryFunding: (request: {
328
+ readonly stake: Amount;
329
+ readonly core?: Amount | null | undefined;
330
+ readonly cash?: EntryCash | undefined;
331
+ readonly sourceDex?: "spot" | "" | undefined;
332
+ readonly hypercoreNonce?: number | undefined;
333
+ readonly signal?: AbortSignal | undefined;
334
+ }) => Promise<PreparedEntryFunding>;
300
335
  signEntry: (request: {
301
336
  readonly quote: EntryQuote;
302
337
  readonly core?: Amount | null;
@@ -304,6 +339,7 @@ export declare function createTotalis(options: TotalisOptions): {
304
339
  readonly sourceDex?: "spot" | "";
305
340
  readonly acceptanceId?: string;
306
341
  readonly hypercoreNonce?: number;
342
+ readonly prepared?: PreparedEntryFunding;
307
343
  }) => Promise<SignedEntry>;
308
344
  signCashout: (request: {
309
345
  readonly quote: CashoutBid;
package/dist/totalis.js CHANGED
@@ -87,6 +87,11 @@ const minimumDeposit = 1000000n;
87
87
  * best of them win instead of the fastest.
88
88
  */
89
89
  const defaultCollectMs = 1_000;
90
+ const defaultRefreshMs = 10_000;
91
+ const defaultMaxAttempts = 4;
92
+ /** Accept refusals that mean the chosen quote went away before the accept, not that the position cannot open. */
93
+ const refreshCodes = new Set(["QUOTE_EXPIRED", "QUOTE_REPLACED"]);
94
+ const passedOn = new Set(["DECLINED", "TIMED_OUT"]);
90
95
  const timerSlackMs = 250;
91
96
  function termsOf(quote) {
92
97
  return quote.kind === "ENTRY" ? quote.entry : quote.kind === "SELL_BACK" ? quote.sell_back : quote.transfer;
@@ -641,11 +646,46 @@ export function createTotalis(options) {
641
646
  throw new EntryError("INSUFFICIENT_FUNDS", `${bridge - (routable > 0n ? routable : 0n)} USDC base units short for this stake`);
642
647
  return { wallet: shortfall, bridge };
643
648
  }
649
+ function signBridge(signer, chain, amount, request) {
650
+ return amount > 0n ? signSendAsset(signer, bridgeAction(hyperliquidNetwork(chain.chain_id), amount, {
651
+ ...(request.hypercoreNonce === undefined ? {} : { nonce: request.hypercoreNonce }),
652
+ ...(request.sourceDex === undefined ? {} : { sourceDex: request.sourceDex }),
653
+ })) : undefined;
654
+ }
655
+ /**
656
+ * Plan and sign an entry's funding before choosing its quote: the wallet's
657
+ * USDC authorization and any bridge from `core`, planned from `cash` or a
658
+ * fresh balances read as `signEntry` plans them. Neither names a quote, so
659
+ * once one is chosen `signEntry({ quote, prepared })` signs only the quote
660
+ * and the accept can go straight out.
661
+ */
662
+ async function prepareEntryFunding(request) {
663
+ const { signer, account, chain, scope } = await recordScope();
664
+ const stake = BigInt(request.stake);
665
+ const core = request.core === undefined || request.core === null ? request.core : BigInt(request.core);
666
+ const plan = planEntry(stake, core, request.cash ?? await readSpendableCash(request.signal));
667
+ const [bridge, funding] = await Promise.all([
668
+ signBridge(signer, chain, plan.bridge, request),
669
+ plan.wallet > 0n ? depositAuthorization(chain, account, plan.wallet) : undefined,
670
+ ]);
671
+ return { stake: stake.toString(), ...scope, ...(funding === undefined ? {} : { funding }), ...(bridge === undefined ? {} : { bridge }) };
672
+ }
644
673
  async function signEntryWith(request, funding) {
645
- const { quote, cash } = request;
674
+ const { quote, cash, prepared } = request;
646
675
  const { signer, vaultSigner, account, chain, scope } = await recordScope();
647
676
  if (quote.entry.taker.toLowerCase() !== account)
648
677
  throw new Error("the quote is for another taker");
678
+ const record = (taker_signature, fundingCommand, bridge) => ({
679
+ kind: "ENTRY", version: 2, acceptanceId: request.acceptanceId ?? uuidv7(), rfqId: quote.rfq_id, quoteId: quote.quote_id, ...scope, expiry: quote.entry.expiry,
680
+ command: { taker_signature, ...(fundingCommand === undefined ? {} : { funding: fundingCommand }) },
681
+ ...(bridge === undefined ? {} : { bridge }),
682
+ });
683
+ if (prepared !== undefined) {
684
+ if (prepared.stake !== quote.entry.stake || prepared.account !== scope.account || prepared.chainId !== scope.chainId || prepared.vault !== scope.vault) {
685
+ throw new Error("the prepared funding is for another stake, account or deployment");
686
+ }
687
+ return record(await vaultSigner.signTypedData(takerAcceptTypedData(chain, quote.entry)), prepared.funding, prepared.bridge);
688
+ }
649
689
  const stake = BigInt(quote.entry.stake);
650
690
  const core = request.core === undefined || request.core === null ? request.core : BigInt(request.core);
651
691
  // Known cash is planned before any prompt, so a plan that fails asks the wallet for nothing.
@@ -664,18 +704,11 @@ export function createTotalis(options) {
664
704
  const walletAmount = plan?.wallet ?? 0n;
665
705
  const [taker_signature, bridge, walletFunding] = await Promise.all([
666
706
  signing,
667
- bridgeAmount > 0n ? signSendAsset(signer, bridgeAction(hyperliquidNetwork(chain.chain_id), bridgeAmount, {
668
- ...(request.hypercoreNonce === undefined ? {} : { nonce: request.hypercoreNonce }),
669
- ...(request.sourceDex === undefined ? {} : { sourceDex: request.sourceDex }),
670
- })) : undefined,
707
+ signBridge(signer, chain, bridgeAmount, request),
671
708
  walletAmount > 0n ? depositAuthorization(chain, account, walletAmount) : undefined,
672
709
  ]);
673
710
  const fundingCommand = plan === undefined ? (funding === "none" || funding === "plan" ? undefined : funding) : walletFunding;
674
- return {
675
- kind: "ENTRY", version: 2, acceptanceId: request.acceptanceId ?? uuidv7(), rfqId: quote.rfq_id, quoteId: quote.quote_id, ...scope, expiry: quote.entry.expiry,
676
- command: { taker_signature, ...(fundingCommand === undefined ? {} : { funding: fundingCommand }) },
677
- ...(bridge === undefined ? {} : { bridge }),
678
- };
711
+ return record(taker_signature, fundingCommand, bridge);
679
712
  }
680
713
  /**
681
714
  * Sign a Combo entry for a quote without sending it. Funding is planned
@@ -691,7 +724,8 @@ export function createTotalis(options) {
691
724
  * shows them in turn. `sourceDex: ""` bridges from perps instead of spot.
692
725
  * Persist the record, then `sendAcceptance` it; after a restart,
693
726
  * `recoverAcceptance` it. Pass `hypercoreNonce` if your app reserves
694
- * Hyperliquid nonces itself.
727
+ * Hyperliquid nonces itself. With `prepared` (`prepareEntryFunding`), only
728
+ * the quote is signed, and its funding is the prepared one.
695
729
  */
696
730
  function signEntry(request) {
697
731
  return signEntryWith(request, "plan");
@@ -773,9 +807,13 @@ export function createTotalis(options) {
773
807
  * record from SDK 0.63 or older is only read. One still `PENDING_CONFIRM`
774
808
  * is read again after the maker's deadline, or every 250 ms until 2 s past
775
809
  * it while a bridge is unsent; `wait: false` returns it as it stands instead.
810
+ * A record marked `refused` is `NOT_ADMITTED`, read from the record alone.
776
811
  */
777
812
  async function recoverAcceptance(signed, options = {}) {
778
813
  await checkScope(signed);
814
+ if (signed.kind === "ENTRY" && "refused" in signed && signed.refused !== undefined) {
815
+ return resultOf(signed, { status: "NOT_ADMITTED", quote_id: signed.quoteId, rfq_id: signed.rfqId }, {});
816
+ }
779
817
  const clock = venueClock();
780
818
  const outcome = await fenceAcceptance(signed, clock, undefined, options.wait ?? true, bridgeUnsent(signed));
781
819
  return resultOf(signed, outcome, await moveBridge(signed, outcome, clock, options));
@@ -791,21 +829,30 @@ export function createTotalis(options) {
791
829
  }
792
830
  }
793
831
  /**
794
- * Open a combo position: create the RFQ, take a quote from those the stream
795
- * pushes, sign it (`signEntry`), hand the record to `onSigned` to persist,
796
- * then `sendAcceptance`. Returns the acceptance's status (`ACCEPTED` with
832
+ * Open a combo position: create the RFQ, sign its funding
833
+ * (`prepareEntryFunding`), then take a quote from those the stream pushes,
834
+ * sign it (`signEntry`), hand the record to `onSigned` to persist, and
835
+ * `sendAcceptance` at once: the quote is chosen last, so the one signed is
836
+ * as fresh as the book allows. Returns the acceptance's status (`ACCEPTED` with
797
837
  * `operationId`; `FILLED` or `FAILED`, also with it, when a lost answer was
798
838
  * read after the trade settled; `DECLINED` / `TIMED_OUT` / `CANCELLED`; or
799
839
  * `NOT_ADMITTED` when a lost accept never arrived). The position opens
800
840
  * under `positionId`, the quote's ID, once `waitForOperation(operationId)`
801
841
  * resolves `COMMITTED`; pass its operation and the result's `record` to
802
842
  * `entryFailure` to name a failure. Any vault shortfall is funded from the
803
- * wallet, and from `core` by bridge, unless `funding` says otherwise. An accept refused with
804
- * `QUOTE_EXPIRED` or `QUOTE_REPLACED` takes and signs a fresh quote once
805
- * (`onSigned` gets the new record); a second refusal is thrown.
843
+ * wallet, and from `core` by bridge, unless `funding` says otherwise.
844
+ *
845
+ * Makers replace their quotes as they renew them, so an accept can find its
846
+ * quote gone (`QUOTE_REPLACED`, `QUOTE_EXPIRED`); then `onSigned` gets the
847
+ * record marked `refused`. A maker can also turn its quote down (`DECLINED`)
848
+ * or not answer in time (`TIMED_OUT`). Either way nothing was admitted, so
849
+ * the best other quote paying at least `minPayout` is signed and sent,
850
+ * funding first, until one is taken, `refreshMs` has passed since the first
851
+ * quote was chosen, or `maxAttempts` accepts were sent. Then the last
852
+ * refusal is thrown, or the last decline or timeout returned.
806
853
  */
807
854
  async function openPosition(request) {
808
- const { legs, stake, choose = bestQuote(), collectMs = defaultCollectMs, funding = "wallet", intent, quoteTimeoutMs = 20_000, signal } = request;
855
+ const { legs, stake, choose = bestQuote(), collectMs = defaultCollectMs, funding = "wallet", intent, quoteTimeoutMs = 20_000, refreshMs = defaultRefreshMs, maxAttempts = defaultMaxAttempts, signal } = request;
809
856
  const reading = funding === "wallet" && request.cash === undefined ? readSpendableCash(signal) : undefined;
810
857
  reading?.catch(() => undefined);
811
858
  const account = await (await requireSigner()).address();
@@ -814,20 +861,32 @@ export function createTotalis(options) {
814
861
  const wanted = { taker: account, stake: String(stake), legs: [...legs].sort((a, b) => Number(a.outcome_id) - Number(b.outcome_id)).map(({ outcome_id, side }) => ({ outcome_id, side })) };
815
862
  const entry = { stake: wanted.stake, legs: wanted.legs };
816
863
  const rfqId = await createRfq(intent === undefined ? entry : { ...entry, ...intent });
817
- // Only quotes for exactly this position are offered to the chooser, and the chosen one is checked again before signing.
818
- const matches = (quote) => quote.kind === "ENTRY" && quote.rfq_id === rfqId &&
864
+ const createdAt = performance.now();
865
+ let floor = request.minPayout === undefined ? undefined : BigInt(request.minPayout);
866
+ const refused = new Set();
867
+ const offered = (quote) => quote.kind === "ENTRY" && quote.rfq_id === rfqId &&
819
868
  quote.entry.taker.toLowerCase() === wanted.taker && quote.entry.stake === wanted.stake &&
820
- JSON.stringify(quote.entry.legs.map(({ outcome_id, side }) => ({ outcome_id, side }))) === JSON.stringify(wanted.legs);
821
- const take = (refused) => signedOrCancel(rfqId, async () => {
822
- const offered = (quote) => matches(quote) && quote.quote_id !== refused;
823
- const selected = await awaitOffer(rfqId, offered, choose, collectMs, quoteTimeoutMs, signal);
869
+ JSON.stringify(quote.entry.legs.map(({ outcome_id, side }) => ({ outcome_id, side }))) === JSON.stringify(wanted.legs) &&
870
+ !refused.has(quote.quote_id) && (floor === undefined || BigInt(quote.entry.payout) >= floor);
871
+ // Counted from the first choice, not from its signature, so a slow wallet cannot stretch the window.
872
+ let refreshUntil;
873
+ const take = () => signedOrCancel(rfqId, async () => {
874
+ const prepared = funding === "wallet"
875
+ ? await prepareEntryFunding({ stake: wanted.stake, core: request.core, cash: request.cash ?? await (refreshUntil === undefined ? reading : readSpendableCash(signal)), sourceDex: request.sourceDex, signal })
876
+ : undefined;
877
+ const selected = refreshUntil === undefined
878
+ ? await awaitOffer(rfqId, offered, choose, Math.max(0, Math.round(collectMs - (performance.now() - createdAt))), quoteTimeoutMs, signal)
879
+ : await awaitOffer(rfqId, offered, choose, 0, Math.max(0, Math.round(refreshUntil - performance.now())), signal);
880
+ refreshUntil ??= performance.now() + refreshMs;
824
881
  if (!offered(selected))
825
882
  throw new Error("the chosen quote is not for the requested position");
826
- const signed = await signEntryWith({ quote: selected, core: request.core, cash: request.cash ?? await reading, sourceDex: request.sourceDex }, funding === "wallet" ? "plan" : funding);
883
+ const signed = await signEntryWith({ quote: selected, prepared }, funding === "wallet" ? "plan" : funding);
827
884
  await request.onSigned?.(signed);
828
885
  return { selected, signed };
829
886
  });
830
887
  let { selected, signed } = await take();
888
+ floor ??= BigInt(selected.entry.payout);
889
+ const refreshEnds = refreshUntil ?? 0;
831
890
  let record = signed;
832
891
  const send = () => sendAcceptance(signed, {
833
892
  ...(request.transport === undefined ? {} : { transport: request.transport }),
@@ -837,18 +896,56 @@ export function createTotalis(options) {
837
896
  await request.onSigned?.(next);
838
897
  },
839
898
  });
840
- let result;
841
- try {
842
- result = await send();
843
- }
844
- catch (error) {
845
- if (!(error instanceof ApiError) || error.code !== "QUOTE_EXPIRED" && error.code !== "QUOTE_REPLACED")
846
- throw error;
847
- // The quote went away before the accept (sendAcceptance discarded its bridge): sign a fresh one, once. No fresh quote in time is still that refusal.
848
- ({ selected, signed } = await take(signed.quoteId).catch((retried) => Promise.reject(retried instanceof Error && retried.name === "TimeoutError" ? error : retried)));
849
- record = signed;
850
- result = await send();
851
- }
899
+ const settle = async () => {
900
+ for (let attempt = 1;; attempt += 1) {
901
+ // The maker turned the quote down, or never answered: nothing was admitted and the RFQ stays open.
902
+ let lost;
903
+ try {
904
+ lost = await send();
905
+ if (!passedOn.has(lost.status))
906
+ return lost;
907
+ }
908
+ catch (error) {
909
+ if (!(error instanceof ApiError) || !refreshCodes.has(error.code))
910
+ throw error;
911
+ // Refused outright, so nothing was admitted and sendAcceptance discarded any bridge.
912
+ lost = error;
913
+ record = { ...record, refused: error.code };
914
+ await request.onSigned?.(record);
915
+ }
916
+ refused.add(signed.quoteId);
917
+ const last = record;
918
+ const end = async () => {
919
+ if (record !== last) {
920
+ record = last;
921
+ await request.onSigned?.(last);
922
+ }
923
+ if (lost instanceof ApiError)
924
+ throw lost;
925
+ return lost;
926
+ };
927
+ if (attempt >= maxAttempts || performance.now() >= refreshEnds)
928
+ return end();
929
+ let next;
930
+ try {
931
+ next = await take();
932
+ }
933
+ catch (retried) {
934
+ // No fresh quote at or above the floor in time leaves this outcome standing.
935
+ if (retried instanceof Error && retried.name === "TimeoutError")
936
+ return end();
937
+ throw retried;
938
+ }
939
+ // Signing and persisting can outlast the window: a record signed too late is never sent, and the last sent one is persisted again.
940
+ if (performance.now() >= refreshEnds) {
941
+ record = next.signed;
942
+ return end();
943
+ }
944
+ ({ selected, signed } = next);
945
+ record = signed;
946
+ }
947
+ };
948
+ const result = await settle();
852
949
  const { bridged: _bridged, ...settled } = result;
853
950
  return {
854
951
  ...settled,
@@ -1265,6 +1362,7 @@ export function createTotalis(options) {
1265
1362
  spendableCash: readSpendableCash,
1266
1363
  openPosition,
1267
1364
  cashOut,
1365
+ prepareEntryFunding,
1268
1366
  signEntry,
1269
1367
  signCashout,
1270
1368
  sendAcceptance,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@totalis/hip4-client",
3
- "version": "0.76.0",
3
+ "version": "0.78.0",
4
4
  "description": "Totalis HIP-4 SDK: typed HTTP clients, signer-agnostic typed data, a retrying request layer, pagination, a realtime stream and taker/maker flows",
5
5
  "type": "module",
6
6
  "files": [