From 616b810f54aaf2be4af35ae4730989b3f61b183a Mon Sep 17 00:00:00 2001 From: tony Date: Fri, 17 Apr 2026 16:32:35 +0700 Subject: [PATCH 1/8] wingriders-v2 --- .claude/commands/tx-info.md | 27 + Dockerfile.w2 | 40 + apps/example/package.json | 4 + apps/example/src/kupo.ts | 168 ++++ apps/example/src/tx-info.ts | 48 + apps/example/src/w2.ts | 858 ++++++++++++++++++ packages/ledger-core/src/index.ts | 1 + .../ledger-core/src/plutus-script-utils.ts | 105 +++ packages/ledger-core/src/utxo.ts | 37 +- packages/minswap-build-tx/package.json | 1 + packages/minswap-build-tx/src/index.ts | 1 + .../minswap-build-tx/src/wingriders-v2.ts | 255 ++++++ .../test/wingriders-v2.test.ts | 164 ++++ packages/provider/package.json | 1 + packages/provider/src/cardanoscan.ts | 218 +++++ packages/provider/src/index.ts | 1 + packages/provider/src/kupo.ts | 77 +- packages/syncer/src/wingriders-v2-syncer.ts | 7 +- packages/wingriders-v2/src/index.ts | 2 + .../src/scripts/mainnet-references.json | 10 + .../src/scripts/mainnet-script.json | 10 + .../src/scripts/preprod-references.json | 10 + .../src/scripts/preprod-script.json | 10 + packages/wingriders-v2/src/warehouse.ts | 162 ++++ packages/wingriders-v2/src/wingriders-v2.ts | 271 +++++- pnpm-lock.yaml | 18 + 26 files changed, 2448 insertions(+), 58 deletions(-) create mode 100644 .claude/commands/tx-info.md create mode 100644 Dockerfile.w2 create mode 100644 apps/example/src/kupo.ts create mode 100644 apps/example/src/tx-info.ts create mode 100644 apps/example/src/w2.ts create mode 100644 packages/ledger-core/src/plutus-script-utils.ts create mode 100644 packages/minswap-build-tx/src/wingriders-v2.ts create mode 100644 packages/minswap-build-tx/test/wingriders-v2.test.ts create mode 100644 packages/provider/src/cardanoscan.ts create mode 100644 packages/wingriders-v2/src/scripts/mainnet-references.json create mode 100644 packages/wingriders-v2/src/scripts/mainnet-script.json create mode 100644 packages/wingriders-v2/src/scripts/preprod-references.json create mode 100644 packages/wingriders-v2/src/scripts/preprod-script.json create mode 100644 packages/wingriders-v2/src/warehouse.ts diff --git a/.claude/commands/tx-info.md b/.claude/commands/tx-info.md new file mode 100644 index 0000000..f846742 --- /dev/null +++ b/.claude/commands/tx-info.md @@ -0,0 +1,27 @@ +--- +description: Fetch a Cardano transaction CBOR from Maestro and print the parsed ECSL JSON +argument-hint: +allowed-tools: [Bash] +--- + +# /tx-info + +Runs the script at [apps/example/src/tx-info.ts](apps/example/src/tx-info.ts) to fetch a transaction from Maestro and print its decoded JSON. + +## Arguments + +`$ARGUMENTS` = ` `, e.g. `mainnet 1867a29bf1243f5850ccebdeaf58466325c3c8a13be7a057f3063edc1ff0c144` + +Valid networks: `mainnet`, `testnet-preprod`, `testnet-preview`. + +## Instructions + +Run exactly this command from the repo root, passing `$ARGUMENTS` through verbatim: + +```bash +cd apps/example && pnpm tsx src/tx-info.ts $ARGUMENTS +``` + +The script reads `MAESTRO_MAINNET_KEY` / `MAESTRO_PREPROD_KEY` / `MAESTRO_PREVIEW_KEY` from the environment depending on the network. If the corresponding env var is missing, the Maestro fetch returns `{"message": "..."}` rather than `{"data": "..."}` and `ECSL.Transaction.from_hex` will throw — surface that error to the user and suggest setting the right env var. + +Do not modify or re-interpret the arguments. Print the command's stdout back to the user. diff --git a/Dockerfile.w2 b/Dockerfile.w2 new file mode 100644 index 0000000..4468fcd --- /dev/null +++ b/Dockerfile.w2 @@ -0,0 +1,40 @@ +# Image for running CLI scripts in apps/example (e.g. src/w2.ts). +# Same multi-stage turbo-prune layout as Dockerfile.backend. + +# Stage 1: Base +FROM node:22-slim AS base +WORKDIR /usr/src/app + +RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* +RUN corepack enable && corepack prepare pnpm@9.0.0 --activate + +ENV PNPM_HOME="/root/.local/share/pnpm" +ENV PATH="${PNPM_HOME}:${PATH}" + +# Stage 2: Prune +FROM base AS cleaner +RUN pnpm add -g turbo +COPY . . +ARG APP_NAME=@apps/example +RUN turbo prune ${APP_NAME} --docker + +# Stage 3: Install +FROM base AS installer +COPY --from=cleaner /usr/src/app/out/json/ . +COPY --from=cleaner /usr/src/app/out/pnpm-lock.yaml ./pnpm-lock.yaml +RUN pnpm install --frozen-lockfile + +# Stage 4: Build (compiles all workspace deps of @apps/example) +FROM base AS builder +COPY --from=installer /usr/src/app ./ +COPY --from=cleaner /usr/src/app/out/full/ . +COPY turbo.json turbo.json +ARG APP_NAME=@apps/example +RUN pnpm turbo build --filter=${APP_NAME} + +# Stage 5: Release +FROM base AS release +COPY --from=builder /usr/src/app ./ + +# Override in k8s with `args:` — defaults to printing CLI help. +CMD ["pnpm", "--filter=@apps/example", "exec", "tsx", "src/w2.ts", "--help"] diff --git a/apps/example/package.json b/apps/example/package.json index c8e9f47..2a35da0 100644 --- a/apps/example/package.json +++ b/apps/example/package.json @@ -16,10 +16,13 @@ "@types/json-bigint": "^1.0.4", "@types/node": "^22.15.3", "dpdm": "^3.15.1", + "tsx": "^4.19.4", "typescript": "5.8.2" }, "dependencies": { "@cardano-ogmios/client": "^6.11.0", + "@minswap/felis-build-tx": "workspace:*", + "@minswap/felis-cip": "workspace:*", "@minswap/felis-dex-v2": "workspace:*", "@minswap/felis-ledger-core": "workspace:*", "@minswap/felis-ledger-utils": "workspace:*", @@ -28,6 +31,7 @@ "@minswap/felis-sundaeswap-v3": "workspace:*", "@minswap/felis-syncer": "workspace:*", "@minswap/felis-tx-builder": "workspace:*", + "@minswap/felis-wingriders-v2": "workspace:*", "@minswap/tiny-invariant": "^1.2.0", "socket.io-client": "^4.8.3" } diff --git a/apps/example/src/kupo.ts b/apps/example/src/kupo.ts new file mode 100644 index 0000000..4c92296 --- /dev/null +++ b/apps/example/src/kupo.ts @@ -0,0 +1,168 @@ +/** + * Kupo CLI — query a Kupo indexer for UTxOs, datums, and health. + * + * Usage: + * pnpm tsx src/kupo.ts --network [targets...] [--raw] + * + * Queries (pick one): + * --health Ping the Kupo instance. + * --datum --datum-hash [...] Resolve one or more datum hashes. + * --utxo Fetch UTxOs matching any of the targets. + * + * Targets for --utxo (repeatable, combined as a union): + * --tx-in UTxO at a specific output reference. + * --address All UTxOs at an address. + * --payment-credential All UTxOs at a payment credential hash. + * --asset UTxOs containing the asset. + * --policy-id UTxOs containing any asset under the policy. + * + * Output: + * Default: pretty XJSON (BigInt-safe). + * --raw Print one hex-encoded UTxO per line (only with --utxo). + * + * Env: + * KUPO_MAINNET_URL, KUPO_PREPROD_URL, KUPO_PREVIEW_URL override defaults. + * + * Examples: + * pnpm tsx src/kupo.ts --health --network mainnet + * pnpm tsx src/kupo.ts --utxo --network testnet-preprod --address addr_test1... + * pnpm tsx src/kupo.ts --utxo --network mainnet --tx-in abcd...#0 --raw + * pnpm tsx src/kupo.ts --datum --network mainnet --datum-hash + */ +import { Address, Asset, Bytes, NetworkEnvironment, TxIn, Utxo, XJSON } from "@minswap/felis-ledger-core"; +import { RustModule } from "@minswap/felis-ledger-utils"; +import { KupoService } from "@minswap/felis-provider"; + +type Flags = { + // Query kind + utxo: boolean; + health: boolean; + datum: boolean; + // Output + raw: boolean; + // Target + network?: string; + txIns: string[]; + addresses: string[]; + paymentCredentials: string[]; + assets: string[]; + policyIds: string[]; + datumHashes: string[]; +}; + +const parseArgs = (argv: string[]): Flags => { + const flags: Flags = { + utxo: false, + health: false, + datum: false, + raw: false, + txIns: [], + addresses: [], + paymentCredentials: [], + assets: [], + policyIds: [], + datumHashes: [], + }; + const take = (i: number): string => { + const v = argv[i + 1]; + if (!v) throw new Error(`Missing value for ${argv[i]}`); + return v; + }; + for (let i = 0; i < argv.length; i++) { + const a = argv[i]; + switch (a) { + case "--utxo": flags.utxo = true; break; + case "--health": flags.health = true; break; + case "--datum": flags.datum = true; break; + case "--raw": flags.raw = true; break; + case "--network": flags.network = take(i); i++; break; + case "--tx-in": flags.txIns.push(take(i)); i++; break; + case "--address": flags.addresses.push(take(i)); i++; break; + case "--payment-credential": flags.paymentCredentials.push(take(i)); i++; break; + case "--asset": flags.assets.push(take(i)); i++; break; + case "--policy-id": flags.policyIds.push(take(i)); i++; break; + case "--datum-hash": flags.datumHashes.push(take(i)); i++; break; + default: + throw new Error(`Unknown argument: ${a}`); + } + } + return flags; +}; + +const parseNetwork = (s?: string): NetworkEnvironment => { + switch (s) { + case "mainnet": return NetworkEnvironment.MAINNET; + case "testnet-preview": return NetworkEnvironment.TESTNET_PREVIEW; + case "testnet-preprod": return NetworkEnvironment.TESTNET_PREPROD; + default: throw new Error(`--network must be one of mainnet|testnet-preprod|testnet-preview (got ${s})`); + } +}; + +const resolveKupoUrl = (network: NetworkEnvironment): string => { + switch (network) { + case NetworkEnvironment.MAINNET: return process.env["KUPO_MAINNET_URL"] ?? "http://mainnet-staging:1442"; + case NetworkEnvironment.TESTNET_PREPROD: return process.env["KUPO_PREPROD_URL"] ?? "http://testnet-preprod:1442"; + case NetworkEnvironment.TESTNET_PREVIEW: return process.env["KUPO_PREVIEW_URL"] ?? "http://dev-3:1442"; + } +}; + +const printJson = (v: unknown) => { + console.log(XJSON.stringify(v, 2)); +}; + +const main = async () => { + await RustModule.load(); + const flags = parseArgs(process.argv.slice(2)); + const network = parseNetwork(flags.network); + const kupo = new KupoService(resolveKupoUrl(network)); + + if (flags.health) { + printJson(await kupo.health()); + return; + } + + if (flags.datum) { + if (flags.datumHashes.length === 0) throw new Error("--datum requires --datum-hash "); + const mapDatum = await kupo.getDatums(flags.datumHashes); + printJson(mapDatum); + return; + } + + if (!flags.utxo) throw new Error("Specify a query: --utxo | --health | --datum"); + const utxos: Utxo[] = []; + if (flags.txIns.length > 0) { + const txIns = flags.txIns.map((s) => TxIn.fromString(s)); + utxos.push(...(await kupo.utxosByTxIn(...txIns))); + } + if (flags.addresses.length > 0) { + const addresses = flags.addresses.map((s) => Address.fromBech32(s)); + utxos.push(...(await kupo.utxosByAddress(addresses))); + } + for (const pc of flags.paymentCredentials) { + utxos.push(...(await kupo.getUtxosByPaymentCredential(Bytes.fromHex(pc)))); + } + for (const asset of flags.assets) { + const kupoUtxos = await kupo.kupoUtxosByAsset(Asset.fromString(asset)); + for (const u of kupoUtxos) utxos.push(Utxo.fromKupo(u)); + } + for (const policyId of flags.policyIds) { + const kupoUtxos = await kupo.kupoUtxosByPolicyID(Bytes.fromHex(policyId)); + for (const u of kupoUtxos) utxos.push(Utxo.fromKupo(u)); + } + + if (utxos.length === 0) { + console.error("No UTXOs found"); + return; + } + + if (flags.raw) { + for (const u of utxos) console.log(Utxo.toHex(u)); + } else { + printJson(utxos); + } +}; + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/apps/example/src/tx-info.ts b/apps/example/src/tx-info.ts new file mode 100644 index 0000000..24739f1 --- /dev/null +++ b/apps/example/src/tx-info.ts @@ -0,0 +1,48 @@ +import { NetworkEnvironment } from "@minswap/felis-ledger-core"; +import { RustModule } from "@minswap/felis-ledger-utils"; +import invariant from "@minswap/tiny-invariant"; + +export function parseEnvironment(s: string): NetworkEnvironment { + switch (s) { + case "mainnet": + return NetworkEnvironment.MAINNET; + case "testnet-preview": + return NetworkEnvironment.TESTNET_PREVIEW; + case "testnet-preprod": + return NetworkEnvironment.TESTNET_PREPROD; + default: + throw new Error(`Unexpected environment ${s}`); + } +} + +function cardanoscanBaseUrl(networkEnv: NetworkEnvironment): string { + switch (networkEnv) { + case NetworkEnvironment.MAINNET: + return "https://api.cardanoscan.io/api/v1"; + case NetworkEnvironment.TESTNET_PREPROD: + return "https://preprod.cardanoscan.io/api/v1"; + case NetworkEnvironment.TESTNET_PREVIEW: + return "https://preview.cardanoscan.io/api/v1"; + } +} + +const main = async () => { + await RustModule.load(); + const ECSL = RustModule.getE; + const networkEnv: NetworkEnvironment = parseEnvironment(process.argv[2]); + const txHash: string = process.argv[3]; + const apiKey = process.env["CARDANOSCAN_API_KEY"]; + invariant(apiKey, "CARDANOSCAN_API_KEY env var is required"); + + const baseUrl = cardanoscanBaseUrl(networkEnv); + const res = await fetch(`${baseUrl}/transaction/cbor?hash=${txHash}`, { + method: "GET", + headers: { apiKey }, + }); + if (!res.ok) throw new Error(`Cardanoscan ${res.status}: ${await res.text()}`); + const body = (await res.json()) as { cbor: string }; + const eTx = ECSL.Transaction.from_hex(body.cbor); + console.log(eTx.to_json()); +}; + +main(); diff --git a/apps/example/src/w2.ts b/apps/example/src/w2.ts new file mode 100644 index 0000000..df71b12 --- /dev/null +++ b/apps/example/src/w2.ts @@ -0,0 +1,858 @@ +import { + ADA, + Address, + Asset, + Bytes, + DatumSourceType, + NetworkEnvironment, + TxIn, + TxOut, + Utxo, + Value, + XJSON, +} from "@minswap/felis-ledger-core"; +import { Maybe, RustModule } from "@minswap/felis-ledger-utils"; +import { CardanoscanProvider, KupoService } from "@minswap/felis-provider"; +import { CoinSelectionAlgorithm, ECSLConverter, EmulatorProvider, TxBuilder } from "@minswap/felis-tx-builder"; +import { WingridersV2 as WRV2Build } from "@minswap/felis-build-tx"; +import { baseAddressWalletFromSeed } from "@minswap/felis-cip"; +import { + normalizePair, + WingridersV2, + WingridersV2Warehouse, +} from "@minswap/felis-wingriders-v2"; +import invariant from "@minswap/tiny-invariant"; + +// ─── CLI plumbing ────────────────────────────────────────────────────────── + +type CreatePoolFlags = { + network: NetworkEnvironment; + owner: Address; + assetA: Asset; + amountA: bigint; + assetB: Asset; + amountB: bigint; +}; + +const DEFAULT_NETWORK = "testnet-preprod"; + +function parseNetwork(s: string | undefined): NetworkEnvironment { + switch (s ?? DEFAULT_NETWORK) { + case "mainnet": + return NetworkEnvironment.MAINNET; + case "testnet-preview": + return NetworkEnvironment.TESTNET_PREVIEW; + case "testnet-preprod": + return NetworkEnvironment.TESTNET_PREPROD; + default: + throw new Error(`--network must be one of mainnet|testnet-preprod|testnet-preview (got ${s})`); + } +} + +function resolveKupoUrl(network: NetworkEnvironment): string { + switch (network) { + case NetworkEnvironment.MAINNET: + return process.env["KUPO_MAINNET_URL"] ?? "http://mainnet-staging:1442"; + case NetworkEnvironment.TESTNET_PREPROD: + return process.env["KUPO_PREPROD_URL"] ?? "http://testnet-preprod:1442"; + case NetworkEnvironment.TESTNET_PREVIEW: + return process.env["KUPO_PREVIEW_URL"] ?? "http://dev-3:1442"; + } +} + +function parseCreatePoolFlags(argv: string[]): CreatePoolFlags { + const m: Record = {}; + for (let i = 0; i < argv.length; i++) { + const key = argv[i]; + if (!key.startsWith("--")) throw new Error(`Unexpected positional arg: ${key}`); + const val = argv[i + 1]; + if (val === undefined) throw new Error(`Missing value for ${key}`); + m[key.slice(2)] = val; + i++; + } + const required = ["owner", "asset-a", "amount-a", "asset-b", "amount-b"]; + for (const r of required) invariant(m[r] !== undefined, `--${r} is required`); + return { + network: parseNetwork(m["network"]), + owner: Address.fromBech32(m["owner"]), + assetA: Asset.fromString(m["asset-a"]), + amountA: BigInt(m["amount-a"]), + assetB: Asset.fromString(m["asset-b"]), + amountB: BigInt(m["amount-b"]), + }; +} + +// ─── Use case: create pool ───────────────────────────────────────────────── + +async function runCreatePool(argv: string[]): Promise { + const flags = parseCreatePoolFlags(argv); + const [assetA, assetB] = normalizePair([flags.assetA, flags.assetB]); + // Rotate amounts if the pair was reordered. + const [amountA, amountB] = + assetA.equals(flags.assetA) ? [flags.amountA, flags.amountB] : [flags.amountB, flags.amountA]; + + const warehouse = WingridersV2Warehouse.getInstance(flags.network); + const kupo = new KupoService(resolveKupoUrl(flags.network)); + + const factoryAsset = warehouse.factoryAsset; + console.log(factoryAsset.toString()); + console.log(warehouse.factoryAddress.bech32); + const factoryUtxos = await kupo.utxoAtScriptHashWithAsset( + warehouse.factoryScriptHash, + factoryAsset, + ); + console.log(XJSON.stringify(factoryUtxos, 2)); + + const lpAsset = WingridersV2.computeLpAsset(warehouse.dexSymbolHash, assetA, assetB); + const shareTokenName = lpAsset.tokenName; + const factoryInput = pickFactoryCoveringShare(factoryUtxos, shareTokenName); + invariant( + factoryInput !== null, + `No factory UTxO covers shareTokenName ${shareTokenName.hex} — has this pool already been created?`, + ); + + // 2) Fetch wallet UTxOs (owner's payment credential). + const paymentCredential = flags.owner.toPaymentCredential(); + invariant(paymentCredential, "owner address has no payment credential"); + const walletUtxos = await kupo.getUtxosByPaymentCredential(paymentCredential.payload); + invariant(walletUtxos.length > 0, `Owner ${flags.owner.bech32} has no UTxOs`); + + // 3) Build + complete (dry-run with EmulatorProvider — no submit). + const txBuilder = WRV2Build.buildCreatePool({ + networkEnv: flags.network, + owner: flags.owner, + factoryInput, + assetA, + amountA, + assetB, + amountB, + }); + const result = await txBuilder.complete({ + walletUtxos, + coinSelectionAlgorithm: CoinSelectionAlgorithm.SPEND_ALL, + provider: new EmulatorProvider(flags.network), + changeAddress: flags.owner, + }); + if (result.type !== "ok") { + console.error("complete() failed:", result.error); + process.exit(1); + } + + const cborHex = result.value.complete(); + console.log( + XJSON.stringify( + { + lpAsset: lpAsset.toString(), + factoryInput: { + txIn: `${factoryInput.input.txId.hex}#${factoryInput.input.index}`, + }, + normalizedPair: { + assetA: assetA.toString(), + amountA: amountA.toString(), + assetB: assetB.toString(), + amountB: amountB.toString(), + }, + }, + 2, + ), + ); + console.log("\n--- unsigned tx cbor ---"); + console.log(cborHex); +} + +function pickFactoryCoveringShare(factories: Utxo[], shareTokenName: Bytes): Utxo | null { + for (const utxo of factories) { + if (!utxo.output.datumSource || utxo.output.datumSource.type !== DatumSourceType.INLINE_DATUM) continue; + try { + const datum = WingridersV2.FactoryDatum.fromDataHex(utxo.output.datumSource.data.hex); + // head < shareTokenName <= tail (byte-lexicographic) + if (datum.head.compare(shareTokenName) < 0 && shareTokenName.compare(datum.tail) <= 0) { + return utxo; + } + } catch { + // not a FactoryDatum — skip + } + } + return null; +} + +// ─── Use case: batch (apply one order to a pool) ─────────────────────────── + +type BatchFlags = { + network: NetworkEnvironment; + agent: Address; + dry: boolean; + loopMs: bigint | null; +}; + +const DEFAULT_AGENT_BY_NETWORK: Partial> = { + [NetworkEnvironment.MAINNET]: + "addr1qy95vag5yter2lw5anh94p9j3zuc9vsrwks9q0x8anw6t2j8w4y93hwj09d2hztp04ed05jq40xdqdh6fdmplguhs73szf602s", + [NetworkEnvironment.TESTNET_PREPROD]: + "addr_test1qz427uy6zgxycqxmvetz2vejxx0ccagrfwwjyjjejdmgqkn8t7n238qksqw6w3sntsy9xarjh895hwlwcly92zf6gj8sq9u6hw", +}; + +function parseBatchFlags(argv: string[]): BatchFlags { + const m: Record = {}; + let dry = false; + let loopMs: bigint | null = null; + for (let i = 0; i < argv.length; i++) { + const key = argv[i]; + if (!key.startsWith("--")) throw new Error(`Unexpected positional arg: ${key}`); + if (key === "--dry") { + dry = true; + continue; + } + if (key === "--loop") { + const next = argv[i + 1]; + if (next !== undefined && !next.startsWith("--")) { + loopMs = BigInt(next); + i++; + } else { + loopMs = 20_000n; + } + continue; + } + const val = argv[i + 1]; + if (val === undefined) throw new Error(`Missing value for ${key}`); + m[key.slice(2)] = val; + i++; + } + const network = parseNetwork(m["network"]); + const agentBech32 = m["agent"] ?? DEFAULT_AGENT_BY_NETWORK[network]; + invariant(agentBech32, `--agent is required (no default for network ${m["network"]})`); + return { + network, + agent: Address.fromBech32(agentBech32), + dry, + loopMs, + }; +} + +const ceilDiv = (a: bigint, b: bigint): bigint => (a + b - 1n) / b; + +type OrderCandidate = { + utxo: Utxo; + datum: Extract; +}; + +type SwapPlan = { + compensation: Value; + newPoolValue: Value; + newDatum: WingridersV2.PoolDatum; +}; + +function planSwap( + pool: Utxo, + poolDatum: WingridersV2.PoolDatum, + order: OrderCandidate, + lastInteractionMs: bigint, +): SwapPlan { + const aToB = order.datum.direction === WingridersV2.SwapDirection.A_TO_B; + const assetIn = aToB ? poolDatum.assetA : poolDatum.assetB; + const assetOut = aToB ? poolDatum.assetB : poolDatum.assetA; + + // Amount of assetIn the caller is locking: total value of assetIn in the + // order UTxO, minus oil and agent fee when assetIn is ADA (both paid in ADA). + const oil = order.datum.oil; + const agentFee = poolDatum.agentFeeAda; + const rawIn = order.utxo.output.value.get(assetIn); + const lockX = assetIn.equals(ADA) ? rawIn - oil - agentFee : rawIn; + invariant(lockX > 0n, "order lockX must be positive"); + + // Effective reserves as seen by the curve: UTxO value minus treasuries + // (and minPoolAda on the ADA side). + const valueA = pool.output.value.get(poolDatum.assetA); + const valueB = pool.output.value.get(poolDatum.assetB); + const adaIsA = poolDatum.assetA.equals(ADA); + const reserveA = valueA - poolDatum.treasuryA - poolDatum.projectTreasuryA - poolDatum.reserveTreasuryA - (adaIsA ? WingridersV2Warehouse.MIN_POOL_ADA : 0n); + const reserveB = valueB - poolDatum.treasuryB - poolDatum.projectTreasuryB - poolDatum.reserveTreasuryB - (!adaIsA && poolDatum.assetB.equals(ADA) ? WingridersV2Warehouse.MIN_POOL_ADA : 0n); + + const rIn = aToB ? reserveA : reserveB; + const rOut = aToB ? reserveB : reserveA; + + const fb = poolDatum.feeBasis; + const swapFee = ceilDiv(lockX * poolDatum.swapFeeInBasis, fb); + const protocolFee = (lockX * poolDatum.protocolFeeInBasis) / fb; + const projectFee = (lockX * poolDatum.projectFeeInBasis) / fb; + const reserveFee = (lockX * poolDatum.reserveFeeInBasis) / fb; + + const effectiveIn = rIn + lockX - swapFee - protocolFee - projectFee - reserveFee; + const newOutReserve = ceilDiv(rIn * rOut, effectiveIn); + const amountOut = rOut - newOutReserve; + invariant(amountOut >= order.datum.minWanted, `slippage: amountOut ${amountOut} < minWanted ${order.datum.minWanted}`); + + const compensation = new Value().add(ADA, oil); + compensation.add(assetOut, amountOut); + + const newPoolValue = pool.output.value.clone(); + newPoolValue.add(assetIn, lockX); + newPoolValue.subtract(assetOut, amountOut); + + const newDatum: WingridersV2.PoolDatum = { + ...poolDatum, + lastInteraction: lastInteractionMs, + treasuryA: poolDatum.treasuryA + (aToB ? protocolFee : 0n), + treasuryB: poolDatum.treasuryB + (aToB ? 0n : protocolFee), + projectTreasuryA: poolDatum.projectTreasuryA + (aToB ? projectFee : 0n), + projectTreasuryB: poolDatum.projectTreasuryB + (aToB ? 0n : projectFee), + reserveTreasuryA: poolDatum.reserveTreasuryA + (aToB ? reserveFee : 0n), + reserveTreasuryB: poolDatum.reserveTreasuryB + (aToB ? 0n : reserveFee), + }; + + return { compensation, newPoolValue, newDatum }; +} + +async function runBatch(argv: string[]): Promise { + const flags = parseBatchFlags(argv); + const warehouse = WingridersV2Warehouse.getInstance(flags.network); + const kupo = new KupoService(resolveKupoUrl(flags.network)); + + const runOnce = async (): Promise => { + const debugData: Record = { + network: NetworkEnvironment[flags.network], + agent: flags.agent.bech32, + dry: flags.dry, + kupoBaseUrl: kupo.kupoBaseUrl, + }; + try { + await runBatchInner(flags, warehouse, kupo, debugData); + } catch (err) { + console.error("\n--- batch failed; debugData ---"); + console.error(XJSON.stringify(debugData, 2)); + throw err; + } + }; + + if (flags.loopMs === null) { + await runOnce(); + return; + } + + const intervalMs = Number(flags.loopMs); + for (let iteration = 1; ; iteration++) { + const startedAt = Date.now(); + console.log(`\n=== batch iteration ${iteration} @ ${new Date(startedAt).toISOString()} ===`); + try { + await runOnce(); + } catch (err) { + console.error(`iteration ${iteration} errored; continuing loop:`, err); + } + const elapsedMs = Date.now() - startedAt; + const sleepMs = Math.max(0, intervalMs - elapsedMs); + console.log(`iteration ${iteration} took ${elapsedMs}ms; sleeping ${sleepMs}ms`); + await new Promise((resolve) => setTimeout(resolve, sleepMs)); + } +} + +async function runBatchInner( + flags: BatchFlags, + warehouse: WingridersV2Warehouse, + kupo: KupoService, + debugData: Record, +): Promise { + + // 1) All active CP pools (at poolScriptHashCP, carrying the validity token) + const poolUtxos = await kupo.utxoAtScriptHashWithAsset(warehouse.poolScriptHashCP, warehouse.validityAsset); + console.log(`Found ${poolUtxos.length} pool UTxOs`); + const pools: Array<{ utxo: Utxo; datum: WingridersV2.PoolDatum }> = []; + for (const utxo of poolUtxos) { + if (!utxo.output.datumSource || utxo.output.datumSource.type !== DatumSourceType.INLINE_DATUM) continue; + try { + pools.push({ utxo, datum: WingridersV2.PoolDatum.fromCborHex(utxo.output.datumSource.data.hex, flags.network) }); + } catch { + // skip non-CP / malformed + } + } + console.log(`Parsed ${pools.length} CP pool datums`); + debugData.poolUtxosCount = poolUtxos.length; + debugData.parsedPoolsCount = pools.length; + + // 2) All request UTxOs at the CP request validator + const orderUtxos = await kupo.utxosByAddress([warehouse.requestAddressCP]); + console.log(`Found ${orderUtxos.length} order UTxOs`); + debugData.orderUtxosCount = orderUtxos.length; + + // 3) Pick the first valid swap order that has a matching pool + const nowMs = BigInt(Date.now()); + let match: { order: OrderCandidate; pool: { utxo: Utxo; datum: WingridersV2.PoolDatum } } | null = null; + const skipCounts: Record = { + notInlineDatum: 0, + badDatum: 0, + notSwap: 0, + nonNoDatumType: 0, + nonPubKeyBeneficiary: 0, + deadlineSoon: 0, + noMatchingPool: 0, + }; + const skipSamples: Array<{ ref: string; reason: string; detail?: unknown }> = []; + const noteSkip = (utxo: Utxo, reason: keyof typeof skipCounts, detail?: unknown) => { + skipCounts[reason]++; + if (skipSamples.length < 5) { + skipSamples.push({ ref: `${utxo.input.txId.hex}#${utxo.input.index}`, reason, detail }); + } + }; + + for (const utxo of orderUtxos) { + if (!utxo.output.datumSource || utxo.output.datumSource.type !== DatumSourceType.INLINE_DATUM) { + noteSkip(utxo, "notInlineDatum"); + continue; + } + let datum: WingridersV2.OrderDatum; + try { + datum = WingridersV2.OrderDatum.fromDataHex(utxo.output.datumSource.data.hex, flags.network); + } catch (err) { + noteSkip(utxo, "badDatum", String(err)); + continue; + } + if (datum.type !== WingridersV2.OrderType.Swap) { + noteSkip(utxo, "notSwap", { type: datum.type }); + continue; + } + if (datum.datumType !== WingridersV2.DatumType.No) { + noteSkip(utxo, "nonNoDatumType", { datumType: datum.datumType }); + continue; + } + if (Maybe.isNothing(datum.beneficiary.toPubKeyHash())) { + noteSkip(utxo, "nonPubKeyBeneficiary", { beneficiary: datum.beneficiary.bech32 }); + continue; + } + if (datum.deadline <= nowMs + 60_000n) { + noteSkip(utxo, "deadlineSoon", { deadline: datum.deadline.toString(), nowMs: nowMs.toString() }); + continue; + } + + const pool = pools.find( + (p) => p.datum.assetA.equals(datum.assetA) && p.datum.assetB.equals(datum.assetB), + ); + if (!pool) { + noteSkip(utxo, "noMatchingPool", { + assetA: datum.assetA.toString(), + assetB: datum.assetB.toString(), + }); + continue; + } + + match = { order: { utxo, datum: datum as OrderCandidate["datum"] }, pool }; + break; + } + debugData.orderFilter = { skipCounts, skipSamples }; + if (!match) { + console.log( + `No valid swap order with a matching CP pool — skipping. skipCounts=${XJSON.stringify(skipCounts)}`, + ); + return; + } + debugData.matched = { + pool: `${match.pool.utxo.input.txId.hex}#${match.pool.utxo.input.index}`, + order: `${match.order.utxo.input.txId.hex}#${match.order.utxo.input.index}`, + assetA: match.pool.datum.assetA.toString(), + assetB: match.pool.datum.assetB.toString(), + direction: match.order.datum.direction === WingridersV2.SwapDirection.A_TO_B ? "A_TO_B" : "B_TO_A", + }; + + // 4) Compute execution plan + const SLOT_MS = 1000n; + const validFromMs = ((nowMs - 60_000n) / SLOT_MS) * SLOT_MS; + const validToMs = validFromMs + 30n * 60_000n; + invariant(validToMs < match.order.datum.deadline, "order deadline too close"); + + const plan = planSwap(match.pool.utxo, match.pool.datum, match.order, validFromMs); + debugData.plan = { + validFromMs: validFromMs.toString(), + validToMs: validToMs.toString(), + deadline: match.order.datum.deadline.toString(), + minWanted: match.order.datum.minWanted.toString(), + compensation: plan.compensation.toJSON?.() ?? null, + newDatum: { + treasuryA: plan.newDatum.treasuryA.toString(), + treasuryB: plan.newDatum.treasuryB.toString(), + lastInteraction: plan.newDatum.lastInteraction.toString(), + }, + }; + + // 5) Fetch agent UTxOs from kupo. The pool's evolve redeemer enforces + // (pAT) that the agent input carries EXACTLY 1 of warehouse.agentAsset + // (see DEX/Pool.hs: `pvalueOf # agentInput.value # agentSymbol # agentToken #== 1`). + // So we must find a UTxO holding qty == 1n, not just any UTxO that has the token. + const agentPaymentCredential = flags.agent.toPaymentCredential(); + invariant(agentPaymentCredential, "agent address has no payment credential"); + const agentUtxos = await kupo.getUtxosByPaymentCredential(agentPaymentCredential.payload); + invariant(agentUtxos.length > 0, `agent ${flags.agent.bech32} has no UTxOs`); + + const agentAssetQty = (u: Utxo) => u.output.value.get(warehouse.agentAsset); + const exactOneTokenUtxos = agentUtxos.filter((u) => agentAssetQty(u) === 1n); + const anyTokenUtxos = agentUtxos.filter((u) => agentAssetQty(u) > 0n); + debugData.agentAsset = warehouse.agentAsset.toString(); + debugData.agentTokenUtxos = anyTokenUtxos.map((u) => ({ + ref: `${u.input.txId.hex}#${u.input.index}`, + qty: agentAssetQty(u).toString(), + ada: u.output.value.get(ADA).toString(), + })); + invariant( + exactOneTokenUtxos.length > 0, + `agent ${flags.agent.bech32} has no UTxO holding EXACTLY 1 ${warehouse.agentAsset.toString()}. ` + + `Pool 'pAT' check requires qty == 1. ` + + `Found ${anyTokenUtxos.length} bulk UTxO(s) (qty != 1). Split one first by sending 1 ${warehouse.agentAsset.toString()} back to the agent in its own output.`, + ); + // Among single-token UTxOs prefer the one with the most ADA (helps cover fee + change min-ada). + const agentInput = exactOneTokenUtxos.reduce((best, u) => + u.output.value.get(ADA) > best.output.value.get(ADA) ? u : best, + ); + debugData.agentInput = { + ref: `${agentInput.input.txId.hex}#${agentInput.input.index}`, + ada: agentInput.output.value.get(ADA).toString(), + agentTokenQty: agentAssetQty(agentInput).toString(), + walletUtxoCount: agentUtxos.length, + }; + + // 6) Build + complete (dry-run) + const txBuilder = WRV2Build.buildBatchTx({ + networkEnv: flags.network, + agent: flags.agent, + agentInput, + pool: { input: match.pool.utxo, newValue: plan.newPoolValue, newDatum: plan.newDatum }, + order: { input: match.order.utxo, compensation: plan.compensation }, + validFromMs, + validToMs, + }); + + // IMPORTANT: pass only [agentInput] as walletUtxos. The pool redeemer encodes + // input indexes computed from sorting [pool, agent, order], and the contract + // also enforces inputCount == 2 + numRequests (3 here) — so we cannot add any + // extra agent UTxOs to body.inputs. + const walletUtxos = [agentInput]; + // Conway requires collateral inputs to be ADA-only (or carry a `collateral_return`). + // tx-builder's first-branch fast path returns no collateralReturn when walletCollaterals + // covers >=5 ADA, so we MUST pass pure-ADA UTxOs only — never the bulk-tokens UTxO. + const isPureAda = (u: Utxo) => u.output.value.assets().every((a) => a.equals(ADA)); + const otherAgentUtxos = agentUtxos.filter((u) => !TxIn.equals(u.input, agentInput.input)); + const pureAdaCollaterals = otherAgentUtxos.filter(isPureAda); + debugData.allAgentUtxos = agentUtxos.map((u) => ({ + ref: `${u.input.txId.hex}#${u.input.index}`, + ada: u.output.value.get(ADA).toString(), + assets: u.output.value.assets().map((a) => a.toString()), + })); + invariant( + pureAdaCollaterals.length > 0, + `agent ${flags.agent.bech32} has no pure-ADA UTxO for collateral. ` + + `agent has ${agentUtxos.length} UTxO(s); see debugData.allAgentUtxos. ` + + `If split-agent-token was just submitted, wait for kupo to sync (typically <1 min).`, + ); + const walletCollaterals = pureAdaCollaterals; + debugData.walletCollateralCandidates = walletCollaterals.map((u) => ({ + ref: `${u.input.txId.hex}#${u.input.index}`, + ada: u.output.value.get(ADA).toString(), + })); + const result = await txBuilder.complete({ + walletUtxos, + walletCollaterals, + coinSelectionAlgorithm: CoinSelectionAlgorithm.MINWALLET_SEND_ALL, + provider: new EmulatorProvider(flags.network), + changeAddress: flags.agent, + }); + if (result.type !== "ok") { + debugData.completeError = result.error; + throw new Error(`txBuilder.complete() failed: ${String(result.error)}`); + } + debugData.unsignedTxCbor = result.value.complete(); + + console.log( + XJSON.stringify( + { + picked: { + pool: `${match.pool.utxo.input.txId.hex}#${match.pool.utxo.input.index}`, + order: `${match.order.utxo.input.txId.hex}#${match.order.utxo.input.index}`, + assetA: match.pool.datum.assetA.toString(), + assetB: match.pool.datum.assetB.toString(), + direction: match.order.datum.direction === WingridersV2.SwapDirection.A_TO_B ? "A_TO_B" : "B_TO_A", + compensation: { + assetOut: (match.order.datum.direction === WingridersV2.SwapDirection.A_TO_B + ? match.pool.datum.assetB + : match.pool.datum.assetA + ).toString(), + amountOut: plan.compensation + .get( + match.order.datum.direction === WingridersV2.SwapDirection.A_TO_B + ? match.pool.datum.assetB + : match.pool.datum.assetA, + ) + .toString(), + minWanted: match.order.datum.minWanted.toString(), + }, + }, + validity: { validFromMs: validFromMs.toString(), validToMs: validToMs.toString() }, + }, + 2, + ), + ); + if (flags.dry) { + console.log("\n--- unsigned tx cbor ---"); + console.log(result.value.complete()); + return; + } + + const seedPhrase = process.env["AGENT_SEED_PHRASE"]; + invariant(seedPhrase, "AGENT_SEED_PHRASE env var is required when --dry is omitted"); + const wallet = baseAddressWalletFromSeed(seedPhrase, flags.network); + invariant( + wallet.address.bech32 === flags.agent.bech32, + `AGENT_SEED_PHRASE derives ${wallet.address.bech32} but --agent is ${flags.agent.bech32}`, + ); + + const signedTxHex = result.value.signWithPrivateKey(wallet.paymentKey).complete(); + debugData.signedTxCbor = signedTxHex; + console.log("\n--- signed tx cbor ---"); + console.log(signedTxHex); + + const cardanoscanKey = process.env["CARDANOSCAN_KEY"]; + invariant(cardanoscanKey, "CARDANOSCAN_KEY env var is required to submit (use --dry to skip submit)"); + const cardanoscan = CardanoscanProvider.forNetwork(flags.network, cardanoscanKey); + await cardanoscan.submitTx(signedTxHex); + const txHash = ECSLConverter.getTxHash(RustModule.getE.Transaction.from_hex(signedTxHex)); + debugData.submitted = true; + debugData.txHash = txHash; + console.log(`\nsubmitted via Cardanoscan, txHash: ${txHash}`); +} + +// ─── Use case: split-agent-token ─────────────────────────────────────────── +// Carve off a UTxO holding EXACTLY 1 agent token from a bulk UTxO. The pool's +// `pAT` check requires `pvalueOf agentInput agentSymbol agentToken == 1`. + +type SplitAgentFlags = { + network: NetworkEnvironment; + agent: Address; + dry: boolean; +}; + +function parseSplitAgentFlags(argv: string[]): SplitAgentFlags { + const m: Record = {}; + let dry = false; + for (let i = 0; i < argv.length; i++) { + const key = argv[i]; + if (!key.startsWith("--")) throw new Error(`Unexpected positional arg: ${key}`); + if (key === "--dry") { + dry = true; + continue; + } + const val = argv[i + 1]; + if (val === undefined) throw new Error(`Missing value for ${key}`); + m[key.slice(2)] = val; + i++; + } + const network = parseNetwork(m["network"]); + const agentBech32 = m["agent"] ?? DEFAULT_AGENT_BY_NETWORK[network]; + invariant(agentBech32, `--agent is required (no default for network ${m["network"]})`); + return { network, agent: Address.fromBech32(agentBech32), dry }; +} + +async function runSplitAgentToken(argv: string[]): Promise { + const flags = parseSplitAgentFlags(argv); + const warehouse = WingridersV2Warehouse.getInstance(flags.network); + const kupo = new KupoService(resolveKupoUrl(flags.network)); + + const pc = flags.agent.toPaymentCredential(); + invariant(pc, "agent address has no payment credential"); + const agentUtxos = await kupo.getUtxosByPaymentCredential(pc.payload); + invariant(agentUtxos.length > 0, `agent ${flags.agent.bech32} has no UTxOs`); + invariant( + agentUtxos.some((u) => u.output.value.get(warehouse.agentAsset) > 0n), + `agent ${flags.agent.bech32} holds no ${warehouse.agentAsset.toString()}`, + ); + + // Spend ALL agent UTxOs and emit one fresh output: 5 ADA + 1 X token. + // MINWALLET_SEND_ALL pushes every wallet UTxO into inputs and sends the remainder + // back to changeAddress. + const splitOut = new TxOut( + flags.agent, + new Value().add(ADA, 5_000_000n).add(warehouse.agentAsset, 1n), + ); + const collateral = new TxOut( + flags.agent, + new Value().add(ADA, 5_000_000n), + ); + + const txBuilder = new TxBuilder(flags.network).payTo(splitOut, collateral); + + const result = await txBuilder.complete({ + walletUtxos: agentUtxos, + coinSelectionAlgorithm: CoinSelectionAlgorithm.MINWALLET_SEND_ALL, + provider: new EmulatorProvider(flags.network), + changeAddress: flags.agent, + }); + if (result.type !== "ok") { + console.error("complete() failed:", result.error); + process.exit(1); + } + + console.log( + XJSON.stringify( + { + spentAgentUtxos: agentUtxos.length, + agentInputOut: { ada: "5000000", agentTokenQty: "1" }, + collateralOut: { ada: "5000000" }, + }, + 2, + ), + ); + + if (flags.dry) { + console.log("\n--- unsigned tx cbor ---"); + console.log(result.value.complete()); + return; + } + + const seedPhrase = process.env["AGENT_SEED_PHRASE"]; + invariant(seedPhrase, "AGENT_SEED_PHRASE env var is required when --dry is omitted"); + const wallet = baseAddressWalletFromSeed(seedPhrase, flags.network); + invariant( + wallet.address.bech32 === flags.agent.bech32, + `AGENT_SEED_PHRASE derives ${wallet.address.bech32} but --agent is ${flags.agent.bech32}`, + ); + const signedTxHex = result.value.signWithPrivateKey(wallet.paymentKey).complete(); + console.log("\n--- signed tx cbor ---"); + console.log(signedTxHex); + + const cardanoscanKey = process.env["CARDANOSCAN_KEY"]; + invariant(cardanoscanKey, "CARDANOSCAN_KEY env var is required to submit (use --dry to skip submit)"); + const cardanoscan = CardanoscanProvider.forNetwork(flags.network, cardanoscanKey); + await cardanoscan.submitTx(signedTxHex); + const txHash = ECSLConverter.getTxHash(RustModule.getE.Transaction.from_hex(signedTxHex)); + console.log(`\nsubmitted via Cardanoscan, txHash: ${txHash}`); +} + +// ─── Use case: show info ─────────────────────────────────────────────────── + +type InfoFlags = { + network: NetworkEnvironment; +}; + +function parseInfoFlags(argv: string[]): InfoFlags { + const m: Record = {}; + for (let i = 0; i < argv.length; i++) { + const key = argv[i]; + if (!key.startsWith("--")) throw new Error(`Unexpected positional arg: ${key}`); + const val = argv[i + 1]; + if (val === undefined) throw new Error(`Missing value for ${key}`); + m[key.slice(2)] = val; + i++; + } + return { network: parseNetwork(m["network"]) }; +} + +function runInfo(argv: string[]): void { + const flags = parseInfoFlags(argv); + const w = WingridersV2Warehouse.getInstance(flags.network); + + console.log( + XJSON.stringify( + { + network: flags.network, + factory: { + scriptHash: w.factoryScriptHash.hex, + address: w.factoryAddress.bech32, + refInput: `${w.factoryRefInput.input.txId.hex}#${w.factoryRefInput.input.index}`, + }, + dexSymbol: { + hash: w.dexSymbolHash.hex, + refInput: `${w.dexSymbolRefInput.input.txId.hex}#${w.dexSymbolRefInput.input.index}`, + }, + pool: { + scriptHashCP: w.poolScriptHashCP.hex, + scriptHashSS: w.poolScriptHashSS.hex, + addressCP: w.poolAddressCP.bech32, + addressSS: w.poolAddressSS.bech32, + }, + request: { + validatorHashCP: w.requestValidatorHashCP.hex, + validatorHashSS: w.requestValidatorHashSS.hex, + addressCP: w.requestAddressCP.bech32, + orderRefInput: `${w.orderRefInput.input.txId.hex}#${w.orderRefInput.input.index}`, + poolCpRefInput: `${w.poolCpRefInput.input.txId.hex}#${w.poolCpRefInput.input.index}`, + }, + assets: { + factoryAsset: w.factoryAsset.toString(), + validityAsset: w.validityAsset.toString(), + }, + constants: { + MIN_POOL_ADA: WingridersV2Warehouse.MIN_POOL_ADA.toString(), + MAX_LP_TOKENS: WingridersV2Warehouse.MAX_LP_TOKENS.toString(), + BURNED_SHARE_TOKENS: WingridersV2Warehouse.BURNED_SHARE_TOKENS.toString(), + DEFAULT_SWAP_FEE_IN_BASIS: WingridersV2Warehouse.DEFAULT_SWAP_FEE_IN_BASIS.toString(), + DEFAULT_PROTOCOL_FEE_IN_BASIS: WingridersV2Warehouse.DEFAULT_PROTOCOL_FEE_IN_BASIS.toString(), + DEFAULT_FEE_BASIS: WingridersV2Warehouse.DEFAULT_FEE_BASIS.toString(), + DEFAULT_AGENT_FEE_ADA: WingridersV2Warehouse.DEFAULT_AGENT_FEE_ADA.toString(), + MAX_TX_VALIDITY_RANGE_MS: WingridersV2Warehouse.MAX_TX_VALIDITY_RANGE_MS.toString(), + }, + }, + 2, + ), + ); +} + +// ─── dispatch ────────────────────────────────────────────────────────────── + +const USAGE = `\ +WingRiders V2 CLI + +Usage: + pnpm tsx src/w2.ts create-pool \\ + [--network ] # default: testnet-preprod \\ + --owner \\ + --asset-a --amount-a \\ + --asset-b --amount-b + + pnpm tsx src/w2.ts info \\ + [--network ] # default: testnet-preprod + + pnpm tsx src/w2.ts batch \\ + [--network ] # default: testnet-preprod \\ + [--agent ] # defaults per network for mainnet / testnet-preprod + [--dry] # build only; without --dry, signs with AGENT_SEED_PHRASE + [--loop []] # repeat forever; default interval 20000ms; errors are logged, loop continues + + # If your agent UTxO holds >1 X (agent token), the pool's pAT check fails. Split first: + pnpm tsx src/w2.ts split-agent-token \\ + [--network ] \\ + [--agent ] \\ + [--dry] + +Env: + KUPO_MAINNET_URL, KUPO_PREPROD_URL, KUPO_PREVIEW_URL + AGENT_SEED_PHRASE (mnemonic; required for batch without --dry) + CARDANOSCAN_KEY (required for batch without --dry; used to submit) +`; + +const main = async (): Promise => { + await RustModule.load(); + const argv = process.argv.slice(2); + const [subcommand, ...rest] = argv; + switch (subcommand) { + case "create-pool": + await runCreatePool(rest); + break; + case "info": + runInfo(rest); + break; + case "batch": + await runBatch(rest); + break; + case "split-agent-token": + await runSplitAgentToken(rest); + break; + case undefined: + case "--help": + case "-h": + console.log(USAGE); + break; + default: + console.error(`Unknown subcommand: ${subcommand}`); + console.error(USAGE); + process.exit(1); + } +}; + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/packages/ledger-core/src/index.ts b/packages/ledger-core/src/index.ts index 01ea673..28bb21e 100644 --- a/packages/ledger-core/src/index.ts +++ b/packages/ledger-core/src/index.ts @@ -9,6 +9,7 @@ export * from "./native-script"; export * from "./network-id"; export * from "./plutus"; export * from "./plutus-json"; +export * from "./plutus-script-utils"; export * from "./plutus-version"; export * from "./protocol-parameters"; export * from "./redeemer"; diff --git a/packages/ledger-core/src/plutus-script-utils.ts b/packages/ledger-core/src/plutus-script-utils.ts new file mode 100644 index 0000000..fadcbaa --- /dev/null +++ b/packages/ledger-core/src/plutus-script-utils.ts @@ -0,0 +1,105 @@ +import { type CSLPlutusData, RustModule, safeFreeRustObjects } from "@minswap/felis-ledger-utils"; +import { Bytes } from "./bytes"; + +export type ScriptType = "Native" | "PlutusV1" | "PlutusV2" | "PlutusV3"; + +export type Script = { + type: ScriptType; + script: Bytes; +}; + +export type PlutusValidatorCompiled = { + title: string; + compiledCode: string; +}; +export type PlutusCompiled = { + validators: PlutusValidatorCompiled[]; +}; + +export function applyDoubleCborEncoding(script: Bytes): Bytes { + const CSL = RustModule.get; + try { + const ps1 = CSL.PlutusScript.from_bytes(script.bytes); + const ps2 = CSL.PlutusScript.from_bytes(ps1.bytes()); + safeFreeRustObjects(ps1, ps2); + return script; + } catch (_e) { + const ps = CSL.PlutusScript.new(script.bytes); + const ret = new Bytes(ps.to_bytes()); + safeFreeRustObjects(ps); + return ret; + } +} + +export function applyParamsToScript(params: CSLPlutusData[], script: Bytes): Bytes { + const CSL = RustModule.get; + const UPLC = RustModule.getU; + const paramsList = CSL.PlutusList.new(); + for (const param of params) { + paramsList.add(param); + } + const doubleCborScript = CSL.PlutusScript.from_bytes(applyDoubleCborEncoding(script).bytes); + const appliedRaw = UPLC.apply_params_to_plutus_script(paramsList.to_bytes(), doubleCborScript.bytes()); + const plutusScript = CSL.PlutusScript.new(appliedRaw); + const ret = new Bytes(plutusScript.to_bytes()); + safeFreeRustObjects(paramsList, doubleCborScript, plutusScript); + return ret; +} + +/** + * @deprecated + * This function is deprecated and will be removed in the future. + */ +export function applyParamsToScriptCSL(params: CSLPlutusData[], script: Bytes): Bytes { + const CSL = RustModule.get; + const paramsList = CSL.PlutusList.new(); + for (const param of params) { + paramsList.add(param); + } + const doubleCborScript = CSL.PlutusScript.from_bytes(applyDoubleCborEncoding(script).bytes); + const plutusScript = CSL.apply_params_to_plutus_script(paramsList, doubleCborScript); + const ret = new Bytes(plutusScript.to_bytes()); + safeFreeRustObjects(paramsList, plutusScript); + return ret; +} + +export function validatorToScriptHash(validator: Script): Bytes { + const CSL = RustModule.get; + let scriptHash: string; + switch (validator.type) { + case "Native": { + const nativeScript = CSL.NativeScript.from_bytes(validator.script.bytes); + const nativeScriptHash = nativeScript.hash(); + scriptHash = nativeScriptHash.to_hex(); + safeFreeRustObjects(nativeScript, nativeScriptHash); + break; + } + case "PlutusV1": { + const v1Script = CSL.PlutusScript.from_bytes(applyDoubleCborEncoding(validator.script).bytes); + const v1ScriptHash = v1Script.hash(); + scriptHash = v1ScriptHash.to_hex(); + safeFreeRustObjects(v1Script, v1ScriptHash); + break; + } + case "PlutusV2": { + const v2Script = CSL.PlutusScript.from_bytes_v2(applyDoubleCborEncoding(validator.script).bytes); + const v2ScriptHash = v2Script.hash(); + scriptHash = v2ScriptHash.to_hex(); + safeFreeRustObjects(v2Script, v2ScriptHash); + break; + } + case "PlutusV3": { + const v3Script = CSL.PlutusScript.from_bytes_with_version( + applyDoubleCborEncoding(validator.script).bytes, + CSL.Language.new_plutus_v3(), + ); + const v3ScriptHash = v3Script.hash(); + scriptHash = v3ScriptHash.to_hex(); + safeFreeRustObjects(v3Script, v3ScriptHash); + break; + } + default: + throw new Error("No variant matched"); + } + return Bytes.fromHex(scriptHash); +} diff --git a/packages/ledger-core/src/utxo.ts b/packages/ledger-core/src/utxo.ts index 5c5a001..75ad33a 100644 --- a/packages/ledger-core/src/utxo.ts +++ b/packages/ledger-core/src/utxo.ts @@ -807,8 +807,7 @@ export namespace Utxo { return utxos.some((utxo) => Utxo.toHex(utxo) === Utxo.toHex(search)); } - export function fromKupo(utxo: KupoUtxo, datum?: string): Utxo { - // TODO: support script ref + export function fromKupo(utxo: KupoUtxo, datum?: string, scriptData?: { language: string; script: string }): Utxo { let datumSource: Maybe = null; if (utxo.datum_hash) { // just type safety check @@ -837,13 +836,43 @@ export namespace Utxo { }; } } + + let scriptRef: Maybe = null; + if (scriptData) { + const CSL = RustModule.get; + const rawBytes = Bytes.fromHex(scriptData.script); + switch (scriptData.language) { + case "native": + case "plutus:v1": { + break; + } + case "plutus:v2": { + const plutusScript = CSL.PlutusScript.new_v2(rawBytes.bytes); + scriptRef = { + plutusVersion: PlutusVersion.V2, + script: Bytes.fromHex(plutusScript.to_hex()), + }; + safeFreeRustObjects(plutusScript); + break; + } + case "plutus:v3": { + const plutusScript = CSL.PlutusScript.new_v3(rawBytes.bytes); + scriptRef = { + plutusVersion: PlutusVersion.V3, + script: Bytes.fromHex(plutusScript.to_hex()), + }; + safeFreeRustObjects(plutusScript); + break; + } + } + } + return { input: { txId: Bytes.fromHex(utxo.transaction_id), index: utxo.output_index, }, - // TODO: add support for script ref - output: new TxOut(Address.fromBech32(utxo.address), Value.fromKupo(utxo.value), datumSource), + output: new TxOut(Address.fromBech32(utxo.address), Value.fromKupo(utxo.value), datumSource, scriptRef), }; } diff --git a/packages/minswap-build-tx/package.json b/packages/minswap-build-tx/package.json index e50e2b5..aa5aecd 100644 --- a/packages/minswap-build-tx/package.json +++ b/packages/minswap-build-tx/package.json @@ -39,6 +39,7 @@ "@minswap/felis-ledger-core": "workspace:*", "@minswap/felis-ledger-utils": "workspace:*", "@minswap/felis-tx-builder": "workspace:*", + "@minswap/felis-wingriders-v2": "workspace:*", "@minswap/tiny-invariant": "^1.2.0", "bignumber.js": "^9.1.2" } diff --git a/packages/minswap-build-tx/src/index.ts b/packages/minswap-build-tx/src/index.ts index 00058fa..aa6850c 100644 --- a/packages/minswap-build-tx/src/index.ts +++ b/packages/minswap-build-tx/src/index.ts @@ -1,3 +1,4 @@ export * from "./dex-order-transaction"; export * from "./djed"; export * from "./metadata"; +export * from "./wingriders-v2"; diff --git a/packages/minswap-build-tx/src/wingriders-v2.ts b/packages/minswap-build-tx/src/wingriders-v2.ts new file mode 100644 index 0000000..a58c034 --- /dev/null +++ b/packages/minswap-build-tx/src/wingriders-v2.ts @@ -0,0 +1,255 @@ +import { + ADA, + type Address, + type Asset, + Bytes, + DatumSourceType, + type NetworkEnvironment, + type PlutusData, + TxIn, + TxOut, + type Utxo, + Value, +} from "@minswap/felis-ledger-core"; +import { Maybe } from "@minswap/felis-ledger-utils"; +import { TxBuilder } from "@minswap/felis-tx-builder"; +import { isNormalizePair, WingridersV2Warehouse, WingridersV2 as WRV2 } from "@minswap/felis-wingriders-v2"; +import invariant from "@minswap/tiny-invariant"; + +export namespace WingridersV2 { + export type BuildCreatePoolOptions = { + networkEnv: NetworkEnvironment; + owner: Address; + factoryInput: Utxo; + assetA: Asset; + amountA: bigint; + assetB: Asset; + amountB: bigint; + }; + + // Integer square root over bigint (Newton's method). Used to compute initial LP. + function isqrt(value: bigint): bigint { + invariant(value >= 0n, "isqrt: negative input"); + if (value < 2n) return value; + let x = value; + let y = (x + 1n) / 2n; + while (y < x) { + x = y; + y = (x + value / x) / 2n; + } + return x; + } + + export function buildCreatePool(options: BuildCreatePoolOptions): TxBuilder { + const { networkEnv, owner, factoryInput, assetA, amountA, assetB, amountB } = options; + + invariant( + isNormalizePair([assetA, assetB]), + "WingridersV2.buildCreatePool: assets must be normalized (assetA < assetB)", + ); + invariant(amountA > 0n && amountB > 0n, "WingridersV2.buildCreatePool: amounts must be positive"); + + const warehouse = WingridersV2Warehouse.getInstance(networkEnv); + + invariant( + factoryInput.output.value.has(warehouse.factoryAsset), + "WingridersV2.buildCreatePool: factory input must contain the factory token", + ); + invariant( + Maybe.isJust(factoryInput.output.datumSource) && + factoryInput.output.datumSource.type === DatumSourceType.INLINE_DATUM, + "WingridersV2.buildCreatePool: factory input must have an inline datum", + ); + const factoryDatum = WRV2.FactoryDatum.fromDataHex(factoryInput.output.datumSource.data.hex); + + // LP / share asset — the factory-split boundary is `lpAsset.tokenName`. + const lpAsset = WRV2.computeLpAsset(warehouse.dexSymbolHash, assetA, assetB); + const shareTokenName = lpAsset.tokenName; + + // Uniswap-V2-style initial LP: isqrt(A * B) − burnedShareTokens. + const totalShares = isqrt(amountA * amountB); + const initialLp = totalShares - WingridersV2Warehouse.BURNED_SHARE_TOKENS; + invariant( + initialLp >= 1n, + "WingridersV2.buildCreatePool: initial deposit too small (isqrt(amountA * amountB) must exceed burnedShareTokens)", + ); + + // Validity range — on-chain lastInteraction = lowerBound (POSIXTime ms); range must be < 24h. + // Round to slot boundary (1000ms) so `validFromMs` survives the slot round-trip unchanged. + const SLOT_MS = 1000n; + const now = BigInt(Date.now()); + const validFromMs = ((now - 60_000n) / SLOT_MS) * SLOT_MS; + const validToMs = validFromMs + 30n * 60_000n; + invariant( + validToMs - validFromMs < WingridersV2Warehouse.MAX_TX_VALIDITY_RANGE_MS, + "WingridersV2.buildCreatePool: validity window must be < 24h", + ); + + const factoryRedeemer = WRV2.FactoryRedeemer.toPlutusJson({ + poolChoice: { type: WRV2.PoolChoiceType.UseConstantProduct }, + assetA, + assetB, + }); + + const poolDatum: WRV2.PoolDatum = { + reqValidatorHash: warehouse.requestValidatorHashCP, + assetA, + assetB, + swapFeeInBasis: WingridersV2Warehouse.DEFAULT_SWAP_FEE_IN_BASIS, + protocolFeeInBasis: WingridersV2Warehouse.DEFAULT_PROTOCOL_FEE_IN_BASIS, + projectFeeInBasis: 0n, + reserveFeeInBasis: 0n, + feeBasis: WingridersV2Warehouse.DEFAULT_FEE_BASIS, + agentFeeAda: WingridersV2Warehouse.DEFAULT_AGENT_FEE_ADA, + lastInteraction: validFromMs, + treasuryA: 0n, + treasuryB: 0n, + projectTreasuryA: 0n, + projectTreasuryB: 0n, + reserveTreasuryA: 0n, + reserveTreasuryB: 0n, + projectBeneficiary: null, + reserveBeneficiary: null, + poolSpecifics: { constructor: 0, fields: [] }, + }; + + const mintValue = new Value() + .add(warehouse.factoryAsset, 1n) + .add(warehouse.validityAsset, 1n) + .add(lpAsset, WingridersV2Warehouse.MAX_LP_TOKENS - WingridersV2Warehouse.BURNED_SHARE_TOKENS); + + const factory1Datum: WRV2.FactoryDatum = { head: factoryDatum.head, tail: shareTokenName }; + const factory1Out = new TxOut( + warehouse.factoryAddress, + new Value().add(warehouse.factoryAsset, 1n), + WRV2.FactoryDatum.toDatumSource(factory1Datum), + ).addMinimumADAIfRequired(networkEnv); + + const factory2Datum: WRV2.FactoryDatum = { head: shareTokenName, tail: factoryDatum.tail }; + const factory2Out = new TxOut( + warehouse.factoryAddress, + new Value().add(warehouse.factoryAsset, 1n), + WRV2.FactoryDatum.toDatumSource(factory2Datum), + ).addMinimumADAIfRequired(networkEnv); + + const poolValue = new Value() + .add(warehouse.validityAsset, 1n) + .add(lpAsset, WingridersV2Warehouse.MAX_LP_TOKENS - totalShares); + if (assetA.equals(ADA)) { + poolValue.add(ADA, amountA + WingridersV2Warehouse.MIN_POOL_ADA); + poolValue.add(assetB, amountB); + } else { + poolValue.add(ADA, WingridersV2Warehouse.MIN_POOL_ADA); + poolValue.add(assetA, amountA); + poolValue.add(assetB, amountB); + } + const poolOut = new TxOut(warehouse.poolAddressCP, poolValue, { + type: DatumSourceType.INLINE_DATUM, + data: Bytes.fromHex(WRV2.PoolDatum.toCborHex(poolDatum)), + }); + + const ownerOut = new TxOut(owner, new Value().add(lpAsset, initialLp)).addMinimumADAIfRequired(networkEnv); + + return new TxBuilder(networkEnv) + .readFrom(warehouse.factoryRefInput, warehouse.dexSymbolRefInput) + .collectFromPlutusContract([factoryInput], factoryRedeemer) + .mintAssets(mintValue, WRV2.ValidityRedeemer.mintNewPool()) + .payTo(factory1Out, factory2Out, poolOut, ownerOut) + .validFromUnixTime(Number(validFromMs)) + .validToUnixTime(Number(validToMs)); + } + + export type BuildBatchTxOptions = { + networkEnv: NetworkEnvironment; + agent: Address; + agentInput: Utxo; + pool: { + input: Utxo; + newValue: Value; + newDatum: WRV2.PoolDatum; + }; + order: { + input: Utxo; + compensation: Value; + extraData?: PlutusData; + }; + validFromMs?: bigint; + validToMs?: bigint; + }; + + export function buildBatchTx(options: BuildBatchTxOptions): TxBuilder { + const { networkEnv, agent, agentInput, pool, order } = options; + const warehouse = WingridersV2Warehouse.getInstance(networkEnv); + + invariant( + pool.input.output.value.has(warehouse.validityAsset), + "WingridersV2.buildBatchTx: pool input must hold the validity token", + ); + + const SLOT_MS = 1000n; + const nowMs = BigInt(Date.now()); + const validFromMs = options.validFromMs ?? ((nowMs - 60_000n) / SLOT_MS) * SLOT_MS; + const validToMs = options.validToMs ?? validFromMs + 30n * 60_000n; + invariant( + validToMs - validFromMs < WingridersV2Warehouse.MAX_TX_VALIDITY_RANGE_MS, + "WingridersV2.buildBatchTx: validity window must be < 24h", + ); + invariant( + pool.newDatum.lastInteraction === validFromMs, + "WingridersV2.buildBatchTx: newDatum.lastInteraction must equal validFromMs", + ); + + const orderDatum = WRV2.OrderDatum.fromDataHex( + Maybe.isJust(order.input.output.datumSource) && + order.input.output.datumSource.type === DatumSourceType.INLINE_DATUM + ? order.input.output.datumSource.data.hex + : (() => { + throw new Error("WingridersV2.buildBatchTx: order input must carry an inline OrderDatum"); + })(), + networkEnv, + ); + invariant(orderDatum.type === WRV2.OrderType.Swap, "WingridersV2.buildBatchTx: only swap orders supported in v1"); + invariant( + orderDatum.datumType === WRV2.DatumType.No, + "WingridersV2.buildBatchTx: only pubkey beneficiaries (datumType=No) supported in v1", + ); + + const sortedInputs = [pool.input, agentInput, order.input].slice().sort((a, b) => TxIn.compare(a.input, b.input)); + const indexOf = (u: Utxo): bigint => { + const idx = sortedInputs.findIndex((s) => TxIn.equals(s.input, u.input)); + invariant(idx >= 0, "WingridersV2.buildBatchTx: internal index lookup failed"); + return BigInt(idx); + }; + const poolLocation = indexOf(pool.input); + const agentLocation = indexOf(agentInput); + const orderLocation = indexOf(order.input); + + const evolveRedeemer = WRV2.PoolRedeemer.evolve({ + poolLocation, + agentLocation, + requestLocations: [ + { + inputIndex: orderLocation, + extraData: order.extraData ?? { int: "0" }, + }, + ], + }); + const applyRedeemer = WRV2.OrderRedeemer.apply(poolLocation); + + const poolOut = new TxOut(pool.input.output.address, pool.newValue, { + type: DatumSourceType.INLINE_DATUM, + data: Bytes.fromHex(WRV2.PoolDatum.toCborHex(pool.newDatum)), + }); + const compensationOut = new TxOut(orderDatum.beneficiary, order.compensation).addMinimumADAIfRequired(networkEnv); + + return new TxBuilder(networkEnv) + .readFrom(warehouse.poolCpRefInput, warehouse.orderRefInput) + .collectFromPlutusContract([pool.input], evolveRedeemer) + .collectFromPlutusContract([order.input], applyRedeemer) + .collectFromPubKey(agentInput) + .payTo(poolOut, compensationOut) + .validFromUnixTime(Number(validFromMs)) + .validToUnixTime(Number(validToMs)) + .addSigner(agent); + } +} diff --git a/packages/minswap-build-tx/test/wingriders-v2.test.ts b/packages/minswap-build-tx/test/wingriders-v2.test.ts new file mode 100644 index 0000000..1ffe10c --- /dev/null +++ b/packages/minswap-build-tx/test/wingriders-v2.test.ts @@ -0,0 +1,164 @@ +import { + ADA, + Address, + Asset, + Bytes, + DatumSourceType, + NetworkEnvironment, + TxIn, + TxOut, + type Utxo, + Value, +} from "@minswap/felis-ledger-core"; +import { RustModule } from "@minswap/felis-ledger-utils"; +import { CoinSelectionAlgorithm, EmulatorProvider } from "@minswap/felis-tx-builder"; +import { WingridersV2, WingridersV2Warehouse } from "@minswap/felis-wingriders-v2"; +import { beforeAll, describe, expect, it } from "vitest"; +import * as B from "../src"; + +beforeAll(async () => { + await RustModule.load(); +}); + +describe("wingriders-v2", () => { + it("buildCreatePool", async () => { + const owner = Address.fromBech32( + "addr1qxjy58p6catwuzvprv5jcfgpna9srvty89umn2ykjrjnkn4fhszw0dd6hguypfq49f2yzhf7vjyehknve2s8s3p7u3uqhmh7m6", + ); + const assetB = Asset.fromString("c0ee29a85b13209423b10447d3c2e6a50641a15c57770e27cb9d5073.57696e67526964657273"); + + const options: B.WingridersV2.BuildCreatePoolOptions = { + networkEnv: NetworkEnvironment.MAINNET, + owner, + factoryInput: { + input: TxIn.fromString("3f04baf552df8bddc402e77ac7cc4d2f63ab3c1a294467288797ab39d32063de#0"), + output: new TxOut( + Address.fromBech32("addr1w8zhkkywhknnt4yspr284l0ysxvwcdcx94kwkavgqdg4qycanh6hq"), + new Value() + .add(ADA, 5_000_000n) + .add(WingridersV2Warehouse.getInstance(NetworkEnvironment.MAINNET).factoryAsset, 1n), + { + type: DatumSourceType.INLINE_DATUM, + data: Bytes.fromHex("d8799f41005821ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff"), + }, + ), + }, + assetA: ADA, + amountA: 10_000_000n, + assetB, + amountB: 194_523_298n, + }; + const walletUtxos: Utxo[] = [ + { + input: TxIn.fromString("258c718aace3853ffa273b57546a9fd3d1942105bea1d7904debedf6e1b94e54#1"), + output: new TxOut(owner, new Value().add(ADA, 554_828_911n)), + }, + { + input: TxIn.fromString("9403fc3a6bd1f2b92d14cf61eac76bc64136574f1ac5e2afb9c20b9c1a629316#0"), + output: new TxOut(owner, new Value().add(ADA, 1_180_940n).add(assetB, 400_000_000n)), + }, + ]; + // Cross-check share tokenName against the real mainnet create-pool tx + // 1867a29bf1243f5850ccebdeaf58466325c3c8a13be7a057f3063edc1ff0c144: the + // pool minted share asset was 6fdc63….e650ce568d…34d2b3eb — confirms + // computeLpAsset is wired correctly. + const warehouse = WingridersV2Warehouse.getInstance(NetworkEnvironment.MAINNET); + const lpAsset = WingridersV2.computeLpAsset(warehouse.dexSymbolHash, ADA, assetB); + expect(lpAsset.tokenName.hex).equal("e650ce568d1dc1ebfb9bfbd8c06982f3ea95a9959ddaefc783edd95134d2b3eb"); + + const txBuilder = B.WingridersV2.buildCreatePool(options); + const txComplete = await txBuilder.complete({ + walletUtxos, + coinSelectionAlgorithm: CoinSelectionAlgorithm.SPEND_ALL, + provider: new EmulatorProvider(NetworkEnvironment.MAINNET), + changeAddress: options.owner, + }); + expect(txComplete.type).equal("ok"); + }); + + // Fixture from mainnet tx + // 3c58f27236de3a0cb2de80ca9279671ac2691a5ca0ad3770ce14c8fcfb9bc9b1 — single + // ADA→NIGHT swap batched through the WR V2 CP NIGHT/ADA pool. + it("buildBatchTx", async () => { + const networkEnv = NetworkEnvironment.MAINNET; + const warehouse = WingridersV2Warehouse.getInstance(networkEnv); + + const NIGHT = Asset.fromString("0691b2fecca1ac4f53cb6dfb00b7013e561d1f34403b957cbb5af1fa.4e49474854"); + const LP_SHARE = Asset.fromString( + "6fdc63a1d71dc2c65502b79baae7fb543185702b12c3c5fb639ed737.9098d8f2a436c6637776c82b3efa5a1eaa1517bec1077b7d86b4b04a96cec27f", + ); + const AGENT_TOKEN = Asset.fromString("1ad3767073087df4fc97fba7ac4a71a0a6cd556f1ad96a7b1c9870c4.58"); + + const poolAddress = Address.fromBech32( + "addr1zxhew7fmsup08qvhdnkg8ccra88pw7q5trrncja3dlszhqacq84zk0xfmaxftjjzm9f2q5mauka7jcrdjj89ehx3ex8qa2epvx", + ); + const agentAddress = Address.fromBech32( + "addr1qy95vag5yter2lw5anh94p9j3zuc9vsrwks9q0x8anw6t2j8w4y93hwj09d2hztp04ed05jq40xdqdh6fdmplguhs73szf602s", + ); + + // Old pool datum from the batch tx's input UTxO (inline). + const oldPoolDatumHex = + "d8799f581cc134d839a64a5dfb9b155869ef3f34280751a622f69958baa8ffd29c4040581c0691b2fecca1ac4f53cb6dfb00b7013e561d1f34403b957cbb5af1fa454e494748540d0200001927101a001e84801b0000019da8215ce01a423e754a1b0000000170c1aa3000000000d87a80d87a80d87980ff"; + const oldPoolDatum = WingridersV2.PoolDatum.fromCborHex(oldPoolDatumHex, networkEnv); + + const poolInput: Utxo = { + input: TxIn.fromString("340a7735af490a9e2d3a86693de22f7b79c23eaa234efe444287c31dc9ffb116#0"), + output: new TxOut( + poolAddress, + new Value() + .add(ADA, 160_531_684_429n) + .add(NIGHT, 1_071_050_470_051n) + .add(warehouse.validityAsset, 1n) + .add(LP_SHARE, 9_223_371_661_900_298_006n), + { type: DatumSourceType.INLINE_DATUM, data: Bytes.fromHex(oldPoolDatumHex) }, + ), + }; + + const agentInput: Utxo = { + input: TxIn.fromString("b85c474db82c8127e981d7576d3b8e2a626f861064d432c86fc6dd2ec6001512#2"), + output: new TxOut(agentAddress, new Value().add(ADA, 15_160_770n).add(AGENT_TOKEN, 1n)), + }; + + const orderDatumHex = + "d8799f1a001e8480d8799fd8799f581c4cd10e5198280feba661a1d63e11f5f482b5d1024a4306155086ecc2ffd8799fd8799fd8799f581c21629cecb2aa94766daf96eaf576d924a4e0cb3116638873ed4ec468ffffffffd8799fd8799f581c4cd10e5198280feba661a1d63e11f5f482b5d1024a4306155086ecc2ffd8799fd8799fd8799f581c21629cecb2aa94766daf96eaf576d924a4e0cb3116638873ed4ec468ffffffff80d879801b0000019da9ea2b5e4040581c0691b2fecca1ac4f53cb6dfb00b7013e561d1f34403b957cbb5af1fa454e49474854d8799fd879801aea18a13cff0101ff"; + const orderInput: Utxo = { + input: TxIn.fromString("d76ac537e714c0a34fecea1cfe9f0cacb8f72b6e4a4164625a420a4bde92657a#1"), + output: new TxOut(warehouse.requestAddressCP, new Value().add(ADA, 598_000_000n), { + type: DatumSourceType.INLINE_DATUM, + data: Bytes.fromHex(orderDatumHex), + }), + }; + + // Execution plan — in production the agent computes this from CP math. + // Here we lift the exact new state out of the mainnet tx's output datum. + const newPoolDatum: WingridersV2.PoolDatum = { + ...oldPoolDatum, + treasuryA: 1_111_508_314n, + lastInteraction: 1_776_643_394_000n, + }; + const newPoolValue = new Value() + .add(ADA, 161_125_684_429n) + .add(NIGHT, 1_067_103_349_953n) + .add(warehouse.validityAsset, 1n) + .add(LP_SHARE, 9_223_371_661_900_298_006n); + const compensation = new Value().add(ADA, 2_000_000n).add(NIGHT, 3_947_120_098n); + + const txBuilder = B.WingridersV2.buildBatchTx({ + networkEnv, + agent: agentAddress, + agentInput, + pool: { input: poolInput, newValue: newPoolValue, newDatum: newPoolDatum }, + order: { input: orderInput, compensation }, + validFromMs: 1_776_643_394_000n, + validToMs: 1_776_648_792_000n, // ≈ 1.5h window, same shape as mainnet + }); + + const result = await txBuilder.complete({ + walletUtxos: [agentInput], + coinSelectionAlgorithm: CoinSelectionAlgorithm.SPEND_ALL, + provider: new EmulatorProvider(networkEnv), + changeAddress: agentAddress, + }); + expect(result.type).equal("ok"); + }); +}); diff --git a/packages/provider/package.json b/packages/provider/package.json index a5c990e..0ea7659 100644 --- a/packages/provider/package.json +++ b/packages/provider/package.json @@ -36,6 +36,7 @@ "@minswap/felis-ledger-core": "workspace:*", "@minswap/felis-ledger-utils": "workspace:*", "@minswap/tiny-invariant": "^1.2.0", + "json-bigint": "^1.0.0", "remeda": "^2.19.2" } } diff --git a/packages/provider/src/cardanoscan.ts b/packages/provider/src/cardanoscan.ts new file mode 100644 index 0000000..f775114 --- /dev/null +++ b/packages/provider/src/cardanoscan.ts @@ -0,0 +1,218 @@ +import type { Address, NetworkEnvironment } from "@minswap/felis-ledger-core"; +import { NetworkEnvironment as NE } from "@minswap/felis-ledger-core"; + +export type CardanoscanTxIO = { + address: string; + value: string; + tokens?: Array<{ + value: string; + assetId: string; + }>; + datum?: string; + scriptRef?: string; +}; + +export type CardanoscanTransaction = { + hash: string; + blockHash: string; + fees: string; + slot: number; + epoch: number; + blockHeight: number; + timestamp: string; + index: number; + inputs: (CardanoscanTxIO & { txId: string; index: number })[]; + outputs: CardanoscanTxIO[]; + collateral: CardanoscanTxIO[]; + certificates?: { + stakeDelegations?: Array<{ stakeAddress: string; poolId: string }>; + poolRegistrations?: Array>; + governanceActions?: Array>; + }; + withdrawals?: Array<{ address: string; amount: string }>; + metadata?: { hash: string; labels: Record }; + mint?: Array<{ quantity: string; unit: string }>; + redeemers?: Array<{ + index: number; + purpose: string; + scriptHash: string; + redeemerDataHash: string; + executionUnits: { memory: number; steps: number }; + }>; + status: boolean; + votingProcedures?: Array>; +}; + +export type CardanoscanTransactionListResponse = { + pageNo: number; + limit: number; + transactions: CardanoscanTransaction[]; +}; + +export type GetTransactionListOptions = { + address: string; + pageNo: number; + limit?: number; + order: "asc" | "desc"; +}; + +export class CardanoscanProvider { + static readonly MAINNET_URL = "https://api.cardanoscan.io/api/v1"; + static readonly PREVIEW_URL = "https://api-preview.cardanoscan.io/api/v1"; + static readonly PREPROD_URL = "https://api-preprod.cardanoscan.io/api/v1"; + + private readonly baseUrl: string; + private readonly apiKey: string; + + constructor(baseUrl: string, apiKey: string) { + this.baseUrl = baseUrl.replace(/\/$/, ""); + this.apiKey = apiKey; + } + + static forNetwork(networkEnv: NetworkEnvironment, apiKey: string): CardanoscanProvider { + switch (networkEnv) { + case NE.MAINNET: + return new CardanoscanProvider(CardanoscanProvider.MAINNET_URL, apiKey); + case NE.TESTNET_PREPROD: + return new CardanoscanProvider(CardanoscanProvider.PREPROD_URL, apiKey); + case NE.TESTNET_PREVIEW: + return new CardanoscanProvider(CardanoscanProvider.PREVIEW_URL, apiKey); + } + } + + async getTransactionList(options: GetTransactionListOptions): Promise { + const { address, pageNo, limit = 20, order } = options; + if (!address || address.length > 200) { + throw new Error("Address is required and must be max 200 characters"); + } + if (pageNo < 1) { + throw new Error("pageNo must be at least 1"); + } + if (limit < 1 || limit > 50) { + throw new Error("limit must be between 1 and 50"); + } + if (!["asc", "desc"].includes(order)) { + throw new Error("order must be 'asc' or 'desc'"); + } + + const url = new URL(`${this.baseUrl}/transaction/list`); + url.searchParams.set("address", address); + url.searchParams.set("pageNo", pageNo.toString()); + url.searchParams.set("limit", limit.toString()); + url.searchParams.set("order", order); + + const response = await fetch(url.toString(), { + method: "GET", + headers: { + Accept: "application/json", + apiKey: this.apiKey, + }, + }); + + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`Cardanoscan API error: ${response.status} ${response.statusText} ${errorText}`); + } + + return (await response.json()) as CardanoscanTransactionListResponse; + } + + async getTransactionListByAddress( + address: Address, + pageNo: number, + limit: number, + order: "asc" | "desc", + ): Promise { + return this.getTransactionList({ + address: address.toHex(), + pageNo, + limit, + order, + }); + } + + async getAllTransactionsByAddress(address: Address, maxPages?: number): Promise { + const allTransactions: CardanoscanTransaction[] = []; + let pageNo = 1; + let hasMore = true; + while (hasMore && (!maxPages || pageNo <= maxPages)) { + const response = await this.getTransactionListByAddress(address, pageNo, 50, "desc"); + allTransactions.push(...response.transactions); + hasMore = response.transactions.length === response.limit; + pageNo++; + } + return allTransactions; + } + + async getLatestTransaction(address: Address): Promise { + const response = await this.getTransactionListByAddress(address, 1, 1, "desc"); + return response.transactions[0] ?? null; + } + + async findTransactionByHash( + address: Address, + txHash: string, + pageSize?: number, + maxPage?: number, + ): Promise { + let pageNo = 1; + let hasMore = true; + while (hasMore) { + const response = await this.getTransactionListByAddress(address, pageNo, pageSize ?? 50, "desc"); + const found = response.transactions.find((tx) => tx.hash === txHash); + if (found) return found; + hasMore = response.transactions.length === response.limit; + pageNo++; + if (pageNo > (maxPage ?? 100)) break; + } + return null; + } + + async findTransactionHasSpent( + address: Address, + txHash: string, + index: number, + pageSize?: number, + maxPage?: number, + ): Promise { + let pageNo = 1; + let hasMore = true; + while (hasMore) { + const response = await this.getTransactionListByAddress(address, pageNo, pageSize ?? 50, "desc"); + for (const tx of response.transactions) { + const spent = tx.inputs.find((input) => input.txId === txHash && input.index === index); + if (spent) return tx; + } + hasMore = response.transactions.length === response.limit; + pageNo++; + if (pageNo > (maxPage ?? 100)) break; + } + return null; + } + + /** + * Submit a signed transaction CBOR to the Cardano network. + * On 204 success, returns the submitted tx hex (caller can hash separately if needed). + */ + async submitTx(txCbor: string): Promise { + const url = `${this.baseUrl}/transaction/submit`; + const txBytes = Buffer.from(txCbor, "hex"); + + const response = await fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/cbor", + apiKey: this.apiKey, + }, + body: txBytes, + }); + + if (response.status === 204) { + return txCbor; + } + + const errorData = (await response.json().catch(() => ({}))) as { error?: string }; + const errorMsg = errorData.error ?? response.statusText; + throw new Error(`Cardanoscan submit failed (${response.status}): ${errorMsg}`); + } +} diff --git a/packages/provider/src/index.ts b/packages/provider/src/index.ts index 7840d0d..56af2ac 100644 --- a/packages/provider/src/index.ts +++ b/packages/provider/src/index.ts @@ -1 +1,2 @@ +export * from "./cardanoscan"; export * from "./kupo"; diff --git a/packages/provider/src/kupo.ts b/packages/provider/src/kupo.ts index 8480fa9..e81b729 100644 --- a/packages/provider/src/kupo.ts +++ b/packages/provider/src/kupo.ts @@ -1,12 +1,27 @@ import invariant from "@minswap/tiny-invariant"; import { Address, type Asset, type Bytes, type KupoUtxo, type TxIn, Utxo, Value } from "@minswap/felis-ledger-core"; import { type CborHex, type CSLTransactionUnspentOutput, Maybe } from "@minswap/felis-ledger-utils"; +import JSONBig from "json-bigint"; import * as R from "remeda"; import { uniq } from "./lodash"; +// Kupo response asset quantities (e.g. LP tokens) can exceed 2^53. Native JSON.parse +// rounds them to JS Number, breaking conservation when we re-serialize the value +// into a tx. Parse responses with json-bigint so quantities arrive as native bigints. +const jsonBig = JSONBig({ useNativeBigInt: true }); + +async function fetchJsonBig(input: string, init?: RequestInit): Promise { + const res = await fetch(input, init); + if (!res.ok) { + throw new Error(`Kupo request failed: ${res.status} ${res.statusText}`); + } + return jsonBig.parse(await res.text()) as T; +} + const KUPO_MAX_REQUEST = 100; const DATUM_REGEX = /^[0-9a-fA-F]{64}$/; +const SCRIPT_HASH_REGEX = /^[0-9a-fA-F]{56}$/; export type KupoHealthResponse = { connection_status: "connected" | "disconnected"; @@ -176,10 +191,9 @@ export class KupoService { async health(): Promise { const kupoUrl = `${this.kupoBaseUrl}/health`; - const fetchRes = await fetch(kupoUrl, { + const response = await fetchJsonBig(kupoUrl, { headers: { Accept: "application/json;charset=utf-8" }, }); - const response = (await fetchRes.json()) as KupoHealthResponse; // We assume slot won't exceed MAX_SAFE_INTEGER const latestSyncedSlot = response.most_recent_checkpoint ?? 0; @@ -198,15 +212,11 @@ export class KupoService { async getUtxosByMatches(matches: string): Promise { const kupoUrl = `${this.kupoBaseUrl}/matches/${matches}`; - const fetchRes = await fetch(kupoUrl); - if (!fetchRes.ok) { - throw new Error(`Kupo request failed: ${fetchRes.status} ${fetchRes.statusText}`); - } - const data = await fetchRes.json(); + const data = await fetchJsonBig(kupoUrl); if (!Array.isArray(data)) { throw new Error(`Kupo returned unexpected response: ${JSON.stringify(data)}`); } - return (data as KupoUtxosResponse[]).map((d) => ({ + return data.map((d) => ({ ...d, transaction_index: Number(d.transaction_index.toString()), output_index: Number(d.output_index.toString()), @@ -254,8 +264,7 @@ export class KupoService { return null; } const kupoUrl = `${this.kupoBaseUrl}/datums/${datumHash}`; - const fetchRes = await fetch(kupoUrl); - const data = (await fetchRes.json()) as { datum: string } | null; + const data = await fetchJsonBig<{ datum: string } | null>(kupoUrl); return data ? data.datum : null; } @@ -304,27 +313,55 @@ export class KupoService { return mapDatumHash; } + async getScript(scriptHash: string): Promise<{ language: string; script: string } | null> { + if (!SCRIPT_HASH_REGEX.test(scriptHash)) { + return null; + } + const kupoUrl = `${this.kupoBaseUrl}/scripts/${scriptHash}`; + return await fetchJsonBig<{ language: string; script: string } | null>(kupoUrl); + } + + async getScripts(scriptHashes: string[]): Promise> { + if (scriptHashes.length === 0) { + return {}; + } + const uniqueHashes = uniq(scriptHashes); + const mapScript: Record = {}; + for (let i = 0; i < uniqueHashes.length; i += KUPO_MAX_REQUEST) { + const batch = uniqueHashes.slice(i, i + KUPO_MAX_REQUEST); + const tasks = batch.map((hash) => this.getScript(hash)); + const results = await Promise.all(tasks); + for (let j = 0; j < batch.length; j++) { + const result = results[j]; + if (result) { + mapScript[batch[j]] = result; + } + } + } + return mapScript; + } + private async parseUtxo(kupoUtxos: KupoUtxo[]): Promise { const datumHashes = kupoUtxos.filter((u) => u.datum_hash && u.datum_type).map((u) => u.datum_hash) as string[]; + const scriptHashes = kupoUtxos.filter((u) => u.script_hash).map((u) => u.script_hash) as string[]; + + const [mapDatum, mapScript] = await Promise.all([this.getDatums(datumHashes), this.getScripts(scriptHashes)]); - if (!datumHashes.length) { - return kupoUtxos.map((u) => Utxo.fromKupo(u)); - } - const mapDatum: Record = await this.getDatums(datumHashes); const utxos: Utxo[] = []; for (const u of kupoUtxos) { + let datum: string | undefined; /** * In Kupo, inline datums are not stored in the UTXO, so we need to fetch them separately. */ if (u.datum_hash && u.datum_type === "inline") { - const datumRaw = mapDatum[u.datum_hash]; - invariant(datumRaw, `InlineDatum requires a valid datum, not found datum for ${u.datum_hash}`); - utxos.push(Utxo.fromKupo(u, datumRaw)); + datum = mapDatum[u.datum_hash]; + invariant(datum, `InlineDatum requires a valid datum, not found datum for ${u.datum_hash}`); } else if (u.datum_hash && u.datum_type === "hash") { - utxos.push(Utxo.fromKupo(u, mapDatum[u.datum_hash])); - } else { - utxos.push(Utxo.fromKupo(u)); + datum = mapDatum[u.datum_hash]; } + + const scriptData = u.script_hash ? mapScript[u.script_hash] : undefined; + utxos.push(Utxo.fromKupo(u, datum, scriptData)); } return utxos; } diff --git a/packages/syncer/src/wingriders-v2-syncer.ts b/packages/syncer/src/wingriders-v2-syncer.ts index 7ab45ae..75aa32d 100644 --- a/packages/syncer/src/wingriders-v2-syncer.ts +++ b/packages/syncer/src/wingriders-v2-syncer.ts @@ -1,6 +1,6 @@ import { DatumSourceType, type NetworkEnvironment } from "@minswap/felis-ledger-core"; import { Maybe } from "@minswap/felis-ledger-utils"; -import { WingridersV2 } from "@minswap/felis-wingriders-v2"; +import { WingridersV2, WingridersV2Warehouse } from "@minswap/felis-wingriders-v2"; import type { Transaction } from "./transaction"; import type { WrapAddress, WrapAsset, WrapNum } from "./types"; @@ -32,9 +32,10 @@ export namespace WingridersV2Syncer { plutusData: Transaction["witnessSet"]["plutusData"], networkEnv: NetworkEnvironment, ): Maybe { - // Check if output is to the order script + // Check if output is to the request (order) script for CP pools + const warehouse = WingridersV2Warehouse.getInstance(networkEnv); const scriptHash = output.address.toScriptHash(); - if (!scriptHash || scriptHash.hex !== WingridersV2.ORDER_SCRIPT_HASH) { + if (!scriptHash || scriptHash.hex !== warehouse.requestValidatorHashCP.hex) { return null; } diff --git a/packages/wingriders-v2/src/index.ts b/packages/wingriders-v2/src/index.ts index ad8f344..56e1e53 100644 --- a/packages/wingriders-v2/src/index.ts +++ b/packages/wingriders-v2/src/index.ts @@ -1 +1,3 @@ export * from "./wingriders-v2"; +export * from "./warehouse"; +export * from "./utils"; diff --git a/packages/wingriders-v2/src/scripts/mainnet-references.json b/packages/wingriders-v2/src/scripts/mainnet-references.json new file mode 100644 index 0000000..f21abb6 --- /dev/null +++ b/packages/wingriders-v2/src/scripts/mainnet-references.json @@ -0,0 +1,10 @@ +{ + "dexSymbolRefInput": "828258205ec56338104fcbfe32288c649d9633f0d9060abce8b8608b156294f0a81d29e204a300581d7151936f3c98a04b6609aa9b5c832ba1182cf43a58e534fcc05db09d69011a00349cc003d818590253820259024e59024b010000323232323232323232323232323222323232533300e001149858c8c8c94ccc040cdc3a400400426664446601c44a66602c002294054ccc054cdc424000666444666602200490001199980900124000eb4dd5800801991bab30193015301a0013230193015301a001301800100500414a2260046028002002646464a66602666e1d20000021375c602c0022c602e00460260026ea8c04cc03c014038dd6180980209991199809001000a503322323300124a22940cc8c038894ccc05800448940044ccc00cc060004888c00800c4c008c05000494ccc04ccdd7801980b180b800891180100189128008009809980a0041bac301300433332222333300d0034a0464a66602e002294054cc048d5d1980a80089929980999b8f375c603200200a266e1cdd6980e000803980c8009bab00100148008c8c8c94ccc04ccdc3a400000426eb8c05800458c05c008c04c004dd5180998078028071bab301332301030103010001300f00430140023010001375464646464010a66602066e1d20000021324994ccc04400452616153330103370e900100109924ca6660220022930b0b180a00118080009baa0043010300f001300f0014c012fd8799fd8799fd8799f5820914a0f1e615f41b3f462855f6052a6b37d1d291aab53918ad4c9d5f4245204ceff01ffff0023002375200244446600a44a66601a002200a2a66601866ebcc038c03c0040184c010c044c03c0044c008c02c0040048c8c0088cc0080080048c0088cc00800800488ccc01800800400c5282450146005744ae6955cfaab9d5742460046ea800555cf1", + "dexSymbolTxId": "5ec56338104fcbfe32288c649d9633f0d9060abce8b8608b156294f0a81d29e2#4", + "factoryRefInput": "828258205ec56338104fcbfe32288c649d9633f0d9060abce8b8608b156294f0a81d29e200a300581d7151936f3c98a04b6609aa9b5c832ba1182cf43a58e534fcc05db09d69011a0164947403d818591461820259145c5914590100003232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323222233322232533303c001149858c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c00488cc8c8c94ccc150cdc3a40040042646464644666600200a0080060046644444444444464646464646464646464646464646464646464646464603c44660c4660c466e3cdd7183f00c0109983119b8f375c60fc02e03a660c466e3cdd7183f00b00e1983119b8f375c60fc02a036660c466e3cdd7183f00a00d1983119b87375a60fc01c09a660c466e1cdd6983f009a4014660c466e1cdd6983f0078261983119b87375a60fc02490011983119b87375a60fc02290001983119b87375a60fc02090001983119b87375a60fc01890001983119b87375a60fc01690001983119b87375a60fc01490001983119b87375a60fc01290001983119b87375a60fc01090001983119b87375a60fc00e90001983119824183f00319ba5480081b4cc188cc120c1f8014cdd2a40040da660c466e1cdd6983f0068011983119b87375a60fc00604866e1cdd6983f183e8018119983119b87337020ce03266e0cdd6983f00219b823370490020120119983129983c9999824a412c026eb4c1f8010cdc100f81219b8201e023133330494825804cdc01bad307e00448008cdc100f81219b8201e023330623371090000121983119b884800008d4cc1e4cdc381224004266e1c08d2002307a0013074307a3079001307800130770013076001307500130740013073001307200130710013070001306f001306e001306d001306c001306b001306a001306900130680013067001306600130603232323200e53330663370e900000109919299983419b87371a002901c099191919299983629983519b87001480004cdc3800a40702646464a6660de66e24dc6800a4080264646464a6660e6a660e266e1c005200013370e002901c0991919299983b19b89371a0029020099191919191919191919191919191919191919191929998458099b89480000044c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c92653330b101001149858c2c4040194ccc2b804cdc3a400000426464646464646464646493299985c008008a4c2c6170020066eb4004c2dc04004c2d40400cdd6800985a008009859008019bad00130b1010011630b10100230b1010013754002615a0200261560200ca6661500266e1d20000021323232323232324994ccc2bc040045261630af0100653330ac013370e900000109919191919191919191919191924ca666172020022930b185c80803299985b0099b87480000084c8c8c8c8c8c8c92653330bd01001149858c2f4040194ccc2e804cdc3a4000004264646464646464932999860808008a4c2c61820200ca66617c0266e1d20000021323253330c0013370e6e340052038132324994ccc308040045261630c20100316375c0026182020022a66617c0266e1d20020021323253330c0013370e6e340052038132324994ccc308040045261630c20100316375c0026182020022c6182020046182020026ea8004c2f40400454ccc2e804cdc3a4004004264646464646464646464932999862008008a4c2c6188020066eb4004c30c04004c3040400cdd6800986000800985f008019bad00130bd010011630bd0100230bd0100137540026172020022a66616c0266e1d20020021324994ccc2dc04004526161630b90100230b9010013754002616a0200261660200ca6661600266e1d20000021323253330b2013370e6e340052038132324994ccc2d0040045261630b40100316375c0026166020022a6661600266e1d20020021323253330b2013370e6e340052038132324994ccc2d0040045261630b40100316375c0026166020022c6166020046166020026ea8004c2bc0400458c2bc04008c2bc04004dd50009855808008a9998540099b87480080084c92653330a90100114985858c2ac04008c2ac04004dd500098538080098528080329998510099b87480000084c8c8c8c8c8c8c92653330a901001149858c2a4040194ccc29804cdc3a4000004264646464646464646464646464932999859808008a4c2c61660200ca6661600266e1d20000021323232323232324994ccc2dc040045261630b70100653330b4013370e900000109919191919191924ca666176020022930b185d80803299985c0099b87480000084c8c94ccc2e804cdc39b8d001480e04c8c92653330bc01001149858c2f00400c58dd7000985d808008a99985c0099b87480080084c8c94ccc2e804cdc39b8d001480e04c8c92653330bc01001149858c2f00400c58dd7000985d808008b185d80801185d808009baa00130b701001153330b4013370e900100109919191919191919191924ca66617c020022930b185f008019bad00130bd0100130bb01003375a0026174020026170020066eb4004c2dc0400458c2dc04008c2dc04004dd50009859808008a9998580099b87480080084c92653330b10100114985858c2cc04008c2cc04004dd500098578080098568080329998550099b87480000084c8c94ccc2b004cdc39b8d001480e04c8c92653330ae01001149858c2b80400c58dd70009856808008a9998550099b87480080084c8c94ccc2b004cdc39b8d001480e04c8c92653330ae01001149858c2b80400c58dd70009856808008b1856808011856808009baa00130a9010011630a90100230a9010013754002614a020022a6661440266e1d20020021324994ccc28c04004526161630a50100230a5010013754002614202002613e020066eb4004c27804004c2700400cdd6800984d80800984c808019bad001309801001309601003375a002612a020026126020066eb4004c24804004c2400400cdd68009847808009846808018b1bad001308c01001308a01003375a002611202002610e020066eb4004c21804004c2100400cdd68009841808009840808019bad001308001001307e003375a00260fa00260f60066eb4004c1e8004c1e000c58dd7000983b800983a8020b1b8d001375c00260e600260e20062c6eb8004c1c0004c1b801058dc68009bae001306c001306a00316375c00260d20022c60d200460d20026ea8028dd6982d0021bad305a3059004375c60b20266eb8c16004cccccccc09d221013100375a60ae0026eb4c15cc158004dd7182b80c1bae3057017375c60ae02c6eb8c15cc1580584c8c8c8c88cccc00401401000c0088888888888c02088c8c8c8cc134cdd799ba548000cc164dd48071982c9ba900a330593752012660b26ea4020cc164dd48039982c9ba8480f0cc164dd424014660b26ea1200033059375090001982c9ba8037330593750070660b26ea0014cc164dd424000660b26ea1200033059375090001982c9ba848000cc164dd424000660b26ea12000330593374a900102c1982c99ba548008160cc164cdd2a40000b00b001e6609a66e24cdc100180180099b880013370400400466e0802c028cdc0000a400466e0413c00cdd7182c80a9bae305801533333330274881013000480092002375c60ae0306eb8c15c05cdd7182b80b1bae3057305601630570023057001375460a802c44446464646464646464646608a66e20cdc0807007a4101e164a46608a66e1cccc154054dd71830983000d827240046608a66e1cccc154054dd71830983000d80699b8104a042330453370e6090646600240022c66466444660bc44a6660ca002244a0022a6660c866ebcc1a4c19c0040104c014c19c0044c008c1980040048c888c00800cdd598320009ba9001375c60c260c003602a90031982299999999998050030058080020019bae3061022375c60c20426eb8c184080dd718309830010001198229982299b8848000010cc114cdc424000006a660b866e2520000051304b375c60c20446608a66e212000337020020846608a6608aa6660bc66e3cdd718308111bae30610201337206eb8c184084dd718309830010099b90375c60c20446eb8c184080cc114c10d4cc148cdc79bae3061022375c60c260c0036266e3cdd71830810806998229821a9982919b8f375c60c20406eb8c184c18006c4cdc79bae3061306002000d3304530435330523371e6eb8c184088dd71830983000d899b8f375c60c204209c6086a660a466e3cdd718308101bae3061306001b13371e6eb8c184c180080138cc114cdc39823003999119b815333060304d0021480205200a53330603370e00290000a4004290001bae3061022002330453370e609002490011982299b87333055015375c60c260c00360b29001198229982299b90375c60c203e01a66e40034dd71830983000fa99982f8088a50153305232533306000114a02a660a66ae8cc1840044cc114cc128038dd71831183081018310009830008899822198249bae306101f00d3061011337020920026660a600a6eb8c17cc17806402cccc148010dd7182f00e9bae305e305d01d533305a3047375c60ba03c200226660a20066eb8c174078dd7182e80e99b81333050002047047482026fb808c11cc16cc168008dd5982d000982c182980099299982b8008b099299982c0008982d0010b182c00099823129982519826002000899b8733304d3050001375c60b260b002608c90011bac305800f323232323253330533370e90010010991919299982b19b87480080084c8c88cc00400c008dd6982d0011bad3059004163059002305900137546460ae60a200260ac60aa0082c60ac00460ac0026ea8c8c150c138004c14c004c130004c144018cc0f894cc108cc11000c0044cdc399982298240009bae3051305000b04948008dd618280039981e91929982119822001800899b873330453048001375c60a260a001609290011828182798250009bac304f00732323232533304f3370e900100109bae305200116305200230520013754609e6092002609c60900026644646464a66609e66e1d20020021323304c225333053001161325333053323375e6e9cc144014dd39828800982b0008982b182a80089801982a8011827982a80080298290008b182900118290009baa002304d304c0063758609a00a6eacc130008c8c12cc12c004c128004c124c124004c120c120004c108c120004c104028c114004c110004c10c004c0f4030c0f001cc100004c0fc004c0f8004c0e0008c8c8c8c801d4ccc0eccdc3a40000042646464646464649329998210008a4c2c60840066eb8004c104004c0fc00cdd7000981f0008b181f001181f0009baa00332323232006533303b3370e90000010991919191919191919299982229982119b87001480004cdc3800a40702646464a66608e66e24dc6800a4080264646464a666096a6609266e1c005200013370e002901c0991919299982719b89371a00290200991924ca6660a00022930b18280018b1bae001304f001304d00416371a0026eb8004c12c004c12400c58dd7000982400098230020b1b8d001375c0026088002608400ca66607e66e1d20000021324994ccc100004526161533303f3370e900100109919191919191924ca66608c0022930b18230019bad00130450013043003375a00260840022c608400460840026ea8004c0f800458c0f8008c0f8004dd5001000a6019ad8799f581caf97793b8702f381976cec83e303e9ce17781458c73c4bb16fe02b83581cc134d839a64a5dfb9b155869ef3f34280751a622f69958baa8ffd29c581c946ae228430f2fc64aa8b3acb910ee27e9b3e47aa8f925fac27834a1581c23680ea6701b56f2c12ae79d8af94fd36f509b7b007029c7ce114840581c6fdc63a1d71dc2c65502b79baae7fb543185702b12c3c5fb639ed737ff002232323253330383370e90010010991919299981d99b8748000008528099baf374e0086e9c004c0f8008c0f8004dd50020991919299981d99b8748008008528099baf374e0086e9c004c0f8008c0f8004dd5002181d801181d8009baa0022222323232330203371266e04ccc02001c01800ccccc02401c01800c009200033710900019b8133300800700100333330090070010030023370000a900119b800030023370400400244466e00cdc119b8233704006901000100099b8233704004004004444466e08cdc12401000466e00cdc119b820044802000400d20a09c01482024bd0048888888dca19b8a372800e66e28dca1b983008006337146e50dcc180400299b8a372866e2801000cdca19b8a0020013028223372ca66605c66e20005200010031004323253330303370e00490000980380089919b96001300800230040023370a6022004900a19b843010001480512401012d004901002533302a3370e002900008010a99981519b8700148008400c54ccc0a8cdc3800a400820082a66605466e1c005200610051533302a3370e002900408030a99981519b8700148028401c54ccc0a8cdc3800a401820102a66605466e1c005200e10091533302a3370e002900808050a99981519b8700148048402c592410130004901013100490101320049010133004901013400490101350049010136004901013700490101380049010139002533301f337120029000898010008800919b814800000520d00f233301c0014a094488c8cc010cdd798061810180f80080199b873005375660400029002180f180c8009119980d801000a5023330022233700004600a6eacc07c005200000123014222533301c001100213300333004002301e001301d00133012222533301a0011002133003337000049001180d800a40004466e9520003300a3752004660146ea400402520feffffffffffffffff0123371e002004910100232323253330153370e90020010980c0008b180c001180c0009baa001489014c002300c2253330130011004132533301330040011330060013003301500213003301500230150014bd702ba02233300e00200100314a044646464a66601e66e1d200000214a0266e3cdd71809000802980900118090009baa323010300a001300f30090012223333004002480008cccc014009200075a6eac00400c8c008dd480091111980391299980700088028a99980699baf30123010001006130043011301000113002300f0010012375660146012600800291101460023230022330020020012300223300200200123008375400246600600200429455cd2ab9f5744ae8555cf2ab9d1", + "factoryTxId": "5ec56338104fcbfe32288c649d9633f0d9060abce8b8608b156294f0a81d29e2#0", + "orderRefInput": "828258205ec56338104fcbfe32288c649d9633f0d9060abce8b8608b156294f0a81d29e201a300581d7151936f3c98a04b6609aa9b5c832ba1182cf43a58e534fcc05db09d69011a00293c2203d8185901a682025901a159019e010000323232323232323232222325333008001149858c8c8c94ccc028cdc3a40040042664601444a666aae7c0045280a99980699baf301000100314a226004601c00264646464a66601c66e1d20000021301100116301100230110013754601c601a002601a6010601800c646eb0c038c8c034c034c034c034c034c034c028004c034004c034c0300104ccc888cdc79919191bae301300132323253330123370e90000010b0800980a801180a8009baa3012301100132301230110013011300f301000133300c222533301033712900500109980199b8100248028c044c044c044c044c04400454ccc040cdc3801240002602600226644a66602466e20009200016133301122253330153370e00490000980c00089980199b8100248008c058004008004cdc0801240046022002004646eb0c044c040004c040c03c00400cdd70039bad300d001004300d002300d00137540046ea52211caf97793b8702f381976cec83e303e9ce17781458c73c4bb16fe02b83002300430040012323002233002002001230022330020020015734ae888c00cdd5000aba15573caae741", + "orderTxId": "5ec56338104fcbfe32288c649d9633f0d9060abce8b8608b156294f0a81d29e2#1", + "poolCpRefInput": "82825820babc647257b8d78b86e862ba9769401714ed403e7c46ed1b59c3fc32e0247c8200a300581d7151936f3c98a04b6609aa9b5c832ba1182cf43a58e534fcc05db09d69011a034f84ba03d81859318a8202593185593182010000323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232222325333056001149858c8c8c94ccc160cdc3a4000004264646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464600244646464646466464466002006004900124004446464646464660c2660bc612c02016646464a66612e0266e1d2002002130990100116309a01002309a010013754612c02612a0206a660c266e1cccc1c8dd5984b008049bae30960103b07548008cc184cc184c144cdc39bad30960103c375a612c0207a660c266e1cccc1c8dd5984b00984a808061bae309601039375c612c020749001191919299984b8099b874800000852889919832982a99b8f00101530553371e0020186eb8c26404004c26804008c26804004dd519184b80984c00800984b00806198309982f001180091111111111111119ba548000cc22004dd481019844009ba902a3308801375205266110026ea40a0cc22004dd481399844009ba80263308801375004a66110026ea0090cc22004dd401199844009ba80223308801375004266110026ea0074cc22004dd400619844009ba800b3308801375001466110026ea0024cc22004dd400419844009ba8007330880130a50102c330880130a50102b33088013374a90000438084380998309999999998301bae30960103b07500501b01a0190183756612c02006002660c2660c266e1c0c0cdc02400860ca6eb0c25804c254040f0cdc424008060660c260a26466612802444a6660f600429404c94ccc26404cc2600488cc004894ccc27404cdc38010028a511300400114a000429444cc010c2680400ccc164004008dd6984e809826984d008010009128009bac30960130950103c3371066e040340392080f0b252323333309301222223300222533307e0031613333007006333006005002309d01003001309c01003100322230022232323001222232323253330a2013370e90000010980411111801911180291119191980d111111111111111299985e80808899999999983680300280200180100780700811111119119800801003191919119999999999999980080500480b80200a80180980100880800780700680600599b800030103370000602266e0000c0484ccccccccc1b401801401000c00803803c040888888c88cc004008018c8c8c88ccccccccccccccc00402402805c05801005000c04800804003c03803403002ccdc000180799b800030103370000602244a66616002660fa66e2120000033307d337109000000a9998580080209983e99b89001006337126eb4c2c804c2c40403c0184cc1f4cdc480080399b89375a61640261620201e00e20042ca66615a02002200e200c646464a66615e0266e1d200200214a02944c2c804008c2c804004dd51857008058a9998510099b87480100084c038888888888888888c05c8888c00c888c014888d4ccc2ec04cdc39bad30bd0101a480004c8c004888888888888888c8c88ccc00404c00c008cdc0814807a4000604e444444444444444646002444444444444444666446024444444444444444a6661d60202226666666661360200c00a00800600401e01c0204444446446600200400c646464466666666666666600201401202e00802a00602600402202001e01c01a01801666e0000c040cdc000180899b80003012133333333309b0100600500400300200e00f01022222232233001002006323232233333333333333300100900a01701600401400301200201000f00e00d00c00b3370000601e66e0000c040cdc0001808a513370203e01e440046661380206c44a0020262a6661760266e1cdd6985e80985e0080d24000264600244444444444444464644666002026006004900019b8102900f302722222222222222232300122222222222222233322301222222222222222253330eb01011133333333309b0100600500400300200f00e01022222232233001002006323232233333333333333300100a00901700401500301300201101000f00e00d00c00b3370000602066e0000c044cdc0001809099999999984d8080300280200180100700780811111119119800801003191919119999999999999980080480500b80b00200a00180900100800780700680600599b8000300f3370000602066e0000c0452819b8101e00e2200233309c010362250010131323001222222222222222323223330010130030023370205201e66e040a403cccc2340409c8940040108894ccc2f804cc22c04cdc42400000e661160266e24008018cc22c04cdc49bad30c00101d0063308b013371200200a66e24dd6986000985f8080e80288018b0a9998510099b87480180084c038888888888888888c05c8888c0088894ccc2e004cc214054cc1f4cdc424000026266e21200001233085013371202600666e240480084c8c88ccccccccccccccc00406406005c00c00805004c04804404003c03803403002d2000480005854ccc28804cdc3a40100042601044446006444600a4446030444444444444444a6661760266e1c0492000132233333333333333300100201000f00e00d00c00b00a0090080070060050040033370002a01e2c2a6661440266e1d200a0021300e22222222222222230172222300222253330b801330850153307d337109000008899b8848000040cc21404cdc480880199b8901000213232233333333333333300101901801701601500300201201101000f00e00d00c00b48001200016153330a2013370e900600109807111111111111111180b91111801111299985c009984280a9983e99b884800003c4cdc42400001c6610a0266e2403c00ccdc480700109919119999999999999980080c80c00b80b00a80a009801801008007807006806005a400090000b09804111118019111802911191a9998568099b87007480004ccc88c06c8888888888888894ccc2f8040444ccccccccc1b801801401000c00803c038040888888c88cc004008018c8c8c88ccccccccccccccc00402802405c01005400c04c00804404003c03803403002ccdc000180819b800030113370000602426666666660dc00c00a00800600401c01e0204444446446600200400c646464466666666666666600201201402e02c00802800602400402001e01c01a01801666e0000c03ccdc000180819b800030114a0002446004444444444444444646466644605c444444444444444a6661a20202226666666661020200c00a00800600401e01c0204444446446600200400c646464466666666666666600201401202e00802a00602600402202001e01c01a01801666e0000c040cdc000180899b8000301213333333330810100600500400300200e00f01022222232233001002006323232233333333333333300100900a01701600401400301200201000f00e00d00c00b3370000601e66e0000c040cdc0001808a50337020289001111801111111111111111192999869009984f8099b893370666e0804c050088cdc199b820240140233371066e0ccdc100880a00819b833370400202801e26661080204a0480262c66e040a8cdc0812a400466e0406004ccdc0848808068a9998568099b87006480004ccc88c06c8888888888888894ccc2f8040444ccccccccc1b801801401000c00803c038040888888c88cc004008018c8c8c88ccccccccccccccc00402802405c01005400c04c00804404003c03803403002ccdc000180819b800030113370000602426666666660dc00c00a00800600401c01e0204444446446600200400c646464466666666666666600201201402e02c00802800602400402001e01c01a01801666e0000c03ccdc000180819b800030114a2002446004444444444444444646466644605c444444444444444a6661a20202226666666661020200c00a00800600401e01c0204444446446600200400c646464466666666666666600201401202e00802a00602600402202001e01c01a01801666e0000c040cdc000180899b8000301213333333330810100600500400300200e00f01022222232233001002006323232233333333333333300100900a01701600401400301200201000f00e00d00c00b3370000601e66e0000c040cdc0001808a51337020289001111801111111111111111192999869009984f8099b893370666e0804c05008ccdc199b820240140223309f013371066e0ccdc100880a00799b83337040020280206613e0266e2120000263371004c05826661080204a0260482c66e040accdc0812a400466e0406404ccdc084880806899982f80c803803111801111111111111111299985f00998458099b8948000034cc22c04cdc424000020661160266e24dd698600080e80999b89010013101116375a00e614a02004614a020026ea8010cccc01c004008c8c8c8c8c80154ccc27804cdc3a4000004264646464646464646464646464646464646464646464646464a66616e0266e25200000113232323253330bb01533080013370e0029000099b87001480e04c8c8c94ccc2f804cdc49b8d001481004c8c8c8c94ccc308054cc21c04cdc3800a4000266e1c0052038132323253330c501337126e340052040132323232323232323232323232324994ccc2d4040045261630d201003375a00261a202002619e020066eb4004c33804004c330040194ccc32804cdc3a400000426464646464646464646493299985b008008a4c2c61a6020066eb4004c34804004c340040194ccc33804cdc3a4000004264932999858808008a4c2c2a66619c0266e1d20020021324994ccc2c404004526161630d10100230d10100137540026198020022a6661940266e1d20020021323232324994ccc2c0040045261630cd01003375a0026198020022a6661940266e1d20040021323232323232324994ccc2cc040045261630d001003375a002619e02002619a020066eb4004c3300400454ccc32804cdc3a400c004264932999856808008a4c2c2a6661940266e1d20080021324994ccc2b40400452616153330ca013370e900500109924ca66615a020022930b0a9998650099b87480300084c92653330ad0100114985858c33404008c33404004dd50009864008009863008018b1bae00130c50100130c30100416371a0026eb8004c30404004c2fc0400c58dd7000985f00800985e008020b1b8d001375c0026174020026170020062c6eb4004c2dc04004c2d4040194ccc2cc04cdc3a400000426493299984b008008a4c2c2a6661660266e1d20020021324994ccc2580400452616153330b3013370e900200109924ca66612c020022930b0b185b00801185b008009baa00130b10100130af0100230af0100130ad0100653330ab013370e900000109919191919191919191919191924ca666134020022930b185b80803299985a8099b87480000084c8c8c8c8c8c8c926533309e01001149858c2ec040194ccc2e404cdc3a4000004264646464646464932999851008008a4c2c617e0200ca66617a0266e1d20000021323253330bf013370e6e340052038132324994ccc28c040045261630c00100316375c002617e020022a66617a0266e1d20020021323253330bf013370e6e340052038132324994ccc28c040045261630c00100316375c002617e020022c6180020046180020026ea8004c2ec0400454ccc2e404cdc3a4004004264646464646464646464932999852808008a4c2c6184020066eb4004c30404004c2fc0400cdd6800985f00800985e008019bad00130bb010011630bc0100230bc010013754002616e020022a66616a0266e1d20020021324994ccc26004004526161630b80100230b801001375400261660200261620200ca66615e0266e1d20000021323253330b1013370e6e340052038132324994ccc254040045261630b20100316375c0026162020022a66615e0266e1d20020021323253330b1013370e6e340052038132324994ccc254040045261630b20100316375c0026162020022c6164020046164020026ea8004c2b40400458c2b804008c2b804004dd500098548080098538080329998528099b87480000084c8c8c8c8c8c8c8c8c8c8c8c8c926533309401001149858c2c4040194ccc2bc04cdc3a400000426464646464646493299984c008008a4c2c616a0200ca6661660266e1d20000021323232323232324994ccc270040045261630b90100653330b7013370e900000109919299985c8099b87371a002901c0991924ca66613a020022930b185d008018b1bae00130b901001153330b7013370e900100109919299985c8099b87371a002901c0991924ca66613a020022930b185d008018b1bae00130b9010011630ba0100230ba010013754002616a020022a6661660266e1d20020021323232323232323232324994ccc27c040045261630bc01003375a0026176020026172020066eb4004c2e004004c2d80400cdd6800985a808008b185b00801185b008009baa00130b101001153330af013370e900100109924ca666124020022930b0b1859008011859008009baa00130ad0100130ab0100653330a9013370e90000010991929998558099b87371a002901c0991924ca66611e020022930b1856008018b1bae00130ab01001153330a9013370e90010010991929998558099b87371a002901c0991924ca66611e020022930b1856008018b1bae00130ab010011630ac0100230ac010013754002614e020022c6150020046150020026ea8004c28c04004c2840400cdd68009850008008b1850808011850808009baa001306e309c01309701309d01001004309b01309a01309c010023333333333333333333069375a612c020466eb4c25804088dd6984b008109bad309601020375a612c0203e6eb4c2580407806c06806406005c05805405004cdd7184b0081d8029bab309601009010333330930122222533307c00112250011323233533309b013371000200826609e00200c26609e66e04cdc08008022400400a44660ba644660020080046142020086666012010002006613c0200a2c6eb4c27c04004c138c26c040040c489400520ce0f3758612c02612a02078612a026eb0c258040c8ccccccccccccc88888888888888888c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c94ccc2dc04cc21004cdd7985c80809985c80807198420099b8801a375a617202016661080266e3cc2d404c2e40404406ccc21004cc21004cdc79bae30b90100a02133084013371e6eb8c2e404024080cc21004cdc79bae30b90100801f3371e6eb8c2e40401c078cc21004cc88c8c8c94ccc2f004cdc3a40040042646464a66617e0266e1d200200213375e6e9c010dd38008a5030c20100230c20100137540082a6661780266e1d2004002132323253330bf013370e9002001099baf374e0086e9c0045281861008011861008009baa004132323253330bf013370e9000001099baf374e0086e9c0045281861008011861008009baa00430bf0100230bf010013754004617202617002024006661080266e25208092f40100153330b70133712610c026eacc2e404049200a132323253330ba013370e90030010991919299985e8099b87480080084cdc79bae30bf0100102a14a06180020046180020026ea8c8c2f404c2f804004c2f00404454ccc2e804cdc3a401000426610e026122020486610e0266e1cccc26004dd5985e0080981300fa400466e1cccc26004dd5985e0080a81300fa40042a6661740266e1d200a00213308701330840130bc01011307001c32323253330bd013370e90000010a51132323253330c0013370e90000010a5114a06186020046186020026ea8c2fc04048c30004008c30004004dd519185e80985f00800985e008088a99985d0099b87480300084cc21c04cc21004c2f004044c1c006cc8c8c94ccc2f404cdc3a400000429444c8c8c94ccc30004cdc3a400000429445281861808011861808009baa30bf0101230c00100230c001001375464617a02617c020026178020222944c2f404008c2f404004dd50010b099191199980080280180100c9999999983781101081000f80f00e9bab30ba010130023333333306e02102001f01e01d01c375661720202066e0000408858dd6985c00807185b80802191919299985b8099b87480000084cdd2a4000136022646464a6661740266e1d200000213374a90021984f80a60103d879800009e01153330ba013370e9001001099ba548008cc27c04dd4991b94376600261780202013c02266e9520043309f0130bc0101009e0130bd0100230bd0100137546172020186174020046174020026ea8004c8c2d804c2dc04004c2d404028c2cc04004c2c804004c2c404004c2c004004c2bc04004c2b804004c2b404004c2b004004c2ac04c2ac04004c2a804004c2ac04018c2a004004c2a404018c29804004c29c04004dd7184a8081b9bae30950103601101a019018017375c612a02074008020018612a02038612a0203660ac60cc61280261260200261240200261260266660d40086eb8c248040dc1c4dd6184900817199999983b8440080100080b00a80a009984500984700801184600800984680984600984580800984600998209bad308b01032026308b0132308b01308a01308c0100133040375a61140206004a60fe610e02610002610c02046610c02610a020186eb8c21404078dd69842008099bad308301013375a6104020266eb4c2040404cdd69840008099bad307f013375c60fc0266eb8c1f404cdd7183e0099bae307b0133079001307800130770013076001307500130740013073001307200130710013070001306f001306e001306d001306c001306b001306a0013069001306800130670013068013333063222330012233005337000089001000880124000002660c444a666090002244a00226604a60ce002600460cc0026eb0c19400cc18c004c188004c18cc188004c18802cc17c004c178004c174004c170004c16c004c170020c16400454ccc160cdc3a4008004264646464646464646603c6603e0026eb0c188010888c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8cc158c150cc1ac8cc1c0dd718460081398388009bac308b0102f330563370e60b4660d64660e005660e20026eb0c22c040bd2002330563304a308b0102c029330563304b3756611602056050660ac66ebcc22c04098c22c04064cc158cdd798458081298458080c1982b19baf308b01024308b01017330563375e61160204661160202c660ac66ebcc22c04088c22c04054cc158cdd79845808101845808071982b19baf308b0101f308b0100d330563375e61160203c611602018660ac66ebcc22c04074c22c0402ccc158cdd798458080e1845808051982b19baf308b0101b308b01009330563375e611602034611602010660ac66ebcc22c04c21804c22804068c22c04c22804018cc158cdd79845808109845808079982b19b8848000014cc158cdc480282e1982b19b8848000010cc158cdc424000006660ac66e252000002330563371290000009982b19b883370066e00cdc00020018010008029982b1998261bae308b01031375c6116026114020626eb0c22c040bccc158c8c8c94ccc23004cdc3a40000042646094646464a6661200266e1d200200214a02646464a6661260266e1d200000214a02944c25804008c25804004dd51849008009849808011849808009baa308f01308e01309001001308e0100114a2611e02004611e020026ea8c22c0401cc8c8c94ccc23004cdc3a40000042646094646464a6661200266e1d200200214a02646464a6661260266e1d200000214a02944c25804008c25804004dd51849008009849808011849808009baa308f01308e01309001001308e0100114a2611e02004611e020026ea8c22c04018dd69845008081bad308901010375a6110020206eb4c21c04040dd6984300805984200800984180800984100800984080800984000800983f800983f000983e800983e000983d800983d000983c800983c000983b800983b000983a800983a000983980098390009839981a182200718380009837800983700098368009836000983580098350009818983480098340009833800983300098328009833008982e9830801182f800983019812182f982f0021bac305f003305d001305c001305d305c001305c0053021305b007153330583370e9003001099191919191919191980f1980f8009bac30620042223232323232323232323232323232323232323232323232323232323232323232323232323232323305730553306c233071375c611a0205060e40026eb0c230040c0cc15ccdc3982d9983611983881618390009bac308c0103048008cc15ccc12cc230040b40a8cc15ccc130dd59846008160149982b99baf308c01027308c01014330573375e61180204c611802026660ae66ebcc23004094c23004048cc15ccdd79846008121846008089982b99baf308c01023308c01010330573375e61180203a611802012660ae66ebcc23004070c23004020cc15ccdd798460080d9846008039982b99baf308c0101a308c01006330573375e61180203261180200a660ae66ebcc23004060c23004010cc15ccdd798460080b9846008019982b99baf308c01016308c01002330573375e61180261160202a611802611602002660ae66ebcc23004088c2300403ccc15ccdd79846008109846008071982b99baf308c01020308c0100d330573375e61180203e611802018660ae66ebcc23004078c2300402ccc15ccdd798460080a9846008009982b99b8948000dd69846008051982b99b89375a6118020140ba66609a6eb8c230040c8dd71846009845808191bac308c01030308a01001308901001308801001308701001308601001308501001308401001308301001308201001308101001308001001307f001307e001307d001307c001307b001307a001307900130780013079303a304a0143076001307500130740013073001307200130710013070001306f001306e306e001306d001306c001306b001306a001306900130680013067001306600130650013066011305d3061002305f001306033024305f305e004375860be00660ba00260b800260ba60b800260b800a6460b460b4604400260b600e2646464646464646464600244646464646464646464646464646464646464664644660020060049001240044464646464646002444444444444444646464a66611c0266e2120000011323305c330593091010333232325333092013370e90010010984a008008b184a80801184a808009baa3091013090010373305c3371066e00dd6984880810a41014191021205c660b860b402e660b866ebcc244040c8c24404058cc170cdd78020019982e19b8733306d37566122020626eb8c24404c248040f81c1200233333333305b375c61220261240207c0e00306eb8c244040acdd71848808151bae309101029375c6122020506eacc24404054ccc184050894004008c1a4cc1c08cc1d40c0c1d8004dd618480081a8b19b8133306b3756611e020266eb8c23c04c240040f005803cc180c23804c23404048c17cc23404c230040b4ccccccccccccccccccc140dd6983e8061bad307d00b375a60fa0146eb4c1f4024dd6983e8041bad307d007375c60fa02e6eb8c1f4058dd7183e80a9bae307d014375a60fa0266eb4c1f4048dd6983e8089bad307d010375a60fa01e6eb8c1f4c1f80a8010dd5983e80e983e983c183e003983d800983e199982980d1bae307b307c02805a375860f603c660b44660be6eb8c1ec058c180004dd6183d00f999999982f8380010009bae3079013375c60f20246eb8c1e4044dd7183c808183a800983a0009839800983900098388009838000983780098370009836800983600098358009835000983480098340009833800983300098328009833008982d1831182d9830802982e9830801182f8009830182f982f000982f99982d911299982f99b89480280084cc00ccdc08012401460c060c060c060c060c00022a6660be66e1c0092000130610011332253330613371000490000b0999830111299983219b87002480004c1980044cc00ccdc08012400460ca00200400266e0400920023060001375a60bc00a6eb0c17800cc170004c16c004c170c16c004c16c010c16c008c16c004dd50012601bed8799f581c6fdc63a1d71dc2c65502b79baae7fb543185702b12c3c5fb639ed7374158581c1ad3767073087df4fc97fba7ac4a71a0a6cd556f1ad96a7b1c9870c4581c31070a43f38dc8edd236d1ec7562fb1b54b55d710f3d385a68ffdc46581c0ceaab7c2cec8f93c38b7c3598fa239f50bc14fcc6da9f016adb035c581c22855ac1e693ba59143f5aa75b015d2d0bb823a497060db2547db60c4156581cb35c358bb2bcdeef5ea052bcaaae2492d3797b22f76a5fd9a920aa584159ff0022222222323232323232322333333001004003002007006005337020100026601c66e0802001ccdc099b813370266e04cdc000400300280200180119b813370266e04cdc000380280180100099b833370400801000e66e0ccdc100180400319b833370400401000a6601266e0800402001088cdc019b83002001533305133710900019b86002001148008520002223003222222222222222323232233001002003323232233333333333333300100400300201201101000f00e00d00c00b00a0090080073370202200666e0004404ccdc00088099980a19b833370402400202066e0ccdc100880080799b8103300d22533304f33712004002200420026096444a66609e66e1c009200010011330012233005337020089001000891280091919bb0304f001304f304e001305037540024646464a66609c66e1d2000002130500011630510023051001375400244444444646464466600200800600466606200800c00a66606000600e00c666604e00401000e002608e444a66609666e25200a002133003337020049005180398039803980398038008a99982599b87002480004c0180044cc010cdc080124004600e00244a66609266e20009200016133003002001304522253330493370e00490000980200089980199b8100248008c0140048cc00488008588cc00488004588ccc110005282512330012233001226100216223330132232325333047323232533304a3370e90000010a5013371e6eb8c130004024c134008c134004dd5191825182580098248010998039919191199800802001801182598250019bab304a0023049002004100430470013048001225001001222233002004003223375e6086608460880046086608460880024466ebcdd30011ba60012223303f22533302500114a02a66608466e212000333020323756608a6088608c00264608a6088608c002608800200a00829444c008c10c0040048c8c8c8c80154ccc100cdc3a400000426464a66608466e1cdc6800a4070264646464a66608ca6601666e1c005200013370e002901c0991919299982499b89371a00290200991919192999826a9980919b87001480004cdc3800a40702646464a6660a066e24dc6800a40802646464646464646464646464646464646464646464a6660ca66e252000001132323232323232323232323232323232323232323232323232323232323232323232323232324994ccc1b400452616308a010065333088013370e900000109924ca6660d60022930b0b1845808011845808009baa0013086010013084010065333082013370e900000109919191919191924ca6660d60022930b18440080329998430099b87480000084c8c8c8c8c8c8c8c8c8c8c8c8c9265333075001149858c248040194ccc24004cdc3a400000426464646464646493299983c8008a4c2c612c0200ca6661280266e1d20000021323232323232324994ccc1f400452616309a010065333098013370e900000109919299984d0099b87371a002901c0991924ca6660fc0022930b184d808018b1bae001309a0100115333098013370e900100109919299984d0099b87371a002901c0991924ca6660fc0022930b184d808018b1bae001309a0100116309b01002309b010013754002612c020022a6661280266e1d20020021323232323232323232324994ccc2000400452616309d01003375a0026138020026134020066eb4004c26404004c25c0400cdd6800984b008008b184b80801184b808009baa00130920100115333090013370e900100109924ca6660e60022930b0b1849808011849808009baa001308e01001308c01006533308a013370e90000010991929998460099b87371a002901c0991924ca6660e00022930b1846808018b1bae001308c010011533308a013370e90010010991929998460099b87371a002901c0991924ca6660e00022930b1846808018b1bae001308c0100116308d01002308d0100137540026110020022c6112020046112020026ea8004c2100400454ccc20804cdc3a40040042649329998328008a4c2c2c610a02004610a020026ea8004c20004004c1f80194ccc1f0cdc3a40000042646464646464649329998328008a4c2c61040200ca6661000266e1d20000021323232323232323232323232324994ccc1bc00452616308c01006533308a013370e900000109919191919191924ca6660e60022930b18480080329998470099b87480000084c8c8c8c8c8c8c9265333077001149858c250040194ccc24804cdc3a400000426464a6661280266e1cdc6800a40702646493299983c0008a4c2c612a020062c6eb8004c2500400454ccc24804cdc3a400400426464a6661280266e1cdc6800a40702646493299983c0008a4c2c612a020062c6eb8004c2500400458c25404008c25404004dd50009848008008a9998470099b87480080084c8c8c8c8c8c8c8c8c8c926533307a001149858c25c0400cdd6800984b00800984a008019bad001309301001309101003375a0026120020022c6122020046122020026ea8004c2300400454ccc22804cdc3a40040042649329998368008a4c2c2c611a02004611a020026ea8004c22004004c218040194ccc21004cdc3a400000426464a66610c0266e1cdc6800a4070264649329998350008a4c2c610e020062c6eb8004c2180400454ccc21004cdc3a400400426464a66610c0266e1cdc6800a4070264649329998350008a4c2c610e020062c6eb8004c2180400458c21c04008c21c04004dd50009841008008b1841808011841808009baa001307e0011533307c3370e900100109924ca6660be0022930b0b183f801183f8009baa001307a0013078003375a00260ee00260ea0066eb4004c1d0004c1c800cdd6800983880098378019bad001306e001306c003375a00260d600260d20066eb4004c1a0004c19800c58dd6800983280098318019bad00130620013060003375a00260be00260ba0066eb4004c170004c16800cdd6800982c800982b8019bad00130560013054003375a00260a600260a20062c6eb8004c140004c13801058dc68009bae001304c001304a00316375c0026092002608e0082c6e34004dd7000982280098218018b1bae001304200116304300230430013754002466078002004294488c8c8c94ccc0f8cdc3a400400426600c00860800022c608200460820026ea800888cc0e4894ccc07c004584c94ccc0f4cc01c010c0fc0044c0fcc0f80044c00cc0f8008c0fcc0f80040088c0e4c0cc00488cdd79ba7303b002374e6076002ae8c888888888c004888888888888888cc0654ccc130c08c0544cdc399981500880a80a19b803370066e00cdc000780600500401289980c99b8733302a0110150143370066e00cdc000780600500419b8902533302a011024024330193370e66605402202602466e00cdc019b8000e00b009007330193370e66605402203002c01a6603266e1cccc0a804406005d20023370e6603402a01a60360224466606a00400294088cdc0a99981a18058010a401029005299981a19b8700148000520021480008ccc00888cdc000118029bab3038001480000048c0c08894ccc05c00440084cc00ccc010008c0d8004c0d4004cc0b88894ccc05400440084cc00ccdc00012400460660029000111180191111111111111119191919119999999999999980080200180100880800780700680600580500480400380319b800100133370202066e0ccdc100900800119b810103370666e08044040004cdc0809806a41fdfffffffffffffffe024646464a66606066e1d20040021303200116303300230330013754002444444444444444444464646464466666666666666600200a00800603002e02c02a02802601c01a0180160140044a00266603c00800c00a66e04cdc099b8133301d00300c00b01301100f3370266e04cdc0999980a00100680600b809808807911119299981799b894800000454ccc0bcc01801040044ccc03401401000c58cdc0999806002003003000919b8f001002488100482026fb8088888c014cc03094cc01ccc0440140044cdc39998049bab302d302c302e0010040034800800494ccc024004584c94ccc0280044c0a400858c09c00488ccc09800800400c5281111999802001240004666600a00490003ad3756002006460046ea40048888cc08c894ccc024004401454ccc098cdd7981598140008030980218151814000898011813800800a45014c002301e2253330040011005132533302230040011330070013003302300213003302300230230015573e97ae0574044646464a66603e66e1d200000214a0266e3cdd71810800802981100118110009baa32301f3020001301e301f0012301c301b301d0012222222372866e28dca00399b8a37286e60c020018cdc51b943730601000a66e28dca19b8a004003372866e28008004c05888cdcb299980c99b8800148000400c4010c8c94ccc06ccdc3801240002600e00226466e58004c020008c010008cdc298088012402866e10c04000520144901012d00490100253330153370e002900008010a99980a99b8700148008400c54ccc054cdc3800a400820082a66602a66e1c00520061005153330153370e002900408030a99980a99b8700148028401c54ccc054cdc3800a401820102a66602a66e1c005200e1009153330153370e002900808050a99980a99b8700148048402c592410130004901013100490101320049010133004901013400490101350049010136004901013700490101380049010139002533300a337120029000898010008800919b81480000052201013000232323232533300b3370e90010010991919299980719b87480080084c8c88cc00400c008dd698088011bad30100041630110023011001375464601c601e002601a60180082c601c004601c0026ea8c8c02cc030004c028004c0280048c01cc0080048c018c0080048c014c0140048c8c8c94ccc018cdc3a400400426eb8c02000458c024008c024004dd518029803000919180111980100100091801119801001000ab9a5744ae848c008dd5000aab9e5573a1", + "poolCpRefTxId": "babc647257b8d78b86e862ba9769401714ed403e7c46ed1b59c3fc32e0247c82#0" +} diff --git a/packages/wingriders-v2/src/scripts/mainnet-script.json b/packages/wingriders-v2/src/scripts/mainnet-script.json new file mode 100644 index 0000000..206008e --- /dev/null +++ b/packages/wingriders-v2/src/scripts/mainnet-script.json @@ -0,0 +1,10 @@ +{ + "factoryScript": "5914590100003232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323222233322232533303c001149858c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c00488cc8c8c94ccc150cdc3a40040042646464644666600200a0080060046644444444444464646464646464646464646464646464646464646464603c44660c4660c466e3cdd7183f00c0109983119b8f375c60fc02e03a660c466e3cdd7183f00b00e1983119b8f375c60fc02a036660c466e3cdd7183f00a00d1983119b87375a60fc01c09a660c466e1cdd6983f009a4014660c466e1cdd6983f0078261983119b87375a60fc02490011983119b87375a60fc02290001983119b87375a60fc02090001983119b87375a60fc01890001983119b87375a60fc01690001983119b87375a60fc01490001983119b87375a60fc01290001983119b87375a60fc01090001983119b87375a60fc00e90001983119824183f00319ba5480081b4cc188cc120c1f8014cdd2a40040da660c466e1cdd6983f0068011983119b87375a60fc00604866e1cdd6983f183e8018119983119b87337020ce03266e0cdd6983f00219b823370490020120119983129983c9999824a412c026eb4c1f8010cdc100f81219b8201e023133330494825804cdc01bad307e00448008cdc100f81219b8201e023330623371090000121983119b884800008d4cc1e4cdc381224004266e1c08d2002307a0013074307a3079001307800130770013076001307500130740013073001307200130710013070001306f001306e001306d001306c001306b001306a001306900130680013067001306600130603232323200e53330663370e900000109919299983419b87371a002901c099191919299983629983519b87001480004cdc3800a40702646464a6660de66e24dc6800a4080264646464a6660e6a660e266e1c005200013370e002901c0991919299983b19b89371a0029020099191919191919191919191919191919191919191929998458099b89480000044c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c92653330b101001149858c2c4040194ccc2b804cdc3a400000426464646464646464646493299985c008008a4c2c6170020066eb4004c2dc04004c2d40400cdd6800985a008009859008019bad00130b1010011630b10100230b1010013754002615a0200261560200ca6661500266e1d20000021323232323232324994ccc2bc040045261630af0100653330ac013370e900000109919191919191919191919191924ca666172020022930b185c80803299985b0099b87480000084c8c8c8c8c8c8c92653330bd01001149858c2f4040194ccc2e804cdc3a4000004264646464646464932999860808008a4c2c61820200ca66617c0266e1d20000021323253330c0013370e6e340052038132324994ccc308040045261630c20100316375c0026182020022a66617c0266e1d20020021323253330c0013370e6e340052038132324994ccc308040045261630c20100316375c0026182020022c6182020046182020026ea8004c2f40400454ccc2e804cdc3a4004004264646464646464646464932999862008008a4c2c6188020066eb4004c30c04004c3040400cdd6800986000800985f008019bad00130bd010011630bd0100230bd0100137540026172020022a66616c0266e1d20020021324994ccc2dc04004526161630b90100230b9010013754002616a0200261660200ca6661600266e1d20000021323253330b2013370e6e340052038132324994ccc2d0040045261630b40100316375c0026166020022a6661600266e1d20020021323253330b2013370e6e340052038132324994ccc2d0040045261630b40100316375c0026166020022c6166020046166020026ea8004c2bc0400458c2bc04008c2bc04004dd50009855808008a9998540099b87480080084c92653330a90100114985858c2ac04008c2ac04004dd500098538080098528080329998510099b87480000084c8c8c8c8c8c8c92653330a901001149858c2a4040194ccc29804cdc3a4000004264646464646464646464646464932999859808008a4c2c61660200ca6661600266e1d20000021323232323232324994ccc2dc040045261630b70100653330b4013370e900000109919191919191924ca666176020022930b185d80803299985c0099b87480000084c8c94ccc2e804cdc39b8d001480e04c8c92653330bc01001149858c2f00400c58dd7000985d808008a99985c0099b87480080084c8c94ccc2e804cdc39b8d001480e04c8c92653330bc01001149858c2f00400c58dd7000985d808008b185d80801185d808009baa00130b701001153330b4013370e900100109919191919191919191924ca66617c020022930b185f008019bad00130bd0100130bb01003375a0026174020026170020066eb4004c2dc0400458c2dc04008c2dc04004dd50009859808008a9998580099b87480080084c92653330b10100114985858c2cc04008c2cc04004dd500098578080098568080329998550099b87480000084c8c94ccc2b004cdc39b8d001480e04c8c92653330ae01001149858c2b80400c58dd70009856808008a9998550099b87480080084c8c94ccc2b004cdc39b8d001480e04c8c92653330ae01001149858c2b80400c58dd70009856808008b1856808011856808009baa00130a9010011630a90100230a9010013754002614a020022a6661440266e1d20020021324994ccc28c04004526161630a50100230a5010013754002614202002613e020066eb4004c27804004c2700400cdd6800984d80800984c808019bad001309801001309601003375a002612a020026126020066eb4004c24804004c2400400cdd68009847808009846808018b1bad001308c01001308a01003375a002611202002610e020066eb4004c21804004c2100400cdd68009841808009840808019bad001308001001307e003375a00260fa00260f60066eb4004c1e8004c1e000c58dd7000983b800983a8020b1b8d001375c00260e600260e20062c6eb8004c1c0004c1b801058dc68009bae001306c001306a00316375c00260d20022c60d200460d20026ea8028dd6982d0021bad305a3059004375c60b20266eb8c16004cccccccc09d221013100375a60ae0026eb4c15cc158004dd7182b80c1bae3057017375c60ae02c6eb8c15cc1580584c8c8c8c88cccc00401401000c0088888888888c02088c8c8c8cc134cdd799ba548000cc164dd48071982c9ba900a330593752012660b26ea4020cc164dd48039982c9ba8480f0cc164dd424014660b26ea1200033059375090001982c9ba8037330593750070660b26ea0014cc164dd424000660b26ea1200033059375090001982c9ba848000cc164dd424000660b26ea12000330593374a900102c1982c99ba548008160cc164cdd2a40000b00b001e6609a66e24cdc100180180099b880013370400400466e0802c028cdc0000a400466e0413c00cdd7182c80a9bae305801533333330274881013000480092002375c60ae0306eb8c15c05cdd7182b80b1bae3057305601630570023057001375460a802c44446464646464646464646608a66e20cdc0807007a4101e164a46608a66e1cccc154054dd71830983000d827240046608a66e1cccc154054dd71830983000d80699b8104a042330453370e6090646600240022c66466444660bc44a6660ca002244a0022a6660c866ebcc1a4c19c0040104c014c19c0044c008c1980040048c888c00800cdd598320009ba9001375c60c260c003602a90031982299999999998050030058080020019bae3061022375c60c20426eb8c184080dd718309830010001198229982299b8848000010cc114cdc424000006a660b866e2520000051304b375c60c20446608a66e212000337020020846608a6608aa6660bc66e3cdd718308111bae30610201337206eb8c184084dd718309830010099b90375c60c20446eb8c184080cc114c10d4cc148cdc79bae3061022375c60c260c0036266e3cdd71830810806998229821a9982919b8f375c60c20406eb8c184c18006c4cdc79bae3061306002000d3304530435330523371e6eb8c184088dd71830983000d899b8f375c60c204209c6086a660a466e3cdd718308101bae3061306001b13371e6eb8c184c180080138cc114cdc39823003999119b815333060304d0021480205200a53330603370e00290000a4004290001bae3061022002330453370e609002490011982299b87333055015375c60c260c00360b29001198229982299b90375c60c203e01a66e40034dd71830983000fa99982f8088a50153305232533306000114a02a660a66ae8cc1840044cc114cc128038dd71831183081018310009830008899822198249bae306101f00d3061011337020920026660a600a6eb8c17cc17806402cccc148010dd7182f00e9bae305e305d01d533305a3047375c60ba03c200226660a20066eb8c174078dd7182e80e99b81333050002047047482026fb808c11cc16cc168008dd5982d000982c182980099299982b8008b099299982c0008982d0010b182c00099823129982519826002000899b8733304d3050001375c60b260b002608c90011bac305800f323232323253330533370e90010010991919299982b19b87480080084c8c88cc00400c008dd6982d0011bad3059004163059002305900137546460ae60a200260ac60aa0082c60ac00460ac0026ea8c8c150c138004c14c004c130004c144018cc0f894cc108cc11000c0044cdc399982298240009bae3051305000b04948008dd618280039981e91929982119822001800899b873330453048001375c60a260a001609290011828182798250009bac304f00732323232533304f3370e900100109bae305200116305200230520013754609e6092002609c60900026644646464a66609e66e1d20020021323304c225333053001161325333053323375e6e9cc144014dd39828800982b0008982b182a80089801982a8011827982a80080298290008b182900118290009baa002304d304c0063758609a00a6eacc130008c8c12cc12c004c128004c124c124004c120c120004c108c120004c104028c114004c110004c10c004c0f4030c0f001cc100004c0fc004c0f8004c0e0008c8c8c8c801d4ccc0eccdc3a40000042646464646464649329998210008a4c2c60840066eb8004c104004c0fc00cdd7000981f0008b181f001181f0009baa00332323232006533303b3370e90000010991919191919191919299982229982119b87001480004cdc3800a40702646464a66608e66e24dc6800a4080264646464a666096a6609266e1c005200013370e002901c0991919299982719b89371a00290200991924ca6660a00022930b18280018b1bae001304f001304d00416371a0026eb8004c12c004c12400c58dd7000982400098230020b1b8d001375c0026088002608400ca66607e66e1d20000021324994ccc100004526161533303f3370e900100109919191919191924ca66608c0022930b18230019bad00130450013043003375a00260840022c608400460840026ea8004c0f800458c0f8008c0f8004dd5001000a6019ad8799f581caf97793b8702f381976cec83e303e9ce17781458c73c4bb16fe02b83581cc134d839a64a5dfb9b155869ef3f34280751a622f69958baa8ffd29c581c946ae228430f2fc64aa8b3acb910ee27e9b3e47aa8f925fac27834a1581c23680ea6701b56f2c12ae79d8af94fd36f509b7b007029c7ce114840581c6fdc63a1d71dc2c65502b79baae7fb543185702b12c3c5fb639ed737ff002232323253330383370e90010010991919299981d99b8748000008528099baf374e0086e9c004c0f8008c0f8004dd50020991919299981d99b8748008008528099baf374e0086e9c004c0f8008c0f8004dd5002181d801181d8009baa0022222323232330203371266e04ccc02001c01800ccccc02401c01800c009200033710900019b8133300800700100333330090070010030023370000a900119b800030023370400400244466e00cdc119b8233704006901000100099b8233704004004004444466e08cdc12401000466e00cdc119b820044802000400d20a09c01482024bd0048888888dca19b8a372800e66e28dca1b983008006337146e50dcc180400299b8a372866e2801000cdca19b8a0020013028223372ca66605c66e20005200010031004323253330303370e00490000980380089919b96001300800230040023370a6022004900a19b843010001480512401012d004901002533302a3370e002900008010a99981519b8700148008400c54ccc0a8cdc3800a400820082a66605466e1c005200610051533302a3370e002900408030a99981519b8700148028401c54ccc0a8cdc3800a401820102a66605466e1c005200e10091533302a3370e002900808050a99981519b8700148048402c592410130004901013100490101320049010133004901013400490101350049010136004901013700490101380049010139002533301f337120029000898010008800919b814800000520d00f233301c0014a094488c8cc010cdd798061810180f80080199b873005375660400029002180f180c8009119980d801000a5023330022233700004600a6eacc07c005200000123014222533301c001100213300333004002301e001301d00133012222533301a0011002133003337000049001180d800a40004466e9520003300a3752004660146ea400402520feffffffffffffffff0123371e002004910100232323253330153370e90020010980c0008b180c001180c0009baa001489014c002300c2253330130011004132533301330040011330060013003301500213003301500230150014bd702ba02233300e00200100314a044646464a66601e66e1d200000214a0266e3cdd71809000802980900118090009baa323010300a001300f30090012223333004002480008cccc014009200075a6eac00400c8c008dd480091111980391299980700088028a99980699baf30123010001006130043011301000113002300f0010012375660146012600800291101460023230022330020020012300223300200200123008375400246600600200429455cd2ab9f5744ae8555cf2ab9d1", + "factoryScriptHash": "c57b588ebda735d49008d47afde48198ec37062d6ceb758803515013", + "dexSymbolScript": "59024b010000323232323232323232323232323222323232533300e001149858c8c8c94ccc040cdc3a400400426664446601c44a66602c002294054ccc054cdc424000666444666602200490001199980900124000eb4dd5800801991bab30193015301a0013230193015301a001301800100500414a2260046028002002646464a66602666e1d20000021375c602c0022c602e00460260026ea8c04cc03c014038dd6180980209991199809001000a503322323300124a22940cc8c038894ccc05800448940044ccc00cc060004888c00800c4c008c05000494ccc04ccdd7801980b180b800891180100189128008009809980a0041bac301300433332222333300d0034a0464a66602e002294054cc048d5d1980a80089929980999b8f375c603200200a266e1cdd6980e000803980c8009bab00100148008c8c8c94ccc04ccdc3a400000426eb8c05800458c05c008c04c004dd5180998078028071bab301332301030103010001300f00430140023010001375464646464010a66602066e1d20000021324994ccc04400452616153330103370e900100109924ca6660220022930b0b180a00118080009baa0043010300f001300f0014c012fd8799fd8799fd8799f5820914a0f1e615f41b3f462855f6052a6b37d1d291aab53918ad4c9d5f4245204ceff01ffff0023002375200244446600a44a66601a002200a2a66601866ebcc038c03c0040184c010c044c03c0044c008c02c0040048c8c0088cc0080080048c0088cc00800800488ccc01800800400c5282450146005744ae6955cfaab9d5742460046ea800555cf1", + "dexSymbolScriptHash": "6fdc63a1d71dc2c65502b79baae7fb543185702b12c3c5fb639ed737", + "poolScriptHashCP": "af97793b8702f381976cec83e303e9ce17781458c73c4bb16fe02b83", + "poolScriptHashSS": "946ae228430f2fc64aa8b3acb910ee27e9b3e47aa8f925fac27834a1", + "requestValidatorHashCP": "c134d839a64a5dfb9b155869ef3f34280751a622f69958baa8ffd29c", + "requestValidatorHashSS": "23680ea6701b56f2c12ae79d8af94fd36f509b7b007029c7ce114840" +} diff --git a/packages/wingriders-v2/src/scripts/preprod-references.json b/packages/wingriders-v2/src/scripts/preprod-references.json new file mode 100644 index 0000000..4b5ae47 --- /dev/null +++ b/packages/wingriders-v2/src/scripts/preprod-references.json @@ -0,0 +1,10 @@ +{ + "dexSymbolRefInput": "82825820d8f4eceb883f6dde3dc359579570fc0049fd688b1e853ee2e7142bbca6f546ad00a3005839007290b6e451c55ec417ccf332119a8b735cb44f3dc7dac405b9101cf2bf5b5bced089f4c15e6d937a2bb441ee6988fdef1eedac40f9caded5011a0036742803d818590253820259024e59024b010000323232323232323232323232323222323232533300e001149858c8c8c94ccc040cdc3a400400426664446601c44a66602c002294054ccc054cdc424000666444666602200490001199980900124000eb4dd5800801991bab30193015301a0013230193015301a001301800100500414a2260046028002002646464a66602666e1d20000021375c602c0022c602e00460260026ea8c04cc03c014038dd6180980209991199809001000a503322323300124a22940cc8c038894ccc05800448940044ccc00cc060004888c00800c4c008c05000494ccc04ccdd7801980b180b800891180100189128008009809980a0041bac301300433332222333300d0034a0464a66602e002294054cc048d5d1980a80089929980999b8f375c603200200a266e1cdd6980e000803980c8009bab00100148008c8c8c94ccc04ccdc3a400000426eb8c05800458c05c008c04c004dd5180998078028071bab301332301030103010001300f00430140023010001375464646464010a66602066e1d20000021324994ccc04400452616153330103370e900100109924ca6660220022930b0b180a00118080009baa0043010300f001300f0014c012fd8799fd8799fd8799f5820bb29f1d7fedc667fbfa6c74b40fce5b3ab512d217ace7afcc9cf5b3f023ccba9ff01ffff0023002375200244446600a44a66601a002200a2a66601866ebcc038c03c0040184c010c044c03c0044c008c02c0040048c8c0088cc0080080048c0088cc00800800488ccc01800800400c5282450146005744ae6955cfaab9d5742460046ea800555cf1", + "dexSymbolTxId": "d8f4eceb883f6dde3dc359579570fc0049fd688b1e853ee2e7142bbca6f546ad#0", + "factoryRefInput": "8282582069f5c487838b4b572df15b7ab7cf7ba3ddae284b1cb98c36c9d7bbcbcee5fc2400a3005839007290b6e451c55ec417ccf332119a8b735cb44f3dc7dac405b9101cf2bf5b5bced089f4c15e6d937a2bb441ee6988fdef1eedac40f9caded5011a01666bdc03d818591461820259145c5914590100003232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323222233322232533303c001149858c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c00488cc8c8c94ccc150cdc3a40040042646464644666600200a0080060046644444444444464646464646464646464646464646464646464646464603c44660c4660c466e3cdd7183f00c0109983119b8f375c60fc02e03a660c466e3cdd7183f00b00e1983119b8f375c60fc02a036660c466e3cdd7183f00a00d1983119b87375a60fc01c09a660c466e1cdd6983f009a4014660c466e1cdd6983f0078261983119b87375a60fc02490011983119b87375a60fc02290001983119b87375a60fc02090001983119b87375a60fc01890001983119b87375a60fc01690001983119b87375a60fc01490001983119b87375a60fc01290001983119b87375a60fc01090001983119b87375a60fc00e90001983119824183f00319ba5480081b4cc188cc120c1f8014cdd2a40040da660c466e1cdd6983f0068011983119b87375a60fc00604866e1cdd6983f183e8018119983119b87337020ce03266e0cdd6983f00219b823370490020120119983129983c9999824a412c026eb4c1f8010cdc100f81219b8201e023133330494825804cdc01bad307e00448008cdc100f81219b8201e023330623371090000121983119b884800008d4cc1e4cdc381224004266e1c08d2002307a0013074307a3079001307800130770013076001307500130740013073001307200130710013070001306f001306e001306d001306c001306b001306a001306900130680013067001306600130603232323200e53330663370e900000109919299983419b87371a002901c099191919299983629983519b87001480004cdc3800a40702646464a6660de66e24dc6800a4080264646464a6660e6a660e266e1c005200013370e002901c0991919299983b19b89371a0029020099191919191919191919191919191919191919191929998458099b89480000044c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c92653330b101001149858c2c4040194ccc2b804cdc3a400000426464646464646464646493299985c008008a4c2c6170020066eb4004c2dc04004c2d40400cdd6800985a008009859008019bad00130b1010011630b10100230b1010013754002615a0200261560200ca6661500266e1d20000021323232323232324994ccc2bc040045261630af0100653330ac013370e900000109919191919191919191919191924ca666172020022930b185c80803299985b0099b87480000084c8c8c8c8c8c8c92653330bd01001149858c2f4040194ccc2e804cdc3a4000004264646464646464932999860808008a4c2c61820200ca66617c0266e1d20000021323253330c0013370e6e340052038132324994ccc308040045261630c20100316375c0026182020022a66617c0266e1d20020021323253330c0013370e6e340052038132324994ccc308040045261630c20100316375c0026182020022c6182020046182020026ea8004c2f40400454ccc2e804cdc3a4004004264646464646464646464932999862008008a4c2c6188020066eb4004c30c04004c3040400cdd6800986000800985f008019bad00130bd010011630bd0100230bd0100137540026172020022a66616c0266e1d20020021324994ccc2dc04004526161630b90100230b9010013754002616a0200261660200ca6661600266e1d20000021323253330b2013370e6e340052038132324994ccc2d0040045261630b40100316375c0026166020022a6661600266e1d20020021323253330b2013370e6e340052038132324994ccc2d0040045261630b40100316375c0026166020022c6166020046166020026ea8004c2bc0400458c2bc04008c2bc04004dd50009855808008a9998540099b87480080084c92653330a90100114985858c2ac04008c2ac04004dd500098538080098528080329998510099b87480000084c8c8c8c8c8c8c92653330a901001149858c2a4040194ccc29804cdc3a4000004264646464646464646464646464932999859808008a4c2c61660200ca6661600266e1d20000021323232323232324994ccc2dc040045261630b70100653330b4013370e900000109919191919191924ca666176020022930b185d80803299985c0099b87480000084c8c94ccc2e804cdc39b8d001480e04c8c92653330bc01001149858c2f00400c58dd7000985d808008a99985c0099b87480080084c8c94ccc2e804cdc39b8d001480e04c8c92653330bc01001149858c2f00400c58dd7000985d808008b185d80801185d808009baa00130b701001153330b4013370e900100109919191919191919191924ca66617c020022930b185f008019bad00130bd0100130bb01003375a0026174020026170020066eb4004c2dc0400458c2dc04008c2dc04004dd50009859808008a9998580099b87480080084c92653330b10100114985858c2cc04008c2cc04004dd500098578080098568080329998550099b87480000084c8c94ccc2b004cdc39b8d001480e04c8c92653330ae01001149858c2b80400c58dd70009856808008a9998550099b87480080084c8c94ccc2b004cdc39b8d001480e04c8c92653330ae01001149858c2b80400c58dd70009856808008b1856808011856808009baa00130a9010011630a90100230a9010013754002614a020022a6661440266e1d20020021324994ccc28c04004526161630a50100230a5010013754002614202002613e020066eb4004c27804004c2700400cdd6800984d80800984c808019bad001309801001309601003375a002612a020026126020066eb4004c24804004c2400400cdd68009847808009846808018b1bad001308c01001308a01003375a002611202002610e020066eb4004c21804004c2100400cdd68009841808009840808019bad001308001001307e003375a00260fa00260f60066eb4004c1e8004c1e000c58dd7000983b800983a8020b1b8d001375c00260e600260e20062c6eb8004c1c0004c1b801058dc68009bae001306c001306a00316375c00260d20022c60d200460d20026ea8028dd6982d0021bad305a3059004375c60b20266eb8c16004cccccccc09d221013100375a60ae0026eb4c15cc158004dd7182b80c1bae3057017375c60ae02c6eb8c15cc1580584c8c8c8c88cccc00401401000c0088888888888c02088c8c8c8cc134cdd799ba548000cc164dd48071982c9ba900a330593752012660b26ea4020cc164dd48039982c9ba8480f0cc164dd424014660b26ea1200033059375090001982c9ba8037330593750070660b26ea0014cc164dd424000660b26ea1200033059375090001982c9ba848000cc164dd424000660b26ea12000330593374a900102c1982c99ba548008160cc164cdd2a40000b00b001e6609a66e24cdc100180180099b880013370400400466e0802c028cdc0000a400466e0413c00cdd7182c80a9bae305801533333330274881013000480092002375c60ae0306eb8c15c05cdd7182b80b1bae3057305601630570023057001375460a802c44446464646464646464646608a66e20cdc0807007a4101e164a46608a66e1cccc154054dd71830983000d827240046608a66e1cccc154054dd71830983000d80699b8104a042330453370e6090646600240022c66466444660bc44a6660ca002244a0022a6660c866ebcc1a4c19c0040104c014c19c0044c008c1980040048c888c00800cdd598320009ba9001375c60c260c003602a90031982299999999998050030058080020019bae3061022375c60c20426eb8c184080dd718309830010001198229982299b8848000010cc114cdc424000006a660b866e2520000051304b375c60c20446608a66e212000337020020846608a6608aa6660bc66e3cdd718308111bae30610201337206eb8c184084dd718309830010099b90375c60c20446eb8c184080cc114c10d4cc148cdc79bae3061022375c60c260c0036266e3cdd71830810806998229821a9982919b8f375c60c20406eb8c184c18006c4cdc79bae3061306002000d3304530435330523371e6eb8c184088dd71830983000d899b8f375c60c204209c6086a660a466e3cdd718308101bae3061306001b13371e6eb8c184c180080138cc114cdc39823003999119b815333060304d0021480205200a53330603370e00290000a4004290001bae3061022002330453370e609002490011982299b87333055015375c60c260c00360b29001198229982299b90375c60c203e01a66e40034dd71830983000fa99982f8088a50153305232533306000114a02a660a66ae8cc1840044cc114cc128038dd71831183081018310009830008899822198249bae306101f00d3061011337020920026660a600a6eb8c17cc17806402cccc148010dd7182f00e9bae305e305d01d533305a3047375c60ba03c200226660a20066eb8c174078dd7182e80e99b81333050002047047482026fb808c11cc16cc168008dd5982d000982c182980099299982b8008b099299982c0008982d0010b182c00099823129982519826002000899b8733304d3050001375c60b260b002608c90011bac305800f323232323253330533370e90010010991919299982b19b87480080084c8c88cc00400c008dd6982d0011bad3059004163059002305900137546460ae60a200260ac60aa0082c60ac00460ac0026ea8c8c150c138004c14c004c130004c144018cc0f894cc108cc11000c0044cdc399982298240009bae3051305000b04948008dd618280039981e91929982119822001800899b873330453048001375c60a260a001609290011828182798250009bac304f00732323232533304f3370e900100109bae305200116305200230520013754609e6092002609c60900026644646464a66609e66e1d20020021323304c225333053001161325333053323375e6e9cc144014dd39828800982b0008982b182a80089801982a8011827982a80080298290008b182900118290009baa002304d304c0063758609a00a6eacc130008c8c12cc12c004c128004c124c124004c120c120004c108c120004c104028c114004c110004c10c004c0f4030c0f001cc100004c0fc004c0f8004c0e0008c8c8c8c801d4ccc0eccdc3a40000042646464646464649329998210008a4c2c60840066eb8004c104004c0fc00cdd7000981f0008b181f001181f0009baa00332323232006533303b3370e90000010991919191919191919299982229982119b87001480004cdc3800a40702646464a66608e66e24dc6800a4080264646464a666096a6609266e1c005200013370e002901c0991919299982719b89371a00290200991924ca6660a00022930b18280018b1bae001304f001304d00416371a0026eb8004c12c004c12400c58dd7000982400098230020b1b8d001375c0026088002608400ca66607e66e1d20000021324994ccc100004526161533303f3370e900100109919191919191924ca66608c0022930b18230019bad00130450013043003375a00260840022c608400460840026ea8004c0f800458c0f8008c0f8004dd5001000a6019ad8799f581ce1909c771ecebeff57ad38eadbf28cb41cb4c820bc3396fa48865926581c7ad4d2213f545012f670718283380ef209b9cbaec32b70132ae78483581c946ae228430f2fc64aa8b3acb910ee27e9b3e47aa8f925fac27834a1581c23680ea6701b56f2c12ae79d8af94fd36f509b7b007029c7ce114840581c4fea4b43ea3e9f78a364599b93a26be90b942b02f1d6d2efa024038bff002232323253330383370e90010010991919299981d99b8748000008528099baf374e0086e9c004c0f8008c0f8004dd50020991919299981d99b8748008008528099baf374e0086e9c004c0f8008c0f8004dd5002181d801181d8009baa0022222323232330203371266e04ccc02001c01800ccccc02401c01800c009200033710900019b8133300800700100333330090070010030023370000a900119b800030023370400400244466e00cdc119b8233704006901000100099b8233704004004004444466e08cdc12401000466e00cdc119b820044802000400d20a09c01482024bd0048888888dca19b8a372800e66e28dca1b983008006337146e50dcc180400299b8a372866e2801000cdca19b8a0020013028223372ca66605c66e20005200010031004323253330303370e00490000980380089919b96001300800230040023370a6022004900a19b843010001480512401012d004901002533302a3370e002900008010a99981519b8700148008400c54ccc0a8cdc3800a400820082a66605466e1c005200610051533302a3370e002900408030a99981519b8700148028401c54ccc0a8cdc3800a401820102a66605466e1c005200e10091533302a3370e002900808050a99981519b8700148048402c592410130004901013100490101320049010133004901013400490101350049010136004901013700490101380049010139002533301f337120029000898010008800919b814800000520d00f233301c0014a094488c8cc010cdd798061810180f80080199b873005375660400029002180f180c8009119980d801000a5023330022233700004600a6eacc07c005200000123014222533301c001100213300333004002301e001301d00133012222533301a0011002133003337000049001180d800a40004466e9520003300a3752004660146ea400402520feffffffffffffffff0123371e002004910100232323253330153370e90020010980c0008b180c001180c0009baa001489014c002300c2253330130011004132533301330040011330060013003301500213003301500230150014bd702ba02233300e00200100314a044646464a66601e66e1d200000214a0266e3cdd71809000802980900118090009baa323010300a001300f30090012223333004002480008cccc014009200075a6eac00400c8c008dd480091111980391299980700088028a99980699baf30123010001006130043011301000113002300f0010012375660146012600800291101460023230022330020020012300223300200200123008375400246600600200429455cd2ab9f5744ae8555cf2ab9d1", + "factoryTxId": "69f5c487838b4b572df15b7ab7cf7ba3ddae284b1cb98c36c9d7bbcbcee5fc24#0", + "orderRefInput": "828258201a0e6cc009bf6de2c4f41b49c967db92780b3a74b19271e3353d3611617db3e200a3005839007290b6e451c55ec417ccf332119a8b735cb44f3dc7dac405b9101cf2bf5b5bced089f4c15e6d937a2bb441ee6988fdef1eedac40f9caded5011a002b138a03d8185901a682025901a159019e010000323232323232323232222325333008001149858c8c8c94ccc028cdc3a40040042664601444a666aae7c0045280a99980699baf301000100314a226004601c00264646464a66601c66e1d20000021301100116301100230110013754601c601a002601a6010601800c646eb0c038c8c034c034c034c034c034c034c028004c034004c034c0300104ccc888cdc79919191bae301300132323253330123370e90000010b0800980a801180a8009baa3012301100132301230110013011300f301000133300c222533301033712900500109980199b8100248028c044c044c044c044c04400454ccc040cdc3801240002602600226644a66602466e20009200016133301122253330153370e00490000980c00089980199b8100248008c058004008004cdc0801240046022002004646eb0c044c040004c040c03c00400cdd70039bad300d001004300d002300d00137540046ea52211ce1909c771ecebeff57ad38eadbf28cb41cb4c820bc3396fa48865926002300430040012323002233002002001230022330020020015734ae888c00cdd5000aba15573caae741", + "orderTxId": "1a0e6cc009bf6de2c4f41b49c967db92780b3a74b19271e3353d3611617db3e2#0", + "poolCpRefInput": "8282582065692bcc396173f4f67181ec8d8b60cc94422e81210d70c7a2c411c8ea1a6a9b00a3005839007290b6e451c55ec417ccf332119a8b735cb44f3dc7dac405b9101cf2bf5b5bced089f4c15e6d937a2bb441ee6988fdef1eedac40f9caded5011a03515c2203d81859318a8202593185593182010000323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232222325333056001149858c8c8c94ccc160cdc3a4000004264646464646464646464646464646464646464646464646464646464646464646464646464646464646464646464600244646464646466464466002006004900124004446464646464660c2660bc612c02016646464a66612e0266e1d2002002130990100116309a01002309a010013754612c02612a0206a660c266e1cccc1c8dd5984b008049bae30960103b07548008cc184cc184c144cdc39bad30960103c375a612c0207a660c266e1cccc1c8dd5984b00984a808061bae309601039375c612c020749001191919299984b8099b874800000852889919832982a99b8f00101530553371e0020186eb8c26404004c26804008c26804004dd519184b80984c00800984b00806198309982f001180091111111111111119ba548000cc22004dd481019844009ba902a3308801375205266110026ea40a0cc22004dd481399844009ba80263308801375004a66110026ea0090cc22004dd401199844009ba80223308801375004266110026ea0074cc22004dd400619844009ba800b3308801375001466110026ea0024cc22004dd400419844009ba8007330880130a50102c330880130a50102b33088013374a90000438084380998309999999998301bae30960103b07500501b01a0190183756612c02006002660c2660c266e1c0c0cdc02400860ca6eb0c25804c254040f0cdc424008060660c260a26466612802444a6660f600429404c94ccc26404cc2600488cc004894ccc27404cdc38010028a511300400114a000429444cc010c2680400ccc164004008dd6984e809826984d008010009128009bac30960130950103c3371066e040340392080f0b252323333309301222223300222533307e0031613333007006333006005002309d01003001309c01003100322230022232323001222232323253330a2013370e90000010980411111801911180291119191980d111111111111111299985e80808899999999983680300280200180100780700811111119119800801003191919119999999999999980080500480b80200a80180980100880800780700680600599b800030103370000602266e0000c0484ccccccccc1b401801401000c00803803c040888888c88cc004008018c8c8c88ccccccccccccccc00402402805c05801005000c04800804003c03803403002ccdc000180799b800030103370000602244a66616002660fa66e2120000033307d337109000000a9998580080209983e99b89001006337126eb4c2c804c2c40403c0184cc1f4cdc480080399b89375a61640261620201e00e20042ca66615a02002200e200c646464a66615e0266e1d200200214a02944c2c804008c2c804004dd51857008058a9998510099b87480100084c038888888888888888c05c8888c00c888c014888d4ccc2ec04cdc39bad30bd0101a480004c8c004888888888888888c8c88ccc00404c00c008cdc0814807a4000604e444444444444444646002444444444444444666446024444444444444444a6661d60202226666666661360200c00a00800600401e01c0204444446446600200400c646464466666666666666600201401202e00802a00602600402202001e01c01a01801666e0000c040cdc000180899b80003012133333333309b0100600500400300200e00f01022222232233001002006323232233333333333333300100900a01701600401400301200201000f00e00d00c00b3370000601e66e0000c040cdc0001808a513370203e01e440046661380206c44a0020262a6661760266e1cdd6985e80985e0080d24000264600244444444444444464644666002026006004900019b8102900f302722222222222222232300122222222222222233322301222222222222222253330eb01011133333333309b0100600500400300200f00e01022222232233001002006323232233333333333333300100a00901700401500301300201101000f00e00d00c00b3370000602066e0000c044cdc0001809099999999984d8080300280200180100700780811111119119800801003191919119999999999999980080480500b80b00200a00180900100800780700680600599b8000300f3370000602066e0000c0452819b8101e00e2200233309c010362250010131323001222222222222222323223330010130030023370205201e66e040a403cccc2340409c8940040108894ccc2f804cc22c04cdc42400000e661160266e24008018cc22c04cdc49bad30c00101d0063308b013371200200a66e24dd6986000985f8080e80288018b0a9998510099b87480180084c038888888888888888c05c8888c0088894ccc2e004cc214054cc1f4cdc424000026266e21200001233085013371202600666e240480084c8c88ccccccccccccccc00406406005c00c00805004c04804404003c03803403002d2000480005854ccc28804cdc3a40100042601044446006444600a4446030444444444444444a6661760266e1c0492000132233333333333333300100201000f00e00d00c00b00a0090080070060050040033370002a01e2c2a6661440266e1d200a0021300e22222222222222230172222300222253330b801330850153307d337109000008899b8848000040cc21404cdc480880199b8901000213232233333333333333300101901801701601500300201201101000f00e00d00c00b48001200016153330a2013370e900600109807111111111111111180b91111801111299985c009984280a9983e99b884800003c4cdc42400001c6610a0266e2403c00ccdc480700109919119999999999999980080c80c00b80b00a80a009801801008007807006806005a400090000b09804111118019111802911191a9998568099b87007480004ccc88c06c8888888888888894ccc2f8040444ccccccccc1b801801401000c00803c038040888888c88cc004008018c8c8c88ccccccccccccccc00402802405c01005400c04c00804404003c03803403002ccdc000180819b800030113370000602426666666660dc00c00a00800600401c01e0204444446446600200400c646464466666666666666600201201402e02c00802800602400402001e01c01a01801666e0000c03ccdc000180819b800030114a0002446004444444444444444646466644605c444444444444444a6661a20202226666666661020200c00a00800600401e01c0204444446446600200400c646464466666666666666600201401202e00802a00602600402202001e01c01a01801666e0000c040cdc000180899b8000301213333333330810100600500400300200e00f01022222232233001002006323232233333333333333300100900a01701600401400301200201000f00e00d00c00b3370000601e66e0000c040cdc0001808a50337020289001111801111111111111111192999869009984f8099b893370666e0804c050088cdc199b820240140233371066e0ccdc100880a00819b833370400202801e26661080204a0480262c66e040a8cdc0812a400466e0406004ccdc0848808068a9998568099b87006480004ccc88c06c8888888888888894ccc2f8040444ccccccccc1b801801401000c00803c038040888888c88cc004008018c8c8c88ccccccccccccccc00402802405c01005400c04c00804404003c03803403002ccdc000180819b800030113370000602426666666660dc00c00a00800600401c01e0204444446446600200400c646464466666666666666600201201402e02c00802800602400402001e01c01a01801666e0000c03ccdc000180819b800030114a2002446004444444444444444646466644605c444444444444444a6661a20202226666666661020200c00a00800600401e01c0204444446446600200400c646464466666666666666600201401202e00802a00602600402202001e01c01a01801666e0000c040cdc000180899b8000301213333333330810100600500400300200e00f01022222232233001002006323232233333333333333300100900a01701600401400301200201000f00e00d00c00b3370000601e66e0000c040cdc0001808a51337020289001111801111111111111111192999869009984f8099b893370666e0804c05008ccdc199b820240140223309f013371066e0ccdc100880a00799b83337040020280206613e0266e2120000263371004c05826661080204a0260482c66e040accdc0812a400466e0406404ccdc084880806899982f80c803803111801111111111111111299985f00998458099b8948000034cc22c04cdc424000020661160266e24dd698600080e80999b89010013101116375a00e614a02004614a020026ea8010cccc01c004008c8c8c8c8c80154ccc27804cdc3a4000004264646464646464646464646464646464646464646464646464a66616e0266e25200000113232323253330bb01533080013370e0029000099b87001480e04c8c8c94ccc2f804cdc49b8d001481004c8c8c8c94ccc308054cc21c04cdc3800a4000266e1c0052038132323253330c501337126e340052040132323232323232323232323232324994ccc2d4040045261630d201003375a00261a202002619e020066eb4004c33804004c330040194ccc32804cdc3a400000426464646464646464646493299985b008008a4c2c61a6020066eb4004c34804004c340040194ccc33804cdc3a4000004264932999858808008a4c2c2a66619c0266e1d20020021324994ccc2c404004526161630d10100230d10100137540026198020022a6661940266e1d20020021323232324994ccc2c0040045261630cd01003375a0026198020022a6661940266e1d20040021323232323232324994ccc2cc040045261630d001003375a002619e02002619a020066eb4004c3300400454ccc32804cdc3a400c004264932999856808008a4c2c2a6661940266e1d20080021324994ccc2b40400452616153330ca013370e900500109924ca66615a020022930b0a9998650099b87480300084c92653330ad0100114985858c33404008c33404004dd50009864008009863008018b1bae00130c50100130c30100416371a0026eb8004c30404004c2fc0400c58dd7000985f00800985e008020b1b8d001375c0026174020026170020062c6eb4004c2dc04004c2d4040194ccc2cc04cdc3a400000426493299984b008008a4c2c2a6661660266e1d20020021324994ccc2580400452616153330b3013370e900200109924ca66612c020022930b0b185b00801185b008009baa00130b10100130af0100230af0100130ad0100653330ab013370e900000109919191919191919191919191924ca666134020022930b185b80803299985a8099b87480000084c8c8c8c8c8c8c926533309e01001149858c2ec040194ccc2e404cdc3a4000004264646464646464932999851008008a4c2c617e0200ca66617a0266e1d20000021323253330bf013370e6e340052038132324994ccc28c040045261630c00100316375c002617e020022a66617a0266e1d20020021323253330bf013370e6e340052038132324994ccc28c040045261630c00100316375c002617e020022c6180020046180020026ea8004c2ec0400454ccc2e404cdc3a4004004264646464646464646464932999852808008a4c2c6184020066eb4004c30404004c2fc0400cdd6800985f00800985e008019bad00130bb010011630bc0100230bc010013754002616e020022a66616a0266e1d20020021324994ccc26004004526161630b80100230b801001375400261660200261620200ca66615e0266e1d20000021323253330b1013370e6e340052038132324994ccc254040045261630b20100316375c0026162020022a66615e0266e1d20020021323253330b1013370e6e340052038132324994ccc254040045261630b20100316375c0026162020022c6164020046164020026ea8004c2b40400458c2b804008c2b804004dd500098548080098538080329998528099b87480000084c8c8c8c8c8c8c8c8c8c8c8c8c926533309401001149858c2c4040194ccc2bc04cdc3a400000426464646464646493299984c008008a4c2c616a0200ca6661660266e1d20000021323232323232324994ccc270040045261630b90100653330b7013370e900000109919299985c8099b87371a002901c0991924ca66613a020022930b185d008018b1bae00130b901001153330b7013370e900100109919299985c8099b87371a002901c0991924ca66613a020022930b185d008018b1bae00130b9010011630ba0100230ba010013754002616a020022a6661660266e1d20020021323232323232323232324994ccc27c040045261630bc01003375a0026176020026172020066eb4004c2e004004c2d80400cdd6800985a808008b185b00801185b008009baa00130b101001153330af013370e900100109924ca666124020022930b0b1859008011859008009baa00130ad0100130ab0100653330a9013370e90000010991929998558099b87371a002901c0991924ca66611e020022930b1856008018b1bae00130ab01001153330a9013370e90010010991929998558099b87371a002901c0991924ca66611e020022930b1856008018b1bae00130ab010011630ac0100230ac010013754002614e020022c6150020046150020026ea8004c28c04004c2840400cdd68009850008008b1850808011850808009baa001306e309c01309701309d01001004309b01309a01309c010023333333333333333333069375a612c020466eb4c25804088dd6984b008109bad309601020375a612c0203e6eb4c2580407806c06806406005c05805405004cdd7184b0081d8029bab309601009010333330930122222533307c00112250011323233533309b013371000200826609e00200c26609e66e04cdc08008022400400a44660ba644660020080046142020086666012010002006613c0200a2c6eb4c27c04004c138c26c040040c489400520ce0f3758612c02612a02078612a026eb0c258040c8ccccccccccccc88888888888888888c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c94ccc2dc04cc21004cdd7985c80809985c80807198420099b8801a375a617202016661080266e3cc2d404c2e40404406ccc21004cc21004cdc79bae30b90100a02133084013371e6eb8c2e404024080cc21004cdc79bae30b90100801f3371e6eb8c2e40401c078cc21004cc88c8c8c94ccc2f004cdc3a40040042646464a66617e0266e1d200200213375e6e9c010dd38008a5030c20100230c20100137540082a6661780266e1d2004002132323253330bf013370e9002001099baf374e0086e9c0045281861008011861008009baa004132323253330bf013370e9000001099baf374e0086e9c0045281861008011861008009baa00430bf0100230bf010013754004617202617002024006661080266e25208092f40100153330b70133712610c026eacc2e404049200a132323253330ba013370e90030010991919299985e8099b87480080084cdc79bae30bf0100102a14a06180020046180020026ea8c8c2f404c2f804004c2f00404454ccc2e804cdc3a401000426610e026122020486610e0266e1cccc26004dd5985e0080981300fa400466e1cccc26004dd5985e0080a81300fa40042a6661740266e1d200a00213308701330840130bc01011307001c32323253330bd013370e90000010a51132323253330c0013370e90000010a5114a06186020046186020026ea8c2fc04048c30004008c30004004dd519185e80985f00800985e008088a99985d0099b87480300084cc21c04cc21004c2f004044c1c006cc8c8c94ccc2f404cdc3a400000429444c8c8c94ccc30004cdc3a400000429445281861808011861808009baa30bf0101230c00100230c001001375464617a02617c020026178020222944c2f404008c2f404004dd50010b099191199980080280180100c9999999983781101081000f80f00e9bab30ba010130023333333306e02102001f01e01d01c375661720202066e0000408858dd6985c00807185b80802191919299985b8099b87480000084cdd2a4000136022646464a6661740266e1d200000213374a90021984f80a60103d879800009e01153330ba013370e9001001099ba548008cc27c04dd4991b94376600261780202013c02266e9520043309f0130bc0101009e0130bd0100230bd0100137546172020186174020046174020026ea8004c8c2d804c2dc04004c2d404028c2cc04004c2c804004c2c404004c2c004004c2bc04004c2b804004c2b404004c2b004004c2ac04c2ac04004c2a804004c2ac04018c2a004004c2a404018c29804004c29c04004dd7184a8081b9bae30950103601101a019018017375c612a02074008020018612a02038612a0203660ac60cc61280261260200261240200261260266660d40086eb8c248040dc1c4dd6184900817199999983b8440080100080b00a80a009984500984700801184600800984680984600984580800984600998209bad308b01032026308b0132308b01308a01308c0100133040375a61140206004a60fe610e02610002610c02046610c02610a020186eb8c21404078dd69842008099bad308301013375a6104020266eb4c2040404cdd69840008099bad307f013375c60fc0266eb8c1f404cdd7183e0099bae307b0133079001307800130770013076001307500130740013073001307200130710013070001306f001306e001306d001306c001306b001306a0013069001306800130670013068013333063222330012233005337000089001000880124000002660c444a666090002244a00226604a60ce002600460cc0026eb0c19400cc18c004c188004c18cc188004c18802cc17c004c178004c174004c170004c16c004c170020c16400454ccc160cdc3a4008004264646464646464646603c6603e0026eb0c188010888c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8cc158c150cc1ac8cc1c0dd718460081398388009bac308b0102f330563370e60b4660d64660e005660e20026eb0c22c040bd2002330563304a308b0102c029330563304b3756611602056050660ac66ebcc22c04098c22c04064cc158cdd798458081298458080c1982b19baf308b01024308b01017330563375e61160204661160202c660ac66ebcc22c04088c22c04054cc158cdd79845808101845808071982b19baf308b0101f308b0100d330563375e61160203c611602018660ac66ebcc22c04074c22c0402ccc158cdd798458080e1845808051982b19baf308b0101b308b01009330563375e611602034611602010660ac66ebcc22c04c21804c22804068c22c04c22804018cc158cdd79845808109845808079982b19b8848000014cc158cdc480282e1982b19b8848000010cc158cdc424000006660ac66e252000002330563371290000009982b19b883370066e00cdc00020018010008029982b1998261bae308b01031375c6116026114020626eb0c22c040bccc158c8c8c94ccc23004cdc3a40000042646094646464a6661200266e1d200200214a02646464a6661260266e1d200000214a02944c25804008c25804004dd51849008009849808011849808009baa308f01308e01309001001308e0100114a2611e02004611e020026ea8c22c0401cc8c8c94ccc23004cdc3a40000042646094646464a6661200266e1d200200214a02646464a6661260266e1d200000214a02944c25804008c25804004dd51849008009849808011849808009baa308f01308e01309001001308e0100114a2611e02004611e020026ea8c22c04018dd69845008081bad308901010375a6110020206eb4c21c04040dd6984300805984200800984180800984100800984080800984000800983f800983f000983e800983e000983d800983d000983c800983c000983b800983b000983a800983a000983980098390009839981a182200718380009837800983700098368009836000983580098350009818983480098340009833800983300098328009833008982e9830801182f800983019812182f982f0021bac305f003305d001305c001305d305c001305c0053021305b007153330583370e9003001099191919191919191980f1980f8009bac30620042223232323232323232323232323232323232323232323232323232323232323232323232323232323305730553306c233071375c611a0205060e40026eb0c230040c0cc15ccdc3982d9983611983881618390009bac308c0103048008cc15ccc12cc230040b40a8cc15ccc130dd59846008160149982b99baf308c01027308c01014330573375e61180204c611802026660ae66ebcc23004094c23004048cc15ccdd79846008121846008089982b99baf308c01023308c01010330573375e61180203a611802012660ae66ebcc23004070c23004020cc15ccdd798460080d9846008039982b99baf308c0101a308c01006330573375e61180203261180200a660ae66ebcc23004060c23004010cc15ccdd798460080b9846008019982b99baf308c01016308c01002330573375e61180261160202a611802611602002660ae66ebcc23004088c2300403ccc15ccdd79846008109846008071982b99baf308c01020308c0100d330573375e61180203e611802018660ae66ebcc23004078c2300402ccc15ccdd798460080a9846008009982b99b8948000dd69846008051982b99b89375a6118020140ba66609a6eb8c230040c8dd71846009845808191bac308c01030308a01001308901001308801001308701001308601001308501001308401001308301001308201001308101001308001001307f001307e001307d001307c001307b001307a001307900130780013079303a304a0143076001307500130740013073001307200130710013070001306f001306e306e001306d001306c001306b001306a001306900130680013067001306600130650013066011305d3061002305f001306033024305f305e004375860be00660ba00260b800260ba60b800260b800a6460b460b4604400260b600e2646464646464646464600244646464646464646464646464646464646464664644660020060049001240044464646464646002444444444444444646464a66611c0266e2120000011323305c330593091010333232325333092013370e90010010984a008008b184a80801184a808009baa3091013090010373305c3371066e00dd6984880810a41014191021205c660b860b402e660b866ebcc244040c8c24404058cc170cdd78020019982e19b8733306d37566122020626eb8c24404c248040f81c1200233333333305b375c61220261240207c0e00306eb8c244040acdd71848808151bae309101029375c6122020506eacc24404054ccc184050894004008c1a4cc1c08cc1d40c0c1d8004dd618480081a8b19b8133306b3756611e020266eb8c23c04c240040f005803cc180c23804c23404048c17cc23404c230040b4ccccccccccccccccccc140dd6983e8061bad307d00b375a60fa0146eb4c1f4024dd6983e8041bad307d007375c60fa02e6eb8c1f4058dd7183e80a9bae307d014375a60fa0266eb4c1f4048dd6983e8089bad307d010375a60fa01e6eb8c1f4c1f80a8010dd5983e80e983e983c183e003983d800983e199982980d1bae307b307c02805a375860f603c660b44660be6eb8c1ec058c180004dd6183d00f999999982f8380010009bae3079013375c60f20246eb8c1e4044dd7183c808183a800983a0009839800983900098388009838000983780098370009836800983600098358009835000983480098340009833800983300098328009833008982d1831182d9830802982e9830801182f8009830182f982f000982f99982d911299982f99b89480280084cc00ccdc08012401460c060c060c060c060c00022a6660be66e1c0092000130610011332253330613371000490000b0999830111299983219b87002480004c1980044cc00ccdc08012400460ca00200400266e0400920023060001375a60bc00a6eb0c17800cc170004c16c004c170c16c004c16c010c16c008c16c004dd50012601bed8799f581c4fea4b43ea3e9f78a364599b93a26be90b942b02f1d6d2efa024038b4158581cd2efbcdf7f346ce85aec08780662d6aa7f638ae8320b58fdbb9e0590581c31070a43f38dc8edd236d1ec7562fb1b54b55d710f3d385a68ffdc46581c0ceaab7c2cec8f93c38b7c3598fa239f50bc14fcc6da9f016adb035c581c4e18ba4b417dd4cd100feebf0c2ec86b742ef1374f174c2f3917ca784156581c7834b98572fec82cbb5cb85cac3ad9230bcbfae8142eed1f917b8bce4159ff0022222222323232323232322333333001004003002007006005337020100026601c66e0802001ccdc099b813370266e04cdc000400300280200180119b813370266e04cdc000380280180100099b833370400801000e66e0ccdc100180400319b833370400401000a6601266e0800402001088cdc019b83002001533305133710900019b86002001148008520002223003222222222222222323232233001002003323232233333333333333300100400300201201101000f00e00d00c00b00a0090080073370202200666e0004404ccdc00088099980a19b833370402400202066e0ccdc100880080799b8103300d22533304f33712004002200420026096444a66609e66e1c009200010011330012233005337020089001000891280091919bb0304f001304f304e001305037540024646464a66609c66e1d2000002130500011630510023051001375400244444444646464466600200800600466606200800c00a66606000600e00c666604e00401000e002608e444a66609666e25200a002133003337020049005180398039803980398038008a99982599b87002480004c0180044cc010cdc080124004600e00244a66609266e20009200016133003002001304522253330493370e00490000980200089980199b8100248008c0140048cc00488008588cc00488004588ccc110005282512330012233001226100216223330132232325333047323232533304a3370e90000010a5013371e6eb8c130004024c134008c134004dd5191825182580098248010998039919191199800802001801182598250019bab304a0023049002004100430470013048001225001001222233002004003223375e6086608460880046086608460880024466ebcdd30011ba60012223303f22533302500114a02a66608466e212000333020323756608a6088608c00264608a6088608c002608800200a00829444c008c10c0040048c8c8c8c80154ccc100cdc3a400000426464a66608466e1cdc6800a4070264646464a66608ca6601666e1c005200013370e002901c0991919299982499b89371a00290200991919192999826a9980919b87001480004cdc3800a40702646464a6660a066e24dc6800a40802646464646464646464646464646464646464646464a6660ca66e252000001132323232323232323232323232323232323232323232323232323232323232323232323232324994ccc1b400452616308a010065333088013370e900000109924ca6660d60022930b0b1845808011845808009baa0013086010013084010065333082013370e900000109919191919191924ca6660d60022930b18440080329998430099b87480000084c8c8c8c8c8c8c8c8c8c8c8c8c9265333075001149858c248040194ccc24004cdc3a400000426464646464646493299983c8008a4c2c612c0200ca6661280266e1d20000021323232323232324994ccc1f400452616309a010065333098013370e900000109919299984d0099b87371a002901c0991924ca6660fc0022930b184d808018b1bae001309a0100115333098013370e900100109919299984d0099b87371a002901c0991924ca6660fc0022930b184d808018b1bae001309a0100116309b01002309b010013754002612c020022a6661280266e1d20020021323232323232323232324994ccc2000400452616309d01003375a0026138020026134020066eb4004c26404004c25c0400cdd6800984b008008b184b80801184b808009baa00130920100115333090013370e900100109924ca6660e60022930b0b1849808011849808009baa001308e01001308c01006533308a013370e90000010991929998460099b87371a002901c0991924ca6660e00022930b1846808018b1bae001308c010011533308a013370e90010010991929998460099b87371a002901c0991924ca6660e00022930b1846808018b1bae001308c0100116308d01002308d0100137540026110020022c6112020046112020026ea8004c2100400454ccc20804cdc3a40040042649329998328008a4c2c2c610a02004610a020026ea8004c20004004c1f80194ccc1f0cdc3a40000042646464646464649329998328008a4c2c61040200ca6661000266e1d20000021323232323232323232323232324994ccc1bc00452616308c01006533308a013370e900000109919191919191924ca6660e60022930b18480080329998470099b87480000084c8c8c8c8c8c8c9265333077001149858c250040194ccc24804cdc3a400000426464a6661280266e1cdc6800a40702646493299983c0008a4c2c612a020062c6eb8004c2500400454ccc24804cdc3a400400426464a6661280266e1cdc6800a40702646493299983c0008a4c2c612a020062c6eb8004c2500400458c25404008c25404004dd50009848008008a9998470099b87480080084c8c8c8c8c8c8c8c8c8c926533307a001149858c25c0400cdd6800984b00800984a008019bad001309301001309101003375a0026120020022c6122020046122020026ea8004c2300400454ccc22804cdc3a40040042649329998368008a4c2c2c611a02004611a020026ea8004c22004004c218040194ccc21004cdc3a400000426464a66610c0266e1cdc6800a4070264649329998350008a4c2c610e020062c6eb8004c2180400454ccc21004cdc3a400400426464a66610c0266e1cdc6800a4070264649329998350008a4c2c610e020062c6eb8004c2180400458c21c04008c21c04004dd50009841008008b1841808011841808009baa001307e0011533307c3370e900100109924ca6660be0022930b0b183f801183f8009baa001307a0013078003375a00260ee00260ea0066eb4004c1d0004c1c800cdd6800983880098378019bad001306e001306c003375a00260d600260d20066eb4004c1a0004c19800c58dd6800983280098318019bad00130620013060003375a00260be00260ba0066eb4004c170004c16800cdd6800982c800982b8019bad00130560013054003375a00260a600260a20062c6eb8004c140004c13801058dc68009bae001304c001304a00316375c0026092002608e0082c6e34004dd7000982280098218018b1bae001304200116304300230430013754002466078002004294488c8c8c94ccc0f8cdc3a400400426600c00860800022c608200460820026ea800888cc0e4894ccc07c004584c94ccc0f4cc01c010c0fc0044c0fcc0f80044c00cc0f8008c0fcc0f80040088c0e4c0cc00488cdd79ba7303b002374e6076002ae8c888888888c004888888888888888cc0654ccc130c08c0544cdc399981500880a80a19b803370066e00cdc000780600500401289980c99b8733302a0110150143370066e00cdc000780600500419b8902533302a011024024330193370e66605402202602466e00cdc019b8000e00b009007330193370e66605402203002c01a6603266e1cccc0a804406005d20023370e6603402a01a60360224466606a00400294088cdc0a99981a18058010a401029005299981a19b8700148000520021480008ccc00888cdc000118029bab3038001480000048c0c08894ccc05c00440084cc00ccc010008c0d8004c0d4004cc0b88894ccc05400440084cc00ccdc00012400460660029000111180191111111111111119191919119999999999999980080200180100880800780700680600580500480400380319b800100133370202066e0ccdc100900800119b810103370666e08044040004cdc0809806a41fdfffffffffffffffe024646464a66606066e1d20040021303200116303300230330013754002444444444444444444464646464466666666666666600200a00800603002e02c02a02802601c01a0180160140044a00266603c00800c00a66e04cdc099b8133301d00300c00b01301100f3370266e04cdc0999980a00100680600b809808807911119299981799b894800000454ccc0bcc01801040044ccc03401401000c58cdc0999806002003003000919b8f001002488100482026fb8088888c014cc03094cc01ccc0440140044cdc39998049bab302d302c302e0010040034800800494ccc024004584c94ccc0280044c0a400858c09c00488ccc09800800400c5281111999802001240004666600a00490003ad3756002006460046ea40048888cc08c894ccc024004401454ccc098cdd7981598140008030980218151814000898011813800800a45014c002301e2253330040011005132533302230040011330070013003302300213003302300230230015573e97ae0574044646464a66603e66e1d200000214a0266e3cdd71810800802981100118110009baa32301f3020001301e301f0012301c301b301d0012222222372866e28dca00399b8a37286e60c020018cdc51b943730601000a66e28dca19b8a004003372866e28008004c05888cdcb299980c99b8800148000400c4010c8c94ccc06ccdc3801240002600e00226466e58004c020008c010008cdc298088012402866e10c04000520144901012d00490100253330153370e002900008010a99980a99b8700148008400c54ccc054cdc3800a400820082a66602a66e1c00520061005153330153370e002900408030a99980a99b8700148028401c54ccc054cdc3800a401820102a66602a66e1c005200e1009153330153370e002900808050a99980a99b8700148048402c592410130004901013100490101320049010133004901013400490101350049010136004901013700490101380049010139002533300a337120029000898010008800919b81480000052201013000232323232533300b3370e90010010991919299980719b87480080084c8c88cc00400c008dd698088011bad30100041630110023011001375464601c601e002601a60180082c601c004601c0026ea8c8c02cc030004c028004c0280048c01cc0080048c018c0080048c014c0140048c8c8c94ccc018cdc3a400400426eb8c02000458c024008c024004dd518029803000919180111980100100091801119801001000ab9a5744ae848c008dd5000aab9e5573a1", + "poolCpRefTxId": "65692bcc396173f4f67181ec8d8b60cc94422e81210d70c7a2c411c8ea1a6a9b#0" +} diff --git a/packages/wingriders-v2/src/scripts/preprod-script.json b/packages/wingriders-v2/src/scripts/preprod-script.json new file mode 100644 index 0000000..09d356a --- /dev/null +++ b/packages/wingriders-v2/src/scripts/preprod-script.json @@ -0,0 +1,10 @@ +{ + "factoryScript": "59145c5914590100003232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323232323222233322232533303c001149858c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c00488cc8c8c94ccc150cdc3a40040042646464644666600200a0080060046644444444444464646464646464646464646464646464646464646464603c44660c4660c466e3cdd7183f00c0109983119b8f375c60fc02e03a660c466e3cdd7183f00b00e1983119b8f375c60fc02a036660c466e3cdd7183f00a00d1983119b87375a60fc01c09a660c466e1cdd6983f009a4014660c466e1cdd6983f0078261983119b87375a60fc02490011983119b87375a60fc02290001983119b87375a60fc02090001983119b87375a60fc01890001983119b87375a60fc01690001983119b87375a60fc01490001983119b87375a60fc01290001983119b87375a60fc01090001983119b87375a60fc00e90001983119824183f00319ba5480081b4cc188cc120c1f8014cdd2a40040da660c466e1cdd6983f0068011983119b87375a60fc00604866e1cdd6983f183e8018119983119b87337020ce03266e0cdd6983f00219b823370490020120119983129983c9999824a412c026eb4c1f8010cdc100f81219b8201e023133330494825804cdc01bad307e00448008cdc100f81219b8201e023330623371090000121983119b884800008d4cc1e4cdc381224004266e1c08d2002307a0013074307a3079001307800130770013076001307500130740013073001307200130710013070001306f001306e001306d001306c001306b001306a001306900130680013067001306600130603232323200e53330663370e900000109919299983419b87371a002901c099191919299983629983519b87001480004cdc3800a40702646464a6660de66e24dc6800a4080264646464a6660e6a660e266e1c005200013370e002901c0991919299983b19b89371a0029020099191919191919191919191919191919191919191929998458099b89480000044c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c8c92653330b101001149858c2c4040194ccc2b804cdc3a400000426464646464646464646493299985c008008a4c2c6170020066eb4004c2dc04004c2d40400cdd6800985a008009859008019bad00130b1010011630b10100230b1010013754002615a0200261560200ca6661500266e1d20000021323232323232324994ccc2bc040045261630af0100653330ac013370e900000109919191919191919191919191924ca666172020022930b185c80803299985b0099b87480000084c8c8c8c8c8c8c92653330bd01001149858c2f4040194ccc2e804cdc3a4000004264646464646464932999860808008a4c2c61820200ca66617c0266e1d20000021323253330c0013370e6e340052038132324994ccc308040045261630c20100316375c0026182020022a66617c0266e1d20020021323253330c0013370e6e340052038132324994ccc308040045261630c20100316375c0026182020022c6182020046182020026ea8004c2f40400454ccc2e804cdc3a4004004264646464646464646464932999862008008a4c2c6188020066eb4004c30c04004c3040400cdd6800986000800985f008019bad00130bd010011630bd0100230bd0100137540026172020022a66616c0266e1d20020021324994ccc2dc04004526161630b90100230b9010013754002616a0200261660200ca6661600266e1d20000021323253330b2013370e6e340052038132324994ccc2d0040045261630b40100316375c0026166020022a6661600266e1d20020021323253330b2013370e6e340052038132324994ccc2d0040045261630b40100316375c0026166020022c6166020046166020026ea8004c2bc0400458c2bc04008c2bc04004dd50009855808008a9998540099b87480080084c92653330a90100114985858c2ac04008c2ac04004dd500098538080098528080329998510099b87480000084c8c8c8c8c8c8c92653330a901001149858c2a4040194ccc29804cdc3a4000004264646464646464646464646464932999859808008a4c2c61660200ca6661600266e1d20000021323232323232324994ccc2dc040045261630b70100653330b4013370e900000109919191919191924ca666176020022930b185d80803299985c0099b87480000084c8c94ccc2e804cdc39b8d001480e04c8c92653330bc01001149858c2f00400c58dd7000985d808008a99985c0099b87480080084c8c94ccc2e804cdc39b8d001480e04c8c92653330bc01001149858c2f00400c58dd7000985d808008b185d80801185d808009baa00130b701001153330b4013370e900100109919191919191919191924ca66617c020022930b185f008019bad00130bd0100130bb01003375a0026174020026170020066eb4004c2dc0400458c2dc04008c2dc04004dd50009859808008a9998580099b87480080084c92653330b10100114985858c2cc04008c2cc04004dd500098578080098568080329998550099b87480000084c8c94ccc2b004cdc39b8d001480e04c8c92653330ae01001149858c2b80400c58dd70009856808008a9998550099b87480080084c8c94ccc2b004cdc39b8d001480e04c8c92653330ae01001149858c2b80400c58dd70009856808008b1856808011856808009baa00130a9010011630a90100230a9010013754002614a020022a6661440266e1d20020021324994ccc28c04004526161630a50100230a5010013754002614202002613e020066eb4004c27804004c2700400cdd6800984d80800984c808019bad001309801001309601003375a002612a020026126020066eb4004c24804004c2400400cdd68009847808009846808018b1bad001308c01001308a01003375a002611202002610e020066eb4004c21804004c2100400cdd68009841808009840808019bad001308001001307e003375a00260fa00260f60066eb4004c1e8004c1e000c58dd7000983b800983a8020b1b8d001375c00260e600260e20062c6eb8004c1c0004c1b801058dc68009bae001306c001306a00316375c00260d20022c60d200460d20026ea8028dd6982d0021bad305a3059004375c60b20266eb8c16004cccccccc09d221013100375a60ae0026eb4c15cc158004dd7182b80c1bae3057017375c60ae02c6eb8c15cc1580584c8c8c8c88cccc00401401000c0088888888888c02088c8c8c8cc134cdd799ba548000cc164dd48071982c9ba900a330593752012660b26ea4020cc164dd48039982c9ba8480f0cc164dd424014660b26ea1200033059375090001982c9ba8037330593750070660b26ea0014cc164dd424000660b26ea1200033059375090001982c9ba848000cc164dd424000660b26ea12000330593374a900102c1982c99ba548008160cc164cdd2a40000b00b001e6609a66e24cdc100180180099b880013370400400466e0802c028cdc0000a400466e0413c00cdd7182c80a9bae305801533333330274881013000480092002375c60ae0306eb8c15c05cdd7182b80b1bae3057305601630570023057001375460a802c44446464646464646464646608a66e20cdc0807007a4101e164a46608a66e1cccc154054dd71830983000d827240046608a66e1cccc154054dd71830983000d80699b8104a042330453370e6090646600240022c66466444660bc44a6660ca002244a0022a6660c866ebcc1a4c19c0040104c014c19c0044c008c1980040048c888c00800cdd598320009ba9001375c60c260c003602a90031982299999999998050030058080020019bae3061022375c60c20426eb8c184080dd718309830010001198229982299b8848000010cc114cdc424000006a660b866e2520000051304b375c60c20446608a66e212000337020020846608a6608aa6660bc66e3cdd718308111bae30610201337206eb8c184084dd718309830010099b90375c60c20446eb8c184080cc114c10d4cc148cdc79bae3061022375c60c260c0036266e3cdd71830810806998229821a9982919b8f375c60c20406eb8c184c18006c4cdc79bae3061306002000d3304530435330523371e6eb8c184088dd71830983000d899b8f375c60c204209c6086a660a466e3cdd718308101bae3061306001b13371e6eb8c184c180080138cc114cdc39823003999119b815333060304d0021480205200a53330603370e00290000a4004290001bae3061022002330453370e609002490011982299b87333055015375c60c260c00360b29001198229982299b90375c60c203e01a66e40034dd71830983000fa99982f8088a50153305232533306000114a02a660a66ae8cc1840044cc114cc128038dd71831183081018310009830008899822198249bae306101f00d3061011337020920026660a600a6eb8c17cc17806402cccc148010dd7182f00e9bae305e305d01d533305a3047375c60ba03c200226660a20066eb8c174078dd7182e80e99b81333050002047047482026fb808c11cc16cc168008dd5982d000982c182980099299982b8008b099299982c0008982d0010b182c00099823129982519826002000899b8733304d3050001375c60b260b002608c90011bac305800f323232323253330533370e90010010991919299982b19b87480080084c8c88cc00400c008dd6982d0011bad3059004163059002305900137546460ae60a200260ac60aa0082c60ac00460ac0026ea8c8c150c138004c14c004c130004c144018cc0f894cc108cc11000c0044cdc399982298240009bae3051305000b04948008dd618280039981e91929982119822001800899b873330453048001375c60a260a001609290011828182798250009bac304f00732323232533304f3370e900100109bae305200116305200230520013754609e6092002609c60900026644646464a66609e66e1d20020021323304c225333053001161325333053323375e6e9cc144014dd39828800982b0008982b182a80089801982a8011827982a80080298290008b182900118290009baa002304d304c0063758609a00a6eacc130008c8c12cc12c004c128004c124c124004c120c120004c108c120004c104028c114004c110004c10c004c0f4030c0f001cc100004c0fc004c0f8004c0e0008c8c8c8c801d4ccc0eccdc3a40000042646464646464649329998210008a4c2c60840066eb8004c104004c0fc00cdd7000981f0008b181f001181f0009baa00332323232006533303b3370e90000010991919191919191919299982229982119b87001480004cdc3800a40702646464a66608e66e24dc6800a4080264646464a666096a6609266e1c005200013370e002901c0991919299982719b89371a00290200991924ca6660a00022930b18280018b1bae001304f001304d00416371a0026eb8004c12c004c12400c58dd7000982400098230020b1b8d001375c0026088002608400ca66607e66e1d20000021324994ccc100004526161533303f3370e900100109919191919191924ca66608c0022930b18230019bad00130450013043003375a00260840022c608400460840026ea8004c0f800458c0f8008c0f8004dd5001000a6019ad8799f581ce1909c771ecebeff57ad38eadbf28cb41cb4c820bc3396fa48865926581c7ad4d2213f545012f670718283380ef209b9cbaec32b70132ae78483581c946ae228430f2fc64aa8b3acb910ee27e9b3e47aa8f925fac27834a1581c23680ea6701b56f2c12ae79d8af94fd36f509b7b007029c7ce114840581c4fea4b43ea3e9f78a364599b93a26be90b942b02f1d6d2efa024038bff002232323253330383370e90010010991919299981d99b8748000008528099baf374e0086e9c004c0f8008c0f8004dd50020991919299981d99b8748008008528099baf374e0086e9c004c0f8008c0f8004dd5002181d801181d8009baa0022222323232330203371266e04ccc02001c01800ccccc02401c01800c009200033710900019b8133300800700100333330090070010030023370000a900119b800030023370400400244466e00cdc119b8233704006901000100099b8233704004004004444466e08cdc12401000466e00cdc119b820044802000400d20a09c01482024bd0048888888dca19b8a372800e66e28dca1b983008006337146e50dcc180400299b8a372866e2801000cdca19b8a0020013028223372ca66605c66e20005200010031004323253330303370e00490000980380089919b96001300800230040023370a6022004900a19b843010001480512401012d004901002533302a3370e002900008010a99981519b8700148008400c54ccc0a8cdc3800a400820082a66605466e1c005200610051533302a3370e002900408030a99981519b8700148028401c54ccc0a8cdc3800a401820102a66605466e1c005200e10091533302a3370e002900808050a99981519b8700148048402c592410130004901013100490101320049010133004901013400490101350049010136004901013700490101380049010139002533301f337120029000898010008800919b814800000520d00f233301c0014a094488c8cc010cdd798061810180f80080199b873005375660400029002180f180c8009119980d801000a5023330022233700004600a6eacc07c005200000123014222533301c001100213300333004002301e001301d00133012222533301a0011002133003337000049001180d800a40004466e9520003300a3752004660146ea400402520feffffffffffffffff0123371e002004910100232323253330153370e90020010980c0008b180c001180c0009baa001489014c002300c2253330130011004132533301330040011330060013003301500213003301500230150014bd702ba02233300e00200100314a044646464a66601e66e1d200000214a0266e3cdd71809000802980900118090009baa323010300a001300f30090012223333004002480008cccc014009200075a6eac00400c8c008dd480091111980391299980700088028a99980699baf30123010001006130043011301000113002300f0010012375660146012600800291101460023230022330020020012300223300200200123008375400246600600200429455cd2ab9f5744ae8555cf2ab9d1", + "factoryScriptHash": "c1c6f3131ed5a7f16722926d7bec3182c7313b309c65afb2c042b74e", + "dexSymbolScript": "59024e59024b010000323232323232323232323232323222323232533300e001149858c8c8c94ccc040cdc3a400400426664446601c44a66602c002294054ccc054cdc424000666444666602200490001199980900124000eb4dd5800801991bab30193015301a0013230193015301a001301800100500414a2260046028002002646464a66602666e1d20000021375c602c0022c602e00460260026ea8c04cc03c014038dd6180980209991199809001000a503322323300124a22940cc8c038894ccc05800448940044ccc00cc060004888c00800c4c008c05000494ccc04ccdd7801980b180b800891180100189128008009809980a0041bac301300433332222333300d0034a0464a66602e002294054cc048d5d1980a80089929980999b8f375c603200200a266e1cdd6980e000803980c8009bab00100148008c8c8c94ccc04ccdc3a400000426eb8c05800458c05c008c04c004dd5180998078028071bab301332301030103010001300f00430140023010001375464646464010a66602066e1d20000021324994ccc04400452616153330103370e900100109924ca6660220022930b0b180a00118080009baa0043010300f001300f0014c012fd8799fd8799fd8799f5820bb29f1d7fedc667fbfa6c74b40fce5b3ab512d217ace7afcc9cf5b3f023ccba9ff01ffff0023002375200244446600a44a66601a002200a2a66601866ebcc038c03c0040184c010c044c03c0044c008c02c0040048c8c0088cc0080080048c0088cc00800800488ccc01800800400c5282450146005744ae6955cfaab9d5742460046ea800555cf1", + "dexSymbolScriptHash": "4fea4b43ea3e9f78a364599b93a26be90b942b02f1d6d2efa024038b", + "poolScriptHashCP": "e1909c771ecebeff57ad38eadbf28cb41cb4c820bc3396fa48865926", + "poolScriptHashSS": "946ae228430f2fc64aa8b3acb910ee27e9b3e47aa8f925fac27834a1", + "requestValidatorHashCP": "7ad4d2213f545012f670718283380ef209b9cbaec32b70132ae78483", + "requestValidatorHashSS": "23680ea6701b56f2c12ae79d8af94fd36f509b7b007029c7ce114840" +} diff --git a/packages/wingriders-v2/src/warehouse.ts b/packages/wingriders-v2/src/warehouse.ts new file mode 100644 index 0000000..750d9da --- /dev/null +++ b/packages/wingriders-v2/src/warehouse.ts @@ -0,0 +1,162 @@ +import { + Address, + AddressType, + Asset, + Bytes, + CredentialType, + NetworkEnvironment, + networkEnvironmentToNetworkID, + Utxo, +} from "@minswap/felis-ledger-core"; +import { Maybe } from "@minswap/felis-ledger-utils"; +import invariant from "@minswap/tiny-invariant"; +import mainnetReferences from "./scripts/mainnet-references.json"; +import mainnetScripts from "./scripts/mainnet-script.json"; +import preprodReferences from "./scripts/preprod-references.json"; +import preprodScripts from "./scripts/preprod-script.json"; + +type WingridersV2Scripts = { + factoryScript: string; + factoryScriptHash: string; + dexSymbolScript: string; + dexSymbolScriptHash: string; + poolScriptHashCP: string; + poolScriptHashSS: string; + requestValidatorHashCP: string; + requestValidatorHashSS: string; +}; + +type WingridersV2References = { + dexSymbolRefInput: string; + dexSymbolTxId: string; + factoryRefInput: string; + factoryTxId: string; + orderRefInput: string; + orderTxId: string; + poolCpRefInput: string; + poolCpRefTxId: string; +}; + +type WingridersV2Config = { + scripts: WingridersV2Scripts; + references: WingridersV2References; +}; + +export class WingridersV2Warehouse { + private static instance: WingridersV2Warehouse | null = null; + + // ─── Protocol constants (network-independent) ─────────────────────────── + static readonly FACTORY_ASSET_NAME = "46"; // "F" + static readonly VALIDITY_ASSET_NAME = "4c"; // "L" + + static readonly MIN_POOL_ADA = 3_000_000n; + static readonly MAX_LP_TOKENS = 9_223_372_036_854_775_807n; // 2^63 − 1 + static readonly BURNED_SHARE_TOKENS = 1_000n; + + static readonly DEFAULT_A_SCALE = 1n; + static readonly DEFAULT_B_SCALE = 1n; + static readonly DEFAULT_SWAP_FEE_IN_BASIS = 30n; + static readonly DEFAULT_PROTOCOL_FEE_IN_BASIS = 5n; + static readonly DEFAULT_FEE_BASIS = 10_000n; + static readonly DEFAULT_AGENT_FEE_ADA = 2_000_000n; + static readonly MAX_TX_VALIDITY_RANGE_MS = 86_400_000n; + static readonly AGENT_TOKEN_NAME = "58"; // "X" + // PoolConfig.agentSymbol per network (extracted from deployed pool script's applied params). + private static readonly AGENT_SYMBOL_BY_NETWORK: Partial> = { + [NetworkEnvironment.MAINNET]: "1ad3767073087df4fc97fba7ac4a71a0a6cd556f1ad96a7b1c9870c4", + [NetworkEnvironment.TESTNET_PREPROD]: "d2efbcdf7f346ce85aec08780662d6aa7f638ae8320b58fdbb9e0590", + }; + + // ─── Per-network config ───────────────────────────────────────────────── + readonly networkEnv: NetworkEnvironment; + + // Factory + readonly factoryScriptHash: Bytes; + readonly factoryRefInput: Utxo; + readonly factoryAddress: Address; + + // Validity minting policy (also used as the LP policy) + readonly dexSymbolHash: Bytes; + readonly dexSymbolRefInput: Utxo; + + // Pool validators + readonly poolScriptHashCP: Bytes; + readonly poolScriptHashSS: Bytes; + readonly poolAddressCP: Address; + readonly poolAddressSS: Address; + + // Request validators + readonly requestValidatorHashCP: Bytes; + readonly requestValidatorHashSS: Bytes; + readonly requestAddressCP: Address; + readonly orderRefInput: Utxo; + readonly poolCpRefInput: Utxo; + + // Well-known assets minted under `dexSymbolHash` + readonly factoryAsset: Asset; + readonly validityAsset: Asset; + + // Agent authority token (PoolConfig.agentSymbol/agentToken). + // The pool's `evolve` redeemer requires the agent input to hold exactly 1 of this token. + readonly agentAsset: Asset; + + private constructor(networkEnv: NetworkEnvironment) { + const config = WingridersV2Warehouse.getConfig(networkEnv); + this.networkEnv = networkEnv; + + this.factoryScriptHash = Bytes.fromHex(config.scripts.factoryScriptHash); + this.factoryRefInput = Utxo.fromHex(config.references.factoryRefInput); + + this.dexSymbolHash = Bytes.fromHex(config.scripts.dexSymbolScriptHash); + this.dexSymbolRefInput = Utxo.fromHex(config.references.dexSymbolRefInput); + + this.poolScriptHashCP = Bytes.fromHex(config.scripts.poolScriptHashCP); + this.poolScriptHashSS = Bytes.fromHex(config.scripts.poolScriptHashSS); + + this.requestValidatorHashCP = Bytes.fromHex(config.scripts.requestValidatorHashCP); + this.requestValidatorHashSS = Bytes.fromHex(config.scripts.requestValidatorHashSS); + this.orderRefInput = Utxo.fromHex(config.references.orderRefInput); + this.poolCpRefInput = Utxo.fromHex(config.references.poolCpRefInput); + + this.factoryAddress = WingridersV2Warehouse.scriptAddress(networkEnv, this.factoryScriptHash); + this.poolAddressCP = WingridersV2Warehouse.scriptAddress(networkEnv, this.poolScriptHashCP); + this.poolAddressSS = WingridersV2Warehouse.scriptAddress(networkEnv, this.poolScriptHashSS); + this.requestAddressCP = WingridersV2Warehouse.scriptAddress(networkEnv, this.requestValidatorHashCP); + + this.factoryAsset = new Asset(this.dexSymbolHash, Bytes.fromHex(WingridersV2Warehouse.FACTORY_ASSET_NAME)); + this.validityAsset = new Asset(this.dexSymbolHash, Bytes.fromHex(WingridersV2Warehouse.VALIDITY_ASSET_NAME)); + + const agentSymbol: string | undefined = WingridersV2Warehouse.AGENT_SYMBOL_BY_NETWORK[networkEnv]; + invariant(agentSymbol, `WingridersV2Warehouse: no agent symbol configured for network ${networkEnv}`); + this.agentAsset = new Asset(Bytes.fromHex(agentSymbol), Bytes.fromHex(WingridersV2Warehouse.AGENT_TOKEN_NAME)); + } + + private static getConfig(networkEnv: NetworkEnvironment): WingridersV2Config { + switch (networkEnv) { + case NetworkEnvironment.MAINNET: + return { scripts: mainnetScripts, references: mainnetReferences }; + case NetworkEnvironment.TESTNET_PREPROD: + return { scripts: preprodScripts, references: preprodReferences }; + default: + throw new Error(`WingridersV2Warehouse: unsupported network ${networkEnv}`); + } + } + + private static scriptAddress(networkEnv: NetworkEnvironment, scriptHash: Bytes): Address { + return Address.fromCardanoAddress({ + type: AddressType.ENTERPRISE_ADDRESS, + network: networkEnvironmentToNetworkID(networkEnv), + payment: { + type: CredentialType.SCRIPT_CREDENTIAL, + payload: scriptHash, + }, + }); + } + + public static getInstance(networkEnv: NetworkEnvironment): WingridersV2Warehouse { + if (Maybe.isNothing(WingridersV2Warehouse.instance) || WingridersV2Warehouse.instance.networkEnv !== networkEnv) { + WingridersV2Warehouse.instance = new WingridersV2Warehouse(networkEnv); + } + return WingridersV2Warehouse.instance; + } +} diff --git a/packages/wingriders-v2/src/wingriders-v2.ts b/packages/wingriders-v2/src/wingriders-v2.ts index a09e36f..4f296fd 100644 --- a/packages/wingriders-v2/src/wingriders-v2.ts +++ b/packages/wingriders-v2/src/wingriders-v2.ts @@ -11,31 +11,155 @@ import { Asset, BaseUtxoModel, Bytes, + type DatumSource, + DatumSourceType, type NetworkEnvironment, PlutusBool, PlutusBytes, PlutusConstr, PlutusData, PlutusInt, + PlutusMaybe, type Utxo, Value, } from "@minswap/felis-ledger-core"; -import { type CborHex, Maybe, Result } from "@minswap/felis-ledger-utils"; +import { type CborHex, type CSLPlutusData, Maybe, Result } from "@minswap/felis-ledger-utils"; import { blake2b } from "blakejs"; import invariant from "@minswap/tiny-invariant"; +import { WingridersV2Warehouse } from "./warehouse"; export namespace WingridersV2 { - export const ORDER_SCRIPT_HASH = "c134d839a64a5dfb9b155869ef3f34280751a622f69958baa8ffd29c"; - export const POOL_SCRIPT_HASH = "af97793b8702f381976cec83e303e9ce17781458c73c4bb16fe02b83"; - export const LP_POLICY_ID = "6fdc63a1d71dc2c65502b79baae7fb543185702b12c3c5fb639ed737"; - export const FACTORY_POLICY_ID = "6fdc63a1d71dc2c65502b79baae7fb543185702b12c3c5fb639ed737"; - export const FACTORY_ASSET_NAME = "46"; // "F" in hex - export const VALIDITY_ASSET_NAME = "4c"; // "L" in hex - - export const MIN_POOL_ADA = 3_000_000n; - export const MAX_LP_TOKENS = 9_223_372_036_854_775_807n; - export const DEFAULT_A_SCALE = 1n; - export const DEFAULT_B_SCALE = 1n; + export type FactoryDatum = { + head: Bytes; + tail: Bytes; + }; + + export namespace FactoryDatum { + export function fromPlutusJson(d: PlutusData): FactoryDatum { + const { fields } = PlutusConstr.unwrap(d, { + [0]: 2, + }); + return { + head: Bytes.fromHex(PlutusBytes.unwrap(fields[0])), + tail: Bytes.fromHex(PlutusBytes.unwrap(fields[1])), + }; + } + + export function toPlutusJson(d: FactoryDatum): PlutusData { + return { + constructor: 0, + fields: [PlutusBytes.wrap(d.head), PlutusBytes.wrap(d.tail)], + }; + } + + export function fromDataHex(data: CborHex): FactoryDatum { + const plutusData = PlutusData.fromDataHex(data); + return fromPlutusJson(plutusData); + } + + export function toDataHex(data: FactoryDatum): CborHex { + const plutusJson = toPlutusJson(data); + return PlutusData.toDataHex(plutusJson); + } + + export function toDatumSource(data: FactoryDatum): DatumSource { + // Factory only support Inline Datum + return { + type: DatumSourceType.INLINE_DATUM, + data: Bytes.fromHex(FactoryDatum.toDataHex(data)), + }; + } + } + + // ============================================================================ + // Factory Redeemer + // ============================================================================ + + export enum PoolChoiceType { + UseConstantProduct = 0, + UseStableswap = 1, + } + + export type PoolChoice = + | { type: PoolChoiceType.UseConstantProduct } + | { type: PoolChoiceType.UseStableswap; aScale: bigint; bScale: bigint }; + + export namespace PoolChoice { + export function fromPlutusJson(d: PlutusData): PoolChoice { + const constr = PlutusConstr.unwrap(d, { + [PoolChoiceType.UseConstantProduct]: 0, + [PoolChoiceType.UseStableswap]: 2, + }); + switch (constr.constructor) { + case PoolChoiceType.UseConstantProduct: + return { type: PoolChoiceType.UseConstantProduct }; + case PoolChoiceType.UseStableswap: + return { + type: PoolChoiceType.UseStableswap, + aScale: PlutusInt.unwrapToBigInt(constr.fields[0]), + bScale: PlutusInt.unwrapToBigInt(constr.fields[1]), + }; + default: + throw new Error("WingridersV2: Unexpected PoolChoice."); + } + } + + export function toPlutusJson(pc: PoolChoice): PlutusData { + switch (pc.type) { + case PoolChoiceType.UseConstantProduct: + return { constructor: PoolChoiceType.UseConstantProduct, fields: [] }; + case PoolChoiceType.UseStableswap: + return { + constructor: PoolChoiceType.UseStableswap, + fields: [PlutusInt.wrap(pc.aScale), PlutusInt.wrap(pc.bScale)], + }; + } + } + } + + export type FactoryRedeemer = { + poolChoice: PoolChoice; + assetA: Asset; + assetB: Asset; + }; + + export namespace FactoryRedeemer { + export function fromPlutusJson(d: PlutusData): FactoryRedeemer { + const { fields } = PlutusConstr.unwrap(d, { [0]: 5 }); + return { + poolChoice: PoolChoice.fromPlutusJson(fields[0]), + assetA: new Asset( + Bytes.fromHex(PlutusBytes.unwrap(fields[1])), + Bytes.fromHex(PlutusBytes.unwrap(fields[2])), + ), + assetB: new Asset( + Bytes.fromHex(PlutusBytes.unwrap(fields[3])), + Bytes.fromHex(PlutusBytes.unwrap(fields[4])), + ), + }; + } + + export function toPlutusJson(r: FactoryRedeemer): PlutusData { + return { + constructor: 0, + fields: [ + PoolChoice.toPlutusJson(r.poolChoice), + PlutusBytes.wrap(r.assetA.currencySymbol), + PlutusBytes.wrap(r.assetA.tokenName), + PlutusBytes.wrap(r.assetB.currencySymbol), + PlutusBytes.wrap(r.assetB.tokenName), + ], + }; + } + + export function fromDataHex(data: CborHex): FactoryRedeemer { + return fromPlutusJson(PlutusData.fromDataHex(data)); + } + + export function toDataHex(data: FactoryRedeemer): CborHex { + return PlutusData.toDataHex(toPlutusJson(data)); + } + } // ============================================================================ // Swap Direction @@ -323,13 +447,19 @@ export namespace WingridersV2 { projectTreasuryB: bigint; reserveTreasuryA: bigint; reserveTreasuryB: bigint; + projectBeneficiary: Maybe
; + reserveBeneficiary: Maybe
; + // Opaque tail field — Constr 0 [] for ConstantProduct; parameters for Stableswap. + poolSpecifics: PlutusData; }; export namespace PoolDatum { - export function fromPlutusJson(data: PlutusData): PoolDatum { - const { fields } = PlutusConstr.unwrap(data, { [0]: 18 }); + export function fromPlutusJson(data: PlutusData, networkEnv: NetworkEnvironment): PoolDatum { + const { fields } = PlutusConstr.unwrap(data, { [0]: 21 }); const assetA = new Asset(Bytes.fromHex(PlutusBytes.unwrap(fields[1])), Bytes.fromHex(PlutusBytes.unwrap(fields[2]))); const assetB = new Asset(Bytes.fromHex(PlutusBytes.unwrap(fields[3])), Bytes.fromHex(PlutusBytes.unwrap(fields[4]))); + const projectBeneficiaryData = PlutusMaybe.unwrap(fields[18]); + const reserveBeneficiaryData = PlutusMaybe.unwrap(fields[19]); return { reqValidatorHash: Bytes.fromHex(PlutusBytes.unwrap(fields[0])), @@ -348,6 +478,13 @@ export namespace WingridersV2 { projectTreasuryB: PlutusInt.unwrapToBigInt(fields[15]), reserveTreasuryA: PlutusInt.unwrapToBigInt(fields[16]), reserveTreasuryB: PlutusInt.unwrapToBigInt(fields[17]), + projectBeneficiary: Maybe.isNothing(projectBeneficiaryData) + ? null + : Address.fromPlutusJson(projectBeneficiaryData, networkEnv), + reserveBeneficiary: Maybe.isNothing(reserveBeneficiaryData) + ? null + : Address.fromPlutusJson(reserveBeneficiaryData, networkEnv), + poolSpecifics: fields[20], }; } @@ -373,13 +510,16 @@ export namespace WingridersV2 { PlutusInt.wrap(datum.projectTreasuryB), PlutusInt.wrap(datum.reserveTreasuryA), PlutusInt.wrap(datum.reserveTreasuryB), + PlutusMaybe.wrap(Maybe.isJust(datum.projectBeneficiary) ? datum.projectBeneficiary.toPlutusJson() : null), + PlutusMaybe.wrap(Maybe.isJust(datum.reserveBeneficiary) ? datum.reserveBeneficiary.toPlutusJson() : null), + datum.poolSpecifics, ], }; } - export function fromCborHex(data: CborHex): PoolDatum { + export function fromCborHex(data: CborHex, networkEnv: NetworkEnvironment): PoolDatum { const plutusData = PlutusData.fromDataHex(data); - return fromPlutusJson(plutusData); + return fromPlutusJson(plutusData, networkEnv); } export function toCborHex(data: PoolDatum): CborHex { @@ -405,6 +545,9 @@ export namespace WingridersV2 { projectTreasuryB: datum.projectTreasuryB, reserveTreasuryA: datum.reserveTreasuryA, reserveTreasuryB: datum.reserveTreasuryB, + projectBeneficiary: datum.projectBeneficiary, + reserveBeneficiary: datum.reserveBeneficiary, + poolSpecifics: datum.poolSpecifics, }; } } @@ -420,23 +563,19 @@ export namespace WingridersV2 { }; /** - * Compute LP asset using blake2b hash - * poolTypeId: 0, aScale: 1, bScale: 1 - * tokenNameHash = blake2b(blake2b("0") + blake2b("1") + blake2b("1") + blake2b(assetA) + blake2b(assetB)) - * lpAsset = lpPolicyId + blake2b(tokenNameHash) + * Compute LP asset using blake2b hash (ConstantProduct, aScale = bScale = 1). + * tokenName = blake2b(blake2b("0") + blake2b("1") + blake2b("1") + blake2b(assetA) + blake2b(assetB)) + * lpAsset = (dexSymbolHash, tokenName) — dexSymbolHash is per-network (see WingridersV2Warehouse). */ - export function computeLpAsset(assetA: Asset, assetB: Asset): Asset { + export function computeLpAsset(dexSymbolHash: Bytes, assetA: Asset, assetB: Asset): Asset { const tokenNameHash = new Bytes(blake2b(Bytes.fromString("0").bytes, undefined, 32)) .concat(new Bytes(blake2b(Bytes.fromString("1").bytes, undefined, 32))) .concat(new Bytes(blake2b(Bytes.fromString("1").bytes, undefined, 32))) .concat(new Bytes(blake2b(assetA.currencySymbol.concat(assetA.tokenName).bytes, undefined, 32))) .concat(new Bytes(blake2b(assetB.currencySymbol.concat(assetB.tokenName).bytes, undefined, 32))).bytes; - return new Asset(Bytes.fromHex(LP_POLICY_ID), new Bytes(blake2b(tokenNameHash, undefined, 32))); + return new Asset(dexSymbolHash, new Bytes(blake2b(tokenNameHash, undefined, 32))); } - export const FACTORY_ASSET = new Asset(Bytes.fromHex(FACTORY_POLICY_ID), Bytes.fromHex(FACTORY_ASSET_NAME)); - export const VALIDITY_ASSET = new Asset(Bytes.fromHex(FACTORY_POLICY_ID), Bytes.fromHex(VALIDITY_ASSET_NAME)); - export class Pool extends BaseUtxoModel { readonly datum: PoolDatum; readonly networkEnv: NetworkEnvironment; @@ -445,7 +584,7 @@ export namespace WingridersV2 { const { output } = utxo; const { address, value } = output; super(utxo.input, address, value, rawDatum); - this.datum = PoolDatum.fromCborHex(rawDatum); + this.datum = PoolDatum.fromCborHex(rawDatum, networkEnv); this.networkEnv = networkEnv; } @@ -458,7 +597,11 @@ export namespace WingridersV2 { } get lpAsset(): Asset { - return computeLpAsset(this.datum.assetA, this.datum.assetB); + return computeLpAsset( + WingridersV2Warehouse.getInstance(this.networkEnv).dexSymbolHash, + this.datum.assetA, + this.datum.assetB, + ); } /** @@ -468,7 +611,7 @@ export namespace WingridersV2 { const rawReserve = this.value.get(this.assetA) - this.datum.treasuryA - this.datum.projectTreasuryA - this.datum.reserveTreasuryA; if (this.assetA.equals(ADA)) { - return rawReserve - MIN_POOL_ADA; + return rawReserve - WingridersV2Warehouse.MIN_POOL_ADA; } return rawReserve; } @@ -485,7 +628,7 @@ export namespace WingridersV2 { */ get liquidity(): bigint { const burnedShareTokens = this.value.get(this.lpAsset); - return MAX_LP_TOKENS - burnedShareTokens; + return WingridersV2Warehouse.MAX_LP_TOKENS - burnedShareTokens; } get pair(): [Asset, Asset] { @@ -505,9 +648,13 @@ export namespace WingridersV2 { try { const { output } = utxo; const { value } = output; - invariant(output.address.toScriptHash()?.hex === POOL_SCRIPT_HASH, `WingridersV2 Pool must have correct script hash`); + const warehouse = WingridersV2Warehouse.getInstance(networkEnv); + invariant( + output.address.toScriptHash()?.hex === warehouse.poolScriptHashCP.hex, + `WingridersV2 Pool must have correct script hash`, + ); invariant(Maybe.isJust(output.datumSource), `WingridersV2 Pool must have datum`); - invariant(value.has(VALIDITY_ASSET), `WingridersV2 Pool must have validity token`); + invariant(value.has(warehouse.validityAsset), `WingridersV2 Pool must have validity token`); const pool = new Pool({ utxo, @@ -537,6 +684,13 @@ export namespace WingridersV2 { } export namespace OrderRedeemer { + export function apply(poolInputLocation: bigint): PlutusData { + return { + constructor: OrderRedeemerType.Apply, + fields: [PlutusInt.wrap(poolInputLocation)], + }; + } + export function cancel(): PlutusData { return { constructor: OrderRedeemerType.Reclaim, @@ -544,4 +698,59 @@ export namespace WingridersV2 { }; } } + + // ============================================================================ + // Pool Redeemer + // ============================================================================ + + export enum PoolRedeemerType { + Evolve = 0, + EmergencyWithdrawal = 1, + ChangeFees = 2, + ChangeAgentFee = 3, + } + + export type EvolveRedeemer = { + poolLocation: bigint; + agentLocation: bigint; + /** For CP simple swaps pass `{ int: "0" }` (matches mainnet convention). Haskell type is `(Int, Data)`. */ + requestLocations: Array<{ inputIndex: bigint; extraData: PlutusData }>; + }; + + export namespace PoolRedeemer { + export function evolve(r: EvolveRedeemer): PlutusData { + return { + constructor: PoolRedeemerType.Evolve, + fields: [ + PlutusInt.wrap(r.poolLocation), + PlutusInt.wrap(r.agentLocation), + { + list: r.requestLocations.map((rl) => ({ + constructor: 0, + fields: [PlutusInt.wrap(rl.inputIndex), rl.extraData], + })), + }, + ], + }; + } + } + + // ============================================================================ + // Validity Minting Policy Redeemer + // ============================================================================ + + export enum ValidityRedeemerType { + MintFirstFactory = 0, + MintNewPool = 1, + } + + export namespace ValidityRedeemer { + export function mintFirstFactory(): PlutusData { + return { constructor: ValidityRedeemerType.MintFirstFactory, fields: [] }; + } + + export function mintNewPool(): PlutusData { + return { constructor: ValidityRedeemerType.MintNewPool, fields: [] }; + } + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7b89023..29ec290 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,6 +32,12 @@ importers: '@cardano-ogmios/client': specifier: ^6.11.0 version: 6.14.0 + '@minswap/felis-build-tx': + specifier: workspace:* + version: link:../../packages/minswap-build-tx + '@minswap/felis-cip': + specifier: workspace:* + version: link:../../packages/cip '@minswap/felis-dex-v2': specifier: workspace:* version: link:../../packages/minswap-dex-v2 @@ -56,6 +62,9 @@ importers: '@minswap/felis-tx-builder': specifier: workspace:* version: link:../../packages/tx-builder + '@minswap/felis-wingriders-v2': + specifier: workspace:* + version: link:../../packages/wingriders-v2 '@minswap/tiny-invariant': specifier: ^1.2.0 version: 1.2.0 @@ -75,6 +84,9 @@ importers: dpdm: specifier: ^3.15.1 version: 3.15.1 + tsx: + specifier: ^4.19.4 + version: 4.20.3 typescript: specifier: 5.8.2 version: 5.8.2 @@ -422,6 +434,9 @@ importers: '@minswap/felis-tx-builder': specifier: workspace:* version: link:../tx-builder + '@minswap/felis-wingriders-v2': + specifier: workspace:* + version: link:../wingriders-v2 '@minswap/tiny-invariant': specifier: ^1.2.0 version: 1.2.0 @@ -622,6 +637,9 @@ importers: '@minswap/tiny-invariant': specifier: ^1.2.0 version: 1.2.0 + json-bigint: + specifier: ^1.0.0 + version: 1.0.0 remeda: specifier: ^2.19.2 version: 2.28.0 From a210533b9806f7ebbcb5440b2a3fa902380e58f3 Mon Sep 17 00:00:00 2001 From: tony Date: Thu, 30 Apr 2026 19:28:23 +0700 Subject: [PATCH 2/8] s1 wip wip --- apps/example/src/s1.ts | 986 ++++++++++++++++++ biome.json | 2 + packages/minswap-build-tx/package.json | 1 + packages/minswap-build-tx/src/index.ts | 1 + .../minswap-build-tx/src/sundaeswap-v1.ts | 348 +++++++ .../test/sundaeswap-v1.test.ts | 276 +++++ packages/sundaeswap-v1/src/index.ts | 1 + .../src/scripts/mainnet-scripts.json | 15 + .../src/scripts/preprod-scripts.json | 17 + packages/sundaeswap-v1/src/sundaeswap-v1.ts | 217 +++- packages/sundaeswap-v1/src/warehouse.ts | 133 +++ packages/sundaeswap-v1/test/sample.test.ts | 122 ++- packages/tx-builder/src/tx-builder.ts | 6 + packages/wingriders-v2/src/index.ts | 4 +- packages/wingriders-v2/src/utils.ts | 2 +- packages/wingriders-v2/src/wingriders-v2.ts | 39 +- pnpm-lock.yaml | 3 + 17 files changed, 2131 insertions(+), 42 deletions(-) create mode 100644 apps/example/src/s1.ts create mode 100644 packages/minswap-build-tx/src/sundaeswap-v1.ts create mode 100644 packages/minswap-build-tx/test/sundaeswap-v1.test.ts create mode 100644 packages/sundaeswap-v1/src/scripts/mainnet-scripts.json create mode 100644 packages/sundaeswap-v1/src/scripts/preprod-scripts.json create mode 100644 packages/sundaeswap-v1/src/warehouse.ts diff --git a/apps/example/src/s1.ts b/apps/example/src/s1.ts new file mode 100644 index 0000000..5197169 --- /dev/null +++ b/apps/example/src/s1.ts @@ -0,0 +1,986 @@ +import { + ADA, + Address, + Asset, + Bytes, + DatumSourceType, + NetworkEnvironment, + TxIn, + TxOut, + type Utxo, + Value, + XJSON, +} from "@minswap/felis-ledger-core"; +import { baseAddressWalletFromSeed } from "@minswap/felis-cip"; +import { Maybe, RustModule } from "@minswap/felis-ledger-utils"; +import { CardanoscanProvider, KupoService } from "@minswap/felis-provider"; +import { SundaeSwapV1 as SundaeSwapV1Build } from "@minswap/felis-build-tx"; +import { + SundaeSwapV1, + SundaeSwapV1Warehouse, +} from "@minswap/felis-sundaeswap-v1"; +import { + CoinSelectionAlgorithm, + ECSLConverter, + EmulatorProvider, + TxBuilder, +} from "@minswap/felis-tx-builder"; +import invariant from "@minswap/tiny-invariant"; + +// ─── CLI plumbing ────────────────────────────────────────────────────────── + +const DEFAULT_NETWORK = "testnet-preprod"; + +function parseNetwork(s: string | undefined): NetworkEnvironment { + switch (s ?? DEFAULT_NETWORK) { + case "mainnet": + return NetworkEnvironment.MAINNET; + case "testnet-preview": + return NetworkEnvironment.TESTNET_PREVIEW; + case "testnet-preprod": + return NetworkEnvironment.TESTNET_PREPROD; + default: + throw new Error( + `--network must be one of mainnet|testnet-preprod|testnet-preview (got ${s})`, + ); + } +} + +function resolveKupoUrl(network: NetworkEnvironment): string { + switch (network) { + case NetworkEnvironment.MAINNET: + return process.env["KUPO_MAINNET_URL"] ?? "http://mainnet-staging:1442"; + case NetworkEnvironment.TESTNET_PREPROD: + return process.env["KUPO_PREPROD_URL"] ?? "http://testnet-preprod:1442"; + case NetworkEnvironment.TESTNET_PREVIEW: + return process.env["KUPO_PREVIEW_URL"] ?? "http://dev-3:1442"; + } +} + +function parseFlags(argv: string[]): Record { + const m: Record = {}; + for (let i = 0; i < argv.length; i++) { + const key = argv[i]; + if (!key.startsWith("--")) + throw new Error(`Unexpected positional arg: ${key}`); + const val = argv[i + 1]; + if (val === undefined) throw new Error(`Missing value for ${key}`); + m[key.slice(2)] = val; + i++; + } + return m; +} + +// ─── Use case: info ──────────────────────────────────────────────────────── + +function runInfo(argv: string[]): void { + const m = parseFlags(argv); + const network = parseNetwork(m["network"]); + const w = SundaeSwapV1Warehouse.getInstance(network); + console.log( + XJSON.stringify( + { + network, + factory: { + mintScriptHash: w.factoryMintScriptHash.hex, + spendScriptHash: w.factorySpendScriptHash.hex, + address: w.factoryAddress.bech32, + authenAsset: w.factoryAuthenAsset.toString(), + }, + pool: { + mintScriptHash: w.poolMintScriptHash.hex, + spendScriptHash: w.poolSpendScriptHash.hex, + address: w.poolAddress.bech32, + }, + order: { + spendScriptHash: w.orderSpendScriptHash.hex, + address: w.orderAddress.bech32, + }, + constants: { + POOL_NFT_NAME_PREFIX: SundaeSwapV1Warehouse.POOL_NFT_NAME_PREFIX, + LP_TOKEN_NAME_PREFIX: SundaeSwapV1Warehouse.LP_TOKEN_NAME_PREFIX, + FACTORY_TOKEN_NAME_HEX: SundaeSwapV1Warehouse.FACTORY_TOKEN_NAME_HEX, + MIN_LOVELACE: SundaeSwapV1Warehouse.MIN_LOVELACE.toString(), + WEEK_MS: SundaeSwapV1Warehouse.WEEK_MS.toString(), + DEFAULT_FEE: `${SundaeSwapV1Warehouse.DEFAULT_FEE_NUM}/${SundaeSwapV1Warehouse.DEFAULT_FEE_DEN}`, + }, + }, + 2, + ), + ); +} + +// ─── Use case: create pool ───────────────────────────────────────────────── + +type CreatePoolFlags = { + network: NetworkEnvironment; + owner: Address; + assetA: Asset; + amountA: bigint; + assetB: Asset; + amountB: bigint; + feeNum?: bigint; + feeDen?: bigint; +}; + +function parseCreatePoolFlags(argv: string[]): CreatePoolFlags { + const m = parseFlags(argv); + for (const r of ["owner", "asset-a", "amount-a", "asset-b", "amount-b"]) { + invariant(m[r] !== undefined, `--${r} is required`); + } + return { + network: parseNetwork(m["network"]), + owner: Address.fromBech32(m["owner"]), + assetA: Asset.fromString(m["asset-a"]), + amountA: BigInt(m["amount-a"]), + assetB: Asset.fromString(m["asset-b"]), + amountB: BigInt(m["amount-b"]), + feeNum: m["fee-num"] !== undefined ? BigInt(m["fee-num"]) : undefined, + feeDen: m["fee-den"] !== undefined ? BigInt(m["fee-den"]) : undefined, + }; +} + +async function runCreatePool(argv: string[]): Promise { + const flags = parseCreatePoolFlags(argv); + + // Sort canonically by [policy, name] bytes (matches on-chain factory check). + const [assetA, assetB, amountA, amountB] = + flags.assetA.compare(flags.assetB) <= 0 + ? [flags.assetA, flags.assetB, flags.amountA, flags.amountB] + : [flags.assetB, flags.assetA, flags.amountB, flags.amountA]; + + const warehouse = SundaeSwapV1Warehouse.getInstance(flags.network); + const kupo = new KupoService(resolveKupoUrl(flags.network)); + + // 1) Locate the factory UTxO. + const factoryUtxos = await kupo.utxoAtScriptHashWithAsset( + warehouse.factorySpendScriptHash, + warehouse.factoryAuthenAsset, + ); + invariant( + factoryUtxos.length > 0, + `No factory UTxO at ${warehouse.factoryAddress.bech32} holding ${warehouse.factoryAuthenAsset.toString()} — has the factory been bootstrapped on this network?`, + ); + invariant( + factoryUtxos.length === 1, + `Expected exactly 1 factory UTxO, found ${factoryUtxos.length}`, + ); + const factoryInput = factoryUtxos[0]; + + // 2) Resolve the factory datum. Kupo returns OUTLINE_DATUM for V1-style + // datum-hash outputs, which carries both the hash and the resolved body + // inline — no extra `/datums/` round-trip needed. + invariant( + Maybe.isJust(factoryInput.output.datumSource) && + factoryInput.output.datumSource.type === DatumSourceType.OUTLINE_DATUM, + "Factory UTxO must carry a datum hash with resolved data (OUTLINE_DATUM)", + ); + const factoryDatum = SundaeSwapV1.FactoryDatum.fromDataHex( + factoryInput.output.datumSource.data.hex, + ); + + // 3) Fetch wallet UTxOs. + const paymentCredential = flags.owner.toPaymentCredential(); + invariant( + Maybe.isJust(paymentCredential), + "owner address has no payment credential", + ); + const walletUtxos = await kupo.getUtxosByPaymentCredential( + paymentCredential.payload, + ); + invariant(walletUtxos.length > 0, `Owner ${flags.owner.bech32} has no UTxOs`); + + // 4) Build + complete (dry-run with EmulatorProvider — no submit). + const txBuilder: TxBuilder = SundaeSwapV1Build.buildCreatePool({ + networkEnv: flags.network, + owner: flags.owner, + factoryInput, + factoryDatum, + assetA, + amountA, + assetB, + amountB, + feeNum: flags.feeNum, + feeDen: flags.feeDen, + }); + const result = await txBuilder.complete({ + walletUtxos, + coinSelectionAlgorithm: CoinSelectionAlgorithm.MINSWAP, + provider: new EmulatorProvider(flags.network), + changeAddress: flags.owner, + }); + if (result.type !== "ok") { + console.error("complete() failed:", result.error); + process.exit(1); + } + + const ident = factoryDatum.nextPoolIdent; + const nextIdentHex = SundaeSwapV1.incrementIdentLE(ident.hex); + const lpSupply = SundaeSwapV1.isqrt(amountA * amountB); + const cborHex = result.value.complete(); + console.log( + XJSON.stringify( + { + factoryInput: `${factoryInput.input.txId.hex}#${factoryInput.input.index}`, + ident: ident.hex, + nextIdent: nextIdentHex, + poolNftAsset: warehouse.poolNftAsset(ident).toString(), + lpAsset: warehouse.lpAsset(ident).toString(), + lpSupply: lpSupply.toString(), + normalizedPair: { + assetA: assetA.toString(), + amountA: amountA.toString(), + assetB: assetB.toString(), + amountB: amountB.toString(), + }, + }, + 2, + ), + ); + console.log("\n--- unsigned tx cbor ---"); + console.log(cborHex); +} + +// ─── Use case: batch (apply one swap order to a pool) ───────────────────── + +type BatchFlags = { + network: NetworkEnvironment; + scooper: Address; + dry: boolean; + loopMs: bigint | null; +}; + +const DEFAULT_SCOOPER_BY_NETWORK: Partial> = + { + // No defaults yet — pass --scooper. + }; + +// On-chain license token name = "scooper " + suffix, where the prefix is +// 8 ASCII bytes (16 hex). We split here to recover the suffix. +const SCOOPER_LICENSE_NAME_PREFIX_HEX = "73636f6f70657220"; // "scooper " + +function parseBatchFlags(argv: string[]): BatchFlags { + const m: Record = {}; + let dry = false; + let loopMs: bigint | null = null; + for (let i = 0; i < argv.length; i++) { + const key = argv[i]; + if (!key.startsWith("--")) + throw new Error(`Unexpected positional arg: ${key}`); + if (key === "--dry") { + dry = true; + continue; + } + if (key === "--loop") { + const next = argv[i + 1]; + if (next !== undefined && !next.startsWith("--")) { + loopMs = BigInt(next); + i++; + } else { + loopMs = 20_000n; + } + continue; + } + const val = argv[i + 1]; + if (val === undefined) throw new Error(`Missing value for ${key}`); + m[key.slice(2)] = val; + i++; + } + const network = parseNetwork(m["network"]); + const scooperBech32 = m["scooper"] ?? DEFAULT_SCOOPER_BY_NETWORK[network]; + invariant(scooperBech32, "--scooper is required"); + return { + network, + scooper: Address.fromBech32(scooperBech32), + dry, + loopMs, + }; +} + +type SwapOrderCandidate = { + utxo: Utxo; + datum: SundaeSwapV1.OrderDatum; +}; + +type ParsedPool = { + utxo: Utxo; + datum: SundaeSwapV1.PoolDatum; +}; + +/** + * V1 swap math (Uniswap V2 with integer fee fraction `feeNum/feeDen`): + * + * amountInWithFee = amountIn * (feeDen − feeNum) + * amountOut = amountInWithFee * reserveOut / (reserveIn * feeDen + amountInWithFee) + * + * Reserves are read directly from the pool UTxO value (the pool NFT and any + * tiny min-ada surplus aren't part of the reserve — they ride along). + * + * Compensation paid to the order's beneficiary: + * - amountOut of assetOut + * - the user's deposit (whatever ADA was attached to the order minus the + * scooperFee, and minus amountIn if assetIn was ADA) + */ +type ScoopPlan = { + compensation: Value; + newPoolValue: Value; + amountOut: bigint; + assetIn: Asset; + assetOut: Asset; + amountIn: bigint; +}; + +function planSwap(pool: ParsedPool, order: SwapOrderCandidate): ScoopPlan { + const aToB = + order.datum.swapDirection.direction === SundaeSwapV1.SwapDirection.A_TO_B; + const assetIn = aToB ? pool.datum.assetA : pool.datum.assetB; + const assetOut = aToB ? pool.datum.assetB : pool.datum.assetA; + + const amountIn = order.datum.swapDirection.amount; + const reserveIn = pool.utxo.output.value.get(assetIn); + const reserveOut = pool.utxo.output.value.get(assetOut); + + const feeNum = BigInt(pool.datum.tradingFee[0]); + const feeDen = BigInt(pool.datum.tradingFee[1]); + const amountInWithFee = amountIn * (feeDen - feeNum); + const numerator = amountInWithFee * reserveOut; + const denominator = reserveIn * feeDen + amountInWithFee; + const amountOut = numerator / denominator; + + const minReceivable = order.datum.swapDirection.minReceivable ?? 0n; + invariant( + amountOut >= minReceivable, + `slippage: amountOut ${amountOut} < minReceivable ${minReceivable}`, + ); + + // Compensation ADA = orderAda − scooperFee, with the ADA leg of the swap + // applied: subtract amountIn when assetIn==ADA (user's ADA went into pool), + // add amountOut when assetOut==ADA (user receives ADA from pool). + // For example, with order=6.5 ADA, scooperFee=2.5, amountIn=2 ADA → user + // gets 2 ADA back as deposit refund. + const orderAda = order.utxo.output.value.get(ADA); + const scooperFee = order.datum.scooperFee; + let compAda = orderAda - scooperFee; + if (assetIn.equals(ADA)) compAda -= amountIn; + if (assetOut.equals(ADA)) compAda += amountOut; + invariant( + compAda >= 0n, + `order does not have enough ADA to cover scooperFee + ADA-leg (orderAda=${orderAda}, scooperFee=${scooperFee}, amountIn=${amountIn})`, + ); + + const compensation = new Value().add(ADA, compAda); + if (!assetOut.equals(ADA)) compensation.add(assetOut, amountOut); + const newPoolValue = pool.utxo.output.value + .clone() + .add(assetIn, amountIn) + .subtract(assetOut, amountOut); + + return { compensation, newPoolValue, amountOut, assetIn, assetOut, amountIn }; +} + +async function runBatch(argv: string[]): Promise { + const flags = parseBatchFlags(argv); + const warehouse = SundaeSwapV1Warehouse.getInstance(flags.network); + const kupo = new KupoService(resolveKupoUrl(flags.network)); + + const runOnce = async (): Promise => { + const debugData: Record = { + network: NetworkEnvironment[flags.network], + scooper: flags.scooper.bech32, + dry: flags.dry, + }; + try { + await runBatchInner(flags, warehouse, kupo, debugData); + } catch (err) { + console.error("\n--- batch failed; debugData ---"); + console.error(XJSON.stringify(debugData, 2)); + throw err; + } + }; + + if (flags.loopMs === null) { + await runOnce(); + return; + } + + const intervalMs = Number(flags.loopMs); + for (let iteration = 1; ; iteration++) { + const startedAt = Date.now(); + console.log( + `\n=== batch iteration ${iteration} @ ${new Date(startedAt).toISOString()} ===`, + ); + try { + await runOnce(); + } catch (err) { + console.error(`iteration ${iteration} errored; continuing loop:`, err); + } + const elapsedMs = Date.now() - startedAt; + const sleepMs = Math.max(0, intervalMs - elapsedMs); + console.log( + `iteration ${iteration} took ${elapsedMs}ms; sleeping ${sleepMs}ms`, + ); + await new Promise((resolve) => setTimeout(resolve, sleepMs)); + } +} + +async function runBatchInner( + flags: BatchFlags, + warehouse: SundaeSwapV1Warehouse, + kupo: KupoService, + debugData: Record, +): Promise { + // 1) All UTxOs at the pool spend address that hold something under the pool + // mint policy (i.e. carry a pool NFT). Filter to ones with a parseable + // PoolDatum. + const poolUtxos = await kupo.utxoAtScriptHashWithPolicyId( + warehouse.poolSpendScriptHash, + warehouse.poolMintScriptHash, + ); + console.log(`Found ${poolUtxos.length} pool-spend UTxOs`); + const pools: ParsedPool[] = []; + for (const utxo of poolUtxos) { + if ( + !utxo.output.datumSource || + utxo.output.datumSource.type !== DatumSourceType.OUTLINE_DATUM + ) + continue; + try { + pools.push({ + utxo, + datum: SundaeSwapV1.PoolDatum.fromCborHex( + utxo.output.datumSource.data.hex, + ), + }); + } catch { + // Skip non-pool UTxOs that happen to live at the pool address. + } + } + console.log(`Parsed ${pools.length} pool datums`); + debugData.poolUtxosCount = poolUtxos.length; + debugData.parsedPoolsCount = pools.length; + + // 2) All UTxOs at the order spend address. + const orderUtxos = await kupo.utxosByAddress([warehouse.orderAddress]); + console.log(`Found ${orderUtxos.length} order UTxOs`); + debugData.orderUtxosCount = orderUtxos.length; + + // 3) Pick the first parseable swap order whose poolIdent matches a pool we found. + const skipCounts: Record = { + notOutlineDatum: 0, + badDatum: 0, + nonPubKeyBeneficiary: 0, + noMatchingPool: 0, + }; + const skipSamples: Array<{ ref: string; reason: string; detail?: unknown }> = + []; + const noteSkip = ( + utxo: Utxo, + reason: keyof typeof skipCounts, + detail?: unknown, + ) => { + skipCounts[reason]++; + if (skipSamples.length < 5) { + skipSamples.push({ + ref: `${utxo.input.txId.hex}#${utxo.input.index}`, + reason, + detail, + }); + } + }; + + let match: { order: SwapOrderCandidate; pool: ParsedPool } | null = null; + for (const utxo of orderUtxos) { + if ( + !utxo.output.datumSource || + utxo.output.datumSource.type !== DatumSourceType.OUTLINE_DATUM + ) { + noteSkip(utxo, "notOutlineDatum"); + continue; + } + let datum: SundaeSwapV1.OrderDatum; + try { + datum = SundaeSwapV1.OrderDatum.fromDataHex( + utxo.output.datumSource.data.hex, + flags.network, + ); + } catch (err) { + noteSkip(utxo, "badDatum", String(err)); + continue; + } + if ( + Maybe.isNothing(datum.orderAddresses.destination.address.toPubKeyHash()) + ) { + noteSkip(utxo, "nonPubKeyBeneficiary", { + beneficiary: datum.orderAddresses.destination.address.bech32, + }); + continue; + } + const pool = pools.find((p) => p.datum.ident.hex === datum.poolIdent.hex); + if (!pool) { + noteSkip(utxo, "noMatchingPool", { poolIdent: datum.poolIdent.hex }); + continue; + } + match = { order: { utxo, datum }, pool }; + break; + } + debugData.orderFilter = { skipCounts, skipSamples }; + if (!match) { + console.log( + `No valid swap order with a matching pool — skipping. skipCounts=${XJSON.stringify(skipCounts)}`, + ); + return; + } + debugData.matched = { + pool: `${match.pool.utxo.input.txId.hex}#${match.pool.utxo.input.index}`, + order: `${match.order.utxo.input.txId.hex}#${match.order.utxo.input.index}`, + poolIdent: match.pool.datum.ident.hex, + assetA: match.pool.datum.assetA.toString(), + assetB: match.pool.datum.assetB.toString(), + direction: + match.order.datum.swapDirection.direction === + SundaeSwapV1.SwapDirection.A_TO_B + ? "A_TO_B" + : "B_TO_A", + }; + + // 4) Compute execution plan. + const plan = planSwap(match.pool, match.order); + debugData.plan = { + assetIn: plan.assetIn.toString(), + assetOut: plan.assetOut.toString(), + amountIn: plan.amountIn.toString(), + amountOut: plan.amountOut.toString(), + minReceivable: ( + match.order.datum.swapDirection.minReceivable ?? 0n + ).toString(), + }; + + // 5) Find the single valid (non-expired) license UTxO at the scooper address. + // Requirements: exactly 1 token under factoryMintScriptHash, qty=1, no other + // non-ADA assets, token name starts with "scooper " prefix, and the encoded + // week number matches the current POSIX-time week. + const scooperPc = flags.scooper.toPaymentCredential(); + invariant( + Maybe.isJust(scooperPc), + "scooper address has no payment credential", + ); + const scooperUtxos = await kupo.getUtxosByPaymentCredential( + scooperPc.payload, + ); + invariant( + scooperUtxos.length > 0, + `scooper ${flags.scooper.bech32} has no UTxOs`, + ); + + const nowMs = BigInt(Date.now()); + const WEEK_MS = SundaeSwapV1Warehouse.WEEK_MS; + + let scooperLicenseInput: Utxo | null = null; + let licenseAsset: Asset | null = null; + let licenseSuffix: Bytes | null = null; + + for (const utxo of scooperUtxos) { + if (utxo.output.value.hasPolicyID(warehouse.factoryMintScriptHash)) { + scooperLicenseInput = utxo; + const _licenseAsset = utxo.output.value.findAsset( + warehouse.factoryMintScriptHash, + ); + invariant( + _licenseAsset, + "Expected scooper license asset under factory mint script policy", + ); + licenseAsset = _licenseAsset; + const nameHex = licenseAsset.tokenName.hex; + if (!nameHex.startsWith(SCOOPER_LICENSE_NAME_PREFIX_HEX)) continue; + const suffixHex = nameHex.slice(SCOOPER_LICENSE_NAME_PREFIX_HEX.length); + licenseSuffix = Bytes.fromHex(suffixHex); + break; + } + // const allAssets = utxo.output.value.assets(); + // const licenseAssets = allAssets.filter((a) => + // a.currencySymbol.equals(warehouse.factoryMintScriptHash), + // ); + // if (licenseAssets.length !== 1) continue; + // const candidate = licenseAssets[0]; + // if (utxo.output.value.get(candidate) !== 1n) continue; + // if ( + // allAssets.filter((a) => !a.equals(ADA) && !a.equals(candidate)).length !== + // 0 + // ) + // continue; + // const nameHex = candidate.tokenName.hex; + // if (!nameHex.startsWith(SCOOPER_LICENSE_NAME_PREFIX_HEX)) continue; + // const suffixHex = nameHex.slice(SCOOPER_LICENSE_NAME_PREFIX_HEX.length); + // // Parse the week number from the little-endian suffix bytes. + // const suffixBytes = Buffer.from(suffixHex, "hex"); + // let weekNum = 0n; + // for (let i = suffixBytes.length - 1; i >= 0; i--) + // weekNum = (weekNum << 8n) | BigInt(suffixBytes[i]); + // if (nowMs < weekNum * WEEK_MS || nowMs >= (weekNum + 1n) * WEEK_MS) + // continue; // expired + // scooperLicenseInput = utxo; + // licenseAsset = candidate; + // licenseSuffix = Bytes.fromHex(suffixHex); + // break; + } + + console.log("scooperLicenseInput", XJSON.stringify(scooperLicenseInput, 2)); + invariant( + scooperLicenseInput !== null && + licenseAsset !== null && + licenseSuffix !== null, + `scooper ${flags.scooper.bech32} has no valid (non-expired) license token under policy ` + + `${warehouse.factoryMintScriptHash.hex}. Run \`split-scooper-token\` to prepare license UTxOs.`, + ); + debugData.scooperLicenseInput = { + ref: `${scooperLicenseInput.input.txId.hex}#${scooperLicenseInput.input.index}`, + ada: scooperLicenseInput.output.value.get(ADA).toString(), + licenseAsset: licenseAsset.toString(), + licenseSuffix: licenseSuffix.hex, + }; + + // 6) Build + complete (dry-run with EmulatorProvider). + // + // On-chain `PT5` invariant: licenseEscrowAda + txFee == Σ scooperFee. + // That's circular (escrow ADA depends on tx fee, which depends on tx size, + // which depends on escrow ADA). To break the loop we pin the tx fee to a + // conservative 1 ADA via `hardCodedTxFee` and derive + // escrowAda = scooperFee − 1 ADA + // so the on-chain check holds exactly. + const HARDCODED_TX_FEE = 818_370n; + const totalScooperFee = match.order.datum.scooperFee; + invariant( + totalScooperFee > HARDCODED_TX_FEE, + `scooperFee ${totalScooperFee} must exceed hard-coded tx fee ${HARDCODED_TX_FEE}`, + ); + const escrowAda = totalScooperFee - HARDCODED_TX_FEE; + const currentSlot = await kupo.getCurrentSlot(); + const txBuilder = SundaeSwapV1Build.buildScoopTx({ + networkEnv: flags.network, + scooper: flags.scooper, + scooperLicenseInput, + license: licenseAsset, + licenseSuffix, + pool: { + input: match.pool.utxo, + datum: match.pool.datum, + newValue: plan.newPoolValue, + newDatum: match.pool.datum, + }, + order: { + input: match.order.utxo, + datum: match.order.datum, + compensation: plan.compensation, + }, + licenseEscrow: new Value().add(ADA, escrowAda), + currentSlot, + }); + + // Exclude the license UTxO from walletUtxos — it is already forced into + // body.inputs via collectFromPubKey. Passing only the remaining UTxOs lets + // MINSWAP cover escrow + fee cleanly. + const otherScooperUtxos = scooperUtxos.filter( + (u) => !TxIn.equals(u.input, scooperLicenseInput.input), + ); + const isPureAda = (u: Utxo) => + u.output.value.assets().every((a) => a.equals(ADA)); + const pureAdaCollaterals = otherScooperUtxos.filter(isPureAda); + debugData.pureAdaCollateralCount = pureAdaCollaterals.length; + invariant( + pureAdaCollaterals.length > 0, + `scooper ${flags.scooper.bech32} has no pure-ADA UTxO for collateral. ` + + `If you just sent yourself one, wait for kupo to sync (typically <1 min).`, + ); + + const result = await txBuilder.complete({ + walletUtxos: otherScooperUtxos, + walletCollaterals: pureAdaCollaterals, + coinSelectionAlgorithm: CoinSelectionAlgorithm.MINSWAP, + provider: new EmulatorProvider(flags.network), + changeAddress: flags.scooper, + hardCodedTxFee: HARDCODED_TX_FEE, + debug: true, + }); + if (result.type !== "ok") { + debugData.completeError = result.error; + throw new Error(`txBuilder.complete() failed: ${String(result.error)}`); + } + + console.log( + XJSON.stringify( + { + picked: { + pool: `${match.pool.utxo.input.txId.hex}#${match.pool.utxo.input.index}`, + order: `${match.order.utxo.input.txId.hex}#${match.order.utxo.input.index}`, + poolIdent: match.pool.datum.ident.hex, + assetIn: plan.assetIn.toString(), + assetOut: plan.assetOut.toString(), + amountIn: plan.amountIn.toString(), + amountOut: plan.amountOut.toString(), + }, + }, + 2, + ), + ); + + if (flags.dry) { + console.log("\n--- unsigned tx cbor ---"); + console.log(result.value.complete()); + return; + } + + const seedPhrase = process.env["AGENT_SEED_PHRASE"]; + invariant( + seedPhrase, + "AGENT_SEED_PHRASE env var is required when --dry is omitted", + ); + const wallet = baseAddressWalletFromSeed(seedPhrase, flags.network); + invariant( + wallet.address.bech32 === flags.scooper.bech32, + `AGENT_SEED_PHRASE derives ${wallet.address.bech32} but --scooper is ${flags.scooper.bech32}`, + ); + const signedTxHex = result.value + .signWithPrivateKey(wallet.paymentKey) + .complete(); + console.log("\n--- signed tx cbor ---"); + console.log(signedTxHex); + + const cardanoscanKey = process.env["CARDANOSCAN_KEY"]; + invariant( + cardanoscanKey, + "CARDANOSCAN_KEY env var is required to submit (use --dry to skip submit)", + ); + const cardanoscan = CardanoscanProvider.forNetwork( + flags.network, + cardanoscanKey, + ); + await cardanoscan.submitTx(signedTxHex); + const txHash = ECSLConverter.getTxHash( + RustModule.getE.Transaction.from_hex(signedTxHex), + ); + console.log(`\nsubmitted via Cardanoscan, txHash: ${txHash}`); +} + +// ─── Use case: split-scooper-token ───────────────────────────────────────── +// Carve scooper-license tokens (assets minted under factoryMintScriptHash) into +// individual UTxOs, each holding exactly 1 license token + 2 ADA. Mirrors +// w2.ts's split-agent-token, but emits one output per license unit so each +// scoop tx can pick up an isolated license input. + +type SplitScooperFlags = { + network: NetworkEnvironment; + scooper: Address; + dry: boolean; +}; + +function parseSplitScooperFlags(argv: string[]): SplitScooperFlags { + const m: Record = {}; + let dry = false; + for (let i = 0; i < argv.length; i++) { + const key = argv[i]; + if (!key.startsWith("--")) + throw new Error(`Unexpected positional arg: ${key}`); + if (key === "--dry") { + dry = true; + continue; + } + const val = argv[i + 1]; + if (val === undefined) throw new Error(`Missing value for ${key}`); + m[key.slice(2)] = val; + i++; + } + const network = parseNetwork(m["network"]); + const scooperBech32 = m["scooper"] ?? DEFAULT_SCOOPER_BY_NETWORK[network]; + invariant(scooperBech32, "--scooper is required"); + return { network, scooper: Address.fromBech32(scooperBech32), dry }; +} + +async function runSplitScooperToken(argv: string[]): Promise { + const flags = parseSplitScooperFlags(argv); + const warehouse = SundaeSwapV1Warehouse.getInstance(flags.network); + const kupo = new KupoService(resolveKupoUrl(flags.network)); + + const pc = flags.scooper.toPaymentCredential(); + invariant(Maybe.isJust(pc), "scooper address has no payment credential"); + const scooperUtxos = await kupo.getUtxosByPaymentCredential(pc.payload); + invariant( + scooperUtxos.length > 0, + `scooper ${flags.scooper.bech32} has no UTxOs`, + ); + + // Pick UTxOs that hold any asset under factoryMintScriptHash (license policy). + const licenseUtxos = scooperUtxos.filter((u) => + u.output.value + .assets() + .some((a) => a.currencySymbol.equals(warehouse.factoryMintScriptHash)), + ); + invariant( + licenseUtxos.length > 0, + `scooper ${flags.scooper.bech32} holds no asset under license policy ${warehouse.factoryMintScriptHash.hex}`, + ); + + // Sum up license-asset quantities across ALL license-bearing UTxOs. + const totals = new Map(); + for (const u of licenseUtxos) { + for (const asset of u.output.value.assets()) { + if (!asset.currencySymbol.equals(warehouse.factoryMintScriptHash)) + continue; + const key = asset.toString(); + const qty = u.output.value.get(asset); + const prev = totals.get(key); + totals.set(key, { asset, qty: (prev?.qty ?? 0n) + qty }); + } + } + + // Emit one output per license unit: 2 ADA + 1 token. + const outputs: TxOut[] = []; + for (const { asset, qty } of totals.values()) { + for (let i = 0n; i < qty; i++) { + outputs.push( + new TxOut( + flags.scooper, + new Value().add(ADA, 2_000_000n).add(asset, 1n), + ), + ); + } + } + + // Force every license-bearing UTxO into the tx so all licenses are consolidated + // and re-emitted as one-token outputs. Pass ALL scooper UTxOs to MINSWAP so it + // can pull extra pure-ADA inputs to cover fee + min-ada on each split output + // (the license UTxOs alone usually don't carry enough ADA). + const txBuilder = new TxBuilder(flags.network) + .collectFromPubKey(...licenseUtxos) + .payTo(...outputs); + + const result = await txBuilder.complete({ + walletUtxos: scooperUtxos, + coinSelectionAlgorithm: CoinSelectionAlgorithm.MINSWAP, + provider: new EmulatorProvider(flags.network), + changeAddress: flags.scooper, + }); + if (result.type !== "ok") { + console.error("complete() failed:", result.error); + process.exit(1); + } + + console.log( + XJSON.stringify( + { + spentLicenseUtxos: licenseUtxos.length, + licenseTokens: Array.from(totals.values()).map(({ asset, qty }) => ({ + asset: asset.toString(), + qty: qty.toString(), + })), + emittedOutputs: outputs.length, + }, + 2, + ), + ); + + if (flags.dry) { + console.log("\n--- unsigned tx cbor ---"); + console.log(result.value.complete()); + return; + } + + const seedPhrase = process.env["AGENT_SEED_PHRASE"]; + invariant( + seedPhrase, + "AGENT_SEED_PHRASE env var is required when --dry is omitted", + ); + const wallet = baseAddressWalletFromSeed(seedPhrase, flags.network); + invariant( + wallet.address.bech32 === flags.scooper.bech32, + `AGENT_SEED_PHRASE derives ${wallet.address.bech32} but --scooper is ${flags.scooper.bech32}`, + ); + const signedTxHex = result.value + .signWithPrivateKey(wallet.paymentKey) + .complete(); + console.log("\n--- signed tx cbor ---"); + console.log(signedTxHex); + + const cardanoscanKey = process.env["CARDANOSCAN_KEY"]; + invariant( + cardanoscanKey, + "CARDANOSCAN_KEY env var is required to submit (use --dry to skip submit)", + ); + const cardanoscan = CardanoscanProvider.forNetwork( + flags.network, + cardanoscanKey, + ); + await cardanoscan.submitTx(signedTxHex); + const txHash = ECSLConverter.getTxHash( + RustModule.getE.Transaction.from_hex(signedTxHex), + ); + console.log(`\nsubmitted via Cardanoscan, txHash: ${txHash}`); +} + +// ─── dispatch ────────────────────────────────────────────────────────────── + +const USAGE = `\ +SundaeSwap V1 CLI + +Usage: + pnpm tsx src/s1.ts info \\ + [--network ] # default: testnet-preprod + + pnpm tsx src/s1.ts create-pool \\ + [--network ] # default: testnet-preprod \\ + --owner \\ + --asset-a --amount-a \\ + --asset-b --amount-b \\ + [--fee-num ] [--fee-den ] # default: 3/1000 + + pnpm tsx src/s1.ts batch \\ + [--network ] # default: testnet-preprod \\ + --scooper # license token + week suffix are auto-detected from scooper utxos + [--dry] # build only; without --dry, signs with AGENT_SEED_PHRASE + [--loop []] # repeat forever; default interval 20000ms + + # Carve scooper licenses into one-token-per-UTxO (each: 2 ADA + 1 license). + # Required before the first scoop if your licenses are bundled in a multi-asset UTxO. + pnpm tsx src/s1.ts split-scooper-token \\ + [--network ] \\ + --scooper \\ + [--dry] + +Env: + KUPO_MAINNET_URL, KUPO_PREPROD_URL, KUPO_PREVIEW_URL + AGENT_SEED_PHRASE (mnemonic; required for batch without --dry) + CARDANOSCAN_KEY (required for batch without --dry; used to submit) +`; + +const main = async (): Promise => { + await RustModule.load(); + const argv = process.argv.slice(2); + const [subcommand, ...rest] = argv; + switch (subcommand) { + case "info": + runInfo(rest); + break; + case "create-pool": + await runCreatePool(rest); + break; + case "batch": + await runBatch(rest); + break; + case "split-scooper-token": + await runSplitScooperToken(rest); + break; + case undefined: + case "--help": + case "-h": + console.log(USAGE); + break; + default: + console.error(`Unknown subcommand: ${subcommand}`); + console.error(USAGE); + process.exit(1); + } +}; + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/biome.json b/biome.json index 5b43dfe..0734ee3 100644 --- a/biome.json +++ b/biome.json @@ -15,6 +15,8 @@ "packages/minswap-dex-v2/**", "packages/minswap-lending-market/**", "packages/tx-builder/**", + "packages/wingriders-v2/**", + "packages/sundaeswap-v1/**", "apps/web/**", "apps/long-short-backend/src/**" ], diff --git a/packages/minswap-build-tx/package.json b/packages/minswap-build-tx/package.json index aa5aecd..d4d4053 100644 --- a/packages/minswap-build-tx/package.json +++ b/packages/minswap-build-tx/package.json @@ -38,6 +38,7 @@ "@minswap/felis-dex-v2": "workspace:*", "@minswap/felis-ledger-core": "workspace:*", "@minswap/felis-ledger-utils": "workspace:*", + "@minswap/felis-sundaeswap-v1": "workspace:*", "@minswap/felis-tx-builder": "workspace:*", "@minswap/felis-wingriders-v2": "workspace:*", "@minswap/tiny-invariant": "^1.2.0", diff --git a/packages/minswap-build-tx/src/index.ts b/packages/minswap-build-tx/src/index.ts index aa6850c..33b6a32 100644 --- a/packages/minswap-build-tx/src/index.ts +++ b/packages/minswap-build-tx/src/index.ts @@ -1,4 +1,5 @@ export * from "./dex-order-transaction"; export * from "./djed"; export * from "./metadata"; +export * from "./sundaeswap-v1"; export * from "./wingriders-v2"; diff --git a/packages/minswap-build-tx/src/sundaeswap-v1.ts b/packages/minswap-build-tx/src/sundaeswap-v1.ts new file mode 100644 index 0000000..f77abbb --- /dev/null +++ b/packages/minswap-build-tx/src/sundaeswap-v1.ts @@ -0,0 +1,348 @@ +import { + ADA, + type Address, + type Asset, + Bytes, + DatumSource, + DatumSourceType, + type NetworkEnvironment, + PlutusData, + TxOut, + type Utxo, + Value, +} from "@minswap/felis-ledger-core"; +import { Maybe } from "@minswap/felis-ledger-utils"; +import { SundaeSwapV1 as S1, SundaeSwapV1Warehouse } from "@minswap/felis-sundaeswap-v1"; +import { TxBuilder } from "@minswap/felis-tx-builder"; +import invariant from "@minswap/tiny-invariant"; + +export namespace SundaeSwapV1 { + export type BuildCreatePoolOptions = { + networkEnv: NetworkEnvironment; + owner: Address; + /** Factory UTxO carrying `factoryAuthenAsset` (datum-hash style). */ + factoryInput: Utxo; + /** + * Resolved factory datum. The factory output uses a datum hash, so the + * caller must look up the datum body via Kupo (or another provider) and + * pass it in. We embed the body in the witness set to satisfy the input. + */ + factoryDatum: S1.FactoryDatum; + /** Sorted-canonical pair (assetA <= assetB by [policy, name] bytes). */ + assetA: Asset; + amountA: bigint; + assetB: Asset; + amountB: bigint; + /** Trading fee fraction; defaults to 3/1000 (0.3%). */ + feeNum?: bigint; + feeDen?: bigint; + }; + + function isCanonicallySorted(a: Asset, b: Asset): boolean { + return a.compare(b) <= 0; + } + + function newOutlineDatumOut(address: Address, value: Value, datum: PlutusData): TxOut { + return new TxOut(address, value, DatumSource.newOutlineDatum(Bytes.fromHex(PlutusData.toDataHex(datum)))); + } + + /** + * Build a SundaeSwap V1 CreatePool transaction. + * + * Outputs (canonical order, matching mainnet debug3.json): + * [0] continuing factory at warehouse.factoryAddress (datum-hash, new factory datum) + * [1] new pool at warehouse.poolAddress (datum-hash, pool datum) + * [2] LP creator change (handled by coin selection + lp payout) + * + * Mints (under warehouse.poolMintScriptHash): + * +1 pool NFT (name = "p " + ident) + * +lpSupply LP (name = "lp " + ident) with lpSupply = isqrt(amountA * amountB) + * + * The factory.spend script is consumed via inline-script witness and the + * input factory datum is provided via the witness set so its hash resolves. + */ + export function buildCreatePool(options: BuildCreatePoolOptions): TxBuilder { + const { networkEnv, owner, factoryInput, factoryDatum, assetA, amountA, assetB, amountB } = options; + const feeNum = options.feeNum ?? SundaeSwapV1Warehouse.DEFAULT_FEE_NUM; + const feeDen = options.feeDen ?? SundaeSwapV1Warehouse.DEFAULT_FEE_DEN; + + invariant(amountA > 0n && amountB > 0n, "SundaeSwapV1.buildCreatePool: amounts must be positive"); + invariant( + isCanonicallySorted(assetA, assetB), + "SundaeSwapV1.buildCreatePool: assets must be sorted by [policy, name] bytes (assetA <= assetB)", + ); + invariant(feeNum >= 0n && feeDen > 0n, "SundaeSwapV1.buildCreatePool: invalid fee fraction"); + + const warehouse = SundaeSwapV1Warehouse.getInstance(networkEnv); + + invariant( + factoryInput.output.value.has(warehouse.factoryAuthenAsset), + "SundaeSwapV1.buildCreatePool: factory input must contain the factory authen token", + ); + invariant( + Maybe.isJust(factoryInput.output.datumSource) && + (factoryInput.output.datumSource.type === DatumSourceType.DATUM_HASH || + factoryInput.output.datumSource.type === DatumSourceType.OUTLINE_DATUM), + "SundaeSwapV1.buildCreatePool: factory input must use a datum hash (V1 style)", + ); + + // Sanity-check that the supplied factoryDatum hashes to the value carried + // by the input. This prevents subtle silent mismatches if the caller + // passes a stale or wrong datum. + { + const expectedHex = factoryInput.output.datumSource.hash.hex; + const computedHex = PlutusData.hashPlutusData(S1.FactoryDatum.toPlutusJson(factoryDatum)); + invariant( + expectedHex === computedHex, + `SundaeSwapV1.buildCreatePool: factoryDatum does not match input datum hash (expected ${expectedHex}, got ${computedHex})`, + ); + } + + const ident = factoryDatum.nextPoolIdent; + const nextIdentHex = S1.incrementIdentLE(ident.hex); + const nextIdent = Bytes.fromHex(nextIdentHex); + + const poolNftAsset = warehouse.poolNftAsset(ident); + const lpAsset = warehouse.lpAsset(ident); + const lpSupply = S1.isqrt(amountA * amountB); + invariant(lpSupply >= 1n, "SundaeSwapV1.buildCreatePool: initial reserves too small (lpSupply < 1)"); + + const newFactoryDatum: S1.FactoryDatum = { + nextPoolIdent: nextIdent, + proposal: factoryDatum.proposal, + scooperIdent: factoryDatum.scooperIdent, + scoopers: factoryDatum.scoopers, + }; + + const poolDatum: S1.PoolDatum = { + assetA, + assetB, + ident, + liquidity: lpSupply, + tradingFee: [Number(feeNum), Number(feeDen)], + }; + + // ─── Outputs ──────────────────────────────────────────────────────── + const factoryOut = newOutlineDatumOut( + warehouse.factoryAddress, + new Value().add(ADA, SundaeSwapV1Warehouse.MIN_LOVELACE).add(warehouse.factoryAuthenAsset, 1n), + S1.FactoryDatum.toPlutusJson(newFactoryDatum), + ); + + const poolValue = new Value().add(poolNftAsset, 1n); + if (assetA.equals(ADA)) { + poolValue.add(ADA, amountA + SundaeSwapV1Warehouse.MIN_LOVELACE); + poolValue.add(assetB, amountB); + } else if (assetB.equals(ADA)) { + // Sorted order means ADA always lands at index 0; this branch should be + // unreachable, but kept for safety. + poolValue.add(assetA, amountA); + poolValue.add(ADA, amountB + SundaeSwapV1Warehouse.MIN_LOVELACE); + } else { + poolValue.add(ADA, SundaeSwapV1Warehouse.MIN_LOVELACE); + poolValue.add(assetA, amountA); + poolValue.add(assetB, amountB); + } + const poolOut = newOutlineDatumOut(warehouse.poolAddress, poolValue, S1.PoolDatum.toPlutusJson(poolDatum)); + + const lpOut = new TxOut(owner, new Value().add(lpAsset, lpSupply)); + + // ─── Validity range ───────────────────────────────────────────────── + // factory.spend reuses the scooper-license week constant (604_800_000ms). + // Any tx must satisfy [weekStart, weekEnd) on both lower and upper bounds. + // Use a 30-minute window starting ~60s in the past, but bump if it would + // straddle a week boundary. + const SLOT_MS = 1000n; + const WEEK_MS = SundaeSwapV1Warehouse.WEEK_MS; + const nowMs = BigInt(Date.now()); + let validFromMs = ((nowMs - 60_000n) / SLOT_MS) * SLOT_MS; + let validToMs = validFromMs + 30n * 60_000n; + const fromWeek = validFromMs / WEEK_MS; + const toWeek = (validToMs - 1n) / WEEK_MS; + if (fromWeek !== toWeek) { + // If we straddle, pull validToMs back to the end of the from-week minus + // a slot. Caller should retry near the start of a new week if this fires + // close to the boundary. + validToMs = (fromWeek + 1n) * WEEK_MS - SLOT_MS; + invariant( + validToMs > validFromMs, + "SundaeSwapV1.buildCreatePool: validity window collapses at week boundary; retry", + ); + } + // Also guard against validFromMs landing exactly on a week boundary that + // the on-chain check rejects (open interval on either end is unclear). + if (validFromMs % WEEK_MS === 0n) { + validFromMs += SLOT_MS; + } + + // ─── Mints ────────────────────────────────────────────────────────── + const mintValue = new Value().add(poolNftAsset, 1n).add(lpAsset, lpSupply); + + // ─── Redeemers ────────────────────────────────────────────────────── + const factoryRedeemer = S1.FactoryRedeemer.createPool({ assetA, assetB }); + const poolMintRedeemer = S1.PoolMintRedeemer.createPool(ident); + + return new TxBuilder(networkEnv) + .attachValidator({ type: "PlutusV1", script: warehouse.factorySpendScript.hex }) + .attachValidator({ type: "PlutusV1", script: warehouse.poolMintScript.hex }) + .collectFromPlutusContract([factoryInput], factoryRedeemer, S1.FactoryDatum.toPlutusJson(factoryDatum)) + .mintAssets(mintValue, poolMintRedeemer) + .payTo(factoryOut, poolOut, lpOut) + .validFromUnixTime(Number(validFromMs)) + .validToUnixTime(Number(validToMs)) + .addSigner(owner); + } + + /** + * The single order being scooped: the input UTxO at the order validator, + * the pre-resolved order datum (datum-hash style on V1), and the value + * that should be paid back to the order's beneficiary as compensation. + * + * V1 scoops here are intentionally one-order-per-tx — multi-order batching + * is not supported. + */ + export type ScoopOrder = { + input: Utxo; + datum: S1.OrderDatum; + /** Beneficiary compensation (typically swap-out asset + the order's deposit). */ + compensation: Value; + }; + + export type BuildScoopTxOptions = { + networkEnv: NetworkEnvironment; + /** Scooper pubkey address — receives change and signs the tx. */ + scooper: Address; + /** Scooper's pubkey UTxO holding the active license NFT (will be consumed and returned). */ + scooperLicenseInput: Utxo; + /** Active license token (`policy.scooperPrefix||suffix`) — used to build the order redeemer. */ + license: Asset; + /** + * Suffix of the license token name. The on-chain order validator concatenates + * the static `"scooper "` prefix internally, so we only pass the suffix. + */ + licenseSuffix: Bytes; + pool: { + input: Utxo; + datum: S1.PoolDatum; + /** Updated pool value after applying the order. */ + newValue: Value; + /** + * Updated pool datum — pass the SAME datum the input had for swap-only + * scoops (LP doesn't change), or a new datum for deposit/withdraw + * scoops where `liquidity` shifts. + */ + newDatum: S1.PoolDatum; + }; + /** Exactly one order is scooped per tx. */ + order: ScoopOrder; + /** Value for the escrow output (address comes from warehouse.licenseEscrowAddress). */ + licenseEscrow: Value; + currentSlot: number; + validFromMs?: bigint; + validToMs?: bigint; + }; + + /** + * Build a SundaeSwap V1 Scoop transaction for EXACTLY ONE order. + * + * Mirrors the structure observed in the mainnet scoop tx + * `e1615e56f980b2ff86fdaf6d554bdbb347cd3b249250334b3d189c3b9ac6ec2a`: + * + * inputs: [scooper-pubkey..., poolInput, orderInput] + * outputs: [poolOut, licenseEscrowOut, beneficiaryCompensationOut, licenseReturnOut, scooper-change] + * redeemers: + * pool.spend = Scoop (Constr 0 [scooperPkh, licenseSuffix]) + * order.spend = Scoop (Constr 0 []) + */ + export function buildScoopTx(options: BuildScoopTxOptions): TxBuilder { + const { + networkEnv, + scooper, + scooperLicenseInput, + license, + licenseSuffix, + pool, + order, + licenseEscrow, + currentSlot, + } = options; + + const warehouse = SundaeSwapV1Warehouse.getInstance(networkEnv); + + invariant( + scooperLicenseInput.output.value.has(license), + `SundaeSwapV1.buildScoopTx: scooperLicenseInput must hold the license token ${license.toString()}`, + ); + invariant( + pool.input.output.address.bech32 === warehouse.poolAddress.bech32, + "SundaeSwapV1.buildScoopTx: pool input must sit at the pool spend address", + ); + + const scooperPkh = scooper.toPaymentCredential(); + invariant(Maybe.isJust(scooperPkh), "SundaeSwapV1.buildScoopTx: scooper address must have a payment credential"); + + // ─── Validity range ───────────────────────────────────────────────── + // Scoop redeemers must run inside the current scooper-license week window. + // Default to a small recent slot range; the caller can override. + const SLOT_MS = 1000n; + const nowMs = BigInt(Date.now()); + const validFromMs = options.validFromMs ?? ((nowMs - 60_000n) / SLOT_MS) * SLOT_MS; + const _validToMs = options.validToMs ?? validFromMs + 30n * 60_000n; + + // ─── Outputs ──────────────────────────────────────────────────────── + const poolOut = new TxOut( + pool.input.output.address, + pool.newValue, + DatumSource.newOutlineDatum(Bytes.fromHex(S1.PoolDatum.toCborHex(pool.newDatum))), + ); + + const escrowOut = new TxOut( + warehouse.licenseEscrowAddress, + licenseEscrow, + DatumSource.newOutlineDatum( + Bytes.fromHex( + PlutusData.toDataHex(S1.LicenseDatum.toPlutusJson({ scooperPkh: scooperPkh.payload, licenseSuffix })), + ), + ), + ); + + // The order's deposit refund can fall below min-utxo when the beneficiary's + // assetOut is a multi-asset output. Top up to min-ada from scooper change + // (same pattern as WingRiders V2 buildBatchTx's compensationOut). + const compensationOut = new TxOut( + order.datum.orderAddresses.destination.address, + order.compensation, + ).addMinimumADAIfRequired(networkEnv); + + // License is returned intact (2 ADA + 1 token) so the scooper UTxO set + // remains stable across batches. The escrow and fee are covered by other + // scooper UTxOs fed via walletUtxos in the .complete() call. + const licenseReturnOut = new TxOut( + scooper, + new Value().add(ADA, SundaeSwapV1Warehouse.MIN_LOVELACE).add(license, 1n), + ); + + // ─── Redeemers ────────────────────────────────────────────────────── + const poolRedeemer = S1.PoolRedeemer.scoop(scooperPkh.payload, licenseSuffix); + const orderRedeemer = S1.OrderRedeemer.scoop(); + + return ( + new TxBuilder(networkEnv) + .attachValidator({ type: "PlutusV1", script: warehouse.poolSpendScript.hex }) + .attachValidator({ type: "PlutusV1", script: warehouse.orderSpendScript.hex }) + .collectFromPubKey(scooperLicenseInput) + .collectFromPlutusContract([pool.input], poolRedeemer, S1.PoolDatum.toCborHex(pool.datum)) + .collectFromPlutusContract( + [order.input], + orderRedeemer, + PlutusData.toDataHex(S1.OrderDatum.toPlutusJson(order.datum)), + ) + .payTo(poolOut, escrowOut, compensationOut, licenseReturnOut) + .validFrom(currentSlot) + .validTo(currentSlot + 2000) + // .validFromUnixTime(Number(validFromMs)) + // .validToUnixTime(Number(validToMs)) + .addSigner(scooper) + ); + } +} diff --git a/packages/minswap-build-tx/test/sundaeswap-v1.test.ts b/packages/minswap-build-tx/test/sundaeswap-v1.test.ts new file mode 100644 index 0000000..db6d148 --- /dev/null +++ b/packages/minswap-build-tx/test/sundaeswap-v1.test.ts @@ -0,0 +1,276 @@ +import { + ADA, + Address, + Asset, + Bytes, + DatumSource, + NetworkEnvironment, + PlutusData, + TxIn, + TxOut, + type Utxo, + Value, +} from "@minswap/felis-ledger-core"; +import { Maybe, RustModule } from "@minswap/felis-ledger-utils"; +import { SundaeSwapV1, SundaeSwapV1Warehouse } from "@minswap/felis-sundaeswap-v1"; +import { CoinSelectionAlgorithm, EmulatorProvider } from "@minswap/felis-tx-builder"; +import { beforeAll, describe, expect, it } from "vitest"; +import * as B from "../src"; + +beforeAll(async () => { + await RustModule.load(); +}); + +describe("sundaeswap-v1", () => { + // Fixture mirrors mainnet CreatePool tx in + // packages/sundaeswap-v1/src/debug3.json (RBERRY/SBERRY, ident "00"). + // We run on TESTNET_PREPROD because the warehouse loads preprod-scripts.json + // for that network; the script hashes happen to be identical across the + // bundled JSONs, but pinning the network keeps the address derivation + // self-consistent. + it("buildCreatePool — mints pool NFT + LP, splits factory, pays change", async () => { + const networkEnv = NetworkEnvironment.TESTNET_PREPROD; + const warehouse = SundaeSwapV1Warehouse.getInstance(networkEnv); + + // Pair from debug3.json (sorted: RBERRY < SBERRY by token name). + const assetA = Asset.fromString("7de52b397c138e44fb6e61aaaeb26219a8059b1749b7c3bd87bd9488.524245525259"); + const assetB = Asset.fromString("7de52b397c138e44fb6e61aaaeb26219a8059b1749b7c3bd87bd9488.534245525259"); + const amountA = 10_000_000_000n; + const amountB = 15_000_000_000n; + + // Factory datum copied from debug3.json's input factory datum. + const factoryDatum: SundaeSwapV1.FactoryDatum = { + nextPoolIdent: Bytes.fromHex("00"), + proposal: { constructor: 0, fields: [] }, + scooperIdent: Bytes.fromHex("00"), + scoopers: [ + Bytes.fromHex("9d1cbb54faf284f5d262f591b1f9201a1858de155157dad49f3881c4"), + Bytes.fromHex("694bc6017f9d74a5d9b3ef377b42b9fe4967a04fb1844959057f35bb"), + ], + }; + const factoryDatumCborHex = SundaeSwapV1.FactoryDatum.toCborHex(factoryDatum); + + const owner = Address.fromBech32( + "addr_test1qz427uy6zgxycqxmvetz2vejxx0ccagrfwwjyjjejdmgqkn8t7n238qksqw6w3sntsy9xarjh895hwlwcly92zf6gj8sq9u6hw", + ); + + const factoryInput: Utxo = { + input: TxIn.fromString("ac037cbfeb9535b5b5289f24ba9881a29487630827472dbeff14761ed7ca726c#0"), + output: new TxOut( + warehouse.factoryAddress, + new Value().add(ADA, 2_000_000n).add(warehouse.factoryAuthenAsset, 1n), + // V1 carries datum-by-hash; OUTLINE_DATUM bundles hash + body so the + // builder can both verify the input and embed the body in witnesses. + DatumSource.newOutlineDatum(Bytes.fromHex(factoryDatumCborHex)), + ), + }; + + const walletUtxos: Utxo[] = [ + { + input: TxIn.fromString("87cf9490fc911098a885b85bd9085bb7265ef410d7ccf87ea71c2c779600dec1#1"), + output: new TxOut( + owner, + new Value().add(ADA, 50_000_000n).add(assetA, 1_000_000_000_000n).add(assetB, 1_000_000_000_000n), + ), + }, + ]; + + const txBuilder = B.SundaeSwapV1.buildCreatePool({ + networkEnv, + owner, + factoryInput, + factoryDatum, + assetA, + amountA, + assetB, + amountB, + }); + + const result = await txBuilder.complete({ + walletUtxos, + coinSelectionAlgorithm: CoinSelectionAlgorithm.MINSWAP, + provider: new EmulatorProvider(networkEnv), + changeAddress: owner, + }); + expect(result.type).equal("ok"); + + // Sanity checks on the draft after task expansion. + const draft = txBuilder.getTxDraft(); + + // Mints: 1 pool NFT + lpSupply LP, both under poolMintScriptHash. + const lpSupply = SundaeSwapV1.isqrt(amountA * amountB); + expect(lpSupply).toEqual(12_247_448_713n); + const ident = factoryDatum.nextPoolIdent; + const poolNft = warehouse.poolNftAsset(ident); + const lpAsset = warehouse.lpAsset(ident); + expect(draft.body.mint.get(poolNft)).toEqual(1n); + expect(draft.body.mint.get(lpAsset)).toEqual(lpSupply); + expect(poolNft.tokenName.hex).toEqual("702000"); // "p \x00" + expect(lpAsset.tokenName.hex).toEqual("6c702000"); // "lp \x00" + + // The continuing factory output carries the bumped ident. + const factoryOut = draft.body.outputs.find((o) => o.address.bech32 === warehouse.factoryAddress.bech32); + expect(factoryOut).toBeDefined(); + expect(factoryOut?.value.get(warehouse.factoryAuthenAsset)).toEqual(1n); + + // The new pool output carries the pool NFT and both reserves. + const poolOut = draft.body.outputs.find((o) => o.address.bech32 === warehouse.poolAddress.bech32); + expect(poolOut).toBeDefined(); + expect(poolOut?.value.get(poolNft)).toEqual(1n); + expect(poolOut?.value.get(assetA)).toEqual(amountA); + expect(poolOut?.value.get(assetB)).toEqual(amountB); + + // Both factory.spend and pool.mint scripts were attached inline. + expect(draft.plutusScripts[warehouse.factorySpendScriptHash.hex]).toBeDefined(); + expect(draft.plutusScripts[warehouse.poolMintScriptHash.hex]).toBeDefined(); + }); + + // Fixture mirrors mainnet scoop tx + // e1615e56f980b2ff86fdaf6d554bdbb347cd3b249250334b3d189c3b9ac6ec2a: + // pool ident "03" (ADA / LQ), one swap order, scooper holds the active + // license NFT, license-escrow output records the scoop. We don't run the + // emulator here because the on-chain order validator's checks against the + // license escrow require constants we don't yet hard-code; instead we assert + // the draft has the right shape (inputs, redeemers, outputs). + it("buildScoopTx — pool spend + 1 order spend + license escrow", () => { + const networkEnv = NetworkEnvironment.TESTNET_PREPROD; + const warehouse = SundaeSwapV1Warehouse.getInstance(networkEnv); + + const ident = Bytes.fromHex("03"); + const LQ = Asset.fromString("da8c30857834c6ae7203935b89278c532b3995245295456f993e1d24.4c51"); + + const poolDatum: SundaeSwapV1.PoolDatum = { + assetA: ADA, + assetB: LQ, + ident, + liquidity: 41_423_744_972n, + tradingFee: [3, 1000], + }; + const poolDatumCborHex = SundaeSwapV1.PoolDatum.toCborHex(poolDatum); + + const poolInput: Utxo = { + input: TxIn.fromString("cd2ba4372dd5b322da92cfeace6b5bbf4ac57cb27a0f4a2b1b173b363ba15fa6#0"), + output: new TxOut( + warehouse.poolAddress, + new Value().add(ADA, 42_000_000_000n).add(warehouse.poolNftAsset(ident), 1n).add(LQ, 56_000_000_000n), + DatumSource.newOutlineDatum(Bytes.fromHex(poolDatumCborHex)), + ), + }; + + // Order: a user wants to swap LQ → ADA. Using the same beneficiary as the + // mainnet tx for shape parity. + const beneficiary = Address.fromBech32( + "addr1qxlwpgwaqyeyygtc085w7pl09rfzmtvw7upz698jx5csn5j26m6ldzampksupgucjgt5p0ws3lqy733vmp4g887a8j9symft3f", + ); + const orderDatum: SundaeSwapV1.OrderDatum = { + poolIdent: ident, + orderAddresses: { + destination: { address: beneficiary, datum: null }, + alternate: null, + }, + scooperFee: 2_500_000n, + swapDirection: { + direction: SundaeSwapV1.SwapDirection.B_TO_A, + amount: 419_224_000n, + minReceivable: 558_048_006n, + }, + }; + const orderDatumHex = SundaeSwapV1.OrderDatum.toDataHex(orderDatum); + const orderInput: Utxo = { + input: TxIn.fromString("3f2ffa5c8d1cf54845f72bcdbebf0039e98887b275c851694e8173f83775d182#4"), + output: new TxOut( + warehouse.orderAddress, + new Value().add(ADA, 5_000_000n).add(LQ, 419_224_000n), + DatumSource.newOutlineDatum(Bytes.fromHex(orderDatumHex)), + ), + }; + + const scooper = Address.fromBech32("addr1v9j3pglvpfhjw0334nyzl8e0lvyfgy65ngzpf84r0mudxwccapsss"); + const licensePolicy = Bytes.fromHex("e8a447d4e19016ca2aa74d20b4c4de87adb1f21dfb5493bf2d7281a6"); + const licenseSuffix = Bytes.fromHex("7b0b"); + const license = new Asset(licensePolicy, Bytes.fromHex("73636f6f706572207b0b")); + const scooperLicenseInput: Utxo = { + input: TxIn.fromString("b92ae9d305217f99eefa41639cb6a5353c33cfe51719b9b02523f14693f60a22#1"), + output: new TxOut(scooper, new Value().add(ADA, 100_000_000n).add(license, 1n)), + }; + + const newPoolValue = poolInput.output.value + .clone() + .add(LQ, 419_224_000n) // order brings in LQ + .subtract(ADA, 558_048_006n); // pool releases ADA to beneficiary + + const txBuilder = B.SundaeSwapV1.buildScoopTx({ + networkEnv, + scooper, + scooperLicenseInput, + license, + licenseSuffix, + pool: { input: poolInput, datum: poolDatum, newValue: newPoolValue, newDatum: poolDatum }, + order: { + input: orderInput, + datum: orderDatum, + compensation: new Value().add(ADA, 2_000_000n + 558_048_006n), + }, + licenseEscrow: new Value().add(ADA, 2_000_000n), + validFromMs: 1_739_999_000_000n, + validToMs: 1_739_999_000_000n + 30n * 60_000n, + }); + + const draft = txBuilder.getTxDraft(); + + // Inputs: license-pubkey + pool + order. + const inputTxIns = draft.body.inputs.map((u) => `${u.input.txId.hex}#${u.input.index}`); + expect(inputTxIns).toContain("cd2ba4372dd5b322da92cfeace6b5bbf4ac57cb27a0f4a2b1b173b363ba15fa6#0"); + expect(inputTxIns).toContain("3f2ffa5c8d1cf54845f72bcdbebf0039e98887b275c851694e8173f83775d182#4"); + expect(inputTxIns).toContain("b92ae9d305217f99eefa41639cb6a5353c33cfe51719b9b02523f14693f60a22#1"); + + // Both pool.spend and order.spend scripts were attached inline. + expect(draft.plutusScripts[warehouse.poolSpendScriptHash.hex]).toBeDefined(); + expect(draft.plutusScripts[warehouse.orderSpendScriptHash.hex]).toBeDefined(); + + // Pool redeemer is `Constr 0 [scooperPkh, licenseSuffix]` — the pool-spend + // script authenticates the scooper. + const poolRedeemer = draft.witness.redeemers.find( + (r) => r.ref === `${poolInput.input.txId.hex}#${poolInput.input.index}`, + ); + expect(poolRedeemer).toBeDefined(); + const expectedScooperPkh = scooper.toPaymentCredential(); + invariantOrThrow(Maybe.isJust(expectedScooperPkh), "scooper must have payment credential"); + expect(PlutusData.toDataHex(poolRedeemer?.redeemerData)).toEqual( + PlutusData.toDataHex(SundaeSwapV1.PoolRedeemer.scoop(expectedScooperPkh.payload, licenseSuffix)), + ); + + // Order redeemer is bare `Constr 0 []` — order delegates scooper auth to pool. + const orderRedeemer = draft.witness.redeemers.find( + (r) => r.ref === `${orderInput.input.txId.hex}#${orderInput.input.index}`, + ); + expect(orderRedeemer).toBeDefined(); + expect(PlutusData.toDataHex(orderRedeemer?.redeemerData)).toEqual( + PlutusData.toDataHex(SundaeSwapV1.OrderRedeemer.scoop()), + ); + + // Outputs: poolOut, escrowOut (warehouse.licenseEscrowAddress), beneficiaryOut, licenseReturnOut (scooper). + // (Scooper change is added by coin selection at .complete() time.) + expect(draft.body.outputs.length).toBeGreaterThanOrEqual(4); + const addresses = draft.body.outputs.map((o) => o.address.bech32); + expect(addresses).toContain(warehouse.poolAddress.bech32); + expect(addresses).toContain(warehouse.licenseEscrowAddress.bech32); + expect(addresses).toContain(beneficiary.bech32); + expect(addresses).toContain(scooper.bech32); // license return + + // License return is the scooper output that carries the license token. + const licenseReturnOut = draft.body.outputs.find( + (o) => o.address.bech32 === scooper.bech32 && o.value.has(license), + ); + expect(licenseReturnOut?.value.get(license)).toEqual(1n); + expect(licenseReturnOut?.value.get(ADA)).toEqual(2_000_000n); + + // Scooper is required to sign. + const scooperPkhHex = expectedScooperPkh.payload.hex; + expect(draft.body.requireSigners.some((k) => k.keyHash.hex === scooperPkhHex)).toBe(true); + }); +}); + +function invariantOrThrow(cond: boolean, msg: string): asserts cond { + if (!cond) throw new Error(msg); +} diff --git a/packages/sundaeswap-v1/src/index.ts b/packages/sundaeswap-v1/src/index.ts index afd3777..f2418f2 100644 --- a/packages/sundaeswap-v1/src/index.ts +++ b/packages/sundaeswap-v1/src/index.ts @@ -1 +1,2 @@ export * from "./sundaeswap-v1"; +export * from "./warehouse"; diff --git a/packages/sundaeswap-v1/src/scripts/mainnet-scripts.json b/packages/sundaeswap-v1/src/scripts/mainnet-scripts.json new file mode 100644 index 0000000..e40efe1 --- /dev/null +++ b/packages/sundaeswap-v1/src/scripts/mainnet-scripts.json @@ -0,0 +1,15 @@ +{ + "factoryMintScript": "590d49590d46010000332333222323233223322333222323232332232333222332233322232332232323332223233322232333333332222222232323333222232323233322232323233223322323233333222223322332233223322332233222332230020012232232325335305a330063333573466e1d40112002204a23333573466e1d40152000204c2350413530423357389201035054310004349926498cccd5cd19b8735573aa004900011980519191919191919191919191999ab9a3370e6aae754029200023333333333018335026232323333573466e1cd55cea8012400046603c606c6ae854008c0acd5d09aba250022350503530513357389201035054310005249926135573ca00226ea8004d5d0a80519a8130139aba150093335502d75ca0586ae854020ccd540b5d728161aba1500733502603a35742a00c66a04c66aa096086eb4d5d0a8029919191999ab9a3370e6aae754009200023350203232323333573466e1cd55cea80124000466a05066a078eb4d5d0a80118209aba135744a00446a0a86a60aa66ae712401035054310005649926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502633503c75a6ae854008c104d5d09aba250022350543530553357389201035054310005649926135573ca00226ea8004d5d09aba250022350503530513357389201035054310005249926135573ca00226ea8004d5d0a80219a8133ae35742a00666a04c66aa096eb88004d5d0a80118199aba135744a00446a0986a609a66ae71241035054310004e49926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031180e98191aba135573ca00646666ae68cdc3a801240084603860786ae84d55cf280211999ab9a3370ea00690011180e18161aba135573ca00a46666ae68cdc3a802240004603e6eb8d5d09aab9e50062350473530483357389201035054310004949926499264984d55cea80089baa001357426ae8940088d4100d4c104cd5ce249035054310004249926104113503f353040335738920103505435000414984d55cf280089baa001135573a6ea800488d4c01000488c8d4c02400c8c88888888894cd4c1480404c02801c4ccc8894cd4d4178cc170029418041a0884d4d5541940088894cd4d418c01054cd4c1b0ccd5cd19b8f00800306e06d15335306c333573466e3c01c0081b81b44c01800441b441b48841bd4030cdc524410873636f6f70657220003322333064371a00200200466e0cd4d406001088d4d4070004894ccd4d40d8008840044d4d41698984d4d4169899208090e4c00433716900024500253353065333573466e24005200006606713353056120012353018001223530290012223330530025013488107666163746f72790000c10673200135505d22533535053001105d221353550580022253353060333573466e3c009402018818454cd4c180ccd4c1504800488d4d5417000888d4d5417800c894cd4c198ccd5cd19b8f0040020680671333573466e1c00c0041a019c419c004cd4160cd5416d22107666163746f7279004800941644cd4c12403488cd4c14c480048cc894cd4c194ccd5cd19b8f35305100222002353051001220020670661333573466e1cd4c14400888004d4c1440048800419c1984198d4c0540048800800c0248cd4c148480048d4c05000488d4c094004888ccc13c00801d220107666163746f72790000810611300600315333353012001213503e35303f3357389201024c68000404988400484d40f8d4c0fccd5ce249024c680004049884d40f8d4c0fccd5ce2481024c68000404988848cc00400c0088004888888888848cccccccccc00402c02802402001c01801401000c00880048848cc00400c008800448848cc00400c0084800448848cc00400c0084800448848cc00400c00848004848888c010014848888c00c014848888c008014848888c004014800448c88c008dd6000990009aa822911999aab9f0012503e233503d30043574200460066ae880080ac8c8c8c8cccd5cd19b8735573aa006900011998039919191999ab9a3370e6aae754009200023300d302935742a00466a02004a6ae84d5d1280111a8171a981799ab9c491035054310003049926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a018eb8d5d09aba2500223502a35302b335738921035054310002c49926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540fc88c8cccd55cf8011281c919a81c19aa81d98031aab9d5002300535573ca00460086ae8800c0984d5d080089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a0486a604a66ae712410350543100026499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046604c600a6ae854008dd69aba135744a00446a03c6a603e66ae71241035054310002049926135573ca00226ea80048c8cccd5cd19b8735573aa002900011bae357426aae7940088d4070d4c074cd5ce249035054310001e499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a80f9a981019ab9c490103505431000214992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880cc8cccd5cd19b8750024800080cc8d405cd4c060cd5ce2490350543100019499264984d55ce9baa001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d407cd4c080cd5ce2481035054310002149926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4040d4c044cd5ce2490350543100012499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423500a35300b3357389201035054310000c499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa03a600c6ae854008c014d5d09aba25002235007353008335738921035054310000949926135573ca00226ea8004498480048004888c8c8c004014c8004d5407c894cd4d4054004407c884d4d54068008894cd4c088ccd5cd19b8f00200902402313007001130060033200135501e22533535014001101e221353550190022253353021333573466e3c00801c08c08854cd4c084ccd5cd19b870014800008c0884088408c4c01800c88488cc00801000c8488c00400c80048848cc00400c00880044880084880048004c8004d54048884894cd4d402800440508854cd4c054c010008405c4cd4c018480040100044848c00400848004c8004d5403c8848894cd4d402000854cd4d4020004404c8840508854cd4d402800c40508854cd4c054cc01c0100084ccd4c0244800401c00c0044058c8004d540388894cd4d40140084004884d4d5402800888cc01c00cccc020008004014c8004d5403488894cd4d40140084004884d4d54028008894cd4c048ccd5cd19b870014800005004c4ccc02001c00c0144ccc02001c00ccd4028ccd55403c01c0080040144488008488488cc00401000c48004448848cc00400c00844800444488848ccc00401000c0084448004c8004d540148888c94cd4c01cccd5cd19b8900248000024020400c54cd4c01cccd5cd19b89004480000240204cdc599b860024820010c018cdc18012410008266e2ccdc32800a41000866600a66e040112002333718900119b81371a006900100199b803370600490400219b83500148200104cdc019b860014820010cdc700124000640026aa00844a66a6006666ae68cdc3800a400000a008291010013371666e1800520800430023370600290400209100109100090008891918008009119801980100100099991119911999111980199802a4520a0450d82d0b2bfb9560e3845a1f0274ee2251a5e23935275450267f3e75760520048008cd401d2211caaaf709a120c4c00db6656253332319f8c75034b9d224a599376805a0033500748811caaaf709a120c4c00db6656253332319f8c75034b9d224a599376805a00500822122330020040032122300100320012212330010030022001112200212212233001004003120011", + "factoryMintScriptHash": "e8a447d4e19016ca2aa74d20b4c4de87adb1f21dfb5493bf2d7281a6", + "factoryAuthenToken": "e8a447d4e19016ca2aa74d20b4c4de87adb1f21dfb5493bf2d7281a6.666163746f7279", + "poolMintScript": "5906240100003332332233322232333222333222323322323333333322222222332233333222223332223232323232222322323535502c5001225335300e335300d120012325335301033300b001353550305005220015007101215335301033300b00100950071012133300b00100a48907666163746f7279003535502f300b0012253353502e0011350264988854cd4d40c00044d40a1262213535503530110022253353503400113502c4988854cd4d40d80044d40b9262215335350380011350304988854cd4d40e800454cd4d40e80244ccd5406dd719aa80dbae75a0084426a068931109a81a24c004204426a0466a604266ae71240103505435000224984d4d540acc01c004894cd4d40a80044c0a458884d4d540bcc02c008894cd4d40b80044c0b4588854cd4d40c00044c0bc588854cd4d40c80044c0c4588854cd4d40d00044c0cc588854cd4d40d80044c0d4588854cd4d40e00044c0dc588854cd4d40e80044c0e4588854cd4d40f00044c0ec588854cd4d40f80044c0f4588854cd4d41000044c0fc588854cd4d410800454cd4d410805c4c104588c8c8854cccd4c0b0cccd5cd19b875002480188c0c4c8c8c8c8c8c8cccd5cd19b8750014803080f88cccd5cd19b8750024802881008cccd5cd19b875003480208cc0f8dd71aba15005375a6ae84d5d1280291999ab9a3370ea00890031198201bae35742a00e6eb8d5d09aba2500723333573466e1d40152004233045304735742a0126eb8d5d09aba2500923333573466e1d40192002230473048357426aae79402c8cccd5cd19b875007480008c118c124d5d09aab9e500c23504c35304a335738921035054310004b49926499264992649926135573aa00826aae79400c4d55cf280109aab9e5001137540026ae84d55cf280211999ab9a3370ea006900211818181e9aba135573ca00a46666ae68cdc3a80224004460606464646666ae68cdc39aab9d5002480008cc0e4c8c8cccd5cd19b8735573aa002900011bae357426aae7940088d4120d4c118cd5ce249035054310004749926137540026ae854008dd69aba135744a00446a08a6a608666ae712401035054310004449926135573ca00226ea8004d5d09aab9e500623333573466e1d4015200023033375c6ae84d55cf280391a8211a982019ab9c49010350543100041499264992649884c11858854cd4d411c0084cd5412cc09cdd600c80091098240b1098230b1098230b09aab9d50011375400244260862c266e29220102702000375c0024446464600200a640026aa05644a66a6a05000220184426a6aa05a00444a66a601e666ae68cdc78010048088080980380089803001990009aa81511299a9a81380088059109a9aa81600111299a9807199ab9a3371e00400e02001e2a66a601c666ae68cdc3800a400002001e201e20202600c00646466aa04e6aae754004c00cd55cf280089baa001320013550252233335573e0024a04a466a0486ae84008c00cd5d100124c224464460046eac004c8004d5409888c8cccd55cf80112813919a81319aa81498031aab9d5002300535573ca00460086ae8800c06c4d5d0800990009aa81191091299a9a8110008803110a99a980398020010804899a980309000802000891001091000900090911118020029091111801802909111180100290911118008029000910919800801801100090911111118038041109111111198030048041091111111802804091111110020911111100191091111111980100480411091111111980080480410009191919191999ab9a3370ea002900111998059bad35742a0086eb4d5d0a8019bad357426ae89400c8cccd5cd19b875002480008c034c8c8c8cccd5cd19b875001480088c04cdd71aba135573ca00646666ae68cdc3a801240004602a6eb8d5d09aab9e500423500f35300d335738921035054310000e499264984d55cea80089baa001357426aae7940188d4028d4c020cd5ce2490350543100009499264984d55cea80189aba25001135573ca00226ea80044800480044988488c00800c888488ccc00401401000c80048488c00800c8488c00400c8004984488008488488cc00401000c48004448848cc00400c008448004448c8c00400488cc00cc0080080052211ce8a447d4e19016ca2aa74d20b4c4de87adb1f21dfb5493bf2d7281a60048810001", + "poolMintScriptHash": "0029cb7c88c7567b63d1a512c0ed626aa169688ec980730c0473b913", + "orderSpendScript": "59084601000033233322232332232333222323332223322323332223233223233223332223333222233322233223322332233223332223322332233322232323232322222325335300b001103c13503d35303b3357389201035054350003c498ccc888c8c8c94cd4c05c0144d4c0680188888cd4c04c480048d4c0ed40188888888888cd4c078480048ccd5cd19b8f375c0020180440420066a6040006446a6048004446a605000444666aa60302400244a66a6a07c0044266a08c0020042002a0886466a002a088a08a2446600466a609000846a0820024a0806600400e00226a606ca002444444444466a6032240024646464666ae68cdc399991119191800802990009aa82c1119a9a826000a4000446a6aa08a00444a66a6050666ae68cdc78010048150148980380089803001990009aa82b9119a9a825800a4000446a6aa08800444a66a604e666ae68cdc7801003814814080089803001999aa81e3ae335503c75ceb4d4c084cccd5cd19b8735573aa006900011998119aba1500335742a00466a080eb8d5d09aba2500223505135304f33573892010350543100050499262220020183371491010270200035302801422220044800808007c4d5d1280089aab9e500113754002012264a66a6a070601a6aae78dd50008a81a910a99a9a81d0008a81b910a99a9a81e0008a81c910a99a9a81f0008a81d910a99a9a8200008a81e910a99a9a8210008a81f910a99a9a8220008a820910a99a9a8230008a821910a99a9a8240008a822910a99a9a8250008a823910a99a9a82600089999999999825981000a18100090080071810006181000500418100031810002001110a8259a980a1999ab9a3370e6aae754009200023301635742a0046ae84d5d1280111a8211a982019ab9c490103505431000414992622002135573ca00226ea8004cd40148c8c8c8c8cccd5cd19b8735573aa00890001199980d9bae35742a0086464646666ae68cdc39aab9d5002480008cc88cc08c008004c8c8c8cccd5cd19b8735573aa004900011991198148010009919191999ab9a3370e6aae754009200023302d304735742a00466a07a4646464646666ae68cdc3a800a4004466606a6eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d4009200023037304e357426aae7940188d4154d4c14ccd5ce2490350543100054499264984d55cea80189aba25001135573ca00226ea8004d5d09aba2500223504e35304c335738921035054310004d49926135573ca00226ea8004d5d0a80119a81cbae357426ae8940088d4128d4c120cd5ce249035054310004949926135573ca00226ea8004d5d0a80119a81abae357426ae8940088d4118d4c110cd5ce249035054310004549926135573ca00226ea8004d5d0a8019bad35742a00464646464646666ae68cdc3a800a40084605c646464646666ae68cdc3a800a40044606c6464646666ae68cdc39aab9d5002480008cc88cd40f8008004dd69aba15002375a6ae84d5d1280111a8289a982799ab9c491035054310005049926135573ca00226ea8004d5d09aab9e500423333573466e1d40092000233036304b35742a0086eb4d5d09aba2500423504e35304c335738921035054310004d499264984d55cea80109aab9e5001137540026ae84d55cf280291999ab9a3370ea0049001118169bad357426aae7940188cccd5cd19b875003480008ccc0bcc11cd5d0a8031bad35742a00a66a072eb4d5d09aba2500523504a353048335738920103505431000494992649926135573aa00626ae8940044d55cf280089baa001357426ae8940088d4108d4c100cd5ce249035054310004149926135744a00226ae8940044d55cf280089baa0010033350052323333573466e1d40052002201623333573466e1d40092000201623504035303e335738921035054310003f499264984d55ce9baa001002335005200100112001230023758002640026aa072446666aae7c004940c08cd40bcd5d080118019aba2002498c8004d540e088448894cd4d40bc0044008884cc014008ccd54c01c48004014010004c8004d540dc884894cd4d40b400440188854cd4c01cc01000840244cd4c01848004010004488008488004800488848ccc00401000c00880048848cc00400c00880044880084880048004888848cccc00401401000c00880048848cc00400c00880048848cc00400c00880048848cc00400c00880048488c00800c888488ccc00401401000c800484888c00c0108884888ccc00801801401084888c00401080048488c00800c88488cc00401000c800448848cc00400c008480044488c88c008dd5800990009aa80d11191999aab9f0022501223350113355008300635573aa004600a6aae794008c010d5d100180c09aba10011122123300100300211200112232323333573466e1d400520002350083005357426aae79400c8cccd5cd19b87500248008940208d405cd4c054cd5ce24810350543100016499264984d55cea80089baa00112122300200311220011200113500d35300b3357389211f556e6578706563746564205478496e666f20636f6e737472756374696f6e2e0000c498888888888848cccccccccc00402c02802402001c01801401000c00880044488008488488cc00401000c480048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423500c35300a3357389201035054310000b499264984d55cea80089baa001212230020032122300100320012323333573466e1d40052002200823333573466e1d40092000200a2350073530053357389210350543100006499264984d55ce9baa0011200120011261220021220012001112323001001223300330020020014891c0029cb7c88c7567b63d1a512c0ed626aa169688ec980730c0473b9130001", + "orderSpendScriptHash": "ba158766c1bae60e2117ee8987621441fac66a5e0fb9c7aca58cf20a", + "poolSpendScript": "5929210100003333323332223232333222333222333222323322332233223322332233322232323232332233223322323233223233223332223332223333222232323232323333333322222222323233332222323232323232323232332233223233223232323232332233223322333222332233223233223322332233223232323232332232333322223233223232323233332222323232323232333332222233223232333322223232323232332223222222232533530ad010011087011350384901035054350033322235305b0032232223232323232353508f0100a22325335305700f223213530290112235304f002223222222223225335350b4013504a12233355055223530b0010022223530c4010032233530d10100220072533530d701333573466e3c0040c036404360044cc02802001c401c0040344d4c2b004d42a405262223330050030020012215335350b60100113530ae010022223330070030020012213530b001350ad01498888ccc02400c008004888d4c2b404d4c0e0080894cccd4c0f000484d42b404d4c2a804cd5ce2491a73637269707420696e707574206e6f7420666f756e6420212121000ab0149884d42b404d4c2a804cd5ce24811a73637269707420696e707574206e6f7420666f756e6420212121000ab0149884d42b404d4c2a804cd5ce24811a73637269707420696e707574206e6f7420666f756e6420212121000ab0149884d4c17c00c8888888888cc13002802c8c888c8c94cd4d42ec04d4144488ccd5417088d4c2dc04008888d4c32c0400c88cd4c36004008801c94cd4c37804ccd5cd19b8f001500f0e0010df0113300a008007100700101413530b301350b001498888ccc01000c0080048854cd4d42f4040044d4c2d404008888ccc01800c008004884d4c2dc04d42d0052622233300800300200122232323232353550c4010212232353550c701023223232323530695002222322533530e701333355305012001508f01220ea010100e90110e8011533530e701333573466e24d4d41dc07c88d4d41f4008894ccd4d42900400884d4d41f8010894ccd4d429c0400884cdc080080209a86500a4c26a194029309a86380a4c26a18e029324101756e061d2021d0022a66a6a1b80202442a66a6a1ba02660ea00203c42a66a61d202666ae68cdd7800991a984b0080091111a9a86200802111a9aa86d00801111a9aa86e00801911a9a851008039119ba548000cd5d019ba548000cd5d01999aa84e80ba975200c00a66ae80cccd5427405d4ba900400337629319aba0375201666ae80dd400519aba03374a900019aba0375000466ae80dd40009bb1498dd8a4c666612802074072a01006c1d6021d4022a66a61d202604c661980200ea1a4022a66a61d202666ae68cdc3a80401b87580875008a99a9874809999aa982909000a8488091076009824012075808a8020875008a99a9a8680098240120875009109a9aaa86b80801111299a9a86a808020a99a987700999ab9a3371e0900061e0021de022a66a61dc02666ae68cdc799b8a489036c70200003e0020f0010ef011533530ee01333573466e1c004cdc0a80681e07800877808a804887780887780887780910878808875008875008874808874008874008a99a9872809991299a987380999ab9a3370e6a6aa1a202004446660ba006004002900107480874008999ab9a337126644660040029000190009aa87500911299a9a8688080108009109980280099b8000348008c11800520060e9010e80110e80133550d00103f3371491010270200003500f1533530e501333573466e1cccc16403c02c028cdc0001282007380873008a99a987280999ab9a3370e6660b201e01000e66e00005410039c043980454cd4c39404ccc2f404104cdc52450873636f6f70657220000280241533530e501333573466e20cdc099b83353507501d2235350790012253335350a20100221001135350800162613535080016264820243930010ccc09d20004800920004800839c043980454cd4c39404ccd5cd19b8833305901948810048810000c0e6010e70115335350da01018215335350db013307300101c21333573466ebc004c8d4c2040400488cdd2a400066ae80dd480119aba037520026ec526330810102b02a0e9010e80110e70110e60110e60110e60110e60110e60110e60110e6011353068500122220011332235350d4010022223333302d00300200150ce0100433350d30100400102e33553048120012353550cb010012200100933305300d00200133305000a0020013370200266609a02a91100488100330a30148000004d4150488ccd5417c88d4c2e00400888d4c2f00400488c8c88d4c3480401488cd4c37c040089401494cd4c39404ccd5cd19b8f00150160e7010e601100b1500515335350d501333506c2323232323333573466e1cd55cea802240004666610a026eb8d5d0a8021919191999ab9a3370e6aae7540092000233223308d010020013232323333573466e1cd55cea8012400046644661be020040026140026ae854008cd428405d71aba135744a00446a194026a618e0266ae71240103505431000c80149926135573ca00226ea8004d5d0a80119a84e80bae357426ae8940088d431804d4c30c04cd5ce24903505431000c40149926135573ca00226ea8004d5d0a8019bad35742a00464646464646666ae68cdc3a800a40084617602646464646666ae68cdc3a800a4004461860266a12c02eb4d5d09aab9e500423333573466e1d400920002330c30130960135742a0086eb4d5d09aba250042350ce013530cb0133573892103505431000cc01499264984d55cea80109aab9e5001137540026ae84d55cf280291999ab9a3370ea00490011185d009bad357426aae7940188cccd5cd19b875003480008ccc2f004c24804d5d0a8031bad35742a00a66a14202eb4d5d09aba250052350ca013530c701335738920103505431000c8014992649926135573aa00626ae8940044d55cf280089baa001357426ae8940088d430804d4c2fc04cd5ce24903505431000c00149926135744a00226ae8940044d55cf280089baa0010210042135307f00122223232533530e701333573466e2001120000e8010e9011533530e701333573466e20ccc16ccccc8888cd54c14c480048d4d543580400488cd5436404008ccd4d54148004800488cc0200080048004ccd54c13848005434c04cd54c14c480048d4d543580400488cd5436404008d54154004ccd54c1384800488d4d5435c04008894cd4c3bc04ccd54c16048004c8cd42680488ccd4d413c00c88008008004d4d413400488004cd4134894cd4c3c40400843cc0440043c0048d4d543680400488cc02c008014018400c4cd435c0401000d435004004cd54c14c480048d4d543580400488c8cd543680400cc004014c8004d543c404894cd4d435c040044d5415400c884d4d5437004008894cd4c3d004cc0340080204cd5416801c0044c01800c0083940488004024d4190488cc008cd5435004d4d5434c040c088008cd434404cd5435004d4d5434c040c088004cdc09a9aa869808181119982f806801000999a985a80802919a985d0080091a9a860808009100111299a985f8080108008a4000444a66a617e02006200429000124000a1a40200291100488100337000089040497a0087400874808a99a987380999ab9a3371e00c06e1d2021d0022666a616602006466a61700200246a6a17e0200244a66a61d602666ae68cdc41a9aa86a8081911199830a80700100080107600876808a99a987580999ab9a337106a6aa1aa02062446660c2a01c0040020021d8021da022a66a61d602666ae68cdc4001240041d8021da022a66a61d602666ae68cdc4000a40041d8021da022a00c2a00a2a00a2a00a2a00a44a66a61d402666ae68cdc4299a985e8080109a9aa86a0081891199830280680100089a9aa86a0081811199830280680100080087580876008a99a987500999ab9a33710002900107580876008a8028a8020a802111299a987500999ab9a33710a66a617a0200626a6aa1a802062446660c0a01a00400226a6aa1a802060446660c0a01a0040020041d6021d8022a66a61d402666ae68cdc4001240041d6021d8022a00a2a0082a0084a66a61d002666ae68cdc419982e2804821281b80087480875008a99a987400999ab9a33710002900107480875008a8018a8010a8010a800880688068a99a9873009a861009a985f8099ab9c490110657363726f7720696e636f7272656374000c0014985400440304cc038cd5434004008cd5434004d4c2140400c8800800402c40184c144004004060c120008c1100084d4c2fc0400488cd4c330040088d42b804d4c2ac04cd5ce24918696e76616c696420706f6f6c20736372697074207574786f000ac014988004c8004d5431804894cd4d42b004004431c04884d4d5542cc04008888d4c2e00400c894cd4c33004ccccc04400800401000c0284c024018433404c8004d543040488894cd4c30804ccd5cd19b88003371a00a18602188022002266600866e0000d20023370490400200119b800013370466e3801400c0084d4c09803888d4c13000888888888894cd4c32004cd4c178480048d4c2980400488d4c2a8040048894cd4c33804ccc2a4040080a9220107666163746f72790015335350c301333505a2323333573466e1cd55cea800a405046464646464646464646666ae68cdc39aab9d5008480008cccccccc88888888cccccccc2180402001c01801401000c008004dd71aba15008375c6ae85401cdd71aba15006375c6ae854014dd71aba150043350810175a6ae85400cdd71aba15002375c6ae84d5d1280111a85b809a985a0099ab9c490103505431000b50149926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d55cf280089baa001357426aae7940088d42b404d4c2a804cd5ce24903505431000ab0149926137540026a607004044004008421a202219e02219e020142a66a61900266a60bc2400246a614c02002446a615402002444646a617e02008446a61820200644666618e020080060040026617e0261940204ca18802014219202219402219202640026aa17c02444444a66a6a150020022184024426a61440200444644a66a618e026a616e02008446a61720201a4466a618c02008466a618e0200646619602004002419c02466a618e02006419c02466196020040022a66a6a1780200242a66a6a17a0201642a66a619202666ae68cdc780100086580865008a802099999807006806005805003899999806806285f008050048030a99a9a85e0080510999998068060058050048030a801099999806005805004804002899299a986300999ab9a337126a6a170020104440066a6aa1600201a446660780060040021900218e022a66a618c02666ae68cdc49a9a85c008041110011a9aa858008061119981e00180100086400863808a99a986300999ab9a337126a6a17002010444002666074002040a02a1900218e0221900226666601601401201000e00826666601601401201000e00826666601601401201000e0086607000c0026a0064440026a0044440046a00244400664664646460020024466006600400400244246a6008246a60080066a00600200242466600246a006444600600846a006444600400846a006444600200800224444666008444444466a61200200c46a6a12e02002446464a66a618a02666ae68cdc48008020630086380899199801a400000266a13a02900000099b833370401266e040040100284c8ccc00c005200033509d0100148000cdc080200099b83337040040120104446a6a13802002446464646464646666602c66e0004c018cdc000900299b800110043350b60133550b90101533350c10100300200101000f33700006900019b8048000020cdc02400001066e0ccdc100100680799b810070023370200e00444646464a66a61300200a26666601a66e00028010024004cd42b404cd542c004030ccd42e00520004800000801c0184ccccc034028cdc000480200099a8568099aa85800806199a85c00a4000900000100380319b80007001337026a6a0e26607c66e08cdc100080119b81500f35350710102200233704a66a612c020062010200ea01e446a6a0e6666609c0069001001000912999a981f181e9999828002a4004008006420024200226a146029300319b82005005222222232323333300a3370200e00466e04018004cdc080280419a8550099aa85680804999a85a80801000a400000800666e0ccdc100380280219b833370400c00a00644444a66a6a14a020022666608c00400a0080064426a6aa1540200444666a611c020024666666601a00600201401201000e0084446a6a0e60244464a66a61360200c26464a66a619402666ae68cdc480800106580866008a99a9a85f8080310a999a9826998260018008a8010a80109a85380a4c2a00226a14c029309999980899b800100063370201e00201c66a1620266aa16802012666a178029000000a400001a01466e0ccdc119b8200e0050013370066e0803c008cdc100280089919299a986500999ab9a3371202200419602198022a66a6a17e0200c42a666a609a660980060022a0042a00426a14e02930a80089a85300a4c26666602266e04040004cdc000780300719a8588099aa85a00804999a85e00800a4000900000680519b833370466e0803c014004cdc019b8200e0023370400a00266e040040088ccccccc03000c00402802402001c0104cdc11a9a83100091000a4008266e29221036c70200000133500e2323232323333573466e1cd55cea80224000466660ba66a0ae66aa0f6eb9d71aba15004375c6ae85400cdd69aba1500233505875a6ae84d5d1280111a847009a98458099ab9c491035054310008c0149926135744a00226ae8940044d55cf280089baa00100333500e23232323333573466e1d40052002204d23333573466e1d400920002332233051002001375c6ae854010dd71aba135744a00846a11c026a61160266ae71241035054310008c01499264984d55cea80109aab9e50011375400200466a01c464646666ae68cdc39aab9d5002480008cc06cc8c8c8c8c8c8c8c8c8c8c8cccd5cd19b8735573aa01490001199999999982599a836119191999ab9a3370e6aae754009200023309e01307635742a00460e26ae84d5d1280111a84e009a984c8099ab9c491035054310009a0149926135573ca00226ea8004d5d0a80519a8360369aba150093335507175ca0e06ae854020ccd541c5d728381aba1500733506c07a35742a00c66a0d866aa10a0210602eb4d5d0a8029919191999ab9a3370e6aae754009200023350513232323333573466e1cd55cea80124000466a0b266a0f8eb4d5d0a8011840809aba135744a00446a140026a613a0266ae712401035054310009e0149926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233505733507c75a6ae854008c20404d5d09aba250022350a00135309d013357389201035054310009e0149926135573ca00226ea8004d5d09aba2500223509c01353099013357389201035054310009a0149926135573ca00226ea8004d5d0a80219a8363ae35742a00666a0d866aa10a02eb88004d5d0a80118399aba135744a00446a130026a612a0266ae7124103505431000960149926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031181218391aba135573ca00646666ae68cdc3a801240084604660f86ae84d55cf280211999ab9a3370ea00690011181198361aba135573ca00a46666ae68cdc3a802240004604c6eb8d5d09aab9e50062350930135309001335738920103505431000910149926499264984d55cea80089baa001357426ae8940088d423004d4c22404cd5ce249035054310008a0149926135573ca00226ea80040044cc0840052014122123300100300212001233002001508b01320013550a30122253353508a0100210012213535508f010022233007003333008002001005320013550a201222253353508a0100210012213535508f0100222533530a701333573466e1c00520000a9010a8011333008007003005133300800700333508f013335550940100700200100512001320013550a00122112225335350890100110022213300500233355300712001005004001112122230030041122122233002005004112122230010041120013200135509b0122112253353508301001150850122133508601300400233553006120010040013200135509a01221122253353508301001135350450032200122133353504700522002300400233355300712001005004001221233001003002200121222230040052122223003005212222300200521222230010052001320013550920122253353507900213506e35306b33573892011a73637269707420696e707574206e6f7420666f756e64202121210006c498884d4c1c4008894cd4c258054cd4c25804ccd5cd19b8f353077002220023530770052200209801097011333573466e1cd4c1dc00888004d4c1dc014880042600425c04425c0440044cc01c00c0148cc0092002001223232325335309201333573466e200040082500424c044d41b8d4c1accd5ce2481216e6f7420656e6f7567682041646120746f20636f766572207468652072696465720006c49854cd4c24804ccd5cd19b8733702002004900004a00849808a801899a83d19aa83ea4410033507a335507d4890033702002004a0f6a00666600a006910100488100337049040497a00801899980224500001507822232323001005320013550930122335350790014800088d4d541f8008894cd4c25804ccd5cd19b8f00200909801097011300700113006003320013550920122335350780014800088d4d541f4008894cd4c25404ccd5cd19b8f00200709701096011001130060033200135508e01222253353507600210012213535507b002225335309301333573466e3c01c00825404250044ccc02001c00c0144ccc02001c00ccd41ec010014c8004d542340488c8c8c94cd4c23c04ccd5cd19b8833704a006a0049000048808480088038a99a984780999ab9a3370ea0069000048808480089804240002a66a611e02666ae68cdc3a801a801048808480089804240042a66a611e02666ae68cdc42801a801048808480089804a40002a66a611e02666ae68cdc42801a400012202120022600a6601466e05200050033370290002801099800a400466e01200233706a006a004640026aa1220244464a66a612402666ae68cdc399b8233704006006a00aa00c1280212602260160062a66a612402666ae68cdc380119b80003480082500424c044c03000c54cd4c24804ccd5cd19b893370466e0940054005401540182500424c044cc01140040084cc01000d40044cdc199b80002001480104d4d40ec008880044d4d40e80048800884888c00c01084888c0080104888004800488cc044cdc1001299a984380999ab9a3370e002900004480844008a40002a66a610e02666ae68cdc4800a4000110021120229001099b814800120025335308701333573466e200052000089010880113370290000008800990009aa8438091119b803370266e14cdc099a9802890009a802091198012405c6600490520119801240346600490650199801241a4046600490650119801241340666004905c0199801241f4026600490720199801241dc0466004905f0119801241fc0266004902019801240ac6600490570080099b81001480080052080043371c0049000299a984380999ab9a3370e002900104480844008a4000266006666e312002337026e3400920020025335308701333573466e1ccdc3000a400890000448084400899b83001480104cdc019b8248018005200212335001506c506d320013550850122122533530860133323535006001222222220063333333350060850100722333573466e20008004228042240488ccd5cd19b8900200108a010890122333573466e24008004224042280488ccd5cd19b880020010890108a01225335308801333573466e24008004228042240440044008894cd4c22004ccd5cd19b8900200108a010890110021001001480004d40452410350543600153353506d00213501149103505437002215335308801333573466e1c00d200008a01089011002133530061200100133702006900109111111109199999998008048040038030028020018010900091299a984080999ab9a3370e0040021060210402200c2a66a610202666ae68cdc48010008418084100880208028911001891100109110009000889119000990009aa83f91299a9a83280088019109980300118020009111091999800802802001801100091111980299b803370400800266e0800800ccdc10018009119299a983c999ab9a3370e004900003d83d09a80224810350543300133502833708006a00266e1000940044cc1094cd4c1e0ccd5cd19b88002480001e81e44cdc0a40000042004a66a60f0666ae68cdc4000a40000f40f2266e0520000011001123505235304f3357380020a09309111a982b000911299a9a837800909a98048031111111111299a9a83d19809005801109a83e98090008a83d8a838190009aa83b111299a9a82e8008a8361109a9aa83100111299a983d199ab9a3371e00c0040f80f626a0e200226600e00c006444444444424666666666600201601401201000e00c00a0080060044002244246600200600424002244246600200600424002244246600200600424002640026aa0da44244a66a6a0aa00220de442a66a60e0600800420e4266a600c240020080022424600200424002442466002006004400244244660040080062440024002444424666600200a0080060044002442466002006004400244444444246666666600201201000e00c00a00800600440024646666ae68cdc3a800a4004406646666ae68cdc3a80124000406a46a0786a607266ae712401035054310003a499264984d55ce9baa00112232323333573466e1cd55cea801240004664466a072004002600a6ae854008c014d5d09aba2500223503c353039335738921035054310003a49926135573ca00226ea8004488c8c8cccd5cd19b8735573aa0049000119a80718029aba150023005357426ae8940088d40ecd4c0e0cd5ce249035054310003949926135573ca00226ea8004448888cdd2a400066ae80c010008cd5d018018009bb14984cd4008888c00cc008004800448848cc00400c008480048888cccc01801000c008004888848cccc00401401000c008800448848cc00400c0084800448c88c008dd6000990009aa82a111999aab9f0012503d233503c30043574200460066ae880080b48c8c8c8cccd5cd19b8735573aa0069000119981a18031aba150033335500975ca0106ae854008cd401dd71aba135744a00446a0606a605a66ae71241035054310002e49926135744a00226aae7940044dd5000919191999ab9a3370e6aae7540092000233043301a35742a00466a00a0326ae84d5d1280111a8171a981599ab9c491035054310002c49926135573ca00226ea8004488c8c8cccd5cd19b875001480008d4124c014d5d09aab9e500323333573466e1d400920022504923502f35302c3357389201035054310002d499264984d55cea80089baa0011335500175ceb44488c88c008dd5800990009aa82811191999aab9f0022503a2335039335503c300635573aa004600a6aae794008c010d5d100181509aba1001232323333573466e1cd55cea80124000466060600a6ae854008dd69aba135744a00446a0546a604e66ae71241035054310002849926135573ca00226ea80048c8cccd5cd19b8735573aa002900011bae357426aae7940088d40a0d4c094cd5ce2490350543100026499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8159a981419ab9c490103505431000294992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800881208cccd5cd19b8750024800081208d408cd4c080cd5ce2490350543100021499264984d55ce9baa001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d40acd4c0a0cd5ce2481035054310002949926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660706eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002303a3008357426aae7940188d4070d4c064cd5ce249035054310001a499264984d55cea80189aba25001135573ca00226ea80048c8c8cccd5cd19b875001480088c0e0dd71aba135573ca00646666ae68cdc3a80124000460746eb8d5d09aab9e500423501935301633573892010350543100017499264984d55cea80089baa0011122232323333573466e1cd55cea80124000466aa050600c6ae854008c014d5d09aba25002235019353016335738921035054310001749926135573ca00226ea8004c8004d540e48894cd4c0e4ccd5cd19b87001480000ec0e840084cc00c004cdc2801000990009aa81c111299a9a80f8008801110998029a9aa8120011119b8000600200121222300300422212223330020060050042122230010042001212230020032212233001004003200112200212200120011221233001003002120013200135502b22225335350130011350083530053357389212373636f6f70657220746f6b656e206d7573742065786973747320696e20696e7075747300006498884d4c02c00888d4c03c0048894cd4c0ccccc03800802802440d44ccc02c028024018480048004888c8c8c004014c8004d540b0894cd4d404800440b0884d4d5405c008894cd4c0bcccd5cd19b8f00200903103013007001130060033200135502b22533535011001102b22135355016002225335302e333573466e3c00801c0c00bc54cd4c0b8ccd5cd19b87001480000c00bc40bc40c04c01800c4988848cc00400c008800488848ccc00401000c00880048848cc00400c0088004c8004d540808894cd4d401c0084004884d4d54030008894cd4d4064cccc0280080154069403484d4d5403c00488d4d5404400888c8d4d4070ccd403c03801c00c888cc040030cd4054ccd55406802cccd404804402801801001ccdc0000a400426600e00666a018666aaa02200400290010029119b80002001122235350100022223535013004222323232333501a0030020013300b0060033300a006003330090060033200135501d222225335350060031533535014002213535500a0012235019335500d335500d0020010041501522135355500d00222253353501900721333300b00a00500833500d00600715335302435301200922353016002223530160072235301a002225335302c3333020006005002001153353502100721533535022004213302c002001102e15335350210032102e102e102d1333300a0090043501b335500f0020010061333300a009004501a33500c00500611220021221223300100400312001112212330010030021120011112221233300100400300211120012212330010030022001221233001003002200112221233300100400300212001222232335300c0052335300d004253353013333573466e3c0080040540505400c405080508cd4c034010805094cd4c04cccd5cd19b8f00200101501415003101415335350050032153353500600221335300a0022335300b0022335300f002233530100022330140020012017233530100022017233014002001222017222335300d004201722253353018333573466e1c01800c06806454cd4c060ccd5cd19b8700500201a01913301700400110191019101215335350050012101210121212230020031122001120012122300200322212233300100500400320012122300200321223001003200122333573466e3c00800401801488ccd5cd19b870020010050041220021220012001112323001001223300330020020014891ce8a447d4e19016ca2aa74d20b4c4de87adb1f21dfb5493bf2d7281a60048811c0029cb7c88c7567b63d1a512c0ed626aa169688ec980730c0473b9130048811c646ad13fb9e22d3b802049d85e95dc04edb6471eaa9237a932d38d490048811cba158766c1bae60e2117ee8987621441fac66a5e0fb9c7aca58cf20a0001", + "poolSpendScriptHash": "4020e7fc2de75a0729c3cc3af715b34d98381e0cdbcfa99c950bc3ac", + "factorySpendScript": "59265e59265b0100003323322332233322233322233223322333332222233223232333222323332223233333333222222223233322232333322223232332232333222323232332233223232333332222233223322332233223322332222323232322323232232325335303533300d3333573466e1cd55cea805a400046666444466660b40080060040026eb8d5d0a805991919191999ab9a3370ea002900111991198310010009bad35742a0066464646464646464646666ae68cdc39aab9d5008480008cccccccc88888888cccccccc1d402001c01801401000c008004dd71aba15008375c6ae85401cdd71aba15006375c6ae854014dd71aba150043232323333573466e1cd55cea80124000466a0e66eb4d5d0a8011bad357426ae8940088d4178d4c17ccd5ce2481035054310006049926135573ca00226ea8004d5d0a8019bae35742a0046eb8d5d09aba2500223505a35305b335738921035054310005c49926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d55cf280089baa001357426ae89400c8cccd5cd19b8750024800081888d4144d4c148cd5ce24810350543100053499264984d55cea80109aab9e5001137540026ae854028dd71aba1500933502075c6ae84d5d1280491a8259a982619ab9c491035054310004d499263333573466e1d4011200823051375c6ae84d55cf280391999ab9a3370ea00a9002102811999ab9a3370ea00c9001102911999ab9a3370ea00e9000119911982b801000999aa826bae75c6ae854024ccd54135d73ae357426ae8940248d4138d4c13ccd5ce24903505431000504992649926498cccd5cd19b8735573aa004900011980819191919191919191919191999ab9a3370e6aae75402920002333333333301e33502c232323333573466e1cd55cea8012400046604860786ae854008c0c4d5d09aba2500223505b35305c335738921035054310005d49926135573ca00226ea8004d5d0a80519a8160169aba150093335503375ca0646ae854020ccd540cdd728191aba1500733502c04535742a00c66a05866aa0ac09ceb4d5d0a8029919191999ab9a3370e6aae754009200023350263232323333573466e1cd55cea80124000466a05c66a088eb4d5d0a80118249aba135744a00446a0be6a60c066ae712401035054310006149926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502c33504475a6ae854008c124d5d09aba2500223505f3530603357389201035054310006149926135573ca00226ea8004d5d09aba2500223505b35305c3357389201035054310005d49926135573ca00226ea8004d5d0a80219a8163ae35742a00666a05866aa0aceb88004d5d0a801181d9aba135744a00446a0ae6a60b066ae71241035054310005949926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea002900311811981e9aba135573ca00646666ae68cdc3a8012400846044608e6ae84d55cf280211999ab9a3370ea00690011181118191aba135573ca00a46666ae68cdc3a802240004604a6eb8d5d09aab9e50062350523530533357389201035054310005449926499264984d55cea80089baa001357426ae8940088d412cd4c130cd5ce249035054310004d49926104c13504a35304b3357389201035054350004c4984d55cf280089baa001135573aa00426aae7940044dd500089aba25001135744a00226aae7940044dd50009109198008018011000911111111109199999999980080580500480400380300280200180110009109198008018011000891091980080180109000891091980080180109000891091980080180109000909111180200290911118018029091111801002909111180080290008919118011bac001320013550422233335573e0024a07a466a07860086ae84008c00cd5d100101811919191999ab9a3370e6aae75400d200023330073232323333573466e1cd55cea8012400046601a605c6ae854008cd40400a8d5d09aba25002235033353034335738921035054310003549926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a018eb8d5d09aba2500223502f353030335738921035054310003149926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540f088c8cccd55cf8011281c119a81b99aa81998031aab9d5002300535573ca00460086ae8800c0ac4d5d080089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a0526a605466ae71241035054310002b499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a0466a604866ae71241035054310002549926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d407cd4c080cd5ce24810350543100021499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8111a981199ab9c490103505431000244992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4068d4c06ccd5ce249035054310001c499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d407cd4c080cd5ce2481035054310002149926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4040d4c044cd5ce2490350543100012499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423500a35300b3357389201035054310000c499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa020600c6ae854008c014d5d09aba25002235007353008335738921035054310000949926135573ca00226ea80044984800480048848888cc010018014848888c00c01448888008488880048004448848cc00400c008448004888848cccc00401401000c00880044488008488488cc00401000c4800448800888488cc00401000c80048888888848cccccccc00402402001c01801401000c008800448848cc00400c00848004448c8c00400488cc00cc008008004cccccc8ccc888cc88c8cc88ccc888c8c8c8cc88cc88ccc888cc88c8c8ccc888ccc888ccc888c8c8cc88c8c8cccc8888c8c8c8c8c8ccccc88888cc88cc88cc88cc88cc88cc88c8c8cc88c8c8c8c8cccc8888c8c8c8cccccccc88888888cc88cc88cc88cccc8888cc88cc88cc88cc88c8c8c8c8cc88c8c8c8c8c8c8c8cc88ccc888c8cccc8888c8c8c8c8ccccc88888cc88c8c8c8c888888888c8c8c8d4c17c02c88d4c220040208888d4c118d4c058028894cccd4c06800484d41d8d4c28404cd5ce2491a73637269707420696e707574206e6f7420666f756e6420212121000a20149884d41d8d4c28404cd5ce24811a73637269707420696e707574206e6f7420666f756e6420212121000a20149884d41d8d4c28404cd5ce24811a73637269707420696e707574206e6f7420666f756e6420212121000a20149884d4c0ec00c8888888888cc0ac02802c888c8c8c8c94cccd4c1d0048884c00d2080bab70321300248203032f24084c0052080bab7031300148202eadc0c8c94cccd4c1d404c8854cd4c28804cc0dc068d4c141401888800854cd4c28804ccc17025004024d4c14140188880085400c428c04428c04854cd4c28404cc0d8064d4c13d401488800854cd4c28404ccc16c24c04020d4c13d40148880085400842880442880454cd4c28004cc0d4060d4c139401088800854cd4c28004ccc1682480401cd4c1394010888008540044284044284045400454cd4c27c04ccd5cd19b8935301d011223530410022222222222353504d00422353505300222533353505700221353505400422533353505a002213370200200826a11a029309a84680a4c26a114029309a84500a4c00214202140022a6666a60e8024446426a6040028446a60880044444444444a66a6a16c02666aa60d424002446a60bc004444a66a61660266605a00405066e292210270200001f1533530b3013309701003330990130a301029509d0113350bc010050041004100450b801009135305c3508901498888cccc04000c008004d423005262215335350b801001135305e00222233330120030020010052213530603508d01498888cccc05000c008004d4240052622323222325333530313030323350a50100148008cdc11a9aa855808049119982328030010009a9aa8558080411199823280300100090980100090980100089918010009a84180a4c4a66a6150026a6aa15602012446a6aa15a0201444a666a61160266114020080042a666a6116026611402006002215a02215a02215c02215a02215c022a66a6150026666a07610802a02ea01866661360266046900100a0098090088a99a98540099983104d00a80c19a8588099aa8560080e9a8168911980119aa8570099b8a4890270200001648008cc008cd542b804cdc52441036c70200001600300150b2011533530a801333573466e20d4d542ac0402488ccc119401800800520020a9010aa011533530a801333573466e20d4d542ac0402088ccc119401800800520020a9010aa011533530a80133303e48018cd542b004074cdc524502702000014500415335350910133350462323232323333573466e1cd55cea80224000466664444666610802008006004002664464646666ae68cdc39aab9d5002480008cc88cd422404008004c014d5d0a80118029aba135744a00446a11c026a61720266ae71240103505431000ba0149926135573ca00226ea80048c8c8cccd5cd19b8735573aa0049000119aa85b009bae35742a0046eb8d5d09aba2500223508d013530b801335738920103505431000b90149926135573ca00226ea8004d5d0a8021bae35742a0066eb4d5d0a80119a84400bad357426ae8940088d422404d4c2d004cd5ce248103505431000b50149926135744a00226ae8940044d55cf280089baa00150170022135307a001222235350800100422533530af0133092010020101533530af01330920100100f1533530af01333573466e3c01406c2c4042c00454cd4c2bc04ccd5cd19b870040080b1010b001133355307212001350705073235350ab010042235350ad0100322533530b401333573466e1c0100082d8042d4044cc2980400c00442d404cd54c1c4480048004d40d0488cc008cc0f92002482807ccc008cc0f92006483403ccc008cc0f92002483200400442c00442c00442c00442c0044d420c052610a90110a90110a90110a90110a90110a90113232533530a601333573466e200040082a00429c044d420404d4c2b004cd5ce2481216e6f7420656e6f7567682041646120746f20636f76657220746865207269646572000ad0149854cd4c29804ccd5cd19b8733702002004900005400853808a801899a8578099aa85500a441003350af0133550aa014890033702002004a16002a006666082006910100488100337049040497a00a40042666144029110000150ad012135301e012223530420022222222222533530ac0133355306f120013506d507030940100d0031533530ac0133355306f120013506d507030940100d015135305a50102225335350980100121533535099013304f00100621533530b101333573466ebc004c234040942cc042c80454cd4c2c404ccc1ac28c05408542ec0442cc044cccc88894cd4d42f404c13001042d804884d4d5541440088894cd4d43080401054cd4c2e804ccd5cd19b8f0080030bc010bb011533530ba01333573466e3c01c0082f0042ec044c01800442ec0442ec048842f40540840a4cdc52450873636f6f70657220003302c33706a0449040487260022822905a008859008858808858008856808856808a99a9a853809a8120911a980f809911a98218011111111111199aa825111a9aa8590080e111a982e802111a9830800911299a985b0099981800100380309980b804804080400680509a98259a83d24c4466008004002442a66a6a1520200226a609a004446600c0040024426a609e6a0fc9311198040010008a99a984a808050850009109a984d00800911191911111299a9a859809a8180911a981580f911a98278011111111111199aa82b111a9834801111299a985f009985100801998520098570080e285400899809802802080200680489a982c9a84300a4c44466660120060040026a1120293110a99a9a85a8080089a982d80111119998058018010008029109a982e9a84500a4c444666601a0060040026a11a02931111299a9854809981f0030018a99a9854809999a81e04280a80c000999984e0080a85080a81e8090a99a9854809999aa98360900099a8399111801980100090009105600807055808a99a985480999ab9a3371066e05406802805c2a8042ac0454cd4d42c404c8d4c0a007088d4c1300088888888888ccd54c1c84800488d4c198008888d4c2800404888d4c28804014894cd4c2fc04cccc2980401000c0080044cd4320040240204021430004024cc23c04c26404081424c044d4c15cd42100526222333300800300200135087014988854cd4d42cc040044d4c164008888cccc02800c008004014884d4c16cd42200526222333300c0030020013508b0149842a80442a80442a80442a80488894cd4c2a004cccd40ec8cdd2a405066ae80c21804004dd8a4ca02e0020102a66a6a16002607e666aa60c824002446a6aa15a0200444a66a615802666ae68cdc7801245000ae010ad01100313350b50100400350b20100310a9012213535550440022225335350b5010041533530ad01333573466e3c09400c2bc042b80454cd4c2b404ccd5cd19b8f48907666163746f7279000020af010ae011533530ad01333573466e1c00520020af010ae01133306709f01501d3350b60133550b10100a3350b60133550b101488107666163746f7279004800942dc0542dc0442b80442b80442b8048842c00442a40442800488c94cd4c28004cc21004020cc21804c24004c8cd4c0bc0048d4c0cc00488800c8d4c26c0400488888888021400542280454cd4c28004ccc16824805404142a8044cd4c0b940048d4c0c80048894cd4c29004ccd5cd19b8f00e0020a6010a501133335037080015013500833330970101000f3301f4800803800442940494cd4c25c040304cccd40d01f540414014cccc25004034cc88cc26804008005404800402c0288842900442840442840454cd4d422004ccd40f48c8c8cccd5cd19b875001480088c8c0d0004c8c8c8c8cccd5cd19b8735573aa0069000119991119981e8018010009bae35742a0066eb8d5d0a801191857009bac001357426ae8940088d420c04d4c2b804cd5ce248103505431000af0149926135744a00226aae7940044dd50009aba135573ca00646666ae68cdc3a8012400046460680026464646464646464646666ae68cdc39aab9d5008480008cccccccc88888888cccccccc2c00402001c01801401000c008004dd71aba15008375c6ae85401cdd71aba15006375c6ae854014dd71aba150043350880175a6ae85400cdd71aba15002375c6ae84d5d1280111a844809a985a0099ab9c49103505431000b50149926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d55cf280089baa001357426aae7940108d41fcd4c2a804cd5ce24903505431000ab01499264984d55cea80089baa001500e001210011353506e62615335350a4010011350774988854cd4d4298040044008884d41ed2635301900d2235303d002222222222253353509001533335302700b2150920121509201215092012133355306b120013350722253353509301002210031001509201235305500122533530ab013303200200413509601003150950100b2135305400122353058001222333553069120012235305d00222235309701008223530990100522533530b601333309d0100400300200113350bf01009008100850b70100f135082013530ad013357389201024c66000ae014984d4c03c00c880084d4c03800888d4c0c8008888888888801c4d4c03400488d4c0c40088888888888d4d40f401088d4d4104004894ccd4d411c008840044d4d41b98984d4d41b9898888c8c8c004014c8004d5425404894cd4d425004004423404884d4d5424404008894cd4c24004ccd5cd19b8f0020090920109101130070011300600332001355094012253353509301001108c012213535509001002225335308f01333573466e3c00801c244042400454cd4c23c04ccd5cd19b870014800024404240044240044244044c01800c88ccc00cdc6800800801190009aa84780911119299a984480999ab9a337120049000045808450088018a99a984480999ab9a3371200890000458084500899b8b3370c004904002180319b8300248200104cdc599b8650014820010ccc014cdc080224004666e312002337026e3400d20020033370066e0c00920800433706a002904002099b803370c00290400219b8e00248000c8004d5423804894cd4c21404ccd5cd19b870014800021c0421804522010013371666e180052080043002337060029040021109198008018011000909111180200290911118018029091111801002909111180080290008919a800a84280a84300990009aa84280911299a9a8428080109a82c1a98418099ab9c4911a73637269707420696e707574206e6f7420666f756e64202121210008401498884d4c0ac008894cd4c20404cc02000801440044cc01c00c014894cd4c1ecccd5cd19b8f35302b0022200235302b0012200207d07c1333573466e1cd4c0ac00888004d4c0ac004880041f41f041f0c8004d5420c0488c8c8c94cd4c1f4ccd5cd19b8833704a006a004900003f83f08038a99a983e999ab9a3370ea006900003f83f09804240002a66a60fa666ae68cdc3a801a80103f83f09804240042a66a60fa666ae68cdc42801a80103f83f09804a40002a66a60fa666ae68cdc42801a40000fe0fc2600a6601466e05200050033370290002801099800a400466e01200233706a006a004640026aa10e0244464a66a610002666ae68cdc399b8233704006006a00aa00c1040210202260160062a66a610002666ae68cdc380119b800034800820804204044c03000c54cd4c20004ccd5cd19b893370466e09400540054015401820804204044cc01140040084cc01000d40044cdc199b80002001480104d4d41d8008880044d4d41d40048800884888c00c01084888c0080104888004800488cc88c94cd4c1e0ccd5cd19b87002480001e81e44d414cd4c1f8cd5ce2481035054330007f4984cd41d0cdc2001a80099b84002500113307453353077333573466e20009200007907813370290000010801299a983b999ab9a33710002900003c83c099b81480000044004cdc1001299a983a999ab9a3370e002900003b83b0a40002a66a60ea666ae68cdc4800a40000ec0ee29001099b8148001200253353075333573466e2000520000770761337029000000880090911801001909118008019000911091998008020018011000891111a980f801111299a9a82e800909a980a8039111111111299a9a8341980f00580110999ab9a3375e0026026020104021020221000220ea266e2d2000489002233300348010cd541c400922107666163746f7279000012225335306d333573466e1cd4d541c000888ccc02c00c008005200206f06e1333573466e24cc88cc00800520003200135507822253353507800210012213300500133700006900118020008018378370837119801000a83a190009aa839911299a9a83980108009109a9aa8380011119803801999804001000802990009aa8391111299a9a83980108009109a9aa83800111299a9837999ab9a3370e00290000388380999804003801802899980400380199a83c199aaa80500380100080288891109199800802001801088900091119191800802990009aa8399119a9a839000a4000446a6aa0de00444a66a60dc666ae68cdc78010048380378980380089803001990009aa8391119a9a838800a4000446a6aa0dc00444a66a60da666ae68cdc7801003837837080089803001889119000990009aa83811299a9a837800880191099803001180200089111a9809800911299a9a828800909a98048031111111111299a9a82e19809005801109a82f98090008a82e8a829190009aa836111299a9a8360008a8271109a9aa83480111299a9834199ab9a3371e00c0040d40d226a0a600226600e00c006444444444424666666666600201601401201000e00c00a0080060044002244246600200600424002244246600200600424002244246600200600424002242444600600822444004224440022400244246600200600440024442466600200800600440024424660020060044002424444444600e01044244444446600c012010424444444600a01024444444008244444440064424444444660040120104424444444660020120104002444666aa6018240026a022a02046a6aa09a00244666aa601e240026a028a02646a6aa0a000244666a6aa01800246601490000009119805801000919805000a400000266aa60162400246a6aa09a0024466aa0a0004666a6aa012002466aa601e2400246a6aa0a20024466aa0a80046aa02000200244666aaa01206c004002466aa601e2400246a6aa0a20024466aa0a80046aa01c002002666aaa008062004002222444666aa600824002a0a266aa60142400246a6aa0980024466aa09e0046aa016002666aa600824002446a6aa09a00444a66a6098666aa601e240026a01aa02046a6aa0a0002446601400400a00c2006266a0aa008006a0a400266aa60142400246a6aa098002446466aa0a0006600200a640026aa0ac44a66a6a0aa00226aa0160064426a6aa0a400444a66a60a266018004010266aa02000e0022600c006004640026aa09e4422444a66a6a0a200220044426600a004666aa600e2400200a00800222424446006008224424446600400a008224244460020082240022466a01444666a6a00e006440040040026a6a00a00244002640026aa092442244a66a6a0940022a09844266a09a600800466aa600c24002008002640026aa0904422444a66a6a09400226a6a01600644002442666a6a01a00a440046008004666aa600e2400200a008002266a00444a66a607c0042080200207a244246600200600424002266a00a44a66a6076004200220780762466a00644666a6a00c006440040040026a6a0080024400224424660020060042400224424660020060042400244246600200600440022424600200424002444424666600200a008006004400224424660020060042400244244446600800c00a424444600600a2444400424444002400224464646666ae68cdc39aab9d5002480008cd409cc014d5d0a80118029aba135744a00446a00c6a606266ae712401035054310003249926135573ca00226ea80044988d4c0640048888cdd2a400066ae80dd480219aba05335301f00313374a90001bb1498884cdd2a400466ae80dd400119aba0300800137629319aba0375200466ae80dd398138009bb14988d4c07400488888888d4d409c00c88cdd2a400066ae80dd480519aba0375201266ae80dd480419aba0375200e66ae80dd480319aba03374a900019aba0375000466ae80dd40009bb1498cd5d01ba90043357406ea400cdd8a4c44a66a6048666ae68cdc780100081301288030a99a9812199ab9a3372200400204c04a2008200a2444006244400424440024002446a6aa044004446a6aa04800644a66a6046666ae68cdc78020010128120998058018008812111a9801801111a9802801911999804802001801000910919800801801100091111919a9806802919a98070021299a9810199ab9a3371e0040020440422a00620424042466a601c00840424a66a6040666ae68cdc78010008110108a80188108a99a9a80300190a99a9a8038011099a9805801119a9806001119a9808001119a98088011198058010009012119a9808801101211980580100091101211119a98070021012111299a9812999ab9a3370e00c00604e04c2a66a604a666ae68cdc380280101381309980b80200088130813080f8a99a9a8030009080f880f91199ab9a3371e0040020380362424460040062244002240024244600400644424466600200a00800640024244600400642446002006400244666ae68cdc38010008090089111199980300200180100091110919998008028020018011000891001110911980080200190009111111109199999998008048040038030028020018011000891091980080180109000990009aa807111299a9803199ab9a3370e0029000004003880109980180099b850020013200135500d2253353500c001137629311099aba037520046008002640026aa0184444a66a6a01a00420024426a6aa01400444a66a6012666ae68cdc78038010058050999804003801802899980400380199a809002002891001091000900088910919800801801089000990009aa803111999aab9f001250082335007375c6ae84008c00cd5d1001002090009000889100109109119800802001890008891918008009119801980100100099911991198012410121c9800866aa0089111c2739b84594eb8c5745b67795d07c67c9bdd1dbd52cdad176f5d3311500488112696e746572696d2d676f7665726e616e63650022123300100300220011122123300100300211200148811c9c5c2b78cb63dfedacc35cda81ac8a32c7d3f76bb9137d80d31430cb0048811cac2c4af1b7a1bab36c08784037a8c44f2f13939517f58c53793713760048811c5ea58d284c7e41763233d329f2af8f07be256ab861ccf274ce1b8ad60048811cc67322f7650e9f8fb32dc0335daa319ac9d160a6cbff7730425276070001", + "factorySpendScriptHash": "e533525a774432e6284e5c586a018b842fc53fe6975fadb6debc8305", + "factoryAddress": "addr1w82z6yrftsxz77el0ce2q4vuspcym2x0xgpgneurrwvasfge778fd", + "licenseEscrowAddress": "addr1w9jx45flh83z6wuqypyash54mszwmdj8r64fydafxtfc6jgrw4rm3" +} diff --git a/packages/sundaeswap-v1/src/scripts/preprod-scripts.json b/packages/sundaeswap-v1/src/scripts/preprod-scripts.json new file mode 100644 index 0000000..039fca2 --- /dev/null +++ b/packages/sundaeswap-v1/src/scripts/preprod-scripts.json @@ -0,0 +1,17 @@ +{ + "factoryMintScript": "590d49590d46010000332333222323233223322333222323232332232333222332233322232332232323332223233322232333333332222222232323333222232323233322232323233223322323233333222223322332233223322332233222332230020012232232325335305a330063333573466e1d40112002204a23333573466e1d40152000204c2350413530423357389201035054310004349926498cccd5cd19b8735573aa004900011980519191919191919191919191999ab9a3370e6aae754029200023333333333018335026232323333573466e1cd55cea8012400046603c606c6ae854008c0acd5d09aba250022350503530513357389201035054310005249926135573ca00226ea8004d5d0a80519a8130139aba150093335502d75ca0586ae854020ccd540b5d728161aba1500733502603a35742a00c66a04c66aa096086eb4d5d0a8029919191999ab9a3370e6aae754009200023350203232323333573466e1cd55cea80124000466a05066a078eb4d5d0a80118209aba135744a00446a0a86a60aa66ae712401035054310005649926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502633503c75a6ae854008c104d5d09aba250022350543530553357389201035054310005649926135573ca00226ea8004d5d09aba250022350503530513357389201035054310005249926135573ca00226ea8004d5d0a80219a8133ae35742a00666a04c66aa096eb88004d5d0a80118199aba135744a00446a0986a609a66ae71241035054310004e49926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031180e98191aba135573ca00646666ae68cdc3a801240084603860786ae84d55cf280211999ab9a3370ea00690011180e18161aba135573ca00a46666ae68cdc3a802240004603e6eb8d5d09aab9e50062350473530483357389201035054310004949926499264984d55cea80089baa001357426ae8940088d4100d4c104cd5ce249035054310004249926104113503f353040335738920103505435000414984d55cf280089baa001135573a6ea800488d4c01000488c8d4c02400c8c88888888894cd4c1480404c02801c4ccc8894cd4d4178cc170029418041a0884d4d5541940088894cd4d418c01054cd4c1b0ccd5cd19b8f00800306e06d15335306c333573466e3c01c0081b81b44c01800441b441b48841bd4030cdc524410873636f6f70657220003322333064371a00200200466e0cd4d406001088d4d4070004894ccd4d40d8008840044d4d41698984d4d4169899208090e4c00433716900024500253353065333573466e24005200006606713353056120012353018001223530290012223330530025013488107666163746f72790000c10673200135505d22533535053001105d221353550580022253353060333573466e3c009402018818454cd4c180ccd4c1504800488d4d5417000888d4d5417800c894cd4c198ccd5cd19b8f0040020680671333573466e1c00c0041a019c419c004cd4160cd5416d22107666163746f7279004800941644cd4c12403488cd4c14c480048cc894cd4c194ccd5cd19b8f35305100222002353051001220020670661333573466e1cd4c14400888004d4c1440048800419c1984198d4c0540048800800c0248cd4c148480048d4c05000488d4c094004888ccc13c00801d220107666163746f72790000810611300600315333353012001213503e35303f3357389201024c68000404988400484d40f8d4c0fccd5ce249024c680004049884d40f8d4c0fccd5ce2481024c68000404988848cc00400c0088004888888888848cccccccccc00402c02802402001c01801401000c00880048848cc00400c008800448848cc00400c0084800448848cc00400c0084800448848cc00400c00848004848888c010014848888c00c014848888c008014848888c004014800448c88c008dd6000990009aa822911999aab9f0012503e233503d30043574200460066ae880080ac8c8c8c8cccd5cd19b8735573aa006900011998039919191999ab9a3370e6aae754009200023300d302935742a00466a02004a6ae84d5d1280111a8171a981799ab9c491035054310003049926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a018eb8d5d09aba2500223502a35302b335738921035054310002c49926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540fc88c8cccd55cf8011281c919a81c19aa81d98031aab9d5002300535573ca00460086ae8800c0984d5d080089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a0486a604a66ae712410350543100026499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046604c600a6ae854008dd69aba135744a00446a03c6a603e66ae71241035054310002049926135573ca00226ea80048c8cccd5cd19b8735573aa002900011bae357426aae7940088d4070d4c074cd5ce249035054310001e499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a80f9a981019ab9c490103505431000214992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880cc8cccd5cd19b8750024800080cc8d405cd4c060cd5ce2490350543100019499264984d55ce9baa001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d407cd4c080cd5ce2481035054310002149926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4040d4c044cd5ce2490350543100012499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423500a35300b3357389201035054310000c499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa03a600c6ae854008c014d5d09aba25002235007353008335738921035054310000949926135573ca00226ea8004498480048004888c8c8c004014c8004d5407c894cd4d4054004407c884d4d54068008894cd4c088ccd5cd19b8f00200902402313007001130060033200135501e22533535014001101e221353550190022253353021333573466e3c00801c08c08854cd4c084ccd5cd19b870014800008c0884088408c4c01800c88488cc00801000c8488c00400c80048848cc00400c00880044880084880048004c8004d54048884894cd4d402800440508854cd4c054c010008405c4cd4c018480040100044848c00400848004c8004d5403c8848894cd4d402000854cd4d4020004404c8840508854cd4d402800c40508854cd4c054cc01c0100084ccd4c0244800401c00c0044058c8004d540388894cd4d40140084004884d4d5402800888cc01c00cccc020008004014c8004d5403488894cd4d40140084004884d4d54028008894cd4c048ccd5cd19b870014800005004c4ccc02001c00c0144ccc02001c00ccd4028ccd55403c01c0080040144488008488488cc00401000c48004448848cc00400c00844800444488848ccc00401000c0084448004c8004d540148888c94cd4c01cccd5cd19b8900248000024020400c54cd4c01cccd5cd19b89004480000240204cdc599b860024820010c018cdc18012410008266e2ccdc32800a41000866600a66e040112002333718900119b81371a006900100199b803370600490400219b83500148200104cdc019b860014820010cdc700124000640026aa00844a66a6006666ae68cdc3800a400000a008291010013371666e1800520800430023370600290400209100109100090008891918008009119801980100100099991119911999111980199802a4520af7ee5e9bdf11c4810627499bbb065efed9ba2f748735d01e1db56929a38ca160048008cd401d2211caaaf709a120c4c00db6656253332319f8c75034b9d224a599376805a0033500748811caaaf709a120c4c00db6656253332319f8c75034b9d224a599376805a00500822122330020040032122300100320012212330010030022001112200212212233001004003120011", + "factoryMintScriptHash": "6bbf39934636181e15e94d1af9f95aee76d7832edebc20dfc2dff5bf", + "factoryAuthenToken": "6bbf39934636181e15e94d1af9f95aee76d7832edebc20dfc2dff5bf.666163746f7279", + "poolMintScript": "5906275906240100003332332233322232333222333222323322323333333322222222332233333222223332223232323232222322323535502c5001225335300e335300d120012325335301033300b001353550305005220015007101215335301033300b00100950071012133300b00100a48907666163746f7279003535502f300b0012253353502e0011350264988854cd4d40c00044d40a1262213535503530110022253353503400113502c4988854cd4d40d80044d40b9262215335350380011350304988854cd4d40e800454cd4d40e80244ccd5406dd719aa80dbae75a0084426a068931109a81a24c004204426a0466a604266ae71240103505435000224984d4d540acc01c004894cd4d40a80044c0a458884d4d540bcc02c008894cd4d40b80044c0b4588854cd4d40c00044c0bc588854cd4d40c80044c0c4588854cd4d40d00044c0cc588854cd4d40d80044c0d4588854cd4d40e00044c0dc588854cd4d40e80044c0e4588854cd4d40f00044c0ec588854cd4d40f80044c0f4588854cd4d41000044c0fc588854cd4d410800454cd4d410805c4c104588c8c8854cccd4c0b0cccd5cd19b875002480188c0c4c8c8c8c8c8c8cccd5cd19b8750014803080f88cccd5cd19b8750024802881008cccd5cd19b875003480208cc0f8dd71aba15005375a6ae84d5d1280291999ab9a3370ea00890031198201bae35742a00e6eb8d5d09aba2500723333573466e1d40152004233045304735742a0126eb8d5d09aba2500923333573466e1d40192002230473048357426aae79402c8cccd5cd19b875007480008c118c124d5d09aab9e500c23504c35304a335738921035054310004b49926499264992649926135573aa00826aae79400c4d55cf280109aab9e5001137540026ae84d55cf280211999ab9a3370ea006900211818181e9aba135573ca00a46666ae68cdc3a80224004460606464646666ae68cdc39aab9d5002480008cc0e4c8c8cccd5cd19b8735573aa002900011bae357426aae7940088d4120d4c118cd5ce249035054310004749926137540026ae854008dd69aba135744a00446a08a6a608666ae712401035054310004449926135573ca00226ea8004d5d09aab9e500623333573466e1d4015200023033375c6ae84d55cf280391a8211a982019ab9c49010350543100041499264992649884c11858854cd4d411c0084cd5412cc09cdd600c80091098240b1098230b1098230b09aab9d50011375400244260862c266e29220102702000375c0024446464600200a640026aa05644a66a6a05000220184426a6aa05a00444a66a601e666ae68cdc78010048088080980380089803001990009aa81511299a9a81380088059109a9aa81600111299a9807199ab9a3371e00400e02001e2a66a601c666ae68cdc3800a400002001e201e20202600c00646466aa04e6aae754004c00cd55cf280089baa001320013550252233335573e0024a04a466a0486ae84008c00cd5d100124c224464460046eac004c8004d5409888c8cccd55cf80112813919a81319aa81498031aab9d5002300535573ca00460086ae8800c06c4d5d0800990009aa81191091299a9a8110008803110a99a980398020010804899a980309000802000891001091000900090911118020029091111801802909111180100290911118008029000910919800801801100090911111118038041109111111198030048041091111111802804091111110020911111100191091111111980100480411091111111980080480410009191919191999ab9a3370ea002900111998059bad35742a0086eb4d5d0a8019bad357426ae89400c8cccd5cd19b875002480008c034c8c8c8cccd5cd19b875001480088c04cdd71aba135573ca00646666ae68cdc3a801240004602a6eb8d5d09aab9e500423500f35300d335738921035054310000e499264984d55cea80089baa001357426aae7940188d4028d4c020cd5ce2490350543100009499264984d55cea80189aba25001135573ca00226ea80044800480044988488c00800c888488ccc00401401000c80048488c00800c8488c00400c8004984488008488488cc00401000c48004448848cc00400c008448004448c8c00400488cc00cc0080080052211c6bbf39934636181e15e94d1af9f95aee76d7832edebc20dfc2dff5bf0048810001", + "poolMintScriptHash": "0f3c6bf44ab1b1f9507be0a91f41081a40a5172cb9fd6cd248ec0e32", + "orderSpendScript": "59084959084601000033233322232332232333222323332223322323332223233223233223332223333222233322233223322332233223332223322332233322232323232322222325335300b001103c13503d35303b3357389201035054350003c498ccc888c8c8c94cd4c05c0144d4c0680188888cd4c04c480048d4c0ed40188888888888cd4c078480048ccd5cd19b8f375c0020180440420066a6040006446a6048004446a605000444666aa60302400244a66a6a07c0044266a08c0020042002a0886466a002a088a08a2446600466a609000846a0820024a0806600400e00226a606ca002444444444466a6032240024646464666ae68cdc399991119191800802990009aa82c1119a9a826000a4000446a6aa08a00444a66a6050666ae68cdc78010048150148980380089803001990009aa82b9119a9a825800a4000446a6aa08800444a66a604e666ae68cdc7801003814814080089803001999aa81e3ae335503c75ceb4d4c084cccd5cd19b8735573aa006900011998119aba1500335742a00466a080eb8d5d09aba2500223505135304f33573892010350543100050499262220020183371491010270200035302801422220044800808007c4d5d1280089aab9e500113754002012264a66a6a070601a6aae78dd50008a81a910a99a9a81d0008a81b910a99a9a81e0008a81c910a99a9a81f0008a81d910a99a9a8200008a81e910a99a9a8210008a81f910a99a9a8220008a820910a99a9a8230008a821910a99a9a8240008a822910a99a9a8250008a823910a99a9a82600089999999999825981000a18100090080071810006181000500418100031810002001110a8259a980a1999ab9a3370e6aae754009200023301635742a0046ae84d5d1280111a8211a982019ab9c490103505431000414992622002135573ca00226ea8004cd40148c8c8c8c8cccd5cd19b8735573aa00890001199980d9bae35742a0086464646666ae68cdc39aab9d5002480008cc88cc08c008004c8c8c8cccd5cd19b8735573aa004900011991198148010009919191999ab9a3370e6aae754009200023302d304735742a00466a07a4646464646666ae68cdc3a800a4004466606a6eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d4009200023037304e357426aae7940188d4154d4c14ccd5ce2490350543100054499264984d55cea80189aba25001135573ca00226ea8004d5d09aba2500223504e35304c335738921035054310004d49926135573ca00226ea8004d5d0a80119a81cbae357426ae8940088d4128d4c120cd5ce249035054310004949926135573ca00226ea8004d5d0a80119a81abae357426ae8940088d4118d4c110cd5ce249035054310004549926135573ca00226ea8004d5d0a8019bad35742a00464646464646666ae68cdc3a800a40084605c646464646666ae68cdc3a800a40044606c6464646666ae68cdc39aab9d5002480008cc88cd40f8008004dd69aba15002375a6ae84d5d1280111a8289a982799ab9c491035054310005049926135573ca00226ea8004d5d09aab9e500423333573466e1d40092000233036304b35742a0086eb4d5d09aba2500423504e35304c335738921035054310004d499264984d55cea80109aab9e5001137540026ae84d55cf280291999ab9a3370ea0049001118169bad357426aae7940188cccd5cd19b875003480008ccc0bcc11cd5d0a8031bad35742a00a66a072eb4d5d09aba2500523504a353048335738920103505431000494992649926135573aa00626ae8940044d55cf280089baa001357426ae8940088d4108d4c100cd5ce249035054310004149926135744a00226ae8940044d55cf280089baa0010033350052323333573466e1d40052002201623333573466e1d40092000201623504035303e335738921035054310003f499264984d55ce9baa001002335005200100112001230023758002640026aa072446666aae7c004940c08cd40bcd5d080118019aba2002498c8004d540e088448894cd4d40bc0044008884cc014008ccd54c01c48004014010004c8004d540dc884894cd4d40b400440188854cd4c01cc01000840244cd4c01848004010004488008488004800488848ccc00401000c00880048848cc00400c00880044880084880048004888848cccc00401401000c00880048848cc00400c00880048848cc00400c00880048848cc00400c00880048488c00800c888488ccc00401401000c800484888c00c0108884888ccc00801801401084888c00401080048488c00800c88488cc00401000c800448848cc00400c008480044488c88c008dd5800990009aa80d11191999aab9f0022501223350113355008300635573aa004600a6aae794008c010d5d100180c09aba10011122123300100300211200112232323333573466e1d400520002350083005357426aae79400c8cccd5cd19b87500248008940208d405cd4c054cd5ce24810350543100016499264984d55cea80089baa00112122300200311220011200113500d35300b3357389211f556e6578706563746564205478496e666f20636f6e737472756374696f6e2e0000c498888888888848cccccccccc00402c02802402001c01801401000c00880044488008488488cc00401000c480048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423500c35300a3357389201035054310000b499264984d55cea80089baa001212230020032122300100320012323333573466e1d40052002200823333573466e1d40092000200a2350073530053357389210350543100006499264984d55ce9baa0011200120011261220021220012001112323001001223300330020020014891c0f3c6bf44ab1b1f9507be0a91f41081a40a5172cb9fd6cd248ec0e320001", + "orderSpendScriptHash": "6c7da4287336dfb1d34c64f85efbb48d674367469df95b822140d4a5", + "orderAddress": "addr_test1wpk8mfpgwvmdlvwnf3j0shhmkjxkwsm8g6wljkuzy9qdffg56qf9y", + "poolSpendScript": "5929245929210100003333323332223232333222333222333222323322332233223322332233322232323232332233223322323233223233223332223332223333222232323232323333333322222222323233332222323232323232323232332233223233223232323232332233223322333222332233223233223322332233223232323232332232333322223233223232323233332222323232323232333332222233223232333322223232323232332223222222232533530ad010011087011350384901035054350033322235305b0032232223232323232353508f0100a22325335305700f223213530290112235304f002223222222223225335350b4013504a12233355055223530b0010022223530c4010032233530d10100220072533530d701333573466e3c0040c036404360044cc02802001c401c0040344d4c2b004d42a405262223330050030020012215335350b60100113530ae010022223330070030020012213530b001350ad01498888ccc02400c008004888d4c2b404d4c0e0080894cccd4c0f000484d42b404d4c2a804cd5ce2491a73637269707420696e707574206e6f7420666f756e6420212121000ab0149884d42b404d4c2a804cd5ce24811a73637269707420696e707574206e6f7420666f756e6420212121000ab0149884d42b404d4c2a804cd5ce24811a73637269707420696e707574206e6f7420666f756e6420212121000ab0149884d4c17c00c8888888888cc13002802c8c888c8c94cd4d42ec04d4144488ccd5417088d4c2dc04008888d4c32c0400c88cd4c36004008801c94cd4c37804ccd5cd19b8f001500f0e0010df0113300a008007100700101413530b301350b001498888ccc01000c0080048854cd4d42f4040044d4c2d404008888ccc01800c008004884d4c2dc04d42d0052622233300800300200122232323232353550c4010212232353550c701023223232323530695002222322533530e701333355305012001508f01220ea010100e90110e8011533530e701333573466e24d4d41dc07c88d4d41f4008894ccd4d42900400884d4d41f8010894ccd4d429c0400884cdc080080209a86500a4c26a194029309a86380a4c26a18e029324101756e061d2021d0022a66a6a1b80202442a66a6a1ba02660ea00203c42a66a61d202666ae68cdd7800991a984b0080091111a9a86200802111a9aa86d00801111a9aa86e00801911a9a851008039119ba548000cd5d019ba548000cd5d01999aa84e80ba975200c00a66ae80cccd5427405d4ba900400337629319aba0375201666ae80dd400519aba03374a900019aba0375000466ae80dd40009bb1498dd8a4c666612802074072a01006c1d6021d4022a66a61d202604c661980200ea1a4022a66a61d202666ae68cdc3a80401b87580875008a99a9874809999aa982909000a8488091076009824012075808a8020875008a99a9a8680098240120875009109a9aaa86b80801111299a9a86a808020a99a987700999ab9a3371e0900061e0021de022a66a61dc02666ae68cdc799b8a489036c70200003e0020f0010ef011533530ee01333573466e1c004cdc0a80681e07800877808a804887780887780887780910878808875008875008874808874008874008a99a9872809991299a987380999ab9a3370e6a6aa1a202004446660ba006004002900107480874008999ab9a337126644660040029000190009aa87500911299a9a8688080108009109980280099b8000348008c11800520060e9010e80110e80133550d00103f3371491010270200003500f1533530e501333573466e1cccc16403c02c028cdc0001282007380873008a99a987280999ab9a3370e6660b201e01000e66e00005410039c043980454cd4c39404ccc2f404104cdc52450873636f6f70657220000280241533530e501333573466e20cdc099b83353507501d2235350790012253335350a20100221001135350800162613535080016264820243930010ccc09d20004800920004800839c043980454cd4c39404ccd5cd19b8833305901948810048810000c0e6010e70115335350da01018215335350db013307300101c21333573466ebc004c8d4c2040400488cdd2a400066ae80dd480119aba037520026ec526330810102b02a0e9010e80110e70110e60110e60110e60110e60110e60110e60110e6011353068500122220011332235350d4010022223333302d00300200150ce0100433350d30100400102e33553048120012353550cb010012200100933305300d00200133305000a0020013370200266609a02a91100488100330a30148000004d4150488ccd5417c88d4c2e00400888d4c2f00400488c8c88d4c3480401488cd4c37c040089401494cd4c39404ccd5cd19b8f00150160e7010e601100b1500515335350d501333506c2323232323333573466e1cd55cea802240004666610a026eb8d5d0a8021919191999ab9a3370e6aae7540092000233223308d010020013232323333573466e1cd55cea8012400046644661be020040026140026ae854008cd428405d71aba135744a00446a194026a618e0266ae71240103505431000c80149926135573ca00226ea8004d5d0a80119a84e80bae357426ae8940088d431804d4c30c04cd5ce24903505431000c40149926135573ca00226ea8004d5d0a8019bad35742a00464646464646666ae68cdc3a800a40084617602646464646666ae68cdc3a800a4004461860266a12c02eb4d5d09aab9e500423333573466e1d400920002330c30130960135742a0086eb4d5d09aba250042350ce013530cb0133573892103505431000cc01499264984d55cea80109aab9e5001137540026ae84d55cf280291999ab9a3370ea00490011185d009bad357426aae7940188cccd5cd19b875003480008ccc2f004c24804d5d0a8031bad35742a00a66a14202eb4d5d09aba250052350ca013530c701335738920103505431000c8014992649926135573aa00626ae8940044d55cf280089baa001357426ae8940088d430804d4c2fc04cd5ce24903505431000c00149926135744a00226ae8940044d55cf280089baa0010210042135307f00122223232533530e701333573466e2001120000e8010e9011533530e701333573466e20ccc16ccccc8888cd54c14c480048d4d543580400488cd5436404008ccd4d54148004800488cc0200080048004ccd54c13848005434c04cd54c14c480048d4d543580400488cd5436404008d54154004ccd54c1384800488d4d5435c04008894cd4c3bc04ccd54c16048004c8cd42680488ccd4d413c00c88008008004d4d413400488004cd4134894cd4c3c40400843cc0440043c0048d4d543680400488cc02c008014018400c4cd435c0401000d435004004cd54c14c480048d4d543580400488c8cd543680400cc004014c8004d543c404894cd4d435c040044d5415400c884d4d5437004008894cd4c3d004cc0340080204cd5416801c0044c01800c0083940488004024d4190488cc008cd5435004d4d5434c040c088008cd434404cd5435004d4d5434c040c088004cdc09a9aa869808181119982f806801000999a985a80802919a985d0080091a9a860808009100111299a985f8080108008a4000444a66a617e02006200429000124000a1a40200291100488100337000089040497a0087400874808a99a987380999ab9a3371e00c06e1d2021d0022666a616602006466a61700200246a6a17e0200244a66a61d602666ae68cdc41a9aa86a8081911199830a80700100080107600876808a99a987580999ab9a337106a6aa1aa02062446660c2a01c0040020021d8021da022a66a61d602666ae68cdc4001240041d8021da022a66a61d602666ae68cdc4000a40041d8021da022a00c2a00a2a00a2a00a2a00a44a66a61d402666ae68cdc4299a985e8080109a9aa86a0081891199830280680100089a9aa86a0081811199830280680100080087580876008a99a987500999ab9a33710002900107580876008a8028a8020a802111299a987500999ab9a33710a66a617a0200626a6aa1a802062446660c0a01a00400226a6aa1a802060446660c0a01a0040020041d6021d8022a66a61d402666ae68cdc4001240041d6021d8022a00a2a0082a0084a66a61d002666ae68cdc419982e2804821281b80087480875008a99a987400999ab9a33710002900107480875008a8018a8010a8010a800880688068a99a9873009a861009a985f8099ab9c490110657363726f7720696e636f7272656374000c0014985400440304cc038cd5434004008cd5434004d4c2140400c8800800402c40184c144004004060c120008c1100084d4c2fc0400488cd4c330040088d42b804d4c2ac04cd5ce24918696e76616c696420706f6f6c20736372697074207574786f000ac014988004c8004d5431804894cd4d42b004004431c04884d4d5542cc04008888d4c2e00400c894cd4c33004ccccc04400800401000c0284c024018433404c8004d543040488894cd4c30804ccd5cd19b88003371a00a18602188022002266600866e0000d20023370490400200119b800013370466e3801400c0084d4c09803888d4c13000888888888894cd4c32004cd4c178480048d4c2980400488d4c2a8040048894cd4c33804ccc2a4040080a9220107666163746f72790015335350c301333505a2323333573466e1cd55cea800a405046464646464646464646666ae68cdc39aab9d5008480008cccccccc88888888cccccccc2180402001c01801401000c008004dd71aba15008375c6ae85401cdd71aba15006375c6ae854014dd71aba150043350810175a6ae85400cdd71aba15002375c6ae84d5d1280111a85b809a985a0099ab9c490103505431000b50149926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d55cf280089baa001357426aae7940088d42b404d4c2a804cd5ce24903505431000ab0149926137540026a607004044004008421a202219e02219e020142a66a61900266a60bc2400246a614c02002446a615402002444646a617e02008446a61820200644666618e020080060040026617e0261940204ca18802014219202219402219202640026aa17c02444444a66a6a150020022184024426a61440200444644a66a618e026a616e02008446a61720201a4466a618c02008466a618e0200646619602004002419c02466a618e02006419c02466196020040022a66a6a1780200242a66a6a17a0201642a66a619202666ae68cdc780100086580865008a802099999807006806005805003899999806806285f008050048030a99a9a85e0080510999998068060058050048030a801099999806005805004804002899299a986300999ab9a337126a6a170020104440066a6aa1600201a446660780060040021900218e022a66a618c02666ae68cdc49a9a85c008041110011a9aa858008061119981e00180100086400863808a99a986300999ab9a337126a6a17002010444002666074002040a02a1900218e0221900226666601601401201000e00826666601601401201000e00826666601601401201000e0086607000c0026a0064440026a0044440046a00244400664664646460020024466006600400400244246a6008246a60080066a00600200242466600246a006444600600846a006444600400846a006444600200800224444666008444444466a61200200c46a6a12e02002446464a66a618a02666ae68cdc48008020630086380899199801a400000266a13a02900000099b833370401266e040040100284c8ccc00c005200033509d0100148000cdc080200099b83337040040120104446a6a13802002446464646464646666602c66e0004c018cdc000900299b800110043350b60133550b90101533350c10100300200101000f33700006900019b8048000020cdc02400001066e0ccdc100100680799b810070023370200e00444646464a66a61300200a26666601a66e00028010024004cd42b404cd542c004030ccd42e00520004800000801c0184ccccc034028cdc000480200099a8568099aa85800806199a85c00a4000900000100380319b80007001337026a6a0e26607c66e08cdc100080119b81500f35350710102200233704a66a612c020062010200ea01e446a6a0e6666609c0069001001000912999a981f181e9999828002a4004008006420024200226a146029300319b82005005222222232323333300a3370200e00466e04018004cdc080280419a8550099aa85680804999a85a80801000a400000800666e0ccdc100380280219b833370400c00a00644444a66a6a14a020022666608c00400a0080064426a6aa1540200444666a611c020024666666601a00600201401201000e0084446a6a0e60244464a66a61360200c26464a66a619402666ae68cdc480800106580866008a99a9a85f8080310a999a9826998260018008a8010a80109a85380a4c2a00226a14c029309999980899b800100063370201e00201c66a1620266aa16802012666a178029000000a400001a01466e0ccdc119b8200e0050013370066e0803c008cdc100280089919299a986500999ab9a3371202200419602198022a66a6a17e0200c42a666a609a660980060022a0042a00426a14e02930a80089a85300a4c26666602266e04040004cdc000780300719a8588099aa85a00804999a85e00800a4000900000680519b833370466e0803c014004cdc019b8200e0023370400a00266e040040088ccccccc03000c00402802402001c0104cdc11a9a83100091000a4008266e29221036c70200000133500e2323232323333573466e1cd55cea80224000466660ba66a0ae66aa0f6eb9d71aba15004375c6ae85400cdd69aba1500233505875a6ae84d5d1280111a847009a98458099ab9c491035054310008c0149926135744a00226ae8940044d55cf280089baa00100333500e23232323333573466e1d40052002204d23333573466e1d400920002332233051002001375c6ae854010dd71aba135744a00846a11c026a61160266ae71241035054310008c01499264984d55cea80109aab9e50011375400200466a01c464646666ae68cdc39aab9d5002480008cc06cc8c8c8c8c8c8c8c8c8c8c8cccd5cd19b8735573aa01490001199999999982599a836119191999ab9a3370e6aae754009200023309e01307635742a00460e26ae84d5d1280111a84e009a984c8099ab9c491035054310009a0149926135573ca00226ea8004d5d0a80519a8360369aba150093335507175ca0e06ae854020ccd541c5d728381aba1500733506c07a35742a00c66a0d866aa10a0210602eb4d5d0a8029919191999ab9a3370e6aae754009200023350513232323333573466e1cd55cea80124000466a0b266a0f8eb4d5d0a8011840809aba135744a00446a140026a613a0266ae712401035054310009e0149926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233505733507c75a6ae854008c20404d5d09aba250022350a00135309d013357389201035054310009e0149926135573ca00226ea8004d5d09aba2500223509c01353099013357389201035054310009a0149926135573ca00226ea8004d5d0a80219a8363ae35742a00666a0d866aa10a02eb88004d5d0a80118399aba135744a00446a130026a612a0266ae7124103505431000960149926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea00290031181218391aba135573ca00646666ae68cdc3a801240084604660f86ae84d55cf280211999ab9a3370ea00690011181198361aba135573ca00a46666ae68cdc3a802240004604c6eb8d5d09aab9e50062350930135309001335738920103505431000910149926499264984d55cea80089baa001357426ae8940088d423004d4c22404cd5ce249035054310008a0149926135573ca00226ea80040044cc0840052014122123300100300212001233002001508b01320013550a30122253353508a0100210012213535508f010022233007003333008002001005320013550a201222253353508a0100210012213535508f0100222533530a701333573466e1c00520000a9010a8011333008007003005133300800700333508f013335550940100700200100512001320013550a00122112225335350890100110022213300500233355300712001005004001112122230030041122122233002005004112122230010041120013200135509b0122112253353508301001150850122133508601300400233553006120010040013200135509a01221122253353508301001135350450032200122133353504700522002300400233355300712001005004001221233001003002200121222230040052122223003005212222300200521222230010052001320013550920122253353507900213506e35306b33573892011a73637269707420696e707574206e6f7420666f756e64202121210006c498884d4c1c4008894cd4c258054cd4c25804ccd5cd19b8f353077002220023530770052200209801097011333573466e1cd4c1dc00888004d4c1dc014880042600425c04425c0440044cc01c00c0148cc0092002001223232325335309201333573466e200040082500424c044d41b8d4c1accd5ce2481216e6f7420656e6f7567682041646120746f20636f766572207468652072696465720006c49854cd4c24804ccd5cd19b8733702002004900004a00849808a801899a83d19aa83ea4410033507a335507d4890033702002004a0f6a00666600a006910100488100337049040497a00801899980224500001507822232323001005320013550930122335350790014800088d4d541f8008894cd4c25804ccd5cd19b8f00200909801097011300700113006003320013550920122335350780014800088d4d541f4008894cd4c25404ccd5cd19b8f00200709701096011001130060033200135508e01222253353507600210012213535507b002225335309301333573466e3c01c00825404250044ccc02001c00c0144ccc02001c00ccd41ec010014c8004d542340488c8c8c94cd4c23c04ccd5cd19b8833704a006a0049000048808480088038a99a984780999ab9a3370ea0069000048808480089804240002a66a611e02666ae68cdc3a801a801048808480089804240042a66a611e02666ae68cdc42801a801048808480089804a40002a66a611e02666ae68cdc42801a400012202120022600a6601466e05200050033370290002801099800a400466e01200233706a006a004640026aa1220244464a66a612402666ae68cdc399b8233704006006a00aa00c1280212602260160062a66a612402666ae68cdc380119b80003480082500424c044c03000c54cd4c24804ccd5cd19b893370466e0940054005401540182500424c044cc01140040084cc01000d40044cdc199b80002001480104d4d40ec008880044d4d40e80048800884888c00c01084888c0080104888004800488cc044cdc1001299a984380999ab9a3370e002900004480844008a40002a66a610e02666ae68cdc4800a4000110021120229001099b814800120025335308701333573466e200052000089010880113370290000008800990009aa8438091119b803370266e14cdc099a9802890009a802091198012405c6600490520119801240346600490650199801241a4046600490650119801241340666004905c0199801241f4026600490720199801241dc0466004905f0119801241fc0266004902019801240ac6600490570080099b81001480080052080043371c0049000299a984380999ab9a3370e002900104480844008a4000266006666e312002337026e3400920020025335308701333573466e1ccdc3000a400890000448084400899b83001480104cdc019b8248018005200212335001506c506d320013550850122122533530860133323535006001222222220063333333350060850100722333573466e20008004228042240488ccd5cd19b8900200108a010890122333573466e24008004224042280488ccd5cd19b880020010890108a01225335308801333573466e24008004228042240440044008894cd4c22004ccd5cd19b8900200108a010890110021001001480004d40452410350543600153353506d00213501149103505437002215335308801333573466e1c00d200008a01089011002133530061200100133702006900109111111109199999998008048040038030028020018010900091299a984080999ab9a3370e0040021060210402200c2a66a610202666ae68cdc48010008418084100880208028911001891100109110009000889119000990009aa83f91299a9a83280088019109980300118020009111091999800802802001801100091111980299b803370400800266e0800800ccdc10018009119299a983c999ab9a3370e004900003d83d09a80224810350543300133502833708006a00266e1000940044cc1094cd4c1e0ccd5cd19b88002480001e81e44cdc0a40000042004a66a60f0666ae68cdc4000a40000f40f2266e0520000011001123505235304f3357380020a09309111a982b000911299a9a837800909a98048031111111111299a9a83d19809005801109a83e98090008a83d8a838190009aa83b111299a9a82e8008a8361109a9aa83100111299a983d199ab9a3371e00c0040f80f626a0e200226600e00c006444444444424666666666600201601401201000e00c00a0080060044002244246600200600424002244246600200600424002244246600200600424002640026aa0da44244a66a6a0aa00220de442a66a60e0600800420e4266a600c240020080022424600200424002442466002006004400244244660040080062440024002444424666600200a0080060044002442466002006004400244444444246666666600201201000e00c00a00800600440024646666ae68cdc3a800a4004406646666ae68cdc3a80124000406a46a0786a607266ae712401035054310003a499264984d55ce9baa00112232323333573466e1cd55cea801240004664466a072004002600a6ae854008c014d5d09aba2500223503c353039335738921035054310003a49926135573ca00226ea8004488c8c8cccd5cd19b8735573aa0049000119a80718029aba150023005357426ae8940088d40ecd4c0e0cd5ce249035054310003949926135573ca00226ea8004448888cdd2a400066ae80c010008cd5d018018009bb14984cd4008888c00cc008004800448848cc00400c008480048888cccc01801000c008004888848cccc00401401000c008800448848cc00400c0084800448c88c008dd6000990009aa82a111999aab9f0012503d233503c30043574200460066ae880080b48c8c8c8cccd5cd19b8735573aa0069000119981a18031aba150033335500975ca0106ae854008cd401dd71aba135744a00446a0606a605a66ae71241035054310002e49926135744a00226aae7940044dd5000919191999ab9a3370e6aae7540092000233043301a35742a00466a00a0326ae84d5d1280111a8171a981599ab9c491035054310002c49926135573ca00226ea8004488c8c8cccd5cd19b875001480008d4124c014d5d09aab9e500323333573466e1d400920022504923502f35302c3357389201035054310002d499264984d55cea80089baa0011335500175ceb44488c88c008dd5800990009aa82811191999aab9f0022503a2335039335503c300635573aa004600a6aae794008c010d5d100181509aba1001232323333573466e1cd55cea80124000466060600a6ae854008dd69aba135744a00446a0546a604e66ae71241035054310002849926135573ca00226ea80048c8cccd5cd19b8735573aa002900011bae357426aae7940088d40a0d4c094cd5ce2490350543100026499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8159a981419ab9c490103505431000294992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800881208cccd5cd19b8750024800081208d408cd4c080cd5ce2490350543100021499264984d55ce9baa001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d40acd4c0a0cd5ce2481035054310002949926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660706eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002303a3008357426aae7940188d4070d4c064cd5ce249035054310001a499264984d55cea80189aba25001135573ca00226ea80048c8c8cccd5cd19b875001480088c0e0dd71aba135573ca00646666ae68cdc3a80124000460746eb8d5d09aab9e500423501935301633573892010350543100017499264984d55cea80089baa0011122232323333573466e1cd55cea80124000466aa050600c6ae854008c014d5d09aba25002235019353016335738921035054310001749926135573ca00226ea8004c8004d540e48894cd4c0e4ccd5cd19b87001480000ec0e840084cc00c004cdc2801000990009aa81c111299a9a80f8008801110998029a9aa8120011119b8000600200121222300300422212223330020060050042122230010042001212230020032212233001004003200112200212200120011221233001003002120013200135502b22225335350130011350083530053357389212373636f6f70657220746f6b656e206d7573742065786973747320696e20696e7075747300006498884d4c02c00888d4c03c0048894cd4c0ccccc03800802802440d44ccc02c028024018480048004888c8c8c004014c8004d540b0894cd4d404800440b0884d4d5405c008894cd4c0bcccd5cd19b8f00200903103013007001130060033200135502b22533535011001102b22135355016002225335302e333573466e3c00801c0c00bc54cd4c0b8ccd5cd19b87001480000c00bc40bc40c04c01800c4988848cc00400c008800488848ccc00401000c00880048848cc00400c0088004c8004d540808894cd4d401c0084004884d4d54030008894cd4d4064cccc0280080154069403484d4d5403c00488d4d5404400888c8d4d4070ccd403c03801c00c888cc040030cd4054ccd55406802cccd404804402801801001ccdc0000a400426600e00666a018666aaa02200400290010029119b80002001122235350100022223535013004222323232333501a0030020013300b0060033300a006003330090060033200135501d222225335350060031533535014002213535500a0012235019335500d335500d0020010041501522135355500d00222253353501900721333300b00a00500833500d00600715335302435301200922353016002223530160072235301a002225335302c3333020006005002001153353502100721533535022004213302c002001102e15335350210032102e102e102d1333300a0090043501b335500f0020010061333300a009004501a33500c00500611220021221223300100400312001112212330010030021120011112221233300100400300211120012212330010030022001221233001003002200112221233300100400300212001222232335300c0052335300d004253353013333573466e3c0080040540505400c405080508cd4c034010805094cd4c04cccd5cd19b8f00200101501415003101415335350050032153353500600221335300a0022335300b0022335300f002233530100022330140020012017233530100022017233014002001222017222335300d004201722253353018333573466e1c01800c06806454cd4c060ccd5cd19b8700500201a01913301700400110191019101215335350050012101210121212230020031122001120012122300200322212233300100500400320012122300200321223001003200122333573466e3c00800401801488ccd5cd19b870020010050041220021220012001112323001001223300330020020014891c6bbf39934636181e15e94d1af9f95aee76d7832edebc20dfc2dff5bf0048811c0f3c6bf44ab1b1f9507be0a91f41081a40a5172cb9fd6cd248ec0e320048811c646ad13fb9e22d3b802049d85e95dc04edb6471eaa9237a932d38d490048811c6c7da4287336dfb1d34c64f85efbb48d674367469df95b822140d4a50001", + "poolSpendScriptHash": "57ca0438f1c384d6a819c5c55e6b5cdf54936f926363ec48fbc2bd43", + "poolAddress": "addr_test1wptu5ppc78pcf44gr8zu2hnttn04fym0jf3k8mzgl0pt6sc0t92jy", + "factorySpendScript": "59265e59265b0100003323322332233322233322233223322333332222233223232333222323332223233333333222222223233322232333322223232332232333222323232332233223232333332222233223322332233223322332222323232322323232232325335303533300d3333573466e1cd55cea805a400046666444466660b40080060040026eb8d5d0a805991919191999ab9a3370ea002900111991198310010009bad35742a0066464646464646464646666ae68cdc39aab9d5008480008cccccccc88888888cccccccc1d402001c01801401000c008004dd71aba15008375c6ae85401cdd71aba15006375c6ae854014dd71aba150043232323333573466e1cd55cea80124000466a0e66eb4d5d0a8011bad357426ae8940088d4178d4c17ccd5ce2481035054310006049926135573ca00226ea8004d5d0a8019bae35742a0046eb8d5d09aba2500223505a35305b335738921035054310005c49926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d55cf280089baa001357426ae89400c8cccd5cd19b8750024800081888d4144d4c148cd5ce24810350543100053499264984d55cea80109aab9e5001137540026ae854028dd71aba1500933502075c6ae84d5d1280491a8259a982619ab9c491035054310004d499263333573466e1d4011200823051375c6ae84d55cf280391999ab9a3370ea00a9002102811999ab9a3370ea00c9001102911999ab9a3370ea00e9000119911982b801000999aa826bae75c6ae854024ccd54135d73ae357426ae8940248d4138d4c13ccd5ce24903505431000504992649926498cccd5cd19b8735573aa004900011980819191919191919191919191999ab9a3370e6aae75402920002333333333301e33502c232323333573466e1cd55cea8012400046604860786ae854008c0c4d5d09aba2500223505b35305c335738921035054310005d49926135573ca00226ea8004d5d0a80519a8160169aba150093335503375ca0646ae854020ccd540cdd728191aba1500733502c04535742a00c66a05866aa0ac09ceb4d5d0a8029919191999ab9a3370e6aae754009200023350263232323333573466e1cd55cea80124000466a05c66a088eb4d5d0a80118249aba135744a00446a0be6a60c066ae712401035054310006149926135573ca00226ea8004d5d0a8011919191999ab9a3370e6aae7540092000233502c33504475a6ae854008c124d5d09aba2500223505f3530603357389201035054310006149926135573ca00226ea8004d5d09aba2500223505b35305c3357389201035054310005d49926135573ca00226ea8004d5d0a80219a8163ae35742a00666a05866aa0aceb88004d5d0a801181d9aba135744a00446a0ae6a60b066ae71241035054310005949926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d5d1280089aba25001135573ca00226ea8004d5d0a8011919191999ab9a3370ea002900311811981e9aba135573ca00646666ae68cdc3a8012400846044608e6ae84d55cf280211999ab9a3370ea00690011181118191aba135573ca00a46666ae68cdc3a802240004604a6eb8d5d09aab9e50062350523530533357389201035054310005449926499264984d55cea80089baa001357426ae8940088d412cd4c130cd5ce249035054310004d49926104c13504a35304b3357389201035054350004c4984d55cf280089baa001135573aa00426aae7940044dd500089aba25001135744a00226aae7940044dd50009109198008018011000911111111109199999999980080580500480400380300280200180110009109198008018011000891091980080180109000891091980080180109000891091980080180109000909111180200290911118018029091111801002909111180080290008919118011bac001320013550422233335573e0024a07a466a07860086ae84008c00cd5d100101811919191999ab9a3370e6aae75400d200023330073232323333573466e1cd55cea8012400046601a605c6ae854008cd40400a8d5d09aba25002235033353034335738921035054310003549926135573ca00226ea8004d5d0a801999aa805bae500a35742a00466a018eb8d5d09aba2500223502f353030335738921035054310003149926135744a00226aae7940044dd50009110919980080200180110009109198008018011000899aa800bae75a224464460046eac004c8004d540f088c8cccd55cf8011281c119a81b99aa81998031aab9d5002300535573ca00460086ae8800c0ac4d5d080089119191999ab9a3370ea002900011a80418029aba135573ca00646666ae68cdc3a801240044a01046a0526a605466ae71241035054310002b499264984d55cea80089baa001121223002003112200112001232323333573466e1cd55cea8012400046600c600e6ae854008dd69aba135744a00446a0466a604866ae71241035054310002549926135573ca00226ea80048848cc00400c00880048c8cccd5cd19b8735573aa002900011bae357426aae7940088d407cd4c080cd5ce24810350543100021499261375400224464646666ae68cdc3a800a40084a00e46666ae68cdc3a8012400446a014600c6ae84d55cf280211999ab9a3370ea00690001280511a8111a981199ab9c490103505431000244992649926135573aa00226ea8004484888c00c0104488800844888004480048c8cccd5cd19b8750014800880188cccd5cd19b8750024800080188d4068d4c06ccd5ce249035054310001c499264984d55ce9baa0011220021220012001232323232323333573466e1d4005200c200b23333573466e1d4009200a200d23333573466e1d400d200823300b375c6ae854014dd69aba135744a00a46666ae68cdc3a8022400c46601a6eb8d5d0a8039bae357426ae89401c8cccd5cd19b875005480108cc048c050d5d0a8049bae357426ae8940248cccd5cd19b875006480088c050c054d5d09aab9e500b23333573466e1d401d2000230133016357426aae7940308d407cd4c080cd5ce2481035054310002149926499264992649926135573aa00826aae79400c4d55cf280109aab9e500113754002424444444600e01044244444446600c012010424444444600a010244444440082444444400644244444446600401201044244444446600201201040024646464646666ae68cdc3a800a400446660106eb4d5d0a8021bad35742a0066eb4d5d09aba2500323333573466e1d400920002300a300b357426aae7940188d4040d4c044cd5ce2490350543100012499264984d55cea80189aba25001135573ca00226ea80048488c00800c888488ccc00401401000c80048c8c8cccd5cd19b875001480088c018dd71aba135573ca00646666ae68cdc3a80124000460106eb8d5d09aab9e500423500a35300b3357389201035054310000c499264984d55cea80089baa001212230020032122300100320011122232323333573466e1cd55cea80124000466aa020600c6ae854008c014d5d09aba25002235007353008335738921035054310000949926135573ca00226ea80044984800480048848888cc010018014848888c00c01448888008488880048004448848cc00400c008448004888848cccc00401401000c00880044488008488488cc00401000c4800448800888488cc00401000c80048888888848cccccccc00402402001c01801401000c008800448848cc00400c00848004448c8c00400488cc00cc008008004cccccc8ccc888cc88c8cc88ccc888c8c8c8cc88cc88ccc888cc88c8c8ccc888ccc888ccc888c8c8cc88c8c8cccc8888c8c8c8c8c8ccccc88888cc88cc88cc88cc88cc88cc88c8c8cc88c8c8c8c8cccc8888c8c8c8cccccccc88888888cc88cc88cc88cccc8888cc88cc88cc88cc88c8c8c8c8cc88c8c8c8c8c8c8c8cc88ccc888c8cccc8888c8c8c8c8ccccc88888cc88c8c8c8c888888888c8c8c8d4c17c02c88d4c220040208888d4c118d4c058028894cccd4c06800484d41d8d4c28404cd5ce2491a73637269707420696e707574206e6f7420666f756e6420212121000a20149884d41d8d4c28404cd5ce24811a73637269707420696e707574206e6f7420666f756e6420212121000a20149884d41d8d4c28404cd5ce24811a73637269707420696e707574206e6f7420666f756e6420212121000a20149884d4c0ec00c8888888888cc0ac02802c888c8c8c8c94cccd4c1d0048884c00d2080bab70321300248203032f24084c0052080bab7031300148202eadc0c8c94cccd4c1d404c8854cd4c28804cc0dc068d4c141401888800854cd4c28804ccc17025004024d4c14140188880085400c428c04428c04854cd4c28404cc0d8064d4c13d401488800854cd4c28404ccc16c24c04020d4c13d40148880085400842880442880454cd4c28004cc0d4060d4c139401088800854cd4c28004ccc1682480401cd4c1394010888008540044284044284045400454cd4c27c04ccd5cd19b8935301d011223530410022222222222353504d00422353505300222533353505700221353505400422533353505a002213370200200826a11a029309a84680a4c26a114029309a84500a4c00214202140022a6666a60e8024446426a6040028446a60880044444444444a66a6a16c02666aa60d424002446a60bc004444a66a61660266605a00405066e292210270200001f1533530b3013309701003330990130a301029509d0113350bc010050041004100450b801009135305c3508901498888cccc04000c008004d423005262215335350b801001135305e00222233330120030020010052213530603508d01498888cccc05000c008004d4240052622323222325333530313030323350a50100148008cdc11a9aa855808049119982328030010009a9aa8558080411199823280300100090980100090980100089918010009a84180a4c4a66a6150026a6aa15602012446a6aa15a0201444a666a61160266114020080042a666a6116026611402006002215a02215a02215c02215a02215c022a66a6150026666a07610802a02ea01866661360266046900100a0098090088a99a98540099983104d00a80c19a8588099aa8560080e9a8168911980119aa8570099b8a4890270200001648008cc008cd542b804cdc52441036c70200001600300150b2011533530a801333573466e20d4d542ac0402488ccc119401800800520020a9010aa011533530a801333573466e20d4d542ac0402088ccc119401800800520020a9010aa011533530a80133303e48018cd542b004074cdc524502702000014500415335350910133350462323232323333573466e1cd55cea80224000466664444666610802008006004002664464646666ae68cdc39aab9d5002480008cc88cd422404008004c014d5d0a80118029aba135744a00446a11c026a61720266ae71240103505431000ba0149926135573ca00226ea80048c8c8cccd5cd19b8735573aa0049000119aa85b009bae35742a0046eb8d5d09aba2500223508d013530b801335738920103505431000b90149926135573ca00226ea8004d5d0a8021bae35742a0066eb4d5d0a80119a84400bad357426ae8940088d422404d4c2d004cd5ce248103505431000b50149926135744a00226ae8940044d55cf280089baa00150170022135307a001222235350800100422533530af0133092010020101533530af01330920100100f1533530af01333573466e3c01406c2c4042c00454cd4c2bc04ccd5cd19b870040080b1010b001133355307212001350705073235350ab010042235350ad0100322533530b401333573466e1c0100082d8042d4044cc2980400c00442d404cd54c1c4480048004d40d0488cc008cc0f92002482807ccc008cc0f92006483403ccc008cc0f92002483200400442c00442c00442c00442c0044d420c052610a90110a90110a90110a90110a90110a90113232533530a601333573466e200040082a00429c044d420404d4c2b004cd5ce2481216e6f7420656e6f7567682041646120746f20636f76657220746865207269646572000ad0149854cd4c29804ccd5cd19b8733702002004900005400853808a801899a8578099aa85500a441003350af0133550aa014890033702002004a16002a006666082006910100488100337049040497a00a40042666144029110000150ad012135301e012223530420022222222222533530ac0133355306f120013506d507030940100d0031533530ac0133355306f120013506d507030940100d015135305a50102225335350980100121533535099013304f00100621533530b101333573466ebc004c234040942cc042c80454cd4c2c404ccc1ac28c05408542ec0442cc044cccc88894cd4d42f404c13001042d804884d4d5541440088894cd4d43080401054cd4c2e804ccd5cd19b8f0080030bc010bb011533530ba01333573466e3c01c0082f0042ec044c01800442ec0442ec048842f40540840a4cdc52450873636f6f70657220003302c33706a0449040487260022822905a008859008858808858008856808856808a99a9a853809a8120911a980f809911a98218011111111111199aa825111a9aa8590080e111a982e802111a9830800911299a985b0099981800100380309980b804804080400680509a98259a83d24c4466008004002442a66a6a1520200226a609a004446600c0040024426a609e6a0fc9311198040010008a99a984a808050850009109a984d00800911191911111299a9a859809a8180911a981580f911a98278011111111111199aa82b111a9834801111299a985f009985100801998520098570080e285400899809802802080200680489a982c9a84300a4c44466660120060040026a1120293110a99a9a85a8080089a982d80111119998058018010008029109a982e9a84500a4c444666601a0060040026a11a02931111299a9854809981f0030018a99a9854809999a81e04280a80c000999984e0080a85080a81e8090a99a9854809999aa98360900099a8399111801980100090009105600807055808a99a985480999ab9a3371066e05406802805c2a8042ac0454cd4d42c404c8d4c0a007088d4c1300088888888888ccd54c1c84800488d4c198008888d4c2800404888d4c28804014894cd4c2fc04cccc2980401000c0080044cd4320040240204021430004024cc23c04c26404081424c044d4c15cd42100526222333300800300200135087014988854cd4d42cc040044d4c164008888cccc02800c008004014884d4c16cd42200526222333300c0030020013508b0149842a80442a80442a80442a80488894cd4c2a004cccd40ec8cdd2a405066ae80c21804004dd8a4ca02e0020102a66a6a16002607e666aa60c824002446a6aa15a0200444a66a615802666ae68cdc7801245000ae010ad01100313350b50100400350b20100310a9012213535550440022225335350b5010041533530ad01333573466e3c09400c2bc042b80454cd4c2b404ccd5cd19b8f48907666163746f7279000020af010ae011533530ad01333573466e1c00520020af010ae01133306709f01501d3350b60133550b10100a3350b60133550b101488107666163746f7279004800942dc0542dc0442b80442b80442b8048842c00442a40442800488c94cd4c28004cc21004020cc21804c24004c8cd4c0bc0048d4c0cc00488800c8d4c26c0400488888888021400542280454cd4c28004ccc16824805404142a8044cd4c0b940048d4c0c80048894cd4c29004ccd5cd19b8f00e0020a6010a501133335037080015013500833330970101000f3301f4800803800442940494cd4c25c040304cccd40d01f540414014cccc25004034cc88cc26804008005404800402c0288842900442840442840454cd4d422004ccd40f48c8c8cccd5cd19b875001480088c8c0d0004c8c8c8c8cccd5cd19b8735573aa0069000119991119981e8018010009bae35742a0066eb8d5d0a801191857009bac001357426ae8940088d420c04d4c2b804cd5ce248103505431000af0149926135744a00226aae7940044dd50009aba135573ca00646666ae68cdc3a8012400046460680026464646464646464646666ae68cdc39aab9d5008480008cccccccc88888888cccccccc2c00402001c01801401000c008004dd71aba15008375c6ae85401cdd71aba15006375c6ae854014dd71aba150043350880175a6ae85400cdd71aba15002375c6ae84d5d1280111a844809a985a0099ab9c49103505431000b50149926135744a00226ae8940044d5d1280089aba25001135744a00226ae8940044d55cf280089baa001357426aae7940108d41fcd4c2a804cd5ce24903505431000ab01499264984d55cea80089baa001500e001210011353506e62615335350a4010011350774988854cd4d4298040044008884d41ed2635301900d2235303d002222222222253353509001533335302700b2150920121509201215092012133355306b120013350722253353509301002210031001509201235305500122533530ab013303200200413509601003150950100b2135305400122353058001222333553069120012235305d00222235309701008223530990100522533530b601333309d0100400300200113350bf01009008100850b70100f135082013530ad013357389201024c66000ae014984d4c03c00c880084d4c03800888d4c0c8008888888888801c4d4c03400488d4c0c40088888888888d4d40f401088d4d4104004894ccd4d411c008840044d4d41b98984d4d41b9898888c8c8c004014c8004d5425404894cd4d425004004423404884d4d5424404008894cd4c24004ccd5cd19b8f0020090920109101130070011300600332001355094012253353509301001108c012213535509001002225335308f01333573466e3c00801c244042400454cd4c23c04ccd5cd19b870014800024404240044240044244044c01800c88ccc00cdc6800800801190009aa84780911119299a984480999ab9a337120049000045808450088018a99a984480999ab9a3371200890000458084500899b8b3370c004904002180319b8300248200104cdc599b8650014820010ccc014cdc080224004666e312002337026e3400d20020033370066e0c00920800433706a002904002099b803370c00290400219b8e00248000c8004d5423804894cd4c21404ccd5cd19b870014800021c0421804522010013371666e180052080043002337060029040021109198008018011000909111180200290911118018029091111801002909111180080290008919a800a84280a84300990009aa84280911299a9a8428080109a82c1a98418099ab9c4911a73637269707420696e707574206e6f7420666f756e64202121210008401498884d4c0ac008894cd4c20404cc02000801440044cc01c00c014894cd4c1ecccd5cd19b8f35302b0022200235302b0012200207d07c1333573466e1cd4c0ac00888004d4c0ac004880041f41f041f0c8004d5420c0488c8c8c94cd4c1f4ccd5cd19b8833704a006a004900003f83f08038a99a983e999ab9a3370ea006900003f83f09804240002a66a60fa666ae68cdc3a801a80103f83f09804240042a66a60fa666ae68cdc42801a80103f83f09804a40002a66a60fa666ae68cdc42801a40000fe0fc2600a6601466e05200050033370290002801099800a400466e01200233706a006a004640026aa10e0244464a66a610002666ae68cdc399b8233704006006a00aa00c1040210202260160062a66a610002666ae68cdc380119b800034800820804204044c03000c54cd4c20004ccd5cd19b893370466e09400540054015401820804204044cc01140040084cc01000d40044cdc199b80002001480104d4d41d8008880044d4d41d40048800884888c00c01084888c0080104888004800488cc88c94cd4c1e0ccd5cd19b87002480001e81e44d414cd4c1f8cd5ce2481035054330007f4984cd41d0cdc2001a80099b84002500113307453353077333573466e20009200007907813370290000010801299a983b999ab9a33710002900003c83c099b81480000044004cdc1001299a983a999ab9a3370e002900003b83b0a40002a66a60ea666ae68cdc4800a40000ec0ee29001099b8148001200253353075333573466e2000520000770761337029000000880090911801001909118008019000911091998008020018011000891111a980f801111299a9a82e800909a980a8039111111111299a9a8341980f00580110999ab9a3375e0026026020104021020221000220ea266e2d2000489002233300348010cd541c400922107666163746f7279000012225335306d333573466e1cd4d541c000888ccc02c00c008005200206f06e1333573466e24cc88cc00800520003200135507822253353507800210012213300500133700006900118020008018378370837119801000a83a190009aa839911299a9a83980108009109a9aa8380011119803801999804001000802990009aa8391111299a9a83980108009109a9aa83800111299a9837999ab9a3370e00290000388380999804003801802899980400380199a83c199aaa80500380100080288891109199800802001801088900091119191800802990009aa8399119a9a839000a4000446a6aa0de00444a66a60dc666ae68cdc78010048380378980380089803001990009aa8391119a9a838800a4000446a6aa0dc00444a66a60da666ae68cdc7801003837837080089803001889119000990009aa83811299a9a837800880191099803001180200089111a9809800911299a9a828800909a98048031111111111299a9a82e19809005801109a82f98090008a82e8a829190009aa836111299a9a8360008a8271109a9aa83480111299a9834199ab9a3371e00c0040d40d226a0a600226600e00c006444444444424666666666600201601401201000e00c00a0080060044002244246600200600424002244246600200600424002244246600200600424002242444600600822444004224440022400244246600200600440024442466600200800600440024424660020060044002424444444600e01044244444446600c012010424444444600a01024444444008244444440064424444444660040120104424444444660020120104002444666aa6018240026a022a02046a6aa09a00244666aa601e240026a028a02646a6aa0a000244666a6aa01800246601490000009119805801000919805000a400000266aa60162400246a6aa09a0024466aa0a0004666a6aa012002466aa601e2400246a6aa0a20024466aa0a80046aa02000200244666aaa01206c004002466aa601e2400246a6aa0a20024466aa0a80046aa01c002002666aaa008062004002222444666aa600824002a0a266aa60142400246a6aa0980024466aa09e0046aa016002666aa600824002446a6aa09a00444a66a6098666aa601e240026a01aa02046a6aa0a0002446601400400a00c2006266a0aa008006a0a400266aa60142400246a6aa098002446466aa0a0006600200a640026aa0ac44a66a6a0aa00226aa0160064426a6aa0a400444a66a60a266018004010266aa02000e0022600c006004640026aa09e4422444a66a6a0a200220044426600a004666aa600e2400200a00800222424446006008224424446600400a008224244460020082240022466a01444666a6a00e006440040040026a6a00a00244002640026aa092442244a66a6a0940022a09844266a09a600800466aa600c24002008002640026aa0904422444a66a6a09400226a6a01600644002442666a6a01a00a440046008004666aa600e2400200a008002266a00444a66a607c0042080200207a244246600200600424002266a00a44a66a6076004200220780762466a00644666a6a00c006440040040026a6a0080024400224424660020060042400224424660020060042400244246600200600440022424600200424002444424666600200a008006004400224424660020060042400244244446600800c00a424444600600a2444400424444002400224464646666ae68cdc39aab9d5002480008cd409cc014d5d0a80118029aba135744a00446a00c6a606266ae712401035054310003249926135573ca00226ea80044988d4c0640048888cdd2a400066ae80dd480219aba05335301f00313374a90001bb1498884cdd2a400466ae80dd400119aba0300800137629319aba0375200466ae80dd398138009bb14988d4c07400488888888d4d409c00c88cdd2a400066ae80dd480519aba0375201266ae80dd480419aba0375200e66ae80dd480319aba03374a900019aba0375000466ae80dd40009bb1498cd5d01ba90043357406ea400cdd8a4c44a66a6048666ae68cdc780100081301288030a99a9812199ab9a3372200400204c04a2008200a2444006244400424440024002446a6aa044004446a6aa04800644a66a6046666ae68cdc78020010128120998058018008812111a9801801111a9802801911999804802001801000910919800801801100091111919a9806802919a98070021299a9810199ab9a3371e0040020440422a00620424042466a601c00840424a66a6040666ae68cdc78010008110108a80188108a99a9a80300190a99a9a8038011099a9805801119a9806001119a9808001119a98088011198058010009012119a9808801101211980580100091101211119a98070021012111299a9812999ab9a3370e00c00604e04c2a66a604a666ae68cdc380280101381309980b80200088130813080f8a99a9a8030009080f880f91199ab9a3371e0040020380362424460040062244002240024244600400644424466600200a00800640024244600400642446002006400244666ae68cdc38010008090089111199980300200180100091110919998008028020018011000891001110911980080200190009111111109199999998008048040038030028020018011000891091980080180109000990009aa807111299a9803199ab9a3370e0029000004003880109980180099b850020013200135500d2253353500c001137629311099aba037520046008002640026aa0184444a66a6a01a00420024426a6aa01400444a66a6012666ae68cdc78038010058050999804003801802899980400380199a809002002891001091000900088910919800801801089000990009aa803111999aab9f001250082335007375c6ae84008c00cd5d1001002090009000889100109109119800802001890008891918008009119801980100100099911991198012410121c9800866aa0089111c2739b84594eb8c5745b67795d07c67c9bdd1dbd52cdad176f5d3311500488112696e746572696d2d676f7665726e616e63650022123300100300220011122123300100300211200148811c6bbf39934636181e15e94d1af9f95aee76d7832edebc20dfc2dff5bf0048811cac2c4af1b7a1bab36c08784037a8c44f2f13939517f58c53793713760048811c57ca0438f1c384d6a819c5c55e6b5cdf54936f926363ec48fbc2bd430048811c0f3c6bf44ab1b1f9507be0a91f41081a40a5172cb9fd6cd248ec0e320001", + "factorySpendScriptHash": "1625f1271521654de941518d6da504a6b40034007f1519ad14f99f33", + "factoryAddress": "addr_test1wqtztuf8z5sk2n0fg9gc6md9qjntgqp5qpl32xddznue7vcd2xp6l", + "licenseEscrowAddress": "addr_test1wpjx45flh83z6wuqypyash54mszwmdj8r64fydafxtfc6jgcxpl55" +} diff --git a/packages/sundaeswap-v1/src/sundaeswap-v1.ts b/packages/sundaeswap-v1/src/sundaeswap-v1.ts index 338b323..65b0e41 100644 --- a/packages/sundaeswap-v1/src/sundaeswap-v1.ts +++ b/packages/sundaeswap-v1/src/sundaeswap-v1.ts @@ -10,20 +10,18 @@ import { Asset, BaseUtxoModel, Bytes, - DatumSource, DatumSourceType, type NetworkEnvironment, PlutusBytes, PlutusConstr, PlutusData, PlutusInt, + PlutusList, PlutusMaybe, - TxIn, - TxOut, type Utxo, - Value, + type Value, } from "@minswap/felis-ledger-core"; -import { CborHex, Maybe, Result } from "@minswap/felis-ledger-utils"; +import { type CborHex, Maybe, Result } from "@minswap/felis-ledger-utils"; import invariant from "@minswap/tiny-invariant"; export namespace SundaeSwapV1 { @@ -64,7 +62,9 @@ export namespace SundaeSwapV1 { return { destination: { address: Address.fromPlutusJson(destinationConstr.fields[0], networkEnv), - datum: Maybe.map(PlutusMaybe.unwrap(destinationConstr.fields[1]), (d) => Bytes.fromHex(PlutusBytes.unwrap(d))), + datum: Maybe.map(PlutusMaybe.unwrap(destinationConstr.fields[1]), (d) => + Bytes.fromHex(PlutusBytes.unwrap(d)), + ), }, alternate: Maybe.map(PlutusMaybe.unwrap(fields[1]), (d) => Bytes.fromHex(PlutusBytes.unwrap(d))), }; @@ -269,14 +269,8 @@ export namespace SundaeSwapV1 { super(utxo.input, address, value, rawDatum); this.datum = PoolDatum.fromCborHex(rawDatum); this.networkEnv = networkEnv; - this.authenAsset = new Asset( - Bytes.fromHex(AUTHEN_POLICY_ID), - Bytes.fromHex(`7020${this.datum.ident.hex}`), - ); - this.lpAsset = new Asset( - Bytes.fromHex(POOL_SCRIPT_HASH), - Bytes.fromHex(`6c7020${this.datum.ident.hex}`), - ); + this.authenAsset = new Asset(Bytes.fromHex(AUTHEN_POLICY_ID), Bytes.fromHex(`7020${this.datum.ident.hex}`)); + this.lpAsset = new Asset(Bytes.fromHex(POOL_SCRIPT_HASH), Bytes.fromHex(`6c7020${this.datum.ident.hex}`)); } get assetA(): Asset { @@ -307,13 +301,16 @@ export namespace SundaeSwapV1 { try { const { output } = utxo; const { value } = output; - invariant(output.address.toScriptHash()?.hex === POOL_SCRIPT_HASH, `SundaeSwap V1 Pool must have correct script hash`); - invariant(output.datumSource && output.datumSource.type !== DatumSourceType.INLINE_DATUM, `SundaeSwap V1 Pool must have datum hash`); - const datum = PoolDatum.fromCborHex(rawDatum); - const authenAsset = new Asset( - Bytes.fromHex(AUTHEN_POLICY_ID), - Bytes.fromHex(`7020${datum.ident.hex}`), + invariant( + output.address.toScriptHash()?.hex === POOL_SCRIPT_HASH, + `SundaeSwap V1 Pool must have correct script hash`, + ); + invariant( + output.datumSource && output.datumSource.type !== DatumSourceType.INLINE_DATUM, + `SundaeSwap V1 Pool must have datum hash`, ); + const datum = PoolDatum.fromCborHex(rawDatum); + const authenAsset = new Asset(Bytes.fromHex(AUTHEN_POLICY_ID), Bytes.fromHex(`7020${datum.ident.hex}`)); invariant(value.has(authenAsset), `pool value doesn't have authen asset ${authenAsset.toString()}`); const pool = new Pool({ utxo, @@ -332,4 +329,184 @@ export namespace SundaeSwapV1 { } } } + + /** + * FactoryDatum — schema as observed in mainnet debug3.json: + * Constr 0 [ + * bytes nextPoolIdent, -- next ident to assign on next CreatePool + * PlutusData proposal, -- proposal state (preserved as-is on CreatePool) + * bytes scooperIdent, -- current scooper-license window ident + * list scoopers, -- pubkey hashes of authorized scoopers + * ] + * `proposal` is preserved as raw PlutusData because CreatePool copies it + * through unchanged; we don't need to decode the proposal variant here. + */ + export type FactoryDatum = { + nextPoolIdent: Bytes; + proposal: PlutusData; + scooperIdent: Bytes; + scoopers: Bytes[]; + }; + + export namespace FactoryDatum { + export function fromPlutusJson(data: PlutusData): FactoryDatum { + const { fields } = PlutusConstr.unwrap(data, { [0]: 4 }); + const scoopersList = PlutusList.unwrap(fields[3]); + return { + nextPoolIdent: Bytes.fromHex(PlutusBytes.unwrap(fields[0])), + proposal: fields[1], + scooperIdent: Bytes.fromHex(PlutusBytes.unwrap(fields[2])), + scoopers: scoopersList.map((d) => Bytes.fromHex(PlutusBytes.unwrap(d))), + }; + } + + export function toPlutusJson(d: FactoryDatum): PlutusData { + return { + constructor: 0, + fields: [ + PlutusBytes.wrap(d.nextPoolIdent), + d.proposal, + PlutusBytes.wrap(d.scooperIdent), + { list: d.scoopers.map((b) => PlutusBytes.wrap(b)) }, + ], + }; + } + + export function fromCborHex(data: CborHex): FactoryDatum { + return fromPlutusJson(PlutusData.fromDataHex(data)); + } + + export function toCborHex(d: FactoryDatum): CborHex { + return PlutusData.toDataHex(toPlutusJson(d)); + } + + export function fromDataHex(data: string): FactoryDatum { + return fromPlutusJson(PlutusData.fromDataHex(data)); + } + } + + /** + * Factory.spend redeemer. Only the CreatePool variant is implemented here + * (governance/proposal flows live on different constructor indices and are + * out of scope for the v0 CLI). + * + * On-chain shape (matches mainnet debug3.json): + * Constr 0 [ + * Constr 0 [bytes policyA, bytes nameA], -- assetA (sorted) + * Constr 0 [bytes policyB, bytes nameB], -- assetB (sorted) + * ] + */ + export namespace FactoryRedeemer { + export function createPool({ assetA, assetB }: { assetA: Asset; assetB: Asset }): PlutusData { + return { + constructor: 0, + fields: [assetA.toPlutusJson(), assetB.toPlutusJson()], + }; + } + } + + /** + * Pool.mint redeemer. SundaeSwap V1's pool-mint policy takes a single + * `bytes` argument: the ident of the pool being created. The on-chain + * policy uses this ident to derive the expected pool NFT (`p ` + ident) + * and LP token (`lp ` + ident) token names. + */ + export namespace PoolMintRedeemer { + export function createPool(ident: Bytes): PlutusData { + return PlutusBytes.wrap(ident); + } + } + + /** + * Pool.spend redeemer. The Scoop variant carries the scooper's pubkey hash + * and the suffix of the active license token name (the validator concatenates + * the on-chain `"scooper "` prefix to derive the full asset name); the + * pool-spend script is the one that authenticates the scooper. + * + * On-chain shape (matches the mainnet scoop tx): + * Constr 0 [bytes scooperPkh, bytes licenseSuffix] + * CBOR e.g. `d8799f581c42ff` + * + * Other constructor indices cover admin/governance flows that aren't + * modelled here. + */ + export namespace PoolRedeemer { + export function scoop(scooperPkh: Bytes, licenseSuffix: Bytes): PlutusData { + return { + constructor: 0, + fields: [PlutusBytes.wrap(scooperPkh), PlutusBytes.wrap(licenseSuffix)], + }; + } + } + + /** + * Order.spend redeemer. The Scoop variant is bare (`Constr 0 []`, CBOR + * `d87980`) — the order validator delegates scooper-license verification + * to the pool-spend redeemer that runs in the same tx. + */ + export namespace OrderRedeemer { + export function scoop(): PlutusData { + return { constructor: 0, fields: [] }; + } + } + + /** + * Datum sitting on the treasury-escrow output the scooper produces every scoop. + * The scooper records *who* scooped (their pubkey hash) and *which* license + * window (the suffix of the license token name) so the escrow can audit + * activity. Mirrors witness datum #2 in the example mainnet scoop tx. + */ + export type LicenseDatum = { + scooperPkh: Bytes; + licenseSuffix: Bytes; + }; + + export namespace LicenseDatum { + export function toPlutusJson(d: LicenseDatum): PlutusData { + return { + constructor: 0, + fields: [PlutusBytes.wrap(d.scooperPkh), PlutusBytes.wrap(d.licenseSuffix)], + }; + } + + export function fromPlutusJson(data: PlutusData): LicenseDatum { + const { fields } = PlutusConstr.unwrap(data, { [0]: 2 }); + return { + scooperPkh: Bytes.fromHex(PlutusBytes.unwrap(fields[0])), + licenseSuffix: Bytes.fromHex(PlutusBytes.unwrap(fields[1])), + }; + } + } + + /** + * Increment a little-endian, base-256 byte string. Mirrors the on-chain + * factory.spend's increment logic: the next ident is `currentIdent + 1` + * with carry. `"00"` → `"01"`, `"ff"` → `"0001"`, `"01ff"` → `"02ff"` (LE). + */ + export function incrementIdentLE(hex: string): string { + const bytes = (hex.match(/../g) ?? []).map((b) => Number.parseInt(b, 16)); + let i = 0; + let carry = 1; + while (carry && i < bytes.length) { + const sum = bytes[i] + carry; + bytes[i] = sum & 0xff; + carry = sum >> 8; + i++; + } + if (carry) bytes.push(carry); + return bytes.map((b) => b.toString(16).padStart(2, "0")).join(""); + } + + /** Integer square root over BigInt (Newton's method). Used for initial LP supply. */ + export function isqrt(value: bigint): bigint { + invariant(value >= 0n, "isqrt: negative input"); + if (value < 2n) return value; + let x = value; + let y = (x + 1n) / 2n; + while (y < x) { + x = y; + y = (x + value / x) / 2n; + } + return x; + } } diff --git a/packages/sundaeswap-v1/src/warehouse.ts b/packages/sundaeswap-v1/src/warehouse.ts new file mode 100644 index 0000000..75c461a --- /dev/null +++ b/packages/sundaeswap-v1/src/warehouse.ts @@ -0,0 +1,133 @@ +import { + Address, + AddressType, + Asset, + Bytes, + CredentialType, + NetworkEnvironment, + networkEnvironmentToNetworkID, +} from "@minswap/felis-ledger-core"; +import { Maybe } from "@minswap/felis-ledger-utils"; +import mainnetScripts from "./scripts/mainnet-scripts.json"; +import preprodScripts from "./scripts/preprod-scripts.json"; + +type SundaeSwapV1Scripts = { + factoryMintScript: string; + factoryMintScriptHash: string; + factoryAuthenToken: string; + factorySpendScript: string; + factorySpendScriptHash: string; + factoryAddress: string; + poolMintScript: string; + poolMintScriptHash: string; + poolSpendScript: string; + poolSpendScriptHash: string; + orderSpendScript: string; + orderSpendScriptHash: string; + licenseEscrowAddress: string; +}; + +export class SundaeSwapV1Warehouse { + private static instance: SundaeSwapV1Warehouse | null = null; + + // ─── Protocol constants (network-independent) ─────────────────────────── + static readonly POOL_NFT_NAME_PREFIX = "7020"; // ascii "p " + static readonly LP_TOKEN_NAME_PREFIX = "6c7020"; // ascii "lp " + static readonly FACTORY_TOKEN_NAME_HEX = "666163746f7279"; // ascii "factory" + + // The factory.spend validator restricts each tx's validity range to the + // current scooper-license week. SundaeSwap V1 uses a 7-day window. + static readonly WEEK_MS = 604_800_000n; + + // Minimum lovelace held by the pool/factory UTxOs (matches mainnet pattern in debug3.json). + static readonly MIN_LOVELACE = 2_000_000n; + + static readonly DEFAULT_FEE_NUM = 3n; + static readonly DEFAULT_FEE_DEN = 1_000n; + + // ─── Per-network config ───────────────────────────────────────────────── + readonly networkEnv: NetworkEnvironment; + + readonly factoryMintScript: Bytes; + readonly factoryMintScriptHash: Bytes; + readonly factoryAuthenAsset: Asset; + readonly factorySpendScript: Bytes; + readonly factorySpendScriptHash: Bytes; + readonly factoryAddress: Address; + + readonly poolMintScript: Bytes; + readonly poolMintScriptHash: Bytes; + readonly poolSpendScript: Bytes; + readonly poolSpendScriptHash: Bytes; + readonly poolAddress: Address; + + readonly orderSpendScript: Bytes; + readonly orderSpendScriptHash: Bytes; + readonly orderAddress: Address; + + readonly licenseEscrowAddress: Address; + + private constructor(networkEnv: NetworkEnvironment) { + const config = SundaeSwapV1Warehouse.getConfig(networkEnv); + this.networkEnv = networkEnv; + + this.factoryMintScript = Bytes.fromHex(config.factoryMintScript); + this.factoryMintScriptHash = Bytes.fromHex(config.factoryMintScriptHash); + this.factoryAuthenAsset = new Asset( + this.factoryMintScriptHash, + Bytes.fromHex(SundaeSwapV1Warehouse.FACTORY_TOKEN_NAME_HEX), + ); + this.factorySpendScript = Bytes.fromHex(config.factorySpendScript); + this.factorySpendScriptHash = Bytes.fromHex(config.factorySpendScriptHash); + this.factoryAddress = Address.fromBech32(config.factoryAddress); + + this.poolMintScript = Bytes.fromHex(config.poolMintScript); + this.poolMintScriptHash = Bytes.fromHex(config.poolMintScriptHash); + this.poolSpendScript = Bytes.fromHex(config.poolSpendScript); + this.poolSpendScriptHash = Bytes.fromHex(config.poolSpendScriptHash); + this.poolAddress = SundaeSwapV1Warehouse.scriptEnterpriseAddress(networkEnv, this.poolSpendScriptHash); + + this.orderSpendScript = Bytes.fromHex(config.orderSpendScript); + this.orderSpendScriptHash = Bytes.fromHex(config.orderSpendScriptHash); + this.orderAddress = SundaeSwapV1Warehouse.scriptEnterpriseAddress(networkEnv, this.orderSpendScriptHash); + + this.licenseEscrowAddress = Address.fromBech32(config.licenseEscrowAddress); + } + + private static getConfig(networkEnv: NetworkEnvironment): SundaeSwapV1Scripts { + switch (networkEnv) { + case NetworkEnvironment.MAINNET: + return mainnetScripts; + case NetworkEnvironment.TESTNET_PREPROD: + return preprodScripts; + default: + throw new Error(`SundaeSwapV1Warehouse: unsupported network ${networkEnv}`); + } + } + + private static scriptEnterpriseAddress(networkEnv: NetworkEnvironment, scriptHash: Bytes): Address { + return Address.fromCardanoAddress({ + type: AddressType.ENTERPRISE_ADDRESS, + network: networkEnvironmentToNetworkID(networkEnv), + payment: { + type: CredentialType.SCRIPT_CREDENTIAL, + payload: scriptHash, + }, + }); + } + + poolNftAsset(ident: Bytes): Asset { + return new Asset(this.poolMintScriptHash, Bytes.fromHex(SundaeSwapV1Warehouse.POOL_NFT_NAME_PREFIX + ident.hex)); + } + + lpAsset(ident: Bytes): Asset { + return new Asset(this.poolMintScriptHash, Bytes.fromHex(SundaeSwapV1Warehouse.LP_TOKEN_NAME_PREFIX + ident.hex)); + } + + public static getInstance(networkEnv: NetworkEnvironment): SundaeSwapV1Warehouse { + if (Maybe.isNothing(SundaeSwapV1Warehouse.instance) || SundaeSwapV1Warehouse.instance.networkEnv !== networkEnv) { + SundaeSwapV1Warehouse.instance = new SundaeSwapV1Warehouse(networkEnv); + } + return SundaeSwapV1Warehouse.instance; + } +} diff --git a/packages/sundaeswap-v1/test/sample.test.ts b/packages/sundaeswap-v1/test/sample.test.ts index fa9486a..c8dbb29 100644 --- a/packages/sundaeswap-v1/test/sample.test.ts +++ b/packages/sundaeswap-v1/test/sample.test.ts @@ -1,17 +1,26 @@ -import { describe, it, expect } from "vitest"; +import { ADA, Asset, Bytes, NetworkEnvironment, PlutusData, Value } from "@minswap/felis-ledger-core"; +import { RustModule } from "@minswap/felis-ledger-utils"; +import { beforeAll, describe, expect, it } from "vitest"; import * as S1 from "../src"; -import { ADA, Asset, NetworkEnvironment, Value, XJSON } from "@minswap/felis-ledger-core"; + +beforeAll(async () => { + await RustModule.load(); +}); describe("sample", () => { it("order datum", () => { - const rawDatum = "d8799f421f04d8799fd8799fd8799fd8799f581cb019495147164900262ad18cbf97a0e81c6de6214a0dea46e836504effd8799fd8799fd8799f581c37138b4127d05ca91491ab334f326b4a0725ed56d6af1aaedeac2109ffffffffd87a80ffd87a80ff1a002625a0d8799fd879801a0a84a0b4d8799f1a000143caffffff"; + const rawDatum = + "d8799f421f04d8799fd8799fd8799fd8799f581cb019495147164900262ad18cbf97a0e81c6de6214a0dea46e836504effd8799fd8799fd8799f581c37138b4127d05ca91491ab334f326b4a0725ed56d6af1aaedeac2109ffffffffd87a80ffd87a80ff1a002625a0d8799fd879801a0a84a0b4d8799f1a000143caffffff"; const datum = S1.SundaeSwapV1.OrderDatum.fromDataHex(rawDatum, NetworkEnvironment.MAINNET); expect(datum.poolIdent.hex).toEqual("1f04"); }); it("order info", () => { const assetB = Asset.fromString("533bb94a8850ee3ccbe483106489399112b74c905342cb1792a797a0.494e4459"); - const datum = S1.SundaeSwapV1.OrderDatum.fromDataHex("d8799f42b003d8799fd8799fd8799fd8799f581cf9328b14f7918e07af661c42694ba036d47590787fa8af621cc02b0dffd8799fd8799fd8799f581c283330ea86e785339cc69d67948e1350b9ddfa49a2841f7f3cb5f411ffffffffd87a80ffd87a80ff1a002625a0d8799fd87a801a6638d2c0d8799f1a4441e1acffffff", NetworkEnvironment.MAINNET); + const datum = S1.SundaeSwapV1.OrderDatum.fromDataHex( + "d8799f42b003d8799fd8799fd8799fd8799f581cf9328b14f7918e07af661c42694ba036d47590787fa8af621cc02b0dffd8799fd8799fd8799f581c283330ea86e785339cc69d67948e1350b9ddfa49a2841f7f3cb5f411ffffffffd87a80ffd87a80ff1a002625a0d8799fd87a801a6638d2c0d8799f1a4441e1acffffff", + NetworkEnvironment.MAINNET, + ); const order = S1.SundaeSwapV1.getOrderInfo({ value: new Value().add(ADA, 4_500_000n).add(assetB, 1715000000n), datum, @@ -20,4 +29,109 @@ describe("sample", () => { }); expect(order.assetIn).toEqual(assetB); }); + + // ─── CreatePool building blocks (mainnet debug3.json reference tx) ────── + + // Mainnet CreatePool tx witness datums and output datum hashes. + // src: packages/sundaeswap-v1/src/debug3.json + const FACTORY_INPUT_DATUM_JSON = { + constructor: 0, + fields: [ + { bytes: "00" }, + { constructor: 0, fields: [] }, + { bytes: "00" }, + { + list: [ + { bytes: "9d1cbb54faf284f5d262f591b1f9201a1858de155157dad49f3881c4" }, + { bytes: "694bc6017f9d74a5d9b3ef377b42b9fe4967a04fb1844959057f35bb" }, + ], + }, + ], + } as const; + + const _FACTORY_OUTPUT_DATUM_JSON = { + constructor: 0, + fields: [ + { bytes: "01" }, // nextPoolIdent bumped from "00" → "01" + { constructor: 0, fields: [] }, + { bytes: "00" }, + { + list: [ + { bytes: "9d1cbb54faf284f5d262f591b1f9201a1858de155157dad49f3881c4" }, + { bytes: "694bc6017f9d74a5d9b3ef377b42b9fe4967a04fb1844959057f35bb" }, + ], + }, + ], + } as const; + + const POOL_OUTPUT_DATUM_JSON = { + constructor: 0, + fields: [ + { + constructor: 0, + fields: [ + { + constructor: 0, + fields: [{ bytes: "7de52b397c138e44fb6e61aaaeb26219a8059b1749b7c3bd87bd9488" }, { bytes: "524245525259" }], + }, + { + constructor: 0, + fields: [{ bytes: "7de52b397c138e44fb6e61aaaeb26219a8059b1749b7c3bd87bd9488" }, { bytes: "534245525259" }], + }, + ], + }, + { bytes: "00" }, + { int: "12247448713" }, + { constructor: 0, fields: [{ int: "1" }, { int: "100" }] }, + ], + } as const; + + // Datum hashes from mainnet outputs[0] / outputs[1] in debug3.json. + const FACTORY_OUTPUT_DATUM_HASH = "7f81ce41100a327c2a4b145553fd79703caca92f34595bc11b2e94a583d3da90"; + const POOL_OUTPUT_DATUM_HASH = "d5f0c9a0bcebc3852fb60864065d13b040a703139ba413e58a47779ca518149e"; + + it("FactoryDatum round-trips to mainnet input/output datum hashes", () => { + // Input side: parse → re-serialize → hash matches debug3.json factory input. + const inputDatum = S1.SundaeSwapV1.FactoryDatum.fromPlutusJson(FACTORY_INPUT_DATUM_JSON); + expect(inputDatum.nextPoolIdent.hex).toEqual("00"); + expect(inputDatum.scoopers.length).toEqual(2); + expect(PlutusData.hashPlutusData(S1.SundaeSwapV1.FactoryDatum.toPlutusJson(inputDatum))).toEqual( + PlutusData.hashPlutusData(FACTORY_INPUT_DATUM_JSON), + ); + + // Output side: bump ident the way buildCreatePool does and check the + // produced datum hash matches the actual mainnet output datum hash. + const nextIdentHex = S1.SundaeSwapV1.incrementIdentLE(inputDatum.nextPoolIdent.hex); + expect(nextIdentHex).toEqual("01"); + const newDatum = { + ...inputDatum, + nextPoolIdent: Bytes.fromHex(nextIdentHex), + }; + expect(PlutusData.hashPlutusData(S1.SundaeSwapV1.FactoryDatum.toPlutusJson(newDatum))).toEqual( + FACTORY_OUTPUT_DATUM_HASH, + ); + }); + + it("PoolDatum round-trips to mainnet output datum hash", () => { + const datum = S1.SundaeSwapV1.PoolDatum.fromPlutusJson(POOL_OUTPUT_DATUM_JSON); + expect(datum.ident.hex).toEqual("00"); + expect(datum.liquidity).toEqual(12_247_448_713n); + expect(datum.tradingFee).toEqual([1, 100]); + const rebuilt = S1.SundaeSwapV1.PoolDatum.toPlutusJson(datum); + expect(PlutusData.hashPlutusData(rebuilt)).toEqual(POOL_OUTPUT_DATUM_HASH); + }); + + it("incrementIdentLE handles carry across byte boundaries", () => { + expect(S1.SundaeSwapV1.incrementIdentLE("00")).toEqual("01"); + expect(S1.SundaeSwapV1.incrementIdentLE("ff")).toEqual("0001"); + expect(S1.SundaeSwapV1.incrementIdentLE("01ff")).toEqual("02ff"); // LE: low byte first + expect(S1.SundaeSwapV1.incrementIdentLE("ffff")).toEqual("000001"); + }); + + it("isqrt(reserveA × reserveB) reproduces mainnet initial LP supply", () => { + // From mainnet debug3.json: 10_000_000_000 RBERRY × 15_000_000_000 SBERRY + // → isqrt = 12_247_448_713 (matches the liquidity field in the pool datum). + const supply = S1.SundaeSwapV1.isqrt(10_000_000_000n * 15_000_000_000n); + expect(supply).toEqual(12_247_448_713n); + }); }); diff --git a/packages/tx-builder/src/tx-builder.ts b/packages/tx-builder/src/tx-builder.ts index aeaae01..ff5fcfe 100644 --- a/packages/tx-builder/src/tx-builder.ts +++ b/packages/tx-builder/src/tx-builder.ts @@ -64,6 +64,7 @@ export type TxBuilderBuildOptions = { // Sometimes, we need to pass extra fee for some special cases like when CSL compute Insufficient fee due to unknown reason. // For example, Metadata Message contains special characters like (Ť Ŏ Ǹ Ȳ) requires more fee than CSL calculated. extraFee?: bigint; + hardCodedTxFee?: bigint; // hard-code txFee, unsafe }; const MAX_FAKE_EX_UNIT: ExUnit = { @@ -689,6 +690,7 @@ export class TxBuilder { walletCollaterals: _walletCollaterals, coinSelectionAlgorithm, debug, + hardCodedTxFee, } = options; let finalTx: string | undefined; @@ -806,6 +808,10 @@ export class TxBuilder { } } + if (hardCodedTxFee) { + this.txDraft.body.fee = hardCodedTxFee; + } + if (isUsingPlutus) { const computer = new CSLTxSerializer(this.networkEnv, this.getTxDraft(), protocolParameters); // evaluatedRedeemersResult = computer.evaluate(allUtxos); diff --git a/packages/wingriders-v2/src/index.ts b/packages/wingriders-v2/src/index.ts index 56e1e53..dac25a1 100644 --- a/packages/wingriders-v2/src/index.ts +++ b/packages/wingriders-v2/src/index.ts @@ -1,3 +1,3 @@ -export * from "./wingriders-v2"; -export * from "./warehouse"; export * from "./utils"; +export * from "./warehouse"; +export * from "./wingriders-v2"; diff --git a/packages/wingriders-v2/src/utils.ts b/packages/wingriders-v2/src/utils.ts index 29cc950..02167ee 100644 --- a/packages/wingriders-v2/src/utils.ts +++ b/packages/wingriders-v2/src/utils.ts @@ -1,4 +1,4 @@ -import { Asset } from "@minswap/felis-ledger-core"; +import type { Asset } from "@minswap/felis-ledger-core"; export function normalizePair([a, b]: [Asset, Asset]): [Asset, Asset] { if (a.compare(b) > 0) { diff --git a/packages/wingriders-v2/src/wingriders-v2.ts b/packages/wingriders-v2/src/wingriders-v2.ts index 4f296fd..68818d0 100644 --- a/packages/wingriders-v2/src/wingriders-v2.ts +++ b/packages/wingriders-v2/src/wingriders-v2.ts @@ -21,11 +21,11 @@ import { PlutusInt, PlutusMaybe, type Utxo, - Value, + type Value, } from "@minswap/felis-ledger-core"; import { type CborHex, type CSLPlutusData, Maybe, Result } from "@minswap/felis-ledger-utils"; -import { blake2b } from "blakejs"; import invariant from "@minswap/tiny-invariant"; +import { blake2b } from "blakejs"; import { WingridersV2Warehouse } from "./warehouse"; export namespace WingridersV2 { @@ -128,14 +128,8 @@ export namespace WingridersV2 { const { fields } = PlutusConstr.unwrap(d, { [0]: 5 }); return { poolChoice: PoolChoice.fromPlutusJson(fields[0]), - assetA: new Asset( - Bytes.fromHex(PlutusBytes.unwrap(fields[1])), - Bytes.fromHex(PlutusBytes.unwrap(fields[2])), - ), - assetB: new Asset( - Bytes.fromHex(PlutusBytes.unwrap(fields[3])), - Bytes.fromHex(PlutusBytes.unwrap(fields[4])), - ), + assetA: new Asset(Bytes.fromHex(PlutusBytes.unwrap(fields[1])), Bytes.fromHex(PlutusBytes.unwrap(fields[2]))), + assetB: new Asset(Bytes.fromHex(PlutusBytes.unwrap(fields[3])), Bytes.fromHex(PlutusBytes.unwrap(fields[4]))), }; } @@ -218,7 +212,11 @@ export namespace WingridersV2 { minWantedB: bigint; } | { - type: OrderType.ExtractTreasury | OrderType.AddStakingReward | OrderType.ExtractProjectTreasury | OrderType.ExtractReserveTreasury; + type: + | OrderType.ExtractTreasury + | OrderType.AddStakingReward + | OrderType.ExtractProjectTreasury + | OrderType.ExtractReserveTreasury; } ); @@ -456,8 +454,14 @@ export namespace WingridersV2 { export namespace PoolDatum { export function fromPlutusJson(data: PlutusData, networkEnv: NetworkEnvironment): PoolDatum { const { fields } = PlutusConstr.unwrap(data, { [0]: 21 }); - const assetA = new Asset(Bytes.fromHex(PlutusBytes.unwrap(fields[1])), Bytes.fromHex(PlutusBytes.unwrap(fields[2]))); - const assetB = new Asset(Bytes.fromHex(PlutusBytes.unwrap(fields[3])), Bytes.fromHex(PlutusBytes.unwrap(fields[4]))); + const assetA = new Asset( + Bytes.fromHex(PlutusBytes.unwrap(fields[1])), + Bytes.fromHex(PlutusBytes.unwrap(fields[2])), + ); + const assetB = new Asset( + Bytes.fromHex(PlutusBytes.unwrap(fields[3])), + Bytes.fromHex(PlutusBytes.unwrap(fields[4])), + ); const projectBeneficiaryData = PlutusMaybe.unwrap(fields[18]); const reserveBeneficiaryData = PlutusMaybe.unwrap(fields[19]); @@ -620,7 +624,9 @@ export namespace WingridersV2 { * Reserve B, subtracting all treasuries */ get reserveB(): bigint { - return this.value.get(this.assetB) - this.datum.treasuryB - this.datum.projectTreasuryB - this.datum.reserveTreasuryB; + return ( + this.value.get(this.assetB) - this.datum.treasuryB - this.datum.projectTreasuryB - this.datum.reserveTreasuryB + ); } /** @@ -640,7 +646,10 @@ export namespace WingridersV2 { */ get totalFeeInBasis(): bigint { return ( - this.datum.swapFeeInBasis + this.datum.protocolFeeInBasis + this.datum.projectFeeInBasis + this.datum.reserveFeeInBasis + this.datum.swapFeeInBasis + + this.datum.protocolFeeInBasis + + this.datum.projectFeeInBasis + + this.datum.reserveFeeInBasis ); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 29ec290..daf92af 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -431,6 +431,9 @@ importers: '@minswap/felis-ledger-utils': specifier: workspace:* version: link:../ledger-utils + '@minswap/felis-sundaeswap-v1': + specifier: workspace:* + version: link:../sundaeswap-v1 '@minswap/felis-tx-builder': specifier: workspace:* version: link:../tx-builder From adc451553771e52e37d95c1cd9eb7052078ae717 Mon Sep 17 00:00:00 2001 From: tony Date: Mon, 4 May 2026 16:33:01 +0700 Subject: [PATCH 3/8] fix --- apps/example/src/w2.ts | 2 +- turbo.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/example/src/w2.ts b/apps/example/src/w2.ts index df71b12..8c95398 100644 --- a/apps/example/src/w2.ts +++ b/apps/example/src/w2.ts @@ -54,7 +54,7 @@ function resolveKupoUrl(network: NetworkEnvironment): string { case NetworkEnvironment.MAINNET: return process.env["KUPO_MAINNET_URL"] ?? "http://mainnet-staging:1442"; case NetworkEnvironment.TESTNET_PREPROD: - return process.env["KUPO_PREPROD_URL"] ?? "http://testnet-preprod:1442"; + return process.env["KUPO_PREPROD_URL"] ?? "http://testnet-preprod.tail2feb3.ts.net:1442"; case NetworkEnvironment.TESTNET_PREVIEW: return process.env["KUPO_PREVIEW_URL"] ?? "http://dev-3:1442"; } diff --git a/turbo.json b/turbo.json index 8e3d7ee..b6f54b5 100644 --- a/turbo.json +++ b/turbo.json @@ -68,7 +68,7 @@ "outputs": ["dist/**"] }, "@minswap/felis-build-tx#build": { - "dependsOn": ["@minswap/felis-tx-builder#build", "@minswap/felis-dex-v2#build"], + "dependsOn": ["@minswap/felis-tx-builder#build", "@minswap/felis-dex-v2#build", "@minswap/felis-wingriders-v2#build", "@minswap/felis-sundaeswap-v1#build"], "inputs": ["$TURBO_DEFAULT$", ".env*"], "outputs": ["dist/**"] }, From 7b6ac1725e5b9ccf30d4c7c41f2d54a1710820f0 Mon Sep 17 00:00:00 2001 From: tony Date: Wed, 6 May 2026 15:48:02 +0700 Subject: [PATCH 4/8] wip --- apps/example/src/w2.ts | 168 +++++++++++------- biome.json | 1 + .../minswap-build-tx/src/wingriders-v2.ts | 19 +- 3 files changed, 116 insertions(+), 72 deletions(-) diff --git a/apps/example/src/w2.ts b/apps/example/src/w2.ts index 8c95398..564c53e 100644 --- a/apps/example/src/w2.ts +++ b/apps/example/src/w2.ts @@ -258,7 +258,7 @@ function planSwap( const agentFee = poolDatum.agentFeeAda; const rawIn = order.utxo.output.value.get(assetIn); const lockX = assetIn.equals(ADA) ? rawIn - oil - agentFee : rawIn; - invariant(lockX > 0n, "order lockX must be positive"); + invariant(lockX > 0n, `order lockX must be positive | lockX | ${lockX}| rawIn | ${rawIn} | oil | ${oil} | agentFee | ${agentFee} | ${assetIn.toString()}`); // Effective reserves as seen by the curve: UTxO value minus treasuries // (and minPoolAda on the ADA side). @@ -373,14 +373,17 @@ async function runBatchInner( console.log(`Found ${orderUtxos.length} order UTxOs`); debugData.orderUtxosCount = orderUtxos.length; - // 3) Pick the first valid swap order that has a matching pool + // 3) Collect ALL valid swap orders that have a matching pool. We try them + // one at a time below; if planning/building/completing fails for one, + // we fall through to the next candidate instead of bailing out. const nowMs = BigInt(Date.now()); - let match: { order: OrderCandidate; pool: { utxo: Utxo; datum: WingridersV2.PoolDatum } } | null = null; + type Candidate = { order: OrderCandidate; pool: { utxo: Utxo; datum: WingridersV2.PoolDatum } }; + const candidates: Candidate[] = []; const skipCounts: Record = { notInlineDatum: 0, badDatum: 0, notSwap: 0, - nonNoDatumType: 0, + unsupportedDatumType: 0, nonPubKeyBeneficiary: 0, deadlineSoon: 0, noMatchingPool: 0, @@ -409,11 +412,19 @@ async function runBatchInner( noteSkip(utxo, "notSwap", { type: datum.type }); continue; } - if (datum.datumType !== WingridersV2.DatumType.No) { - noteSkip(utxo, "nonNoDatumType", { datumType: datum.datumType }); + if ( + datum.datumType !== WingridersV2.DatumType.No && + datum.datumType !== WingridersV2.DatumType.Inline + ) { + noteSkip(utxo, "unsupportedDatumType", { datumType: datum.datumType }); continue; } - if (Maybe.isNothing(datum.beneficiary.toPubKeyHash())) { + // Pubkey-only check applies only to datumType=No. With datumType=Inline the + // beneficiary may be a script address (the inline datum is the script's datum). + if ( + datum.datumType === WingridersV2.DatumType.No && + Maybe.isNothing(datum.beneficiary.toPubKeyHash()) + ) { noteSkip(utxo, "nonPubKeyBeneficiary", { beneficiary: datum.beneficiary.bech32 }); continue; } @@ -433,45 +444,18 @@ async function runBatchInner( continue; } - match = { order: { utxo, datum: datum as OrderCandidate["datum"] }, pool }; - break; + candidates.push({ order: { utxo, datum: datum as OrderCandidate["datum"] }, pool }); } debugData.orderFilter = { skipCounts, skipSamples }; - if (!match) { + debugData.candidatesCount = candidates.length; + if (candidates.length === 0) { console.log( - `No valid swap order with a matching CP pool — skipping. skipCounts=${XJSON.stringify(skipCounts)}`, + `No valid swap orders with a matching CP pool — skipping. skipCounts=${XJSON.stringify(skipCounts)}`, ); return; } - debugData.matched = { - pool: `${match.pool.utxo.input.txId.hex}#${match.pool.utxo.input.index}`, - order: `${match.order.utxo.input.txId.hex}#${match.order.utxo.input.index}`, - assetA: match.pool.datum.assetA.toString(), - assetB: match.pool.datum.assetB.toString(), - direction: match.order.datum.direction === WingridersV2.SwapDirection.A_TO_B ? "A_TO_B" : "B_TO_A", - }; - - // 4) Compute execution plan - const SLOT_MS = 1000n; - const validFromMs = ((nowMs - 60_000n) / SLOT_MS) * SLOT_MS; - const validToMs = validFromMs + 30n * 60_000n; - invariant(validToMs < match.order.datum.deadline, "order deadline too close"); - const plan = planSwap(match.pool.utxo, match.pool.datum, match.order, validFromMs); - debugData.plan = { - validFromMs: validFromMs.toString(), - validToMs: validToMs.toString(), - deadline: match.order.datum.deadline.toString(), - minWanted: match.order.datum.minWanted.toString(), - compensation: plan.compensation.toJSON?.() ?? null, - newDatum: { - treasuryA: plan.newDatum.treasuryA.toString(), - treasuryB: plan.newDatum.treasuryB.toString(), - lastInteraction: plan.newDatum.lastInteraction.toString(), - }, - }; - - // 5) Fetch agent UTxOs from kupo. The pool's evolve redeemer enforces + // 4) Fetch agent UTxOs from kupo. The pool's evolve redeemer enforces // (pAT) that the agent input carries EXACTLY 1 of warehouse.agentAsset // (see DEX/Pool.hs: `pvalueOf # agentInput.value # agentSymbol # agentToken #== 1`). // So we must find a UTxO holding qty == 1n, not just any UTxO that has the token. @@ -506,22 +490,6 @@ async function runBatchInner( walletUtxoCount: agentUtxos.length, }; - // 6) Build + complete (dry-run) - const txBuilder = WRV2Build.buildBatchTx({ - networkEnv: flags.network, - agent: flags.agent, - agentInput, - pool: { input: match.pool.utxo, newValue: plan.newPoolValue, newDatum: plan.newDatum }, - order: { input: match.order.utxo, compensation: plan.compensation }, - validFromMs, - validToMs, - }); - - // IMPORTANT: pass only [agentInput] as walletUtxos. The pool redeemer encodes - // input indexes computed from sorting [pool, agent, order], and the contract - // also enforces inputCount == 2 + numRequests (3 here) — so we cannot add any - // extra agent UTxOs to body.inputs. - const walletUtxos = [agentInput]; // Conway requires collateral inputs to be ADA-only (or carry a `collateral_return`). // tx-builder's first-branch fast path returns no collateralReturn when walletCollaterals // covers >=5 ADA, so we MUST pass pure-ADA UTxOs only — never the bulk-tokens UTxO. @@ -544,18 +512,82 @@ async function runBatchInner( ref: `${u.input.txId.hex}#${u.input.index}`, ada: u.output.value.get(ADA).toString(), })); - const result = await txBuilder.complete({ - walletUtxos, - walletCollaterals, - coinSelectionAlgorithm: CoinSelectionAlgorithm.MINWALLET_SEND_ALL, - provider: new EmulatorProvider(flags.network), - changeAddress: flags.agent, - }); - if (result.type !== "ok") { - debugData.completeError = result.error; - throw new Error(`txBuilder.complete() failed: ${String(result.error)}`); + + // 5) Try each candidate; on plan/build/complete failure, fall through to the next. + const SLOT_MS = 1000n; + const validFromMs = ((nowMs - 60_000n) / SLOT_MS) * SLOT_MS; + const validToMs = validFromMs + 30n * 60_000n; + + // IMPORTANT: pass only [agentInput] as walletUtxos. The pool redeemer encodes + // input indexes computed from sorting [pool, agent, order], and the contract + // also enforces inputCount == 2 + numRequests (3 here) — so we cannot add any + // extra agent UTxOs to body.inputs. + const tryCandidate = async (cand: Candidate) => { + invariant(validToMs < cand.order.datum.deadline, "order deadline too close"); + const plan = planSwap(cand.pool.utxo, cand.pool.datum, cand.order, validFromMs); + const txBuilder = WRV2Build.buildBatchTx({ + networkEnv: flags.network, + agent: flags.agent, + agentInput, + pool: { input: cand.pool.utxo, newValue: plan.newPoolValue, newDatum: plan.newDatum }, + order: { input: cand.order.utxo, compensation: plan.compensation }, + validFromMs, + validToMs, + }); + const completeResult = await txBuilder.complete({ + walletUtxos: [agentInput], + walletCollaterals, + coinSelectionAlgorithm: CoinSelectionAlgorithm.MINWALLET_SEND_ALL, + provider: new EmulatorProvider(flags.network), + changeAddress: flags.agent, + }); + if (completeResult.type !== "ok") { + throw new Error(`txBuilder.complete() failed: ${String(completeResult.error)}`); + } + return { match: cand, plan, completed: completeResult.value }; + }; + + const attemptErrors: Array<{ order: string; error: string }> = []; + let chosen: Awaited> | null = null; + for (const cand of candidates) { + const orderRef = `${cand.order.utxo.input.txId.hex}#${cand.order.utxo.input.index}`; + try { + chosen = await tryCandidate(cand); + console.log(`Order ${orderRef} planned successfully (skipped ${attemptErrors.length} prior)`); + break; + } catch (err) { + const errMsg = err instanceof Error ? err.message : String(err); + attemptErrors.push({ order: orderRef, error: errMsg }); + console.log(`Order ${orderRef} failed: ${errMsg}; trying next candidate...`); + } + } + debugData.attemptErrors = attemptErrors; + if (!chosen) { + console.log(`All ${candidates.length} candidate order(s) failed; nothing to submit.`); + return; } - debugData.unsignedTxCbor = result.value.complete(); + + const { match, plan, completed } = chosen; + debugData.matched = { + pool: `${match.pool.utxo.input.txId.hex}#${match.pool.utxo.input.index}`, + order: `${match.order.utxo.input.txId.hex}#${match.order.utxo.input.index}`, + assetA: match.pool.datum.assetA.toString(), + assetB: match.pool.datum.assetB.toString(), + direction: match.order.datum.direction === WingridersV2.SwapDirection.A_TO_B ? "A_TO_B" : "B_TO_A", + }; + debugData.plan = { + validFromMs: validFromMs.toString(), + validToMs: validToMs.toString(), + deadline: match.order.datum.deadline.toString(), + minWanted: match.order.datum.minWanted.toString(), + compensation: plan.compensation.toJSON?.() ?? null, + newDatum: { + treasuryA: plan.newDatum.treasuryA.toString(), + treasuryB: plan.newDatum.treasuryB.toString(), + lastInteraction: plan.newDatum.lastInteraction.toString(), + }, + }; + debugData.unsignedTxCbor = completed.complete(); console.log( XJSON.stringify( @@ -588,7 +620,7 @@ async function runBatchInner( ); if (flags.dry) { console.log("\n--- unsigned tx cbor ---"); - console.log(result.value.complete()); + console.log(completed.complete()); return; } @@ -600,7 +632,7 @@ async function runBatchInner( `AGENT_SEED_PHRASE derives ${wallet.address.bech32} but --agent is ${flags.agent.bech32}`, ); - const signedTxHex = result.value.signWithPrivateKey(wallet.paymentKey).complete(); + const signedTxHex = completed.signWithPrivateKey(wallet.paymentKey).complete(); debugData.signedTxCbor = signedTxHex; console.log("\n--- signed tx cbor ---"); console.log(signedTxHex); diff --git a/biome.json b/biome.json index 0734ee3..46aec2c 100644 --- a/biome.json +++ b/biome.json @@ -17,6 +17,7 @@ "packages/tx-builder/**", "packages/wingriders-v2/**", "packages/sundaeswap-v1/**", + "apps/example/**", "apps/web/**", "apps/long-short-backend/src/**" ], diff --git a/packages/minswap-build-tx/src/wingriders-v2.ts b/packages/minswap-build-tx/src/wingriders-v2.ts index a58c034..cc78be4 100644 --- a/packages/minswap-build-tx/src/wingriders-v2.ts +++ b/packages/minswap-build-tx/src/wingriders-v2.ts @@ -5,7 +5,7 @@ import { Bytes, DatumSourceType, type NetworkEnvironment, - type PlutusData, + PlutusData, TxIn, TxOut, type Utxo, @@ -210,8 +210,8 @@ export namespace WingridersV2 { ); invariant(orderDatum.type === WRV2.OrderType.Swap, "WingridersV2.buildBatchTx: only swap orders supported in v1"); invariant( - orderDatum.datumType === WRV2.DatumType.No, - "WingridersV2.buildBatchTx: only pubkey beneficiaries (datumType=No) supported in v1", + orderDatum.datumType === WRV2.DatumType.No || orderDatum.datumType === WRV2.DatumType.Inline, + `WingridersV2.buildBatchTx: only datumType=No or datumType=Inline supported (got ${orderDatum.datumType})`, ); const sortedInputs = [pool.input, agentInput, order.input].slice().sort((a, b) => TxIn.compare(a.input, b.input)); @@ -240,7 +240,18 @@ export namespace WingridersV2 { type: DatumSourceType.INLINE_DATUM, data: Bytes.fromHex(WRV2.PoolDatum.toCborHex(pool.newDatum)), }); - const compensationOut = new TxOut(orderDatum.beneficiary, order.compensation).addMinimumADAIfRequired(networkEnv); + const compensationDatumSource = + orderDatum.datumType === WRV2.DatumType.Inline + ? { + type: DatumSourceType.INLINE_DATUM as const, + data: Bytes.fromHex(PlutusData.toDataHex(orderDatum.compensationDatum)), + } + : undefined; + const compensationOut = new TxOut( + orderDatum.beneficiary, + order.compensation, + compensationDatumSource, + ).addMinimumADAIfRequired(networkEnv); return new TxBuilder(networkEnv) .readFrom(warehouse.poolCpRefInput, warehouse.orderRefInput) From 4c9a2dc8389bc7c082008ea95b03078c095af098 Mon Sep 17 00:00:00 2001 From: tony Date: Wed, 6 May 2026 15:48:07 +0700 Subject: [PATCH 5/8] format --- .../example/data/minswap-dex-v2-map-pool.json | 2 +- apps/example/data/sundaeswap-v1-map-pool.json | 2 +- apps/example/data/sundaeswap-v3-map-pool.json | 2 +- apps/example/src/cardanoscan.ts | 26 +- apps/example/src/fetchMapPools.ts | 16 +- apps/example/src/kupo.ts | 72 ++++-- apps/example/src/s1.ts | 229 +++++------------- apps/example/src/syncer.ts | 37 ++- apps/example/src/w2.ts | 77 +++--- 9 files changed, 205 insertions(+), 258 deletions(-) diff --git a/apps/example/data/minswap-dex-v2-map-pool.json b/apps/example/data/minswap-dex-v2-map-pool.json index 2c73346..970612c 100644 --- a/apps/example/data/minswap-dex-v2-map-pool.json +++ b/apps/example/data/minswap-dex-v2-map-pool.json @@ -12207,4 +12207,4 @@ "assetA": "lovelace", "assetB": "cc98659e68801f2827779bbdbb95af5aeaccfdcc80b01e55c5b92f7c.4348414c4945" } -} \ No newline at end of file +} diff --git a/apps/example/data/sundaeswap-v1-map-pool.json b/apps/example/data/sundaeswap-v1-map-pool.json index 9360dba..544968d 100644 --- a/apps/example/data/sundaeswap-v1-map-pool.json +++ b/apps/example/data/sundaeswap-v1-map-pool.json @@ -5799,4 +5799,4 @@ "assetA": "lovelace", "assetB": "1dd1a7dde0e1e82761325ee5f4719d0d4b7c24dfba77d9bee01eed4b.4d454f57" } -} \ No newline at end of file +} diff --git a/apps/example/data/sundaeswap-v3-map-pool.json b/apps/example/data/sundaeswap-v3-map-pool.json index af3da20..11286a0 100644 --- a/apps/example/data/sundaeswap-v3-map-pool.json +++ b/apps/example/data/sundaeswap-v3-map-pool.json @@ -8707,4 +8707,4 @@ "assetA": "8db269c3ec630e06ae29f74bc39edd1f87c819f1056206e879a1cd61.446a65644d6963726f555344", "assetB": "c48cbb3d5e57ed56e276bc45f99ab39abe94e6cd7ac39fb402da47ad.0014df105553444d" } -} \ No newline at end of file +} diff --git a/apps/example/src/cardanoscan.ts b/apps/example/src/cardanoscan.ts index 1713959..f40846d 100644 --- a/apps/example/src/cardanoscan.ts +++ b/apps/example/src/cardanoscan.ts @@ -1,14 +1,30 @@ import fs from "node:fs"; +import { Bytes, NetworkEnvironment } from "@minswap/felis-ledger-core"; import { RustModule } from "@minswap/felis-ledger-utils"; -import { MinswapStableswapSyncer, MinswapV1Syncer, MinswapV2Syncer, SplashSyncer, SundaeSwapV1Syncer, SundaeSwapV3Syncer, Transaction, WingridersV1Syncer, WingridersV2Syncer } from "@minswap/felis-syncer"; +import { + MinswapStableswapSyncer, + MinswapV1Syncer, + MinswapV2Syncer, + SplashSyncer, + SundaeSwapV1Syncer, + SundaeSwapV3Syncer, + Transaction, + WingridersV1Syncer, + WingridersV2Syncer, +} from "@minswap/felis-syncer"; import socketIO from "socket.io-client"; -import { Bytes, NetworkEnvironment } from "@minswap/felis-ledger-core"; const main = async () => { await RustModule.load(); - const minswapV2MapPool: MinswapV2Syncer.MapPool = JSON.parse(fs.readFileSync("data/minswap-dex-v2-map-pool.json", "utf-8")); - const sundaeswapV1MapPool: SundaeSwapV1Syncer.MapPool = JSON.parse(fs.readFileSync("data/sundaeswap-v1-map-pool.json", "utf-8")); - const sundaeswapV3MapPool: SundaeSwapV3Syncer.MapPool = JSON.parse(fs.readFileSync("data/sundaeswap-v3-map-pool.json", "utf-8")); + const minswapV2MapPool: MinswapV2Syncer.MapPool = JSON.parse( + fs.readFileSync("data/minswap-dex-v2-map-pool.json", "utf-8"), + ); + const sundaeswapV1MapPool: SundaeSwapV1Syncer.MapPool = JSON.parse( + fs.readFileSync("data/sundaeswap-v1-map-pool.json", "utf-8"), + ); + const sundaeswapV3MapPool: SundaeSwapV3Syncer.MapPool = JSON.parse( + fs.readFileSync("data/sundaeswap-v3-map-pool.json", "utf-8"), + ); const socketCardano = socketIO("https://socket.cardanoscan.io/private", { auth: { diff --git a/apps/example/src/fetchMapPools.ts b/apps/example/src/fetchMapPools.ts index 75aafa6..06c459e 100644 --- a/apps/example/src/fetchMapPools.ts +++ b/apps/example/src/fetchMapPools.ts @@ -1,12 +1,12 @@ import fs from "node:fs"; -import { Bytes, NetworkEnvironment, Utxo, XJSON } from "@minswap/felis-ledger-core"; import { getDexV2Configs, PoolV2 } from "@minswap/felis-dex-v2"; +import { Bytes, NetworkEnvironment } from "@minswap/felis-ledger-core"; import { KupoService } from "@minswap/felis-provider"; -import { MinswapV2Syncer, SundaeSwapV1Syncer, SundaeSwapV3Syncer } from "@minswap/felis-syncer"; import { SundaeSwapV1 } from "@minswap/felis-sundaeswap-v1"; import { SundaeSwapV3 } from "@minswap/felis-sundaeswap-v3"; +import type { MinswapV2Syncer, SundaeSwapV1Syncer, SundaeSwapV3Syncer } from "@minswap/felis-syncer"; -const fetchMinswapV2Pools = async (kupo: KupoService, networkEnv: NetworkEnvironment) => { +const _fetchMinswapV2Pools = async (kupo: KupoService, networkEnv: NetworkEnvironment) => { const minswapDexV2Configs = getDexV2Configs(networkEnv); const poolScriptHash = minswapDexV2Configs.poolEnterpriseAddress.payment.payload; const poolAuthenAsset = minswapDexV2Configs.poolAuthenAsset; @@ -16,7 +16,8 @@ const fetchMinswapV2Pools = async (kupo: KupoService, networkEnv: NetworkEnviron const mapPool: MinswapV2Syncer.MapPool = JSON.parse(fs.readFileSync("data/minswap-dex-v2-map-pool.json", "utf-8")); for (const utxo of minswapV2Utxos) { const pool = PoolV2.fromUtxo(utxo, networkEnv); - if (pool.type === "ok") {`` + if (pool.type === "ok") { + ``; const lpAsset = pool.value.lpAsset.toString(); const assetA = pool.value.assetA.toString(); const assetB = pool.value.assetB.toString(); @@ -30,8 +31,11 @@ const fetchMinswapV2Pools = async (kupo: KupoService, networkEnv: NetworkEnviron console.log("Minswap V2 | Done fetching map pool"); }; -const fetchSundaeswapV1Pools = async (kupo: KupoService, networkEnv: NetworkEnvironment) => { - const sundaeswapV1Utxos = await kupo.utxoAtScriptHashWithPolicyId(Bytes.fromHex(SundaeSwapV1.POOL_SCRIPT_HASH), Bytes.fromHex(SundaeSwapV1.AUTHEN_POLICY_ID)); +const _fetchSundaeswapV1Pools = async (kupo: KupoService, networkEnv: NetworkEnvironment) => { + const sundaeswapV1Utxos = await kupo.utxoAtScriptHashWithPolicyId( + Bytes.fromHex(SundaeSwapV1.POOL_SCRIPT_HASH), + Bytes.fromHex(SundaeSwapV1.AUTHEN_POLICY_ID), + ); console.log("Fetched sundaeswap v1 Utxos:", sundaeswapV1Utxos.length); const mapPool: SundaeSwapV1Syncer.MapPool = JSON.parse(fs.readFileSync("data/sundaeswap-v1-map-pool.json", "utf-8")); for (const utxo of sundaeswapV1Utxos) { diff --git a/apps/example/src/kupo.ts b/apps/example/src/kupo.ts index 4c92296..69e8225 100644 --- a/apps/example/src/kupo.ts +++ b/apps/example/src/kupo.ts @@ -71,17 +71,46 @@ const parseArgs = (argv: string[]): Flags => { for (let i = 0; i < argv.length; i++) { const a = argv[i]; switch (a) { - case "--utxo": flags.utxo = true; break; - case "--health": flags.health = true; break; - case "--datum": flags.datum = true; break; - case "--raw": flags.raw = true; break; - case "--network": flags.network = take(i); i++; break; - case "--tx-in": flags.txIns.push(take(i)); i++; break; - case "--address": flags.addresses.push(take(i)); i++; break; - case "--payment-credential": flags.paymentCredentials.push(take(i)); i++; break; - case "--asset": flags.assets.push(take(i)); i++; break; - case "--policy-id": flags.policyIds.push(take(i)); i++; break; - case "--datum-hash": flags.datumHashes.push(take(i)); i++; break; + case "--utxo": + flags.utxo = true; + break; + case "--health": + flags.health = true; + break; + case "--datum": + flags.datum = true; + break; + case "--raw": + flags.raw = true; + break; + case "--network": + flags.network = take(i); + i++; + break; + case "--tx-in": + flags.txIns.push(take(i)); + i++; + break; + case "--address": + flags.addresses.push(take(i)); + i++; + break; + case "--payment-credential": + flags.paymentCredentials.push(take(i)); + i++; + break; + case "--asset": + flags.assets.push(take(i)); + i++; + break; + case "--policy-id": + flags.policyIds.push(take(i)); + i++; + break; + case "--datum-hash": + flags.datumHashes.push(take(i)); + i++; + break; default: throw new Error(`Unknown argument: ${a}`); } @@ -91,18 +120,25 @@ const parseArgs = (argv: string[]): Flags => { const parseNetwork = (s?: string): NetworkEnvironment => { switch (s) { - case "mainnet": return NetworkEnvironment.MAINNET; - case "testnet-preview": return NetworkEnvironment.TESTNET_PREVIEW; - case "testnet-preprod": return NetworkEnvironment.TESTNET_PREPROD; - default: throw new Error(`--network must be one of mainnet|testnet-preprod|testnet-preview (got ${s})`); + case "mainnet": + return NetworkEnvironment.MAINNET; + case "testnet-preview": + return NetworkEnvironment.TESTNET_PREVIEW; + case "testnet-preprod": + return NetworkEnvironment.TESTNET_PREPROD; + default: + throw new Error(`--network must be one of mainnet|testnet-preprod|testnet-preview (got ${s})`); } }; const resolveKupoUrl = (network: NetworkEnvironment): string => { switch (network) { - case NetworkEnvironment.MAINNET: return process.env["KUPO_MAINNET_URL"] ?? "http://mainnet-staging:1442"; - case NetworkEnvironment.TESTNET_PREPROD: return process.env["KUPO_PREPROD_URL"] ?? "http://testnet-preprod:1442"; - case NetworkEnvironment.TESTNET_PREVIEW: return process.env["KUPO_PREVIEW_URL"] ?? "http://dev-3:1442"; + case NetworkEnvironment.MAINNET: + return process.env["KUPO_MAINNET_URL"] ?? "http://mainnet-staging:1442"; + case NetworkEnvironment.TESTNET_PREPROD: + return process.env["KUPO_PREPROD_URL"] ?? "http://testnet-preprod:1442"; + case NetworkEnvironment.TESTNET_PREVIEW: + return process.env["KUPO_PREVIEW_URL"] ?? "http://dev-3:1442"; } }; diff --git a/apps/example/src/s1.ts b/apps/example/src/s1.ts index 5197169..7e7c52c 100644 --- a/apps/example/src/s1.ts +++ b/apps/example/src/s1.ts @@ -1,3 +1,5 @@ +import { SundaeSwapV1 as SundaeSwapV1Build } from "@minswap/felis-build-tx"; +import { baseAddressWalletFromSeed } from "@minswap/felis-cip"; import { ADA, Address, @@ -11,20 +13,10 @@ import { Value, XJSON, } from "@minswap/felis-ledger-core"; -import { baseAddressWalletFromSeed } from "@minswap/felis-cip"; import { Maybe, RustModule } from "@minswap/felis-ledger-utils"; import { CardanoscanProvider, KupoService } from "@minswap/felis-provider"; -import { SundaeSwapV1 as SundaeSwapV1Build } from "@minswap/felis-build-tx"; -import { - SundaeSwapV1, - SundaeSwapV1Warehouse, -} from "@minswap/felis-sundaeswap-v1"; -import { - CoinSelectionAlgorithm, - ECSLConverter, - EmulatorProvider, - TxBuilder, -} from "@minswap/felis-tx-builder"; +import { SundaeSwapV1, SundaeSwapV1Warehouse } from "@minswap/felis-sundaeswap-v1"; +import { CoinSelectionAlgorithm, ECSLConverter, EmulatorProvider, TxBuilder } from "@minswap/felis-tx-builder"; import invariant from "@minswap/tiny-invariant"; // ─── CLI plumbing ────────────────────────────────────────────────────────── @@ -40,9 +32,7 @@ function parseNetwork(s: string | undefined): NetworkEnvironment { case "testnet-preprod": return NetworkEnvironment.TESTNET_PREPROD; default: - throw new Error( - `--network must be one of mainnet|testnet-preprod|testnet-preview (got ${s})`, - ); + throw new Error(`--network must be one of mainnet|testnet-preprod|testnet-preview (got ${s})`); } } @@ -61,8 +51,7 @@ function parseFlags(argv: string[]): Record { const m: Record = {}; for (let i = 0; i < argv.length; i++) { const key = argv[i]; - if (!key.startsWith("--")) - throw new Error(`Unexpected positional arg: ${key}`); + if (!key.startsWith("--")) throw new Error(`Unexpected positional arg: ${key}`); const val = argv[i + 1]; if (val === undefined) throw new Error(`Missing value for ${key}`); m[key.slice(2)] = val; @@ -161,10 +150,7 @@ async function runCreatePool(argv: string[]): Promise { factoryUtxos.length > 0, `No factory UTxO at ${warehouse.factoryAddress.bech32} holding ${warehouse.factoryAuthenAsset.toString()} — has the factory been bootstrapped on this network?`, ); - invariant( - factoryUtxos.length === 1, - `Expected exactly 1 factory UTxO, found ${factoryUtxos.length}`, - ); + invariant(factoryUtxos.length === 1, `Expected exactly 1 factory UTxO, found ${factoryUtxos.length}`); const factoryInput = factoryUtxos[0]; // 2) Resolve the factory datum. Kupo returns OUTLINE_DATUM for V1-style @@ -175,19 +161,12 @@ async function runCreatePool(argv: string[]): Promise { factoryInput.output.datumSource.type === DatumSourceType.OUTLINE_DATUM, "Factory UTxO must carry a datum hash with resolved data (OUTLINE_DATUM)", ); - const factoryDatum = SundaeSwapV1.FactoryDatum.fromDataHex( - factoryInput.output.datumSource.data.hex, - ); + const factoryDatum = SundaeSwapV1.FactoryDatum.fromDataHex(factoryInput.output.datumSource.data.hex); // 3) Fetch wallet UTxOs. const paymentCredential = flags.owner.toPaymentCredential(); - invariant( - Maybe.isJust(paymentCredential), - "owner address has no payment credential", - ); - const walletUtxos = await kupo.getUtxosByPaymentCredential( - paymentCredential.payload, - ); + invariant(Maybe.isJust(paymentCredential), "owner address has no payment credential"); + const walletUtxos = await kupo.getUtxosByPaymentCredential(paymentCredential.payload); invariant(walletUtxos.length > 0, `Owner ${flags.owner.bech32} has no UTxOs`); // 4) Build + complete (dry-run with EmulatorProvider — no submit). @@ -250,10 +229,9 @@ type BatchFlags = { loopMs: bigint | null; }; -const DEFAULT_SCOOPER_BY_NETWORK: Partial> = - { - // No defaults yet — pass --scooper. - }; +const DEFAULT_SCOOPER_BY_NETWORK: Partial> = { + // No defaults yet — pass --scooper. +}; // On-chain license token name = "scooper " + suffix, where the prefix is // 8 ASCII bytes (16 hex). We split here to recover the suffix. @@ -265,8 +243,7 @@ function parseBatchFlags(argv: string[]): BatchFlags { let loopMs: bigint | null = null; for (let i = 0; i < argv.length; i++) { const key = argv[i]; - if (!key.startsWith("--")) - throw new Error(`Unexpected positional arg: ${key}`); + if (!key.startsWith("--")) throw new Error(`Unexpected positional arg: ${key}`); if (key === "--dry") { dry = true; continue; @@ -331,8 +308,7 @@ type ScoopPlan = { }; function planSwap(pool: ParsedPool, order: SwapOrderCandidate): ScoopPlan { - const aToB = - order.datum.swapDirection.direction === SundaeSwapV1.SwapDirection.A_TO_B; + const aToB = order.datum.swapDirection.direction === SundaeSwapV1.SwapDirection.A_TO_B; const assetIn = aToB ? pool.datum.assetA : pool.datum.assetB; const assetOut = aToB ? pool.datum.assetB : pool.datum.assetA; @@ -348,10 +324,7 @@ function planSwap(pool: ParsedPool, order: SwapOrderCandidate): ScoopPlan { const amountOut = numerator / denominator; const minReceivable = order.datum.swapDirection.minReceivable ?? 0n; - invariant( - amountOut >= minReceivable, - `slippage: amountOut ${amountOut} < minReceivable ${minReceivable}`, - ); + invariant(amountOut >= minReceivable, `slippage: amountOut ${amountOut} < minReceivable ${minReceivable}`); // Compensation ADA = orderAda − scooperFee, with the ADA leg of the swap // applied: subtract amountIn when assetIn==ADA (user's ADA went into pool), @@ -370,10 +343,7 @@ function planSwap(pool: ParsedPool, order: SwapOrderCandidate): ScoopPlan { const compensation = new Value().add(ADA, compAda); if (!assetOut.equals(ADA)) compensation.add(assetOut, amountOut); - const newPoolValue = pool.utxo.output.value - .clone() - .add(assetIn, amountIn) - .subtract(assetOut, amountOut); + const newPoolValue = pool.utxo.output.value.clone().add(assetIn, amountIn).subtract(assetOut, amountOut); return { compensation, newPoolValue, amountOut, assetIn, assetOut, amountIn }; } @@ -406,9 +376,7 @@ async function runBatch(argv: string[]): Promise { const intervalMs = Number(flags.loopMs); for (let iteration = 1; ; iteration++) { const startedAt = Date.now(); - console.log( - `\n=== batch iteration ${iteration} @ ${new Date(startedAt).toISOString()} ===`, - ); + console.log(`\n=== batch iteration ${iteration} @ ${new Date(startedAt).toISOString()} ===`); try { await runOnce(); } catch (err) { @@ -416,9 +384,7 @@ async function runBatch(argv: string[]): Promise { } const elapsedMs = Date.now() - startedAt; const sleepMs = Math.max(0, intervalMs - elapsedMs); - console.log( - `iteration ${iteration} took ${elapsedMs}ms; sleeping ${sleepMs}ms`, - ); + console.log(`iteration ${iteration} took ${elapsedMs}ms; sleeping ${sleepMs}ms`); await new Promise((resolve) => setTimeout(resolve, sleepMs)); } } @@ -439,17 +405,11 @@ async function runBatchInner( console.log(`Found ${poolUtxos.length} pool-spend UTxOs`); const pools: ParsedPool[] = []; for (const utxo of poolUtxos) { - if ( - !utxo.output.datumSource || - utxo.output.datumSource.type !== DatumSourceType.OUTLINE_DATUM - ) - continue; + if (!utxo.output.datumSource || utxo.output.datumSource.type !== DatumSourceType.OUTLINE_DATUM) continue; try { pools.push({ utxo, - datum: SundaeSwapV1.PoolDatum.fromCborHex( - utxo.output.datumSource.data.hex, - ), + datum: SundaeSwapV1.PoolDatum.fromCborHex(utxo.output.datumSource.data.hex), }); } catch { // Skip non-pool UTxOs that happen to live at the pool address. @@ -471,13 +431,8 @@ async function runBatchInner( nonPubKeyBeneficiary: 0, noMatchingPool: 0, }; - const skipSamples: Array<{ ref: string; reason: string; detail?: unknown }> = - []; - const noteSkip = ( - utxo: Utxo, - reason: keyof typeof skipCounts, - detail?: unknown, - ) => { + const skipSamples: Array<{ ref: string; reason: string; detail?: unknown }> = []; + const noteSkip = (utxo: Utxo, reason: keyof typeof skipCounts, detail?: unknown) => { skipCounts[reason]++; if (skipSamples.length < 5) { skipSamples.push({ @@ -490,26 +445,18 @@ async function runBatchInner( let match: { order: SwapOrderCandidate; pool: ParsedPool } | null = null; for (const utxo of orderUtxos) { - if ( - !utxo.output.datumSource || - utxo.output.datumSource.type !== DatumSourceType.OUTLINE_DATUM - ) { + if (!utxo.output.datumSource || utxo.output.datumSource.type !== DatumSourceType.OUTLINE_DATUM) { noteSkip(utxo, "notOutlineDatum"); continue; } let datum: SundaeSwapV1.OrderDatum; try { - datum = SundaeSwapV1.OrderDatum.fromDataHex( - utxo.output.datumSource.data.hex, - flags.network, - ); + datum = SundaeSwapV1.OrderDatum.fromDataHex(utxo.output.datumSource.data.hex, flags.network); } catch (err) { noteSkip(utxo, "badDatum", String(err)); continue; } - if ( - Maybe.isNothing(datum.orderAddresses.destination.address.toPubKeyHash()) - ) { + if (Maybe.isNothing(datum.orderAddresses.destination.address.toPubKeyHash())) { noteSkip(utxo, "nonPubKeyBeneficiary", { beneficiary: datum.orderAddresses.destination.address.bech32, }); @@ -525,9 +472,7 @@ async function runBatchInner( } debugData.orderFilter = { skipCounts, skipSamples }; if (!match) { - console.log( - `No valid swap order with a matching pool — skipping. skipCounts=${XJSON.stringify(skipCounts)}`, - ); + console.log(`No valid swap order with a matching pool — skipping. skipCounts=${XJSON.stringify(skipCounts)}`); return; } debugData.matched = { @@ -536,11 +481,7 @@ async function runBatchInner( poolIdent: match.pool.datum.ident.hex, assetA: match.pool.datum.assetA.toString(), assetB: match.pool.datum.assetB.toString(), - direction: - match.order.datum.swapDirection.direction === - SundaeSwapV1.SwapDirection.A_TO_B - ? "A_TO_B" - : "B_TO_A", + direction: match.order.datum.swapDirection.direction === SundaeSwapV1.SwapDirection.A_TO_B ? "A_TO_B" : "B_TO_A", }; // 4) Compute execution plan. @@ -550,9 +491,7 @@ async function runBatchInner( assetOut: plan.assetOut.toString(), amountIn: plan.amountIn.toString(), amountOut: plan.amountOut.toString(), - minReceivable: ( - match.order.datum.swapDirection.minReceivable ?? 0n - ).toString(), + minReceivable: (match.order.datum.swapDirection.minReceivable ?? 0n).toString(), }; // 5) Find the single valid (non-expired) license UTxO at the scooper address. @@ -560,20 +499,12 @@ async function runBatchInner( // non-ADA assets, token name starts with "scooper " prefix, and the encoded // week number matches the current POSIX-time week. const scooperPc = flags.scooper.toPaymentCredential(); - invariant( - Maybe.isJust(scooperPc), - "scooper address has no payment credential", - ); - const scooperUtxos = await kupo.getUtxosByPaymentCredential( - scooperPc.payload, - ); - invariant( - scooperUtxos.length > 0, - `scooper ${flags.scooper.bech32} has no UTxOs`, - ); + invariant(Maybe.isJust(scooperPc), "scooper address has no payment credential"); + const scooperUtxos = await kupo.getUtxosByPaymentCredential(scooperPc.payload); + invariant(scooperUtxos.length > 0, `scooper ${flags.scooper.bech32} has no UTxOs`); - const nowMs = BigInt(Date.now()); - const WEEK_MS = SundaeSwapV1Warehouse.WEEK_MS; + const _nowMs = BigInt(Date.now()); + const _WEEK_MS = SundaeSwapV1Warehouse.WEEK_MS; let scooperLicenseInput: Utxo | null = null; let licenseAsset: Asset | null = null; @@ -582,13 +513,8 @@ async function runBatchInner( for (const utxo of scooperUtxos) { if (utxo.output.value.hasPolicyID(warehouse.factoryMintScriptHash)) { scooperLicenseInput = utxo; - const _licenseAsset = utxo.output.value.findAsset( - warehouse.factoryMintScriptHash, - ); - invariant( - _licenseAsset, - "Expected scooper license asset under factory mint script policy", - ); + const _licenseAsset = utxo.output.value.findAsset(warehouse.factoryMintScriptHash); + invariant(_licenseAsset, "Expected scooper license asset under factory mint script policy"); licenseAsset = _licenseAsset; const nameHex = licenseAsset.tokenName.hex; if (!nameHex.startsWith(SCOOPER_LICENSE_NAME_PREFIX_HEX)) continue; @@ -626,9 +552,7 @@ async function runBatchInner( console.log("scooperLicenseInput", XJSON.stringify(scooperLicenseInput, 2)); invariant( - scooperLicenseInput !== null && - licenseAsset !== null && - licenseSuffix !== null, + scooperLicenseInput !== null && licenseAsset !== null && licenseSuffix !== null, `scooper ${flags.scooper.bech32} has no valid (non-expired) license token under policy ` + `${warehouse.factoryMintScriptHash.hex}. Run \`split-scooper-token\` to prepare license UTxOs.`, ); @@ -679,11 +603,8 @@ async function runBatchInner( // Exclude the license UTxO from walletUtxos — it is already forced into // body.inputs via collectFromPubKey. Passing only the remaining UTxOs lets // MINSWAP cover escrow + fee cleanly. - const otherScooperUtxos = scooperUtxos.filter( - (u) => !TxIn.equals(u.input, scooperLicenseInput.input), - ); - const isPureAda = (u: Utxo) => - u.output.value.assets().every((a) => a.equals(ADA)); + const otherScooperUtxos = scooperUtxos.filter((u) => !TxIn.equals(u.input, scooperLicenseInput.input)); + const isPureAda = (u: Utxo) => u.output.value.assets().every((a) => a.equals(ADA)); const pureAdaCollaterals = otherScooperUtxos.filter(isPureAda); debugData.pureAdaCollateralCount = pureAdaCollaterals.length; invariant( @@ -730,34 +651,21 @@ async function runBatchInner( } const seedPhrase = process.env["AGENT_SEED_PHRASE"]; - invariant( - seedPhrase, - "AGENT_SEED_PHRASE env var is required when --dry is omitted", - ); + invariant(seedPhrase, "AGENT_SEED_PHRASE env var is required when --dry is omitted"); const wallet = baseAddressWalletFromSeed(seedPhrase, flags.network); invariant( wallet.address.bech32 === flags.scooper.bech32, `AGENT_SEED_PHRASE derives ${wallet.address.bech32} but --scooper is ${flags.scooper.bech32}`, ); - const signedTxHex = result.value - .signWithPrivateKey(wallet.paymentKey) - .complete(); + const signedTxHex = result.value.signWithPrivateKey(wallet.paymentKey).complete(); console.log("\n--- signed tx cbor ---"); console.log(signedTxHex); const cardanoscanKey = process.env["CARDANOSCAN_KEY"]; - invariant( - cardanoscanKey, - "CARDANOSCAN_KEY env var is required to submit (use --dry to skip submit)", - ); - const cardanoscan = CardanoscanProvider.forNetwork( - flags.network, - cardanoscanKey, - ); + invariant(cardanoscanKey, "CARDANOSCAN_KEY env var is required to submit (use --dry to skip submit)"); + const cardanoscan = CardanoscanProvider.forNetwork(flags.network, cardanoscanKey); await cardanoscan.submitTx(signedTxHex); - const txHash = ECSLConverter.getTxHash( - RustModule.getE.Transaction.from_hex(signedTxHex), - ); + const txHash = ECSLConverter.getTxHash(RustModule.getE.Transaction.from_hex(signedTxHex)); console.log(`\nsubmitted via Cardanoscan, txHash: ${txHash}`); } @@ -778,8 +686,7 @@ function parseSplitScooperFlags(argv: string[]): SplitScooperFlags { let dry = false; for (let i = 0; i < argv.length; i++) { const key = argv[i]; - if (!key.startsWith("--")) - throw new Error(`Unexpected positional arg: ${key}`); + if (!key.startsWith("--")) throw new Error(`Unexpected positional arg: ${key}`); if (key === "--dry") { dry = true; continue; @@ -803,16 +710,11 @@ async function runSplitScooperToken(argv: string[]): Promise { const pc = flags.scooper.toPaymentCredential(); invariant(Maybe.isJust(pc), "scooper address has no payment credential"); const scooperUtxos = await kupo.getUtxosByPaymentCredential(pc.payload); - invariant( - scooperUtxos.length > 0, - `scooper ${flags.scooper.bech32} has no UTxOs`, - ); + invariant(scooperUtxos.length > 0, `scooper ${flags.scooper.bech32} has no UTxOs`); // Pick UTxOs that hold any asset under factoryMintScriptHash (license policy). const licenseUtxos = scooperUtxos.filter((u) => - u.output.value - .assets() - .some((a) => a.currencySymbol.equals(warehouse.factoryMintScriptHash)), + u.output.value.assets().some((a) => a.currencySymbol.equals(warehouse.factoryMintScriptHash)), ); invariant( licenseUtxos.length > 0, @@ -823,8 +725,7 @@ async function runSplitScooperToken(argv: string[]): Promise { const totals = new Map(); for (const u of licenseUtxos) { for (const asset of u.output.value.assets()) { - if (!asset.currencySymbol.equals(warehouse.factoryMintScriptHash)) - continue; + if (!asset.currencySymbol.equals(warehouse.factoryMintScriptHash)) continue; const key = asset.toString(); const qty = u.output.value.get(asset); const prev = totals.get(key); @@ -836,12 +737,7 @@ async function runSplitScooperToken(argv: string[]): Promise { const outputs: TxOut[] = []; for (const { asset, qty } of totals.values()) { for (let i = 0n; i < qty; i++) { - outputs.push( - new TxOut( - flags.scooper, - new Value().add(ADA, 2_000_000n).add(asset, 1n), - ), - ); + outputs.push(new TxOut(flags.scooper, new Value().add(ADA, 2_000_000n).add(asset, 1n))); } } @@ -849,9 +745,7 @@ async function runSplitScooperToken(argv: string[]): Promise { // and re-emitted as one-token outputs. Pass ALL scooper UTxOs to MINSWAP so it // can pull extra pure-ADA inputs to cover fee + min-ada on each split output // (the license UTxOs alone usually don't carry enough ADA). - const txBuilder = new TxBuilder(flags.network) - .collectFromPubKey(...licenseUtxos) - .payTo(...outputs); + const txBuilder = new TxBuilder(flags.network).collectFromPubKey(...licenseUtxos).payTo(...outputs); const result = await txBuilder.complete({ walletUtxos: scooperUtxos, @@ -885,34 +779,21 @@ async function runSplitScooperToken(argv: string[]): Promise { } const seedPhrase = process.env["AGENT_SEED_PHRASE"]; - invariant( - seedPhrase, - "AGENT_SEED_PHRASE env var is required when --dry is omitted", - ); + invariant(seedPhrase, "AGENT_SEED_PHRASE env var is required when --dry is omitted"); const wallet = baseAddressWalletFromSeed(seedPhrase, flags.network); invariant( wallet.address.bech32 === flags.scooper.bech32, `AGENT_SEED_PHRASE derives ${wallet.address.bech32} but --scooper is ${flags.scooper.bech32}`, ); - const signedTxHex = result.value - .signWithPrivateKey(wallet.paymentKey) - .complete(); + const signedTxHex = result.value.signWithPrivateKey(wallet.paymentKey).complete(); console.log("\n--- signed tx cbor ---"); console.log(signedTxHex); const cardanoscanKey = process.env["CARDANOSCAN_KEY"]; - invariant( - cardanoscanKey, - "CARDANOSCAN_KEY env var is required to submit (use --dry to skip submit)", - ); - const cardanoscan = CardanoscanProvider.forNetwork( - flags.network, - cardanoscanKey, - ); + invariant(cardanoscanKey, "CARDANOSCAN_KEY env var is required to submit (use --dry to skip submit)"); + const cardanoscan = CardanoscanProvider.forNetwork(flags.network, cardanoscanKey); await cardanoscan.submitTx(signedTxHex); - const txHash = ECSLConverter.getTxHash( - RustModule.getE.Transaction.from_hex(signedTxHex), - ); + const txHash = ECSLConverter.getTxHash(RustModule.getE.Transaction.from_hex(signedTxHex)); console.log(`\nsubmitted via Cardanoscan, txHash: ${txHash}`); } diff --git a/apps/example/src/syncer.ts b/apps/example/src/syncer.ts index 21f3791..4bece73 100644 --- a/apps/example/src/syncer.ts +++ b/apps/example/src/syncer.ts @@ -1,9 +1,19 @@ import fs from "node:fs"; import * as Ogmios from "@cardano-ogmios/client"; -import invariant from "@minswap/tiny-invariant"; import { NetworkEnvironment } from "@minswap/felis-ledger-core"; import { RustModule } from "@minswap/felis-ledger-utils"; -import { MinswapStableswapSyncer, MinswapV1Syncer, MinswapV2Syncer, SplashSyncer, SundaeSwapV1Syncer, SundaeSwapV3Syncer, Transaction, WingridersV1Syncer, WingridersV2Syncer } from "@minswap/felis-syncer"; +import { + MinswapStableswapSyncer, + MinswapV1Syncer, + MinswapV2Syncer, + SplashSyncer, + SundaeSwapV1Syncer, + SundaeSwapV3Syncer, + Transaction, + WingridersV1Syncer, + WingridersV2Syncer, +} from "@minswap/felis-syncer"; +import invariant from "@minswap/tiny-invariant"; const main = async () => { await RustModule.load(); @@ -11,8 +21,7 @@ const main = async () => { const networkEnv = NetworkEnvironment.TESTNET_PREPROD; const context = await Ogmios.createInteractionContext( (err: Error) => console.error("ogmios error", err), - (code, reason) => - console.info("ogmios connection closed", { code, reason }), + (code, reason) => console.info("ogmios connection closed", { code, reason }), { connection: { host: "dev-6", port: 1338 } }, ); const stateQueryClient = await Ogmios.createLedgerStateQueryClient(context); @@ -21,9 +30,15 @@ const main = async () => { const tip = await stateQueryClient.ledgerTip(); console.info("Current tip:", tip); - const minswapV2MapPool: MinswapV2Syncer.MapPool = JSON.parse(fs.readFileSync("data/minswap-dex-v2-map-pool.json", "utf-8")); - const sundaeSwapV1MapPool: SundaeSwapV1Syncer.MapPool = JSON.parse(fs.readFileSync("data/sundaeswap-v1-map-pool.json", "utf-8")); - const sundaeSwapV3MapPool: SundaeSwapV3Syncer.MapPool = JSON.parse(fs.readFileSync("data/sundaeswap-v3-map-pool.json", "utf-8")); + const minswapV2MapPool: MinswapV2Syncer.MapPool = JSON.parse( + fs.readFileSync("data/minswap-dex-v2-map-pool.json", "utf-8"), + ); + const sundaeSwapV1MapPool: SundaeSwapV1Syncer.MapPool = JSON.parse( + fs.readFileSync("data/sundaeswap-v1-map-pool.json", "utf-8"), + ); + const sundaeSwapV3MapPool: SundaeSwapV3Syncer.MapPool = JSON.parse( + fs.readFileSync("data/sundaeswap-v3-map-pool.json", "utf-8"), + ); const client = await Ogmios.createChainSynchronizationClient( context, @@ -120,8 +135,7 @@ const main = async () => { sundaeSwapV3MapPool[poolIdent] = { assetA, assetB }; } } - } - catch(err) { + } catch (err) { console.error("Error processing block:", err); } @@ -132,7 +146,10 @@ const main = async () => { ); // await client.resume([tip], 1000); - await client.resume([ { id: "13bd544f11f186bf9cddb8a1bd0f47be9c1a9976264612e55838e41c680f271a", slot: 113539634 }], 1000); + await client.resume( + [{ id: "13bd544f11f186bf9cddb8a1bd0f47be9c1a9976264612e55838e41c680f271a", slot: 113539634 }], + 1000, + ); }; main(); diff --git a/apps/example/src/w2.ts b/apps/example/src/w2.ts index 564c53e..0fae0c4 100644 --- a/apps/example/src/w2.ts +++ b/apps/example/src/w2.ts @@ -1,26 +1,22 @@ +import { WingridersV2 as WRV2Build } from "@minswap/felis-build-tx"; +import { baseAddressWalletFromSeed } from "@minswap/felis-cip"; import { ADA, Address, Asset, - Bytes, + type Bytes, DatumSourceType, NetworkEnvironment, TxIn, TxOut, - Utxo, + type Utxo, Value, XJSON, } from "@minswap/felis-ledger-core"; import { Maybe, RustModule } from "@minswap/felis-ledger-utils"; import { CardanoscanProvider, KupoService } from "@minswap/felis-provider"; import { CoinSelectionAlgorithm, ECSLConverter, EmulatorProvider, TxBuilder } from "@minswap/felis-tx-builder"; -import { WingridersV2 as WRV2Build } from "@minswap/felis-build-tx"; -import { baseAddressWalletFromSeed } from "@minswap/felis-cip"; -import { - normalizePair, - WingridersV2, - WingridersV2Warehouse, -} from "@minswap/felis-wingriders-v2"; +import { normalizePair, WingridersV2, WingridersV2Warehouse } from "@minswap/felis-wingriders-v2"; import invariant from "@minswap/tiny-invariant"; // ─── CLI plumbing ────────────────────────────────────────────────────────── @@ -88,8 +84,9 @@ async function runCreatePool(argv: string[]): Promise { const flags = parseCreatePoolFlags(argv); const [assetA, assetB] = normalizePair([flags.assetA, flags.assetB]); // Rotate amounts if the pair was reordered. - const [amountA, amountB] = - assetA.equals(flags.assetA) ? [flags.amountA, flags.amountB] : [flags.amountB, flags.amountA]; + const [amountA, amountB] = assetA.equals(flags.assetA) + ? [flags.amountA, flags.amountB] + : [flags.amountB, flags.amountA]; const warehouse = WingridersV2Warehouse.getInstance(flags.network); const kupo = new KupoService(resolveKupoUrl(flags.network)); @@ -97,10 +94,7 @@ async function runCreatePool(argv: string[]): Promise { const factoryAsset = warehouse.factoryAsset; console.log(factoryAsset.toString()); console.log(warehouse.factoryAddress.bech32); - const factoryUtxos = await kupo.utxoAtScriptHashWithAsset( - warehouse.factoryScriptHash, - factoryAsset, - ); + const factoryUtxos = await kupo.utxoAtScriptHashWithAsset(warehouse.factoryScriptHash, factoryAsset); console.log(XJSON.stringify(factoryUtxos, 2)); const lpAsset = WingridersV2.computeLpAsset(warehouse.dexSymbolHash, assetA, assetB); @@ -258,15 +252,28 @@ function planSwap( const agentFee = poolDatum.agentFeeAda; const rawIn = order.utxo.output.value.get(assetIn); const lockX = assetIn.equals(ADA) ? rawIn - oil - agentFee : rawIn; - invariant(lockX > 0n, `order lockX must be positive | lockX | ${lockX}| rawIn | ${rawIn} | oil | ${oil} | agentFee | ${agentFee} | ${assetIn.toString()}`); + invariant( + lockX > 0n, + `order lockX must be positive | lockX | ${lockX}| rawIn | ${rawIn} | oil | ${oil} | agentFee | ${agentFee} | ${assetIn.toString()}`, + ); // Effective reserves as seen by the curve: UTxO value minus treasuries // (and minPoolAda on the ADA side). const valueA = pool.output.value.get(poolDatum.assetA); const valueB = pool.output.value.get(poolDatum.assetB); const adaIsA = poolDatum.assetA.equals(ADA); - const reserveA = valueA - poolDatum.treasuryA - poolDatum.projectTreasuryA - poolDatum.reserveTreasuryA - (adaIsA ? WingridersV2Warehouse.MIN_POOL_ADA : 0n); - const reserveB = valueB - poolDatum.treasuryB - poolDatum.projectTreasuryB - poolDatum.reserveTreasuryB - (!adaIsA && poolDatum.assetB.equals(ADA) ? WingridersV2Warehouse.MIN_POOL_ADA : 0n); + const reserveA = + valueA - + poolDatum.treasuryA - + poolDatum.projectTreasuryA - + poolDatum.reserveTreasuryA - + (adaIsA ? WingridersV2Warehouse.MIN_POOL_ADA : 0n); + const reserveB = + valueB - + poolDatum.treasuryB - + poolDatum.projectTreasuryB - + poolDatum.reserveTreasuryB - + (!adaIsA && poolDatum.assetB.equals(ADA) ? WingridersV2Warehouse.MIN_POOL_ADA : 0n); const rIn = aToB ? reserveA : reserveB; const rOut = aToB ? reserveB : reserveA; @@ -280,7 +287,10 @@ function planSwap( const effectiveIn = rIn + lockX - swapFee - protocolFee - projectFee - reserveFee; const newOutReserve = ceilDiv(rIn * rOut, effectiveIn); const amountOut = rOut - newOutReserve; - invariant(amountOut >= order.datum.minWanted, `slippage: amountOut ${amountOut} < minWanted ${order.datum.minWanted}`); + invariant( + amountOut >= order.datum.minWanted, + `slippage: amountOut ${amountOut} < minWanted ${order.datum.minWanted}`, + ); const compensation = new Value().add(ADA, oil); compensation.add(assetOut, amountOut); @@ -351,7 +361,6 @@ async function runBatchInner( kupo: KupoService, debugData: Record, ): Promise { - // 1) All active CP pools (at poolScriptHashCP, carrying the validity token) const poolUtxos = await kupo.utxoAtScriptHashWithAsset(warehouse.poolScriptHashCP, warehouse.validityAsset); console.log(`Found ${poolUtxos.length} pool UTxOs`); @@ -412,19 +421,13 @@ async function runBatchInner( noteSkip(utxo, "notSwap", { type: datum.type }); continue; } - if ( - datum.datumType !== WingridersV2.DatumType.No && - datum.datumType !== WingridersV2.DatumType.Inline - ) { + if (datum.datumType !== WingridersV2.DatumType.No && datum.datumType !== WingridersV2.DatumType.Inline) { noteSkip(utxo, "unsupportedDatumType", { datumType: datum.datumType }); continue; } // Pubkey-only check applies only to datumType=No. With datumType=Inline the // beneficiary may be a script address (the inline datum is the script's datum). - if ( - datum.datumType === WingridersV2.DatumType.No && - Maybe.isNothing(datum.beneficiary.toPubKeyHash()) - ) { + if (datum.datumType === WingridersV2.DatumType.No && Maybe.isNothing(datum.beneficiary.toPubKeyHash())) { noteSkip(utxo, "nonPubKeyBeneficiary", { beneficiary: datum.beneficiary.bech32 }); continue; } @@ -433,9 +436,7 @@ async function runBatchInner( continue; } - const pool = pools.find( - (p) => p.datum.assetA.equals(datum.assetA) && p.datum.assetB.equals(datum.assetB), - ); + const pool = pools.find((p) => p.datum.assetA.equals(datum.assetA) && p.datum.assetB.equals(datum.assetB)); if (!pool) { noteSkip(utxo, "noMatchingPool", { assetA: datum.assetA.toString(), @@ -449,9 +450,7 @@ async function runBatchInner( debugData.orderFilter = { skipCounts, skipSamples }; debugData.candidatesCount = candidates.length; if (candidates.length === 0) { - console.log( - `No valid swap orders with a matching CP pool — skipping. skipCounts=${XJSON.stringify(skipCounts)}`, - ); + console.log(`No valid swap orders with a matching CP pool — skipping. skipCounts=${XJSON.stringify(skipCounts)}`); return; } @@ -695,14 +694,8 @@ async function runSplitAgentToken(argv: string[]): Promise { // Spend ALL agent UTxOs and emit one fresh output: 5 ADA + 1 X token. // MINWALLET_SEND_ALL pushes every wallet UTxO into inputs and sends the remainder // back to changeAddress. - const splitOut = new TxOut( - flags.agent, - new Value().add(ADA, 5_000_000n).add(warehouse.agentAsset, 1n), - ); - const collateral = new TxOut( - flags.agent, - new Value().add(ADA, 5_000_000n), - ); + const splitOut = new TxOut(flags.agent, new Value().add(ADA, 5_000_000n).add(warehouse.agentAsset, 1n)); + const collateral = new TxOut(flags.agent, new Value().add(ADA, 5_000_000n)); const txBuilder = new TxBuilder(flags.network).payTo(splitOut, collateral); From cdb98592a5958ee3150193cb2a759384adee0c64 Mon Sep 17 00:00:00 2001 From: tony Date: Wed, 6 May 2026 16:04:11 +0700 Subject: [PATCH 6/8] update logic --- apps/example/src/w2.ts | 9 ++----- .../minswap-build-tx/src/wingriders-v2.ts | 27 ++++++++++++------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/apps/example/src/w2.ts b/apps/example/src/w2.ts index 0fae0c4..953f52a 100644 --- a/apps/example/src/w2.ts +++ b/apps/example/src/w2.ts @@ -392,7 +392,6 @@ async function runBatchInner( notInlineDatum: 0, badDatum: 0, notSwap: 0, - unsupportedDatumType: 0, nonPubKeyBeneficiary: 0, deadlineSoon: 0, noMatchingPool: 0, @@ -421,12 +420,8 @@ async function runBatchInner( noteSkip(utxo, "notSwap", { type: datum.type }); continue; } - if (datum.datumType !== WingridersV2.DatumType.No && datum.datumType !== WingridersV2.DatumType.Inline) { - noteSkip(utxo, "unsupportedDatumType", { datumType: datum.datumType }); - continue; - } - // Pubkey-only check applies only to datumType=No. With datumType=Inline the - // beneficiary may be a script address (the inline datum is the script's datum). + // Pubkey-only check applies only to datumType=No. With datumType=Inline/Hash the + // beneficiary may be a script address (the compensation datum is the script's datum). if (datum.datumType === WingridersV2.DatumType.No && Maybe.isNothing(datum.beneficiary.toPubKeyHash())) { noteSkip(utxo, "nonPubKeyBeneficiary", { beneficiary: datum.beneficiary.bech32 }); continue; diff --git a/packages/minswap-build-tx/src/wingriders-v2.ts b/packages/minswap-build-tx/src/wingriders-v2.ts index cc78be4..465f9fb 100644 --- a/packages/minswap-build-tx/src/wingriders-v2.ts +++ b/packages/minswap-build-tx/src/wingriders-v2.ts @@ -3,6 +3,7 @@ import { type Address, type Asset, Bytes, + DatumSource, DatumSourceType, type NetworkEnvironment, PlutusData, @@ -209,10 +210,6 @@ export namespace WingridersV2 { networkEnv, ); invariant(orderDatum.type === WRV2.OrderType.Swap, "WingridersV2.buildBatchTx: only swap orders supported in v1"); - invariant( - orderDatum.datumType === WRV2.DatumType.No || orderDatum.datumType === WRV2.DatumType.Inline, - `WingridersV2.buildBatchTx: only datumType=No or datumType=Inline supported (got ${orderDatum.datumType})`, - ); const sortedInputs = [pool.input, agentInput, order.input].slice().sort((a, b) => TxIn.compare(a.input, b.input)); const indexOf = (u: Utxo): bigint => { @@ -240,13 +237,23 @@ export namespace WingridersV2 { type: DatumSourceType.INLINE_DATUM, data: Bytes.fromHex(WRV2.PoolDatum.toCborHex(pool.newDatum)), }); - const compensationDatumSource = - orderDatum.datumType === WRV2.DatumType.Inline - ? { - type: DatumSourceType.INLINE_DATUM as const, + const compensationDatumSource = ((): DatumSource | undefined => { + switch (orderDatum.datumType) { + case WRV2.DatumType.No: + return undefined; + case WRV2.DatumType.Inline: + return { + type: DatumSourceType.INLINE_DATUM, data: Bytes.fromHex(PlutusData.toDataHex(orderDatum.compensationDatum)), - } - : undefined; + }; + case WRV2.DatumType.Hash: + // OUTLINE_DATUM stores hash on the output and lets TxBuilder.payTo + // attach the datum as a tx witness so the chain can resolve it. + return DatumSource.newOutlineDatum( + Bytes.fromHex(PlutusData.toDataHex(orderDatum.compensationDatum)), + ); + } + })(); const compensationOut = new TxOut( orderDatum.beneficiary, order.compensation, From 0cf7a35c58cbbe69ec50e244dc3c0b1e5910cf0f Mon Sep 17 00:00:00 2001 From: tony Date: Fri, 8 May 2026 10:19:57 +0700 Subject: [PATCH 7/8] format --- packages/minswap-build-tx/src/wingriders-v2.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/minswap-build-tx/src/wingriders-v2.ts b/packages/minswap-build-tx/src/wingriders-v2.ts index 465f9fb..d712b65 100644 --- a/packages/minswap-build-tx/src/wingriders-v2.ts +++ b/packages/minswap-build-tx/src/wingriders-v2.ts @@ -249,9 +249,7 @@ export namespace WingridersV2 { case WRV2.DatumType.Hash: // OUTLINE_DATUM stores hash on the output and lets TxBuilder.payTo // attach the datum as a tx witness so the chain can resolve it. - return DatumSource.newOutlineDatum( - Bytes.fromHex(PlutusData.toDataHex(orderDatum.compensationDatum)), - ); + return DatumSource.newOutlineDatum(Bytes.fromHex(PlutusData.toDataHex(orderDatum.compensationDatum))); } })(); const compensationOut = new TxOut( From a358348caa57ee95495d17146decaff93ba283c4 Mon Sep 17 00:00:00 2001 From: tony Date: Tue, 12 May 2026 15:26:55 +0700 Subject: [PATCH 8/8] typo --- apps/example/src/fetchMapPools.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/example/src/fetchMapPools.ts b/apps/example/src/fetchMapPools.ts index 06c459e..233d7e0 100644 --- a/apps/example/src/fetchMapPools.ts +++ b/apps/example/src/fetchMapPools.ts @@ -17,7 +17,6 @@ const _fetchMinswapV2Pools = async (kupo: KupoService, networkEnv: NetworkEnviro for (const utxo of minswapV2Utxos) { const pool = PoolV2.fromUtxo(utxo, networkEnv); if (pool.type === "ok") { - ``; const lpAsset = pool.value.lpAsset.toString(); const assetA = pool.value.assetA.toString(); const assetB = pool.value.assetB.toString();