diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 225b965..98bc5ef 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -12,8 +12,8 @@ jobs:
   build-and-test:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v3
-      - uses: actions/setup-node@v3
+      - uses: actions/checkout@v4
+      - uses: actions/setup-node@v4
         with:
           node-version: 18.x
       - name: Install dependencies
@@ -24,5 +24,6 @@ jobs:
         run: npm run test
         env:
           BLOCKFROST_PROJECT_ID_MAINNET: ${{ secrets.BLOCKFROST_PROJECT_ID_MAINNET }}
+          BLOCKFROST_PROJECT_ID_TESTNET: ${{ secrets.BLOCKFROST_PROJECT_ID_TESTNET }}
       - name: Check format & lint
         run: npm run check-format
diff --git a/package-lock.json b/package-lock.json
index 149e140..2c63be0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,7 +12,8 @@
         "@blockfrost/blockfrost-js": "^5.3.1",
         "@minswap/tiny-invariant": "^1.2.0",
         "big.js": "^6.1.1",
-        "lucid-cardano": "^0.10.1"
+        "lucid-cardano": "^0.10.1",
+        "sha3": "^2.1.4"
       },
       "devDependencies": {
         "@types/big.js": "^6.1.3",
@@ -2066,6 +2067,25 @@
       "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
       "dev": true
     },
+    "node_modules/base64-js": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ]
+    },
     "node_modules/bech32": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz",
@@ -2185,6 +2205,29 @@
         "node-int64": "^0.4.0"
       }
     },
+    "node_modules/buffer": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+      "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ],
+      "dependencies": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.2.1"
+      }
+    },
     "node_modules/buffer-from": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@@ -3877,6 +3920,25 @@
         "node": ">=10.17.0"
       }
     },
+    "node_modules/ieee754": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ]
+    },
     "node_modules/ignore": {
       "version": "5.2.0",
       "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz",
@@ -6253,6 +6315,14 @@
         "node": ">=10"
       }
     },
+    "node_modules/sha3": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/sha3/-/sha3-2.1.4.tgz",
+      "integrity": "sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg==",
+      "dependencies": {
+        "buffer": "6.0.3"
+      }
+    },
     "node_modules/shebang-command": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
diff --git a/package.json b/package.json
index c142f11..68fa9eb 100644
--- a/package.json
+++ b/package.json
@@ -43,7 +43,8 @@
     "@blockfrost/blockfrost-js": "^5.3.1",
     "@minswap/tiny-invariant": "^1.2.0",
     "big.js": "^6.1.1",
-    "lucid-cardano": "^0.10.1"
+    "lucid-cardano": "^0.10.1",
+    "sha3": "^2.1.4"
   },
   "devDependencies": {
     "@types/big.js": "^6.1.3",
diff --git a/src/adapter.ts b/src/adapter.ts
index 99f4300..6182f6e 100644
--- a/src/adapter.ts
+++ b/src/adapter.ts
@@ -6,12 +6,24 @@ import { PaginationOptions } from "@blockfrost/blockfrost-js/lib/types";
 import invariant from "@minswap/tiny-invariant";
 import Big from "big.js";
 
-import { POOL_NFT_POLICY_ID, POOL_SCRIPT_HASH } from "./constants";
-import { PoolHistory, PoolState } from "./types/pool";
-import { checkValidPoolOutput, isValidPoolOutput } from "./types/pool.internal";
+import { Asset } from "./types/asset";
+import {
+  DexV1Constant,
+  DexV2Constant,
+  StableswapConstant,
+} from "./types/constants";
+import { NetworkId } from "./types/network";
+import { PoolV1, PoolV2, StablePool } from "./types/pool";
+import {
+  checkValidPoolOutput,
+  isValidPoolOutput,
+  normalizeAssets,
+} from "./types/pool.internal";
+import { TxHistory } from "./types/tx.internal";
 import { getScriptHashFromAddress } from "./utils/address-utils.internal";
 
 export type BlockfrostAdapterOptions = {
+  networkId: NetworkId;
   blockFrost: BlockFrostAPI;
 };
 
@@ -24,7 +36,7 @@ export type GetPoolByIdParams = {
 };
 
 export type GetPoolPriceParams = {
-  pool: PoolState;
+  pool: PoolV1.State;
   decimalsA?: number;
   decimalsB?: number;
 };
@@ -37,26 +49,36 @@ export type GetPoolInTxParams = {
   txHash: string;
 };
 
+export type GetStablePoolInTxParams = {
+  networkId: NetworkId;
+  txHash: string;
+};
+
 export class BlockfrostAdapter {
   private readonly api: BlockFrostAPI;
+  private readonly networkId: NetworkId;
 
-  constructor({ blockFrost }: BlockfrostAdapterOptions) {
+  constructor({ networkId, blockFrost }: BlockfrostAdapterOptions) {
+    this.networkId = networkId;
     this.api = blockFrost;
   }
 
   /**
    * @returns The latest pools or empty array if current page is after last page
    */
-  public async getPools({
+  public async getV1Pools({
     page,
     count = 100,
     order = "asc",
-  }: GetPoolsParams): Promise<PoolState[]> {
-    const utxos = await this.api.addressesUtxos(POOL_SCRIPT_HASH, {
-      count,
-      order,
-      page,
-    });
+  }: GetPoolsParams): Promise<PoolV1.State[]> {
+    const utxos = await this.api.addressesUtxos(
+      DexV1Constant.POOL_SCRIPT_HASH,
+      {
+        count,
+        order,
+        page,
+      }
+    );
     return utxos
       .filter((utxo) =>
         isValidPoolOutput(utxo.address, utxo.amount, utxo.data_hash)
@@ -66,7 +88,7 @@ export class BlockfrostAdapter {
           utxo.data_hash,
           `expect pool to have datum hash, got ${utxo.data_hash}`
         );
-        return new PoolState(
+        return new PoolV1.State(
           utxo.address,
           { txHash: utxo.tx_hash, index: utxo.output_index },
           utxo.amount,
@@ -79,12 +101,12 @@ export class BlockfrostAdapter {
    * Get a specific pool by its ID.
    * @param {Object} params - The parameters.
    * @param {string} params.pool - The pool ID. This is the asset name of a pool's NFT and LP tokens. It can also be acquired by calling pool.id.
-   * @returns {PoolState | null} - Returns the pool or null if not found.
+   * @returns {PoolV1.State | null} - Returns the pool or null if not found.
    */
-  public async getPoolById({
+  public async getV1PoolById({
     id,
-  }: GetPoolByIdParams): Promise<PoolState | null> {
-    const nft = `${POOL_NFT_POLICY_ID}${id}`;
+  }: GetPoolByIdParams): Promise<PoolV1.State | null> {
+    const nft = `${DexV1Constant.POOL_NFT_POLICY_ID}${id}`;
     const nftTxs = await this.api.assetsTransactions(nft, {
       count: 1,
       page: 1,
@@ -93,23 +115,23 @@ export class BlockfrostAdapter {
     if (nftTxs.length === 0) {
       return null;
     }
-    return this.getPoolInTx({ txHash: nftTxs[0].tx_hash });
+    return this.getV1PoolInTx({ txHash: nftTxs[0].tx_hash });
   }
 
-  public async getPoolHistory({
+  public async getV1PoolHistory({
     id,
     page = 1,
     count = 100,
     order = "desc",
-  }: GetPoolHistoryParams): Promise<PoolHistory[]> {
-    const nft = `${POOL_NFT_POLICY_ID}${id}`;
+  }: GetPoolHistoryParams): Promise<TxHistory[]> {
+    const nft = `${DexV1Constant.POOL_NFT_POLICY_ID}${id}`;
     const nftTxs = await this.api.assetsTransactions(nft, {
       count,
       page,
       order,
     });
     return nftTxs.map(
-      (tx): PoolHistory => ({
+      (tx): TxHistory => ({
         txHash: tx.tx_hash,
         txIndex: tx.tx_index,
         blockHeight: tx.block_height,
@@ -122,14 +144,15 @@ export class BlockfrostAdapter {
    * Get pool state in a transaction.
    * @param {Object} params - The parameters.
    * @param {string} params.txHash - The transaction hash containing pool output. One of the way to acquire is by calling getPoolHistory.
-   * @returns {PoolState} - Returns the pool state or null if the transaction doesn't contain pool.
+   * @returns {PoolV1.State} - Returns the pool state or null if the transaction doesn't contain pool.
    */
-  public async getPoolInTx({
+  public async getV1PoolInTx({
     txHash,
-  }: GetPoolInTxParams): Promise<PoolState | null> {
+  }: GetPoolInTxParams): Promise<PoolV1.State | null> {
     const poolTx = await this.api.txsUtxos(txHash);
     const poolUtxo = poolTx.outputs.find(
-      (o) => getScriptHashFromAddress(o.address) === POOL_SCRIPT_HASH
+      (o) =>
+        getScriptHashFromAddress(o.address) === DexV1Constant.POOL_SCRIPT_HASH
     );
     if (!poolUtxo) {
       return null;
@@ -139,7 +162,7 @@ export class BlockfrostAdapter {
       poolUtxo.data_hash,
       `expect pool to have datum hash, got ${poolUtxo.data_hash}`
     );
-    return new PoolState(
+    return new PoolV1.State(
       poolUtxo.address,
       { txHash: txHash, index: poolUtxo.output_index },
       poolUtxo.amount,
@@ -170,7 +193,7 @@ export class BlockfrostAdapter {
    * @param {string} [params.decimalsB] - The decimals of assetB in pool, if undefined then query from Blockfrost.
    * @returns {[string, string]} - Returns a pair of asset A/B price and B/A price, adjusted to decimals.
    */
-  public async getPoolPrice({
+  public async getV1PoolPrice({
     pool,
     decimalsA,
     decimalsB,
@@ -196,4 +219,178 @@ export class BlockfrostAdapter {
     const scriptsDatum = await this.api.scriptsDatumCbor(datumHash);
     return scriptsDatum.cbor;
   }
+
+  public async getAllV2Pools(): Promise<{
+    pools: PoolV2.State[];
+    errors: unknown[];
+  }> {
+    const v2Config = DexV2Constant.CONFIG[this.networkId];
+    const utxos = await this.api.addressesUtxosAssetAll(
+      v2Config.poolScriptHashBech32,
+      v2Config.poolAuthenAsset
+    );
+
+    const pools: PoolV2.State[] = [];
+    const errors: unknown[] = [];
+    for (const utxo of utxos) {
+      try {
+        if (!utxo.inline_datum) {
+          throw new Error(`Cannot find datum of Pool V2, tx: ${utxo.tx_hash}`);
+        }
+        const pool = new PoolV2.State(
+          this.networkId,
+          utxo.address,
+          { txHash: utxo.tx_hash, index: utxo.output_index },
+          utxo.amount,
+          utxo.inline_datum
+        );
+        pools.push(pool);
+      } catch (err) {
+        errors.push(err);
+      }
+    }
+    return {
+      pools: pools,
+      errors: errors,
+    };
+  }
+
+  public async getV2Pools({
+    page,
+    count = 100,
+    order = "asc",
+  }: GetPoolsParams): Promise<{
+    pools: PoolV2.State[];
+    errors: unknown[];
+  }> {
+    const v2Config = DexV2Constant.CONFIG[this.networkId];
+    const utxos = await this.api.addressesUtxosAsset(
+      v2Config.poolScriptHashBech32,
+      v2Config.poolAuthenAsset,
+      {
+        count,
+        order,
+        page,
+      }
+    );
+
+    const pools: PoolV2.State[] = [];
+    const errors: unknown[] = [];
+    for (const utxo of utxos) {
+      try {
+        if (!utxo.inline_datum) {
+          throw new Error(`Cannot find datum of Pool V2, tx: ${utxo.tx_hash}`);
+        }
+        const pool = new PoolV2.State(
+          this.networkId,
+          utxo.address,
+          { txHash: utxo.tx_hash, index: utxo.output_index },
+          utxo.amount,
+          utxo.inline_datum
+        );
+        pools.push(pool);
+      } catch (err) {
+        errors.push(err);
+      }
+    }
+    return {
+      pools: pools,
+      errors: errors,
+    };
+  }
+
+  public async getV2PoolByPair(
+    assetA: Asset,
+    assetB: Asset
+  ): Promise<PoolV2.State | null> {
+    const [normalizedAssetA, normalizedAssetB] = normalizeAssets(
+      Asset.toString(assetA),
+      Asset.toString(assetB)
+    );
+    const { pools: allPools } = await this.getAllV2Pools();
+    return (
+      allPools.find(
+        (pool) =>
+          pool.assetA === normalizedAssetA && pool.assetB === normalizedAssetB
+      ) ?? null
+    );
+  }
+
+  public async getAllStablePools(): Promise<{
+    pools: StablePool.State[];
+    errors: unknown[];
+  }> {
+    const poolAddresses = StableswapConstant.CONFIG[this.networkId].map(
+      (cfg) => cfg.poolAddress
+    );
+    const pools: StablePool.State[] = [];
+    const errors: unknown[] = [];
+    for (const poolAddr of poolAddresses) {
+      const utxos = await this.api.addressesUtxosAll(poolAddr);
+      try {
+        for (const utxo of utxos) {
+          let datum: string;
+          if (utxo.inline_datum) {
+            datum = utxo.inline_datum;
+          } else if (utxo.data_hash) {
+            datum = await this.getDatumByDatumHash(utxo.data_hash);
+          } else {
+            throw new Error("Cannot find datum of Stable Pool");
+          }
+          const pool = new StablePool.State(
+            this.networkId,
+            utxo.address,
+            { txHash: utxo.tx_hash, index: utxo.output_index },
+            utxo.amount,
+            datum
+          );
+          pools.push(pool);
+        }
+      } catch (err) {
+        errors.push(err);
+      }
+    }
+
+    return {
+      pools: pools,
+      errors: errors,
+    };
+  }
+
+  public async getStablePoolByNFT(
+    nft: Asset
+  ): Promise<StablePool.State | null> {
+    const poolAddress = StableswapConstant.CONFIG[this.networkId].find(
+      (cfg) => cfg.nftAsset === Asset.toString(nft)
+    )?.poolAddress;
+    if (!poolAddress) {
+      throw new Error(
+        `Cannot find Stable Pool having NFT ${Asset.toString(nft)}`
+      );
+    }
+    const utxos = await this.api.addressesUtxosAssetAll(
+      poolAddress,
+      Asset.toString(nft)
+    );
+    for (const utxo of utxos) {
+      let datum: string;
+      if (utxo.inline_datum) {
+        datum = utxo.inline_datum;
+      } else if (utxo.data_hash) {
+        datum = await this.getDatumByDatumHash(utxo.data_hash);
+      } else {
+        throw new Error("Cannot find datum of Stable Pool");
+      }
+      const pool = new StablePool.State(
+        this.networkId,
+        utxo.address,
+        { txHash: utxo.tx_hash, index: utxo.output_index },
+        utxo.amount,
+        datum
+      );
+      return pool;
+    }
+
+    return null;
+  }
 }
diff --git a/src/constants.ts b/src/constants.ts
deleted file mode 100644
index b241032..0000000
--- a/src/constants.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-import { Address, Script } from "lucid-cardano";
-
-import { NetworkId } from "./types/network";
-
-export const ORDER_BASE_ADDRESS: Record<number, Address> = {
-  [NetworkId.TESTNET]:
-    "addr_test1zzn9efv2f6w82hagxqtn62ju4m293tqvw0uhmdl64ch8uwurajt8r8wqtygrfduwgukk73m5gcnplmztc5tl5ngy0upq932hcy",
-  [NetworkId.MAINNET]:
-    "addr1zxn9efv2f6w82hagxqtn62ju4m293tqvw0uhmdl64ch8uw6j2c79gy9l76sdg0xwhd7r0c0kna0tycz4y5s6mlenh8pq6s3z70",
-};
-
-export const POOL_SCRIPT_HASH =
-  "script1uychk9f04tqngfhx4qlqdlug5ntzen3uzc62kzj7cyesjk0d9me";
-
-export const FACTORY_POLICY_ID =
-  "13aa2accf2e1561723aa26871e071fdf32c867cff7e7d50ad470d62f";
-export const FACTORY_ASSET_NAME = "4d494e53574150";
-export const LP_POLICY_ID =
-  "e4214b7cce62ac6fbba385d164df48e157eae5863521b4b67ca71d86";
-export const POOL_NFT_POLICY_ID =
-  "0be55d262b29f564998ff81efe21bdc0022621c12f15af08d0f2ddb1";
-export const ORDER_SCRIPT: Script = {
-  type: "PlutusV1",
-  script:
-    "59014f59014c01000032323232323232322223232325333009300e30070021323233533300b3370e9000180480109118011bae30100031225001232533300d3300e22533301300114a02a66601e66ebcc04800400c5288980118070009bac3010300c300c300c300c300c300c300c007149858dd48008b18060009baa300c300b3754601860166ea80184ccccc0288894ccc04000440084c8c94ccc038cd4ccc038c04cc030008488c008dd718098018912800919b8f0014891ce1317b152faac13426e6a83e06ff88a4d62cce3c1634ab0a5ec133090014a0266008444a00226600a446004602600a601a00626600a008601a006601e0026ea8c03cc038dd5180798071baa300f300b300e3754601e00244a0026eb0c03000c92616300a001375400660106ea8c024c020dd5000aab9d5744ae688c8c0088cc0080080048c0088cc00800800555cf2ba15573e6e1d200201",
-};
-
-export const BATCHER_FEE_REDUCTION_SUPPORTED_ASSET: Record<
-  number,
-  [string, string]
-> = {
-  [NetworkId.MAINNET]: [
-    "29d222ce763455e3d7a09a665ce554f00ac89d2e99a1a83d267170c64d494e", // MIN
-    "e4214b7cce62ac6fbba385d164df48e157eae5863521b4b67ca71d866aa2153e1ae896a95539c9d62f76cedcdabdcdf144e564b8955f609d660cf6a2", // ADA-MIN LP
-  ],
-  [NetworkId.TESTNET]: [
-    "e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed724d494e", // MIN
-    "e4214b7cce62ac6fbba385d164df48e157eae5863521b4b67ca71d863bb0079303c57812462dec9de8fb867cef8fd3768de7f12c77f6f0dd80381d0d", // ADA-MIN LP
-  ],
-};
-
-export enum MetadataMessage {
-  DEPOSIT_ORDER = "SDK Minswap: Deposit Order",
-  CANCEL_ORDER = "SDK Minswap: Cancel Order",
-  ZAP_IN_ORDER = "SDK Minswap: Zap Order",
-  SWAP_EXACT_IN_ORDER = "SDK Minswap: Swap Exact In Order",
-  SWAP_EXACT_IN_LIMIT_ORDER = "SDK Minswap: Swap Exact In Limit Order",
-  SWAP_EXACT_OUT_ORDER = "SDK Minswap: Swap Exact Out Order",
-  WITHDRAW_ORDER = "SDK Minswap: Withdraw Order",
-}
-
-export const FIXED_DEPOSIT_ADA = 2_000_000n;
diff --git a/src/dex.ts b/src/dex.ts
index f1831df..2ffe33b 100644
--- a/src/dex.ts
+++ b/src/dex.ts
@@ -12,16 +12,15 @@ import {
 } from "lucid-cardano";
 
 import { getBatcherFee } from "./batcher-fee-reduction/configs.internal";
+import { Asset } from "./types/asset";
 import {
   BATCHER_FEE_REDUCTION_SUPPORTED_ASSET,
+  DexV1Constant,
   FIXED_DEPOSIT_ADA,
   MetadataMessage,
-  ORDER_BASE_ADDRESS,
-  ORDER_SCRIPT,
-} from "./constants";
-import { Asset } from "./types/asset";
+} from "./types/constants";
 import { NetworkId } from "./types/network";
-import { OrderDatum, OrderRedeemer, OrderStepType } from "./types/order";
+import { OrderV1 } from "./types/order";
 
 /**
  * Common options for build Minswap transaction
@@ -156,12 +155,12 @@ export class Dex {
     } else {
       orderAssets["lovelace"] = FIXED_DEPOSIT_ADA + batcherFee;
     }
-    const datum: OrderDatum = {
+    const datum: OrderV1.Datum = {
       sender: sender,
       receiver: sender,
       receiverDatumHash: undefined,
       step: {
-        type: OrderStepType.SWAP_EXACT_IN,
+        type: OrderV1.StepType.SWAP_EXACT_IN,
         desiredAsset: assetOut,
         minimumReceived: minimumAmountOut,
       },
@@ -171,8 +170,8 @@ export class Dex {
     const tx = this.lucid
       .newTx()
       .payToContract(
-        ORDER_BASE_ADDRESS[this.networkId],
-        Data.to(OrderDatum.toPlutusData(datum)),
+        DexV1Constant.ORDER_BASE_ADDRESS[this.networkId],
+        Data.to(OrderV1.Datum.toPlutusData(datum)),
         orderAssets
       )
       .payToAddress(sender, reductionAssets)
@@ -212,12 +211,12 @@ export class Dex {
     } else {
       orderAssets["lovelace"] = FIXED_DEPOSIT_ADA + batcherFee;
     }
-    const datum: OrderDatum = {
+    const datum: OrderV1.Datum = {
       sender: sender,
       receiver: sender,
       receiverDatumHash: undefined,
       step: {
-        type: OrderStepType.SWAP_EXACT_OUT,
+        type: OrderV1.StepType.SWAP_EXACT_OUT,
         desiredAsset: assetOut,
         expectedReceived: expectedAmountOut,
       },
@@ -228,8 +227,8 @@ export class Dex {
     return await this.lucid
       .newTx()
       .payToContract(
-        ORDER_BASE_ADDRESS[this.networkId],
-        Data.to(OrderDatum.toPlutusData(datum)),
+        DexV1Constant.ORDER_BASE_ADDRESS[this.networkId],
+        Data.to(OrderV1.Datum.toPlutusData(datum)),
         orderAssets
       )
       .payToAddress(sender, reductionAssets)
@@ -262,12 +261,12 @@ export class Dex {
     } else {
       orderAssets["lovelace"] = FIXED_DEPOSIT_ADA + batcherFee;
     }
-    const datum: OrderDatum = {
+    const datum: OrderV1.Datum = {
       sender: sender,
       receiver: sender,
       receiverDatumHash: undefined,
       step: {
-        type: OrderStepType.WITHDRAW,
+        type: OrderV1.StepType.WITHDRAW,
         minimumAssetA: minimumAssetAReceived,
         minimumAssetB: minimumAssetBReceived,
       },
@@ -277,8 +276,8 @@ export class Dex {
     return await this.lucid
       .newTx()
       .payToContract(
-        ORDER_BASE_ADDRESS[this.networkId],
-        Data.to(OrderDatum.toPlutusData(datum)),
+        DexV1Constant.ORDER_BASE_ADDRESS[this.networkId],
+        Data.to(OrderV1.Datum.toPlutusData(datum)),
         orderAssets
       )
       .payToAddress(sender, reductionAssets)
@@ -308,12 +307,12 @@ export class Dex {
     } else {
       orderAssets["lovelace"] = FIXED_DEPOSIT_ADA + batcherFee;
     }
-    const datum: OrderDatum = {
+    const datum: OrderV1.Datum = {
       sender: sender,
       receiver: sender,
       receiverDatumHash: undefined,
       step: {
-        type: OrderStepType.ZAP_IN,
+        type: OrderV1.StepType.ZAP_IN,
         desiredAsset: assetOut,
         minimumLP: minimumLPReceived,
       },
@@ -324,8 +323,8 @@ export class Dex {
     return await this.lucid
       .newTx()
       .payToContract(
-        ORDER_BASE_ADDRESS[this.networkId],
-        Data.to(OrderDatum.toPlutusData(datum)),
+        DexV1Constant.ORDER_BASE_ADDRESS[this.networkId],
+        Data.to(OrderV1.Datum.toPlutusData(datum)),
         orderAssets
       )
       .payToAddress(sender, reductionAssets)
@@ -359,12 +358,12 @@ export class Dex {
     } else {
       orderAssets["lovelace"] = FIXED_DEPOSIT_ADA + batcherFee;
     }
-    const datum: OrderDatum = {
+    const datum: OrderV1.Datum = {
       sender: sender,
       receiver: sender,
       receiverDatumHash: undefined,
       step: {
-        type: OrderStepType.DEPOSIT,
+        type: OrderV1.StepType.DEPOSIT,
         minimumLP: minimumLPReceived,
       },
       batcherFee: batcherFee,
@@ -373,8 +372,8 @@ export class Dex {
     return await this.lucid
       .newTx()
       .payToContract(
-        ORDER_BASE_ADDRESS[this.networkId],
-        Data.to(OrderDatum.toPlutusData(datum)),
+        DexV1Constant.ORDER_BASE_ADDRESS[this.networkId],
+        Data.to(OrderV1.Datum.toPlutusData(datum)),
         orderAssets
       )
       .payToAddress(sender, reductionAssets)
@@ -387,13 +386,13 @@ export class Dex {
     options: BuildCancelOrderOptions
   ): Promise<TxComplete> {
     const { orderUtxo } = options;
-    const redeemer = Data.to(new Constr(OrderRedeemer.CANCEL_ORDER, []));
+    const redeemer = Data.to(new Constr(OrderV1.Redeemer.CANCEL_ORDER, []));
     const rawDatum = orderUtxo.datum;
     invariant(
       rawDatum,
       `Cancel Order requires Order UTxOs along with its CBOR Datum`
     );
-    const orderDatum = OrderDatum.fromPlutusData(
+    const orderDatum = OrderV1.Datum.fromPlutusData(
       this.networkId,
       Data.from(rawDatum) as Constr<Data>
     );
@@ -401,7 +400,7 @@ export class Dex {
       .newTx()
       .collectFrom([orderUtxo], redeemer)
       .addSigner(orderDatum.sender)
-      .attachSpendingValidator(<SpendingValidator>ORDER_SCRIPT)
+      .attachSpendingValidator(<SpendingValidator>DexV1Constant.ORDER_SCRIPT)
       .attachMetadata(674, { msg: [MetadataMessage.CANCEL_ORDER] })
       .complete();
   }
diff --git a/src/index.ts b/src/index.ts
index ef6acb2..a273057 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,8 +1,8 @@
 export * from "./adapter";
 export * from "./calculate";
-export * from "./constants";
 export * from "./dex";
 export * from "./types/asset";
+export * from "./types/constants";
 export * from "./types/network";
 export * from "./types/order";
 export * from "./types/pool";
diff --git a/src/types/constants.ts b/src/types/constants.ts
new file mode 100644
index 0000000..d0fc570
--- /dev/null
+++ b/src/types/constants.ts
@@ -0,0 +1,427 @@
+import { Address, OutRef,Script } from "lucid-cardano";
+
+import { NetworkId } from "./network";
+
+export namespace DexV1Constant {
+  export const ORDER_BASE_ADDRESS: Record<number, Address> = {
+    [NetworkId.TESTNET]:
+      "addr_test1zzn9efv2f6w82hagxqtn62ju4m293tqvw0uhmdl64ch8uwurajt8r8wqtygrfduwgukk73m5gcnplmztc5tl5ngy0upq932hcy",
+    [NetworkId.MAINNET]:
+      "addr1zxn9efv2f6w82hagxqtn62ju4m293tqvw0uhmdl64ch8uw6j2c79gy9l76sdg0xwhd7r0c0kna0tycz4y5s6mlenh8pq6s3z70",
+  };
+
+  export const POOL_SCRIPT_HASH =
+    "script1uychk9f04tqngfhx4qlqdlug5ntzen3uzc62kzj7cyesjk0d9me";
+
+  export const FACTORY_POLICY_ID =
+    "13aa2accf2e1561723aa26871e071fdf32c867cff7e7d50ad470d62f";
+  export const FACTORY_ASSET_NAME = "4d494e53574150";
+  export const LP_POLICY_ID =
+    "e4214b7cce62ac6fbba385d164df48e157eae5863521b4b67ca71d86";
+  export const POOL_NFT_POLICY_ID =
+    "0be55d262b29f564998ff81efe21bdc0022621c12f15af08d0f2ddb1";
+  export const ORDER_SCRIPT: Script = {
+    type: "PlutusV1",
+    script:
+      "59014f59014c01000032323232323232322223232325333009300e30070021323233533300b3370e9000180480109118011bae30100031225001232533300d3300e22533301300114a02a66601e66ebcc04800400c5288980118070009bac3010300c300c300c300c300c300c300c007149858dd48008b18060009baa300c300b3754601860166ea80184ccccc0288894ccc04000440084c8c94ccc038cd4ccc038c04cc030008488c008dd718098018912800919b8f0014891ce1317b152faac13426e6a83e06ff88a4d62cce3c1634ab0a5ec133090014a0266008444a00226600a446004602600a601a00626600a008601a006601e0026ea8c03cc038dd5180798071baa300f300b300e3754601e00244a0026eb0c03000c92616300a001375400660106ea8c024c020dd5000aab9d5744ae688c8c0088cc0080080048c0088cc00800800555cf2ba15573e6e1d200201",
+  };
+}
+
+export namespace StableswapConstant {
+  export type Config = {
+    orderAddress: Address;
+    poolAddress: Address;
+    nftAsset: string;
+    lpAsset: string;
+    assets: string[];
+    multiples: bigint[];
+    fee: bigint;
+    adminFee: bigint;
+    feeDenominator: bigint;
+  }
+
+  export type DeployedScripts = {
+    order: OutRef,
+    pool: OutRef,
+    lp: OutRef,
+    poolBatching: OutRef
+  }
+
+  export const CONFIG: Record<NetworkId, Config[]> = {
+    [NetworkId.TESTNET]: [
+      {
+        orderAddress: "addr_test1zq8spknltt6yyz2505rhc5lqw89afc4anhu4u0347n5dz8urajt8r8wqtygrfduwgukk73m5gcnplmztc5tl5ngy0upqa63kst",
+        poolAddress: "addr_test1zr3hs60rn9x49ahuduuzmnlhnema0jsl4d3ujrf3cmurhmvrajt8r8wqtygrfduwgukk73m5gcnplmztc5tl5ngy0upqcgz9yc",
+        nftAsset: "06fe1ba957728130154154d5e5b25a7b533ebe6c4516356c0aa69355646a65642d697573642d76312e342d6c70",
+        lpAsset: "d16339238c9e1fb4d034b6a48facb2f97794a9cdb7bc049dd7c49f54646a65642d697573642d76312e342d6c70",
+        assets: [
+          "e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed7274444a4544",
+          "e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed727469555344"
+        ],
+        multiples: [1n, 1n],
+        fee: 1000000n,
+        adminFee: 5000000000n,
+        feeDenominator: 10000000000n
+      },
+      {
+        orderAddress: "addr_test1zp3mf7r63u8km2d69kh6v2axlvl04yunmmj67vprljuht4urajt8r8wqtygrfduwgukk73m5gcnplmztc5tl5ngy0upqhelj6n",
+        poolAddress: "addr_test1zzc8ar93kgntz3lv95uauhe29kj4yj84mxhg5v9dqj4k7p5rajt8r8wqtygrfduwgukk73m5gcnplmztc5tl5ngy0upqujv25l",
+        nftAsset: "06fe1ba957728130154154d5e5b25a7b533ebe6c4516356c0aa69355757364632d757364742d76312e342d6c70",
+        lpAsset: "8db03e0cc042a5f82434123a0509f590210996f1c7410c94f913ac48757364632d757364742d76312e342d6c70",
+        assets: [
+          "e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed727455534443",
+          "e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed727455534454"
+        ],
+        multiples: [1n, 1n],
+        fee: 1000000n,
+        adminFee: 5000000000n,
+        feeDenominator: 10000000000n
+      },
+      {
+        orderAddress: "addr_test1zqpmw0kkgm6fp9x0asq5vwuaccweeqdv3edhwckqr2gnvzurajt8r8wqtygrfduwgukk73m5gcnplmztc5tl5ngy0upq9z8vxj",
+        poolAddress: "addr_test1zqh2uv0wvrtt579e92q35ktkzcj3lj3nzdm3xjpsdack3q5rajt8r8wqtygrfduwgukk73m5gcnplmztc5tl5ngy0upqud27a8",
+        nftAsset: "06fe1ba957728130154154d5e5b25a7b533ebe6c4516356c0aa69355646a65642d697573642d6461692d76312e342d6c70",
+        lpAsset: "492fd7252d5914c9f5acb7eeb6b905b3a65b9a952c2300de34eb86c5646a65642d697573642d6461692d76312e342d6c70",
+        assets: [
+          "e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed7274444a4544",
+          "e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed727469555344",
+          "e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed7274444149"
+        ],
+        multiples: [1n, 1n, 1n],
+        fee: 1000000n,
+        adminFee: 5000000000n,
+        feeDenominator: 10000000000n
+      }
+    ],
+    [NetworkId.MAINNET]: [
+      {
+        orderAddress: "addr1w9xy6edqv9hkptwzewns75ehq53nk8t73je7np5vmj3emps698n9g",
+        poolAddress: "addr1wy7kkcpuf39tusnnyga5t2zcul65dwx9yqzg7sep3cjscesx2q5m5",
+        nftAsset: "5d4b6afd3344adcf37ccef5558bb87f522874578c32f17160512e398444a45442d695553442d534c50",
+        lpAsset: "2c07095028169d7ab4376611abef750623c8f955597a38cd15248640444a45442d695553442d534c50",
+        assets: [
+          "8db269c3ec630e06ae29f74bc39edd1f87c819f1056206e879a1cd61446a65644d6963726f555344",
+          "f66d78b4a3cb3d37afa0ec36461e51ecbde00f26c8f0a68f94b6988069555344"
+        ],
+        multiples: [1n, 1n],
+        fee: 1000000n,
+        adminFee: 5000000000n,
+        feeDenominator: 10000000000n,
+      },
+      {
+        orderAddress: "addr1w93d8cuht3hvqt2qqfjqgyek3gk5d6ss2j93e5sh505m0ng8cmze2",
+        poolAddress: "addr1wx8d45xlfrlxd7tctve8xgdtk59j849n00zz2pgyvv47t8sxa6t53",
+        nftAsset: "d97fa91daaf63559a253970365fb219dc4364c028e5fe0606cdbfff9555344432d444a45442d534c50",
+        lpAsset: "ac49e0969d76ed5aa9e9861a77be65f4fc29e9a979dc4c37a99eb8f4555344432d444a45442d534c50",
+        assets: [
+          "25c5de5f5b286073c593edfd77b48abc7a48e5a4f3d4cd9d428ff93555534443",
+          "8db269c3ec630e06ae29f74bc39edd1f87c819f1056206e879a1cd61446a65644d6963726f555344"
+        ],
+        multiples: [1n, 100n],
+        fee: 1000000n,
+        adminFee: 5000000000n,
+        feeDenominator: 10000000000n
+      },
+      {
+        orderAddress: "addr1wxtv9k2lcum5pmcc4wu44a5tufulszahz84knff87wcawycez9lug",
+        poolAddress: "addr1w9520fyp6g3pjwd0ymfy4v2xka54ek6ulv4h8vce54zfyfcm2m0sm",
+        nftAsset: "96402c6f5e7a04f16b4d6f500ab039ff5eac5d0226d4f88bf5523ce85553444d2d695553442d534c50",
+        lpAsset: "31f92531ac9f1af3079701fab7c66ce997eb07988277ee5b9d6403015553444d2d695553442d534c50",
+        assets: [
+          "c48cbb3d5e57ed56e276bc45f99ab39abe94e6cd7ac39fb402da47ad0014df105553444d",
+          "f66d78b4a3cb3d37afa0ec36461e51ecbde00f26c8f0a68f94b6988069555344"
+        ],
+        multiples: [1n, 1n],
+        fee: 1000000n,
+        adminFee: 5000000000n,
+        feeDenominator: 10000000000n
+      },
+      {
+        orderAddress: "addr1wxr9ppdymqgw6g0hvaaa7wc6j0smwh730ujx6lczgdynehsguav8d",
+        poolAddress: "addr1wxxdvtj6y4fut4tmu796qpvy2xujtd836yg69ahat3e6jjcelrf94",
+        nftAsset: "07b0869ed7488657e24ac9b27b3f0fb4f76757f444197b2a38a15c3c444a45442d5553444d2d534c50",
+        lpAsset: "5b042cf53c0b2ce4f30a9e743b4871ad8c6dcdf1d845133395f55a8e444a45442d5553444d2d534c50",
+        assets: [
+          "8db269c3ec630e06ae29f74bc39edd1f87c819f1056206e879a1cd61446a65644d6963726f555344",
+          "c48cbb3d5e57ed56e276bc45f99ab39abe94e6cd7ac39fb402da47ad0014df105553444d"
+        ],
+        multiples: [1n, 1n],
+        fee: 1000000n,
+        adminFee: 5000000000n,
+        feeDenominator: 10000000000n
+      }
+    ]
+  }
+
+  export const DEPLOYED_SCRIPTS: Record<NetworkId, Record<string, DeployedScripts>> = {
+    [NetworkId.TESTNET]: {
+      "06fe1ba957728130154154d5e5b25a7b533ebe6c4516356c0aa69355646a65642d697573642d76312e342d6c70": {
+        order: {
+          "txHash": "527e421bc3eb8b9e5ec0a9ad214bb9b76148f57b9a5a8cbd83a51264f943e91d",
+          "outputIndex": 0,
+        },
+        pool: {
+          "txHash": "527e421bc3eb8b9e5ec0a9ad214bb9b76148f57b9a5a8cbd83a51264f943e91d",
+          "outputIndex": 1,
+        },
+        lp: {
+          "txHash": "527e421bc3eb8b9e5ec0a9ad214bb9b76148f57b9a5a8cbd83a51264f943e91d",
+          "outputIndex": 2,
+        },
+        poolBatching: {
+          "txHash": "527e421bc3eb8b9e5ec0a9ad214bb9b76148f57b9a5a8cbd83a51264f943e91d",
+          "outputIndex": 3,
+        }
+      },
+      "06fe1ba957728130154154d5e5b25a7b533ebe6c4516356c0aa69355757364632d757364742d76312e342d6c70": {
+        order: {
+          "txHash": "cf699550642c8ffc1673d1e5d56d8562ca7c7f5c0b513a8428c3f52cdcc8fdb7",
+          "outputIndex": 0,
+        },
+        pool: {
+          "txHash": "cf699550642c8ffc1673d1e5d56d8562ca7c7f5c0b513a8428c3f52cdcc8fdb7",
+          "outputIndex": 1,
+        },
+        lp: {
+          "txHash": "cf699550642c8ffc1673d1e5d56d8562ca7c7f5c0b513a8428c3f52cdcc8fdb7",
+          "outputIndex": 2,
+        },
+        poolBatching: {
+          "txHash": "cf699550642c8ffc1673d1e5d56d8562ca7c7f5c0b513a8428c3f52cdcc8fdb7",
+          "outputIndex": 3,
+        }
+      },
+      "06fe1ba957728130154154d5e5b25a7b533ebe6c4516356c0aa69355646a65642d697573642d6461692d76312e342d6c70": {
+        order: {
+          "txHash": "a8ab602259654697c85e2f61752d34cdb631f314eaeded0676fee6f6be70afe7",
+          "outputIndex": 0,
+        },
+        pool: {
+          "txHash": "a8ab602259654697c85e2f61752d34cdb631f314eaeded0676fee6f6be70afe7",
+          "outputIndex": 1,
+        },
+        lp: {
+          "txHash": "a8ab602259654697c85e2f61752d34cdb631f314eaeded0676fee6f6be70afe7",
+          "outputIndex": 2,
+        },
+        poolBatching: {
+          "txHash": "a8ab602259654697c85e2f61752d34cdb631f314eaeded0676fee6f6be70afe7",
+          "outputIndex": 3,
+        }
+      }
+    },
+    [NetworkId.MAINNET]: {
+      "5d4b6afd3344adcf37ccef5558bb87f522874578c32f17160512e398444a45442d695553442d534c50": {
+        order: {
+          "txHash": "20227174ec2f7853a71a02c435d063b3bf63851d4e0ad9a0c09250a087a6577e",
+          "outputIndex": 0,
+        },
+        pool: {
+          "txHash": "20227174ec2f7853a71a02c435d063b3bf63851d4e0ad9a0c09250a087a6577e",
+          "outputIndex": 1,
+        },
+        lp: {
+          "txHash": "20227174ec2f7853a71a02c435d063b3bf63851d4e0ad9a0c09250a087a6577e",
+          "outputIndex": 2,
+        },
+        poolBatching: {
+          "txHash": "20227174ec2f7853a71a02c435d063b3bf63851d4e0ad9a0c09250a087a6577e",
+          "outputIndex": 3,
+        }
+      },
+      "d97fa91daaf63559a253970365fb219dc4364c028e5fe0606cdbfff9555344432d444a45442d534c50": {
+        order: {
+          "txHash": "8b880e77a726e76e5dd585cda2c4c2ac93f1cfccc06910f00550fb820ae1fc54",
+          "outputIndex": 0,
+        },
+        pool: {
+          "txHash": "8b880e77a726e76e5dd585cda2c4c2ac93f1cfccc06910f00550fb820ae1fc54",
+          "outputIndex": 1,
+        },
+        lp: {
+          "txHash": "8b880e77a726e76e5dd585cda2c4c2ac93f1cfccc06910f00550fb820ae1fc54",
+          "outputIndex": 2,
+        },
+        poolBatching: {
+          "txHash": "8b880e77a726e76e5dd585cda2c4c2ac93f1cfccc06910f00550fb820ae1fc54",
+          "outputIndex": 3,
+        }
+      },
+      "96402c6f5e7a04f16b4d6f500ab039ff5eac5d0226d4f88bf5523ce85553444d2d695553442d534c50": {
+        order: {
+          "txHash": "48019a931af442e1eedab6c5b52b3069cf6eadb2483a2131f517e62fddfd5662",
+          "outputIndex": 0,
+        },
+        pool: {
+          "txHash": "48019a931af442e1eedab6c5b52b3069cf6eadb2483a2131f517e62fddfd5662",
+          "outputIndex": 1,
+        },
+        lp: {
+          "txHash": "48019a931af442e1eedab6c5b52b3069cf6eadb2483a2131f517e62fddfd5662",
+          "outputIndex": 2,
+        },
+        poolBatching: {
+          "txHash": "48019a931af442e1eedab6c5b52b3069cf6eadb2483a2131f517e62fddfd5662",
+          "outputIndex": 3,
+        }
+      },
+      "07b0869ed7488657e24ac9b27b3f0fb4f76757f444197b2a38a15c3c444a45442d5553444d2d534c50": {
+        order: {
+          "txHash": "dddccee9cd58cbf712f2ff2c49ea20537db681a333c701106aa13cd57aee3873",
+          "outputIndex": 0,
+        },
+        pool: {
+          "txHash": "dddccee9cd58cbf712f2ff2c49ea20537db681a333c701106aa13cd57aee3873",
+          "outputIndex": 1,
+        },
+        lp: {
+          "txHash": "dddccee9cd58cbf712f2ff2c49ea20537db681a333c701106aa13cd57aee3873",
+          "outputIndex": 2,
+        },
+        poolBatching: {
+          "txHash": "dddccee9cd58cbf712f2ff2c49ea20537db681a333c701106aa13cd57aee3873",
+          "outputIndex": 3,
+        }
+      }
+    },
+  }
+}
+
+export namespace DexV2Constant {
+  export type Config = {
+    factoryAsset: string;
+    poolAuthenAsset: string;
+    globalSettingAsset: string;
+    lpPolicyId: string;
+    globalSettingScriptHash: string;
+    orderScriptHash: string;
+    poolScriptHash: string;
+    poolScriptHashBech32: string;
+    poolCreationAddress: Address;
+    factoryScriptHash: string;
+    expiredOrderCancelAddress: string;
+    poolBatchingAddress: string;
+  }
+
+  export type DeployedScripts = {
+    order: OutRef,
+    pool: OutRef,
+    factory: OutRef,
+    authen: OutRef,
+    poolBatching: OutRef,
+    expiredOrderCancellation: OutRef
+  }
+
+  export const CONFIG: Record<NetworkId, Config> = {
+    [NetworkId.TESTNET]: {
+      factoryAsset: "d6aae2059baee188f74917493cf7637e679cd219bdfbbf4dcbeb1d0b4d5346",
+      poolAuthenAsset: "d6aae2059baee188f74917493cf7637e679cd219bdfbbf4dcbeb1d0b4d5350",
+      globalSettingAsset: "d6aae2059baee188f74917493cf7637e679cd219bdfbbf4dcbeb1d0b4d534753",
+      lpPolicyId: "d6aae2059baee188f74917493cf7637e679cd219bdfbbf4dcbeb1d0b",
+      globalSettingScriptHash: "d6aae2059baee188f74917493cf7637e679cd219bdfbbf4dcbeb1d0b",
+      orderScriptHash: "da9525463841173ad1230b1d5a1b5d0a3116bbdeb4412327148a1b7a",
+      poolScriptHash: "d6ba9b7509eac866288ff5072d2a18205ac56f744bc82dcd808cb8fe",
+      poolScriptHashBech32: "script166afkagfatyxv2y075rj62scypdv2mm5f0yzmnvq3ju0uqqmszv",
+      poolCreationAddress: "addr_test1zrtt4xm4p84vse3g3l6swtf2rqs943t0w39ustwdszxt3l5rajt8r8wqtygrfduwgukk73m5gcnplmztc5tl5ngy0upqhns793",
+      factoryScriptHash: "6e23fe172b5b50e2ad59aded9ee8d488f74c7f4686f91b032220adad",
+      expiredOrderCancelAddress: "stake_test17rytpnrpxax5p8leepgjx9cq8ecedgly6jz4xwvvv4kvzfqz6sgpf",
+      poolBatchingAddress: "stake_test17rann6nth9675m0y5tz32u3rfhzcfjymanxqnfyexsufu5glcajhf",
+    },
+    [NetworkId.MAINNET]: {
+      factoryAsset: "<not ready yet>",
+      poolAuthenAsset: "<not ready yet>",
+      globalSettingAsset: "<not ready yet>",
+      lpPolicyId: "<not ready yet>",
+      globalSettingScriptHash: "<not ready yet>",
+      orderScriptHash: "<not ready yet>",
+      poolScriptHash: "<not ready yet>",
+      poolScriptHashBech32: "<not ready yet>",
+      poolCreationAddress: "<not ready yet>",
+      factoryScriptHash: "<not ready yet>",
+      expiredOrderCancelAddress: "<not ready yet>",
+      poolBatchingAddress: "<not ready yet>",
+    }
+  }
+
+  export const DEPLOYED_SCRIPTS: Record<NetworkId, DeployedScripts> = {
+    [NetworkId.TESTNET]: {
+      order: {
+        txHash: "8c98f0530cba144d264fbd2731488af25257d7ce6a0cd1586fc7209363724f03",
+        outputIndex: 0
+      },
+      pool: {
+        txHash: "9f30b1c3948a009ceebda32d0b1d25699674b2eaf8b91ef029a43bfc1073ce28",
+        outputIndex: 0
+      },
+      factory: {
+        txHash: "9741d59656e9ad54f197b0763482eede9a6fa1616c4547797eee6617f92a1396",
+        outputIndex: 0
+      },
+      authen: {
+        txHash: "c429b8ee27e5761ba8714e26e3a5899886cd28d136d43e969d4bc1acf0f72d4a",
+        outputIndex: 0
+      },
+      poolBatching: {
+        txHash: "b0a6c5512735c7a183a167eed035ac75c191d6ff5be9736dfa1f1f02f7ae5dbc",
+        outputIndex: 0,
+      },
+      expiredOrderCancellation: {
+        txHash: "ee718dd86e3cb89e802aa8b2be252fccf6f15263f4a26b5f478c5135c40264c6",
+        outputIndex: 0
+      }
+    },
+    [NetworkId.MAINNET]: {
+      order: {
+        txHash: "<not ready yet>",
+        outputIndex: 0
+      },
+      pool: {
+        txHash: "<not ready yet>",
+        outputIndex: 0
+      },
+      factory: {
+        txHash: "<not ready yet>",
+        outputIndex: 0
+      },
+      authen: {
+        txHash: "<not ready yet>",
+        outputIndex: 0
+      },
+      poolBatching: {
+        txHash: "<not ready yet>",
+        outputIndex: 0,
+      },
+      expiredOrderCancellation: {
+        txHash: "<not ready yet>",
+        outputIndex: 0
+      }
+    }
+  }
+}
+
+
+export const BATCHER_FEE_REDUCTION_SUPPORTED_ASSET: Record<
+  number,
+  [string, string]
+> = {
+  [NetworkId.MAINNET]: [
+    "29d222ce763455e3d7a09a665ce554f00ac89d2e99a1a83d267170c64d494e", // MIN
+    "e4214b7cce62ac6fbba385d164df48e157eae5863521b4b67ca71d866aa2153e1ae896a95539c9d62f76cedcdabdcdf144e564b8955f609d660cf6a2", // ADA-MIN LP
+  ],
+  [NetworkId.TESTNET]: [
+    "e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed724d494e", // MIN
+    "e4214b7cce62ac6fbba385d164df48e157eae5863521b4b67ca71d863bb0079303c57812462dec9de8fb867cef8fd3768de7f12c77f6f0dd80381d0d", // ADA-MIN LP
+  ],
+};
+
+export enum MetadataMessage {
+  DEPOSIT_ORDER = "SDK Minswap: Deposit Order",
+  CANCEL_ORDER = "SDK Minswap: Cancel Order",
+  ZAP_IN_ORDER = "SDK Minswap: Zap Order",
+  SWAP_EXACT_IN_ORDER = "SDK Minswap: Swap Exact In Order",
+  SWAP_EXACT_IN_LIMIT_ORDER = "SDK Minswap: Swap Exact In Limit Order",
+  SWAP_EXACT_OUT_ORDER = "SDK Minswap: Swap Exact Out Order",
+  WITHDRAW_ORDER = "SDK Minswap: Withdraw Order",
+}
+
+export const FIXED_DEPOSIT_ADA = 2_000_000n;
diff --git a/src/types/order.ts b/src/types/order.ts
index 2e63f63..a3e4e0d 100644
--- a/src/types/order.ts
+++ b/src/types/order.ts
@@ -4,249 +4,251 @@ import { AddressPlutusData } from "./address.internal";
 import { Asset } from "./asset";
 import { NetworkId } from "./network";
 
-export enum OrderStepType {
-  SWAP_EXACT_IN = 0,
-  SWAP_EXACT_OUT,
-  DEPOSIT,
-  WITHDRAW,
-  ZAP_IN,
-}
+export namespace OrderV1 {
+  export enum StepType {
+    SWAP_EXACT_IN = 0,
+    SWAP_EXACT_OUT,
+    DEPOSIT,
+    WITHDRAW,
+    ZAP_IN,
+  }
 
-export type SwapExactInStep = {
-  type: OrderStepType.SWAP_EXACT_IN;
-  desiredAsset: Asset;
-  minimumReceived: bigint;
-};
+  export type SwapExactIn = {
+    type: StepType.SWAP_EXACT_IN;
+    desiredAsset: Asset;
+    minimumReceived: bigint;
+  };
 
-export type SwapExactOutStep = {
-  type: OrderStepType.SWAP_EXACT_OUT;
-  desiredAsset: Asset;
-  expectedReceived: bigint;
-};
+  export type SwapExactOut = {
+    type: StepType.SWAP_EXACT_OUT;
+    desiredAsset: Asset;
+    expectedReceived: bigint;
+  };
 
-export type DepositStep = {
-  type: OrderStepType.DEPOSIT;
-  minimumLP: bigint;
-};
+  export type Deposit = {
+    type: StepType.DEPOSIT;
+    minimumLP: bigint;
+  };
 
-export type WithdrawStep = {
-  type: OrderStepType.WITHDRAW;
-  minimumAssetA: bigint;
-  minimumAssetB: bigint;
-};
+  export type Withdraw = {
+    type: StepType.WITHDRAW;
+    minimumAssetA: bigint;
+    minimumAssetB: bigint;
+  };
 
-export type ZapInStep = {
-  type: OrderStepType.ZAP_IN;
-  desiredAsset: Asset;
-  minimumLP: bigint;
-};
+  export type ZapIn = {
+    type: StepType.ZAP_IN;
+    desiredAsset: Asset;
+    minimumLP: bigint;
+  };
 
-export type OrderStep =
-  | SwapExactInStep
-  | SwapExactOutStep
-  | DepositStep
-  | WithdrawStep
-  | ZapInStep;
+  export type Step =
+    | SwapExactIn
+    | SwapExactOut
+    | Deposit
+    | Withdraw
+    | ZapIn;
 
-export type OrderDatum = {
-  sender: Address;
-  receiver: Address;
-  receiverDatumHash?: string;
-  step: OrderStep;
-  batcherFee: bigint;
-  depositADA: bigint;
-};
+  export type Datum = {
+    sender: Address;
+    receiver: Address;
+    receiverDatumHash?: string;
+    step: Step;
+    batcherFee: bigint;
+    depositADA: bigint;
+  };
 
-export namespace OrderDatum {
-  export function toPlutusData(datum: OrderDatum): Constr<Data> {
-    const {
-      sender,
-      receiver,
-      receiverDatumHash,
-      step,
-      batcherFee,
-      depositADA,
-    } = datum;
-    const senderConstr = AddressPlutusData.toPlutusData(sender);
-    const receiverConstr = AddressPlutusData.toPlutusData(receiver);
-    const receiverDatumHashConstr = receiverDatumHash
-      ? new Constr(0, [receiverDatumHash])
-      : new Constr(1, []);
-    let datumConstr: Constr<Data>;
-    switch (step.type) {
-      case OrderStepType.SWAP_EXACT_IN: {
-        datumConstr = new Constr(0, [
-          senderConstr,
-          receiverConstr,
-          receiverDatumHashConstr,
-          new Constr(OrderStepType.SWAP_EXACT_IN, [
-            Asset.toPlutusData(step.desiredAsset),
-            step.minimumReceived,
-          ]),
-          batcherFee,
-          depositADA,
-        ]);
-        break;
-      }
-      case OrderStepType.SWAP_EXACT_OUT: {
-        datumConstr = new Constr(0, [
-          senderConstr,
-          receiverConstr,
-          receiverDatumHashConstr,
-          new Constr(OrderStepType.SWAP_EXACT_OUT, [
-            Asset.toPlutusData(step.desiredAsset),
-            step.expectedReceived,
-          ]),
-          batcherFee,
-          depositADA,
-        ]);
-        break;
-      }
-      case OrderStepType.DEPOSIT: {
-        datumConstr = new Constr(0, [
-          senderConstr,
-          receiverConstr,
-          receiverDatumHashConstr,
-          new Constr(OrderStepType.DEPOSIT, [step.minimumLP]),
-          batcherFee,
-          depositADA,
-        ]);
-        break;
-      }
-      case OrderStepType.WITHDRAW: {
-        datumConstr = new Constr(0, [
-          senderConstr,
-          receiverConstr,
-          receiverDatumHashConstr,
-          new Constr(OrderStepType.WITHDRAW, [
-            step.minimumAssetA,
-            step.minimumAssetB,
-          ]),
-          batcherFee,
-          depositADA,
-        ]);
-        break;
-      }
-      case OrderStepType.ZAP_IN: {
-        datumConstr = new Constr(0, [
-          senderConstr,
-          receiverConstr,
-          receiverDatumHashConstr,
-          new Constr(OrderStepType.ZAP_IN, [
-            Asset.toPlutusData(step.desiredAsset),
-            step.minimumLP,
-          ]),
-          batcherFee,
-          depositADA,
-        ]);
-        break;
+  export namespace Datum {
+    export function toPlutusData(datum: Datum): Constr<Data> {
+      const {
+        sender,
+        receiver,
+        receiverDatumHash,
+        step,
+        batcherFee,
+        depositADA,
+      } = datum;
+      const senderConstr = AddressPlutusData.toPlutusData(sender);
+      const receiverConstr = AddressPlutusData.toPlutusData(receiver);
+      const receiverDatumHashConstr = receiverDatumHash
+        ? new Constr(0, [receiverDatumHash])
+        : new Constr(1, []);
+      let datumConstr: Constr<Data>;
+      switch (step.type) {
+        case StepType.SWAP_EXACT_IN: {
+          datumConstr = new Constr(0, [
+            senderConstr,
+            receiverConstr,
+            receiverDatumHashConstr,
+            new Constr(StepType.SWAP_EXACT_IN, [
+              Asset.toPlutusData(step.desiredAsset),
+              step.minimumReceived,
+            ]),
+            batcherFee,
+            depositADA,
+          ]);
+          break;
+        }
+        case StepType.SWAP_EXACT_OUT: {
+          datumConstr = new Constr(0, [
+            senderConstr,
+            receiverConstr,
+            receiverDatumHashConstr,
+            new Constr(StepType.SWAP_EXACT_OUT, [
+              Asset.toPlutusData(step.desiredAsset),
+              step.expectedReceived,
+            ]),
+            batcherFee,
+            depositADA,
+          ]);
+          break;
+        }
+        case StepType.DEPOSIT: {
+          datumConstr = new Constr(0, [
+            senderConstr,
+            receiverConstr,
+            receiverDatumHashConstr,
+            new Constr(StepType.DEPOSIT, [step.minimumLP]),
+            batcherFee,
+            depositADA,
+          ]);
+          break;
+        }
+        case StepType.WITHDRAW: {
+          datumConstr = new Constr(0, [
+            senderConstr,
+            receiverConstr,
+            receiverDatumHashConstr,
+            new Constr(StepType.WITHDRAW, [
+              step.minimumAssetA,
+              step.minimumAssetB,
+            ]),
+            batcherFee,
+            depositADA,
+          ]);
+          break;
+        }
+        case StepType.ZAP_IN: {
+          datumConstr = new Constr(0, [
+            senderConstr,
+            receiverConstr,
+            receiverDatumHashConstr,
+            new Constr(StepType.ZAP_IN, [
+              Asset.toPlutusData(step.desiredAsset),
+              step.minimumLP,
+            ]),
+            batcherFee,
+            depositADA,
+          ]);
+          break;
+        }
       }
-    }
-
-    return datumConstr;
-  }
 
-  export function fromPlutusData(
-    networkId: NetworkId,
-    data: Constr<Data>
-  ): OrderDatum {
-    if (data.index !== 0) {
-      throw new Error(`Index of Order Datum must be 0, actual: ${data.index}`);
+      return datumConstr;
     }
-    const sender = AddressPlutusData.fromPlutusData(
-      networkId,
-      data.fields[0] as Constr<Data>
-    );
-    const receiver = AddressPlutusData.fromPlutusData(
-      networkId,
-      data.fields[1] as Constr<Data>
-    );
-    let receiverDatumHash: string | undefined = undefined;
-    const maybeReceiverDatumHash = data.fields[2] as Constr<Data>;
-    switch (maybeReceiverDatumHash.index) {
-      case 0: {
-        receiverDatumHash = maybeReceiverDatumHash.fields[0] as string;
-        break;
-      }
-      case 1: {
-        receiverDatumHash = undefined;
-        break;
-      }
-      default: {
-        throw new Error(
-          `Index of Receiver Datum Hash must be 0 or 1, actual: ${maybeReceiverDatumHash.index}`
-        );
-      }
-    }
-    let step: OrderStep;
-    const orderStepConstr = data.fields[3] as Constr<Data>;
-    switch (orderStepConstr.index) {
-      case OrderStepType.SWAP_EXACT_IN: {
-        step = {
-          type: OrderStepType.SWAP_EXACT_IN,
-          desiredAsset: Asset.fromPlutusData(
-            orderStepConstr.fields[0] as Constr<Data>
-          ),
-          minimumReceived: orderStepConstr.fields[1] as bigint,
-        };
-        break;
-      }
-      case OrderStepType.SWAP_EXACT_OUT: {
-        step = {
-          type: OrderStepType.SWAP_EXACT_OUT,
-          desiredAsset: Asset.fromPlutusData(
-            orderStepConstr.fields[0] as Constr<Data>
-          ),
-          expectedReceived: orderStepConstr.fields[1] as bigint,
-        };
-        break;
-      }
-      case OrderStepType.DEPOSIT: {
-        step = {
-          type: OrderStepType.DEPOSIT,
-          minimumLP: orderStepConstr.fields[0] as bigint,
-        };
-        break;
-      }
-      case OrderStepType.WITHDRAW: {
-        step = {
-          type: OrderStepType.WITHDRAW,
-          minimumAssetA: orderStepConstr.fields[0] as bigint,
-          minimumAssetB: orderStepConstr.fields[1] as bigint,
-        };
-        break;
+
+    export function fromPlutusData(
+      networkId: NetworkId,
+      data: Constr<Data>
+    ): Datum {
+      if (data.index !== 0) {
+        throw new Error(`Index of Order Datum must be 0, actual: ${data.index}`);
       }
-      case OrderStepType.ZAP_IN: {
-        step = {
-          type: OrderStepType.ZAP_IN,
-          desiredAsset: Asset.fromPlutusData(
-            orderStepConstr.fields[0] as Constr<Data>
-          ),
-          minimumLP: orderStepConstr.fields[1] as bigint,
-        };
-        break;
+      const sender = AddressPlutusData.fromPlutusData(
+        networkId,
+        data.fields[0] as Constr<Data>
+      );
+      const receiver = AddressPlutusData.fromPlutusData(
+        networkId,
+        data.fields[1] as Constr<Data>
+      );
+      let receiverDatumHash: string | undefined = undefined;
+      const maybeReceiverDatumHash = data.fields[2] as Constr<Data>;
+      switch (maybeReceiverDatumHash.index) {
+        case 0: {
+          receiverDatumHash = maybeReceiverDatumHash.fields[0] as string;
+          break;
+        }
+        case 1: {
+          receiverDatumHash = undefined;
+          break;
+        }
+        default: {
+          throw new Error(
+            `Index of Receiver Datum Hash must be 0 or 1, actual: ${maybeReceiverDatumHash.index}`
+          );
+        }
       }
-      default: {
-        throw new Error(
-          `Index of Order Step must be in 0-4, actual: ${orderStepConstr.index}`
-        );
+      let step: Step;
+      const orderStepConstr = data.fields[3] as Constr<Data>;
+      switch (orderStepConstr.index) {
+        case StepType.SWAP_EXACT_IN: {
+          step = {
+            type: StepType.SWAP_EXACT_IN,
+            desiredAsset: Asset.fromPlutusData(
+              orderStepConstr.fields[0] as Constr<Data>
+            ),
+            minimumReceived: orderStepConstr.fields[1] as bigint,
+          };
+          break;
+        }
+        case StepType.SWAP_EXACT_OUT: {
+          step = {
+            type: StepType.SWAP_EXACT_OUT,
+            desiredAsset: Asset.fromPlutusData(
+              orderStepConstr.fields[0] as Constr<Data>
+            ),
+            expectedReceived: orderStepConstr.fields[1] as bigint,
+          };
+          break;
+        }
+        case StepType.DEPOSIT: {
+          step = {
+            type: StepType.DEPOSIT,
+            minimumLP: orderStepConstr.fields[0] as bigint,
+          };
+          break;
+        }
+        case StepType.WITHDRAW: {
+          step = {
+            type: StepType.WITHDRAW,
+            minimumAssetA: orderStepConstr.fields[0] as bigint,
+            minimumAssetB: orderStepConstr.fields[1] as bigint,
+          };
+          break;
+        }
+        case StepType.ZAP_IN: {
+          step = {
+            type: StepType.ZAP_IN,
+            desiredAsset: Asset.fromPlutusData(
+              orderStepConstr.fields[0] as Constr<Data>
+            ),
+            minimumLP: orderStepConstr.fields[1] as bigint,
+          };
+          break;
+        }
+        default: {
+          throw new Error(
+            `Index of Order Step must be in 0-4, actual: ${orderStepConstr.index}`
+          );
+        }
       }
-    }
 
-    const batcherFee = data.fields[4] as bigint;
-    const depositADA = data.fields[5] as bigint;
-    return {
-      sender: sender,
-      receiver: receiver,
-      receiverDatumHash: receiverDatumHash,
-      step: step,
-      batcherFee: batcherFee,
-      depositADA: depositADA,
-    };
+      const batcherFee = data.fields[4] as bigint;
+      const depositADA = data.fields[5] as bigint;
+      return {
+        sender: sender,
+        receiver: receiver,
+        receiverDatumHash: receiverDatumHash,
+        step: step,
+        batcherFee: batcherFee,
+        depositADA: depositADA,
+      };
+    }
   }
-}
 
-export enum OrderRedeemer {
-  APPLY_ORDER = 0,
-  CANCEL_ORDER,
+  export enum Redeemer {
+    APPLY_ORDER = 0,
+    CANCEL_ORDER,
+  }
 }
diff --git a/src/types/pool.internal.ts b/src/types/pool.internal.ts
index 0b10c05..28d1bc1 100644
--- a/src/types/pool.internal.ts
+++ b/src/types/pool.internal.ts
@@ -1,10 +1,9 @@
 import invariant from "@minswap/tiny-invariant";
 import { Address, Constr, Data } from "lucid-cardano";
 
-import { FACTORY_ASSET_NAME, FACTORY_POLICY_ID } from "../constants";
-import { POOL_SCRIPT_HASH } from "../constants";
 import { getScriptHashFromAddress } from "../utils/address-utils.internal";
 import { AddressPlutusData } from "./address.internal";
+import { DexV1Constant } from "./constants";
 import { NetworkId } from "./network";
 import { Value } from "./tx.internal";
 
@@ -80,13 +79,13 @@ export function checkValidPoolOutput(
   datumHash: string | null
 ): void {
   invariant(
-    getScriptHashFromAddress(poolAddress) === POOL_SCRIPT_HASH,
+    getScriptHashFromAddress(poolAddress) === DexV1Constant.POOL_SCRIPT_HASH,
     `invalid pool address: ${poolAddress}`
   );
   // must have 1 factory token
   if (
     value.find(
-      ({ unit }) => unit === `${FACTORY_POLICY_ID}${FACTORY_ASSET_NAME}`
+      ({ unit }) => unit === `${DexV1Constant.FACTORY_POLICY_ID}${DexV1Constant.FACTORY_ASSET_NAME}`
     )?.quantity !== "1"
   ) {
     throw new Error(`expect pool to have 1 factory token`);
diff --git a/src/types/pool.ts b/src/types/pool.ts
index 7d7b495..53ef0ca 100644
--- a/src/types/pool.ts
+++ b/src/types/pool.ts
@@ -1,169 +1,430 @@
 import invariant from "@minswap/tiny-invariant";
-import { Constr, Data } from "lucid-cardano";
+import { Constr, Credential, Data } from "lucid-cardano";
 
-import {
-  FACTORY_POLICY_ID,
-  LP_POLICY_ID,
-  POOL_NFT_POLICY_ID,
-} from "../constants";
+import { sha3 } from "../utils/hash.internal";
+import { LucidCredential } from "./address.internal";
 import { Asset } from "./asset";
+import {
+  DexV1Constant,
+  DexV2Constant,
+  StableswapConstant,
+} from "./constants";
 import { NetworkId } from "./network";
 import { normalizeAssets, PoolFeeSharing } from "./pool.internal";
 import { TxIn, Value } from "./tx.internal";
 
-/**
- * Represents state of a pool UTxO. The state could be latest state or a historical state.
- */
-export class PoolState {
-  /** The transaction hash and output index of the pool UTxO */
-  public readonly address: string;
-  public readonly txIn: TxIn;
-  public readonly value: Value;
-  public readonly datumHash: string;
-  public readonly assetA: string;
-  public readonly assetB: string;
-
-  constructor(address: string, txIn: TxIn, value: Value, datumHash: string) {
-    this.address = address;
-    this.txIn = txIn;
-    this.value = value;
-    this.datumHash = datumHash;
-
-    const nft = value.find(({ unit }) => unit.startsWith(POOL_NFT_POLICY_ID));
-    invariant(nft, "pool doesn't have NFT");
-    const poolId = nft.unit.slice(56);
-    // validate and memoize assetA and assetB
-    const relevantAssets = value.filter(
-      ({ unit }) =>
-        !unit.startsWith(FACTORY_POLICY_ID) && // factory token
-        !unit.endsWith(poolId) // NFT and LP tokens from profit sharing
-    );
-    switch (relevantAssets.length) {
-      case 2: {
-        // ADA/A pool
-        this.assetA = "lovelace";
-        const nonADAAssets = relevantAssets.filter(
-          ({ unit }) => unit !== "lovelace"
-        );
-        invariant(nonADAAssets.length === 1, "pool must have 1 non-ADA asset");
-        this.assetB = nonADAAssets[0].unit;
-        break;
-      }
-      case 3: {
-        // A/B pool
-        const nonADAAssets = relevantAssets.filter(
-          ({ unit }) => unit !== "lovelace"
-        );
-        invariant(nonADAAssets.length === 2, "pool must have 1 non-ADA asset");
-        [this.assetA, this.assetB] = normalizeAssets(
-          nonADAAssets[0].unit,
-          nonADAAssets[1].unit
-        );
-        break;
+export const DEFAULT_POOL_V2_TRADING_FEE_DENOMINATOR = 10000n;
+
+export namespace PoolV1 {
+  /**
+   * Represents state of a pool UTxO. The state could be latest state or a historical state.
+   */
+  export class State {
+    /** The transaction hash and output index of the pool UTxO */
+    public readonly address: string;
+    public readonly txIn: TxIn;
+    public readonly value: Value;
+    public readonly datumHash: string;
+    public readonly assetA: string;
+    public readonly assetB: string;
+
+    constructor(address: string, txIn: TxIn, value: Value, datumHash: string) {
+      this.address = address;
+      this.txIn = txIn;
+      this.value = value;
+      this.datumHash = datumHash;
+
+      const nft = value.find(({ unit }) => unit.startsWith(DexV1Constant.POOL_NFT_POLICY_ID));
+      invariant(nft, "pool doesn't have NFT");
+      const poolId = nft.unit.slice(56);
+      // validate and memoize assetA and assetB
+      const relevantAssets = value.filter(
+        ({ unit }) =>
+          !unit.startsWith(DexV1Constant.FACTORY_POLICY_ID) && // factory token
+          !unit.endsWith(poolId) // NFT and LP tokens from profit sharing
+      );
+      switch (relevantAssets.length) {
+        case 2: {
+          // ADA/A pool
+          this.assetA = "lovelace";
+          const nonADAAssets = relevantAssets.filter(
+            ({ unit }) => unit !== "lovelace"
+          );
+          invariant(nonADAAssets.length === 1, "pool must have 1 non-ADA asset");
+          this.assetB = nonADAAssets[0].unit;
+          break;
+        }
+        case 3: {
+          // A/B pool
+          const nonADAAssets = relevantAssets.filter(
+            ({ unit }) => unit !== "lovelace"
+          );
+          invariant(nonADAAssets.length === 2, "pool must have 1 non-ADA asset");
+          [this.assetA, this.assetB] = normalizeAssets(
+            nonADAAssets[0].unit,
+            nonADAAssets[1].unit
+          );
+          break;
+        }
+        default:
+          throw new Error(
+            "pool must have 2 or 3 assets except factory, NFT and LP tokens"
+          );
       }
-      default:
-        throw new Error(
-          "pool must have 2 or 3 assets except factory, NFT and LP tokens"
-        );
     }
-  }
 
-  get nft(): string {
-    const nft = this.value.find(({ unit }) =>
-      unit.startsWith(POOL_NFT_POLICY_ID)
-    );
-    invariant(nft, "pool doesn't have NFT");
-    return nft.unit;
+    get nft(): string {
+      const nft = this.value.find(({ unit }) =>
+        unit.startsWith(DexV1Constant.POOL_NFT_POLICY_ID)
+      );
+      invariant(nft, "pool doesn't have NFT");
+      return nft.unit;
+    }
+
+    get id(): string {
+      // a pool's ID is the NFT's asset name
+      return this.nft.slice(DexV1Constant.POOL_NFT_POLICY_ID.length);
+    }
+
+    get assetLP(): string {
+      return `${DexV1Constant.LP_POLICY_ID}${this.id}`;
+    }
+
+    get reserveA(): bigint {
+      return BigInt(
+        this.value.find(({ unit }) => unit === this.assetA)?.quantity ?? "0"
+      );
+    }
+
+    get reserveB(): bigint {
+      return BigInt(
+        this.value.find(({ unit }) => unit === this.assetB)?.quantity ?? "0"
+      );
+    }
   }
 
-  get id(): string {
-    // a pool's ID is the NFT's asset name
-    return this.nft.slice(POOL_NFT_POLICY_ID.length);
+  export type Datum = {
+    assetA: Asset;
+    assetB: Asset;
+    totalLiquidity: bigint;
+    rootKLast: bigint;
+    feeSharing?: PoolFeeSharing;
+  };
+
+  export namespace Datum {
+    export function toPlutusData(datum: Datum): Constr<Data> {
+      const { assetA, assetB, totalLiquidity, rootKLast, feeSharing } = datum;
+      return new Constr(0, [
+        Asset.toPlutusData(assetA),
+        Asset.toPlutusData(assetB),
+        totalLiquidity,
+        rootKLast,
+        feeSharing
+          ? new Constr(0, [PoolFeeSharing.toPlutusData(feeSharing)])
+          : new Constr(1, []),
+      ]);
+    }
+
+    export function fromPlutusData(
+      networkId: NetworkId,
+      data: Constr<Data>
+    ): Datum {
+      if (data.index !== 0) {
+        throw new Error(`Index of Pool Datum must be 0, actual: ${data.index}`);
+      }
+      let feeSharing: PoolFeeSharing | undefined = undefined;
+      const maybeFeeSharingConstr = data.fields[4] as Constr<Data>;
+      switch (maybeFeeSharingConstr.index) {
+        case 0: {
+          feeSharing = PoolFeeSharing.fromPlutusData(
+            networkId,
+            maybeFeeSharingConstr.fields[0] as Constr<Data>
+          );
+          break;
+        }
+        case 1: {
+          feeSharing = undefined;
+          break;
+        }
+        default: {
+          throw new Error(
+            `Index of Pool Fee Sharing must be 0 or 1, actual: ${maybeFeeSharingConstr.index}`
+          );
+        }
+      }
+      return {
+        assetA: Asset.fromPlutusData(data.fields[0] as Constr<Data>),
+        assetB: Asset.fromPlutusData(data.fields[1] as Constr<Data>),
+        totalLiquidity: data.fields[2] as bigint,
+        rootKLast: data.fields[3] as bigint,
+        feeSharing: feeSharing,
+      };
+    }
   }
+}
+
+export namespace StablePool {
+  export class State {
+    public readonly address: string;
+    public readonly txIn: TxIn;
+    public readonly value: Value;
+    public readonly datumCbor: string;
+    public readonly datum: Datum;
+    public readonly config: StableswapConstant.Config
+
+    constructor(
+      networkId: NetworkId,
+      address: string,
+      txIn: TxIn,
+      value: Value,
+      datum: string
+    ) {
+      this.address = address
+      this.txIn = txIn
+      this.value = value
+      this.datumCbor = datum
+      this.datum = Datum.fromPlutusData(Data.from(datum))
+      const allConfigs = StableswapConstant.CONFIG[networkId]
+      const config = allConfigs.find((cfg) => cfg.poolAddress === address)
+      if (!config) {
+        throw new Error("Invalid Stable Pool address")
+      }
+      this.config = config
+      if (!value.find((v) => v.unit === config.nftAsset && v.quantity === "1")) {
+        throw new Error("Cannot find the Pool NFT in the value")
+      }
+    }
+
+    get assets(): string[] {
+      return this.config.assets
+    }
+
+    get nft(): string {
+      return this.config.nftAsset
+    }
+
+    get lpAsset(): string {
+      return this.config.lpAsset
+    }
+
+    get reserves(): bigint[] {
+      return this.datum.balances
+    }
+
+    get totalLiquidity(): bigint {
+      return this.datum.totalLiquidity
+    }
+
+    get orderHash(): string {
+      return this.datum.orderHash
+    }
+
+    get amp(): bigint {
+      return this.datum.amplificationCoefficient
+    }
 
-  get assetLP(): string {
-    return `${LP_POLICY_ID}${this.id}`;
+    get id(): string {
+      return this.nft
+    }
   }
 
-  get reserveA(): bigint {
-    return BigInt(
-      this.value.find(({ unit }) => unit === this.assetA)?.quantity ?? "0"
-    );
+  export type Datum = {
+    balances: bigint[];
+    totalLiquidity: bigint;
+    amplificationCoefficient: bigint;
+    orderHash: string;
   }
 
-  get reserveB(): bigint {
-    return BigInt(
-      this.value.find(({ unit }) => unit === this.assetB)?.quantity ?? "0"
-    );
+  export namespace Datum {
+    export function toPlutusData(datum: Datum): Constr<Data> {
+      const { balances, totalLiquidity, amplificationCoefficient, orderHash } = datum;
+      return new Constr(0, [
+        balances,
+        totalLiquidity,
+        amplificationCoefficient,
+        orderHash
+      ]);
+    }
+
+    export function fromPlutusData(data: Constr<Data>): Datum {
+      if (data.index !== 0) {
+        throw new Error(`Index of Pool Datum must be 0, actual: ${data.index}`);
+      }
+      return {
+        balances: data.fields[0] as bigint[],
+        totalLiquidity: data.fields[1] as bigint,
+        amplificationCoefficient: data.fields[2] as bigint,
+        orderHash: data.fields[3] as string
+      }
+    }
   }
 }
 
-/**
- * Represents a historical point of a pool.
- */
-export type PoolHistory = {
-  txHash: string;
-  /** Transaction index within the block */
-  txIndex: number;
-  blockHeight: number;
-  time: Date;
-};
-
-export type PoolDatum = {
-  assetA: Asset;
-  assetB: Asset;
-  totalLiquidity: bigint;
-  rootKLast: bigint;
-  feeSharing?: PoolFeeSharing;
-};
-
-export namespace PoolDatum {
-  export function toPlutusData(datum: PoolDatum): Constr<Data> {
-    const { assetA, assetB, totalLiquidity, rootKLast, feeSharing } = datum;
-    return new Constr(0, [
-      Asset.toPlutusData(assetA),
-      Asset.toPlutusData(assetB),
-      totalLiquidity,
-      rootKLast,
-      feeSharing
-        ? new Constr(0, [PoolFeeSharing.toPlutusData(feeSharing)])
-        : new Constr(1, []),
-    ]);
+export namespace PoolV2 {
+  export function computeLPAssetName(assetA: Asset, assetB: Asset): string {
+    const k1 = sha3(assetA.policyId + assetA.tokenName);
+    const k2 = sha3(assetB.policyId + assetB.tokenName);
+    return sha3(k1 + k2);
   }
-
-  export function fromPlutusData(
-    networkId: NetworkId,
-    data: Constr<Data>
-  ): PoolDatum {
-    if (data.index !== 0) {
-      throw new Error(`Index of Pool Datum must be 0, actual: ${data.index}`);
-    }
-    let feeSharing: PoolFeeSharing | undefined = undefined;
-    const maybeFeeSharingConstr = data.fields[4] as Constr<Data>;
-    switch (maybeFeeSharingConstr.index) {
-      case 0: {
-        feeSharing = PoolFeeSharing.fromPlutusData(
-          networkId,
-          maybeFeeSharingConstr.fields[0] as Constr<Data>
-        );
-        break;
+  export class State {
+    public readonly address: string;
+    public readonly txIn: TxIn;
+    public readonly value: Value;
+    public readonly datumRaw: string;
+    public readonly datum: Datum;
+    public readonly config: DexV2Constant.Config
+    public readonly lpAsset: Asset;
+    public readonly authenAsset: Asset;
+    constructor(
+      networkId: NetworkId,
+      address: string,
+      txIn: TxIn,
+      value: Value,
+      datum: string
+    ) {
+      this.address = address
+      this.txIn = txIn
+      this.value = value
+      this.datumRaw = datum
+      this.datum = Datum.fromPlutusData(Data.from(datum))
+      this.config = DexV2Constant.CONFIG[networkId]
+      this.lpAsset = {
+        policyId: this.config.lpPolicyId,
+        tokenName: computeLPAssetName(this.datum.assetA, this.datum.assetB)
       }
-      case 1: {
-        feeSharing = undefined;
-        break;
+      this.authenAsset = Asset.fromString(this.config.poolAuthenAsset)
+      if (!value.find((v) => v.unit === this.config.poolAuthenAsset && v.quantity === "1")) {
+        throw new Error("Cannot find the Pool Authentication Asset in the value")
       }
-      default: {
-        throw new Error(
-          `Index of Pool Fee Sharing must be 0 or 1, actual: ${maybeFeeSharingConstr.index}`
-        );
+    }
+
+    get assetA(): string {
+      return Asset.toString(this.datum.assetA)
+    }
+
+    get assetB(): string {
+      return Asset.toString(this.datum.assetB)
+    }
+
+    get totalLiquidity(): bigint {
+      return this.datum.totalLiquidity
+    }
+
+    get reserveA(): bigint {
+      return this.datum.reserveA
+    }
+
+    get reserveB(): bigint {
+      return this.datum.reserveB
+    }
+
+    get feeA(): [bigint, bigint] {
+      return [
+        this.datum.baseFee.feeANumerator,
+        DEFAULT_POOL_V2_TRADING_FEE_DENOMINATOR
+      ]
+    }
+
+    get feeB(): [bigint, bigint] {
+      return [
+        this.datum.baseFee.feeBNumerator,
+        DEFAULT_POOL_V2_TRADING_FEE_DENOMINATOR
+      ]
+    }
+
+    get feeShare(): [bigint, bigint] | undefined {
+      if (this.datum.feeSharingNumerator !== undefined) {
+        return [
+          this.datum.feeSharingNumerator,
+          DEFAULT_POOL_V2_TRADING_FEE_DENOMINATOR
+        ]
+      } else {
+        return undefined
       }
     }
-    return {
-      assetA: Asset.fromPlutusData(data.fields[0] as Constr<Data>),
-      assetB: Asset.fromPlutusData(data.fields[1] as Constr<Data>),
-      totalLiquidity: data.fields[2] as bigint,
-      rootKLast: data.fields[3] as bigint,
-      feeSharing: feeSharing,
+  }
+
+  export type Datum = {
+    poolBatchingStakeCredential: Credential;
+    assetA: Asset;
+    assetB: Asset;
+    totalLiquidity: bigint;
+    reserveA: bigint;
+    reserveB: bigint;
+    baseFee: {
+      feeANumerator: bigint;
+      feeBNumerator: bigint;
     };
+    feeSharingNumerator?: bigint;
+    allowDynamicFee: boolean;
+  }
+
+  export namespace Datum {
+    export function toPlutusData(datum: Datum): Constr<Data> {
+      const {
+        poolBatchingStakeCredential,
+        assetA,
+        assetB,
+        totalLiquidity,
+        reserveA,
+        reserveB,
+        baseFee,
+        feeSharingNumerator,
+        allowDynamicFee
+      } = datum;
+      return new Constr(0, [
+        LucidCredential.toPlutusData(poolBatchingStakeCredential),
+        Asset.toPlutusData(assetA),
+        Asset.toPlutusData(assetB),
+        totalLiquidity,
+        reserveA,
+        reserveB,
+        baseFee.feeANumerator,
+        baseFee.feeBNumerator,
+        feeSharingNumerator !== undefined
+          ? new Constr(0, [feeSharingNumerator])
+          : new Constr(1, []),
+        new Constr(allowDynamicFee ? 1 : 0, [])
+      ]);
+    }
+
+    export function fromPlutusData(data: Constr<Data>): Datum {
+      if (data.index !== 0) {
+        throw new Error(`Index of Pool Datum must be 0, actual: ${data.index}`);
+      }
+      let feeSharingNumerator: bigint | undefined = undefined;
+      const maybeFeeSharingConstr = data.fields[8] as Constr<Data>;
+      switch (maybeFeeSharingConstr.index) {
+        case 0: {
+          feeSharingNumerator = maybeFeeSharingConstr.fields[0] as bigint
+          break;
+        }
+        case 1: {
+          feeSharingNumerator = undefined;
+          break;
+        }
+        default: {
+          throw new Error(
+            `Index of Pool Fee Sharing must be 0 or 1, actual: ${maybeFeeSharingConstr.index}`
+          );
+        }
+      }
+      const allowDynamicFeeConstr = data.fields[9] as Constr<Data>;
+      const allowDynamicFee = allowDynamicFeeConstr.index === 1;
+      return {
+        poolBatchingStakeCredential: LucidCredential.fromPlutusData(data.fields[0] as Constr<Data>),
+        assetA: Asset.fromPlutusData(data.fields[1] as Constr<Data>),
+        assetB: Asset.fromPlutusData(data.fields[2] as Constr<Data>),
+        totalLiquidity: data.fields[3] as bigint,
+        reserveA: data.fields[4] as bigint,
+        reserveB: data.fields[5] as bigint,
+        baseFee: {
+          feeANumerator: data.fields[6] as bigint,
+          feeBNumerator: data.fields[7] as bigint
+        },
+        feeSharingNumerator: feeSharingNumerator,
+        allowDynamicFee: allowDynamicFee
+      };
+    }
   }
 }
diff --git a/src/types/tx.internal.ts b/src/types/tx.internal.ts
index 46cb2b2..336e02d 100644
--- a/src/types/tx.internal.ts
+++ b/src/types/tx.internal.ts
@@ -7,3 +7,11 @@ export type TxIn = {
   txHash: string;
   index: number;
 };
+
+export type TxHistory = {
+  txHash: string;
+  /** Transaction index within the block */
+  txIndex: number;
+  blockHeight: number;
+  time: Date;
+}
\ No newline at end of file
diff --git a/src/utils/hash.internal.ts b/src/utils/hash.internal.ts
new file mode 100644
index 0000000..17df03a
--- /dev/null
+++ b/src/utils/hash.internal.ts
@@ -0,0 +1,7 @@
+import { SHA3 } from "sha3";
+
+export function sha3(hex: string): string {
+    const hash = new SHA3(256);
+    hash.update(hex, "hex");
+    return hash.digest("hex");
+}
\ No newline at end of file
diff --git a/test/adapter.test.ts b/test/adapter.test.ts
index 713b8f0..fc0a220 100644
--- a/test/adapter.test.ts
+++ b/test/adapter.test.ts
@@ -1,7 +1,13 @@
 import { BlockFrostAPI } from "@blockfrost/blockfrost-js";
 import { jest } from "@jest/globals";
 
-import { BlockfrostAdapter } from "../src";
+import {
+  ADA,
+  Asset,
+  BlockfrostAdapter,
+  NetworkId,
+  StableswapConstant,
+} from "../src";
 
 function mustGetEnv(key: string): string {
   const val = process.env[key];
@@ -11,11 +17,25 @@ function mustGetEnv(key: string): string {
   return val;
 }
 
-const MIN = "29d222ce763455e3d7a09a665ce554f00ac89d2e99a1a83d267170c64d494e";
-const MIN_ADA_POOL_ID =
+const MIN_TESTNET =
+  "e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed724d494e";
+const MIN_MAINNET =
+  "29d222ce763455e3d7a09a665ce554f00ac89d2e99a1a83d267170c64d494e";
+const MIN_ADA_POOL_V1_ID_TESTNET =
+  "3bb0079303c57812462dec9de8fb867cef8fd3768de7f12c77f6f0dd80381d0d";
+const MIN_ADA_POOL_V1_ID_MAINNET =
   "6aa2153e1ae896a95539c9d62f76cedcdabdcdf144e564b8955f609d660cf6a2";
 
-const adapter = new BlockfrostAdapter({
+const adapterTestnet = new BlockfrostAdapter({
+  networkId: NetworkId.TESTNET,
+  blockFrost: new BlockFrostAPI({
+    projectId: mustGetEnv("BLOCKFROST_PROJECT_ID_TESTNET"),
+    network: "preprod",
+  }),
+});
+
+const adapterMainnet = new BlockfrostAdapter({
+  networkId: NetworkId.MAINNET,
   blockFrost: new BlockFrostAPI({
     projectId: mustGetEnv("BLOCKFROST_PROJECT_ID_MAINNET"),
     network: "mainnet",
@@ -27,12 +47,14 @@ beforeAll(() => {
 });
 
 test("getAssetDecimals", async () => {
-  expect(await adapter.getAssetDecimals("lovelace")).toBe(6);
-  expect(await adapter.getAssetDecimals(MIN)).toBe(6);
+  expect(await adapterTestnet.getAssetDecimals("lovelace")).toBe(6);
+  expect(await adapterTestnet.getAssetDecimals(MIN_TESTNET)).toBe(0);
+  expect(await adapterMainnet.getAssetDecimals("lovelace")).toBe(6);
+  expect(await adapterMainnet.getAssetDecimals(MIN_MAINNET)).toBe(6);
 });
 
-test("getPoolPrice", async () => {
-  const pools = await adapter.getPools({
+async function testPoolPrice(adapter: BlockfrostAdapter): Promise<void> {
+  const pools = await adapter.getV1Pools({
     page: 1,
   });
   expect(pools.length).toBeGreaterThan(0);
@@ -40,26 +62,105 @@ test("getPoolPrice", async () => {
   for (let i = 0; i < 5; i++) {
     const idx = Math.floor(Math.random() * pools.length);
     const pool = pools[idx];
-    const [priceAB, priceBA] = await adapter.getPoolPrice({ pool });
+    const [priceAB, priceBA] = await adapter.getV1PoolPrice({ pool });
     // product of 2 prices must be approximately equal to 1
     // abs(priceAB * priceBA - 1) <= epsilon
     expect(priceAB.mul(priceBA).sub(1).abs().toNumber()).toBeLessThanOrEqual(
       1e-6
     );
   }
+}
+
+test("getPoolPrice", async () => {
+  await testPoolPrice(adapterTestnet);
+  await testPoolPrice(adapterMainnet);
 }, 10000);
 
-test("getPoolById", async () => {
-  const pool = await adapter.getPoolById({ id: MIN_ADA_POOL_ID });
-  expect(pool).not.toBeNull();
-  expect(pool?.assetA).toEqual("lovelace");
-  expect(pool?.assetB).toEqual(MIN);
+test("getV1PoolById", async () => {
+  const adaMINTestnet = await adapterTestnet.getV1PoolById({
+    id: MIN_ADA_POOL_V1_ID_TESTNET,
+  });
+  expect(adaMINTestnet).not.toBeNull();
+  expect(adaMINTestnet?.assetA).toEqual("lovelace");
+  expect(adaMINTestnet?.assetB).toEqual(MIN_TESTNET);
+
+  const adaMINMainnet = await adapterMainnet.getV1PoolById({
+    id: MIN_ADA_POOL_V1_ID_MAINNET,
+  });
+  expect(adaMINMainnet).not.toBeNull();
+  expect(adaMINMainnet?.assetA).toEqual("lovelace");
+  expect(adaMINMainnet?.assetB).toEqual(MIN_MAINNET);
 });
 
-test("get prices of last 5 states of MIN/ADA pool", async () => {
-  const history = await adapter.getPoolHistory({ id: MIN_ADA_POOL_ID });
+async function testPriceHistory(
+  adapter: BlockfrostAdapter,
+  id: string
+): Promise<void> {
+  const history = await adapter.getV1PoolHistory({ id: id });
   for (let i = 0; i < Math.min(5, history.length); i++) {
-    const pool = await adapter.getPoolInTx({ txHash: history[i].txHash });
+    const pool = await adapter.getV1PoolInTx({ txHash: history[i].txHash });
     expect(pool?.txIn.txHash).toEqual(history[i].txHash);
   }
+}
+
+test("get prices of last 5 states of MIN/ADA pool", async () => {
+  await testPriceHistory(adapterTestnet, MIN_ADA_POOL_V1_ID_TESTNET);
+  await testPriceHistory(adapterMainnet, MIN_ADA_POOL_V1_ID_MAINNET);
+});
+
+test("getV2PoolByPair", async () => {
+  const pool = await adapterTestnet.getV2PoolByPair(ADA, {
+    policyId: "e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed72",
+    tokenName: "4d494e",
+  });
+  expect(pool).not.toBeNull();
+  expect(pool?.assetA).toEqual("lovelace");
+  expect(pool?.assetB).toEqual(MIN_TESTNET);
+});
+
+test("getAllV2Pools", async () => {
+  const { pools } = await adapterTestnet.getAllV2Pools();
+  expect(pools.length > 0);
+});
+
+test("getV2Pools", async () => {
+  const { pools } = await adapterTestnet.getV2Pools({
+    page: 1,
+  });
+  expect(pools.length > 0);
+});
+
+test("getAllStablePools", async () => {
+  const numberOfStablePoolsTestnet =
+    StableswapConstant.CONFIG[NetworkId.TESTNET].length;
+  const numberOfStablePoolsMainnet =
+    StableswapConstant.CONFIG[NetworkId.MAINNET].length;
+  const { pools: testnetPools } = await adapterTestnet.getAllStablePools();
+  expect(testnetPools.length === numberOfStablePoolsTestnet);
+
+  const { pools: mainnetPools } = await adapterMainnet.getAllStablePools();
+  expect(mainnetPools.length === numberOfStablePoolsMainnet);
+});
+
+test("getStablePoolByNFT", async () => {
+  const testnetCfgs = StableswapConstant.CONFIG[NetworkId.TESTNET];
+  const mainnetCfgs = StableswapConstant.CONFIG[NetworkId.MAINNET];
+
+  for (const cfg of testnetCfgs) {
+    const pool = await adapterTestnet.getStablePoolByNFT(
+      Asset.fromString(cfg.nftAsset)
+    );
+    expect(pool).not.toBeNull();
+    expect(pool?.nft).toEqual(cfg.nftAsset);
+    expect(pool?.assets).toEqual(cfg.assets);
+  }
+
+  for (const cfg of mainnetCfgs) {
+    const pool = await adapterMainnet.getStablePoolByNFT(
+      Asset.fromString(cfg.nftAsset)
+    );
+    expect(pool).not.toBeNull();
+    expect(pool?.nft).toEqual(cfg.nftAsset);
+    expect(pool?.assets).toEqual(cfg.assets);
+  }
 });
diff --git a/test/order.test.ts b/test/order.test.ts
index 3335b03..148fcb3 100644
--- a/test/order.test.ts
+++ b/test/order.test.ts
@@ -2,10 +2,10 @@ import JSONBig from "json-bigint";
 import { Address, Data } from "lucid-cardano";
 
 import { FIXED_BATCHER_FEE } from "../src/batcher-fee-reduction/configs.internal";
-import { FIXED_DEPOSIT_ADA } from "../src/constants";
+import { FIXED_DEPOSIT_ADA } from "../src/types/constants";
 import { Asset } from "../src/types/asset";
 import { NetworkId } from "../src/types/network";
-import { OrderDatum, OrderStepType } from "../src/types/order";
+import { OrderV1 } from "../src/types/order";
 
 let testSender: Address;
 let testReceiver: Address;
@@ -27,12 +27,12 @@ beforeAll(() => {
 });
 
 test("SwapExactIn Order to PlutusData Converter", () => {
-  const order1: OrderDatum = {
+  const order1: OrderV1.Datum = {
     sender: testSender,
     receiver: testReceiver,
     receiverDatumHash: testReceiverDatumHash,
     step: {
-      type: OrderStepType.SWAP_EXACT_IN,
+      type: OrderV1.StepType.SWAP_EXACT_IN,
       desiredAsset: testAsset,
       minimumReceived: 10n,
     },
@@ -40,12 +40,12 @@ test("SwapExactIn Order to PlutusData Converter", () => {
     depositADA: FIXED_DEPOSIT_ADA,
   };
 
-  const order2: OrderDatum = {
+  const order2: OrderV1.Datum = {
     sender: testSender,
     receiver: testReceiver,
     receiverDatumHash: undefined,
     step: {
-      type: OrderStepType.SWAP_EXACT_IN,
+      type: OrderV1.StepType.SWAP_EXACT_IN,
       desiredAsset: testAsset,
       minimumReceived: 10n,
     },
@@ -53,25 +53,25 @@ test("SwapExactIn Order to PlutusData Converter", () => {
     depositADA: FIXED_DEPOSIT_ADA,
   };
 
-  const convertedOrder1 = OrderDatum.fromPlutusData(
+  const convertedOrder1 = OrderV1.Datum.fromPlutusData(
     networkId,
-    Data.from(Data.to(OrderDatum.toPlutusData(order1)))
+    Data.from(Data.to(OrderV1.Datum.toPlutusData(order1)))
   );
-  const convertedOrder2 = OrderDatum.fromPlutusData(
+  const convertedOrder2 = OrderV1.Datum.fromPlutusData(
     networkId,
-    Data.from(Data.to(OrderDatum.toPlutusData(order2)))
+    Data.from(Data.to(OrderV1.Datum.toPlutusData(order2)))
   );
   expect(JSONBig.stringify(order1)).toEqual(JSONBig.stringify(convertedOrder1));
   expect(JSONBig.stringify(order2)).toEqual(JSONBig.stringify(convertedOrder2));
 });
 
 test("SwapExactOut Order to PlutusData Converter", () => {
-  const order1: OrderDatum = {
+  const order1: OrderV1.Datum = {
     sender: testSender,
     receiver: testReceiver,
     receiverDatumHash: testReceiverDatumHash,
     step: {
-      type: OrderStepType.SWAP_EXACT_OUT,
+      type: OrderV1.StepType.SWAP_EXACT_OUT,
       desiredAsset: testAsset,
       expectedReceived: 10n,
     },
@@ -79,12 +79,12 @@ test("SwapExactOut Order to PlutusData Converter", () => {
     depositADA: FIXED_DEPOSIT_ADA,
   };
 
-  const order2: OrderDatum = {
+  const order2: OrderV1.Datum = {
     sender: testSender,
     receiver: testReceiver,
     receiverDatumHash: undefined,
     step: {
-      type: OrderStepType.SWAP_EXACT_OUT,
+      type: OrderV1.StepType.SWAP_EXACT_OUT,
       desiredAsset: testAsset,
       expectedReceived: 10n,
     },
@@ -92,62 +92,62 @@ test("SwapExactOut Order to PlutusData Converter", () => {
     depositADA: FIXED_DEPOSIT_ADA,
   };
 
-  const convertedOrder1 = OrderDatum.fromPlutusData(
+  const convertedOrder1 = OrderV1.Datum.fromPlutusData(
     networkId,
-    Data.from(Data.to(OrderDatum.toPlutusData(order1)))
+    Data.from(Data.to(OrderV1.Datum.toPlutusData(order1)))
   );
-  const convertedOrder2 = OrderDatum.fromPlutusData(
+  const convertedOrder2 = OrderV1.Datum.fromPlutusData(
     networkId,
-    Data.from(Data.to(OrderDatum.toPlutusData(order2)))
+    Data.from(Data.to(OrderV1.Datum.toPlutusData(order2)))
   );
   expect(JSONBig.stringify(order1)).toEqual(JSONBig.stringify(convertedOrder1));
   expect(JSONBig.stringify(order2)).toEqual(JSONBig.stringify(convertedOrder2));
 });
 
 test("Deposit Order to PlutusData Converter", () => {
-  const order1: OrderDatum = {
+  const order1: OrderV1.Datum = {
     sender: testSender,
     receiver: testReceiver,
     receiverDatumHash: testReceiverDatumHash,
     step: {
-      type: OrderStepType.DEPOSIT,
+      type: OrderV1.StepType.DEPOSIT,
       minimumLP: 10n,
     },
     batcherFee: FIXED_BATCHER_FEE,
     depositADA: FIXED_DEPOSIT_ADA,
   };
 
-  const order2: OrderDatum = {
+  const order2: OrderV1.Datum = {
     sender: testSender,
     receiver: testReceiver,
     receiverDatumHash: undefined,
     step: {
-      type: OrderStepType.DEPOSIT,
+      type: OrderV1.StepType.DEPOSIT,
       minimumLP: 10n,
     },
     batcherFee: FIXED_BATCHER_FEE,
     depositADA: FIXED_DEPOSIT_ADA,
   };
 
-  const convertedOrder1 = OrderDatum.fromPlutusData(
+  const convertedOrder1 = OrderV1.Datum.fromPlutusData(
     networkId,
-    Data.from(Data.to(OrderDatum.toPlutusData(order1)))
+    Data.from(Data.to(OrderV1.Datum.toPlutusData(order1)))
   );
-  const convertedOrder2 = OrderDatum.fromPlutusData(
+  const convertedOrder2 = OrderV1.Datum.fromPlutusData(
     networkId,
-    Data.from(Data.to(OrderDatum.toPlutusData(order2)))
+    Data.from(Data.to(OrderV1.Datum.toPlutusData(order2)))
   );
   expect(JSONBig.stringify(order1)).toEqual(JSONBig.stringify(convertedOrder1));
   expect(JSONBig.stringify(order2)).toEqual(JSONBig.stringify(convertedOrder2));
 });
 
 test("Withdraw Order to PlutusData Converter", () => {
-  const order1: OrderDatum = {
+  const order1: OrderV1.Datum = {
     sender: testSender,
     receiver: testReceiver,
     receiverDatumHash: testReceiverDatumHash,
     step: {
-      type: OrderStepType.WITHDRAW,
+      type: OrderV1.StepType.WITHDRAW,
       minimumAssetA: 10n,
       minimumAssetB: 11n,
     },
@@ -155,12 +155,12 @@ test("Withdraw Order to PlutusData Converter", () => {
     depositADA: FIXED_DEPOSIT_ADA,
   };
 
-  const order2: OrderDatum = {
+  const order2: OrderV1.Datum = {
     sender: testSender,
     receiver: testReceiver,
     receiverDatumHash: undefined,
     step: {
-      type: OrderStepType.WITHDRAW,
+      type: OrderV1.StepType.WITHDRAW,
       minimumAssetA: 10n,
       minimumAssetB: 11n,
     },
@@ -168,25 +168,25 @@ test("Withdraw Order to PlutusData Converter", () => {
     depositADA: FIXED_DEPOSIT_ADA,
   };
 
-  const convertedOrder1 = OrderDatum.fromPlutusData(
+  const convertedOrder1 = OrderV1.Datum.fromPlutusData(
     networkId,
-    Data.from(Data.to(OrderDatum.toPlutusData(order1)))
+    Data.from(Data.to(OrderV1.Datum.toPlutusData(order1)))
   );
-  const convertedOrder2 = OrderDatum.fromPlutusData(
+  const convertedOrder2 = OrderV1.Datum.fromPlutusData(
     networkId,
-    Data.from(Data.to(OrderDatum.toPlutusData(order2)))
+    Data.from(Data.to(OrderV1.Datum.toPlutusData(order2)))
   );
   expect(JSONBig.stringify(order1)).toEqual(JSONBig.stringify(convertedOrder1));
   expect(JSONBig.stringify(order2)).toEqual(JSONBig.stringify(convertedOrder2));
 });
 
 test("Zap Order to PlutusData Converter", () => {
-  const order1: OrderDatum = {
+  const order1: OrderV1.Datum = {
     sender: testSender,
     receiver: testReceiver,
     receiverDatumHash: testReceiverDatumHash,
     step: {
-      type: OrderStepType.ZAP_IN,
+      type: OrderV1.StepType.ZAP_IN,
       desiredAsset: testAsset,
       minimumLP: 11n,
     },
@@ -194,12 +194,12 @@ test("Zap Order to PlutusData Converter", () => {
     depositADA: FIXED_DEPOSIT_ADA,
   };
 
-  const order2: OrderDatum = {
+  const order2: OrderV1.Datum = {
     sender: testSender,
     receiver: testReceiver,
     receiverDatumHash: undefined,
     step: {
-      type: OrderStepType.ZAP_IN,
+      type: OrderV1.StepType.ZAP_IN,
       desiredAsset: testAsset,
       minimumLP: 11n,
     },
@@ -207,13 +207,13 @@ test("Zap Order to PlutusData Converter", () => {
     depositADA: FIXED_DEPOSIT_ADA,
   };
 
-  const convertedOrder1 = OrderDatum.fromPlutusData(
+  const convertedOrder1 = OrderV1.Datum.fromPlutusData(
     networkId,
-    Data.from(Data.to(OrderDatum.toPlutusData(order1)))
+    Data.from(Data.to(OrderV1.Datum.toPlutusData(order1)))
   );
-  const convertedOrder2 = OrderDatum.fromPlutusData(
+  const convertedOrder2 = OrderV1.Datum.fromPlutusData(
     networkId,
-    Data.from(Data.to(OrderDatum.toPlutusData(order2)))
+    Data.from(Data.to(OrderV1.Datum.toPlutusData(order2)))
   );
   expect(JSONBig.stringify(order1)).toEqual(JSONBig.stringify(convertedOrder1));
   expect(JSONBig.stringify(order2)).toEqual(JSONBig.stringify(convertedOrder2));
diff --git a/test/pool.test.ts b/test/pool.test.ts
index d0f4bc5..96cc6a6 100644
--- a/test/pool.test.ts
+++ b/test/pool.test.ts
@@ -3,7 +3,7 @@ import { Data } from "lucid-cardano";
 
 import { NetworkId } from "../src";
 import { ADA, Asset } from "../src/types/asset";
-import { PoolDatum, PoolState } from "../src/types/pool";
+import { PoolV1 } from "../src/types/pool";
 import { isValidPoolOutput, PoolFeeSharing } from "../src/types/pool.internal";
 import { TxIn, Value } from "../src/types/tx.internal";
 
@@ -36,8 +36,8 @@ test("can handle pool with one side being LP tokens", () => {
     isValidPoolOutput(PREPROD_POOL_ADDRESS, value, datumHash)
   ).toBeTruthy();
   expect(
-    new PoolState(PREPROD_POOL_ADDRESS, txIn, value, datumHash)
-  ).toBeInstanceOf(PoolState);
+    new PoolV1.State(PREPROD_POOL_ADDRESS, txIn, value, datumHash)
+  ).toBeInstanceOf(PoolV1.State);
 });
 
 test("Fee Sharing to PlutusData Converter", () => {
@@ -74,7 +74,7 @@ test("Pool Datum to PlutusData Converter", () => {
     policyId: "e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed72",
     tokenName: "4d494e",
   };
-  const poolDatum1: PoolDatum = {
+  const poolDatum1: PoolV1.Datum = {
     assetA: assetA,
     assetB: assetB,
     totalLiquidity: 100000n,
@@ -86,7 +86,7 @@ test("Pool Datum to PlutusData Converter", () => {
     },
   };
 
-  const poolDatum2: PoolDatum = {
+  const poolDatum2: PoolV1.Datum = {
     assetA: assetA,
     assetB: assetB,
     totalLiquidity: 100000n,
@@ -94,13 +94,13 @@ test("Pool Datum to PlutusData Converter", () => {
     feeSharing: undefined,
   };
 
-  const convertedPoolDatum1 = PoolDatum.fromPlutusData(
+  const convertedPoolDatum1 = PoolV1.Datum.fromPlutusData(
     NetworkId.TESTNET,
-    Data.from(Data.to(PoolDatum.toPlutusData(poolDatum1)))
+    Data.from(Data.to(PoolV1.Datum.toPlutusData(poolDatum1)))
   );
-  const convertedPoolDatum2 = PoolDatum.fromPlutusData(
+  const convertedPoolDatum2 = PoolV1.Datum.fromPlutusData(
     NetworkId.TESTNET,
-    Data.from(Data.to(PoolDatum.toPlutusData(poolDatum2)))
+    Data.from(Data.to(PoolV1.Datum.toPlutusData(poolDatum2)))
   );
 
   expect(JSONBig.stringify(poolDatum1)).toEqual(