@lifi/compose-spec
Version:
Public wire-format types and schemas for Compose flows
424 lines (416 loc) • 11.5 kB
JavaScript
// src/flowSchema.ts
import { Schema } from "effect";
var SolTypeSchema = Schema.Literal(
"uint8",
"uint16",
"uint32",
"uint64",
"uint128",
"uint256",
"int128",
"int256",
"address",
"bool",
"bytes",
"bytes4",
"bytes32",
"string"
);
var ChainIdSchema = Schema.Number.pipe(Schema.int(), Schema.positive());
var ResourceSchema = Schema.Union(
Schema.Struct({ kind: Schema.Literal("native"), chainId: ChainIdSchema }),
Schema.Struct({
kind: Schema.Literal("erc20"),
token: Schema.String,
chainId: ChainIdSchema
})
);
var ResourceInputSchema = Schema.Struct({
name: Schema.String,
resource: ResourceSchema
});
var HandleInputSchema = Schema.Struct({
name: Schema.String,
type: SolTypeSchema
});
var FlowInputSchema = Schema.Union(
ResourceInputSchema,
HandleInputSchema
);
var RefSchema = Schema.Struct({
$ref: Schema.String.pipe(
Schema.filter((s) => s.indexOf(".") > 0, {
message: () => '$ref must be a dotpath like "input.name" or "nodeId.port"'
})
)
});
var LiteralBindingSchema = Schema.Struct({
kind: SolTypeSchema,
value: Schema.String
});
var BindValueSchema = Schema.Union(RefSchema, LiteralBindingSchema);
var AppliedGuardSchema = Schema.Struct(
{ kind: Schema.String },
{ key: Schema.String, value: Schema.Unknown }
);
var CallSchema = Schema.Struct({
id: Schema.String,
op: Schema.String,
bind: Schema.optionalWith(
Schema.Record({ key: Schema.String, value: BindValueSchema }),
{ default: () => ({}) }
),
config: Schema.optionalWith(
Schema.Record({ key: Schema.String, value: Schema.Unknown }),
{ default: () => ({}) }
),
guards: Schema.optional(Schema.Array(AppliedGuardSchema))
});
var ContinuationSchema = Schema.Struct({
awaits: Schema.String,
flowId: Schema.String
});
var FlowSchema = Schema.Struct({
version: Schema.Literal(1),
id: Schema.String,
chainId: ChainIdSchema,
inputs: Schema.Array(FlowInputSchema),
nodes: Schema.Array(CallSchema),
continuation: Schema.optional(ContinuationSchema)
});
// src/flow.ts
var isResourceInput = (input) => "resource" in input;
var flowInputType = (input) => isResourceInput(input) ? "uint256" : input.type;
var flowInputMode = (input) => isResourceInput(input) ? "linear" : "copy";
var flowInputResource = (input) => isResourceInput(input) ? input.resource : void 0;
var isResourcePort = (port) => port.kind === "resource";
var isHandlePort = (port) => port.kind === "handle";
var isResourceOutputPort = (port) => port.kind === "resource_output";
var isMaterialiserInput = (s) => typeof s === "object" && s !== null && "kind" in s;
// src/resource.ts
var erc20Resource = (token, chainId) => ({
kind: "erc20",
token,
chainId
});
var nativeResource = (chainId) => ({
kind: "native",
chainId
});
var isERC20Resource = (r) => r.kind === "erc20";
var isNativeResource = (r) => r.kind === "native";
var resourcesEqual = (a, b) => {
if (isNativeResource(a) && isNativeResource(b)) {
return a.chainId === b.chainId;
}
if (isERC20Resource(a) && isERC20Resource(b)) {
return a.chainId === b.chainId && a.token.toLowerCase() === b.token.toLowerCase();
}
return false;
};
var foldResource = (r, cases) => r.kind === "native" ? cases.native(r.chainId) : cases.erc20(r.token, r.chainId);
var resourceKey = (r) => foldResource(r, {
native: (chainId) => `native:${chainId}`,
erc20: (token, chainId) => `erc20:${token.toLowerCase()}:${chainId}`
});
var erc20Token = (r) => {
if (r.kind !== "erc20")
throw new Error(`Expected erc20 resource, got ${r.kind}`);
return r.token;
};
// src/flashloan.ts
var PROVIDER_KIND = {
AAVE_V3: 0,
ERC3156: 1,
BALANCER_V2: 2,
MORPHO_BLUE: 3
};
var providerKindNames = [
"aave-v3",
"erc3156",
"balancer-v2",
"morpho-blue"
];
var providerKindByName = {
"aave-v3": PROVIDER_KIND.AAVE_V3,
erc3156: PROVIDER_KIND.ERC3156,
"balancer-v2": PROVIDER_KIND.BALANCER_V2,
"morpho-blue": PROVIDER_KIND.MORPHO_BLUE
};
// src/constructors.ts
var inputRef = (port) => ({ $ref: `input.${port}` });
var outputRef = (node, port) => ({
$ref: `${node}.${port}`
});
var optionalProp = (key, value) => value != null ? { [key]: value } : {};
var linear = (name, type) => ({ name, type, mode: "linear", availability: "now" });
var copy = (name, type) => ({ name, type, mode: "copy", availability: "now" });
var handle = (name, type) => ({ name, type });
var native = (name, chainId) => ({ name, resource: nativeResource(chainId) });
var erc20 = (name, token, chainId) => ({ name, resource: erc20Resource(token, chainId) });
var resource = (name, accepts, options) => ({
kind: "resource",
name,
accepts,
mode: "linear",
...optionalProp("optional", options?.optional)
});
var readResource = (name, accepts) => ({ kind: "resource", name, accepts, mode: "copy" });
var opHandle = (name, type, options) => ({
kind: "handle",
name,
type,
mode: "copy",
...optionalProp("expose", options?.expose),
...optionalProp("units", options?.units)
});
var resourceOutput = (name, options) => {
const opts = typeof options === "string" ? { availability: options } : options;
return {
kind: "resource_output",
name,
mode: "linear",
...optionalProp("availability", opts?.availability),
...optionalProp("providesMinimum", opts?.providesMinimum),
...optionalProp("omitIfZero", opts?.omitIfZero),
...optionalProp("deliveryAddressInput", opts?.deliveryAddressInput)
};
};
// src/ref.ts
var isContextKey = (key) => key === "sender" || key === "executionAddress";
var RESERVED_REF_SCOPES = /* @__PURE__ */ new Set([
"input",
"context",
"literal"
]);
var isRef = (v) => typeof v === "object" && v !== null && "$ref" in v && typeof v.$ref === "string";
var parseRef = (ref) => {
const dot = ref.$ref.indexOf(".");
if (dot === -1)
throw new Error(`Invalid ref: "${ref.$ref}" (must contain a dot)`);
const prefix = ref.$ref.slice(0, dot);
const suffix = ref.$ref.slice(dot + 1);
if (prefix === "input") return { scope: "input", port: suffix };
if (prefix === "context") {
if (!isContextKey(suffix))
throw new Error(`Unknown context key: "${suffix}"`);
return { scope: "context", key: suffix };
}
return { scope: "output", node: prefix, port: suffix };
};
var foldRef = (ref, cases) => {
if (ref.scope === "input") return cases.input(ref.port);
if (ref.scope === "context") return cases.context(ref.key);
return cases.output(ref.node, ref.port);
};
var refKey = (ref) => foldRef(parseRef(ref), {
input: (port) => `input:${port}`,
context: (key) => `context:${key}`,
output: (node, port) => `output:${node}:${port}`
});
// src/materialiserMetadata.ts
var foldMaterialiserMetadata = (meta, cases) => {
if (meta.kind === "exact") return cases.exact(meta.amount);
if (meta.kind === "exact-native") return cases.exactNative(meta.amount);
return cases.runtime();
};
// src/zodSchemas.ts
import z from "zod";
var SolTypeZod = z.enum([
"uint8",
"uint16",
"uint32",
"uint64",
"uint128",
"uint256",
"int128",
"int256",
"address",
"bool",
"bytes",
"bytes4",
"bytes32",
"string"
]);
var ResourcePortZod = z.object({
kind: z.literal("resource"),
name: z.string(),
accepts: z.enum(["erc20", "native", "any"]),
mode: z.enum(["linear", "copy"]),
optional: z.boolean().optional()
});
var HANDLE_UNITS = [
"raw",
"wad",
"ray",
"bps",
"token-decimals"
];
var STATIC_SOL_TYPES = [
"uint256",
"uint128",
"uint64",
"uint32",
"uint16",
"uint8",
"address",
"bool",
"bytes32"
];
var HandleUnitsZod = z.enum(HANDLE_UNITS);
var StaticSolTypeZod = z.enum(STATIC_SOL_TYPES);
var STATIC_SOL_TYPE_SET = new Set(STATIC_SOL_TYPES);
var isStaticSolType = (value) => STATIC_SOL_TYPE_SET.has(value);
var HandlePortZod = z.object({
kind: z.literal("handle"),
name: z.string(),
type: SolTypeZod,
mode: z.enum(["linear", "copy"]),
expose: z.boolean().optional(),
units: HandleUnitsZod.optional()
});
var InputPortZod = z.discriminatedUnion("kind", [
ResourcePortZod,
HandlePortZod
]);
var ResourceOutputPortZod = z.object({
kind: z.literal("resource_output"),
name: z.string(),
mode: z.enum(["linear", "copy"]),
availability: z.enum(["now", "future"]).optional(),
providesMinimum: z.boolean().optional(),
omitIfZero: z.boolean().optional(),
deliveryAddressInput: z.string().optional()
});
var OutputPortZod = z.discriminatedUnion("kind", [
ResourceOutputPortZod,
HandlePortZod
]);
var ManifestOperationZod = z.object({
id: z.string(),
description: z.string().optional(),
inputs: z.array(InputPortZod),
outputs: z.array(OutputPortZod),
configSchema: z.unknown().optional()
});
var PortKindEnum = z.enum(["resource", "resource_output", "handle"]);
var SelectorMatchZod = z.object({
kind: z.union([PortKindEnum, z.array(PortKindEnum)]),
mode: z.enum(["linear", "copy"]).optional(),
type: z.enum(["erc20", "native", "any"]).optional()
});
var ConfigSelectionZod = z.object({
kind: z.literal("config"),
configKey: z.string(),
cardinality: z.enum(["one", "many"])
});
var AllMatchingSelectionZod = z.object({
kind: z.literal("all_matching")
});
var SelectorSelectionZod = z.discriminatedUnion("kind", [
ConfigSelectionZod,
AllMatchingSelectionZod
]);
var GuardSelectorZod = z.object({
binding: z.string(),
source: z.enum(["inputs", "outputs"]),
match: SelectorMatchZod,
selection: SelectorSelectionZod
});
var GuardCompatibilityZod = z.object({
selectors: z.array(GuardSelectorZod)
});
var ManifestGuardZod = z.object({
kind: z.string(),
description: z.string().optional(),
configSchema: z.unknown().optional(),
compatibility: GuardCompatibilityZod.optional()
});
var ManifestMaterialiserZod = z.object({
kind: z.string(),
description: z.string().optional(),
accepts: z.enum(["resource", "handle", "any"]),
configSchema: z.unknown().optional()
});
var ManifestPreconditionZod = z.object({
type: z.string(),
description: z.string().optional(),
configSchema: z.unknown().optional()
});
var ComposeManifestZod = z.object({
manifestVersion: z.number(),
manifestHash: z.string(),
flowSchema: z.object({}).passthrough(),
operations: z.array(ManifestOperationZod),
guards: z.array(ManifestGuardZod),
materialisers: z.array(ManifestMaterialiserZod),
preconditions: z.array(ManifestPreconditionZod).optional()
});
export {
AppliedGuardSchema,
BindValueSchema,
CallSchema,
ComposeManifestZod,
ContinuationSchema,
FlowInputSchema,
FlowSchema,
GuardCompatibilityZod,
GuardSelectorZod,
HANDLE_UNITS,
HandleInputSchema,
HandlePortZod,
HandleUnitsZod,
InputPortZod,
LiteralBindingSchema,
ManifestGuardZod,
ManifestMaterialiserZod,
ManifestOperationZod,
ManifestPreconditionZod,
OutputPortZod,
PROVIDER_KIND,
RESERVED_REF_SCOPES,
RefSchema,
ResourceInputSchema,
ResourceOutputPortZod,
ResourcePortZod,
ResourceSchema,
STATIC_SOL_TYPES,
SolTypeSchema,
StaticSolTypeZod,
copy,
erc20,
erc20Resource,
erc20Token,
flowInputMode,
flowInputResource,
flowInputType,
foldMaterialiserMetadata,
foldRef,
foldResource,
handle,
inputRef,
isContextKey,
isERC20Resource,
isHandlePort,
isMaterialiserInput,
isNativeResource,
isRef,
isResourceInput,
isResourceOutputPort,
isResourcePort,
isStaticSolType,
linear,
native,
nativeResource,
opHandle,
outputRef,
parseRef,
providerKindByName,
providerKindNames,
readResource,
refKey,
resource,
resourceKey,
resourceOutput,
resourcesEqual
};