@minswap/felis-syncer
v0.0.1
Published
A library for parsing Cardano DEX transactions from various protocols. This package provides syncers that can extract order and pool creation information from raw transactions.
Downloads
26
Keywords
Readme
@minswap/felis-syncer
A library for parsing Cardano DEX transactions from various protocols. This package provides syncers that can extract order and pool creation information from raw transactions.
Installation
pnpm add @minswap/felis-syncerPrerequisites
Before using the syncers, you must initialize the WASM module:
import { RustModule } from "@minswap/felis-ledger-utils";
await RustModule.load();Available Syncers
| Syncer | Protocol | CREATE_ORDER | CREATE_POOL |
|--------|----------|--------------|-------------|
| MinswapV1Syncer | Minswap DEX V1 | Yes | No |
| MinswapV2Syncer | Minswap DEX V2 | Yes | Yes |
| MinswapStableswapSyncer | Minswap Stableswap | Yes | Yes |
| SundaeSwapV1Syncer | SundaeSwap V1 | Yes | No |
| SundaeSwapV3Syncer | SundaeSwap V3 | Yes | Yes |
| WingridersV1Syncer | WingRiders V1 | Yes | No |
| WingridersV2Syncer | WingRiders V2 | Yes | No |
| SplashSyncer | Splash | Yes | No |
Core Types
Transaction
The Transaction type is the input format for all syncers. Convert from CSL (Cardano Serialization Lib) transaction:
import { Transaction } from "@minswap/felis-syncer";
import { RustModule } from "@minswap/felis-ledger-utils";
// From CBOR hex
const cslTx = RustModule.getE.Transaction.from_hex(txCborHex);
const tx: Transaction = Transaction.fromECSL(cslTx);Transaction Structure:
type Transaction = {
body: {
inputs: TxIn[];
outputs: TxOut[];
referenceInputs: TxIn[];
mint: Value;
};
witnessSet: {
plutusData: Record<PlutusDataHash, Bytes>;
};
};Output Types (Wrapped Strings)
All syncers return data as string-wrapped types for easy serialization:
type WrapNum = string; // BigInt as string, e.g. "3000000000"
type WrapAsset = string; // Asset ID, e.g. "lovelace" or "policyId.assetName"
type WrapAddress = string; // Bech32 address, e.g. "addr1..."Usage Examples
MinswapV1Syncer
import { MinswapV1Syncer, Transaction } from "@minswap/felis-syncer";
import { NetworkEnvironment } from "@minswap/felis-ledger-core";
const result = MinswapV1Syncer.parseTx({
tx: transaction,
networkEnv: NetworkEnvironment.MAINNET,
});
if (result) {
if (result.type === MinswapV1Syncer.TxType.CREATE_ORDER) {
for (const order of result.newOrders) {
console.log("Sender:", order.sender);
console.log("Asset In:", order.assetIn);
console.log("Amount In:", order.amountIn);
console.log("Asset Out:", order.assetOut);
console.log("Minimum Receive:", order.minimumReceive);
}
}
}MinswapV2Syncer
MinswapV2Syncer requires a MapPool to resolve LP assets to their underlying pair:
import { MinswapV2Syncer, Transaction } from "@minswap/felis-syncer";
import { NetworkEnvironment } from "@minswap/felis-ledger-core";
// MapPool: LP asset string -> { assetA, assetB }
const mapPool: MinswapV2Syncer.MapPool = {
"policyId.lpAssetName": {
assetA: "lovelace",
assetB: "policyId.tokenName",
},
};
const result = MinswapV2Syncer.parseTx({
tx: transaction,
mapPool: mapPool,
networkEnv: NetworkEnvironment.MAINNET,
});
if (result) {
switch (result.type) {
case MinswapV2Syncer.TxType.CREATE_ORDER:
for (const order of result.newOrders) {
console.log("LP Asset:", order.lpAsset);
console.log("Asset In:", order.assetIn);
console.log("Amount In:", order.amountIn);
console.log("DEX Fee:", order.dexFee);
}
break;
case MinswapV2Syncer.TxType.CREATE_POOL:
console.log("LP Asset:", result.lpAsset);
console.log("Asset A:", result.assetA);
console.log("Asset B:", result.assetB);
console.log("Reserve A:", result.reserveA);
console.log("Reserve B:", result.reserveB);
break;
}
}MinswapStableswapSyncer
Stableswap syncer doesn't require a MapPool - pool configs are loaded from the package:
import { MinswapStableswapSyncer, Transaction } from "@minswap/felis-syncer";
import { NetworkEnvironment } from "@minswap/felis-ledger-core";
const result = MinswapStableswapSyncer.parseTx({
tx: transaction,
networkEnv: NetworkEnvironment.MAINNET,
});
if (result) {
switch (result.type) {
case MinswapStableswapSyncer.TxType.CREATE_ORDER:
for (const order of result.newOrders) {
console.log("LP Asset:", order.lpAsset);
console.log("Asset In:", order.assetIn);
console.log("Amount In:", order.amountIn);
console.log("Asset Out:", order.assetOut);
console.log("Batcher Fee:", order.batcherFee);
}
break;
case MinswapStableswapSyncer.TxType.CREATE_POOL:
console.log("LP Asset:", result.lpAsset);
console.log("Assets:", result.assets);
console.log("Balances:", result.balances);
console.log("Amp Coefficient:", result.amplificationCoefficient);
break;
}
}SundaeSwapV3Syncer
import { SundaeSwapV3Syncer, Transaction } from "@minswap/felis-syncer";
import { NetworkEnvironment } from "@minswap/felis-ledger-core";
// MapPool: pool ident hex -> { assetA, assetB }
const mapPool: SundaeSwapV3Syncer.MapPool = {
"1f04...": {
assetA: "lovelace",
assetB: "policyId.tokenName",
},
};
const result = SundaeSwapV3Syncer.parseTx({
tx: transaction,
mapPool: mapPool,
networkEnv: NetworkEnvironment.MAINNET,
});
if (result) {
switch (result.type) {
case SundaeSwapV3Syncer.TxType.CREATE_ORDER:
for (const order of result.newOrders) {
console.log("Asset In:", order.assetIn);
console.log("Max Protocol Fee:", order.maxProtocolFee);
}
break;
case SundaeSwapV3Syncer.TxType.CREATE_POOL:
console.log("Pool Ident:", result.poolIdent);
console.log("Bid Fee %:", result.bidFeePercent);
console.log("Ask Fee %:", result.askFeePercent);
break;
}
}Helper Functions
The Helper namespace provides utility functions for quick transaction filtering:
import { Helper, Transaction } from "@minswap/felis-syncer";
// Check if transaction pays to any address in a set
const addressSet = new Set(["addr1...", "addr2..."]);
if (Helper.hasPaidTo(tx, addressSet)) {
// Transaction has output to one of these addresses
}
// Check if transaction pays to a specific script hash
if (Helper.hasPaidToScript(tx, "scriptHashHex")) {
// Transaction has output to this script
}
// Check if transaction pays to any script in a set
const scriptHashSet = new Set(["hash1...", "hash2..."]);
if (Helper.hasPaidToScripts(tx, scriptHashSet)) {
// Transaction has output to one of these scripts
}
// Check if transaction references specific UTXOs
const refSet = new Set(["txId#index", "txId2#index2"]);
if (Helper.hasReferenceTo(tx, refSet)) {
// Transaction references one of these UTXOs
}Output Type Reference
TxCreateOrder (Common Fields)
All order types include these fields:
| Field | Type | Description |
|-------|------|-------------|
| sender | WrapAddress | Address that created the order |
| receiver | WrapAddress | Address to receive output |
| assetIn | WrapAsset | Asset being swapped |
| amountIn | WrapNum | Amount being swapped |
| assetOut | WrapAsset | Asset to receive |
| minimumReceive | WrapNum | Minimum acceptable output |
| deposit | WrapNum | ADA deposit (returned after execution) |
Protocol-Specific Order Fields
| Protocol | Additional Fields |
|----------|------------------|
| MinswapV1 | dexFee |
| MinswapV2 | lpAsset, dexFee |
| MinswapStableswap | lpAsset, batcherFee |
| SundaeSwapV1 | scooperFee |
| SundaeSwapV3 | maxProtocolFee |
| WingRiders | dexFee |
| Splash | dexFee |
TxCreatePool
Pool creation output varies by protocol:
MinswapV2:
{
type: "CREATE_POOL";
lpAsset: WrapAsset;
assetA: WrapAsset;
assetB: WrapAsset;
reserveA: WrapNum;
reserveB: WrapNum;
reserveLP: WrapNum;
tradingFeeAPercent: WrapNum;
tradingFeeBPercent: WrapNum;
}MinswapStableswap:
{
type: "CREATE_POOL";
lpAsset: WrapAsset;
assets: WrapAsset[]; // Multi-asset pool (2-3 assets)
balances: WrapNum[]; // Balance for each asset
totalLiquidity: WrapNum;
amplificationCoefficient: WrapNum;
fee: WrapNum;
adminFee: WrapNum;
feeDenominator: WrapNum;
}SundaeSwapV3:
{
type: "CREATE_POOL";
poolIdent: string; // Pool identifier hex
lpAsset: WrapAsset;
assetA: WrapAsset;
assetB: WrapAsset;
reserveA: WrapNum;
reserveB: WrapNum;
reserveLP: WrapNum;
bidFeePercent: WrapNum;
askFeePercent: WrapNum;
}Integration Pattern
Typical integration flow for syncing transactions:
import { RustModule } from "@minswap/felis-ledger-utils";
import { NetworkEnvironment } from "@minswap/felis-ledger-core";
import {
Transaction,
Helper,
MinswapV1Syncer,
MinswapV2Syncer,
MinswapStableswapSyncer,
} from "@minswap/felis-syncer";
// Initialize WASM
await RustModule.load();
// Get transaction from your data source (e.g., Ogmios, Blockfrost)
const txCborHex = "84a400...";
// Parse transaction
const cslTx = RustModule.getE.Transaction.from_hex(txCborHex);
const tx = Transaction.fromECSL(cslTx);
// Try each syncer
const networkEnv = NetworkEnvironment.MAINNET;
// Minswap V1
const v1Result = MinswapV1Syncer.parseTx({ tx, networkEnv });
if (v1Result) {
console.log("Minswap V1 transaction:", v1Result);
}
// Minswap V2 (requires mapPool)
const v2Result = MinswapV2Syncer.parseTx({ tx, mapPool, networkEnv });
if (v2Result) {
console.log("Minswap V2 transaction:", v2Result);
}
// Minswap Stableswap
const stableResult = MinswapStableswapSyncer.parseTx({ tx, networkEnv });
if (stableResult) {
console.log("Minswap Stableswap transaction:", stableResult);
}Notes
- All amounts are in lovelace (1 ADA = 1,000,000 lovelace)
- Asset format:
"lovelace"for ADA,"policyId.assetNameHex"for tokens - Returns
nullif transaction doesn't match the protocol - Each syncer only parses swap/exchange orders (not deposits, withdrawals, etc.)
