@symmio/trading-core 3.4.1 → 3.6.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/dist/core/chains/registry.d.ts.map +1 -1
- package/dist/core/chains/registry.js +6 -0
- package/dist/core/chains/registry.js.map +1 -1
- package/dist/gasless/delegation-preflight.d.ts +22 -0
- package/dist/gasless/delegation-preflight.d.ts.map +1 -1
- package/dist/gasless/delegation-preflight.js +34 -19
- package/dist/gasless/delegation-preflight.js.map +1 -1
- package/dist/index.d.ts +12 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +82 -79
- package/dist/pools/index.d.ts +1 -0
- package/dist/pools/index.d.ts.map +1 -1
- package/dist/pools/token-price/get-token-price.d.ts +71 -0
- package/dist/pools/token-price/get-token-price.d.ts.map +1 -0
- package/dist/pools/token-price/get-token-price.js +30 -0
- package/dist/pools/token-price/get-token-price.js.map +1 -0
- package/dist/pools/token-price/index.d.ts +4 -0
- package/dist/pools/token-price/index.d.ts.map +1 -0
- package/dist/pools/token-price/query.d.ts +52 -0
- package/dist/pools/token-price/query.d.ts.map +1 -0
- package/dist/pools/token-price/query.js +29 -0
- package/dist/pools/token-price/query.js.map +1 -0
- package/dist/pools/token-price/to-token-price.d.ts +14 -0
- package/dist/pools/token-price/to-token-price.d.ts.map +1 -0
- package/dist/pools/token-price/to-token-price.js +8 -0
- package/dist/pools/token-price/to-token-price.js.map +1 -0
- package/dist/pools/transactions/get-pool-transactions.d.ts +59 -17
- package/dist/pools/transactions/get-pool-transactions.d.ts.map +1 -1
- package/dist/pools/transactions/get-pool-transactions.js +7 -4
- package/dist/pools/transactions/get-pool-transactions.js.map +1 -1
- package/dist/pools/transactions/query.d.ts +6 -2
- package/dist/pools/transactions/query.d.ts.map +1 -1
- package/dist/pools/transactions/query.js +12 -13
- package/dist/pools/transactions/query.js.map +1 -1
- package/dist/pools/transactions/to-pool-transaction.d.ts +7 -0
- package/dist/pools/transactions/to-pool-transaction.d.ts.map +1 -1
- package/dist/pools/transactions/to-pool-transaction.js +3 -1
- package/dist/pools/transactions/to-pool-transaction.js.map +1 -1
- package/dist/pools/types/generated/listing-backend.js +8 -8
- package/dist/pools/types/generated/listing-backend.js.map +1 -1
- package/dist/pools/types.d.ts +53 -10
- package/dist/pools/types.d.ts.map +1 -1
- package/dist/pools/types.js +6 -4
- package/dist/pools/types.js.map +1 -1
- package/dist/solvers/instant-close/instant-close/instant-close.d.ts +8 -1
- package/dist/solvers/instant-close/instant-close/instant-close.d.ts.map +1 -1
- package/dist/solvers/instant-close/instant-close/instant-close.js +40 -32
- package/dist/solvers/instant-close/instant-close/instant-close.js.map +1 -1
- package/dist/solvers/instant-close/instant-close-bulk/instant-close-bulk.d.ts +7 -1
- package/dist/solvers/instant-close/instant-close-bulk/instant-close-bulk.d.ts.map +1 -1
- package/dist/solvers/instant-close/instant-close-bulk/instant-close-bulk.js +48 -40
- package/dist/solvers/instant-close/instant-close-bulk/instant-close-bulk.js.map +1 -1
- package/dist/solvers/instant-close/instant-close-bulk-auto/instant-close-bulk-auto.d.ts +4 -2
- package/dist/solvers/instant-close/instant-close-bulk-auto/instant-close-bulk-auto.d.ts.map +1 -1
- package/dist/solvers/instant-close/instant-close-bulk-auto/instant-close-bulk-auto.js +1 -0
- package/dist/solvers/instant-close/instant-close-bulk-auto/instant-close-bulk-auto.js.map +1 -1
- package/dist/solvers/instant-close/prepare-instant-close-params/prepare-instant-close-params.d.ts +4 -2
- package/dist/solvers/instant-close/prepare-instant-close-params/prepare-instant-close-params.d.ts.map +1 -1
- package/dist/solvers/instant-close/prepare-instant-close-params/prepare-instant-close-params.js +2 -1
- package/dist/solvers/instant-close/prepare-instant-close-params/prepare-instant-close-params.js.map +1 -1
- package/dist/solvers/instant-open/instant-open/adapters/enigma-instant-open.d.ts +2 -0
- package/dist/solvers/instant-open/instant-open/adapters/enigma-instant-open.d.ts.map +1 -1
- package/dist/solvers/instant-open/instant-open/adapters/enigma-instant-open.js +64 -56
- package/dist/solvers/instant-open/instant-open/adapters/enigma-instant-open.js.map +1 -1
- package/dist/solvers/instant-open/instant-open/adapters/rasa-instant-open.d.ts +2 -0
- package/dist/solvers/instant-open/instant-open/adapters/rasa-instant-open.d.ts.map +1 -1
- package/dist/solvers/instant-open/instant-open/adapters/rasa-instant-open.js +51 -43
- package/dist/solvers/instant-open/instant-open/adapters/rasa-instant-open.js.map +1 -1
- package/dist/solvers/instant-open/instant-open/instant-open.d.ts +6 -0
- package/dist/solvers/instant-open/instant-open/instant-open.d.ts.map +1 -1
- package/dist/solvers/instant-open/instant-open/instant-open.js.map +1 -1
- package/dist/solvers/instant-open/instant-open/types.d.ts +2 -1
- package/dist/solvers/instant-open/instant-open/types.d.ts.map +1 -1
- package/dist/solvers/instant-open/prepare-instant-open-params/prepare-instant-open-params.d.ts +3 -1
- package/dist/solvers/instant-open/prepare-instant-open-params/prepare-instant-open-params.d.ts.map +1 -1
- package/dist/solvers/instant-open/prepare-instant-open-params/prepare-instant-open-params.js +2 -1
- package/dist/solvers/instant-open/prepare-instant-open-params/prepare-instant-open-params.js.map +1 -1
- package/dist/solvers/shared/index.d.ts +1 -0
- package/dist/solvers/shared/index.d.ts.map +1 -1
- package/dist/solvers/shared/instant-delegation-preflight.d.ts +65 -0
- package/dist/solvers/shared/instant-delegation-preflight.d.ts.map +1 -0
- package/dist/solvers/shared/instant-delegation-preflight.js +34 -0
- package/dist/solvers/shared/instant-delegation-preflight.js.map +1 -0
- package/dist/symmio-contracts/abi/v0.8.6/gasless-layer.d.ts +6 -4
- package/dist/symmio-contracts/abi/v0.8.6/gasless-layer.d.ts.map +1 -1
- package/dist/symmio-contracts/abi/v0.8.6/gasless-layer.js.map +1 -1
- package/package.json +3 -3
package/dist/solvers/instant-open/prepare-instant-open-params/prepare-instant-open-params.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prepare-instant-open-params.js","names":[],"sources":["../../../../src/solvers/instant-open/prepare-instant-open-params/prepare-instant-open-params.ts"],"sourcesContent":["import { RoundingMode, toDecimal } from \"@symmio/utils/decimal\";\nimport { parseUnits, type Address, type Hex } from \"viem\";\nimport type { Config } from \"../../../core/config\";\nimport { SymmError } from \"../../../shared/errors/symm-error\";\nimport type { Compute, WriteSolverParameter } from \"../../../shared/types/properties\";\nimport { decimalPriceToWei } from \"../../../shared/utils/price\";\nimport type { FeeForUser } from \"../../../symmio-contracts/symmio/actions/get-fee-for-user\";\nimport type { EnigmaSolverInfo } from \"../../get-solver-info\";\nimport type { ApiLockedParamsBySymbolIdResponse } from \"../../types/generated/enigma-solver\";\nimport type { InstantOpenParameters } from \"../instant-open/types\";\nimport {\n assertOpenEstimateWithinSlippage,\n assertValidSlippage,\n deriveAutoSlippage,\n fetchOpenEstimatePrice,\n} from \"../shared/open-estimate-guard\";\nimport { resolveInstantOpenSizing, validateInstantOpenBalanceFunding } from \"../shared/resolve-instant-open-sizing\";\nimport { calculateTradeParams, toWeiBigInt } from \"../shared/trade-math\";\nimport { type InstantOpenMarketData, type PositionType } from \"../shared/types\";\nimport { resolveFeeRates, resolveLockedParams, resolveMarket, resolveMarkPrice, resolveSolverInfo } from \"./resolvers\";\n\n/**\n * Default solver-fee rate cap when the market publishes none: `\"0.01\"` — 1% of\n * notional, as a decimal ratio. Applied per side before wei conversion.\n */\nconst DEFAULT_SOLVER_FEE_CAP = \"0.01\";\n\n/**\n * Convert a market's solver-fee cap ratio string to its 18-decimal wei value.\n *\n * An absent cap falls back to {@link DEFAULT_SOLVER_FEE_CAP}; a present but\n * malformed string **throws** rather than silently signing zero caps — the caps\n * are immutable once `sendQuote` lands, so a bad vendor string must surface\n * before the signature, not after.\n *\n * @throws {SymmError} `INVALID_SOLVER_FEE_CAP` when the string does not parse.\n */\nfunction solverFeeCapToWei(field: \"minOpenSolverFeeCap\" | \"minCloseSolverFeeCap\", value: string | undefined): bigint {\n const cap = decimalPriceToWei(value ?? DEFAULT_SOLVER_FEE_CAP);\n if (cap === undefined) {\n throw new SymmError(\n \"validation\",\n \"INVALID_SOLVER_FEE_CAP\",\n `prepareInstantOpenParams: market ${field} \"${value}\" is not a valid decimal ratio.`,\n );\n }\n return cap;\n}\n\n/**\n * Parameters for {@link prepareInstantOpenParams} and `instantOpenAuto`.\n *\n * Required = inputs only the caller can know (wallet, session key, trade\n * intent). Optional = anything derivable from solver / price-service / on-chain\n * reads. Pre-fill an optional field to skip its fetch.\n */\n/**\n * Full-balance funding for a lowcap (Enigma) open — deploy the **entire**\n * selected collateral into the position's Virtual Account instead of typing an\n * initial margin.\n *\n * With a typed `initialMargin` the SubAccount keeps back whatever the position\n * did not need (locks + open fees + settlement), so a user who deposited `$100`\n * and hit \"Max\" saw a smaller spendable figure and left dust behind. In this\n * mode the whole balance moves to the VA and the SDK sizes the quantity **down**\n * so its locks + open fees + worst-case settlement fit inside it — nothing is\n * stranded and the figure the user deployed is the figure they picked. (Close\n * fees are charged at close from the position, so they are not sized in here.)\n *\n * Lowcap (Enigma) only: it relies on the per-position VA and the solver-fee /\n * settlement legs a majors (cross-margin) open does not have.\n */\nexport interface FullBalanceFunding {\n /** Discriminant selecting the full-balance sizing path. */\n mode: \"full-balance\";\n /** Fee-inclusive funding budget to move into the VA, in USD. */\n balance: string;\n}\n\n/**\n * Fields shared by every {@link PrepareInstantOpenParameters} funding mode.\n */\ntype PrepareInstantOpenBaseParameters = WriteSolverParameter & {\n /** Sub-account / partyA address. */\n subAccountAddress: Address;\n /** Market identification + optional pre-fetched precision metadata. */\n market: InstantOpenMarketData;\n /** Trade side. */\n positionType: PositionType;\n /** Position leverage (integer ≥ 1). */\n leverage: number;\n /**\n * Slippage tolerance percent (e.g. `5` for 5%). **Required on majors\n * (non-lowcap) solvers.** On a lowcap solver it may be omitted to\n * auto-derive: the SDK dry-runs the sized order and sets the price bound\n * to the estimated fill price plus 4% headroom (falling back to a flat 4%\n * off mark when the estimate is unavailable).\n */\n slippage?: number;\n /** Pre-fetched mark price as decimal string. When omitted, fetched via Enigma price service. */\n markPrice?: string;\n /**\n * Pre-fetched solver estimated open (fill) price as decimal string —\n * **lowcap/Enigma only**; ignored on any other solver kind. When omitted\n * on a lowcap solver, fetched via `GET /estimated-price`. Pass `null` when\n * an estimate was attempted but unavailable to skip fetching and use the fallback.\n * Feeds the settlement-loss provision, the slippage gate, and (when `slippage` is\n * omitted) the auto-slippage derivation.\n */\n estimatedOpenPrice?: string | null;\n /** Optional estimate arrival time in epoch milliseconds. Accepted for compatibility; core does not use it. */\n estimatedOpenPriceTimestamp?: number;\n /**\n * Pre-fetched solver locked params (matches `getLockedParams` return —\n * `ApiLockedParamsBySymbolIdResponse`). When supplied with all four\n * percent fields, the fetch is skipped.\n */\n lockedParamPercent?: ApiLockedParamsBySymbolIdResponse;\n /**\n * Pre-fetched on-chain fee rates (matches `getFeeForUser` return —\n * `FeeForUser`). When omitted, fetched via `getFeeForUser`.\n */\n feeRates?: FeeForUser;\n /**\n * Pre-fetched solver static fees (matches `getSolverInfo` return) —\n * **lowcap/Enigma only**; ignored on any other solver kind. When omitted on\n * a lowcap solver, fetched via `getSolverInfo` (fail-soft: an unreachable\n * `/info` prices both static legs at `\"0\"`). The flat USD legs ride the\n * `addMargin` transfer like the rate-based solver fees.\n */\n solverInfo?: EnigmaSolverInfo;\n /** Forwarded to {@link InstantOpenParameters}. */\n uuid?: string;\n /** Forwarded to {@link InstantOpenParameters}. */\n addMarginSalt?: Hex;\n /** Forwarded to {@link InstantOpenParameters}. */\n sendQuoteSalt?: Hex;\n /** Forwarded to {@link InstantOpenParameters}. */\n deadline?: bigint;\n /** Lowcap operation expiry, separate from quote expiry. Forwarded to instantOpen. */\n operationDeadline?: bigint;\n};\n\n/**\n * Parameters for {@link prepareInstantOpenParams} and `instantOpenAuto`.\n *\n * The funding source is a choice: pass a typed `initialMargin` (the classic\n * path, works on every solver), **or** `fund` with {@link FullBalanceFunding} to\n * deploy the whole balance on a lowcap (Enigma) solver. Provide **exactly one** —\n * passing both, or neither, throws. (Both are typed optional so the params\n * spread cleanly through wrapper flows; the one-of rule is enforced at runtime.)\n */\nexport type PrepareInstantOpenParameters = Compute<\n PrepareInstantOpenBaseParameters & {\n /** Collateral (USD) the user enters as initial margin. Decimal string. Provide this **or** `fund`. */\n initialMargin?: string;\n /**\n * Raw available collateral (USD decimal string). With initialMargin, automatically\n * use full-balance sizing when required funding exceeds this amount. Lowcap only;\n * must be positive and finite. initialMargin above this balance throws.\n * Omit to preserve typed sizing. Cannot accompany fund.\n */\n availableBalance?: string;\n /**\n * Deploy the entire collateral and size the quantity to fit it — lowcap\n * (Enigma) only. Provide this **or** `initialMargin`. See\n * {@link FullBalanceFunding}.\n */\n fund?: FullBalanceFunding;\n }\n>;\n\n/** Prepared order parameters plus the effective funding mode. Pass directly to instantOpen. */\nexport type PrepareInstantOpenParamsReturnType = InstantOpenParameters & {\n /** Effective sizing mode, including automatic fallback from a typed initial margin. */\n fundingMode: \"initial-margin\" | \"full-balance\";\n};\n\n/**\n * Resolve every input the {@link InstantOpenParameters} primitive needs from a\n * minimal parameter set.\n *\n * The estimate-driven steps (auto slippage, dry-run gate, solver fees,\n * settlement provision) are **lowcap-only** — a majors (non-lowcap) prepare is\n * unchanged: `slippage` required, no estimate fetch, margin = locks +\n * platform fee.\n *\n * Funding is a discriminated choice (see {@link PrepareInstantOpenParameters}):\n * a typed `initialMargin`, or `fund` ({@link FullBalanceFunding}, lowcap only)\n * to deploy the whole balance. In full-balance mode the balance is used as a\n * sizing probe: since the addMargin amount is linear in the sizing input at a\n * fixed fill price, one probe gives the exact factor that shrinks the quantity\n * so its locks + fees + settlement fit the balance, and the transfer then funds\n * the whole balance (any remainder rides as free VA margin). The sized\n * quantity is snapped down to the market's lot grid, re-checked so its costs\n * never exceed the balance (precision rounding can overshoot the linear\n * factor), and validated against the market's published quote constraints;\n * when no fill estimate is available the settlement leg is provisioned at the\n * slippage bound instead of zero — a full-balance open leaves nothing behind\n * to absorb an optimistic estimate.\n *\n * Pass `availableBalance` with `initialMargin` to automatically select full-balance\n * sizing when the typed position's required funding exceeds the raw balance.\n * An initialMargin above availableBalance throws before sizing. Otherwise, funding\n * equality preserves typed sizing. The returned `fundingMode` reports the choice.\n *\n * @param config - SDK configuration used to resolve market and account inputs.\n * @param parameters - Trade intent, optional balance budget, and prefetched inputs.\n * @returns Prepared order parameters and the effective funding mode.\n *\n * Steps:\n * 1. Validate the user's `slippage` ({@link assertValidSlippage}). On a\n * lowcap solver it may be omitted and is auto-derived: dry-run the\n * mark-sized order and set the price bound to the estimated fill plus 4%\n * headroom ({@link deriveAutoSlippage}). On majors an omitted slippage\n * throws `SLIPPAGE_REQUIRED`.\n * 2. Resolve market metadata, mark price, locked params, and fee rates —\n * concurrent fetches with caller-supplied fields short-circuiting.\n * 3. Run {@link calculateTradeParams} to derive `requestedOpenPrice`,\n * `quantity`, `cva`, `lf`, `partyAmm`, `partyBmm`, `notional`.\n * 4. Dry-run the sized order and reject when the expected fill deviates from\n * mark beyond the slippage tolerance\n * ({@link assertOpenEstimateWithinSlippage}; skipped when the estimate is\n * unavailable, reusing the caller-supplied `estimatedOpenPrice` or the\n * auto-slippage estimate when one exists).\n * 5. Run {@link computePlatformFee}, {@link calculateSolverFees}, and\n * {@link calculateExpectedSettlementLoss}, then {@link calculateMargin} to\n * derive the `addMargin` amount. The open funds only the **open-side** legs\n * plus the open-price settlement from the VA, so the transfer funds\n * `locks + platformOpenFee + openSolverFee + staticSolverFeeOpen +\n * expectedSettlementLoss` (the static open leg is a flat USD amount from the\n * solver's `/info` config). The close-side legs (platform close, solver\n * close, static close) are **charged at close** from the position — the\n * contract collects them at close execution — so they are not pre-funded\n * here; pre-funding them would strand collateral in the VA. In full-balance\n * mode the quantity is rescaled to fit the balance — the static open leg is\n * carved off the budget before the linear solve — and the `addMargin` amount\n * is the whole balance instead.\n * 6. Convert all final values to 18-decimal-wei `bigint`.\n *\n * @throws {SymmError} `INVALID_SLIPPAGE` / `SLIPPAGE_REQUIRED` /\n * `SLIPPAGE_EXCEEDED` / `AMBIGUOUS_FUNDING` / `FULL_BALANCE_UNSUPPORTED` /\n * `INITIAL_MARGIN_REQUIRED` / `QUOTE_CONSTRAINT_VIOLATED` /\n * `RESOLVE_MARKET_NOT_FOUND` /\n * `RESOLVE_MARKET_METADATA_INCOMPLETE` /\n * `RESOLVE_MARK_PRICE_NOT_FOUND` / `INVALID_TRADE_PARAMETERS` /\n * `INVALID_SOLVER_FEE_CAP` for invalid inputs or an out-of-tolerance fill.\n */\nexport async function prepareInstantOpenParams(\n config: Config,\n parameters: PrepareInstantOpenParameters,\n): Promise<PrepareInstantOpenParamsReturnType> {\n // Reject a malformed user slippage before any network work. An omitted\n // slippage is auto-derived from the dry-run estimate after sizing.\n if (parameters.slippage !== undefined) assertValidSlippage(parameters.slippage);\n\n /**\n * The estimate-driven behaviors — auto slippage, the dry-run gate, solver\n * fees, and the settlement provision — exist only on lowcap (Enigma)\n * solvers. On majors the flow is unchanged: slippage is required, no\n * estimate is fetched, and the margin carries locks + the platform open fee\n * only (the platform close fee is charged at close).\n */\n const isLowcap = config.getSolver({ chainId: parameters.chainId, solverId: parameters.solverId }).id === \"enigma\";\n if (parameters.slippage === undefined && !isLowcap) {\n throw new SymmError(\n \"validation\",\n \"SLIPPAGE_REQUIRED\",\n \"prepareInstantOpenParams: slippage is required on this solver — auto-derived slippage is lowcap-only.\",\n );\n }\n\n // Full-balance funding relies on the per-position VA plus the solver-fee and\n // settlement legs a majors open does not carry, so it is lowcap-only. When it\n // is off, a typed initialMargin is mandatory (the union guarantees this for TS\n // callers; this guard covers plain-JS ones).\n const isFullBalance = parameters.fund !== undefined;\n validateInstantOpenBalanceFunding({ ...parameters, isLowcap });\n const canSizeFullBalance = isFullBalance || parameters.availableBalance !== undefined;\n if (isFullBalance && parameters.initialMargin !== undefined) {\n throw new SymmError(\n \"validation\",\n \"AMBIGUOUS_FUNDING\",\n \"prepareInstantOpenParams: pass exactly one of initialMargin or fund, not both.\",\n );\n }\n if (isFullBalance && !isLowcap) {\n throw new SymmError(\n \"validation\",\n \"FULL_BALANCE_UNSUPPORTED\",\n \"prepareInstantOpenParams: full-balance funding is lowcap (Enigma) only — pass initialMargin on this solver.\",\n );\n }\n if (!isFullBalance && parameters.initialMargin === undefined) {\n throw new SymmError(\n \"validation\",\n \"INITIAL_MARGIN_REQUIRED\",\n \"prepareInstantOpenParams: pass initialMargin, or fund: { mode: 'full-balance', balance } on a lowcap solver.\",\n );\n }\n // The sizing input: the typed margin, or — in full-balance mode — the whole\n // balance used as a probe. The final quantity is rescaled from it below.\n const initialMargin = isFullBalance ? parameters.fund!.balance : parameters.initialMargin!;\n\n /** Fee caps only exist on the v0.8.6 quote API — don't force a market fetch for them on a legacy chain. */\n const needsSolverFeeCaps = config.getChainConfig(parameters.chainId).contractsVersion === \"0.8.6\";\n\n const market = await resolveMarket(config, {\n chainId: parameters.chainId,\n solverId: parameters.solverId,\n marketId: parameters.market.id,\n marketName: parameters.market.name,\n pricePrecision: parameters.market.pricePrecision,\n quantityPrecision: parameters.market.quantityPrecision,\n minOpenSolverFeeCap: parameters.market.minOpenSolverFeeCap,\n minCloseSolverFeeCap: parameters.market.minCloseSolverFeeCap,\n includeSolverFeeCaps: needsSolverFeeCaps,\n hedgerFeeOpen: parameters.market.hedgerFeeOpen,\n hedgerFeeClose: parameters.market.hedgerFeeClose,\n hedgerFeeCloseEarlyRate: parameters.market.hedgerFeeCloseEarlyRate,\n hedgerFeeCloseEarlyThreshold: parameters.market.hedgerFeeCloseEarlyThreshold,\n hedgerFeeCloseStandardThreshold: parameters.market.hedgerFeeCloseStandardThreshold,\n /** Lowcap only: the solver charges its fees from the VA, so `addMargin` must fund them. */\n includeHedgerFees: isLowcap,\n /** Explicit or automatic full-balance sizing needs the lot grid and published floors. */\n includeQuoteConstraints: canSizeFullBalance,\n minAcceptablePortionLf: parameters.market.minAcceptablePortionLf,\n minAcceptableQuoteValue: parameters.market.minAcceptableQuoteValue,\n maxNotionalValue: parameters.market.maxNotionalValue,\n minNotionalValue: parameters.market.minNotionalValue,\n maxQuantity: parameters.market.maxQuantity,\n lotSize: parameters.market.lotSize,\n });\n const [markPrice, lockedParams, feeRates, staticFees] = await Promise.all([\n resolveMarkPrice(config, {\n chainId: parameters.chainId,\n solverId: parameters.solverId,\n marketName: market.name,\n markPrice: parameters.markPrice,\n }),\n resolveLockedParams(config, {\n chainId: parameters.chainId,\n solverId: parameters.solverId,\n marketName: market.name,\n leverage: parameters.leverage,\n lockedParamPercent: parameters.lockedParamPercent,\n }),\n resolveFeeRates(config, {\n chainId: parameters.chainId,\n subAccountAddress: parameters.subAccountAddress,\n marketId: parameters.market.id,\n feeRates: parameters.feeRates,\n }),\n // Static solver fees are a lowcap leg — a majors open never fetches them.\n isLowcap\n ? resolveSolverInfo(config, {\n chainId: parameters.chainId,\n solverId: parameters.solverId,\n solverInfo: parameters.solverInfo,\n })\n : Promise.resolve({ staticSolverFeeOpen: \"0\", staticSolverFeeClose: \"0\" }),\n ]);\n\n const calculationInput = {\n markPrice,\n positionType: parameters.positionType,\n userInput: initialMargin,\n inputField: \"PRICE\" as const,\n leverage: parameters.leverage,\n pricePrecision: market.pricePrecision,\n quantityPrecision: market.quantityPrecision,\n cvaPercent: lockedParams.cva,\n lfPercent: lockedParams.lf,\n partyAmmPercent: lockedParams.partyAmm,\n partyBmmPercent: lockedParams.partyBmm,\n };\n\n // Auto slippage: dry-run the mark-sized order (quantity is slippage-\n // independent) and set the price bound to the estimated fill plus 4%\n // headroom, re-expressed as a percent off mark. A caller-supplied\n // `estimatedOpenPrice` short-circuits the fetch; either way the estimate is\n // reused by the gate and the settlement provision so the solver is asked at\n // most once.\n let slippage = parameters.slippage;\n let expectedFillPrice = isLowcap ? (parameters.estimatedOpenPrice ?? undefined) : undefined;\n let needsEstimate = isLowcap && parameters.estimatedOpenPrice === undefined;\n if (slippage === undefined) {\n const sized = calculateTradeParams({ ...calculationInput, slippage: 0 });\n if (!sized) {\n throw new SymmError(\n \"validation\",\n \"INVALID_TRADE_PARAMETERS\",\n \"Invalid trade parameters: markPrice or initialMargin is zero/NaN.\",\n );\n }\n if (needsEstimate) {\n needsEstimate = false;\n expectedFillPrice = await fetchOpenEstimatePrice(config, {\n chainId: parameters.chainId,\n solverId: parameters.solverId,\n symbolId: parameters.market.id,\n positionType: parameters.positionType,\n quantity: sized.quantity,\n markPrice,\n });\n }\n slippage = deriveAutoSlippage({ markPrice, expectedFillPrice, positionType: parameters.positionType });\n }\n\n const tradeCalc = calculateTradeParams({ ...calculationInput, slippage });\n if (!tradeCalc) {\n throw new SymmError(\n \"validation\",\n \"INVALID_TRADE_PARAMETERS\",\n \"Invalid trade parameters: markPrice or initialMargin is zero/NaN.\",\n );\n }\n\n // The expected fill price funds the settlement-loss provision and feeds the\n // slippage gate. Fetch it once here when neither the caller nor the\n // auto-slippage path supplied it — pass `estimatedOpenPrice` to skip the\n // round-trip on latency-sensitive submits.\n if (needsEstimate) {\n expectedFillPrice = await fetchOpenEstimatePrice(config, {\n chainId: parameters.chainId,\n solverId: parameters.solverId,\n symbolId: parameters.market.id,\n positionType: parameters.positionType,\n quantity: tradeCalc.quantity,\n markPrice,\n });\n }\n\n // Dry-run gate: reject early when the expected fill deviates from mark\n // beyond the user's slippage — the solver would reject the quote anyway.\n // Best-effort: an unavailable estimate skips the gate, never blocks the open.\n if (expectedFillPrice !== undefined) {\n await assertOpenEstimateWithinSlippage(config, {\n chainId: parameters.chainId,\n solverId: parameters.solverId,\n symbolId: parameters.market.id,\n positionType: parameters.positionType,\n quantity: tradeCalc.quantity,\n markPrice,\n slippage,\n expectedFillPrice,\n });\n }\n\n // The cost legs a sized order pays. The open funds the OPEN-side legs and the\n // open-price settlement from the VA (they ride the SubAccount → VA transfer);\n // the close-side legs are charged at close from the position, not funded here.\n // Majors carry locks + the platform open fee only. The shared cost model\n // (`computeInstantOpenCosts`) is also what `getInstantOpenFees` prices, so the\n // preview equals what the open charges by construction.\n const {\n trade: finalTrade,\n marginAmount,\n fundingMode,\n } = resolveInstantOpenSizing({\n trade: tradeCalc,\n calculationInput: { ...calculationInput, slippage },\n isLowcap,\n fund: parameters.fund,\n availableBalance: parameters.availableBalance,\n expectedFillPrice,\n feeRates,\n hedgerFeeOpen: market.hedgerFeeOpen,\n staticSolverFeeOpen: staticFees.staticSolverFeeOpen,\n constraints: {\n minAcceptablePortionLf: market.minAcceptablePortionLf,\n minAcceptableQuoteValue: market.minAcceptableQuoteValue,\n maxNotionalValue: market.maxNotionalValue,\n minNotionalValue: market.minNotionalValue,\n maxQuantity: market.maxQuantity,\n lotSize: market.lotSize,\n },\n });\n\n return {\n fundingMode,\n chainId: parameters.chainId,\n /**\n * Carried through deliberately: `instantOpen` resolves the solver from it to\n * fill `partyBsWhiteList` — which is signed into the EIP-712 payload — and to\n * pick the submit URL. Dropping it here would sign against the default\n * solver's address while the quote was priced and sized for another.\n */\n solverId: parameters.solverId,\n from: parameters.from,\n subAccountAddress: parameters.subAccountAddress,\n marketId: parameters.market.id,\n positionType: parameters.positionType,\n markPrice: toWeiBigInt(markPrice),\n order: {\n price: toWeiBigInt(finalTrade.requestedOpenPrice),\n quantity: toWeiBigInt(finalTrade.quantity),\n },\n lockedParam: {\n cva: toWeiBigInt(finalTrade.cva),\n lf: toWeiBigInt(finalTrade.lf),\n partyAmm: toWeiBigInt(finalTrade.partyAmm),\n partyBmm: toWeiBigInt(finalTrade.partyBmm),\n },\n margin: {\n // Shift the decimal point without a Decimal multiplication, whose global\n // significant-digit precision can round a full balance up by one wei.\n amount: parseUnits(toDecimal(marginAmount).toFixed(18, RoundingMode.ROUND_DOWN), 18),\n },\n /** Only meaningful on a v0.8.6 chain — absent on v0.8.5, whose flow signs the legacy call. */\n ...(needsSolverFeeCaps\n ? {\n solverFeeCaps: {\n openRateCap: solverFeeCapToWei(\"minOpenSolverFeeCap\", market.minOpenSolverFeeCap),\n closeRateCap: solverFeeCapToWei(\"minCloseSolverFeeCap\", market.minCloseSolverFeeCap),\n },\n }\n : {}),\n uuid: parameters.uuid,\n addMarginSalt: parameters.addMarginSalt,\n sendQuoteSalt: parameters.sendQuoteSalt,\n deadline: parameters.deadline,\n operationDeadline: parameters.operationDeadline,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"prepare-instant-open-params.js","names":[],"sources":["../../../../src/solvers/instant-open/prepare-instant-open-params/prepare-instant-open-params.ts"],"sourcesContent":["import { RoundingMode, toDecimal } from \"@symmio/utils/decimal\";\nimport { parseUnits, type Address, type Hex } from \"viem\";\nimport type { Config } from \"../../../core/config\";\nimport { SymmError } from \"../../../shared/errors/symm-error\";\nimport type { Compute, WriteSolverParameter } from \"../../../shared/types/properties\";\nimport { decimalPriceToWei } from \"../../../shared/utils/price\";\nimport type { FeeForUser } from \"../../../symmio-contracts/symmio/actions/get-fee-for-user\";\nimport type { EnigmaSolverInfo } from \"../../get-solver-info\";\nimport type { InstantDelegationPreflightParameter } from \"../../shared/instant-delegation-preflight\";\nimport type { ApiLockedParamsBySymbolIdResponse } from \"../../types/generated/enigma-solver\";\nimport type { InstantOpenParameters } from \"../instant-open/types\";\nimport {\n assertOpenEstimateWithinSlippage,\n assertValidSlippage,\n deriveAutoSlippage,\n fetchOpenEstimatePrice,\n} from \"../shared/open-estimate-guard\";\nimport { resolveInstantOpenSizing, validateInstantOpenBalanceFunding } from \"../shared/resolve-instant-open-sizing\";\nimport { calculateTradeParams, toWeiBigInt } from \"../shared/trade-math\";\nimport { type InstantOpenMarketData, type PositionType } from \"../shared/types\";\nimport { resolveFeeRates, resolveLockedParams, resolveMarket, resolveMarkPrice, resolveSolverInfo } from \"./resolvers\";\n\n/**\n * Default solver-fee rate cap when the market publishes none: `\"0.01\"` — 1% of\n * notional, as a decimal ratio. Applied per side before wei conversion.\n */\nconst DEFAULT_SOLVER_FEE_CAP = \"0.01\";\n\n/**\n * Convert a market's solver-fee cap ratio string to its 18-decimal wei value.\n *\n * An absent cap falls back to {@link DEFAULT_SOLVER_FEE_CAP}; a present but\n * malformed string **throws** rather than silently signing zero caps — the caps\n * are immutable once `sendQuote` lands, so a bad vendor string must surface\n * before the signature, not after.\n *\n * @throws {SymmError} `INVALID_SOLVER_FEE_CAP` when the string does not parse.\n */\nfunction solverFeeCapToWei(field: \"minOpenSolverFeeCap\" | \"minCloseSolverFeeCap\", value: string | undefined): bigint {\n const cap = decimalPriceToWei(value ?? DEFAULT_SOLVER_FEE_CAP);\n if (cap === undefined) {\n throw new SymmError(\n \"validation\",\n \"INVALID_SOLVER_FEE_CAP\",\n `prepareInstantOpenParams: market ${field} \"${value}\" is not a valid decimal ratio.`,\n );\n }\n return cap;\n}\n\n/**\n * Parameters for {@link prepareInstantOpenParams} and `instantOpenAuto`.\n *\n * Required = inputs only the caller can know (wallet, session key, trade\n * intent). Optional = anything derivable from solver / price-service / on-chain\n * reads. Pre-fill an optional field to skip its fetch.\n */\n/**\n * Full-balance funding for a lowcap (Enigma) open — deploy the **entire**\n * selected collateral into the position's Virtual Account instead of typing an\n * initial margin.\n *\n * With a typed `initialMargin` the SubAccount keeps back whatever the position\n * did not need (locks + open fees + settlement), so a user who deposited `$100`\n * and hit \"Max\" saw a smaller spendable figure and left dust behind. In this\n * mode the whole balance moves to the VA and the SDK sizes the quantity **down**\n * so its locks + open fees + worst-case settlement fit inside it — nothing is\n * stranded and the figure the user deployed is the figure they picked. (Close\n * fees are charged at close from the position, so they are not sized in here.)\n *\n * Lowcap (Enigma) only: it relies on the per-position VA and the solver-fee /\n * settlement legs a majors (cross-margin) open does not have.\n */\nexport interface FullBalanceFunding {\n /** Discriminant selecting the full-balance sizing path. */\n mode: \"full-balance\";\n /** Fee-inclusive funding budget to move into the VA, in USD. */\n balance: string;\n}\n\n/**\n * Fields shared by every {@link PrepareInstantOpenParameters} funding mode.\n * `preflightDelegation` is forwarded to {@link InstantOpenParameters}.\n */\ntype PrepareInstantOpenBaseParameters = WriteSolverParameter &\n InstantDelegationPreflightParameter & {\n /** Sub-account / partyA address. */\n subAccountAddress: Address;\n /** Market identification + optional pre-fetched precision metadata. */\n market: InstantOpenMarketData;\n /** Trade side. */\n positionType: PositionType;\n /** Position leverage (integer ≥ 1). */\n leverage: number;\n /**\n * Slippage tolerance percent (e.g. `5` for 5%). **Required on majors\n * (non-lowcap) solvers.** On a lowcap solver it may be omitted to\n * auto-derive: the SDK dry-runs the sized order and sets the price bound\n * to the estimated fill price plus 4% headroom (falling back to a flat 4%\n * off mark when the estimate is unavailable).\n */\n slippage?: number;\n /** Pre-fetched mark price as decimal string. When omitted, fetched via Enigma price service. */\n markPrice?: string;\n /**\n * Pre-fetched solver estimated open (fill) price as decimal string —\n * **lowcap/Enigma only**; ignored on any other solver kind. When omitted\n * on a lowcap solver, fetched via `GET /estimated-price`. Pass `null` when\n * an estimate was attempted but unavailable to skip fetching and use the fallback.\n * Feeds the settlement-loss provision, the slippage gate, and (when `slippage` is\n * omitted) the auto-slippage derivation.\n */\n estimatedOpenPrice?: string | null;\n /** Optional estimate arrival time in epoch milliseconds. Accepted for compatibility; core does not use it. */\n estimatedOpenPriceTimestamp?: number;\n /**\n * Pre-fetched solver locked params (matches `getLockedParams` return —\n * `ApiLockedParamsBySymbolIdResponse`). When supplied with all four\n * percent fields, the fetch is skipped.\n */\n lockedParamPercent?: ApiLockedParamsBySymbolIdResponse;\n /**\n * Pre-fetched on-chain fee rates (matches `getFeeForUser` return —\n * `FeeForUser`). When omitted, fetched via `getFeeForUser`.\n */\n feeRates?: FeeForUser;\n /**\n * Pre-fetched solver static fees (matches `getSolverInfo` return) —\n * **lowcap/Enigma only**; ignored on any other solver kind. When omitted on\n * a lowcap solver, fetched via `getSolverInfo` (fail-soft: an unreachable\n * `/info` prices both static legs at `\"0\"`). The flat USD legs ride the\n * `addMargin` transfer like the rate-based solver fees.\n */\n solverInfo?: EnigmaSolverInfo;\n /** Forwarded to {@link InstantOpenParameters}. */\n uuid?: string;\n /** Forwarded to {@link InstantOpenParameters}. */\n addMarginSalt?: Hex;\n /** Forwarded to {@link InstantOpenParameters}. */\n sendQuoteSalt?: Hex;\n /** Forwarded to {@link InstantOpenParameters}. */\n deadline?: bigint;\n /** Lowcap operation expiry, separate from quote expiry. Forwarded to instantOpen. */\n operationDeadline?: bigint;\n };\n\n/**\n * Parameters for {@link prepareInstantOpenParams} and `instantOpenAuto`.\n *\n * The funding source is a choice: pass a typed `initialMargin` (the classic\n * path, works on every solver), **or** `fund` with {@link FullBalanceFunding} to\n * deploy the whole balance on a lowcap (Enigma) solver. Provide **exactly one** —\n * passing both, or neither, throws. (Both are typed optional so the params\n * spread cleanly through wrapper flows; the one-of rule is enforced at runtime.)\n */\nexport type PrepareInstantOpenParameters = Compute<\n PrepareInstantOpenBaseParameters & {\n /** Collateral (USD) the user enters as initial margin. Decimal string. Provide this **or** `fund`. */\n initialMargin?: string;\n /**\n * Raw available collateral (USD decimal string). With initialMargin, automatically\n * use full-balance sizing when required funding exceeds this amount. Lowcap only;\n * must be positive and finite. initialMargin above this balance throws.\n * Omit to preserve typed sizing. Cannot accompany fund.\n */\n availableBalance?: string;\n /**\n * Deploy the entire collateral and size the quantity to fit it — lowcap\n * (Enigma) only. Provide this **or** `initialMargin`. See\n * {@link FullBalanceFunding}.\n */\n fund?: FullBalanceFunding;\n }\n>;\n\n/** Prepared order parameters plus the effective funding mode. Pass directly to instantOpen. */\nexport type PrepareInstantOpenParamsReturnType = InstantOpenParameters & {\n /** Effective sizing mode, including automatic fallback from a typed initial margin. */\n fundingMode: \"initial-margin\" | \"full-balance\";\n};\n\n/**\n * Resolve every input the {@link InstantOpenParameters} primitive needs from a\n * minimal parameter set.\n *\n * The estimate-driven steps (auto slippage, dry-run gate, solver fees,\n * settlement provision) are **lowcap-only** — a majors (non-lowcap) prepare is\n * unchanged: `slippage` required, no estimate fetch, margin = locks +\n * platform fee.\n *\n * Funding is a discriminated choice (see {@link PrepareInstantOpenParameters}):\n * a typed `initialMargin`, or `fund` ({@link FullBalanceFunding}, lowcap only)\n * to deploy the whole balance. In full-balance mode the balance is used as a\n * sizing probe: since the addMargin amount is linear in the sizing input at a\n * fixed fill price, one probe gives the exact factor that shrinks the quantity\n * so its locks + fees + settlement fit the balance, and the transfer then funds\n * the whole balance (any remainder rides as free VA margin). The sized\n * quantity is snapped down to the market's lot grid, re-checked so its costs\n * never exceed the balance (precision rounding can overshoot the linear\n * factor), and validated against the market's published quote constraints;\n * when no fill estimate is available the settlement leg is provisioned at the\n * slippage bound instead of zero — a full-balance open leaves nothing behind\n * to absorb an optimistic estimate.\n *\n * Pass `availableBalance` with `initialMargin` to automatically select full-balance\n * sizing when the typed position's required funding exceeds the raw balance.\n * An initialMargin above availableBalance throws before sizing. Otherwise, funding\n * equality preserves typed sizing. The returned `fundingMode` reports the choice.\n *\n * @param config - SDK configuration used to resolve market and account inputs.\n * @param parameters - Trade intent, optional balance budget, and prefetched inputs.\n * @returns Prepared order parameters and the effective funding mode.\n *\n * Steps:\n * 1. Validate the user's `slippage` ({@link assertValidSlippage}). On a\n * lowcap solver it may be omitted and is auto-derived: dry-run the\n * mark-sized order and set the price bound to the estimated fill plus 4%\n * headroom ({@link deriveAutoSlippage}). On majors an omitted slippage\n * throws `SLIPPAGE_REQUIRED`.\n * 2. Resolve market metadata, mark price, locked params, and fee rates —\n * concurrent fetches with caller-supplied fields short-circuiting.\n * 3. Run {@link calculateTradeParams} to derive `requestedOpenPrice`,\n * `quantity`, `cva`, `lf`, `partyAmm`, `partyBmm`, `notional`.\n * 4. Dry-run the sized order and reject when the expected fill deviates from\n * mark beyond the slippage tolerance\n * ({@link assertOpenEstimateWithinSlippage}; skipped when the estimate is\n * unavailable, reusing the caller-supplied `estimatedOpenPrice` or the\n * auto-slippage estimate when one exists).\n * 5. Run {@link computePlatformFee}, {@link calculateSolverFees}, and\n * {@link calculateExpectedSettlementLoss}, then {@link calculateMargin} to\n * derive the `addMargin` amount. The open funds only the **open-side** legs\n * plus the open-price settlement from the VA, so the transfer funds\n * `locks + platformOpenFee + openSolverFee + staticSolverFeeOpen +\n * expectedSettlementLoss` (the static open leg is a flat USD amount from the\n * solver's `/info` config). The close-side legs (platform close, solver\n * close, static close) are **charged at close** from the position — the\n * contract collects them at close execution — so they are not pre-funded\n * here; pre-funding them would strand collateral in the VA. In full-balance\n * mode the quantity is rescaled to fit the balance — the static open leg is\n * carved off the budget before the linear solve — and the `addMargin` amount\n * is the whole balance instead.\n * 6. Convert all final values to 18-decimal-wei `bigint`.\n *\n * @throws {SymmError} `INVALID_SLIPPAGE` / `SLIPPAGE_REQUIRED` /\n * `SLIPPAGE_EXCEEDED` / `AMBIGUOUS_FUNDING` / `FULL_BALANCE_UNSUPPORTED` /\n * `INITIAL_MARGIN_REQUIRED` / `QUOTE_CONSTRAINT_VIOLATED` /\n * `RESOLVE_MARKET_NOT_FOUND` /\n * `RESOLVE_MARKET_METADATA_INCOMPLETE` /\n * `RESOLVE_MARK_PRICE_NOT_FOUND` / `INVALID_TRADE_PARAMETERS` /\n * `INVALID_SOLVER_FEE_CAP` for invalid inputs or an out-of-tolerance fill.\n */\nexport async function prepareInstantOpenParams(\n config: Config,\n parameters: PrepareInstantOpenParameters,\n): Promise<PrepareInstantOpenParamsReturnType> {\n // Reject a malformed user slippage before any network work. An omitted\n // slippage is auto-derived from the dry-run estimate after sizing.\n if (parameters.slippage !== undefined) assertValidSlippage(parameters.slippage);\n\n /**\n * The estimate-driven behaviors — auto slippage, the dry-run gate, solver\n * fees, and the settlement provision — exist only on lowcap (Enigma)\n * solvers. On majors the flow is unchanged: slippage is required, no\n * estimate is fetched, and the margin carries locks + the platform open fee\n * only (the platform close fee is charged at close).\n */\n const isLowcap = config.getSolver({ chainId: parameters.chainId, solverId: parameters.solverId }).id === \"enigma\";\n if (parameters.slippage === undefined && !isLowcap) {\n throw new SymmError(\n \"validation\",\n \"SLIPPAGE_REQUIRED\",\n \"prepareInstantOpenParams: slippage is required on this solver — auto-derived slippage is lowcap-only.\",\n );\n }\n\n // Full-balance funding relies on the per-position VA plus the solver-fee and\n // settlement legs a majors open does not carry, so it is lowcap-only. When it\n // is off, a typed initialMargin is mandatory (the union guarantees this for TS\n // callers; this guard covers plain-JS ones).\n const isFullBalance = parameters.fund !== undefined;\n validateInstantOpenBalanceFunding({ ...parameters, isLowcap });\n const canSizeFullBalance = isFullBalance || parameters.availableBalance !== undefined;\n if (isFullBalance && parameters.initialMargin !== undefined) {\n throw new SymmError(\n \"validation\",\n \"AMBIGUOUS_FUNDING\",\n \"prepareInstantOpenParams: pass exactly one of initialMargin or fund, not both.\",\n );\n }\n if (isFullBalance && !isLowcap) {\n throw new SymmError(\n \"validation\",\n \"FULL_BALANCE_UNSUPPORTED\",\n \"prepareInstantOpenParams: full-balance funding is lowcap (Enigma) only — pass initialMargin on this solver.\",\n );\n }\n if (!isFullBalance && parameters.initialMargin === undefined) {\n throw new SymmError(\n \"validation\",\n \"INITIAL_MARGIN_REQUIRED\",\n \"prepareInstantOpenParams: pass initialMargin, or fund: { mode: 'full-balance', balance } on a lowcap solver.\",\n );\n }\n // The sizing input: the typed margin, or — in full-balance mode — the whole\n // balance used as a probe. The final quantity is rescaled from it below.\n const initialMargin = isFullBalance ? parameters.fund!.balance : parameters.initialMargin!;\n\n /** Fee caps only exist on the v0.8.6 quote API — don't force a market fetch for them on a legacy chain. */\n const needsSolverFeeCaps = config.getChainConfig(parameters.chainId).contractsVersion === \"0.8.6\";\n\n const market = await resolveMarket(config, {\n chainId: parameters.chainId,\n solverId: parameters.solverId,\n marketId: parameters.market.id,\n marketName: parameters.market.name,\n pricePrecision: parameters.market.pricePrecision,\n quantityPrecision: parameters.market.quantityPrecision,\n minOpenSolverFeeCap: parameters.market.minOpenSolverFeeCap,\n minCloseSolverFeeCap: parameters.market.minCloseSolverFeeCap,\n includeSolverFeeCaps: needsSolverFeeCaps,\n hedgerFeeOpen: parameters.market.hedgerFeeOpen,\n hedgerFeeClose: parameters.market.hedgerFeeClose,\n hedgerFeeCloseEarlyRate: parameters.market.hedgerFeeCloseEarlyRate,\n hedgerFeeCloseEarlyThreshold: parameters.market.hedgerFeeCloseEarlyThreshold,\n hedgerFeeCloseStandardThreshold: parameters.market.hedgerFeeCloseStandardThreshold,\n /** Lowcap only: the solver charges its fees from the VA, so `addMargin` must fund them. */\n includeHedgerFees: isLowcap,\n /** Explicit or automatic full-balance sizing needs the lot grid and published floors. */\n includeQuoteConstraints: canSizeFullBalance,\n minAcceptablePortionLf: parameters.market.minAcceptablePortionLf,\n minAcceptableQuoteValue: parameters.market.minAcceptableQuoteValue,\n maxNotionalValue: parameters.market.maxNotionalValue,\n minNotionalValue: parameters.market.minNotionalValue,\n maxQuantity: parameters.market.maxQuantity,\n lotSize: parameters.market.lotSize,\n });\n const [markPrice, lockedParams, feeRates, staticFees] = await Promise.all([\n resolveMarkPrice(config, {\n chainId: parameters.chainId,\n solverId: parameters.solverId,\n marketName: market.name,\n markPrice: parameters.markPrice,\n }),\n resolveLockedParams(config, {\n chainId: parameters.chainId,\n solverId: parameters.solverId,\n marketName: market.name,\n leverage: parameters.leverage,\n lockedParamPercent: parameters.lockedParamPercent,\n }),\n resolveFeeRates(config, {\n chainId: parameters.chainId,\n subAccountAddress: parameters.subAccountAddress,\n marketId: parameters.market.id,\n feeRates: parameters.feeRates,\n }),\n // Static solver fees are a lowcap leg — a majors open never fetches them.\n isLowcap\n ? resolveSolverInfo(config, {\n chainId: parameters.chainId,\n solverId: parameters.solverId,\n solverInfo: parameters.solverInfo,\n })\n : Promise.resolve({ staticSolverFeeOpen: \"0\", staticSolverFeeClose: \"0\" }),\n ]);\n\n const calculationInput = {\n markPrice,\n positionType: parameters.positionType,\n userInput: initialMargin,\n inputField: \"PRICE\" as const,\n leverage: parameters.leverage,\n pricePrecision: market.pricePrecision,\n quantityPrecision: market.quantityPrecision,\n cvaPercent: lockedParams.cva,\n lfPercent: lockedParams.lf,\n partyAmmPercent: lockedParams.partyAmm,\n partyBmmPercent: lockedParams.partyBmm,\n };\n\n // Auto slippage: dry-run the mark-sized order (quantity is slippage-\n // independent) and set the price bound to the estimated fill plus 4%\n // headroom, re-expressed as a percent off mark. A caller-supplied\n // `estimatedOpenPrice` short-circuits the fetch; either way the estimate is\n // reused by the gate and the settlement provision so the solver is asked at\n // most once.\n let slippage = parameters.slippage;\n let expectedFillPrice = isLowcap ? (parameters.estimatedOpenPrice ?? undefined) : undefined;\n let needsEstimate = isLowcap && parameters.estimatedOpenPrice === undefined;\n if (slippage === undefined) {\n const sized = calculateTradeParams({ ...calculationInput, slippage: 0 });\n if (!sized) {\n throw new SymmError(\n \"validation\",\n \"INVALID_TRADE_PARAMETERS\",\n \"Invalid trade parameters: markPrice or initialMargin is zero/NaN.\",\n );\n }\n if (needsEstimate) {\n needsEstimate = false;\n expectedFillPrice = await fetchOpenEstimatePrice(config, {\n chainId: parameters.chainId,\n solverId: parameters.solverId,\n symbolId: parameters.market.id,\n positionType: parameters.positionType,\n quantity: sized.quantity,\n markPrice,\n });\n }\n slippage = deriveAutoSlippage({ markPrice, expectedFillPrice, positionType: parameters.positionType });\n }\n\n const tradeCalc = calculateTradeParams({ ...calculationInput, slippage });\n if (!tradeCalc) {\n throw new SymmError(\n \"validation\",\n \"INVALID_TRADE_PARAMETERS\",\n \"Invalid trade parameters: markPrice or initialMargin is zero/NaN.\",\n );\n }\n\n // The expected fill price funds the settlement-loss provision and feeds the\n // slippage gate. Fetch it once here when neither the caller nor the\n // auto-slippage path supplied it — pass `estimatedOpenPrice` to skip the\n // round-trip on latency-sensitive submits.\n if (needsEstimate) {\n expectedFillPrice = await fetchOpenEstimatePrice(config, {\n chainId: parameters.chainId,\n solverId: parameters.solverId,\n symbolId: parameters.market.id,\n positionType: parameters.positionType,\n quantity: tradeCalc.quantity,\n markPrice,\n });\n }\n\n // Dry-run gate: reject early when the expected fill deviates from mark\n // beyond the user's slippage — the solver would reject the quote anyway.\n // Best-effort: an unavailable estimate skips the gate, never blocks the open.\n if (expectedFillPrice !== undefined) {\n await assertOpenEstimateWithinSlippage(config, {\n chainId: parameters.chainId,\n solverId: parameters.solverId,\n symbolId: parameters.market.id,\n positionType: parameters.positionType,\n quantity: tradeCalc.quantity,\n markPrice,\n slippage,\n expectedFillPrice,\n });\n }\n\n // The cost legs a sized order pays. The open funds the OPEN-side legs and the\n // open-price settlement from the VA (they ride the SubAccount → VA transfer);\n // the close-side legs are charged at close from the position, not funded here.\n // Majors carry locks + the platform open fee only. The shared cost model\n // (`computeInstantOpenCosts`) is also what `getInstantOpenFees` prices, so the\n // preview equals what the open charges by construction.\n const {\n trade: finalTrade,\n marginAmount,\n fundingMode,\n } = resolveInstantOpenSizing({\n trade: tradeCalc,\n calculationInput: { ...calculationInput, slippage },\n isLowcap,\n fund: parameters.fund,\n availableBalance: parameters.availableBalance,\n expectedFillPrice,\n feeRates,\n hedgerFeeOpen: market.hedgerFeeOpen,\n staticSolverFeeOpen: staticFees.staticSolverFeeOpen,\n constraints: {\n minAcceptablePortionLf: market.minAcceptablePortionLf,\n minAcceptableQuoteValue: market.minAcceptableQuoteValue,\n maxNotionalValue: market.maxNotionalValue,\n minNotionalValue: market.minNotionalValue,\n maxQuantity: market.maxQuantity,\n lotSize: market.lotSize,\n },\n });\n\n return {\n fundingMode,\n chainId: parameters.chainId,\n /**\n * Carried through deliberately: `instantOpen` resolves the solver from it to\n * fill `partyBsWhiteList` — which is signed into the EIP-712 payload — and to\n * pick the submit URL. Dropping it here would sign against the default\n * solver's address while the quote was priced and sized for another.\n */\n solverId: parameters.solverId,\n from: parameters.from,\n subAccountAddress: parameters.subAccountAddress,\n marketId: parameters.market.id,\n positionType: parameters.positionType,\n markPrice: toWeiBigInt(markPrice),\n order: {\n price: toWeiBigInt(finalTrade.requestedOpenPrice),\n quantity: toWeiBigInt(finalTrade.quantity),\n },\n lockedParam: {\n cva: toWeiBigInt(finalTrade.cva),\n lf: toWeiBigInt(finalTrade.lf),\n partyAmm: toWeiBigInt(finalTrade.partyAmm),\n partyBmm: toWeiBigInt(finalTrade.partyBmm),\n },\n margin: {\n // Shift the decimal point without a Decimal multiplication, whose global\n // significant-digit precision can round a full balance up by one wei.\n amount: parseUnits(toDecimal(marginAmount).toFixed(18, RoundingMode.ROUND_DOWN), 18),\n },\n /** Only meaningful on a v0.8.6 chain — absent on v0.8.5, whose flow signs the legacy call. */\n ...(needsSolverFeeCaps\n ? {\n solverFeeCaps: {\n openRateCap: solverFeeCapToWei(\"minOpenSolverFeeCap\", market.minOpenSolverFeeCap),\n closeRateCap: solverFeeCapToWei(\"minCloseSolverFeeCap\", market.minCloseSolverFeeCap),\n },\n }\n : {}),\n uuid: parameters.uuid,\n addMarginSalt: parameters.addMarginSalt,\n sendQuoteSalt: parameters.sendQuoteSalt,\n deadline: parameters.deadline,\n operationDeadline: parameters.operationDeadline,\n preflightDelegation: parameters.preflightDelegation,\n };\n}\n"],"mappings":"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"}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* instant-* slices lives here. Slice-specific shared lives under each slice's
|
|
6
6
|
* own `shared/` folder.
|
|
7
7
|
*/
|
|
8
|
+
export * from './instant-delegation-preflight.js';
|
|
8
9
|
export * from './resolvers/index.js';
|
|
9
10
|
export * from './solver-close-fee.js';
|
|
10
11
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/solvers/shared/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/solvers/shared/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,cAAc,gCAAgC,CAAC;AAC/C,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Config } from '../../core/config/index.js';
|
|
2
|
+
import { SignedOperation } from '../instant-open/shared/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Per-call switch for the delegation check that instant trade writes run
|
|
5
|
+
* before signing.
|
|
6
|
+
*
|
|
7
|
+
* `instantOpen`, `instantClose` and `instantCloseBulk` — and every flow built
|
|
8
|
+
* on them (`instantOpenAuto`, `limitOpenAuto`, `instantCloseAuto`,
|
|
9
|
+
* `limitCloseAuto`, `instantCloseBulkAuto`) — confirm that the signer may act
|
|
10
|
+
* for the account each operation runs under before anything is signed or sent
|
|
11
|
+
* to the solver. A session key without the InstantLayer delegation fails right
|
|
12
|
+
* there with `INSTANT_SIGNER_NOT_DELEGATED`, instead of after the solver tries
|
|
13
|
+
* to execute the trade. The account's owner needs no delegation, so an
|
|
14
|
+
* owner-signed trade skips the delegation reads.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* // The app already gates its trade button on delegation: skip the extra read.
|
|
19
|
+
* await instantClose(config, { from: sessionKey, partyA, order, preflightDelegation: false });
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export interface InstantDelegationPreflightParameter {
|
|
23
|
+
/**
|
|
24
|
+
* Check, before signing, that the signer holds an active InstantLayer
|
|
25
|
+
* delegation for every operation it signs, and throw
|
|
26
|
+
* `INSTANT_SIGNER_NOT_DELEGATED` when it does not. Defaults to `true`.
|
|
27
|
+
*
|
|
28
|
+
* Pass `false` when your app already gates the trade on delegation and you
|
|
29
|
+
* want to skip the extra RPC read.
|
|
30
|
+
*/
|
|
31
|
+
preflightDelegation?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Throw when the signer of a set of instant-trade operations lacks the
|
|
35
|
+
* InstantLayer delegation they need — before any of them is signed or sent.
|
|
36
|
+
*
|
|
37
|
+
* Built on the gasless relay's delegation pre-flight
|
|
38
|
+
* (`findUndelegatedGaslessSelectors`), so relayed writes and instant trades
|
|
39
|
+
* share one delegation check. This adds the two steps that check leaves to its
|
|
40
|
+
* caller:
|
|
41
|
+
*
|
|
42
|
+
* 1. Each operation's selector is read from its calldata, so the check covers
|
|
43
|
+
* exactly what is about to be signed.
|
|
44
|
+
* 2. Each operation's `signerAccount` is resolved to its delegator — a live
|
|
45
|
+
* virtual account to its parent sub-account, as the InstantLayer does.
|
|
46
|
+
* Without it a close signed under a virtual account would skip the check.
|
|
47
|
+
*
|
|
48
|
+
* Operations are grouped by signer and delegator, so a bulk close across many
|
|
49
|
+
* virtual accounts of one sub-account costs one check.
|
|
50
|
+
*
|
|
51
|
+
* @param config - The SDK config.
|
|
52
|
+
* @param parameters - The chain, the calling action (named in the error), the
|
|
53
|
+
* operations about to be signed, and `enabled` (`false` skips the check).
|
|
54
|
+
* @throws {SymmError} `INSTANT_SIGNER_NOT_DELEGATED` naming every signer,
|
|
55
|
+
* sub-account and selector that lacks an active delegation.
|
|
56
|
+
*
|
|
57
|
+
* @internal
|
|
58
|
+
*/
|
|
59
|
+
export declare function assertInstantSignerDelegated(config: Config, parameters: {
|
|
60
|
+
chainId?: number;
|
|
61
|
+
action: string;
|
|
62
|
+
operations: readonly Pick<SignedOperation, "signer" | "callData" | "signerAccount">[];
|
|
63
|
+
enabled?: boolean;
|
|
64
|
+
}): Promise<void>;
|
|
65
|
+
//# sourceMappingURL=instant-delegation-preflight.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instant-delegation-preflight.d.ts","sourceRoot":"","sources":["../../../src/solvers/shared/instant-delegation-preflight.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAGhD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAEpE;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,mCAAmC;IAClD;;;;;;;OAOG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AASD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,4BAA4B,CAChD,MAAM,EAAE,MAAM,EACd,UAAU,EAAE;IACV,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,SAAS,IAAI,CAAC,eAAe,EAAE,QAAQ,GAAG,UAAU,GAAG,eAAe,CAAC,EAAE,CAAC;IACtF,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GACA,OAAO,CAAC,IAAI,CAAC,CA4Cf"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { SymmError as e } from "../../shared/errors/symm-error.js";
|
|
2
|
+
import { findUndelegatedGaslessSelectors as t, getCachedDelegatorAccount as n } from "../../gasless/delegation-preflight.js";
|
|
3
|
+
//#region src/solvers/shared/instant-delegation-preflight.ts
|
|
4
|
+
async function r(r, i) {
|
|
5
|
+
if (i.enabled === !1 || i.operations.length === 0) return;
|
|
6
|
+
let { chainId: a } = r.getChainConfig(i.chainId), o = await Promise.all(i.operations.map(async (e) => ({
|
|
7
|
+
operation: e,
|
|
8
|
+
account: await n(r, a, e.signerAccount.addr)
|
|
9
|
+
}))), s = /* @__PURE__ */ new Map();
|
|
10
|
+
for (let { operation: e, account: t } of o) {
|
|
11
|
+
let n = `${e.signer.toLowerCase()}:${t.toLowerCase()}`, r = s.get(n) ?? {
|
|
12
|
+
signer: e.signer,
|
|
13
|
+
account: t,
|
|
14
|
+
selectors: /* @__PURE__ */ new Set()
|
|
15
|
+
};
|
|
16
|
+
r.selectors.add(e.callData.slice(0, 10).toLowerCase()), s.set(n, r);
|
|
17
|
+
}
|
|
18
|
+
let c = (await Promise.all([...s.values()].map(async (e) => ({
|
|
19
|
+
...e,
|
|
20
|
+
missing: await t(r, {
|
|
21
|
+
chainId: a,
|
|
22
|
+
signer: e.signer,
|
|
23
|
+
signerAccount: e.account,
|
|
24
|
+
selectors: [...e.selectors]
|
|
25
|
+
})
|
|
26
|
+
})))).filter((e) => e.missing.length > 0);
|
|
27
|
+
if (c.length === 0) return;
|
|
28
|
+
let l = c.map((e) => `signer ${e.signer} holds no active InstantLayer delegation from sub-account ${e.account} for selector(s) ${e.missing.join(", ")}`).join("; ");
|
|
29
|
+
throw new e("validation", "INSTANT_SIGNER_NOT_DELEGATED", `${i.action}: ${l}. Grant those selectors to the signer (getInstantTradeRequiredSelectors builds the trade set), sign with the sub-account's owner, or pass \`preflightDelegation: false\` to skip this check.`);
|
|
30
|
+
}
|
|
31
|
+
//#endregion
|
|
32
|
+
export { r as assertInstantSignerDelegated };
|
|
33
|
+
|
|
34
|
+
//# sourceMappingURL=instant-delegation-preflight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instant-delegation-preflight.js","names":[],"sources":["../../../src/solvers/shared/instant-delegation-preflight.ts"],"sourcesContent":["import type { Address, Hex } from \"viem\";\nimport type { Config } from \"../../core/config\";\nimport { findUndelegatedGaslessSelectors, getCachedDelegatorAccount } from \"../../gasless/delegation-preflight\";\nimport { SymmError } from \"../../shared/errors/symm-error\";\nimport type { SignedOperation } from \"../instant-open/shared/types\";\n\n/**\n * Per-call switch for the delegation check that instant trade writes run\n * before signing.\n *\n * `instantOpen`, `instantClose` and `instantCloseBulk` — and every flow built\n * on them (`instantOpenAuto`, `limitOpenAuto`, `instantCloseAuto`,\n * `limitCloseAuto`, `instantCloseBulkAuto`) — confirm that the signer may act\n * for the account each operation runs under before anything is signed or sent\n * to the solver. A session key without the InstantLayer delegation fails right\n * there with `INSTANT_SIGNER_NOT_DELEGATED`, instead of after the solver tries\n * to execute the trade. The account's owner needs no delegation, so an\n * owner-signed trade skips the delegation reads.\n *\n * @example\n * ```ts\n * // The app already gates its trade button on delegation: skip the extra read.\n * await instantClose(config, { from: sessionKey, partyA, order, preflightDelegation: false });\n * ```\n */\nexport interface InstantDelegationPreflightParameter {\n /**\n * Check, before signing, that the signer holds an active InstantLayer\n * delegation for every operation it signs, and throw\n * `INSTANT_SIGNER_NOT_DELEGATED` when it does not. Defaults to `true`.\n *\n * Pass `false` when your app already gates the trade on delegation and you\n * want to skip the extra RPC read.\n */\n preflightDelegation?: boolean;\n}\n\n/** The selectors one signer signs under one delegator account. */\ninterface DelegationCheck {\n signer: Address;\n account: Address;\n selectors: Set<Hex>;\n}\n\n/**\n * Throw when the signer of a set of instant-trade operations lacks the\n * InstantLayer delegation they need — before any of them is signed or sent.\n *\n * Built on the gasless relay's delegation pre-flight\n * (`findUndelegatedGaslessSelectors`), so relayed writes and instant trades\n * share one delegation check. This adds the two steps that check leaves to its\n * caller:\n *\n * 1. Each operation's selector is read from its calldata, so the check covers\n * exactly what is about to be signed.\n * 2. Each operation's `signerAccount` is resolved to its delegator — a live\n * virtual account to its parent sub-account, as the InstantLayer does.\n * Without it a close signed under a virtual account would skip the check.\n *\n * Operations are grouped by signer and delegator, so a bulk close across many\n * virtual accounts of one sub-account costs one check.\n *\n * @param config - The SDK config.\n * @param parameters - The chain, the calling action (named in the error), the\n * operations about to be signed, and `enabled` (`false` skips the check).\n * @throws {SymmError} `INSTANT_SIGNER_NOT_DELEGATED` naming every signer,\n * sub-account and selector that lacks an active delegation.\n *\n * @internal\n */\nexport async function assertInstantSignerDelegated(\n config: Config,\n parameters: {\n chainId?: number;\n action: string;\n operations: readonly Pick<SignedOperation, \"signer\" | \"callData\" | \"signerAccount\">[];\n enabled?: boolean;\n },\n): Promise<void> {\n if (parameters.enabled === false || parameters.operations.length === 0) return;\n const { chainId } = config.getChainConfig(parameters.chainId);\n\n const resolved = await Promise.all(\n parameters.operations.map(async (operation) => ({\n operation,\n account: await getCachedDelegatorAccount(config, chainId, operation.signerAccount.addr),\n })),\n );\n\n const checks = new Map<string, DelegationCheck>();\n for (const { operation, account } of resolved) {\n const key = `${operation.signer.toLowerCase()}:${account.toLowerCase()}`;\n const check = checks.get(key) ?? { signer: operation.signer, account, selectors: new Set<Hex>() };\n check.selectors.add(operation.callData.slice(0, 10).toLowerCase() as Hex);\n checks.set(key, check);\n }\n\n const results = await Promise.all(\n [...checks.values()].map(async (check) => ({\n ...check,\n missing: await findUndelegatedGaslessSelectors(config, {\n chainId,\n signer: check.signer,\n signerAccount: check.account,\n selectors: [...check.selectors],\n }),\n })),\n );\n const undelegated = results.filter((result) => result.missing.length > 0);\n if (undelegated.length === 0) return;\n\n const detail = undelegated\n .map(\n (result) =>\n `signer ${result.signer} holds no active InstantLayer delegation from sub-account ${result.account} for selector(s) ${result.missing.join(\", \")}`,\n )\n .join(\"; \");\n throw new SymmError(\n \"validation\",\n \"INSTANT_SIGNER_NOT_DELEGATED\",\n `${parameters.action}: ${detail}. Grant those selectors to the signer (getInstantTradeRequiredSelectors builds the trade set), sign with the sub-account's owner, or pass \\`preflightDelegation: false\\` to skip this check.`,\n );\n}\n"],"mappings":";;;AAsEA,eAAsB,EACpB,GACA,GAMe;CACf,IAAI,EAAW,YAAY,MAAS,EAAW,WAAW,WAAW,GAAG;CACxE,IAAM,EAAE,eAAY,EAAO,eAAe,EAAW,OAAO,GAEtD,IAAW,MAAM,QAAQ,IAC7B,EAAW,WAAW,IAAI,OAAO,OAAe;EAC9C;EACA,SAAS,MAAM,EAA0B,GAAQ,GAAS,EAAU,cAAc,IAAI;CACxF,EAAE,CACJ,GAEM,oBAAS,IAAI,IAA6B;CAChD,KAAK,IAAM,EAAE,cAAW,gBAAa,GAAU;EAC7C,IAAM,IAAM,GAAG,EAAU,OAAO,YAAY,EAAE,GAAG,EAAQ,YAAY,KAC/D,IAAQ,EAAO,IAAI,CAAG,KAAK;GAAE,QAAQ,EAAU;GAAQ;GAAS,2BAAW,IAAI,IAAS;EAAE;EAEhG,AADA,EAAM,UAAU,IAAI,EAAU,SAAS,MAAM,GAAG,EAAE,EAAE,YAAY,CAAQ,GACxE,EAAO,IAAI,GAAK,CAAK;CACvB;CAaA,IAAM,KAAc,MAXE,QAAQ,IAC5B,CAAC,GAAG,EAAO,OAAO,CAAC,EAAE,IAAI,OAAO,OAAW;EACzC,GAAG;EACH,SAAS,MAAM,EAAgC,GAAQ;GACrD;GACA,QAAQ,EAAM;GACd,eAAe,EAAM;GACrB,WAAW,CAAC,GAAG,EAAM,SAAS;EAChC,CAAC;CACH,EAAE,CACJ,GAC4B,QAAQ,MAAW,EAAO,QAAQ,SAAS,CAAC;CACxE,IAAI,EAAY,WAAW,GAAG;CAE9B,IAAM,IAAS,EACZ,KACE,MACC,UAAU,EAAO,OAAO,4DAA4D,EAAO,QAAQ,mBAAmB,EAAO,QAAQ,KAAK,IAAI,GAClJ,EACC,KAAK,IAAI;CACZ,MAAM,IAAI,EACR,cACA,gCACA,GAAG,EAAW,OAAO,IAAI,EAAO,6LAClC;AACF"}
|
|
@@ -14,13 +14,15 @@
|
|
|
14
14
|
* overloads.
|
|
15
15
|
*
|
|
16
16
|
* Verified on 2026-09-17 by read-only `eth_call` against the staging proxy
|
|
17
|
-
* `0x386EF97D913acf02B3C9452da4Cd4aaEc82eFBca` (implementation
|
|
17
|
+
* `0x386EF97D913acf02B3C9452da4Cd4aaEc82eFBca` (then implementation
|
|
18
18
|
* `0x640b404a0b2cC8D7F95b6356CC97e44639b91d64`, Arbitrum): `getGaslessWalletAddress`,
|
|
19
19
|
* `walletOperationNonces`, `getWalletCreationFee`, `previewFeeQuote`, the 5-argument
|
|
20
20
|
* `relayInstantBatch`, `instantLayer`, `collateralToken`, `depositFee` and
|
|
21
|
-
* `minimumDeposit`.
|
|
22
|
-
*
|
|
23
|
-
*
|
|
21
|
+
* `minimumDeposit`. Re-checked on 2026-10-14 against the implementation bytecode
|
|
22
|
+
* behind both Arbitrum proxies — production `0x8347953D80037b8d82827246f37EC7442AD188B4`
|
|
23
|
+
* (implementation `0xe04b9f05a61d9e9b97bc4f5f7a0acef740f6a71d`) and staging (implementation
|
|
24
|
+
* `0xc891d8371cb5475c077a12cab857b9e798bae3b6`): both carry every function in this ABI
|
|
25
|
+
* except `initialize` and `relayNativeGasTopUp`, neither of which the SDK calls.
|
|
24
26
|
*
|
|
25
27
|
* @see Source of truth — the SYMMIO `perps-core` repo, pinned to the verified
|
|
26
28
|
* commit: {@link https://github.com/SYMM-IO/perps-core/blob/b63ee55e4cfbbc6a9757d23212102d5572e5d829/abis/gaslessLayer.json}.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gasless-layer.d.ts","sourceRoot":"","sources":["../../../../src/symmio-contracts/abi/v0.8.6/gasless-layer.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"gasless-layer.d.ts","sourceRoot":"","sources":["../../../../src/symmio-contracts/abi/v0.8.6/gasless-layer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAy5FlB,CAAC"}
|