@lifi/composer-sdk 0.4.1 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/README.md +6 -3
  2. package/dist/authoring/FlowBuilderCore.cjs +1 -5
  3. package/dist/authoring/FlowBuilderCore.cjs.map +1 -1
  4. package/dist/authoring/FlowBuilderCore.d.cts +9 -7
  5. package/dist/authoring/FlowBuilderCore.d.ts +9 -7
  6. package/dist/authoring/FlowBuilderCore.js +2 -6
  7. package/dist/authoring/FlowBuilderCore.js.map +1 -1
  8. package/dist/authoring/handles.cjs +5 -2
  9. package/dist/authoring/handles.cjs.map +1 -1
  10. package/dist/authoring/handles.d.cts +25 -6
  11. package/dist/authoring/handles.d.ts +25 -6
  12. package/dist/authoring/handles.js +3 -1
  13. package/dist/authoring/handles.js.map +1 -1
  14. package/dist/authoring/raw.cjs +3 -0
  15. package/dist/authoring/raw.cjs.map +1 -1
  16. package/dist/authoring/raw.d.cts +33 -3
  17. package/dist/authoring/raw.d.ts +33 -3
  18. package/dist/authoring/raw.js +2 -0
  19. package/dist/authoring/raw.js.map +1 -1
  20. package/dist/authoring/signatureArgs.cjs.map +1 -1
  21. package/dist/authoring/signatureArgs.d.cts +16 -12
  22. package/dist/authoring/signatureArgs.d.ts +16 -12
  23. package/dist/authoring/signatureArgs.js.map +1 -1
  24. package/dist/client.cjs +112 -55
  25. package/dist/client.cjs.map +1 -1
  26. package/dist/client.d.cts +42 -5
  27. package/dist/client.d.ts +42 -5
  28. package/dist/client.js +116 -55
  29. package/dist/client.js.map +1 -1
  30. package/dist/discovery.cjs.map +1 -1
  31. package/dist/discovery.d.cts +18 -2
  32. package/dist/discovery.d.ts +18 -2
  33. package/dist/errors.cjs +42 -3
  34. package/dist/errors.cjs.map +1 -1
  35. package/dist/errors.d.cts +67 -4
  36. package/dist/errors.d.ts +67 -4
  37. package/dist/errors.js +29 -2
  38. package/dist/errors.js.map +1 -1
  39. package/dist/generated/config.generated.cjs.map +1 -1
  40. package/dist/generated/config.generated.d.cts +21 -1
  41. package/dist/generated/config.generated.d.ts +21 -1
  42. package/dist/generated/operations.generated.cjs +6 -0
  43. package/dist/generated/operations.generated.cjs.map +1 -1
  44. package/dist/generated/operations.generated.d.cts +18 -2
  45. package/dist/generated/operations.generated.d.ts +18 -2
  46. package/dist/generated/operations.generated.js +6 -0
  47. package/dist/generated/operations.generated.js.map +1 -1
  48. package/dist/index.cjs +4 -0
  49. package/dist/index.cjs.map +1 -1
  50. package/dist/index.d.cts +7 -7
  51. package/dist/index.d.ts +7 -7
  52. package/dist/index.js +8 -1
  53. package/dist/index.js.map +1 -1
  54. package/dist/{raw-IxCF_bSS.d.ts → raw-BvRghN3r.d.ts} +3 -2
  55. package/dist/{raw-Dj_2rBSQ.d.cts → raw-s1rer8X3.d.cts} +3 -2
  56. package/dist/raw.cjs +2 -0
  57. package/dist/raw.cjs.map +1 -1
  58. package/dist/raw.d.cts +1 -1
  59. package/dist/raw.d.ts +1 -1
  60. package/dist/raw.js +2 -1
  61. package/dist/raw.js.map +1 -1
  62. package/dist/responseSchemas.cjs +3 -0
  63. package/dist/responseSchemas.cjs.map +1 -1
  64. package/dist/responseSchemas.d.cts +3 -0
  65. package/dist/responseSchemas.d.ts +3 -0
  66. package/dist/responseSchemas.js +3 -0
  67. package/dist/responseSchemas.js.map +1 -1
  68. package/dist/run/inputs.cjs +14 -15
  69. package/dist/run/inputs.cjs.map +1 -1
  70. package/dist/run/inputs.d.cts +8 -0
  71. package/dist/run/inputs.d.ts +8 -0
  72. package/dist/run/inputs.js +14 -15
  73. package/dist/run/inputs.js.map +1 -1
  74. package/dist/run/simulate.cjs +5 -0
  75. package/dist/run/simulate.cjs.map +1 -1
  76. package/dist/run/simulate.d.cts +5 -2
  77. package/dist/run/simulate.d.ts +5 -2
  78. package/dist/run/simulate.js +5 -0
  79. package/dist/run/simulate.js.map +1 -1
  80. package/dist/sdk.cjs +2 -0
  81. package/dist/sdk.cjs.map +1 -1
  82. package/dist/sdk.d.cts +28 -1
  83. package/dist/sdk.d.ts +28 -1
  84. package/dist/sdk.js +2 -0
  85. package/dist/sdk.js.map +1 -1
  86. package/dist/types.cjs.map +1 -1
  87. package/dist/types.d.cts +18 -2
  88. package/dist/types.d.ts +18 -2
  89. package/package.json +3 -10
  90. package/src/authoring/FlowBuilderCore.ts +13 -15
  91. package/src/authoring/handles.ts +41 -5
  92. package/src/authoring/raw.ts +36 -2
  93. package/src/authoring/signatureArgs.ts +17 -13
  94. package/src/client.ts +222 -62
  95. package/src/discovery.ts +18 -1
  96. package/src/errors.ts +116 -3
  97. package/src/examples/flySwap.ts +167 -0
  98. package/src/examples/run.ts +35 -13
  99. package/src/examples/supportedChains.ts +122 -0
  100. package/src/generated/config.generated.ts +24 -0
  101. package/src/generated/operations.generated.ts +26 -0
  102. package/src/index.ts +18 -2
  103. package/src/raw.ts +1 -1
  104. package/src/responseSchemas.ts +6 -0
  105. package/src/run/inputs.ts +30 -11
  106. package/src/run/simulate.ts +14 -2
  107. package/src/sdk.ts +30 -0
  108. package/src/types.ts +20 -1
@@ -1,6 +1,7 @@
1
1
  const ref = (path) => ({
2
2
  $ref: path
3
3
  });
4
+ const literal = (kind, value) => ({ kind, value });
4
5
  const rawGuard = (kind, config = {}) => {
5
6
  if ("kind" in config) {
6
7
  throw new Error(
@@ -12,6 +13,7 @@ const rawGuard = (kind, config = {}) => {
12
13
  return { ...config, kind };
13
14
  };
14
15
  export {
16
+ literal,
15
17
  rawGuard,
16
18
  ref
17
19
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/authoring/raw.ts"],"sourcesContent":["import type { AppliedGuard } from '@lifi/compose-spec';\n\nimport type { OutputKind, TypedRef } from '../types.js';\n\n/**\n * Creates a typed `$ref` pointer for use in bind slots.\n *\n * This is an **escape hatch** for referencing values that the typed builder\n * API doesn't cover — for example, outputs of an `untypedOp` node or custom\n * context paths. The caller is responsible for choosing the correct type\n * parameter; no runtime validation occurs.\n *\n * Prefer typed handles (`OutputHandle`, `InputHandle`) and `builder.context`\n * whenever possible. Use `ref` only when you need to construct a `$ref` path\n * manually and want it accepted by a `Bindable<T>` slot.\n *\n * @typeParam T - The output kind this ref represents (e.g. `'address'`, `'uint256'`, `'resource'`).\n * @param path - The `$ref` path (e.g. `\"context.sender\"`, `\"myNode.result\"`).\n * @returns A {@link TypedRef} accepted by `Bindable<T>` slots matching `T`.\n *\n * @example\n * ```ts\n * import { raw } from '@lifi/composer-sdk';\n *\n * // Reference an untypedOp output in a typed operation:\n * builder.untypedOp('custom', 'some.op', {\n * bind: { x: { $ref: 'input.token' } },\n * config: {},\n * });\n * builder.core.add('sum', {\n * bind: {\n * a: raw.ref<'uint256'>('custom.result'),\n * b: someTypedHandle,\n * },\n * });\n * ```\n */\nexport const ref = <T extends OutputKind>(path: string): TypedRef<T> => ({\n $ref: path,\n});\n\n/**\n * Creates a raw guard object for use in the `guards` array of an operation call.\n *\n * @param kind - The guard type (e.g. `\"slippage\"`).\n * @param config - Guard-specific configuration (e.g. `{ toleranceBps: 300 }`).\n * @returns An {@link AppliedGuard} object.\n */\nexport const rawGuard = (\n kind: string,\n config: Record<string, unknown> = {},\n): AppliedGuard => {\n if ('kind' in config) {\n throw new Error(\n `rawGuard: 'kind' must not appear in config (got \"${String(\n config.kind,\n )}\"). Pass it as the first argument instead.`,\n );\n }\n return { ...config, kind };\n};\n"],"mappings":"AAqCO,MAAM,MAAM,CAAuB,UAA+B;AAAA,EACvE,MAAM;AACR;AASO,MAAM,WAAW,CACtB,MACA,SAAkC,CAAC,MAClB;AACjB,MAAI,UAAU,QAAQ;AACpB,UAAM,IAAI;AAAA,MACR,oDAAoD;AAAA,QAClD,OAAO;AAAA,MACT,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO,EAAE,GAAG,QAAQ,KAAK;AAC3B;","names":[]}
1
+ {"version":3,"sources":["../../src/authoring/raw.ts"],"sourcesContent":["import type { AppliedGuard, SolType } from '@lifi/compose-spec';\n\nimport type { OutputKind, TypedLiteral, TypedRef } from '../types.js';\n\n/**\n * Creates a typed `$ref` pointer for use in bind slots.\n *\n * This is an **escape hatch** for referencing values that the typed builder\n * API doesn't cover — for example, outputs of an `untypedOp` node or custom\n * context paths. The caller is responsible for choosing the correct type\n * parameter; no runtime validation occurs.\n *\n * Prefer typed handles (`OutputHandle`, `InputHandle`) and `builder.context`\n * whenever possible. Use `ref` only when you need to construct a `$ref` path\n * manually and want it accepted by a `Bindable<T>` slot.\n *\n * @typeParam T - The output kind this ref represents (e.g. `'address'`, `'uint256'`, `'resource'`).\n * @param path - The `$ref` path (e.g. `\"context.sender\"`, `\"myNode.result\"`).\n * @returns A {@link TypedRef} accepted by `Bindable<T>` slots matching `T`.\n *\n * @example\n * ```ts\n * import { raw } from '@lifi/composer-sdk';\n *\n * // Reference an untypedOp output in a typed operation:\n * builder.untypedOp('custom', 'some.op', {\n * bind: { x: { $ref: 'input.token' } },\n * config: {},\n * });\n * builder.core.add('sum', {\n * bind: {\n * a: raw.ref<'uint256'>('custom.result'),\n * b: someTypedHandle,\n * },\n * });\n * ```\n */\nexport const ref = <T extends OutputKind>(path: string): TypedRef<T> => ({\n $ref: path,\n});\n\n/**\n * Creates a typed literal for use in bind slots: a constant baked into the\n * flow document rather than supplied at run time.\n *\n * Prefer a declared flow input and `builder.inputs.<name>` — a value the\n * caller supplies per run stays out of the document. Use a literal when the\n * flow has no run of its own to take the value from: the `continuationFlow`\n * of a `continuation.settle` node is stored by the settlement backend and\n * re-run later against the deferred proceeds, so its delivery address has to\n * be part of the committed document.\n *\n * @typeParam T - The Solidity type of the literal (e.g. `'address'`).\n * @param kind - The literal's Solidity type.\n * @param value - The literal value, as the wire format carries it: a string.\n * @returns A {@link TypedLiteral} accepted by `Bindable<T>` slots matching `T`.\n *\n * @example\n * ```ts\n * import { raw } from '@lifi/composer-sdk';\n *\n * builder.core.transfer('deliver', {\n * bind: {\n * amount: zap.amountOut,\n * recipient: raw.literal('address', '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045'),\n * },\n * config: {},\n * });\n * ```\n */\nexport const literal = <T extends SolType>(\n kind: T,\n value: string,\n): TypedLiteral<T> => ({ kind, value });\n\n/**\n * Creates a raw guard object for use in the `guards` array of an operation call.\n *\n * @param kind - The guard type (e.g. `\"slippage\"`).\n * @param config - Guard-specific configuration (e.g. `{ toleranceBps: 300 }`).\n * @returns An {@link AppliedGuard} object.\n */\nexport const rawGuard = (\n kind: string,\n config: Record<string, unknown> = {},\n): AppliedGuard => {\n if ('kind' in config) {\n throw new Error(\n `rawGuard: 'kind' must not appear in config (got \"${String(\n config.kind,\n )}\"). Pass it as the first argument instead.`,\n );\n }\n return { ...config, kind };\n};\n"],"mappings":"AAqCO,MAAM,MAAM,CAAuB,UAA+B;AAAA,EACvE,MAAM;AACR;AA+BO,MAAM,UAAU,CACrB,MACA,WACqB,EAAE,MAAM,MAAM;AAS9B,MAAM,WAAW,CACtB,MACA,SAAkC,CAAC,MAClB;AACjB,MAAI,UAAU,QAAQ;AACpB,UAAM,IAAI;AAAA,MACR,oDAAoD;AAAA,QAClD,OAAO;AAAA,MACT,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO,EAAE,GAAG,QAAQ,KAAK;AAC3B;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/authoring/signatureArgs.ts"],"sourcesContent":["import type { Ref } from '@lifi/compose-spec';\nimport { type ParseAbiItem, parseAbiItem } from 'abitype';\n\nimport type { AbiTypeToOutputKind, TypedGuard } from '../types.js';\n\nimport type { AnyBindable, CallArgs } from './FlowBuilderCore.js';\nimport type { Bindable, OutputHandle } from './handles.js';\nimport { handleToRef } from './handles.js';\n\n/**\n * Type-level extraction of named bind keys from a function signature string.\n * When `TSig` is a string literal matching a Solidity function signature,\n * resolves to a record whose keys are the parameter names and values are\n * `Bindable<kind>` where the kind is derived from each parameter's Solidity\n * type via `AbiTypeToOutputKind`. For recognised `SolType` parameters\n * (e.g. `uint256`, `address`, `uint128`) the bind slot is precisely typed;\n * for other Solidity types (e.g. tuples) the slot accepts any scalar handle.\n * Falls back to `Record<string, AnyBindable>` for non-literal strings.\n */\nexport type SignatureBind<TSig extends string> = string extends TSig\n ? Record<string, AnyBindable>\n : ParseAbiItem<TSig> extends {\n type: 'function';\n inputs: infer I extends readonly { name: string; type: string }[];\n }\n ? {\n readonly [P in I[number] as P['name']]: Bindable<\n AbiTypeToOutputKind<P['type']>\n >;\n }\n : Record<string, AnyBindable>;\n\n// ---------------------------------------------------------------------------\n// Return-type inference from function signatures\n// ---------------------------------------------------------------------------\n\n/**\n * Describes the output type(s) of a parsed function signature as a\n * human-readable string for inclusion in type-level error messages.\n */\ntype DescribeOutputs<O extends readonly { type: string }[]> =\n O extends readonly [{ type: infer T extends string }] ? T : 'multiple values';\n\n/**\n * A string-literal type that surfaces a clear error when the user provides a\n * function signature whose return type is not supported by core.call.\n * The message is visible in IDE tooltips and compiler diagnostics.\n */\ntype UnsupportedReturnTypeError<TLabel extends string, TSig extends string> =\n ParseAbiItem<TSig> extends {\n type: 'function';\n outputs: infer O extends readonly { type: string }[];\n }\n ? `[TypeError] ${TLabel}: return type '${DescribeOutputs<O>}' is not supported — only 'uint256' or void signatures are allowed`\n : `[TypeError] ${TLabel}: unable to parse return type from signature`;\n\n/**\n * Conditional output type for `core.call` based on the function signature's\n * return type:\n *\n * - `returns (uint256)` → `{ result: OutputHandle }`\n * - No returns clause → `{ result: undefined }`\n * - Anything else → `{ result: \"[TypeError] ...\" }` (compile-time error)\n *\n * When `TSig` is a non-literal `string` (e.g. a variable, not a string\n * constant), the type falls back to `{ result: OutputHandle | undefined }`\n * so that both void and uint256 call sites remain assignable.\n */\nexport type CoreCallResult<TSig extends string> = string extends TSig\n ? { readonly result: OutputHandle<'uint256'> | undefined }\n : ParseAbiItem<TSig> extends { type: 'function'; outputs: readonly [] }\n ? { readonly result: undefined }\n : ParseAbiItem<TSig> extends {\n type: 'function';\n outputs: readonly [{ type: 'uint256' }];\n }\n ? { readonly result: OutputHandle<'uint256'> }\n : { readonly result: UnsupportedReturnTypeError<'core.call', TSig> };\n\n/**\n * Conditional output type for `core.staticCall`. staticCall always reads a\n * value, so void signatures are also rejected:\n *\n * - `returns (uint256)` → `{ result: OutputHandle }`\n * - No returns / other → `{ result: \"[TypeError] ...\" }` (compile-time error)\n */\nexport type StaticCallResult<TSig extends string> = string extends TSig\n ? { readonly result: OutputHandle<'uint256'> }\n : ParseAbiItem<TSig> extends {\n type: 'function';\n outputs: readonly [{ type: 'uint256' }];\n }\n ? { readonly result: OutputHandle<'uint256'> }\n : ParseAbiItem<TSig> extends { type: 'function'; outputs: readonly [] }\n ? {\n readonly result: `[TypeError] core.staticCall: function has no return value — staticCall requires a 'returns (uint256)' signature`;\n }\n : {\n readonly result: UnsupportedReturnTypeError<'core.staticCall', TSig>;\n };\n\n/**\n * Parses a Solidity function signature and returns parameter names in order.\n * Delegates to abitype's runtime `parseAbiItem` — the same parser that powers\n * the compile-time `ParseAbiItem<TSig>` type — so runtime and type-level\n * parsing can never diverge.\n *\n * @throws if the signature is not a valid function signature or a parameter is unnamed\n */\nexport const parseFunctionParams = (functionSignature: string): string[] => {\n const parsed = parseAbiItem(functionSignature);\n if (parsed.type !== 'function') {\n throw new Error(\n `core.call: expected a function signature, got \"${parsed.type}\": \"${functionSignature}\"`,\n );\n }\n\n return parsed.inputs.map((input, index) => {\n if (!input.name) {\n throw new Error(\n `core.call: parameter at index ${index} in signature has no name. All parameters must be named for named binds to work.`,\n );\n }\n return input.name;\n });\n};\n\n/**\n * Converts any `AnyBindable` to a `Ref` (`{ $ref: string }`).\n * Handles carry a `_tag` property and are converted via `handleToRef`;\n * `Ref` objects pass through as-is.\n */\nexport const toBindRef = (bindable: AnyBindable): Ref => {\n if ('_tag' in bindable) return handleToRef(bindable);\n return bindable;\n};\n\nexport interface BuildCallWireFormatInput {\n readonly resource?: AnyBindable;\n readonly bind: Record<string, AnyBindable>;\n readonly config: Record<string, unknown>;\n readonly guards?: readonly TypedGuard[];\n}\n\nexport interface CallWireResult {\n readonly op: 'core.call' | 'core.invoke';\n readonly bind: Record<string, Ref>;\n readonly config: Record<string, unknown>;\n readonly guards?: readonly TypedGuard[];\n}\n\n/**\n * Parses a function signature from `config`, validates bind keys against it,\n * and converts named binds to positional `$ref` args.\n */\nconst resolvePositionalArgs = (\n label: string,\n bind: Record<string, AnyBindable>,\n config: Record<string, unknown>,\n): Ref[] => {\n const functionSignature = config.functionSignature;\n if (typeof functionSignature !== 'string') {\n throw new Error(\n `${label}: config.functionSignature is required and must be a string`,\n );\n }\n\n const paramNames = parseFunctionParams(functionSignature);\n const paramSet = new Set(paramNames);\n const bindKeys = Object.keys(bind);\n const bindSet = new Set(bindKeys);\n\n const missing = paramNames.filter((n) => !bindSet.has(n));\n const extra = bindKeys.filter((k) => !paramSet.has(k));\n\n if (missing.length > 0 || extra.length > 0) {\n throw new Error(\n `${label}: bind keys [${bindKeys.join(\n ', ',\n )}] do not match signature parameters [${paramNames.join(\n ', ',\n )}]. Missing: ${missing.join(', ') || '(none)'}. Extra: ${\n extra.join(', ') || '(none)'\n }.`,\n );\n }\n\n return paramNames.map((name) => {\n const bindable = bind[name];\n if (bindable === undefined) {\n throw new Error(\n `${label}: bind value for parameter \"${name}\" is undefined`,\n );\n }\n return toBindRef(bindable);\n });\n};\n\n/**\n * Transforms the user-facing `resource` + named `bind` API into the wire\n * format that the backend expects:\n * - With resource: `core.call` with `bind: { input: <resourceRef> }`\n * - Without resource: `core.invoke` with `bind: {}`\n * - `config: { ...userConfig, args: [positional refs...] }`\n */\nexport const buildCallWireFormat = (\n args: BuildCallWireFormatInput,\n): CallWireResult => {\n const positionalArgs = resolvePositionalArgs(\n 'core.call',\n args.bind,\n args.config,\n );\n const hasResource = args.resource !== undefined;\n\n return {\n op: hasResource ? 'core.call' : 'core.invoke',\n bind: hasResource ? { input: toBindRef(args.resource) } : {},\n config: { ...args.config, args: positionalArgs },\n ...(args.guards && args.guards.length > 0 && { guards: args.guards }),\n };\n};\n\nexport interface BuildStaticCallWireFormatInput {\n readonly bind: Record<string, AnyBindable>;\n readonly config: Record<string, unknown>;\n readonly guards?: readonly TypedGuard[];\n}\n\n/**\n * Transforms the user-facing named `bind` API into the wire format that the\n * backend expects for `core.staticCall`. Same as `buildCallWireFormat` but\n * with no `resource` field — staticCall has no resource inputs.\n */\nexport const buildStaticCallWireFormat = (\n args: BuildStaticCallWireFormatInput,\n): CallArgs => {\n const positionalArgs = resolvePositionalArgs(\n 'core.staticCall',\n args.bind,\n args.config,\n );\n\n return {\n bind: {},\n config: { ...args.config, args: positionalArgs },\n ...(args.guards && args.guards.length > 0 && { guards: args.guards }),\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,qBAAgD;AAMhD,qBAA4B;AAsGrB,MAAM,sBAAsB,CAAC,sBAAwC;AAC1E,QAAM,aAAS,6BAAa,iBAAiB;AAC7C,MAAI,OAAO,SAAS,YAAY;AAC9B,UAAM,IAAI;AAAA,MACR,kDAAkD,OAAO,IAAI,OAAO,iBAAiB;AAAA,IACvF;AAAA,EACF;AAEA,SAAO,OAAO,OAAO,IAAI,CAAC,OAAO,UAAU;AACzC,QAAI,CAAC,MAAM,MAAM;AACf,YAAM,IAAI;AAAA,QACR,iCAAiC,KAAK;AAAA,MACxC;AAAA,IACF;AACA,WAAO,MAAM;AAAA,EACf,CAAC;AACH;AAOO,MAAM,YAAY,CAAC,aAA+B;AACvD,MAAI,UAAU,SAAU,YAAO,4BAAY,QAAQ;AACnD,SAAO;AACT;AAoBA,MAAM,wBAAwB,CAC5B,OACA,MACA,WACU;AACV,QAAM,oBAAoB,OAAO;AACjC,MAAI,OAAO,sBAAsB,UAAU;AACzC,UAAM,IAAI;AAAA,MACR,GAAG,KAAK;AAAA,IACV;AAAA,EACF;AAEA,QAAM,aAAa,oBAAoB,iBAAiB;AACxD,QAAM,WAAW,IAAI,IAAI,UAAU;AACnC,QAAM,WAAW,OAAO,KAAK,IAAI;AACjC,QAAM,UAAU,IAAI,IAAI,QAAQ;AAEhC,QAAM,UAAU,WAAW,OAAO,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;AACxD,QAAM,QAAQ,SAAS,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;AAErD,MAAI,QAAQ,SAAS,KAAK,MAAM,SAAS,GAAG;AAC1C,UAAM,IAAI;AAAA,MACR,GAAG,KAAK,gBAAgB,SAAS;AAAA,QAC/B;AAAA,MACF,CAAC,wCAAwC,WAAW;AAAA,QAClD;AAAA,MACF,CAAC,eAAe,QAAQ,KAAK,IAAI,KAAK,QAAQ,YAC5C,MAAM,KAAK,IAAI,KAAK,QACtB;AAAA,IACF;AAAA,EACF;AAEA,SAAO,WAAW,IAAI,CAAC,SAAS;AAC9B,UAAM,WAAW,KAAK,IAAI;AAC1B,QAAI,aAAa,QAAW;AAC1B,YAAM,IAAI;AAAA,QACR,GAAG,KAAK,+BAA+B,IAAI;AAAA,MAC7C;AAAA,IACF;AACA,WAAO,UAAU,QAAQ;AAAA,EAC3B,CAAC;AACH;AASO,MAAM,sBAAsB,CACjC,SACmB;AACnB,QAAM,iBAAiB;AAAA,IACrB;AAAA,IACA,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AACA,QAAM,cAAc,KAAK,aAAa;AAEtC,SAAO;AAAA,IACL,IAAI,cAAc,cAAc;AAAA,IAChC,MAAM,cAAc,EAAE,OAAO,UAAU,KAAK,QAAQ,EAAE,IAAI,CAAC;AAAA,IAC3D,QAAQ,EAAE,GAAG,KAAK,QAAQ,MAAM,eAAe;AAAA,IAC/C,GAAI,KAAK,UAAU,KAAK,OAAO,SAAS,KAAK,EAAE,QAAQ,KAAK,OAAO;AAAA,EACrE;AACF;AAaO,MAAM,4BAA4B,CACvC,SACa;AACb,QAAM,iBAAiB;AAAA,IACrB;AAAA,IACA,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AAEA,SAAO;AAAA,IACL,MAAM,CAAC;AAAA,IACP,QAAQ,EAAE,GAAG,KAAK,QAAQ,MAAM,eAAe;AAAA,IAC/C,GAAI,KAAK,UAAU,KAAK,OAAO,SAAS,KAAK,EAAE,QAAQ,KAAK,OAAO;AAAA,EACrE;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/authoring/signatureArgs.ts"],"sourcesContent":["import type { Ref } from '@lifi/compose-spec';\nimport { type ParseAbiItem, parseAbiItem } from 'abitype';\n\nimport type { AbiTypeToOutputKind, TypedGuard } from '../types.js';\n\nimport type { CallArgs } from './FlowBuilderCore.js';\nimport type { AnyRefBindable, OutputHandle, RefBindable } from './handles.js';\nimport { handleToRef } from './handles.js';\n\n/**\n * Type-level extraction of named bind keys from a function signature string.\n * When `TSig` is a string literal matching a Solidity function signature,\n * resolves to a record whose keys are the parameter names and values are\n * `RefBindable<kind>` where the kind is derived from each parameter's Solidity\n * type via `AbiTypeToOutputKind`. For recognised `SolType` parameters\n * (e.g. `uint256`, `address`, `uint128`) the bind slot is precisely typed;\n * for other Solidity types (e.g. tuples) the slot accepts any scalar handle.\n * Falls back to `Record<string, AnyRefBindable>` for non-literal strings.\n *\n * These slots are lowered into `config.args`, which is positional calldata\n * rather than a node `bind` record, so a literal is not accepted here: only\n * values that resolve to a `$ref`.\n */\nexport type SignatureBind<TSig extends string> = string extends TSig\n ? Record<string, AnyRefBindable>\n : ParseAbiItem<TSig> extends {\n type: 'function';\n inputs: infer I extends readonly { name: string; type: string }[];\n }\n ? {\n readonly [P in I[number] as P['name']]: RefBindable<\n AbiTypeToOutputKind<P['type']>\n >;\n }\n : Record<string, AnyRefBindable>;\n\n// ---------------------------------------------------------------------------\n// Return-type inference from function signatures\n// ---------------------------------------------------------------------------\n\n/**\n * Describes the output type(s) of a parsed function signature as a\n * human-readable string for inclusion in type-level error messages.\n */\ntype DescribeOutputs<O extends readonly { type: string }[]> =\n O extends readonly [{ type: infer T extends string }] ? T : 'multiple values';\n\n/**\n * A string-literal type that surfaces a clear error when the user provides a\n * function signature whose return type is not supported by core.call.\n * The message is visible in IDE tooltips and compiler diagnostics.\n */\ntype UnsupportedReturnTypeError<TLabel extends string, TSig extends string> =\n ParseAbiItem<TSig> extends {\n type: 'function';\n outputs: infer O extends readonly { type: string }[];\n }\n ? `[TypeError] ${TLabel}: return type '${DescribeOutputs<O>}' is not supported — only 'uint256' or void signatures are allowed`\n : `[TypeError] ${TLabel}: unable to parse return type from signature`;\n\n/**\n * Conditional output type for `core.call` based on the function signature's\n * return type:\n *\n * - `returns (uint256)` → `{ result: OutputHandle }`\n * - No returns clause → `{ result: undefined }`\n * - Anything else → `{ result: \"[TypeError] ...\" }` (compile-time error)\n *\n * When `TSig` is a non-literal `string` (e.g. a variable, not a string\n * constant), the type falls back to `{ result: OutputHandle | undefined }`\n * so that both void and uint256 call sites remain assignable.\n */\nexport type CoreCallResult<TSig extends string> = string extends TSig\n ? { readonly result: OutputHandle<'uint256'> | undefined }\n : ParseAbiItem<TSig> extends { type: 'function'; outputs: readonly [] }\n ? { readonly result: undefined }\n : ParseAbiItem<TSig> extends {\n type: 'function';\n outputs: readonly [{ type: 'uint256' }];\n }\n ? { readonly result: OutputHandle<'uint256'> }\n : { readonly result: UnsupportedReturnTypeError<'core.call', TSig> };\n\n/**\n * Conditional output type for `core.staticCall`. staticCall always reads a\n * value, so void signatures are also rejected:\n *\n * - `returns (uint256)` → `{ result: OutputHandle }`\n * - No returns / other → `{ result: \"[TypeError] ...\" }` (compile-time error)\n */\nexport type StaticCallResult<TSig extends string> = string extends TSig\n ? { readonly result: OutputHandle<'uint256'> }\n : ParseAbiItem<TSig> extends {\n type: 'function';\n outputs: readonly [{ type: 'uint256' }];\n }\n ? { readonly result: OutputHandle<'uint256'> }\n : ParseAbiItem<TSig> extends { type: 'function'; outputs: readonly [] }\n ? {\n readonly result: `[TypeError] core.staticCall: function has no return value — staticCall requires a 'returns (uint256)' signature`;\n }\n : {\n readonly result: UnsupportedReturnTypeError<'core.staticCall', TSig>;\n };\n\n/**\n * Parses a Solidity function signature and returns parameter names in order.\n * Delegates to abitype's runtime `parseAbiItem` — the same parser that powers\n * the compile-time `ParseAbiItem<TSig>` type — so runtime and type-level\n * parsing can never diverge.\n *\n * @throws if the signature is not a valid function signature or a parameter is unnamed\n */\nexport const parseFunctionParams = (functionSignature: string): string[] => {\n const parsed = parseAbiItem(functionSignature);\n if (parsed.type !== 'function') {\n throw new Error(\n `core.call: expected a function signature, got \"${parsed.type}\": \"${functionSignature}\"`,\n );\n }\n\n return parsed.inputs.map((input, index) => {\n if (!input.name) {\n throw new Error(\n `core.call: parameter at index ${index} in signature has no name. All parameters must be named for named binds to work.`,\n );\n }\n return input.name;\n });\n};\n\n/**\n * Converts a ref-resolving bindable to a `Ref` (`{ $ref: string }`).\n * Handles carry a `_tag` property and are converted via `handleToRef`;\n * `Ref` objects pass through as-is.\n */\nexport const toBindRef = (bindable: AnyRefBindable): Ref => {\n if ('_tag' in bindable) return handleToRef(bindable);\n return bindable;\n};\n\nexport interface BuildCallWireFormatInput {\n readonly resource?: AnyRefBindable;\n readonly bind: Record<string, AnyRefBindable>;\n readonly config: Record<string, unknown>;\n readonly guards?: readonly TypedGuard[];\n}\n\nexport interface CallWireResult {\n readonly op: 'core.call' | 'core.invoke';\n readonly bind: Record<string, Ref>;\n readonly config: Record<string, unknown>;\n readonly guards?: readonly TypedGuard[];\n}\n\n/**\n * Parses a function signature from `config`, validates bind keys against it,\n * and converts named binds to positional `$ref` args.\n */\nconst resolvePositionalArgs = (\n label: string,\n bind: Record<string, AnyRefBindable>,\n config: Record<string, unknown>,\n): Ref[] => {\n const functionSignature = config.functionSignature;\n if (typeof functionSignature !== 'string') {\n throw new Error(\n `${label}: config.functionSignature is required and must be a string`,\n );\n }\n\n const paramNames = parseFunctionParams(functionSignature);\n const paramSet = new Set(paramNames);\n const bindKeys = Object.keys(bind);\n const bindSet = new Set(bindKeys);\n\n const missing = paramNames.filter((n) => !bindSet.has(n));\n const extra = bindKeys.filter((k) => !paramSet.has(k));\n\n if (missing.length > 0 || extra.length > 0) {\n throw new Error(\n `${label}: bind keys [${bindKeys.join(\n ', ',\n )}] do not match signature parameters [${paramNames.join(\n ', ',\n )}]. Missing: ${missing.join(', ') || '(none)'}. Extra: ${\n extra.join(', ') || '(none)'\n }.`,\n );\n }\n\n return paramNames.map((name) => {\n const bindable = bind[name];\n if (bindable === undefined) {\n throw new Error(\n `${label}: bind value for parameter \"${name}\" is undefined`,\n );\n }\n return toBindRef(bindable);\n });\n};\n\n/**\n * Transforms the user-facing `resource` + named `bind` API into the wire\n * format that the backend expects:\n * - With resource: `core.call` with `bind: { input: <resourceRef> }`\n * - Without resource: `core.invoke` with `bind: {}`\n * - `config: { ...userConfig, args: [positional refs...] }`\n */\nexport const buildCallWireFormat = (\n args: BuildCallWireFormatInput,\n): CallWireResult => {\n const positionalArgs = resolvePositionalArgs(\n 'core.call',\n args.bind,\n args.config,\n );\n const hasResource = args.resource !== undefined;\n\n return {\n op: hasResource ? 'core.call' : 'core.invoke',\n bind: hasResource ? { input: toBindRef(args.resource) } : {},\n config: { ...args.config, args: positionalArgs },\n ...(args.guards && args.guards.length > 0 && { guards: args.guards }),\n };\n};\n\nexport interface BuildStaticCallWireFormatInput {\n readonly bind: Record<string, AnyRefBindable>;\n readonly config: Record<string, unknown>;\n readonly guards?: readonly TypedGuard[];\n}\n\n/**\n * Transforms the user-facing named `bind` API into the wire format that the\n * backend expects for `core.staticCall`. Same as `buildCallWireFormat` but\n * with no `resource` field — staticCall has no resource inputs.\n */\nexport const buildStaticCallWireFormat = (\n args: BuildStaticCallWireFormatInput,\n): CallArgs => {\n const positionalArgs = resolvePositionalArgs(\n 'core.staticCall',\n args.bind,\n args.config,\n );\n\n return {\n bind: {},\n config: { ...args.config, args: positionalArgs },\n ...(args.guards && args.guards.length > 0 && { guards: args.guards }),\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA,qBAAgD;AAMhD,qBAA4B;AA0GrB,MAAM,sBAAsB,CAAC,sBAAwC;AAC1E,QAAM,aAAS,6BAAa,iBAAiB;AAC7C,MAAI,OAAO,SAAS,YAAY;AAC9B,UAAM,IAAI;AAAA,MACR,kDAAkD,OAAO,IAAI,OAAO,iBAAiB;AAAA,IACvF;AAAA,EACF;AAEA,SAAO,OAAO,OAAO,IAAI,CAAC,OAAO,UAAU;AACzC,QAAI,CAAC,MAAM,MAAM;AACf,YAAM,IAAI;AAAA,QACR,iCAAiC,KAAK;AAAA,MACxC;AAAA,IACF;AACA,WAAO,MAAM;AAAA,EACf,CAAC;AACH;AAOO,MAAM,YAAY,CAAC,aAAkC;AAC1D,MAAI,UAAU,SAAU,YAAO,4BAAY,QAAQ;AACnD,SAAO;AACT;AAoBA,MAAM,wBAAwB,CAC5B,OACA,MACA,WACU;AACV,QAAM,oBAAoB,OAAO;AACjC,MAAI,OAAO,sBAAsB,UAAU;AACzC,UAAM,IAAI;AAAA,MACR,GAAG,KAAK;AAAA,IACV;AAAA,EACF;AAEA,QAAM,aAAa,oBAAoB,iBAAiB;AACxD,QAAM,WAAW,IAAI,IAAI,UAAU;AACnC,QAAM,WAAW,OAAO,KAAK,IAAI;AACjC,QAAM,UAAU,IAAI,IAAI,QAAQ;AAEhC,QAAM,UAAU,WAAW,OAAO,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;AACxD,QAAM,QAAQ,SAAS,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;AAErD,MAAI,QAAQ,SAAS,KAAK,MAAM,SAAS,GAAG;AAC1C,UAAM,IAAI;AAAA,MACR,GAAG,KAAK,gBAAgB,SAAS;AAAA,QAC/B;AAAA,MACF,CAAC,wCAAwC,WAAW;AAAA,QAClD;AAAA,MACF,CAAC,eAAe,QAAQ,KAAK,IAAI,KAAK,QAAQ,YAC5C,MAAM,KAAK,IAAI,KAAK,QACtB;AAAA,IACF;AAAA,EACF;AAEA,SAAO,WAAW,IAAI,CAAC,SAAS;AAC9B,UAAM,WAAW,KAAK,IAAI;AAC1B,QAAI,aAAa,QAAW;AAC1B,YAAM,IAAI;AAAA,QACR,GAAG,KAAK,+BAA+B,IAAI;AAAA,MAC7C;AAAA,IACF;AACA,WAAO,UAAU,QAAQ;AAAA,EAC3B,CAAC;AACH;AASO,MAAM,sBAAsB,CACjC,SACmB;AACnB,QAAM,iBAAiB;AAAA,IACrB;AAAA,IACA,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AACA,QAAM,cAAc,KAAK,aAAa;AAEtC,SAAO;AAAA,IACL,IAAI,cAAc,cAAc;AAAA,IAChC,MAAM,cAAc,EAAE,OAAO,UAAU,KAAK,QAAQ,EAAE,IAAI,CAAC;AAAA,IAC3D,QAAQ,EAAE,GAAG,KAAK,QAAQ,MAAM,eAAe;AAAA,IAC/C,GAAI,KAAK,UAAU,KAAK,OAAO,SAAS,KAAK,EAAE,QAAQ,KAAK,OAAO;AAAA,EACrE;AACF;AAaO,MAAM,4BAA4B,CACvC,SACa;AACb,QAAM,iBAAiB;AAAA,IACrB;AAAA,IACA,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AAEA,SAAO;AAAA,IACL,MAAM,CAAC;AAAA,IACP,QAAQ,EAAE,GAAG,KAAK,QAAQ,MAAM,eAAe;AAAA,IAC/C,GAAI,KAAK,UAAU,KAAK,OAAO,SAAS,KAAK,EAAE,QAAQ,KAAK,OAAO;AAAA,EACrE;AACF;","names":[]}
@@ -1,28 +1,32 @@
1
1
  import { Ref } from '@lifi/compose-spec';
2
2
  import { ParseAbiItem } from 'abitype';
3
3
  import { AbiTypeToOutputKind, TypedGuard } from '../types.cjs';
4
- import { AnyBindable, CallArgs } from './FlowBuilderCore.cjs';
5
- import { Bindable, OutputHandle } from './handles.cjs';
4
+ import { CallArgs } from './FlowBuilderCore.cjs';
5
+ import { AnyRefBindable, RefBindable, OutputHandle } from './handles.cjs';
6
6
 
7
7
  /**
8
8
  * Type-level extraction of named bind keys from a function signature string.
9
9
  * When `TSig` is a string literal matching a Solidity function signature,
10
10
  * resolves to a record whose keys are the parameter names and values are
11
- * `Bindable<kind>` where the kind is derived from each parameter's Solidity
11
+ * `RefBindable<kind>` where the kind is derived from each parameter's Solidity
12
12
  * type via `AbiTypeToOutputKind`. For recognised `SolType` parameters
13
13
  * (e.g. `uint256`, `address`, `uint128`) the bind slot is precisely typed;
14
14
  * for other Solidity types (e.g. tuples) the slot accepts any scalar handle.
15
- * Falls back to `Record<string, AnyBindable>` for non-literal strings.
15
+ * Falls back to `Record<string, AnyRefBindable>` for non-literal strings.
16
+ *
17
+ * These slots are lowered into `config.args`, which is positional calldata
18
+ * rather than a node `bind` record, so a literal is not accepted here: only
19
+ * values that resolve to a `$ref`.
16
20
  */
17
- type SignatureBind<TSig extends string> = string extends TSig ? Record<string, AnyBindable> : ParseAbiItem<TSig> extends {
21
+ type SignatureBind<TSig extends string> = string extends TSig ? Record<string, AnyRefBindable> : ParseAbiItem<TSig> extends {
18
22
  type: 'function';
19
23
  inputs: infer I extends readonly {
20
24
  name: string;
21
25
  type: string;
22
26
  }[];
23
27
  } ? {
24
- readonly [P in I[number] as P['name']]: Bindable<AbiTypeToOutputKind<P['type']>>;
25
- } : Record<string, AnyBindable>;
28
+ readonly [P in I[number] as P['name']]: RefBindable<AbiTypeToOutputKind<P['type']>>;
29
+ } : Record<string, AnyRefBindable>;
26
30
  /**
27
31
  * Describes the output type(s) of a parsed function signature as a
28
32
  * human-readable string for inclusion in type-level error messages.
@@ -106,14 +110,14 @@ type StaticCallResult<TSig extends string> = string extends TSig ? {
106
110
  */
107
111
  declare const parseFunctionParams: (functionSignature: string) => string[];
108
112
  /**
109
- * Converts any `AnyBindable` to a `Ref` (`{ $ref: string }`).
113
+ * Converts a ref-resolving bindable to a `Ref` (`{ $ref: string }`).
110
114
  * Handles carry a `_tag` property and are converted via `handleToRef`;
111
115
  * `Ref` objects pass through as-is.
112
116
  */
113
- declare const toBindRef: (bindable: AnyBindable) => Ref;
117
+ declare const toBindRef: (bindable: AnyRefBindable) => Ref;
114
118
  interface BuildCallWireFormatInput {
115
- readonly resource?: AnyBindable;
116
- readonly bind: Record<string, AnyBindable>;
119
+ readonly resource?: AnyRefBindable;
120
+ readonly bind: Record<string, AnyRefBindable>;
117
121
  readonly config: Record<string, unknown>;
118
122
  readonly guards?: readonly TypedGuard[];
119
123
  }
@@ -132,7 +136,7 @@ interface CallWireResult {
132
136
  */
133
137
  declare const buildCallWireFormat: (args: BuildCallWireFormatInput) => CallWireResult;
134
138
  interface BuildStaticCallWireFormatInput {
135
- readonly bind: Record<string, AnyBindable>;
139
+ readonly bind: Record<string, AnyRefBindable>;
136
140
  readonly config: Record<string, unknown>;
137
141
  readonly guards?: readonly TypedGuard[];
138
142
  }
@@ -1,28 +1,32 @@
1
1
  import { Ref } from '@lifi/compose-spec';
2
2
  import { ParseAbiItem } from 'abitype';
3
3
  import { AbiTypeToOutputKind, TypedGuard } from '../types.js';
4
- import { AnyBindable, CallArgs } from './FlowBuilderCore.js';
5
- import { Bindable, OutputHandle } from './handles.js';
4
+ import { CallArgs } from './FlowBuilderCore.js';
5
+ import { AnyRefBindable, RefBindable, OutputHandle } from './handles.js';
6
6
 
7
7
  /**
8
8
  * Type-level extraction of named bind keys from a function signature string.
9
9
  * When `TSig` is a string literal matching a Solidity function signature,
10
10
  * resolves to a record whose keys are the parameter names and values are
11
- * `Bindable<kind>` where the kind is derived from each parameter's Solidity
11
+ * `RefBindable<kind>` where the kind is derived from each parameter's Solidity
12
12
  * type via `AbiTypeToOutputKind`. For recognised `SolType` parameters
13
13
  * (e.g. `uint256`, `address`, `uint128`) the bind slot is precisely typed;
14
14
  * for other Solidity types (e.g. tuples) the slot accepts any scalar handle.
15
- * Falls back to `Record<string, AnyBindable>` for non-literal strings.
15
+ * Falls back to `Record<string, AnyRefBindable>` for non-literal strings.
16
+ *
17
+ * These slots are lowered into `config.args`, which is positional calldata
18
+ * rather than a node `bind` record, so a literal is not accepted here: only
19
+ * values that resolve to a `$ref`.
16
20
  */
17
- type SignatureBind<TSig extends string> = string extends TSig ? Record<string, AnyBindable> : ParseAbiItem<TSig> extends {
21
+ type SignatureBind<TSig extends string> = string extends TSig ? Record<string, AnyRefBindable> : ParseAbiItem<TSig> extends {
18
22
  type: 'function';
19
23
  inputs: infer I extends readonly {
20
24
  name: string;
21
25
  type: string;
22
26
  }[];
23
27
  } ? {
24
- readonly [P in I[number] as P['name']]: Bindable<AbiTypeToOutputKind<P['type']>>;
25
- } : Record<string, AnyBindable>;
28
+ readonly [P in I[number] as P['name']]: RefBindable<AbiTypeToOutputKind<P['type']>>;
29
+ } : Record<string, AnyRefBindable>;
26
30
  /**
27
31
  * Describes the output type(s) of a parsed function signature as a
28
32
  * human-readable string for inclusion in type-level error messages.
@@ -106,14 +110,14 @@ type StaticCallResult<TSig extends string> = string extends TSig ? {
106
110
  */
107
111
  declare const parseFunctionParams: (functionSignature: string) => string[];
108
112
  /**
109
- * Converts any `AnyBindable` to a `Ref` (`{ $ref: string }`).
113
+ * Converts a ref-resolving bindable to a `Ref` (`{ $ref: string }`).
110
114
  * Handles carry a `_tag` property and are converted via `handleToRef`;
111
115
  * `Ref` objects pass through as-is.
112
116
  */
113
- declare const toBindRef: (bindable: AnyBindable) => Ref;
117
+ declare const toBindRef: (bindable: AnyRefBindable) => Ref;
114
118
  interface BuildCallWireFormatInput {
115
- readonly resource?: AnyBindable;
116
- readonly bind: Record<string, AnyBindable>;
119
+ readonly resource?: AnyRefBindable;
120
+ readonly bind: Record<string, AnyRefBindable>;
117
121
  readonly config: Record<string, unknown>;
118
122
  readonly guards?: readonly TypedGuard[];
119
123
  }
@@ -132,7 +136,7 @@ interface CallWireResult {
132
136
  */
133
137
  declare const buildCallWireFormat: (args: BuildCallWireFormatInput) => CallWireResult;
134
138
  interface BuildStaticCallWireFormatInput {
135
- readonly bind: Record<string, AnyBindable>;
139
+ readonly bind: Record<string, AnyRefBindable>;
136
140
  readonly config: Record<string, unknown>;
137
141
  readonly guards?: readonly TypedGuard[];
138
142
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/authoring/signatureArgs.ts"],"sourcesContent":["import type { Ref } from '@lifi/compose-spec';\nimport { type ParseAbiItem, parseAbiItem } from 'abitype';\n\nimport type { AbiTypeToOutputKind, TypedGuard } from '../types.js';\n\nimport type { AnyBindable, CallArgs } from './FlowBuilderCore.js';\nimport type { Bindable, OutputHandle } from './handles.js';\nimport { handleToRef } from './handles.js';\n\n/**\n * Type-level extraction of named bind keys from a function signature string.\n * When `TSig` is a string literal matching a Solidity function signature,\n * resolves to a record whose keys are the parameter names and values are\n * `Bindable<kind>` where the kind is derived from each parameter's Solidity\n * type via `AbiTypeToOutputKind`. For recognised `SolType` parameters\n * (e.g. `uint256`, `address`, `uint128`) the bind slot is precisely typed;\n * for other Solidity types (e.g. tuples) the slot accepts any scalar handle.\n * Falls back to `Record<string, AnyBindable>` for non-literal strings.\n */\nexport type SignatureBind<TSig extends string> = string extends TSig\n ? Record<string, AnyBindable>\n : ParseAbiItem<TSig> extends {\n type: 'function';\n inputs: infer I extends readonly { name: string; type: string }[];\n }\n ? {\n readonly [P in I[number] as P['name']]: Bindable<\n AbiTypeToOutputKind<P['type']>\n >;\n }\n : Record<string, AnyBindable>;\n\n// ---------------------------------------------------------------------------\n// Return-type inference from function signatures\n// ---------------------------------------------------------------------------\n\n/**\n * Describes the output type(s) of a parsed function signature as a\n * human-readable string for inclusion in type-level error messages.\n */\ntype DescribeOutputs<O extends readonly { type: string }[]> =\n O extends readonly [{ type: infer T extends string }] ? T : 'multiple values';\n\n/**\n * A string-literal type that surfaces a clear error when the user provides a\n * function signature whose return type is not supported by core.call.\n * The message is visible in IDE tooltips and compiler diagnostics.\n */\ntype UnsupportedReturnTypeError<TLabel extends string, TSig extends string> =\n ParseAbiItem<TSig> extends {\n type: 'function';\n outputs: infer O extends readonly { type: string }[];\n }\n ? `[TypeError] ${TLabel}: return type '${DescribeOutputs<O>}' is not supported — only 'uint256' or void signatures are allowed`\n : `[TypeError] ${TLabel}: unable to parse return type from signature`;\n\n/**\n * Conditional output type for `core.call` based on the function signature's\n * return type:\n *\n * - `returns (uint256)` → `{ result: OutputHandle }`\n * - No returns clause → `{ result: undefined }`\n * - Anything else → `{ result: \"[TypeError] ...\" }` (compile-time error)\n *\n * When `TSig` is a non-literal `string` (e.g. a variable, not a string\n * constant), the type falls back to `{ result: OutputHandle | undefined }`\n * so that both void and uint256 call sites remain assignable.\n */\nexport type CoreCallResult<TSig extends string> = string extends TSig\n ? { readonly result: OutputHandle<'uint256'> | undefined }\n : ParseAbiItem<TSig> extends { type: 'function'; outputs: readonly [] }\n ? { readonly result: undefined }\n : ParseAbiItem<TSig> extends {\n type: 'function';\n outputs: readonly [{ type: 'uint256' }];\n }\n ? { readonly result: OutputHandle<'uint256'> }\n : { readonly result: UnsupportedReturnTypeError<'core.call', TSig> };\n\n/**\n * Conditional output type for `core.staticCall`. staticCall always reads a\n * value, so void signatures are also rejected:\n *\n * - `returns (uint256)` → `{ result: OutputHandle }`\n * - No returns / other → `{ result: \"[TypeError] ...\" }` (compile-time error)\n */\nexport type StaticCallResult<TSig extends string> = string extends TSig\n ? { readonly result: OutputHandle<'uint256'> }\n : ParseAbiItem<TSig> extends {\n type: 'function';\n outputs: readonly [{ type: 'uint256' }];\n }\n ? { readonly result: OutputHandle<'uint256'> }\n : ParseAbiItem<TSig> extends { type: 'function'; outputs: readonly [] }\n ? {\n readonly result: `[TypeError] core.staticCall: function has no return value — staticCall requires a 'returns (uint256)' signature`;\n }\n : {\n readonly result: UnsupportedReturnTypeError<'core.staticCall', TSig>;\n };\n\n/**\n * Parses a Solidity function signature and returns parameter names in order.\n * Delegates to abitype's runtime `parseAbiItem` — the same parser that powers\n * the compile-time `ParseAbiItem<TSig>` type — so runtime and type-level\n * parsing can never diverge.\n *\n * @throws if the signature is not a valid function signature or a parameter is unnamed\n */\nexport const parseFunctionParams = (functionSignature: string): string[] => {\n const parsed = parseAbiItem(functionSignature);\n if (parsed.type !== 'function') {\n throw new Error(\n `core.call: expected a function signature, got \"${parsed.type}\": \"${functionSignature}\"`,\n );\n }\n\n return parsed.inputs.map((input, index) => {\n if (!input.name) {\n throw new Error(\n `core.call: parameter at index ${index} in signature has no name. All parameters must be named for named binds to work.`,\n );\n }\n return input.name;\n });\n};\n\n/**\n * Converts any `AnyBindable` to a `Ref` (`{ $ref: string }`).\n * Handles carry a `_tag` property and are converted via `handleToRef`;\n * `Ref` objects pass through as-is.\n */\nexport const toBindRef = (bindable: AnyBindable): Ref => {\n if ('_tag' in bindable) return handleToRef(bindable);\n return bindable;\n};\n\nexport interface BuildCallWireFormatInput {\n readonly resource?: AnyBindable;\n readonly bind: Record<string, AnyBindable>;\n readonly config: Record<string, unknown>;\n readonly guards?: readonly TypedGuard[];\n}\n\nexport interface CallWireResult {\n readonly op: 'core.call' | 'core.invoke';\n readonly bind: Record<string, Ref>;\n readonly config: Record<string, unknown>;\n readonly guards?: readonly TypedGuard[];\n}\n\n/**\n * Parses a function signature from `config`, validates bind keys against it,\n * and converts named binds to positional `$ref` args.\n */\nconst resolvePositionalArgs = (\n label: string,\n bind: Record<string, AnyBindable>,\n config: Record<string, unknown>,\n): Ref[] => {\n const functionSignature = config.functionSignature;\n if (typeof functionSignature !== 'string') {\n throw new Error(\n `${label}: config.functionSignature is required and must be a string`,\n );\n }\n\n const paramNames = parseFunctionParams(functionSignature);\n const paramSet = new Set(paramNames);\n const bindKeys = Object.keys(bind);\n const bindSet = new Set(bindKeys);\n\n const missing = paramNames.filter((n) => !bindSet.has(n));\n const extra = bindKeys.filter((k) => !paramSet.has(k));\n\n if (missing.length > 0 || extra.length > 0) {\n throw new Error(\n `${label}: bind keys [${bindKeys.join(\n ', ',\n )}] do not match signature parameters [${paramNames.join(\n ', ',\n )}]. Missing: ${missing.join(', ') || '(none)'}. Extra: ${\n extra.join(', ') || '(none)'\n }.`,\n );\n }\n\n return paramNames.map((name) => {\n const bindable = bind[name];\n if (bindable === undefined) {\n throw new Error(\n `${label}: bind value for parameter \"${name}\" is undefined`,\n );\n }\n return toBindRef(bindable);\n });\n};\n\n/**\n * Transforms the user-facing `resource` + named `bind` API into the wire\n * format that the backend expects:\n * - With resource: `core.call` with `bind: { input: <resourceRef> }`\n * - Without resource: `core.invoke` with `bind: {}`\n * - `config: { ...userConfig, args: [positional refs...] }`\n */\nexport const buildCallWireFormat = (\n args: BuildCallWireFormatInput,\n): CallWireResult => {\n const positionalArgs = resolvePositionalArgs(\n 'core.call',\n args.bind,\n args.config,\n );\n const hasResource = args.resource !== undefined;\n\n return {\n op: hasResource ? 'core.call' : 'core.invoke',\n bind: hasResource ? { input: toBindRef(args.resource) } : {},\n config: { ...args.config, args: positionalArgs },\n ...(args.guards && args.guards.length > 0 && { guards: args.guards }),\n };\n};\n\nexport interface BuildStaticCallWireFormatInput {\n readonly bind: Record<string, AnyBindable>;\n readonly config: Record<string, unknown>;\n readonly guards?: readonly TypedGuard[];\n}\n\n/**\n * Transforms the user-facing named `bind` API into the wire format that the\n * backend expects for `core.staticCall`. Same as `buildCallWireFormat` but\n * with no `resource` field — staticCall has no resource inputs.\n */\nexport const buildStaticCallWireFormat = (\n args: BuildStaticCallWireFormatInput,\n): CallArgs => {\n const positionalArgs = resolvePositionalArgs(\n 'core.staticCall',\n args.bind,\n args.config,\n );\n\n return {\n bind: {},\n config: { ...args.config, args: positionalArgs },\n ...(args.guards && args.guards.length > 0 && { guards: args.guards }),\n };\n};\n"],"mappings":"AACA,SAA4B,oBAAoB;AAMhD,SAAS,mBAAmB;AAsGrB,MAAM,sBAAsB,CAAC,sBAAwC;AAC1E,QAAM,SAAS,aAAa,iBAAiB;AAC7C,MAAI,OAAO,SAAS,YAAY;AAC9B,UAAM,IAAI;AAAA,MACR,kDAAkD,OAAO,IAAI,OAAO,iBAAiB;AAAA,IACvF;AAAA,EACF;AAEA,SAAO,OAAO,OAAO,IAAI,CAAC,OAAO,UAAU;AACzC,QAAI,CAAC,MAAM,MAAM;AACf,YAAM,IAAI;AAAA,QACR,iCAAiC,KAAK;AAAA,MACxC;AAAA,IACF;AACA,WAAO,MAAM;AAAA,EACf,CAAC;AACH;AAOO,MAAM,YAAY,CAAC,aAA+B;AACvD,MAAI,UAAU,SAAU,QAAO,YAAY,QAAQ;AACnD,SAAO;AACT;AAoBA,MAAM,wBAAwB,CAC5B,OACA,MACA,WACU;AACV,QAAM,oBAAoB,OAAO;AACjC,MAAI,OAAO,sBAAsB,UAAU;AACzC,UAAM,IAAI;AAAA,MACR,GAAG,KAAK;AAAA,IACV;AAAA,EACF;AAEA,QAAM,aAAa,oBAAoB,iBAAiB;AACxD,QAAM,WAAW,IAAI,IAAI,UAAU;AACnC,QAAM,WAAW,OAAO,KAAK,IAAI;AACjC,QAAM,UAAU,IAAI,IAAI,QAAQ;AAEhC,QAAM,UAAU,WAAW,OAAO,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;AACxD,QAAM,QAAQ,SAAS,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;AAErD,MAAI,QAAQ,SAAS,KAAK,MAAM,SAAS,GAAG;AAC1C,UAAM,IAAI;AAAA,MACR,GAAG,KAAK,gBAAgB,SAAS;AAAA,QAC/B;AAAA,MACF,CAAC,wCAAwC,WAAW;AAAA,QAClD;AAAA,MACF,CAAC,eAAe,QAAQ,KAAK,IAAI,KAAK,QAAQ,YAC5C,MAAM,KAAK,IAAI,KAAK,QACtB;AAAA,IACF;AAAA,EACF;AAEA,SAAO,WAAW,IAAI,CAAC,SAAS;AAC9B,UAAM,WAAW,KAAK,IAAI;AAC1B,QAAI,aAAa,QAAW;AAC1B,YAAM,IAAI;AAAA,QACR,GAAG,KAAK,+BAA+B,IAAI;AAAA,MAC7C;AAAA,IACF;AACA,WAAO,UAAU,QAAQ;AAAA,EAC3B,CAAC;AACH;AASO,MAAM,sBAAsB,CACjC,SACmB;AACnB,QAAM,iBAAiB;AAAA,IACrB;AAAA,IACA,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AACA,QAAM,cAAc,KAAK,aAAa;AAEtC,SAAO;AAAA,IACL,IAAI,cAAc,cAAc;AAAA,IAChC,MAAM,cAAc,EAAE,OAAO,UAAU,KAAK,QAAQ,EAAE,IAAI,CAAC;AAAA,IAC3D,QAAQ,EAAE,GAAG,KAAK,QAAQ,MAAM,eAAe;AAAA,IAC/C,GAAI,KAAK,UAAU,KAAK,OAAO,SAAS,KAAK,EAAE,QAAQ,KAAK,OAAO;AAAA,EACrE;AACF;AAaO,MAAM,4BAA4B,CACvC,SACa;AACb,QAAM,iBAAiB;AAAA,IACrB;AAAA,IACA,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AAEA,SAAO;AAAA,IACL,MAAM,CAAC;AAAA,IACP,QAAQ,EAAE,GAAG,KAAK,QAAQ,MAAM,eAAe;AAAA,IAC/C,GAAI,KAAK,UAAU,KAAK,OAAO,SAAS,KAAK,EAAE,QAAQ,KAAK,OAAO;AAAA,EACrE;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/authoring/signatureArgs.ts"],"sourcesContent":["import type { Ref } from '@lifi/compose-spec';\nimport { type ParseAbiItem, parseAbiItem } from 'abitype';\n\nimport type { AbiTypeToOutputKind, TypedGuard } from '../types.js';\n\nimport type { CallArgs } from './FlowBuilderCore.js';\nimport type { AnyRefBindable, OutputHandle, RefBindable } from './handles.js';\nimport { handleToRef } from './handles.js';\n\n/**\n * Type-level extraction of named bind keys from a function signature string.\n * When `TSig` is a string literal matching a Solidity function signature,\n * resolves to a record whose keys are the parameter names and values are\n * `RefBindable<kind>` where the kind is derived from each parameter's Solidity\n * type via `AbiTypeToOutputKind`. For recognised `SolType` parameters\n * (e.g. `uint256`, `address`, `uint128`) the bind slot is precisely typed;\n * for other Solidity types (e.g. tuples) the slot accepts any scalar handle.\n * Falls back to `Record<string, AnyRefBindable>` for non-literal strings.\n *\n * These slots are lowered into `config.args`, which is positional calldata\n * rather than a node `bind` record, so a literal is not accepted here: only\n * values that resolve to a `$ref`.\n */\nexport type SignatureBind<TSig extends string> = string extends TSig\n ? Record<string, AnyRefBindable>\n : ParseAbiItem<TSig> extends {\n type: 'function';\n inputs: infer I extends readonly { name: string; type: string }[];\n }\n ? {\n readonly [P in I[number] as P['name']]: RefBindable<\n AbiTypeToOutputKind<P['type']>\n >;\n }\n : Record<string, AnyRefBindable>;\n\n// ---------------------------------------------------------------------------\n// Return-type inference from function signatures\n// ---------------------------------------------------------------------------\n\n/**\n * Describes the output type(s) of a parsed function signature as a\n * human-readable string for inclusion in type-level error messages.\n */\ntype DescribeOutputs<O extends readonly { type: string }[]> =\n O extends readonly [{ type: infer T extends string }] ? T : 'multiple values';\n\n/**\n * A string-literal type that surfaces a clear error when the user provides a\n * function signature whose return type is not supported by core.call.\n * The message is visible in IDE tooltips and compiler diagnostics.\n */\ntype UnsupportedReturnTypeError<TLabel extends string, TSig extends string> =\n ParseAbiItem<TSig> extends {\n type: 'function';\n outputs: infer O extends readonly { type: string }[];\n }\n ? `[TypeError] ${TLabel}: return type '${DescribeOutputs<O>}' is not supported — only 'uint256' or void signatures are allowed`\n : `[TypeError] ${TLabel}: unable to parse return type from signature`;\n\n/**\n * Conditional output type for `core.call` based on the function signature's\n * return type:\n *\n * - `returns (uint256)` → `{ result: OutputHandle }`\n * - No returns clause → `{ result: undefined }`\n * - Anything else → `{ result: \"[TypeError] ...\" }` (compile-time error)\n *\n * When `TSig` is a non-literal `string` (e.g. a variable, not a string\n * constant), the type falls back to `{ result: OutputHandle | undefined }`\n * so that both void and uint256 call sites remain assignable.\n */\nexport type CoreCallResult<TSig extends string> = string extends TSig\n ? { readonly result: OutputHandle<'uint256'> | undefined }\n : ParseAbiItem<TSig> extends { type: 'function'; outputs: readonly [] }\n ? { readonly result: undefined }\n : ParseAbiItem<TSig> extends {\n type: 'function';\n outputs: readonly [{ type: 'uint256' }];\n }\n ? { readonly result: OutputHandle<'uint256'> }\n : { readonly result: UnsupportedReturnTypeError<'core.call', TSig> };\n\n/**\n * Conditional output type for `core.staticCall`. staticCall always reads a\n * value, so void signatures are also rejected:\n *\n * - `returns (uint256)` → `{ result: OutputHandle }`\n * - No returns / other → `{ result: \"[TypeError] ...\" }` (compile-time error)\n */\nexport type StaticCallResult<TSig extends string> = string extends TSig\n ? { readonly result: OutputHandle<'uint256'> }\n : ParseAbiItem<TSig> extends {\n type: 'function';\n outputs: readonly [{ type: 'uint256' }];\n }\n ? { readonly result: OutputHandle<'uint256'> }\n : ParseAbiItem<TSig> extends { type: 'function'; outputs: readonly [] }\n ? {\n readonly result: `[TypeError] core.staticCall: function has no return value — staticCall requires a 'returns (uint256)' signature`;\n }\n : {\n readonly result: UnsupportedReturnTypeError<'core.staticCall', TSig>;\n };\n\n/**\n * Parses a Solidity function signature and returns parameter names in order.\n * Delegates to abitype's runtime `parseAbiItem` — the same parser that powers\n * the compile-time `ParseAbiItem<TSig>` type — so runtime and type-level\n * parsing can never diverge.\n *\n * @throws if the signature is not a valid function signature or a parameter is unnamed\n */\nexport const parseFunctionParams = (functionSignature: string): string[] => {\n const parsed = parseAbiItem(functionSignature);\n if (parsed.type !== 'function') {\n throw new Error(\n `core.call: expected a function signature, got \"${parsed.type}\": \"${functionSignature}\"`,\n );\n }\n\n return parsed.inputs.map((input, index) => {\n if (!input.name) {\n throw new Error(\n `core.call: parameter at index ${index} in signature has no name. All parameters must be named for named binds to work.`,\n );\n }\n return input.name;\n });\n};\n\n/**\n * Converts a ref-resolving bindable to a `Ref` (`{ $ref: string }`).\n * Handles carry a `_tag` property and are converted via `handleToRef`;\n * `Ref` objects pass through as-is.\n */\nexport const toBindRef = (bindable: AnyRefBindable): Ref => {\n if ('_tag' in bindable) return handleToRef(bindable);\n return bindable;\n};\n\nexport interface BuildCallWireFormatInput {\n readonly resource?: AnyRefBindable;\n readonly bind: Record<string, AnyRefBindable>;\n readonly config: Record<string, unknown>;\n readonly guards?: readonly TypedGuard[];\n}\n\nexport interface CallWireResult {\n readonly op: 'core.call' | 'core.invoke';\n readonly bind: Record<string, Ref>;\n readonly config: Record<string, unknown>;\n readonly guards?: readonly TypedGuard[];\n}\n\n/**\n * Parses a function signature from `config`, validates bind keys against it,\n * and converts named binds to positional `$ref` args.\n */\nconst resolvePositionalArgs = (\n label: string,\n bind: Record<string, AnyRefBindable>,\n config: Record<string, unknown>,\n): Ref[] => {\n const functionSignature = config.functionSignature;\n if (typeof functionSignature !== 'string') {\n throw new Error(\n `${label}: config.functionSignature is required and must be a string`,\n );\n }\n\n const paramNames = parseFunctionParams(functionSignature);\n const paramSet = new Set(paramNames);\n const bindKeys = Object.keys(bind);\n const bindSet = new Set(bindKeys);\n\n const missing = paramNames.filter((n) => !bindSet.has(n));\n const extra = bindKeys.filter((k) => !paramSet.has(k));\n\n if (missing.length > 0 || extra.length > 0) {\n throw new Error(\n `${label}: bind keys [${bindKeys.join(\n ', ',\n )}] do not match signature parameters [${paramNames.join(\n ', ',\n )}]. Missing: ${missing.join(', ') || '(none)'}. Extra: ${\n extra.join(', ') || '(none)'\n }.`,\n );\n }\n\n return paramNames.map((name) => {\n const bindable = bind[name];\n if (bindable === undefined) {\n throw new Error(\n `${label}: bind value for parameter \"${name}\" is undefined`,\n );\n }\n return toBindRef(bindable);\n });\n};\n\n/**\n * Transforms the user-facing `resource` + named `bind` API into the wire\n * format that the backend expects:\n * - With resource: `core.call` with `bind: { input: <resourceRef> }`\n * - Without resource: `core.invoke` with `bind: {}`\n * - `config: { ...userConfig, args: [positional refs...] }`\n */\nexport const buildCallWireFormat = (\n args: BuildCallWireFormatInput,\n): CallWireResult => {\n const positionalArgs = resolvePositionalArgs(\n 'core.call',\n args.bind,\n args.config,\n );\n const hasResource = args.resource !== undefined;\n\n return {\n op: hasResource ? 'core.call' : 'core.invoke',\n bind: hasResource ? { input: toBindRef(args.resource) } : {},\n config: { ...args.config, args: positionalArgs },\n ...(args.guards && args.guards.length > 0 && { guards: args.guards }),\n };\n};\n\nexport interface BuildStaticCallWireFormatInput {\n readonly bind: Record<string, AnyRefBindable>;\n readonly config: Record<string, unknown>;\n readonly guards?: readonly TypedGuard[];\n}\n\n/**\n * Transforms the user-facing named `bind` API into the wire format that the\n * backend expects for `core.staticCall`. Same as `buildCallWireFormat` but\n * with no `resource` field — staticCall has no resource inputs.\n */\nexport const buildStaticCallWireFormat = (\n args: BuildStaticCallWireFormatInput,\n): CallArgs => {\n const positionalArgs = resolvePositionalArgs(\n 'core.staticCall',\n args.bind,\n args.config,\n );\n\n return {\n bind: {},\n config: { ...args.config, args: positionalArgs },\n ...(args.guards && args.guards.length > 0 && { guards: args.guards }),\n };\n};\n"],"mappings":"AACA,SAA4B,oBAAoB;AAMhD,SAAS,mBAAmB;AA0GrB,MAAM,sBAAsB,CAAC,sBAAwC;AAC1E,QAAM,SAAS,aAAa,iBAAiB;AAC7C,MAAI,OAAO,SAAS,YAAY;AAC9B,UAAM,IAAI;AAAA,MACR,kDAAkD,OAAO,IAAI,OAAO,iBAAiB;AAAA,IACvF;AAAA,EACF;AAEA,SAAO,OAAO,OAAO,IAAI,CAAC,OAAO,UAAU;AACzC,QAAI,CAAC,MAAM,MAAM;AACf,YAAM,IAAI;AAAA,QACR,iCAAiC,KAAK;AAAA,MACxC;AAAA,IACF;AACA,WAAO,MAAM;AAAA,EACf,CAAC;AACH;AAOO,MAAM,YAAY,CAAC,aAAkC;AAC1D,MAAI,UAAU,SAAU,QAAO,YAAY,QAAQ;AACnD,SAAO;AACT;AAoBA,MAAM,wBAAwB,CAC5B,OACA,MACA,WACU;AACV,QAAM,oBAAoB,OAAO;AACjC,MAAI,OAAO,sBAAsB,UAAU;AACzC,UAAM,IAAI;AAAA,MACR,GAAG,KAAK;AAAA,IACV;AAAA,EACF;AAEA,QAAM,aAAa,oBAAoB,iBAAiB;AACxD,QAAM,WAAW,IAAI,IAAI,UAAU;AACnC,QAAM,WAAW,OAAO,KAAK,IAAI;AACjC,QAAM,UAAU,IAAI,IAAI,QAAQ;AAEhC,QAAM,UAAU,WAAW,OAAO,CAAC,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;AACxD,QAAM,QAAQ,SAAS,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;AAErD,MAAI,QAAQ,SAAS,KAAK,MAAM,SAAS,GAAG;AAC1C,UAAM,IAAI;AAAA,MACR,GAAG,KAAK,gBAAgB,SAAS;AAAA,QAC/B;AAAA,MACF,CAAC,wCAAwC,WAAW;AAAA,QAClD;AAAA,MACF,CAAC,eAAe,QAAQ,KAAK,IAAI,KAAK,QAAQ,YAC5C,MAAM,KAAK,IAAI,KAAK,QACtB;AAAA,IACF;AAAA,EACF;AAEA,SAAO,WAAW,IAAI,CAAC,SAAS;AAC9B,UAAM,WAAW,KAAK,IAAI;AAC1B,QAAI,aAAa,QAAW;AAC1B,YAAM,IAAI;AAAA,QACR,GAAG,KAAK,+BAA+B,IAAI;AAAA,MAC7C;AAAA,IACF;AACA,WAAO,UAAU,QAAQ;AAAA,EAC3B,CAAC;AACH;AASO,MAAM,sBAAsB,CACjC,SACmB;AACnB,QAAM,iBAAiB;AAAA,IACrB;AAAA,IACA,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AACA,QAAM,cAAc,KAAK,aAAa;AAEtC,SAAO;AAAA,IACL,IAAI,cAAc,cAAc;AAAA,IAChC,MAAM,cAAc,EAAE,OAAO,UAAU,KAAK,QAAQ,EAAE,IAAI,CAAC;AAAA,IAC3D,QAAQ,EAAE,GAAG,KAAK,QAAQ,MAAM,eAAe;AAAA,IAC/C,GAAI,KAAK,UAAU,KAAK,OAAO,SAAS,KAAK,EAAE,QAAQ,KAAK,OAAO;AAAA,EACrE;AACF;AAaO,MAAM,4BAA4B,CACvC,SACa;AACb,QAAM,iBAAiB;AAAA,IACrB;AAAA,IACA,KAAK;AAAA,IACL,KAAK;AAAA,EACP;AAEA,SAAO;AAAA,IACL,MAAM,CAAC;AAAA,IACP,QAAQ,EAAE,GAAG,KAAK,QAAQ,MAAM,eAAe;AAAA,IAC/C,GAAI,KAAK,UAAU,KAAK,OAAO,SAAS,KAAK,EAAE,QAAQ,KAAK,OAAO;AAAA,EACrE;AACF;","names":[]}
package/dist/client.cjs CHANGED
@@ -18,14 +18,60 @@ var __copyProps = (to, from, except, desc) => {
18
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
19
  var client_exports = {};
20
20
  __export(client_exports, {
21
+ bigintReplacer: () => bigintReplacer,
21
22
  createComposeClient: () => createComposeClient
22
23
  });
23
24
  module.exports = __toCommonJS(client_exports);
25
+ var import_compose_spec = require("@lifi/compose-spec");
24
26
  var import_errors = require("./errors.js");
25
27
  var import_responseSchemas = require("./responseSchemas.js");
26
- const SDK_VERSION = true ? "0.4.1" : "dev";
28
+ const SDK_VERSION = true ? "0.5.1" : "dev";
29
+ const SDK_VERSION_TRIPLE = (0, import_compose_spec.parseVersion)(SDK_VERSION);
27
30
  const bigintReplacer = (_key, value) => typeof value === "bigint" ? value.toString() : value;
28
31
  const isNonNullObject = (v) => typeof v === "object" && v !== null;
32
+ const INTEGER_STRING = /^\d+$/;
33
+ const decodeAmount = (value, key, field, url) => {
34
+ if (typeof value !== "string" || !INTEGER_STRING.test(value)) {
35
+ throw new import_errors.ComposeError(
36
+ "UNKNOWN_ERROR",
37
+ `Malformed compose response: producedResources.${key}.simulated.${field} is ${value === void 0 ? "absent" : "not an integer string"}`,
38
+ { url }
39
+ );
40
+ }
41
+ return BigInt(value);
42
+ };
43
+ const decodeSimulatedAmounts = (data, url) => {
44
+ if (!isNonNullObject(data)) return;
45
+ const resources = data.producedResources;
46
+ if (!isNonNullObject(resources)) return;
47
+ for (const key of Object.keys(resources)) {
48
+ const resource = resources[key];
49
+ if (!isNonNullObject(resource)) continue;
50
+ const simulated = resource.simulated;
51
+ if (simulated === void 0) continue;
52
+ if (!isNonNullObject(simulated)) {
53
+ throw new import_errors.ComposeError(
54
+ "UNKNOWN_ERROR",
55
+ `Malformed compose response: producedResources.${key}.simulated is not an object`,
56
+ { url }
57
+ );
58
+ }
59
+ simulated.amountOut = decodeAmount(
60
+ simulated.amountOut,
61
+ key,
62
+ "amountOut",
63
+ url
64
+ );
65
+ if (simulated.amountOutMin !== void 0) {
66
+ simulated.amountOutMin = decodeAmount(
67
+ simulated.amountOutMin,
68
+ key,
69
+ "amountOutMin",
70
+ url
71
+ );
72
+ }
73
+ }
74
+ };
29
75
  const parseBody = async (res, url) => {
30
76
  const body = await res.json().catch((_) => null);
31
77
  if (!isNonNullObject(body) || !("data" in body)) {
@@ -37,6 +83,7 @@ const parseBody = async (res, url) => {
37
83
  };
38
84
  const parseCompileSuccessBody = async (res, url) => {
39
85
  const data = await parseBody(res, url);
86
+ decodeSimulatedAmounts(data, url);
40
87
  return { ...data, status: "success" };
41
88
  };
42
89
  const parsePartialBody = async (res, url) => {
@@ -50,6 +97,7 @@ const parsePartialBody = async (res, url) => {
50
97
  );
51
98
  }
52
99
  const data = envelope.data;
100
+ decodeSimulatedAmounts(data, url);
53
101
  return { ...data, status: "partial", error: envelope.error };
54
102
  };
55
103
  const parseSimulateBody = async (res, url) => {
@@ -64,18 +112,56 @@ const parseSimulateBody = async (res, url) => {
64
112
  }
65
113
  return result;
66
114
  };
67
- const postCompile = async ({ fetchFn, baseHeaders }, url, request) => {
68
- let res;
115
+ const assertServerVersion = (res, url) => {
116
+ const serverVersion = res.headers.get(import_compose_spec.COMPOSE_VERSION_HEADER);
117
+ if (serverVersion === null || SDK_VERSION_TRIPLE === void 0) return;
118
+ const serverTriple = (0, import_compose_spec.parseVersion)(serverVersion);
119
+ if (serverTriple === void 0) return;
120
+ const compatibility = (0, import_compose_spec.checkCompatibility)(SDK_VERSION_TRIPLE, serverTriple);
121
+ if (compatibility === "compatible") return;
122
+ const versions = { sdkVersion: SDK_VERSION, serverVersion };
123
+ if (compatibility === "sdk_outdated") {
124
+ throw new import_errors.ComposeError(
125
+ "VALIDATION_ERROR",
126
+ `composer-sdk ${SDK_VERSION} is older than the compose contract served at ${url} (${serverVersion}); upgrade @lifi/composer-sdk and @lifi/compose-spec`,
127
+ {
128
+ status: res.status,
129
+ url,
130
+ kind: "sdk_outdated",
131
+ sdkOutdated: {
132
+ ...versions,
133
+ minimumSdkVersion: `${serverTriple.major}.${serverTriple.minor}.0`
134
+ }
135
+ }
136
+ );
137
+ }
138
+ throw new import_errors.ComposeError(
139
+ "VALIDATION_ERROR",
140
+ `composer-sdk ${SDK_VERSION} targets a newer compose contract than the server at ${url} serves (${serverVersion}); pin @lifi/composer-sdk and @lifi/compose-spec to ${serverVersion} or wait for the server rollout`,
141
+ {
142
+ status: res.status,
143
+ url,
144
+ kind: "server_outdated",
145
+ serverOutdated: versions
146
+ }
147
+ );
148
+ };
149
+ const requestInit = (baseHeaders, init) => init.method === "GET" ? { method: "GET", headers: { ...baseHeaders } } : {
150
+ method: "POST",
151
+ headers: { ...baseHeaders, "Content-Type": "application/json" },
152
+ body: JSON.stringify(init.body, bigintReplacer)
153
+ };
154
+ const send = async ({ fetchFn, baseHeaders }, url, init) => {
69
155
  try {
70
- res = await fetchFn(url, {
71
- method: "POST",
72
- headers: { ...baseHeaders, "Content-Type": "application/json" },
73
- body: JSON.stringify(request, bigintReplacer)
74
- });
156
+ return await fetchFn(url, requestInit(baseHeaders, init));
75
157
  } catch (err) {
76
158
  const message = err instanceof Error ? err.message : String(err);
77
159
  throw new import_errors.ComposeError("NETWORK_ERROR", message, { cause: err });
78
160
  }
161
+ };
162
+ const postCompile = async (transport, url, request) => {
163
+ const res = await send(transport, url, { method: "POST", body: request });
164
+ assertServerVersion(res, url);
79
165
  if (res.status === 206) {
80
166
  return await parsePartialBody(res, url);
81
167
  }
@@ -85,6 +171,14 @@ const postCompile = async ({ fetchFn, baseHeaders }, url, request) => {
85
171
  }
86
172
  return await parseCompileSuccessBody(res, url);
87
173
  };
174
+ const getJson = async (transport, url) => {
175
+ const res = await send(transport, url, { method: "GET" });
176
+ if (!res.ok) {
177
+ const body = await res.text();
178
+ throw (0, import_errors.errorFromHttpResponse)(res.status, body, url);
179
+ }
180
+ return await parseBody(res, url);
181
+ };
88
182
  const createComposeClient = (options) => {
89
183
  if (!options.baseUrl || !/^https?:\/\//i.test(options.baseUrl)) {
90
184
  throw new import_errors.ComposeError(
@@ -103,28 +197,12 @@ const createComposeClient = (options) => {
103
197
  const base = options.baseUrl.replace(/\/$/, "");
104
198
  const baseHeaders = {
105
199
  Accept: "application/json",
106
- "x-lifi-composer-sdk": SDK_VERSION,
200
+ [import_compose_spec.COMPOSER_SDK_VERSION_HEADER]: SDK_VERSION,
107
201
  "x-lifi-api-key": trimmedApiKey
108
202
  };
109
203
  const transport = { fetchFn, baseHeaders };
110
- const getManifest = async () => {
111
- const url = `${base}/compose/manifest`;
112
- let res;
113
- try {
114
- res = await fetchFn(url, {
115
- method: "GET",
116
- headers: { ...baseHeaders }
117
- });
118
- } catch (err) {
119
- const message = err instanceof Error ? err.message : String(err);
120
- throw new import_errors.ComposeError("NETWORK_ERROR", message, { cause: err });
121
- }
122
- if (!res.ok) {
123
- const body = await res.text();
124
- throw (0, import_errors.errorFromHttpResponse)(res.status, body, url);
125
- }
126
- return await parseBody(res, url);
127
- };
204
+ const getManifest = async () => getJson(transport, `${base}/compose/manifest`);
205
+ const getChains = async () => getJson(transport, `${base}/chains`);
128
206
  const compile = async (request) => postCompile(transport, `${base}/compose`, request);
129
207
  const route = async (request) => postCompile(transport, `${base}/compose/route`, request);
130
208
  const getZapPacks = async (options2) => {
@@ -135,46 +213,25 @@ const createComposeClient = (options) => {
135
213
  params.set("protocols", list);
136
214
  }
137
215
  const qs = params.toString();
138
- const url = `${base}/compose/zap-packs${qs ? `?${qs}` : ""}`;
139
- let res;
140
- try {
141
- res = await fetchFn(url, {
142
- method: "GET",
143
- headers: { ...baseHeaders }
144
- });
145
- } catch (err) {
146
- const message = err instanceof Error ? err.message : String(err);
147
- throw new import_errors.ComposeError("NETWORK_ERROR", message, { cause: err });
148
- }
149
- if (!res.ok) {
150
- const body = await res.text();
151
- throw (0, import_errors.errorFromHttpResponse)(res.status, body, url);
152
- }
153
- return await parseBody(res, url);
216
+ return getJson(
217
+ transport,
218
+ `${base}/compose/zap-packs${qs ? `?${qs}` : ""}`
219
+ );
154
220
  };
155
221
  const simulate = async (request) => {
156
222
  const url = `${base}/simulate`;
157
- let res;
158
- try {
159
- res = await fetchFn(url, {
160
- method: "POST",
161
- headers: { ...baseHeaders, "Content-Type": "application/json" },
162
- body: JSON.stringify(request, bigintReplacer)
163
- });
164
- } catch (err) {
165
- const message = err instanceof Error ? err.message : String(err);
166
- throw new import_errors.ComposeError("NETWORK_ERROR", message, { cause: err });
167
- }
223
+ const res = await send(transport, url, { method: "POST", body: request });
168
224
  if (res.status === 200 || res.status === 422) {
169
225
  return await parseSimulateBody(res, url);
170
226
  }
171
227
  const body = await res.text();
172
228
  throw (0, import_errors.errorFromHttpResponse)(res.status, body, url);
173
229
  };
174
- return { getManifest, compile, route, getZapPacks, simulate };
230
+ return { getManifest, getChains, compile, route, getZapPacks, simulate };
175
231
  };
176
232
  // Annotate the CommonJS export names for ESM import in node:
177
233
  0 && (module.exports = {
234
+ bigintReplacer,
178
235
  createComposeClient
179
236
  });
180
237
  //# sourceMappingURL=client.cjs.map