From 7159a5f4da199c70a50dd4b95e5a80ddff678411 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 3 Dec 2024 14:18:51 -0500 Subject: [PATCH 1/3] Splash --- CHANGELOG.md | 3 + README.md | 3 +- package-lock.json | 54 +++- package.json | 6 +- src/constants.ts | 6 + src/dex/api/spectrum-api.ts | 63 ---- src/dex/api/splash-api.ts | 77 +++++ src/dex/api/teddyswap-api.ts | 62 ---- src/dex/base-dex.ts | 2 +- src/dex/definitions/spectrum/order.ts | 85 ------ src/dex/definitions/spectrum/pool.ts | 62 ---- src/dex/definitions/splash/order.ts | 116 ++++++++ .../definitions/{teddyswap => splash}/pool.ts | 21 +- src/dex/definitions/teddyswap/order.ts | 85 ------ src/dex/logo/spectrum.png | Bin 420 -> 0 bytes src/dex/logo/splash.png | Bin 0 -> 1163 bytes src/dex/logo/teddyswap.png | Bin 10282 -> 0 bytes src/dex/{spectrum.ts => splash.ts} | 149 +++++++--- src/dex/teddyswap.ts | 280 ------------------ src/dexter.ts | 6 +- src/index.ts | 3 +- src/requests/swap-request.ts | 4 +- src/utils.ts | 6 +- 23 files changed, 385 insertions(+), 708 deletions(-) delete mode 100644 src/dex/api/spectrum-api.ts create mode 100644 src/dex/api/splash-api.ts delete mode 100644 src/dex/api/teddyswap-api.ts delete mode 100644 src/dex/definitions/spectrum/order.ts delete mode 100644 src/dex/definitions/spectrum/pool.ts create mode 100644 src/dex/definitions/splash/order.ts rename src/dex/definitions/{teddyswap => splash}/pool.ts (72%) delete mode 100644 src/dex/definitions/teddyswap/order.ts delete mode 100644 src/dex/logo/spectrum.png create mode 100644 src/dex/logo/splash.png delete mode 100644 src/dex/logo/teddyswap.png rename src/dex/{spectrum.ts => splash.ts} (53%) delete mode 100644 src/dex/teddyswap.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index cdf19da..f24b800 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ All notable changes to Dexter will be documented in this file. +## [v5.4.9] +- Splash integration + ## [v5.4.0] - SundaeSwap v3 integration diff --git a/README.md b/README.md index 8f787b2..c845837 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,7 @@ - - + ### What You Can Do diff --git a/package-lock.json b/package-lock.json index 0686198..0441087 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,23 @@ { "name": "@indigo-labs/dexter", - "version": "5.4.2", + "version": "5.4.8", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@indigo-labs/dexter", - "version": "5.4.2", + "version": "5.4.8", "license": "MIT", "dependencies": { + "@types/blake2b": "^2.1.3", "@types/crypto-js": "^4.1.1", "axios": "^0.26.1", "axios-retry": "^3.5.1", + "blake2b": "^2.1.4", "bottleneck": "^2.19.5", "crypto-js": "^4.1.1", + "int64-buffer": "^1.0.1", + "js-encoding-utils": "^0.7.3", "lodash": "^4.17.21", "lucid-cardano": "^0.10.9" }, @@ -2698,6 +2702,11 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/blake2b": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@types/blake2b/-/blake2b-2.1.3.tgz", + "integrity": "sha512-MFCdX0MNxFBP/xEILO5Td0kv6nI7+Q2iRWZbTL/yzH2/eDVZS5Wd1LHdsmXClvsCyzqaZfHFzZaN6BUeUCfSDA==" + }, "node_modules/@types/crypto-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz", @@ -2915,6 +2924,11 @@ "is-retry-allowed": "^2.2.0" } }, + "node_modules/b4a": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz", + "integrity": "sha512-OnAYlL5b7LEkALw87fUVafQw5rVR9RjwGd4KUwNQ6DrrNmaVaUCgLipfVlzrPQ4tWOR9P0IXGNOx50jYCCdSJg==" + }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -3136,6 +3150,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/blake2b": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/blake2b/-/blake2b-2.1.4.tgz", + "integrity": "sha512-AyBuuJNI64gIvwx13qiICz6H6hpmjvYS5DGkG6jbXMOT8Z3WUJ3V1X0FlhIoT1b/5JtHE3ki+xjtMvu1nn+t9A==", + "dependencies": { + "blake2b-wasm": "^2.4.0", + "nanoassert": "^2.0.0" + } + }, + "node_modules/blake2b-wasm": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/blake2b-wasm/-/blake2b-wasm-2.4.0.tgz", + "integrity": "sha512-S1kwmW2ZhZFFFOghcx73+ZajEfKBqhP82JMssxtLVMxlaPea1p9uoLiUZ5WYyHn0KddwbLc+0vh4wR0KBNoT5w==", + "dependencies": { + "b4a": "^1.0.1", + "nanoassert": "^2.0.0" + } + }, "node_modules/bottleneck": { "version": "2.19.5", "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", @@ -4119,6 +4151,14 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, + "node_modules/int64-buffer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/int64-buffer/-/int64-buffer-1.0.1.tgz", + "integrity": "sha512-+3azY4pXrjAupJHU1V9uGERWlhoqNswJNji6aD/02xac7oxol508AsMC5lxKhEqyZeDFy3enq5OGWXF4u75hiw==", + "engines": { + "node": ">= 4.5.0" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -5960,6 +6000,11 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/js-encoding-utils": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/js-encoding-utils/-/js-encoding-utils-0.7.3.tgz", + "integrity": "sha512-cfjcyPOzkZ2esMAi6eAjuto7GiT6YpPan5xIeQyN/CFqFHTt1sdqP0PJPgzi3HqAqXKN9j9hduynkgwk+AAJOw==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -6194,6 +6239,11 @@ "url": "https://github.com/sponsors/raouldeheer" } }, + "node_modules/nanoassert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-2.0.0.tgz", + "integrity": "sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA==" + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", diff --git a/package.json b/package.json index 044721a..ec6e1df 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@indigo-labs/dexter", - "version": "5.4.8", + "version": "5.4.9", "license": "MIT", "author": "Zachary Sluder", "keywords": [ @@ -22,11 +22,15 @@ "prepublishOnly": "npm run test" }, "dependencies": { + "@types/blake2b": "^2.1.3", "@types/crypto-js": "^4.1.1", "axios": "^0.26.1", "axios-retry": "^3.5.1", + "blake2b": "^2.1.4", "bottleneck": "^2.19.5", "crypto-js": "^4.1.1", + "int64-buffer": "^1.0.1", + "js-encoding-utils": "^0.7.3", "lodash": "^4.17.21", "lucid-cardano": "^0.10.9" }, diff --git a/src/constants.ts b/src/constants.ts index e3722dd..c502033 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -18,6 +18,7 @@ export enum DatumParameterKey { /** * Swap/wallet info. */ + Address = 'Address', SenderPubKeyHash = 'SenderPubKeyHash', SenderStakingKeyHash = 'SenderStakingKeyHash', SenderKeyHashes = 'SenderKeyHashes', @@ -32,6 +33,10 @@ export enum DatumParameterKey { Expiration = 'Expiration', AllowPartialFill = 'AllowPartialFill', Direction = 'Direction', + FeePaymentKeyHash = 'FeePaymentKeyHash', + Beacon = 'Beacon', + Batcher = 'Batcher', + InToken = 'InToken', /** * Trading fees. @@ -41,6 +46,7 @@ export enum DatumParameterKey { DepositFee = 'DepositFee', ScooperFee = 'ScooperFee', BaseFee = 'BaseFee', + ExecutionFee = 'ExecutionFee', FeeSharingNumerator = 'FeeSharingNumerator', OpeningFee = 'OpeningFee', FinalFee = 'FinalFee', diff --git a/src/dex/api/spectrum-api.ts b/src/dex/api/spectrum-api.ts deleted file mode 100644 index 7c27a31..0000000 --- a/src/dex/api/spectrum-api.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { BaseApi } from './base-api'; -import { Asset, Token } from '../models/asset'; -import { LiquidityPool } from '../models/liquidity-pool'; -import axios, { AxiosInstance } from 'axios'; -import { RequestConfig } from '@app/types'; -import { appendSlash, tokensMatch } from '@app/utils'; -import { Spectrum } from '@dex/spectrum'; - -export class SpectrumApi extends BaseApi { - - protected readonly api: AxiosInstance; - protected readonly dex: Spectrum; - - constructor(dex: Spectrum, requestConfig: RequestConfig) { - super(); - - this.dex = dex; - this.api = axios.create({ - timeout: requestConfig.timeout, - baseURL: `${appendSlash(requestConfig.proxyUrl)}https://analytics-balanced.spectrum.fi/cardano`, - headers: { - 'Content-Type': 'application/json', - } - }); - } - - liquidityPools(assetA: Token, assetB?: Token): Promise { - return this.api.get('/front/pools', ).then((response: any) => { - return response.data.map((poolResponse: any) => { - const tokenA: Token = poolResponse.lockedX.asset.currencySymbol !== '' - ? new Asset(poolResponse.lockedX.asset.currencySymbol, Buffer.from(poolResponse.lockedX.asset.tokenName, 'utf8').toString('hex')) - : 'lovelace'; - const tokenB: Token = poolResponse.lockedY.asset.currencySymbol !== '' - ? new Asset(poolResponse.lockedY.asset.currencySymbol, Buffer.from(poolResponse.lockedY.asset.tokenName, 'utf8').toString('hex')) - : 'lovelace'; - - if (! tokensMatch(tokenA, assetA) || (assetB && ! tokensMatch(tokenB, assetB))) { - return undefined; - } - - let liquidityPool: LiquidityPool = new LiquidityPool( - Spectrum.identifier, - tokenA, - tokenB, - BigInt(poolResponse.lockedX.amount), - BigInt(poolResponse.lockedY.amount), - '', // Not supplied - this.dex.orderAddress, - this.dex.orderAddress, - ); - - const [poolNftPolicyId, poolNftName] = poolResponse.id.split('.'); - liquidityPool.poolNft = new Asset(poolNftPolicyId, Buffer.from(poolNftName, 'utf8').toString('hex')); - liquidityPool.lpToken = new Asset(poolResponse.lockedLQ.asset.currencySymbol, Buffer.from(poolResponse.lockedLQ.asset.tokenName, 'utf8').toString('hex')); - liquidityPool.poolFeePercent = (1 - (poolResponse.poolFeeNum / poolResponse.poolFeeDenum)) * 100; - liquidityPool.identifier = liquidityPool.lpToken.identifier(); - - return liquidityPool; - }).filter((pool: LiquidityPool | undefined) => pool !== undefined) as LiquidityPool[]; - }); - } - -} diff --git a/src/dex/api/splash-api.ts b/src/dex/api/splash-api.ts new file mode 100644 index 0000000..7a3d0d2 --- /dev/null +++ b/src/dex/api/splash-api.ts @@ -0,0 +1,77 @@ +import { BaseApi } from './base-api'; +import { Asset, Token } from '../models/asset'; +import { LiquidityPool } from '../models/liquidity-pool'; +import axios, { AxiosInstance } from 'axios'; +import { RequestConfig } from '@app/types'; +import { appendSlash } from '@app/utils'; +import { Splash } from '@dex/splash'; + +const MAX_INT: bigint = 9_223_372_036_854_775_807n; + +export class SplashApi extends BaseApi { + + protected readonly api: AxiosInstance; + protected readonly dex: Splash; + + constructor(dex: Splash, requestConfig: RequestConfig) { + super(); + + this.dex = dex; + + this.api = axios.create({ + timeout: requestConfig.timeout, + baseURL: `${appendSlash(requestConfig.proxyUrl)}https://api5.splash.trade/platform-api/v1/`, + withCredentials: false, + }); + } + + async liquidityPools(assetA: Token, assetB?: Token): Promise { + const assets: any = (await this.assets()).data['tokens']; + + return this.api.get('/pools/overview?verified=false&duplicated=false').then((response: any) => { + return response.data.map((pool: any) => this.liquidityPoolFromResponse(pool, assets)) as LiquidityPool[]; + }); + } + + private liquidityPoolFromResponse(poolData: any, assets: any): LiquidityPool { + poolData = poolData.pool; + + const tokenA: Token = poolData.x.asset === '.' + ? 'lovelace' + : new Asset(poolData.x.asset.split('.')[0], poolData.x.asset.split('.')[1]); + const tokenB = poolData.y.asset === '.' + ? 'lovelace' + : new Asset(poolData.y.asset.split('.')[0], poolData.y.asset.split('.')[1]); + + if (tokenA !== 'lovelace' && tokenA.identifier('.') in assets) { + tokenA.decimals = assets[tokenA.identifier('.')].decimals; + } + if (tokenB !== 'lovelace' && tokenB.identifier('.') in assets) { + tokenB.decimals = assets[tokenB.identifier('.')].decimals; + } + + const liquidityPool: LiquidityPool = new LiquidityPool( + Splash.identifier, + tokenA, + tokenB, + BigInt(poolData['x']['amount']) - BigInt(poolData['treasuryX']), + BigInt(poolData['y']['amount']) - BigInt(poolData['treasuryY']), + '', + '', + '', + ); + + const [lpTokenPolicyId, lpTokenAssetName] = poolData['lq']['asset'].split('.'); + + liquidityPool.lpToken = new Asset(lpTokenPolicyId, lpTokenAssetName); + liquidityPool.totalLpTokens = MAX_INT - BigInt(poolData['lq']['amount']); + liquidityPool.identifier = poolData['id']; + + return liquidityPool; + } + + private assets(): Promise { + return axios.get('https://spectrum.fi/cardano-token-list-v2.json'); + } + +} diff --git a/src/dex/api/teddyswap-api.ts b/src/dex/api/teddyswap-api.ts deleted file mode 100644 index 51fb920..0000000 --- a/src/dex/api/teddyswap-api.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { BaseApi } from './base-api'; -import { Asset, Token } from '../models/asset'; -import { LiquidityPool } from '../models/liquidity-pool'; -import axios, { AxiosInstance } from 'axios'; -import { RequestConfig } from '@app/types'; -import { appendSlash, tokensMatch } from '@app/utils'; -import { TeddySwap } from '@dex/teddyswap'; - -export class TeddyswapApi extends BaseApi { - - protected readonly api: AxiosInstance; - protected readonly dex: TeddySwap; - - constructor(dex: TeddySwap, requestConfig: RequestConfig) { - super(); - - this.dex = dex; - this.api = axios.create({ - timeout: requestConfig.timeout, - baseURL: `${appendSlash(requestConfig.proxyUrl)}https://analytics.teddyswap.org/v1`, - headers: { - 'Content-Type': 'application/json', - } - }); - } - - liquidityPools(assetA: Token, assetB?: Token): Promise { - return this.api.get('/front/pools', ).then((response: any) => { - return response.data.map((poolResponse: any) => { - const tokenA: Token = poolResponse.lockedX.asset.currencySymbol !== '' - ? new Asset(poolResponse.lockedX.asset.currencySymbol, Buffer.from(poolResponse.lockedX.asset.tokenName, 'utf8').toString('hex')) - : 'lovelace'; - const tokenB: Token = poolResponse.lockedY.asset.currencySymbol !== '' - ? new Asset(poolResponse.lockedY.asset.currencySymbol, Buffer.from(poolResponse.lockedY.asset.tokenName, 'utf8').toString('hex')) - : 'lovelace'; - - if (! tokensMatch(tokenA, assetA) || (assetB && ! tokensMatch(tokenB, assetB))) { - return undefined; - } - - let liquidityPool: LiquidityPool = new LiquidityPool( - TeddySwap.identifier, - tokenA, - tokenB, - BigInt(poolResponse.lockedX.amount), - BigInt(poolResponse.lockedY.amount), - '', // Not supplied - this.dex.orderAddress, - this.dex.orderAddress, - ); - const [poolNftPolicyId, poolNftName] = poolResponse.id.split('.'); - liquidityPool.poolNft = new Asset(poolNftPolicyId, Buffer.from(poolNftName, 'utf8').toString('hex')); - liquidityPool.lpToken = new Asset(poolResponse.lockedLQ.asset.currencySymbol, Buffer.from(poolResponse.lockedLQ.asset.tokenName, 'utf8').toString('hex')); - liquidityPool.poolFeePercent = (1 - (poolResponse.poolFeeNum / poolResponse.poolFeeDenum)) * 100; - liquidityPool.identifier = liquidityPool.lpToken.identifier(); - - return liquidityPool; - }).filter((pool: LiquidityPool | undefined) => pool !== undefined) as LiquidityPool[]; - }); - } - -} diff --git a/src/dex/base-dex.ts b/src/dex/base-dex.ts index 955410d..d990f2c 100644 --- a/src/dex/base-dex.ts +++ b/src/dex/base-dex.ts @@ -47,7 +47,7 @@ export abstract class BaseDex { /** * Craft a swap order for this DEX. */ - abstract buildSwapOrder(liquidityPool: LiquidityPool, swapParameters: DatumParameters, spendUtxos?: SpendUTxO[]): Promise; + abstract buildSwapOrder(liquidityPool: LiquidityPool, swapParameters: DatumParameters, spendUtxos?: SpendUTxO[], dataProvider?: BaseDataProvider): Promise; /** * Craft a swap order cancellation for this DEX. diff --git a/src/dex/definitions/spectrum/order.ts b/src/dex/definitions/spectrum/order.ts deleted file mode 100644 index 2b25d08..0000000 --- a/src/dex/definitions/spectrum/order.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { DatumParameterKey } from '@app/constants'; -import { DatumParameters, DefinitionField } from '@app/types'; - -export default { - constructor: 0, - fields: [ - { - constructor: 0, - fields: [ - { - bytes: DatumParameterKey.SwapInTokenPolicyId - }, - { - bytes: DatumParameterKey.SwapInTokenAssetName - } - ], - }, - { - constructor: 0, - fields: [ - { - bytes: DatumParameterKey.SwapOutTokenPolicyId - }, - { - bytes: DatumParameterKey.SwapOutTokenAssetName - } - ], - }, - { - constructor: 0, - fields: [ - { - bytes: DatumParameterKey.TokenPolicyId // Pool NFT - }, - { - bytes: DatumParameterKey.TokenAssetName - } - ], - }, - { - int: DatumParameterKey.LpFee - }, - { - int: DatumParameterKey.LpFeeNumerator // Execution fee numerator - }, - { - int: DatumParameterKey.LpFeeDenominator // Execution fee denominator - }, - { - bytes: DatumParameterKey.SenderPubKeyHash - }, - (field: DefinitionField, parameters: DatumParameters, shouldExtract: boolean = true) => { - if (! shouldExtract) { - const stakeKeyHash: string = parameters[DatumParameterKey.SenderStakingKeyHash] as string ?? null; - - if (! stakeKeyHash) return; - - return { - constructor: 0, - fields: [ - { - bytes: stakeKeyHash, - } - ], - }; - } - - if ('fields' in field) { - if (field.constructor === 1) return; - - if (field.fields.length > 0 && 'bytes' in field.fields[0]) { - parameters[DatumParameterKey.SenderStakingKeyHash] = field.fields[0].bytes; - } - } - - return; - }, - { - int: DatumParameterKey.SwapInAmount - }, - { - int: DatumParameterKey.MinReceive - } - ], -} diff --git a/src/dex/definitions/spectrum/pool.ts b/src/dex/definitions/spectrum/pool.ts deleted file mode 100644 index e59b246..0000000 --- a/src/dex/definitions/spectrum/pool.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { DatumParameterKey } from '@app/constants'; - -export default { - constructor: 0, - fields: [ - { - constructor: 0, - fields: [ - { - bytes: DatumParameterKey.TokenPolicyId // Pool NFT - }, - { - bytes: DatumParameterKey.TokenAssetName - } - ] - }, - { - constructor: 0, - fields: [ - { - bytes: DatumParameterKey.PoolAssetAPolicyId - }, - { - bytes: DatumParameterKey.PoolAssetAAssetName - } - ] - }, - { - constructor: 0, - fields: [ - { - bytes: DatumParameterKey.PoolAssetBPolicyId - }, - { - bytes: DatumParameterKey.PoolAssetBAssetName - } - ] - }, - { - constructor: 0, - fields: [ - { - bytes: DatumParameterKey.LpTokenPolicyId - }, - { - bytes: DatumParameterKey.LpTokenAssetName - } - ] - }, - { - int: DatumParameterKey.LpFee - }, - [ - { - bytes: DatumParameterKey.StakeAdminPolicy - } - ], - { - int: DatumParameterKey.LqBound - } - ] -} diff --git a/src/dex/definitions/splash/order.ts b/src/dex/definitions/splash/order.ts new file mode 100644 index 0000000..c7bf6a8 --- /dev/null +++ b/src/dex/definitions/splash/order.ts @@ -0,0 +1,116 @@ +import { DatumParameterKey } from '@app/constants'; +import { DatumParameters, DefinitionField } from '@app/types'; + +export default { + constructor: 0, + fields: [ + { + bytes: DatumParameterKey.Action + }, + { + bytes: DatumParameterKey.Beacon + }, + { + constructor: 0, + fields: [ + { + bytes: DatumParameterKey.SwapInTokenPolicyId + }, + { + bytes: DatumParameterKey.SwapInTokenAssetName + } + ] + }, + { + int: DatumParameterKey.SwapInAmount + }, + { + int: DatumParameterKey.BaseFee + }, + { + int: DatumParameterKey.MinReceive + }, + { + constructor: 0, + fields: [ + { + bytes: DatumParameterKey.SwapOutTokenPolicyId + }, + { + bytes: DatumParameterKey.SwapOutTokenAssetName + } + ] + }, + { + constructor: 0, + fields: [ + { + int: DatumParameterKey.LpFeeNumerator, + }, + { + int: DatumParameterKey.LpFeeDenominator, + } + ] + }, + { + int: DatumParameterKey.ExecutionFee + }, + { + constructor: 0, + fields: [ + { + constructor: 0, + fields: [ + { + bytes: DatumParameterKey.SenderPubKeyHash + } + ] + }, + { + constructor: 0, + fields: [ + { + constructor: 0, + fields: [ + (field: DefinitionField, parameters: DatumParameters, shouldExtract: boolean = true) => { + if (! shouldExtract) { + const stakeKeyHash: string = parameters[DatumParameterKey.SenderStakingKeyHash] as string ?? null; + + if (! stakeKeyHash) return; + + return { + constructor: 0, + fields: [ + { + bytes: stakeKeyHash, + } + ], + }; + } + + if ('fields' in field) { + if (field.constructor === 1) return; + + if (field.fields.length > 0 && 'bytes' in field.fields[0]) { + parameters[DatumParameterKey.SenderStakingKeyHash] = field.fields[0].bytes; + } + } + + return; + }, + ] + } + ] + } + ] + }, + { + bytes: DatumParameterKey.SenderPubKeyHash + }, + [ + { + bytes: DatumParameterKey.Batcher + } + ] + ] +} diff --git a/src/dex/definitions/teddyswap/pool.ts b/src/dex/definitions/splash/pool.ts similarity index 72% rename from src/dex/definitions/teddyswap/pool.ts rename to src/dex/definitions/splash/pool.ts index e59b246..ab6dbea 100644 --- a/src/dex/definitions/teddyswap/pool.ts +++ b/src/dex/definitions/splash/pool.ts @@ -1,4 +1,5 @@ import { DatumParameterKey } from '@app/constants'; +import { DatumParameters, DefinitionField } from '@app/types'; export default { constructor: 0, @@ -48,15 +49,19 @@ export default { ] }, { - int: DatumParameterKey.LpFee + int: DatumParameterKey.Unknown }, - [ - { - bytes: DatumParameterKey.StakeAdminPolicy - } - ], { - int: DatumParameterKey.LqBound - } + int: DatumParameterKey.Unknown + }, + { + int: DatumParameterKey.ReserveA + }, + { + int: DatumParameterKey.ReserveB + }, + (field: DefinitionField, parameters: DatumParameters, shouldExtract: boolean = true) => { + return; + }, ] } diff --git a/src/dex/definitions/teddyswap/order.ts b/src/dex/definitions/teddyswap/order.ts deleted file mode 100644 index 2b25d08..0000000 --- a/src/dex/definitions/teddyswap/order.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { DatumParameterKey } from '@app/constants'; -import { DatumParameters, DefinitionField } from '@app/types'; - -export default { - constructor: 0, - fields: [ - { - constructor: 0, - fields: [ - { - bytes: DatumParameterKey.SwapInTokenPolicyId - }, - { - bytes: DatumParameterKey.SwapInTokenAssetName - } - ], - }, - { - constructor: 0, - fields: [ - { - bytes: DatumParameterKey.SwapOutTokenPolicyId - }, - { - bytes: DatumParameterKey.SwapOutTokenAssetName - } - ], - }, - { - constructor: 0, - fields: [ - { - bytes: DatumParameterKey.TokenPolicyId // Pool NFT - }, - { - bytes: DatumParameterKey.TokenAssetName - } - ], - }, - { - int: DatumParameterKey.LpFee - }, - { - int: DatumParameterKey.LpFeeNumerator // Execution fee numerator - }, - { - int: DatumParameterKey.LpFeeDenominator // Execution fee denominator - }, - { - bytes: DatumParameterKey.SenderPubKeyHash - }, - (field: DefinitionField, parameters: DatumParameters, shouldExtract: boolean = true) => { - if (! shouldExtract) { - const stakeKeyHash: string = parameters[DatumParameterKey.SenderStakingKeyHash] as string ?? null; - - if (! stakeKeyHash) return; - - return { - constructor: 0, - fields: [ - { - bytes: stakeKeyHash, - } - ], - }; - } - - if ('fields' in field) { - if (field.constructor === 1) return; - - if (field.fields.length > 0 && 'bytes' in field.fields[0]) { - parameters[DatumParameterKey.SenderStakingKeyHash] = field.fields[0].bytes; - } - } - - return; - }, - { - int: DatumParameterKey.SwapInAmount - }, - { - int: DatumParameterKey.MinReceive - } - ], -} diff --git a/src/dex/logo/spectrum.png b/src/dex/logo/spectrum.png deleted file mode 100644 index c63b757b967a5ee744c033d95e2dc9193650dca8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 420 zcmeAS@N?(olHy`uVBq!ia0vp^8bGYc!3HGV7iFISQk(@Ik;M!Qe1}1p@p%4<6riAF ziEBhjaDG}zd16s2LwR|*US?i)adKios$PCk`s{Z$QVa}?hMq2tArY-_XDsw`aTICW zUn;J@x+z1*FjM#oXW4Amg9*aDY>PMCe4wo(!ILG>ZPyT5!)`uHC+R|hka*o`wVN|{ zKIZ(c^}KoVKjZX$k~55WJ+I8{T*Vr+V}YpnmLRtRqvSZoGpSZv8m9)#5PHKjy>p)0 z1;$?oj5kQWjAb!vP=3HA!61L2GQ&~Z=#78{s~po$tqaU$4L=Qk@@(K=)1SpVqxa2p z-5s+}Y;S1x*vB&GDc9d8@ivpZ3LUleGLEvY1>5teDJz#7QV|UEj2e0($`3f&sruqXzn8DN4 K&t;ucLK6V#Bc-eW diff --git a/src/dex/logo/splash.png b/src/dex/logo/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..ae883bb9f4552c85dbda019afb92fe410694d49e GIT binary patch literal 1163 zcmV;61a$j}P)QR$;7`ruQQwoh6RM5*{B zY9GWuU{TPgeP~f=VguDGK`BU~TB?*%6>2p|6)Qq%5V3`#rdpx+P#Tjo-MKTzhn?M> z-MuqAGaJQ)!```f&dzs#bMCq4uCP_#z_38XBEnxNXBg!yGqTM8<+E2STnW~rW<6Ks z1|miXSP&jp?Yh}V@i~VdeZ_z|j`Ah%$ff@bcE8?CE!a%RCTiw>EbFj909XMFSWb7t z8^8m61)ql1@HvJTeZXb!Ff$UYI`BRnVmRD_)ooZdViBywz_A6AXuIIO25=1Dyl(c* zIehDQ&@Qw%XkKCQgsM8ZQ6cF9A*+$@0-9d%|i4uuiHmTL(i(u zXTf#`gY!g86ERK*M6rd^YDYknxQ_4ZhBtuGBy4QjIgwj29O6xTl(%%E5cn6hyI2Xn zBh*PEDs5mc3tep?TlsF)yT^nFiNYV*Yai_itt!mcUPSpG<(_oF4Am26x>YBS>h-?X zM6Sc|5$^|+RMmHapU?_l*NzY}O~gP_!8Y)msuOm?C!zAF-aaP4c5bnsuuq5Dz)voY zqqSKgc_=eHI#mzTRc}|m5Ue`Jnv>qbHTEb6bp(KKuS^gQoFL-PGzEKwN1BhxjP*{? zyl=!~BiJLH(sdYfjF3B;V1+^?!zZj~?*vQ%Q0Xz|=>uF0<``wn4J$nsX%8Q5z!y=~ zsAEjsegR{wNWI-l^{9aA&6v+rU5hMO_gJl<>R}GmV;@rrsHf`M9ad?vod%kkCOegP z!fMWZpL3D}DH$cN>YY9zvGQp#Tk4It_6dcd_!+CDdVRqP_hF=ha2pcci;>M3Zpv4r zX|YLO4=m+S-JSc-66tZQnFHpr{3%`AcvTHcb`!q5hA$ghWpP`5-zt$kM_;4uIr`2U zSag=St?O16l(R*&JEZT$x8 zy~LFKkcMZn;r+u5n!6GGG+(~Y5jqvEPsp|HxB8|S_BLdJ_g21j+VS{3+>OWc>8iUz zE9GtdVc=KOt-2*7;8}KhGkLJ2&>>=vJ%GnncZl?e^<-$R04M!UaH#PLJG@$*l!dV*3OOVLw1uq?(}cL18f`o z38VX+phY&dwQY-bCskQ2ic%aNK*B0Qtysz2k)3XAsy@#pGgAhm9Q>2Rzae~{Sq}6H zt2OZkr@Y6Wa{vF7g9k8jsK0thVNC?qd|Vd6FAeW|vxvkT`-FvyOe1C29cR!E!`-1n(G7?qVpql@VHPWEA_VLEI#&yiF dEcNsFe*mm|gSR`Vp637n002ovPDHLkV1jahF696K literal 0 HcmV?d00001 diff --git a/src/dex/logo/teddyswap.png b/src/dex/logo/teddyswap.png deleted file mode 100644 index 2b2118e5f7db0bb01adc0a3b34453f3750f813f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10282 zcmdUV^;;X?6E4tVNh$6Yptu!2NP(ioDNaK0rdWX%cemiK#oaAPfue12E5%)kySv_e z?_Y8659it4dFISByJvRJyziNC4K)Qk02KfY4Gm98QC15L4V~+M0|yHw@$iXpL0xd| z74@9Z(167M8|Y~1nG`4?x|5cI3|jRcnqAbxOG{~0X*9IjSl|N;6Ag{EQ%P1@#~uAJ z>)TiGcJlFzNMCQwtbY)0PYB6@0B*1vP?h8hHPO;eh_k$Qf@Wk=!;W$yB;Cu-*~_F@ zYuk7U($cPVXa45QJpBrot~2ekENQ?`q5|BtWWo|uY2zwuJrfyMm;}c$54b2$b{WdI4}egsOBPY80Mv5gfa~Q zNXFnVFW_=GV_1Mlf_yv@G6q>{HZGYkAq{9S!MFd%6hk-|-sCq}-kI0)fvS3$t&)ed znEul;9p)03Bs5@E(qk>CRoyC3mG61@Z<$5bh>eSg9aOlKW;3|=aC@QbjEK!)+xva_yIK1XsPWdL5ctFoPU&ZQ78Z4i=i ze&VaVvWe<*_s2(`NRx{m`h@k<61HmlJVe;Clos|xsCZ;~gv0v|6HlUhcyQovuuML; z^bp`+z_t&}1{11c*9V#i?`X2X6ye%#H` zgZmQEpfqFJE%b|(l|x8QrS@1MvE--pfXFYH=~8ayEpp7FQ4@}DUA~V4&``o2-3duj z(poY!HtrRaH$u7nRcCuNMVpc+D_E0}I@sf$?;q0%{8o)+@-dC&bfdYc%|?2KlyqwJ z?`DU)@zOdxKCtxFM35i!XU6eaB_@XZ$h5(cjQXoWE!In}IbR=!|A@){$h_TE`ss35 z9_8&l=hG(A;S^`ne4q z--;c9@WJTA$FL^U>v}9YCwv63#r{cQ95NWclF_Z>^Q*xgkR2&c@B6hOnHbfFF+1N1AGxiz)4fE_=3h zi>;h$Mbi>R(~=kbKIdEVPcxcYi1(!*>21l5x)zvZ`a9dM`-t@b=jqzA)bbm_JaJRi z&P_Di!Px4#h%n3zvshwz*%$00bI_=o-;oVgr;n}=tX}?_`jzqB-}gjl_8a7h$9C1dHr2A9^~BWr)n{+g}{Bx zd+u&pl4-^`60Jho-te{`8AUBox5%am%Hcc6X>hWZFWr>GxEGuaN9&Z>``Hl=&%rEX zuyyYiA?4+2L-3glSX~5sqY&E&eq||I$a!kG6H3zj=;7R_x}sAiVE5Ij;qz?fPuuAo zAzTNw9sp1i+j!P^1QG~(yWcAORC@6y8*rg!ozv5_q~Vn=O)!ZKs-z07h#j#|eI5FP zXj`WcK>+`>5KPZ|WR!MY8DLw(`ZTUzcb(|?8n23k2wc%^7xC?M0GnEIN!tM_BYWw7 zS9*gh>&|w`WD*;U91XyU&j(~*Alu^XOW9!J$xmrt@lOhgE1jqM8`=`x<=c>>J8uDn z+D}7fww_vZD&h^tS{%G|}NgCp^NB31jkY2(Sr zBE}a;MYCp7AbbSie>&?yyQ$C$NA&zEAnUTz*Fh$u`@N%=iUGaQ8%A}Sxh0CEt4M>k8mM`98d zGvyTE+ZTXMMZzp)pi6~{4{Hy56rznG0Xd)Op?ym({+j*Q0lR-ECm9yB;*7j7Y`bNp zfw9O@Dp=?X*=ZI($v%$H5JV@;(BOsfL)fRb&$qQ2U#Qw#zD+t<$Kk2*$vf??7q`u# zQN6l(WYCi7Bnx>UMM_9po~5_0cmC<7CtJgdCx+zRX^5P_&bt2qIC)&!-(&yLgura$4! z7vDKM+fPx+mX!$U=sC;sYxkFNwuxm2FSsWA>sax{G()52~;NYNN z19{3smk>JO&{$q~?uJ1opXEF9#9~cvkOQs3&c;6R^&LB@tRCC1`YDF_RgLhOifqdO zaZm#}#HM$ylC!_NW(N?3pH))H_$r@cc;C`N9C|@My@s{sSY~%t^N2=_zIbrJx`&~e z_3^=BgXTbey#u+! zXQK?*rSGE&Re1x!1;cC|0$+sFHSI8sS`~eGb;Kzd-6SjA7T!XZUI>zWc(s*8nfyGO(ZHU@`KY=qDlmg|vV3&PMhuAd#MQ*AY5Zae3pG7n&LpJlCy8w}QjoXF-^p&PI)1}at@ADSc?hUKUkrw%w=_el}9!;(n3 z;@=5cf*L18pGS5(WT+<_uq3@#lnJSi=Rw?u3zgxo1tXba9V}=iEYIdiDd{cpvL5&b zsbbFA?m%I*?>y zDy?b+Xm9nn%yfoDG2SLC~=QkdtfShazB`sl%( z5y~`!elDN3Vo{#Med;393F|rEQGHyZNksZE^`dN)Id)ARLK#hyTr+ZGvaRhhV~GEK z%KZs`zs+hQ?!Ln!$@~o6V}pS3jnljvk4E3#CS&Zj&cV;%&0kqJ)(%^1s<-z|*k^;* zB8sGbcU#9*H9)}f#zO!h1b5OgO`kIEj`m*lh^cXpnrcHG{{g;O?~-x(6n^bhcz@WG zlHE6x*ErSDA^lKn#_MyCCvMWP z{M@G-!{u|3;~BDXTeE00-AuH`3N4i8j$DsvgR{d3?Z;{fHSqozwmI}Ee-bkBI{Hgn zH~4gOm6c=UNk4vnc-+T~lm4Hf-W3&~0_XidU0Tok^GK{Q9f_m4`P#L6DMQ~0jyBh} z5F`gi0jHqUA%}@GW-$xvxx~!hS@*vCxT&2Hy;JHGqP9?!2DSWD?)Oi=PHn8mPRACc z%c&hJc+c=2n?5hLO2$cwD1=U~hBKxHl)l}QdAqOA(fd6r2`dGbC4-3!nYl2I6Rb51 z%)oZm)>=<9CO#@12+NuSC|4a*ov72E8xa{7t2;hit|7Oq_GVd15$L14B%<|ev!S|* zw6o0I`AFm=Zsgx#gfVWL?bX+BSDlg(geli=k>fa%MhS^Pl}mD2Twh_Satz-;LfZPN z%8N1DVV5?0GFe+vnIB;6F-tqM!rxo193Rv5=ceX}Ze#=B&epespK9;%*HVnKGV^me zNm7uX#(&ikryfyc)MfHH{d}Ia<1rw{eexb9voc!u`&LW>nOaf%2bmJw^7i$$(Ap{) zicET{LwB0-63BFv3CBoXlDxoX&QaQEvxp( zniQ_#V|KYeOurl7S4CBg#my|{y$|QpKPvb|e1EKNOJ1>3b1R8iJ$$u>GJDa@f@DqD zxG<#wP(_HJ=PISgUd1TXYx~LGS8Cb)@kdcIpCl`hl!lAyKB|^)lvAn_l-#oICF#JX zvk__>l0Sb$l{N1d$tQwE`Rvs2r-QgL0;-}++6z)2UQxKE}|XhAtK&V zpV)f6D)zc|WAey(R$qAT?7q%{P{q5x zmIG($=hgeBugezV1Rl}e-pak9Jd39yH$R`ek1cHeyEu*Q9o})2IKJ-MmiRo~p|K52 z_b4M@<0gR+Zeeun0OTMPc?sMVU*9I9MJG&XCqg{K*zkCDNOP-w#wymRXN}Dg( zw^BwA#*qtmLgNpeCH^9rd1$DAmDaXWujGD)DlfZqhLoXjBSoLQJ&d zIMuFNLwi_oJ~5&<=4?F7SLmkXR}{rEtL!&~6uQ25kh0R71I^E0ofJSwN<|A!g&3A) zV5HJTgiUc%vA!pM5Jh0!U8mpUN|7M}m|@C#p;oLxG+A3{xk=l~ilJrA-Ru~q3=Q_S zBn`nV#(x;?yi0lCU|Hksxi56v1wTJK>RjY$OnqSuM&T%A1k@RGT!y-P2kw+lRc<7# z8Gz)FiE|s!@(O;tvKwvE20>mA(F)2*LxjHJA2p_{M+#Cd%oYM=djH0V*+m=Ilfl%F&3c`$o7 z&EAdrenkEaf!{}?afT|wU(RONw>eDW>-jie1=5J$)$qT&2v1AdG8TY#`c}PvyzqeE|os2icC(Xh~v8sLu>mg}MsdHMC>4OXeYuo8+#}uFuYKs8vU`c&^V=u~r zRp+%4ASSkWc;r&!UkT!71^HY6Wn-%hx(=DC`6CZMVm}6J+ zh@hEtYs+uCjxyB?u&SrX;gz2qsj*x`O$bJEpjQ%M`OE}2fO1$n5$twefq3JJ zI7hXkwuYyMD)*3zj&)M{R1QH#$zgpQ>Ew>^{r41{xGwK8Sad6xbtohD5VxdAm(e z%R>qDVB)lBs^P7#hR-Lbj*&{)B^ykyY#xVw{+iG+Ib-AU8yhp9^UC-ZUmHoZJ7d4u z7u)V?AmpdKST`4Rtcg=NmZ(j1Dr|V`={! zNj7LJ;5DG)m2(8*A?odwy;QIN~^@_Jj$Hs&)((ol;IEH!Px`Vu~G!l&D|GngF*P^1+ zcg)QZmITT(d8*U(y{%YoTX60;U+J)#Zl{Z58#gTu&927((DK9jd6znHj-Ja%!OtzXad5yHEupro5VEAlgB#2xA;>-JA{@wBlO~(hw zuSnaQ45qGZx$jBI`Uw0X=AEA1;mibAQz;$fK2wdc*j>%EyQvrk@+#9CRMdrKbx)F2 zYvY&18tisZWnvJ!S#t2+)YpW!yWVKV@{TBtGz(2?~p| z55%jiVafh^PH|6Pk0A|4o}rPfu2gzLUKjdul-t`plqZvU{E)-e8sy1dP;f)<$Aw=V z&{gj4N)A$&ZQ%ReIQ}_~ta-d@dj2f;oT4o+A;D}brMw-O!C{Y{4PXjk=q9Ec zqDfxbwbg|GX;*g_{ejN@&xS6`GpH26zM7~mN)1koJk2}jzxj8+Mw037G#r&OXIaFB z1>fYu)(%ZBvlL9PLn@6L2Y$g_6ZJTYN~TKzvIrk6U;2CR4z-F?Hhm4!Az+mht6Hix zF2W>9naPcD`SMTBitJc-H*?h`$vmiw?N63CFRPbio#+HMwr=%H#)$HonnY~6S?gk7 z>ME-uw_5@H{p;dNjV4a=$qHZ1%hb%iYHX4edI9R0&3%i)qQHiSu(~t!@DZ-*>b;Me z9+c9gYg#Kxl;VDBnP=8(or4JZ2{TTLARnlNKULTdX$Z0g4%(CY#)?Blc1^fOm{ckM zC(DtU6Cqsi)C_W@1`M(4B#_0lrJq}QN<#~WN&o)Fk8L$3eOW5fXn6-OMFk;Kxgzio zk6f*deD6tyw-$J?{$Y7!eosIRs7VhPvIJEzO|9TPq5(hhQV7|Iw**}r8(vdqtDDZE z*wnIKlLTr2CmMq2vYgTi-hO=2LK$x;Fu_Lq(|mdjK@^73N>AO&P(izkA6$wjw)EoV z1TD+(g}r(UX9KCMBCuWAK;l~Qlheu)nr5EbW}$hWA&Sf!=e!Rt1ujs@pnb#l8ct>} zWPk;v9UDkAvHm4aP{G8!+~`L{;hW&d>dYZ}q^)K{Nm&RW-^a+HO&%2}?o^FApIcTt zab0&rbZYMJmPhTnkri7dLb~D7qd@FJJPZ~-l#+C5`D;vvN5k&D;bT4la~?uR8ZJPX z`(Fo&1HCMvKSTZ_Kn45vK&s@8_+Mc1y$bMmmRn?_Xj9*_f z)qCr+Os_9FkRa2>6RG1F=dxI?7*ow}YgSIC??&6lwPxU=b!eN%Sb?_y(z6DQ$y1W zV@ButocXQJcZE^L_Dz-uoxh3j+8NQz6C*1PXheaG^$xk~g-)kM)$O)^y{)U;vQB;V zTu!aP?XONTco7|{>;`c!#)~ktyx^-Uw=sQ$Z6{B$yRC z(*LG3Kl#3>#lmBKSBeQQ0!foE5L+xMVIxaT8Mxn|G9Nn*m3+_}@fzQ25h$ts$g2jb zRFPSA251+*)resNxp-36peOeFElPMWPH$ndPiznGiIgYFdLNaT@*b);b z+V)&fhvegbE8V2dkANNLbhW?jP&7^6K`h_cOe_B0stGUq37K$4-3>|W6!sH}u)_w% zC*=ahV0hTx>4oMgk%1w>@ac{AG>bVe4ioJweKgxT_#N322LsZX%>+T#WJWPO;{eklf}2``K%1>k zdkC73of|vy<|_jDMaHiVTX54AfxtTCd-l%XJ%N$7HABz36%IRsm;&$Y0s_<@?c^M0rs{~rBH0u!X`W0 z&?gqituz``C>s}!Jtvyo}9)mpeT)U+(_+Qph{oH0C4rAVjUGgf)$6TU9QW4m{ z)Jyl?&U7&QyJUz>7~yo4gSZy*ZLRPJF?3-~SxArP)8c0p&hn+(cmw9e9N(Vn$b1qI zx1m727Ax@egq@dykW!Fk`bX-=zMDSLQeJyxaBco5_7gM-n$3FsZ(Q z;&$Fx%PK>gBg-b4vuo7QVXz_mn5s@|z$*j)t8jrc(z@yTpvc|GMHH~Sj{y7U;Hz2q_lH%EbmZltHBD$^OUKS- z_#^>dxu(c~C{ac5ES$UCF9V)E#+1e`J(F1p_NvxrYOT{eho&OjHkuGVmu4A1i)$NFg&CPQO zI+*$8kgW41fofqnU)H2#i_FX5gg_H6uEH~kQ2m-Cm&$%1XR>m54yf|_{+SSqqnhlG ztQ55Q_buV+0v8uahqSQe1)VtYE#bg!LuZde@IT@$W%bF36Gb4n!hF}UMO`8ekng(5 z+Dr#Dimb|aGeuk>%encLS#aW4V|HE8!I%jVa(XQaP~YOfIkWNG)(tR4`vw+@pQQ#~ zRW^I)W`ntCW};8?fP*AIw~JYSkde9SnT`LJ{bv!Cd@VAK=qH-c9^?4+0a8HzCmc}P zVO~n1O&Im7OUerd!^!3VA(2@E%2C;pC9+9HcPV)?FDZ@WXb<7ZNyt98zV+WvVTp+o z@&B8j<4xlmda?0CbBr`OpEGK2niAy%)r!u^ZTa4(Z9P~r)@3CdpM|_0>`E#cKWnBD zCJ?p5K{)C+qTW3@pqQEK7YyDY*VswjCWp4f=x1~lbtDpziIxP_GbG`xg+K7_>&Zj^ z@zZd=S0ZFG4=SD|!=;rTqmEbm`^n{P*{+84g%RoA`|F_3#EJ^*o}+Fv#?bx&pqw5B zGc}g4EiNEZ#iu=>#6q;@b%-Vv+nY+w%s<^YoZfe)V&@W}zaOQ^m(d}w3Z_#r^^6`) zd~g#8F#%6%wXfrFzVGcl^a;rRyU0|;u>n1O`n_#qjTF)OU7@GI5l=(;`m3pcmIi5w zd=kc0YRK52#+P_25ah-s|3y4Oic7j-Lat|sLWRXNFYh9{5QBE9KZG>({ zbgK+<_qphEi#S64YB%PYJoZ$)zTsd7(v^Y~GK~Jchw$reb;POucGWxB!!j_dgc4IP zp#SR#dkR^#KB=1icm1#}LKs^}0dSdDo;Q;;3zrF5UNzzueceNsz(zm`%WH?dn~uM} zZMoCm_n*wgT}R>pNV>;dR|Q3z_|asid;XAKq|5An(waU6fl1j(u6jT7l~q(F=PDHy zaB>xf1W8mXUO9@;M3S-WQ>c zU4I{4hlJb(-dDH1i6Rt!^FC1dIb%j((}yQwj5MqgaWW)>-a!yuLH$CnJ<{_rVo{0F ze?{23_V!7#SyO?p1tZ>?QXDfo|!aD?*x^LEMt# zUGpeNz)2{1RlRm6E3d(}`4EcQ00(ts9WX0Gj^AYS+=!$!OuKV(+bGKlY9p1q_rW__ zW^kML05Jvu>zEaaTX1wn4-*Iv@=)>mF&j_GPZY#}(J(%wfwgtVcobd$3(^P&eB zJNHYjcAiA_gK2w33nULrqeYqf_DJZnLV(Z6{8r%1cc!)|t`}O(x`4*G; zw`?IYVU2>B^1-%#1V0HrOcA7+<1A{bikmj%gy67sZDyaI)Jb&~Fk&7Y>@MA5VQ8gp zrm>P%hD95&+4`=DVEs37SG9{I*Z6IRpvx4_^7`RN`>b zkLD%8b5PpTaLcOpP_JQ<_^}i*tTnMs_xy$;*QkbKgkwSgjqxP>&4d{@R zj(_W{>v~Wf@J!ktNa5H+>&7_kc>nD@z?eqy3I!T81D^QRD0@ePspVSvKrnwlmHr=7 z&0mwTK^kqeEF@st??hx$D71q)ZG4W*B2X+Z+UaTKoXB!3_R)|+c5T(=5c5bLE-`-MbxJnC$Uxou1CAGhSd zKui)LWo++)*(AwSX@jN~JP$F58IQKdeVFEJvH=eRT;`yt#%i8rL?Nc8G*sg$M>NN!$M7kn{1|3ZyxX`?~Tz!b7K_Jw<`)v}RaHulT9I+Taz zap+e@zsO6naq&u9`!jpC9tB(nfUn;ckB}?252ptrftdL+0-Hs-jsZGXr<0m11!TNT zWDg66>YZyIgxOW-^B-Vf3}0TlRkMH9H}%;HTu`Z#eA|A~rhX|Hn5eXqLGFUX(V_mW z21gwC)AHt3?_AlH-AJcFQjB$tJzJ&n%O6dsehM01nqbmeDZ{L^iRl&kOBt_CJii>h z^Wy+g;lkMur>`kf)a13tf{^9TAat(Zc0$h$Hg-d`@V8vWIBQu4gp4q0SbVbCHR*_w z_@=B*fEVt7@&Wjxj84#pKHUFLd(6&ZI1It1v8TtUOWlw>WV8jUwGT~6PEEF2=2PJR E0c1s#4gdfE diff --git a/src/dex/spectrum.ts b/src/dex/splash.ts similarity index 53% rename from src/dex/spectrum.ts rename to src/dex/splash.ts index 1ec3910..d757f4e 100644 --- a/src/dex/spectrum.ts +++ b/src/dex/splash.ts @@ -15,39 +15,53 @@ import { import { DefinitionBuilder } from '@app/definition-builder'; import { AddressType, DatumParameterKey } from '@app/constants'; import { BaseApi } from '@dex/api/base-api'; -import pool from './definitions/spectrum/pool'; -import order from './definitions/spectrum/order'; -import { correspondingReserves, tokensMatch } from '@app/utils'; -import { SpectrumApi } from '@dex/api/spectrum-api'; -import { Script } from 'lucid-cardano'; +import pool from '@dex/definitions/splash/pool'; +import order from '@dex/definitions/splash/order'; +import { bytesToHex, correspondingReserves, hexToBytes, lucidUtils, tokensMatch } from '@app/utils'; +import { AddressDetails, Script } from 'lucid-cardano'; +import { Uint64BE } from 'int64-buffer'; +import blake2b from 'blake2b'; +import { SplashApi } from '@dex/api/splash-api'; const MAX_INT: bigint = 9_223_372_036_854_775_807n; +const EXECUTOR_FEE: bigint = 1100000n; +const WORST_ORDER_STEP_COST: bigint = 900000n; -export class Spectrum extends BaseDex { +export class Splash extends BaseDex { - public static readonly identifier: string = 'Spectrum'; - public readonly api: BaseApi; + public static readonly identifier: string = 'Splash'; + readonly api: BaseApi; /** * On-Chain constants. */ - public readonly orderAddress: string = 'addr1wynp362vmvr8jtc946d3a3utqgclfdl5y9d3kn849e359hsskr20n'; - public readonly cancelDatum: string = 'd8799f00000001ff'; + public readonly cancelDatum: string = 'd87980'; + public readonly orderScriptHash: string = '464eeee89f05aff787d40045af2a40a83fd96c513197d32fbc54ff02'; + public readonly batcherKey: string = '5cb2c968e5d1c7197a6ce7615967310a375545d9bc65063a964335b2'; public readonly orderScript: Script = { type: 'PlutusV2', - script: '5904f901000032323232323232323232323232323232323232323222253330143232323232323232323232323232323232323232323232323232323253330303370e90010010991919299981999b8753330333370e6eb4c0d0c0d403d2000148000520024800054cc090cdc399814805181a00e240042a66048664466ebcdd3981c0011ba730380013034004303400815330243370e666064444a666060002200426600666e00009200230380014800004d20041533024337126eb4c0d0c0d405800854cc0900044cccc8888cdc499b833370466e08cc0b403800c008004cdc019b823302d00e004483403ccdc100100099b8000648008c0d0078c0d0074dd6981a00b1bad303401b13322332303522533303200114a02a66607066ebcc0e400400c52889801181d0009ba90010023758606864606c606c606c606c606c002606a0286eb8c0d00614cc8cc0cc00452899191929981319b8f375c606c606e0046eb8c0d8c0dc0044cdc79bae3036002375c606c002606e04e606c002606603826666644444646466e24cdc099b81302900d375a00266e0ccdc10028020019814809a99981c191929981599b8f375c607660780046eb8c0ecc0f00044cdc79bae303b002375c60760026078058607600c26ea00144004dd424000606603a6eb4c0cc054004dd6981980c9bad303301853330313232325330253371e6eb8c0d4c0d8008dd7181a981b000899b8f375c606a0046eb8c0d4004c0d8098c0d4004c0c806c4cdc199b82001375a606402e66e04dd6981900b9bad30320181001337026604c01460620346604c00860620342c606600460540026ea8c0b8c0bc064dd599181718179818000981698170009817000998139bad302b00700a37566460566058605a002605460560026056002660486eb4c0a001401ccc88c8c8c94ccc0acc8c8c94ccc0b8cdc3a40040042646464a66606266e1d200000214a0266ebcdd38021ba70013034002302b001375400e2646464a66606266e1d200200214a0266ebcdd38021ba70013034002302b001375400e606200460500026ea8004400858c8c8c8c8c94ccc0bccdc3a4000004264646464a66606666e1d200000213232323253330373370e90010010b099ba548000004c0e8008c0c4004dd5000981a0008b181b00118168009baa001303000113374a9001015181900118148009baa001302c302d302e001302b302d0053756646464a66605866e1d2002002161533302c3371e6eb8c0b40040184c0b4c0b8c0bc01c58c0bc008c098004dd50009918151816000981498158019bae302700b302700a33022375a604c002008604c002604a002604a0206eb0c088008dd61810801181098108009810980f805180f800980f000980e800980e000980d800980d000980c800980c000980c002180b8008a4c2c4a66601600229000099980899baf300d3012001375200c6eb4c054c048dd5980a9809000a4000446660220040020062940cdd2a4000660026ea4008cc004dd48010042ba0489002232333004003375c601c0026eb8c038c03c004c03c004888cccc01000920002333300500248001d69bab00100323002375200244446601444a66600e002200a2a66601a66ebcc024c0380040184c010c044c0380044c008c03c00400555cfa5eb8155ce91299980299b8800248000584cc00c008004c0048894ccc014cdc3801240002600c00226600666e04009200230070012323002233002002001230022330020020015734ae855d1118011baa0015573c1', + script: '59042d01000033232323232323222323232232253330093232533300b0041323300100137566022602460246024602460246024601c6ea8008894ccc040004528099299980719baf00d300f301300214a226600600600260260022646464a66601c6014601e6ea80044c94ccc03cc030c040dd5000899191929998090038a99980900108008a5014a066ebcc020c04cdd5001180b180b980b980b980b980b980b980b980b980b98099baa00f3375e600860246ea8c010c048dd5180a98091baa00230043012375400260286eb0c050c054c054c044dd50028b1991191980080080191299980a8008a60103d87a80001323253330143375e6016602c6ea80080144cdd2a40006603000497ae0133004004001301900230170013758600a60206ea8010c04cc040dd50008b180098079baa0052301230130013322323300100100322533301200114a0264a66602066e3cdd7180a8010020a5113300300300130150013758602060226022602260226022602260226022601a6ea8004dd71808180898089808980898089808980898089808980898069baa0093001300c37540044601e00229309b2b19299980598050008a999804180218048008a51153330083005300900114a02c2c6ea8004c8c94ccc01cc010c020dd50028991919191919191919191919191919191919191919191919299981118128010991919191924c646600200200c44a6660500022930991980180198160011bae302a0015333022301f30233754010264646464a666052605800426464931929998141812800899192999816981800109924c64a666056605000226464a66606060660042649318140008b181880098169baa0021533302b3027001132323232323253330343037002149858dd6981a800981a8011bad30330013033002375a6062002605a6ea800858c0acdd50008b181700098151baa0031533302830240011533302b302a37540062930b0b18141baa002302100316302a001302a0023028001302437540102ca666042603c60446ea802c4c8c8c8c94ccc0a0c0ac00852616375a605200260520046eb4c09c004c08cdd50058b180d006180c8098b1bac30230013023002375c60420026042004603e002603e0046eb4c074004c074008c06c004c06c008c064004c064008dd6980b800980b8011bad30150013015002375a60260026026004602200260220046eb8c03c004c03c008dd7180680098049baa0051625333007300430083754002264646464a66601c60220042930b1bae300f001300f002375c601a00260126ea8004588c94ccc01cc0100044c8c94ccc030c03c00852616375c601a00260126ea800854ccc01cc00c0044c8c94ccc030c03c00852616375c601a00260126ea800858c01cdd50009b8748008dc3a4000ae6955ceaab9e5573eae815d0aba24c0126d8799fd87a9f581c96f5c1bee23481335ff4aece32fe1dfa1aa40a944a66d2d6edc9a9a5ffff0001', }; constructor(requestConfig: RequestConfig = {}) { super(); - this.api = new SpectrumApi(this, requestConfig); + this.api = new SplashApi(this, requestConfig); } public async liquidityPoolAddresses(provider: BaseDataProvider): Promise { return Promise.resolve([ 'addr1x94ec3t25egvhqy2n265xfhq882jxhkknurfe9ny4rl9k6dj764lvrxdayh2ux30fl0ktuh27csgmpevdu89jlxppvrst84slu', 'addr1x8nz307k3sr60gu0e47cmajssy4fmld7u493a4xztjrll0aj764lvrxdayh2ux30fl0ktuh27csgmpevdu89jlxppvrswgxsta', + 'addr1x8xw6pmmy8jcnpss6sg7za9c5lk2v9nflq684vzxyn70unaj764lvrxdayh2ux30fl0ktuh27csgmpevdu89jlxppvrs4tm5z7', + 'addr1x8cq97k066w4rd37wprvd4qrfxctzlyd6a67us2uv6hnen9j764lvrxdayh2ux30fl0ktuh27csgmpevdu89jlxppvrsgzvahe', + 'addr1xxcdveqw6g88w6cvwkf705xw30gflshu79ljc3ysrmmluadj764lvrxdayh2ux30fl0ktuh27csgmpevdu89jlxppvrscak26z', + 'addr1x8mql508pa9emlqfeh0g6lmlzfmauf55eq49zmta8ny7q04j764lvrxdayh2ux30fl0ktuh27csgmpevdu89jlxppvrs08z9dt', + 'addr1xxw7upjedpkr4wq839wf983jsnq3yg40l4cskzd7dy8eyndj764lvrxdayh2ux30fl0ktuh27csgmpevdu89jlxppvrsgddq74', + 'addr1x8zjsd5fagcwpysv2zklwu69kkqfcpwfvtxpz8s0r5kmakaj764lvrxdayh2ux30fl0ktuh27csgmpevdu89jlxppvrszgx7ef', + 'addr1x92m92cttwgpllls5y4c889splwgujjyy0eccl424nlezm9j764lvrxdayh2ux30fl0ktuh27csgmpevdu89jlxppvrswdesh2', + 'addr1xxg94wrfjcdsjncmsxtj0r87zk69e0jfl28n934sznu95tdj764lvrxdayh2ux30fl0ktuh27csgmpevdu89jlxppvrs2993lw', + 'addr1x9wnm7vle7al9q4aw63aw63wxz7aytnpc4h3gcjy0yufxwaj764lvrxdayh2ux30fl0ktuh27csgmpevdu89jlxppvrs84l0h4', ]); } @@ -80,7 +94,6 @@ export class Spectrum extends BaseDex { const relevantAssets = utxo.assetBalances.filter((assetBalance: AssetBalance) => { const assetName = assetBalance.asset === 'lovelace' ? 'lovelace' : assetBalance.asset.assetName; return !assetName?.toLowerCase()?.endsWith('_nft') - && !assetName?.toLowerCase()?.endsWith('_identity') && !assetName?.toLowerCase()?.endsWith('_lq'); }); @@ -89,26 +102,24 @@ export class Spectrum extends BaseDex { return Promise.resolve(undefined); } - // Could be ADA/X or X/X pool - const assetAIndex: number = relevantAssets.length === 2 ? 0 : 1; - const assetBIndex: number = relevantAssets.length === 2 ? 1 : 2; - - const liquidityPool: LiquidityPool = new LiquidityPool( - Spectrum.identifier, - relevantAssets[assetAIndex].asset, - relevantAssets[assetBIndex].asset, - relevantAssets[assetAIndex].quantity, - relevantAssets[assetBIndex].quantity, - utxo.address, - this.orderAddress, - this.orderAddress - ); - try { const builder: DefinitionBuilder = await new DefinitionBuilder().loadDefinition(pool); const datum: DefinitionField = await provider.datumValue(utxo.datumHash); const parameters: DatumParameters = builder.pullParameters(datum as DefinitionConstr); + const liquidityPool: LiquidityPool = new LiquidityPool( + Splash.identifier, + parameters.PoolAssetAPolicyId === '' + ? 'lovelace' + : new Asset(parameters.PoolAssetAPolicyId as string, parameters.PoolAssetAAssetName as string), + new Asset(parameters.PoolAssetBPolicyId as string, parameters.PoolAssetBAssetName as string), + BigInt(parameters.ReserveA as number), + BigInt(parameters.ReserveB as number), + utxo.address, + '', + '', + ); + const [lpTokenPolicyId, lpTokenAssetName] = typeof parameters.LpTokenPolicyId === 'string' && typeof parameters.LpTokenAssetName === 'string' ? [parameters.LpTokenPolicyId, parameters.LpTokenAssetName] : [null, null]; @@ -131,27 +142,22 @@ export class Spectrum extends BaseDex { liquidityPool.identifier = liquidityPool.lpToken.identifier(); liquidityPool.poolFeePercent = typeof parameters.LpFee === 'number' ? (1000 - parameters.LpFee) / 10 : 0.3; } catch (e) { - return liquidityPool; + return undefined; } - return liquidityPool; + return undefined; } estimatedGive(liquidityPool: LiquidityPool, swapOutToken: Token, swapOutAmount: bigint): bigint { const [reserveOut, reserveIn]: bigint[] = correspondingReserves(liquidityPool, swapOutToken); - const receive: bigint = (reserveIn * reserveOut) / (reserveOut - swapOutAmount) - reserveIn; - const swapFee: bigint = ((receive * BigInt(Math.floor(liquidityPool.poolFeePercent * 100))) + BigInt(10000) - 1n) / 10000n; - - return receive + swapFee; + return (reserveIn * reserveOut) / (reserveOut - swapOutAmount) - reserveIn; } estimatedReceive(liquidityPool: LiquidityPool, swapInToken: Token, swapInAmount: bigint): bigint { const [reserveIn, reserveOut]: bigint[] = correspondingReserves(liquidityPool, swapInToken); - const swapFee: bigint = ((swapInAmount * BigInt(Math.floor(liquidityPool.poolFeePercent * 100))) + BigInt(10000) - 1n) / 10000n; - - return reserveOut - (reserveIn * reserveOut) / (reserveIn + swapInAmount - swapFee); + return reserveOut - (reserveIn * reserveOut) / (reserveIn + swapInAmount); } priceImpactPercent(liquidityPool: LiquidityPool, swapInToken: Token, swapInAmount: bigint): number { @@ -162,7 +168,7 @@ export class Spectrum extends BaseDex { return (1 - (Number(reserveIn) / Number(reserveIn + swapInAmount))) * 100; } - public async buildSwapOrder(liquidityPool: LiquidityPool, swapParameters: DatumParameters, spendUtxos: SpendUTxO[] = []): Promise { + public async buildSwapOrder(liquidityPool: LiquidityPool, swapParameters: DatumParameters, spendUtxos: SpendUTxO[] = [], dataProvider?: BaseDataProvider): Promise { const batcherFee: SwapFee | undefined = this.swapOrderFees().find((fee: SwapFee) => fee.id === 'batcherFee'); const deposit: SwapFee | undefined = this.swapOrderFees().find((fee: SwapFee) => fee.id === 'deposit'); const minReceive = swapParameters.MinReceive as bigint; @@ -170,10 +176,18 @@ export class Spectrum extends BaseDex { if (! batcherFee || ! deposit || ! minReceive) { return Promise.reject('Parameters for datum are not set.'); } - if (! liquidityPool.poolNft) { - return Promise.reject('Pool NFT is required.'); + if (! dataProvider) { + return Promise.reject('Data provider is required.'); } + const walletUtxos: UTxO[] = await dataProvider.utxos( + swapParameters[DatumParameterKey.Address] as string, + swapParameters[DatumParameterKey.SwapInTokenPolicyId] !== '' + ? new Asset(DatumParameterKey.SwapInTokenPolicyId, DatumParameterKey.SwapInTokenAssetName) + : undefined + ); + const firstUtxo: UTxO = walletUtxos[0]; + const decimalToFractionalImproved = (decimalValue: bigint | number): [bigint, bigint] => { const [whole, decimals = ''] = decimalValue.toString()?.split('.'); let truncatedDecimals = decimals.slice(0, 15); @@ -182,17 +196,24 @@ export class Spectrum extends BaseDex { return [numerator, denominator]; } - const batcherFeeForToken = Number(batcherFee.value) / Number(minReceive); - const [numerator, denominator] = decimalToFractionalImproved(batcherFeeForToken); - const lpfee: bigint = BigInt(1000 - Math.floor(liquidityPool.poolFeePercent * 10)); + const swapOutToken: Token = swapParameters.SwapOutTokenPolicyId === 'lovelace' + ? 'lovelace' + : new Asset(swapParameters.SwapOutTokenPolicyId as string, swapParameters.SwapOutTokenAssetName as string); + + const outDecimals: number = swapOutToken === 'lovelace' + ? 6 + : (tokensMatch(swapOutToken, liquidityPool.assetA)) ? (liquidityPool.assetA as Asset).decimals : (liquidityPool.assetB as Asset).decimals; + const [numerator, denominator] = decimalToFractionalImproved(Number(minReceive) / 10**outDecimals); swapParameters = { ...swapParameters, - [DatumParameterKey.TokenPolicyId]: liquidityPool.poolNft.policyId, - [DatumParameterKey.TokenAssetName]: liquidityPool.poolNft.nameHex, - [DatumParameterKey.LpFee]: lpfee, + [DatumParameterKey.Action]: '00', + [DatumParameterKey.BaseFee]: WORST_ORDER_STEP_COST, + [DatumParameterKey.ExecutionFee]: EXECUTOR_FEE, [DatumParameterKey.LpFeeNumerator]: numerator, [DatumParameterKey.LpFeeDenominator]: denominator, + [DatumParameterKey.Beacon]: bytesToHex(Uint8Array.from(new Array(28).fill(0))), + [DatumParameterKey.Batcher]: this.batcherKey, }; const datumBuilder: DefinitionBuilder = new DefinitionBuilder(); @@ -200,9 +221,26 @@ export class Spectrum extends BaseDex { builder.pushParameters(swapParameters); }); + const hash: string = blake2b(28).update(hexToBytes(datumBuilder.getCbor())).digest('hex'); + + swapParameters.Beacon = this.getBeacon(firstUtxo, hash); + + await datumBuilder.loadDefinition(order).then((builder: DefinitionBuilder) => { + builder.pushParameters(swapParameters); + }); + return [ this.buildSwapOrderPayment(swapParameters, { - address: this.orderAddress, + address: lucidUtils.credentialToAddress( + { + type: 'Script', + hash: this.orderScriptHash, + }, + { + type: 'Key', + hash: swapParameters.SenderStakingKeyHash as string, + }, + ), addressType: AddressType.Contract, assetBalances: [ { @@ -212,14 +250,16 @@ export class Spectrum extends BaseDex { ], datum: datumBuilder.getCbor(), isInlineDatum: true, - spendUtxos: spendUtxos, + spendUtxos: spendUtxos.concat({ utxo: firstUtxo }), }), ]; } public async buildCancelSwapOrder(txOutputs: UTxO[], returnAddress: string): Promise { const relevantUtxo: UTxO | undefined = txOutputs.find((utxo: UTxO) => { - return utxo.address === this.orderAddress; + const addressDetails: AddressDetails | undefined = lucidUtils.getAddressDetails(utxo.address); + + return (addressDetails.paymentCredential?.hash ?? '') === this.orderScriptHash; }); if (! relevantUtxo) { @@ -266,4 +306,15 @@ export class Spectrum extends BaseDex { }, ]; } + + private getBeacon(utxo: UTxO, datumHash: string) { + return blake2b(28).update( + Uint8Array.from([ + ...hexToBytes(utxo.txHash), + ...new Uint64BE(Number(utxo.outputIndex)).toArray(), + ...new Uint64BE(0).toArray(), + ...hexToBytes(datumHash), + ]) + ).digest('hex'); + } } diff --git a/src/dex/teddyswap.ts b/src/dex/teddyswap.ts deleted file mode 100644 index f818c8d..0000000 --- a/src/dex/teddyswap.ts +++ /dev/null @@ -1,280 +0,0 @@ -import { LiquidityPool } from './models/liquidity-pool'; -import { BaseDataProvider } from '@providers/data/base-data-provider'; -import { Asset, Token } from './models/asset'; -import { BaseDex } from './base-dex'; -import { - AssetBalance, - DatumParameters, - DefinitionConstr, - DefinitionField, - PayToAddress, - RequestConfig, SpendUTxO, - SwapFee, - UTxO -} from '@app/types'; -import { DefinitionBuilder } from '@app/definition-builder'; -import { AddressType, DatumParameterKey } from '@app/constants'; -import { BaseApi } from '@dex/api/base-api'; -import pool from './definitions/teddyswap/pool'; -import order from './definitions/teddyswap/order'; -import { correspondingReserves, tokensMatch } from '@app/utils'; -import { TeddyswapApi } from '@dex/api/teddyswap-api'; -import { Script } from 'lucid-cardano'; - -const MAX_INT: bigint = 9_223_372_036_854_775_807n; - -export class TeddySwap extends BaseDex { - - public static readonly identifier: string = 'TeddySwap'; - public readonly api: BaseApi; - - /** - * On-Chain constants. - */ - public readonly orderAddress: string = 'addr1z99tz7hungv6furtdl3zn72sree86wtghlcr4jc637r2eadkp2avt5gp297dnxhxcmy6kkptepsr5pa409qa7gf8stzs0706a3'; - public readonly cancelDatum: string = 'd8799f00000001ff'; - public readonly orderScript: Script = { - type: 'PlutusV2', - script: '5905090100003232323232323232323232323232323232323232323222253330153232323232323232323232323232323232323232323232323232323253330313370e90010010991919299812a99981a19b8753330343370e6eb4c0d4c0d803d2000148000520024800054cc094cdc399815005181a80e240042a6604a664466ebcdd3981c8011ba730390013035004303500815330253370e666066444a666062002200426600666e00009200230390014800004d20041533025337126eb4c0d4c0d805800854cc0940044cccc8888cdc499b833370466e08cc0b803800c008004cdc019b823302e00e004483403ccdc100100099b8000648008c0d4078c0d4074dd6981a80b1bad303501b13322332303622533303300114a02a66607266ebcc0e800400c52889801181d8009ba90010023758606a64606e606e606e606e606e002606c0286eb8c0d40604cdc3811811a9991981a0008a513232325330273371e6eb8c0dcc0e0008dd7181b981c000899b8f375c606e0046eb8c0dc004c0e00a0c0dc004c0d00704ccccc88888c8c8cdc499b8133702605401a6eb4004cdc199b82005004003302a0135333039323253302c3371e6eb8c0f0c0f4008dd7181e181e800899b8f375c60780046eb8c0f0004c0f40b4c0f00184dd400288009ba848000c0d0074dd6981a00a8009bad3034019375a6068030a666064646464a6604c66e3cdd7181b181b8011bae3036303700113371e6eb8c0d8008dd7181b000981b813981b000981980d899b83337040026eb4c0cc05ccdc09bad3033017375a6066030200266e04cc09c028c0c8068cc09c010c0c806858c0d0008c0ac004dd51817981800c9bab32302f30303031001302e302f001302f00133028375a605800e0146eacc8c0b0c0b4c0b8004c0acc0b0004c0b0004cc094dd6981480280399911919192999816191919299981799b87480080084c8c8c94ccc0c8cdc3a400000429404cdd79ba7004374e002606a00460580026ea801c4c8c8c94ccc0c8cdc3a400400429404cdd79ba7004374e002606a00460580026ea801cc0c8008c0a4004dd500088010b1919191919299981819b87480000084c8c8c8c94ccc0d0cdc3a4000004264646464a66607066e1d20020021613374a9000000981d80118190009baa0013035001163037002302e00137540026062002266e95200202b3033002302a0013754002605a605c605e0026058605c00a6eacc8c8c94ccc0b4cdc3a40040042c2a66605a66e3cdd7181700080309817181798180038b181800118138009baa00132302b302d001302a302c003375c60500166050014660466eb4c09c004010c09c004c098004c098040dd618118011bac3022002302230220013022302000a3020001301f001301e001301d001301c001301b001301a00130190013019004301800114985920aca597c9282533300b0011480004ccc044cdd7980698090009ba9006375a602a60246eacc054c04800520002233301100200100314a066e952000330013752004660026ea40080215d0245002232333004003375c601c0026eb8c038c03c004c03c004888cccc01000920002333300500248001d69bab00100323002375200244446601444a66600e002200a2a66601a66ebcc024c0380040184c010c044c0380044c008c03c00400555cfa5eb8155ce91299980299b8800248000584cc00c008004c0048894ccc014cdc3801240002600c00226600666e04009200230070012323002233002002001230022330020020015734ae855d1118011baa0015573c1', - }; - - constructor(requestConfig: RequestConfig = {}) { - super(); - - this.api = new TeddyswapApi(this, requestConfig); - } - - public async liquidityPoolAddresses(provider: BaseDataProvider): Promise { - return Promise.resolve([ - 'addr1zy5th50h46anh3v7zdvh7ve6amac7k4h3mdfvt0p6czm8zp5hu6t748dfdd6cxlxxssyqez4wqwcrq44crfgkltqh2cqcwcjyr', - 'addr1zy5th50h46anh3v7zdvh7ve6amac7k4h3mdfvt0p6czm8zqgdzhkv23nm3v7tanurzu8v5vll365n7hq8f26937hatlqnv5cpz', - 'addr1zy5th50h46anh3v7zdvh7ve6amac7k4h3mdfvt0p6czm8z8rxrld450e6c360mu72ru7u8zz0602px3esxykcx87f9ns2tytsd', - 'addr1zy5th50h46anh3v7zdvh7ve6amac7k4h3mdfvt0p6czm8zz6mve63ntrqp7yxgkk395rngtzdmzdjzzuzdkdks0afwqsmdsegq', - 'addr1zy5th50h46anh3v7zdvh7ve6amac7k4h3mdfvt0p6czm8zr0vp2360e2j2gve54sxsheawjd6s6we2d25xl96a3r0jdqzvyqkl', - 'addr1zy5th50h46anh3v7zdvh7ve6amac7k4h3mdfvt0p6czm8zzlsgmhduch9juwcjf6vjqeht0jv2g2mlz86wqh42h8akdqglnguu', - 'addr1zy5th50h46anh3v7zdvh7ve6amac7k4h3mdfvt0p6czm8zxk96389hhwyhv0t07gh89wqnaqg9cqkwsz4esd9sm562rs55tl66', - 'addr1zy5th50h46anh3v7zdvh7ve6amac7k4h3mdfvt0p6czm8z9re630pc4dzmhtku8276tyq0glgn53h93vw5rl9e6w4g8su86xvk', - 'addr1zy5th50h46anh3v7zdvh7ve6amac7k4h3mdfvt0p6czm8zpczswng09euafg44jclrg5tm7xg260qzyavu9dysz8g3js7pzqla', - 'addr1zy5th50h46anh3v7zdvh7ve6amac7k4h3mdfvt0p6czm8z92v2k4gz85r5rq035n2llzemqvcz70h7hdr3njur05y6nsmrsjpe', - 'addr1zy5th50h46anh3v7zdvh7ve6amac7k4h3mdfvt0p6czm8zxn5qy8sn2d7wtdtvjcsv7v0h7u9zsleljxv3nschr5sj3sla73t7', - 'addr1zy5th50h46anh3v7zdvh7ve6amac7k4h3mdfvt0p6czm8zzw03em2wpuy6t66rx4hqmggelr8r2whwru8uuptxzwdlfsss26rc', - 'addr1zy5th50h46anh3v7zdvh7ve6amac7k4h3mdfvt0p6czm8zphr7r6v67asj5jc5w5uapfapv0u9433m3v9aag9w46spaqc60ygw', - 'addr1zy5th50h46anh3v7zdvh7ve6amac7k4h3mdfvt0p6czm8zrlxa5g3cwp6thfvzwhd9s4vcjjdwttsss65l09dum7g9rs0mr8px', - ]); - } - - async liquidityPools(provider: BaseDataProvider): Promise { - const poolAddresses: string[] = await this.liquidityPoolAddresses(provider); - - const addressPromises: Promise[] = poolAddresses.map(async (address: string) => { - const utxos: UTxO[] = await provider.utxos(address); - - return await Promise.all( - utxos.map(async (utxo: UTxO) => { - return await this.liquidityPoolFromUtxo(provider, utxo); - }) - ).then((liquidityPools: (LiquidityPool | undefined)[]) => { - return liquidityPools.filter((liquidityPool?: LiquidityPool) => { - return liquidityPool !== undefined; - }) as LiquidityPool[]; - }); - - }); - - return Promise.all(addressPromises).then((liquidityPools: Awaited[]) => liquidityPools.flat()); - } - - public async liquidityPoolFromUtxo(provider: BaseDataProvider, utxo: UTxO): Promise { - if (!utxo.datumHash) { - return Promise.resolve(undefined); - } - - const relevantAssets = utxo.assetBalances.filter((assetBalance: AssetBalance) => { - const assetName = assetBalance.asset === 'lovelace' ? 'lovelace' : assetBalance.asset.assetName; - return !assetName?.toLowerCase()?.endsWith('_nft') - && !assetName?.toLowerCase()?.endsWith('_identity') - && !assetName?.toLowerCase()?.endsWith('_lp'); - }); - - // Irrelevant UTxO - if (![2, 3].includes(relevantAssets.length)) { - return Promise.resolve(undefined); - } - - // Could be ADA/X or X/X pool - const assetAIndex: number = relevantAssets.length === 2 ? 0 : 1; - const assetBIndex: number = relevantAssets.length === 2 ? 1 : 2; - - const liquidityPool: LiquidityPool = new LiquidityPool( - TeddySwap.identifier, - relevantAssets[assetAIndex].asset, - relevantAssets[assetBIndex].asset, - relevantAssets[assetAIndex].quantity, - relevantAssets[assetBIndex].quantity, - utxo.address, - this.orderAddress, - this.orderAddress - ); - - try { - const builder: DefinitionBuilder = await new DefinitionBuilder().loadDefinition(pool); - const datum: DefinitionField = await provider.datumValue(utxo.datumHash); - const parameters: DatumParameters = builder.pullParameters(datum as DefinitionConstr); - - const [lpTokenPolicyId, lpTokenAssetName] = typeof parameters.LpTokenPolicyId === 'string' && typeof parameters.LpTokenAssetName === 'string' - ? [parameters.LpTokenPolicyId, parameters.LpTokenAssetName] - : [null, null]; - const lpTokenBalance: AssetBalance | undefined = utxo.assetBalances.find((assetBalance: AssetBalance) => { - return assetBalance.asset !== 'lovelace' - && assetBalance.asset.policyId === lpTokenPolicyId - && assetBalance.asset.nameHex === lpTokenAssetName; - });const nftToken: Asset | undefined = utxo.assetBalances.find((assetBalance) => { - return (assetBalance.asset as Asset).assetName?.toLowerCase()?.endsWith('_identity'); - })?.asset as Asset | undefined; - - if (! lpTokenBalance || ! nftToken) { - return Promise.resolve(undefined); - } - - liquidityPool.poolNft = nftToken; - liquidityPool.lpToken = lpTokenBalance.asset as Asset; - liquidityPool.totalLpTokens = MAX_INT - lpTokenBalance.quantity; - liquidityPool.identifier = liquidityPool.lpToken.identifier(); - liquidityPool.poolFeePercent = typeof parameters.LpFee === 'number' ? (1000 - parameters.LpFee) / 10 : 0.3; - } catch (e) { - return liquidityPool; - } - - return liquidityPool; - } - - estimatedGive(liquidityPool: LiquidityPool, swapOutToken: Token, swapOutAmount: bigint): bigint { - const [reserveOut, reserveIn]: bigint[] = correspondingReserves(liquidityPool, swapOutToken); - - const receive: bigint = (reserveIn * reserveOut) / (reserveOut - swapOutAmount) - reserveIn; - const swapFee: bigint = ((receive * BigInt(Math.floor(liquidityPool.poolFeePercent * 100))) + BigInt(10000) - 1n) / 10000n; - - return receive + swapFee; - } - - estimatedReceive(liquidityPool: LiquidityPool, swapInToken: Token, swapInAmount: bigint): bigint { - const [reserveIn, reserveOut]: bigint[] = correspondingReserves(liquidityPool, swapInToken); - - const swapFee: bigint = ((swapInAmount * BigInt(Math.floor(liquidityPool.poolFeePercent * 100))) + BigInt(10000) - 1n) / 10000n; - - return reserveOut - (reserveIn * reserveOut) / (reserveIn + swapInAmount - swapFee); - } - - priceImpactPercent(liquidityPool: LiquidityPool, swapInToken: Token, swapInAmount: bigint): number { - const reserveIn: bigint = tokensMatch(swapInToken, liquidityPool.assetA) - ? liquidityPool.reserveA - : liquidityPool.reserveB; - - return (1 - (Number(reserveIn) / Number(reserveIn + swapInAmount))) * 100; - } - - public async buildSwapOrder(liquidityPool: LiquidityPool, swapParameters: DatumParameters, spendUtxos: SpendUTxO[] = []): Promise { - const batcherFee: SwapFee | undefined = this.swapOrderFees().find((fee: SwapFee) => fee.id === 'batcherFee'); - const deposit: SwapFee | undefined = this.swapOrderFees().find((fee: SwapFee) => fee.id === 'deposit'); - const minReceive = swapParameters.MinReceive as bigint; - - if (!batcherFee || !deposit || !minReceive) { - return Promise.reject('Parameters for datum are not set.'); - } - if (! liquidityPool.poolNft) { - return Promise.reject('Pool NFT is required.'); - } - - const decimalToFractionalImproved = (decimalValue: bigint | number): [bigint, bigint] => { - const [whole, decimals = ''] = decimalValue.toString()?.split('.'); - let truncatedDecimals = decimals.slice(0, 15); - const denominator: bigint = BigInt(10 ** truncatedDecimals.length); - const numerator = BigInt(whole) * denominator + BigInt(decimals); - return [numerator, denominator]; - } - - const batcherFeeForToken = Number(batcherFee.value) / Number(minReceive); - const [numerator, denominator] = decimalToFractionalImproved(batcherFeeForToken); - const lpfee: bigint = BigInt(1000 - Math.floor(liquidityPool.poolFeePercent * 10)); - - swapParameters = { - ...swapParameters, - [DatumParameterKey.TokenPolicyId]: liquidityPool.poolNft.policyId, - [DatumParameterKey.TokenAssetName]: liquidityPool.poolNft.nameHex, - [DatumParameterKey.LpFee]: lpfee, - [DatumParameterKey.LpFeeNumerator]: numerator, - [DatumParameterKey.LpFeeDenominator]: denominator, - }; - - const datumBuilder: DefinitionBuilder = new DefinitionBuilder(); - await datumBuilder.loadDefinition(order).then((builder: DefinitionBuilder) => { - builder.pushParameters(swapParameters); - }); - - return [ - this.buildSwapOrderPayment(swapParameters, { - address: this.orderAddress, - addressType: AddressType.Contract, - assetBalances: [ - { - asset: 'lovelace', - quantity: batcherFee?.value + deposit.value, - }, - ], - datum: datumBuilder.getCbor(), - isInlineDatum: true, - spendUtxos: spendUtxos, - }), - ]; - } - - public async buildCancelSwapOrder(txOutputs: UTxO[], returnAddress: string): Promise { - const relevantUtxo: UTxO | undefined = txOutputs.find((utxo: UTxO) => { - return utxo.address === this.orderAddress; - }); - - if (! relevantUtxo) { - return Promise.reject('Unable to find relevant UTxO for cancelling the swap order.'); - } - - return [ - { - address: returnAddress, - addressType: AddressType.Base, - assetBalances: relevantUtxo.assetBalances, - isInlineDatum: false, - spendUtxos: [{ - utxo: relevantUtxo, - redeemer: this.cancelDatum, - validator: this.orderScript, - signer: returnAddress, - }], - } - ]; - } - - public swapOrderFees(): SwapFee[] { - const networkFee: number = 0.5; - const reward: number = 1; - const minNitro: number = 1.2; - const batcherFee: number = (reward + networkFee) * minNitro; - const batcherFeeInAda: bigint = BigInt(Math.round(batcherFee * 10 ** 6)); - - return [ - { - id: 'batcherFee', - title: 'Batcher Fee', - description: 'Fee paid for the service of off-chain batcher to process transactions.', - value: batcherFeeInAda, - isReturned: false, - }, - { - id: 'deposit', - title: 'Deposit', - description: 'This amount of ADA will be held as minimum UTxO ADA and will be returned when your order is processed or cancelled.', - value: 2_000000n, - isReturned: true, - }, - ]; - } -} diff --git a/src/dexter.ts b/src/dexter.ts index 69b6e6f..b1bcd42 100644 --- a/src/dexter.ts +++ b/src/dexter.ts @@ -15,12 +15,11 @@ import { FetchRequest } from '@requests/fetch-request'; import axios from 'axios'; import axiosRetry from 'axios-retry'; import { SplitSwapRequest } from '@requests/split-swap-request'; -import { TeddySwap } from '@dex/teddyswap'; -import { Spectrum } from '@dex/spectrum'; import { SplitCancelSwapRequest } from '@requests/split-cancel-swap-request'; import { SundaeSwapV3 } from '@dex/sundaeswap-v3'; import { MinswapV2 } from '@dex/minswap-v2'; import { WingRidersV2 } from '@dex/wingriders-v2'; +import { Splash } from '@dex/splash'; export class Dexter { @@ -68,8 +67,7 @@ export class Dexter { [WingRiders.identifier]: new WingRiders(this.requestConfig), [WingRidersV2.identifier]: new WingRidersV2(this.requestConfig), [VyFinance.identifier]: new VyFinance(this.requestConfig), - [TeddySwap.identifier]: new TeddySwap(this.requestConfig), - [Spectrum.identifier]: new Spectrum(this.requestConfig), + [Splash.identifier]: new Splash(this.requestConfig), }; } diff --git a/src/index.ts b/src/index.ts index c3554fd..f75b32c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -47,5 +47,4 @@ export * from './dex/muesliswap'; export * from './dex/wingriders'; export * from './dex/wingriders-v2'; export * from './dex/vyfinance'; -export * from './dex/teddyswap'; -export * from './dex/spectrum'; +export * from './dex/splash'; diff --git a/src/requests/swap-request.ts b/src/requests/swap-request.ts index 087a9cb..0e38905 100644 --- a/src/requests/swap-request.ts +++ b/src/requests/swap-request.ts @@ -225,6 +225,7 @@ export class SwapRequest { // Standard parameters for a swap order const defaultSwapParameters: DatumParameters = { + [DatumParameterKey.Address]: this._dexter.walletProvider.address(), [DatumParameterKey.SenderPubKeyHash]: this._dexter.walletProvider.publicKeyHash(), [DatumParameterKey.SenderStakingKeyHash]: this._dexter.walletProvider.stakingKeyHash(), [DatumParameterKey.ReceiverPubKeyHash]: this._dexter.walletProvider.publicKeyHash(), @@ -246,7 +247,8 @@ export class SwapRequest { return { utxo, } - }) as SpendUTxO[] + }) as SpendUTxO[], + this._dexter.dataProvider ); } diff --git a/src/utils.ts b/src/utils.ts index af1979f..a80b97f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -2,6 +2,7 @@ import { Token } from '@dex/models/asset'; import { LiquidityPool } from '@dex/models/liquidity-pool'; import { C, Datum, fromHex, Lucid, toHex, Utils } from 'lucid-cardano'; import { DatumJson } from '@app/types'; +import { encoder } from 'js-encoding-utils'; export const lucidUtils: Utils = new Utils(new Lucid()); @@ -62,4 +63,7 @@ export function datumJsonToCbor(json: DatumJson): Datum { }; return toHex(convert(json).to_bytes()); -} \ No newline at end of file +} + +export const bytesToHex = (bytes: Uint8Array): string => encoder.arrayBufferToHexString(bytes); +export const hexToBytes = (hex: string): Uint8Array => encoder.hexStringToArrayBuffer(hex); \ No newline at end of file From 09b77cc973483e3b716ace3beb2315ec7b10df64 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 3 Dec 2024 19:35:32 -0500 Subject: [PATCH 2/3] Fix x -> ada swaps --- src/dex/splash.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dex/splash.ts b/src/dex/splash.ts index d757f4e..c15b221 100644 --- a/src/dex/splash.ts +++ b/src/dex/splash.ts @@ -183,7 +183,7 @@ export class Splash extends BaseDex { const walletUtxos: UTxO[] = await dataProvider.utxos( swapParameters[DatumParameterKey.Address] as string, swapParameters[DatumParameterKey.SwapInTokenPolicyId] !== '' - ? new Asset(DatumParameterKey.SwapInTokenPolicyId, DatumParameterKey.SwapInTokenAssetName) + ? new Asset(swapParameters.SwapInTokenPolicyId as string, swapParameters.SwapInTokenAssetName as string) : undefined ); const firstUtxo: UTxO = walletUtxos[0]; From b6864f7bfbdd04e64a7c62af7013058731f87f31 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 3 Dec 2024 20:17:57 -0500 Subject: [PATCH 3/3] Fix reserves --- src/dex/definitions/splash/pool.ts | 4 ++-- src/dex/splash.ts | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/dex/definitions/splash/pool.ts b/src/dex/definitions/splash/pool.ts index ab6dbea..5237f6e 100644 --- a/src/dex/definitions/splash/pool.ts +++ b/src/dex/definitions/splash/pool.ts @@ -55,10 +55,10 @@ export default { int: DatumParameterKey.Unknown }, { - int: DatumParameterKey.ReserveA + int: DatumParameterKey.PoolAssetATreasury }, { - int: DatumParameterKey.ReserveB + int: DatumParameterKey.PoolAssetBTreasury }, (field: DefinitionField, parameters: DatumParameters, shouldExtract: boolean = true) => { return; diff --git a/src/dex/splash.ts b/src/dex/splash.ts index c15b221..b54a665 100644 --- a/src/dex/splash.ts +++ b/src/dex/splash.ts @@ -102,6 +102,9 @@ export class Splash extends BaseDex { return Promise.resolve(undefined); } + const assetAIndex: number = relevantAssets.length === 2 ? 0 : 1; + const assetBIndex: number = relevantAssets.length === 2 ? 1 : 2; + try { const builder: DefinitionBuilder = await new DefinitionBuilder().loadDefinition(pool); const datum: DefinitionField = await provider.datumValue(utxo.datumHash); @@ -113,8 +116,8 @@ export class Splash extends BaseDex { ? 'lovelace' : new Asset(parameters.PoolAssetAPolicyId as string, parameters.PoolAssetAAssetName as string), new Asset(parameters.PoolAssetBPolicyId as string, parameters.PoolAssetBAssetName as string), - BigInt(parameters.ReserveA as number), - BigInt(parameters.ReserveB as number), + relevantAssets[assetAIndex].quantity - BigInt(parameters.PoolAssetATreasury as number), + relevantAssets[assetBIndex].quantity - BigInt(parameters.PoolAssetBTreasury as number), utxo.address, '', '',