From a3142fb4dd88734b22a49a08acb565d5eab09cf9 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Fri, 21 Oct 2022 18:54:59 +0200 Subject: [PATCH 01/41] Add DexMeanPrice entity and migration --- indexer/entity/src/dex_mean_price.rs | 65 ++++++++++++++ indexer/entity/src/lib.rs | 1 + indexer/entity/src/prelude.rs | 5 ++ indexer/migration/src/lib.rs | 2 + ...1020_000014_create_dex_mean_price_table.rs | 88 +++++++++++++++++++ 5 files changed, 161 insertions(+) create mode 100644 indexer/entity/src/dex_mean_price.rs create mode 100644 indexer/migration/src/m20221020_000014_create_dex_mean_price_table.rs diff --git a/indexer/entity/src/dex_mean_price.rs b/indexer/entity/src/dex_mean_price.rs new file mode 100644 index 00000000..5232b3a1 --- /dev/null +++ b/indexer/entity/src/dex_mean_price.rs @@ -0,0 +1,65 @@ +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize, Serialize)] +#[sea_orm(table_name = "DexMeanPrice")] +pub struct Model { + #[sea_orm(primary_key, column_type = "BigInteger")] + pub id: i64, + #[sea_orm(column_type = "BigInteger")] + pub tx_id: i64, + #[sea_orm(column_type = "BigInteger")] + pub address_id: i64, + #[sea_orm(column_type = "BigInteger", nullable)] + pub asset1_id: Option, + #[sea_orm(column_type = "BigInteger", nullable)] + pub asset2_id: Option, + #[sea_orm(column_type = "BigUnsigned")] + pub amount1: u64, + #[sea_orm(column_type = "BigUnsigned")] + pub amount2: u64, +} + +#[derive(Copy, Clone, Debug, DeriveRelation, EnumIter)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::transaction::Entity", + from = "Column::TxId", + to = "super::transaction::Column::Id" + )] + Transaction, + #[sea_orm( + belongs_to = "super::address::Entity", + from = "Column::AddressId", + to = "super::address::Column::Id" + )] + Address, + #[sea_orm( + belongs_to = "super::native_asset::Entity", + from = "Column::Asset1Id", + to = "super::native_asset::Column::Id" + )] + Asset1, + #[sea_orm( + belongs_to = "super::native_asset::Entity", + from = "Column::Asset2Id", + to = "super::native_asset::Column::Id" + )] + Asset2, +} + +// TODO: figure out why this isn't automatically handle by the macros above +impl Related for Entity { + fn to() -> RelationDef { + Relation::Transaction.def() + } +} + +// TODO: figure out why this isn't automatically handle by the macros above +impl Related for Entity { + fn to() -> RelationDef { + Relation::Address.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/indexer/entity/src/lib.rs b/indexer/entity/src/lib.rs index 8f31e9d7..9d5aec03 100644 --- a/indexer/entity/src/lib.rs +++ b/indexer/entity/src/lib.rs @@ -11,6 +11,7 @@ pub mod tx_credential; pub use sea_orm; pub mod asset_mint; pub mod cip25_entry; +pub mod dex_mean_price; pub mod native_asset; pub mod plutus_data; pub mod plutus_data_hash; diff --git a/indexer/entity/src/prelude.rs b/indexer/entity/src/prelude.rs index 55d38c8f..e527f913 100644 --- a/indexer/entity/src/prelude.rs +++ b/indexer/entity/src/prelude.rs @@ -19,6 +19,11 @@ pub use super::cip25_entry::{ ActiveModel as Cip25EntryActiveModel, Column as Cip25EntryColumn, Entity as Cip25Entry, Model as Cip25EntryModel, PrimaryKey as Cip25EntryPrimaryKey, Relation as Cip25EntryRelation, }; +pub use super::dex_mean_price::{ + ActiveModel as DexMeanPriceActiveModel, Column as DexMeanPriceColumn, Entity as DexMeanPrice, + Model as DexMeanPriceModel, PrimaryKey as DexMeanPricePrimaryKey, + Relation as DexMeanPriceRelation, +}; pub use super::native_asset::{ ActiveModel as NativeAssetActiveModel, Column as NativeAssetColumn, Entity as NativeAsset, Model as NativeAssetModel, PrimaryKey as NativeAssetPrimaryKey, diff --git a/indexer/migration/src/lib.rs b/indexer/migration/src/lib.rs index 0896f6df..434acee9 100644 --- a/indexer/migration/src/lib.rs +++ b/indexer/migration/src/lib.rs @@ -15,6 +15,7 @@ mod m20220520_000010_create_cip25_entry_table; mod m20220528_000011_create_plutus_data_hash_table; mod m20220528_000012_create_plutus_data_table; mod m20220808_000013_create_transaction_reference_input_table; +mod m20221020_000014_create_dex_mean_price_table; pub struct Migrator; @@ -37,6 +38,7 @@ impl MigratorTrait for Migrator { Box::new(m20220528_000011_create_plutus_data_hash_table::Migration), Box::new(m20220528_000012_create_plutus_data_table::Migration), Box::new(m20220808_000013_create_transaction_reference_input_table::Migration), + Box::new(m20221020_000014_create_dex_mean_price_table::Migration), ] } } diff --git a/indexer/migration/src/m20221020_000014_create_dex_mean_price_table.rs b/indexer/migration/src/m20221020_000014_create_dex_mean_price_table.rs new file mode 100644 index 00000000..5c7117c2 --- /dev/null +++ b/indexer/migration/src/m20221020_000014_create_dex_mean_price_table.rs @@ -0,0 +1,88 @@ +use sea_schema::migration::prelude::*; + +use entity::dex_mean_price::*; +use entity::prelude::{ + Address, AddressColumn, NativeAsset, NativeAssetColumn, Transaction, TransactionColumn, +}; + +pub struct Migration; + +impl MigrationName for Migration { + fn name(&self) -> &str { + "m20221020_000014_create_dex_mean_price_table" + } +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Entity) + .if_not_exists() + .col( + ColumnDef::new(Column::Id) + .big_integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(Column::TxId).big_integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fk-dex_mean_price-tx_id") + .from(Entity, Column::TxId) + .to(Transaction, TransactionColumn::Id) + .on_delete(ForeignKeyAction::Cascade), + ) + .col(ColumnDef::new(Column::AddressId).big_integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fk-dex_mean_price-address_id") + .from(Entity, Column::AddressId) + .to(Address, AddressColumn::Id) + .on_delete(ForeignKeyAction::Cascade), + ) + .col(ColumnDef::new(Column::Asset1Id).big_integer()) + .foreign_key( + ForeignKey::create() + .name("fk-dex_mean_price-asset1_id") + .from(Entity, Column::Asset1Id) + .to(NativeAsset, NativeAssetColumn::Id) + .on_delete(ForeignKeyAction::Cascade), + ) + .col(ColumnDef::new(Column::Asset2Id).big_integer()) + .foreign_key( + ForeignKey::create() + .name("fk-dex_mean_price-asset2_id") + .from(Entity, Column::Asset2Id) + .to(NativeAsset, NativeAssetColumn::Id) + .on_delete(ForeignKeyAction::Cascade), + ) + .col(ColumnDef::new(Column::Amount1).big_unsigned().not_null()) + .col(ColumnDef::new(Column::Amount2).big_unsigned().not_null()) + .to_owned(), + ) + .await?; + + manager + .create_index( + Index::create() + .table(Entity) + .name("index-dex_mean_price-address-native_asset1-native_asset2-transaction") + .col(Column::AddressId) + .col(Column::Asset1Id) + .col(Column::Asset2Id) + .col(Column::TxId) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Entity).to_owned()) + .await + } +} From a7f2e3cbd7e73fc15ca49ad5920016f415dba2f5 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Mon, 24 Oct 2022 17:26:51 +0200 Subject: [PATCH 02/41] Extract asset_from_pair util function --- .../tasks/src/multiera/multiera_asset_mint.rs | 27 ++++++++----------- indexer/tasks/src/multiera/utils/common.rs | 27 +++++++++++++++++++ indexer/tasks/src/multiera/utils/mod.rs | 1 + 3 files changed, 39 insertions(+), 16 deletions(-) create mode 100644 indexer/tasks/src/multiera/utils/common.rs diff --git a/indexer/tasks/src/multiera/multiera_asset_mint.rs b/indexer/tasks/src/multiera/multiera_asset_mint.rs index 6422428a..031132dd 100644 --- a/indexer/tasks/src/multiera/multiera_asset_mint.rs +++ b/indexer/tasks/src/multiera/multiera_asset_mint.rs @@ -1,5 +1,6 @@ use std::collections::{BTreeMap, BTreeSet}; +use super::utils::common::asset_from_pair; use super::{multiera_txs::MultieraTransactionTask, utils::user_asset::AssetName}; use crate::config::ReadonlyConfig::ReadonlyConfig; use crate::utils::blake2b160; @@ -99,22 +100,16 @@ async fn handle_mints( } // 2) Query for which of these pairs already exist in the database - // https://github.com/dcSpark/carp/issues/46 - let mut mint_conditions = Condition::any(); - for (&asset_name, &policy_id) in unique_pairs - .iter() - .flat_map(|(policy_id, assets)| assets.keys().zip(std::iter::repeat(policy_id))) - { - mint_conditions = mint_conditions.add( - Condition::all() - .add(NativeAssetColumn::PolicyId.eq(policy_id.clone())) - .add(NativeAssetColumn::AssetName.eq(asset_name.clone())), - ); - } - let mut found_assets = NativeAsset::find() - .filter(mint_conditions) - .all(db_tx) - .await?; + let mut found_assets = asset_from_pair( + db_tx, + &unique_pairs + .iter() + .flat_map(|(&policy_id, assets)| { + assets.keys().map(|&name| (policy_id.clone(), name.clone())) + }) + .collect::, Vec)>>(), + ) + .await?; // 3) Find which pairs we need that weren't in the database let mut remaining_pairs = unique_pairs.clone(); diff --git a/indexer/tasks/src/multiera/utils/common.rs b/indexer/tasks/src/multiera/utils/common.rs new file mode 100644 index 00000000..ff81edeb --- /dev/null +++ b/indexer/tasks/src/multiera/utils/common.rs @@ -0,0 +1,27 @@ +use std::collections::BTreeSet; + +use entity::{ + prelude::*, + sea_orm::{entity::*, prelude::*, Condition, DatabaseTransaction}, +}; + +pub async fn asset_from_pair( + db_tx: &DatabaseTransaction, + pairs: &[(Vec /* policy id */, Vec /* asset name */)], +) -> Result, DbErr> { + // https://github.com/dcSpark/carp/issues/46 + let mut asset_conditions = Condition::any(); + for (policy_id, asset_name) in pairs.iter() { + asset_conditions = asset_conditions.add( + Condition::all() + .add(NativeAssetColumn::PolicyId.eq(policy_id.clone())) + .add(NativeAssetColumn::AssetName.eq(asset_name.clone())), + ); + } + + let assets = NativeAsset::find() + .filter(asset_conditions) + .all(db_tx) + .await?; + Ok(assets) +} diff --git a/indexer/tasks/src/multiera/utils/mod.rs b/indexer/tasks/src/multiera/utils/mod.rs index 71e3d49a..76cbd331 100644 --- a/indexer/tasks/src/multiera/utils/mod.rs +++ b/indexer/tasks/src/multiera/utils/mod.rs @@ -1,2 +1,3 @@ pub mod cip25_parse; +pub mod common; pub mod user_asset; From 05ed8179177a12093d69dd3530b0532f96f83858 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Mon, 24 Oct 2022 18:40:11 +0200 Subject: [PATCH 03/41] Add MultieraWingRidersV1MeanPriceTask --- indexer/tasks/src/multiera/mod.rs | 1 + .../multiera_wingriders_v1_mean_price.rs | 161 ++++++++++++++++++ indexer/tasks/src/multiera/utils/common.rs | 49 ++++++ indexer/tasks/src/types.rs | 3 + 4 files changed, 214 insertions(+) create mode 100644 indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs diff --git a/indexer/tasks/src/multiera/mod.rs b/indexer/tasks/src/multiera/mod.rs index 3e4a2e20..61bced77 100644 --- a/indexer/tasks/src/multiera/mod.rs +++ b/indexer/tasks/src/multiera/mod.rs @@ -13,5 +13,6 @@ pub mod multiera_txs; pub mod multiera_unused_input; pub mod multiera_used_inputs; pub mod multiera_used_outputs; +pub mod multiera_wingriders_v1_mean_price; pub mod relation_map; pub mod utils; diff --git a/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs new file mode 100644 index 00000000..1750d3b9 --- /dev/null +++ b/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs @@ -0,0 +1,161 @@ +use std::collections::BTreeSet; + +use super::utils::common::{ + get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, +}; +use super::{multiera_address::MultieraAddressTask, utils::common::asset_from_pair}; +use crate::dsl::task_macro::*; +use crate::{config::EmptyConfig::EmptyConfig, types::AssetPair}; +use entity::sea_orm::{DatabaseTransaction, Set}; +use pallas::ledger::{ + primitives::ToCanonicalJson, + traverse::{MultiEraBlock, MultiEraTx}, +}; + +const POOL_SCRIPT_HASH: &str = "e6c90a5923713af5786963dee0fdffd830ca7e0c86a041d9e5833e91"; +const POOL_FIXED_ADA: u64 = 3_000_000; // every pool UTXO holds this amount of ADA + +carp_task! { + name MultieraWingRidersV1MeanPriceTask; + configuration EmptyConfig; + doc "Adds WingRiders V1 mean price updates to the database"; + era multiera; + dependencies [MultieraAddressTask]; + read [multiera_txs, multiera_addresses]; + write []; + should_add_task |block, _properties| { + block.1.txs().iter().any(|tx| tx.outputs().len() > 0) + }; + execute |previous_data, task| handle_mean_price( + task.db_tx, + task.block, + &previous_data.multiera_txs, + &previous_data.multiera_addresses, + ); + merge_result |previous_data, _result| { + }; +} + +struct QueuedMeanPrice { + tx_id: i64, + address: Vec, // pallas::crypto::hash::Hash<32> + asset1: AssetPair, + asset2: AssetPair, + amount1: u64, + amount2: u64, +} + +async fn handle_mean_price( + db_tx: &DatabaseTransaction, + block: BlockInfo<'_, MultiEraBlock<'_>>, + multiera_txs: &[TransactionModel], + multiera_addresses: &BTreeMap, AddressInBlock>, +) -> Result<(), DbErr> { + // 1) Parse mean prices + let mut queued_prices = Vec::::default(); + for (tx_body, cardano_transaction) in block.1.txs().iter().zip(multiera_txs) { + if cardano_transaction.is_valid { + queue_mean_price(&mut queued_prices, tx_body, cardano_transaction.id); + } + } + + if queued_prices.is_empty() { + return Ok(()); + } + + // 2) Remove asset duplicates to build a list of all the to query for. + // ADA is ignored, it's not in the NativeAsset DB table + let mut unique_tokens = BTreeSet::<&(Vec, Vec)>::default(); + for p in &queued_prices { + if let Some(pair) = &p.asset1 { + unique_tokens.insert(&pair); + } + if let Some(pair) = &p.asset2 { + unique_tokens.insert(&pair); + } + } + + // 3) Query for asset ids + let found_assets = asset_from_pair( + db_tx, + &unique_tokens + .iter() + .map(|(policy_id, asset_name)| (policy_id.clone(), asset_name.clone())) + .collect::>(), + ) + .await?; + let mut asset_pair_to_id_map = found_assets + .into_iter() + .map(|asset| (Some((asset.policy_id, asset.asset_name)), Some(asset.id))) + .collect::>(); + asset_pair_to_id_map.insert(None, None); // ADA + + // 4) Add mean prices to DB + DexMeanPrice::insert_many(queued_prices.iter().map(|price| DexMeanPriceActiveModel { + tx_id: Set(price.tx_id), + address_id: Set(multiera_addresses[&price.address].model.id), + asset1_id: Set(asset_pair_to_id_map[&price.asset1]), + asset2_id: Set(asset_pair_to_id_map[&price.asset2]), + amount1: Set(price.amount1), + amount2: Set(price.amount2), + ..Default::default() + })) + .exec(db_tx) + .await?; + + Ok(()) +} + +fn queue_mean_price(queued_prices: &mut Vec, tx: &MultiEraTx, tx_id: i64) { + // Find the pool address (Note: there should be at most one pool output) + for output in tx + .outputs() + .iter() + .find(|o| get_sheley_payment_hash(o.address()).as_deref() == Some(POOL_SCRIPT_HASH)) + { + // Remark: The datum that corresponds to the pool output's datum hash should be present + // in tx.plutus_data() + if let Some(datum) = get_plutus_datum_for_output(output, &tx.plutus_data()) { + let datum = datum.to_json(); + + let treasury1 = datum["fields"][1]["fields"][2]["int"].as_u64().unwrap(); + let treasury2 = datum["fields"][1]["fields"][3]["int"].as_u64().unwrap(); + + let get_asset_item = |i, j| { + let item = datum["fields"][1]["fields"][0]["fields"][i]["fields"][j]["bytes"] + .as_str() + .unwrap() + .to_string(); + hex::decode(item).unwrap() + }; + let get_asset = |policy_id: Vec, asset_name: Vec| { + if policy_id.is_empty() && asset_name.is_empty() { + None + } else { + Some((policy_id, asset_name)) + } + }; + let asset1 = get_asset(get_asset_item(0, 0), get_asset_item(0, 1)); + let asset2 = get_asset(get_asset_item(1, 0), get_asset_item(1, 1)); + + let get_fixed_ada = |pair: &AssetPair| -> u64 { + if pair.is_none() { + POOL_FIXED_ADA + } else { + 0 + } + }; + let amount1 = get_asset_amount(output, &asset1) - treasury1 - get_fixed_ada(&asset1); + let amount2 = get_asset_amount(output, &asset2) - treasury2 - get_fixed_ada(&asset2); + + queued_prices.push(QueuedMeanPrice { + tx_id, + address: output.address().unwrap().to_vec(), + asset1, + asset2, + amount1, + amount2, + }); + } + } +} diff --git a/indexer/tasks/src/multiera/utils/common.rs b/indexer/tasks/src/multiera/utils/common.rs index ff81edeb..393bb08d 100644 --- a/indexer/tasks/src/multiera/utils/common.rs +++ b/indexer/tasks/src/multiera/utils/common.rs @@ -4,6 +4,55 @@ use entity::{ prelude::*, sea_orm::{entity::*, prelude::*, Condition, DatabaseTransaction}, }; +use pallas::{ + codec::utils::KeepRaw, + crypto::hash::Hasher, + ledger::{ + addresses, + primitives::{alonzo, babbage::DatumOption}, + traverse::{MultiEraOutput, Subject}, + }, +}; + +use crate::types::AssetPair; + +pub fn get_sheley_payment_hash( + address: Result, +) -> Option { + if let Ok(addresses::Address::Shelley(shelley_address)) = address { + Some(hex::encode(shelley_address.payment().as_hash().to_vec())) + } else { + None + } +} + +pub fn get_asset_amount(output: &MultiEraOutput, pair: &AssetPair) -> u64 { + output + .assets() + .iter() + .filter(|asset| match &asset.subject { + Subject::Lovelace => pair.is_none(), + Subject::NativeAsset(policy_id, asset_name) => { + pair == &Some((policy_id.to_vec(), asset_name.to_vec())) + } + }) + .map(|a| a.quantity) + .sum() +} + +pub fn get_plutus_datum_for_output( + output: &MultiEraOutput, + plutus_data: &Vec<&KeepRaw>, +) -> Option { + match output.datum() { + Some(DatumOption::Data(datum)) => Some(datum.0), + Some(DatumOption::Hash(hash)) => plutus_data + .iter() + .find(|datum| Hasher::<256>::hash_cbor(datum) == hash) + .map(|&d| d.clone().unwrap()), + None => None, + } +} pub async fn asset_from_pair( db_tx: &DatabaseTransaction, diff --git a/indexer/tasks/src/types.rs b/indexer/tasks/src/types.rs index 46dd810b..f2acf9ef 100644 --- a/indexer/tasks/src/types.rs +++ b/indexer/tasks/src/types.rs @@ -68,3 +68,6 @@ impl From for i32 { } } } + +// ADA = None, token = Some((policy_id, asset_name)) +pub type AssetPair = Option<(Vec, Vec)>; From 023cb6f10882ee577b947ee486faa9c21e2b1cf5 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Mon, 24 Oct 2022 18:40:33 +0200 Subject: [PATCH 04/41] Add dex_prices.toml execution plan --- indexer/execution_plans/dex_prices.toml | 61 +++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 indexer/execution_plans/dex_prices.toml diff --git a/indexer/execution_plans/dex_prices.toml b/indexer/execution_plans/dex_prices.toml new file mode 100644 index 00000000..8e385e33 --- /dev/null +++ b/indexer/execution_plans/dex_prices.toml @@ -0,0 +1,61 @@ +# This file defines which tasks are used when parsing blocks +# Creating your own execution plan with the tasks you need for your application +# Format follows the TOML format: https://toml.io/en/ + +# Note: the order you specify tasks in this file matters +# ex: if task Foo depends on task Bar, place Bar first in the list + +# You can find task the task name by looking at the TASK_NAME field inside the task +# Some tasks may allow extra parameters that you can specify in this file + +[GenesisBlockTask] + +[GenesisTransactionTask] + +[ByronBlockTask] +readonly=false + +[ByronTransactionTask] +readonly=false + +[ByronAddressTask] + +[ByronOutputTask] + +[ByronInputTask] + +[MultieraBlockTask] +readonly=false + +[MultieraTransactionTask] +readonly=false + +[MultieraMetadataTask] +readonly=false + +[MultieraAddressTask] + +[MultieraOutputTask] +readonly=false + +[MultieraReferenceInputTask] +readonly=false + +[MultieraUsedInputTask] +readonly=false + +[MultieraUnusedInputTask] + +[MultieraStakeCredentialTask] + +[MultieraAddressCredentialRelationTask] +readonly=false + +[MultieraTxCredentialRelationTask] + +[MultieraAssetMintTask] +readonly=false + +[MultieraCip25EntryTask] + +[MultieraWingRidersV1MeanPriceTask] From b60ac51613d09682cf77257f21d24dc68fe71a86 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Fri, 28 Oct 2022 16:16:56 +0200 Subject: [PATCH 05/41] Add dex mean price endpoint --- .../app/controllers/DexMeanPriceController.ts | 116 +++ .../app/models/dex/sqlDexMeanPrice.queries.ts | 85 ++ .../server/app/models/dex/sqlDexMeanPrice.sql | 45 + .../server/app/models/zapatos/schema.d.ts | 968 ++++++++++++++++-- webserver/server/app/services/DexMeanPrice.ts | 55 + webserver/shared/constants.ts | 6 + webserver/shared/errors.ts | 7 + webserver/shared/models/DexMeanPrice.ts | 33 + webserver/shared/models/PolicyIdAssetMap.ts | 4 +- webserver/shared/routes.ts | 7 + 10 files changed, 1232 insertions(+), 94 deletions(-) create mode 100644 webserver/server/app/controllers/DexMeanPriceController.ts create mode 100644 webserver/server/app/models/dex/sqlDexMeanPrice.queries.ts create mode 100644 webserver/server/app/models/dex/sqlDexMeanPrice.sql create mode 100644 webserver/server/app/services/DexMeanPrice.ts create mode 100644 webserver/shared/models/DexMeanPrice.ts diff --git a/webserver/server/app/controllers/DexMeanPriceController.ts b/webserver/server/app/controllers/DexMeanPriceController.ts new file mode 100644 index 00000000..80877b79 --- /dev/null +++ b/webserver/server/app/controllers/DexMeanPriceController.ts @@ -0,0 +1,116 @@ +import { Body, Controller, TsoaResponse, Res, Post, Route, SuccessResponse } from 'tsoa'; +import { StatusCodes } from 'http-status-codes'; +import { DEX_PRICE_LIMIT } from '../../../shared/constants'; +import tx from 'pg-tx'; +import pool from '../services/PgPoolSingleton'; +import { resolvePageStart, resolveUntilTransaction } from '../services/PaginationService'; +import type { ErrorShape } from '../../../shared/errors'; +import { genErrorMessage } from '../../../shared/errors'; +import { Errors } from '../../../shared/errors'; +import { expectType } from 'tsd'; +import type { EndpointTypes } from '../../../shared/routes'; +import { Routes } from '../../../shared/routes'; +import { getAddressTypes } from '../models/utils'; +import type { DexMeanPriceResponse } from '../../../shared/models/DexMeanPrice'; +import { dexMeanPrices } from '../services/DexMeanPrice'; + +const route = Routes.dexMeanPrice; + +@Route('dex/mean-price') +export class DexMeanPriceController extends Controller { + /** + * Gets the mean prices for the given liquidity pool addresses and asset pairs. + */ + @SuccessResponse(`${StatusCodes.OK}`) + @Post() + public async dexMeanPrice( + @Body() + requestBody: EndpointTypes[typeof route]['input'], + @Res() + errorResponse: TsoaResponse< + StatusCodes.BAD_REQUEST | StatusCodes.CONFLICT | StatusCodes.UNPROCESSABLE_ENTITY, + ErrorShape + > + ): Promise { + if (requestBody.addresses.length > DEX_PRICE_LIMIT.REQUEST_ADDRESSES) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return errorResponse( + StatusCodes.BAD_REQUEST, + genErrorMessage(Errors.AddressLimitExceeded, { + limit: DEX_PRICE_LIMIT.REQUEST_ADDRESSES, + found: requestBody.addresses.length, + }) + ); + } + const addressTypes = getAddressTypes(requestBody.addresses); + if (addressTypes.invalid.length > 0) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return errorResponse( + StatusCodes.UNPROCESSABLE_ENTITY, + genErrorMessage(Errors.IncorrectAddressFormat, { + addresses: addressTypes.invalid, + }) + ); + } + + if (requestBody.assetPairs.length > DEX_PRICE_LIMIT.REQUEST_ASSET_PAIRS) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return errorResponse( + StatusCodes.BAD_REQUEST, + genErrorMessage(Errors.AssetPairLimitExceeded, { + limit: DEX_PRICE_LIMIT.REQUEST_ASSET_PAIRS, + found: requestBody.assetPairs.length, + }) + ); + } + + // note: we use a SQL transaction to make sure the pagination check works properly + // otherwise, a rollback could happen between getting the pagination info and the history query + const meanPrices = await tx( + pool, + async dbTx => { + const [until, pageStart] = await Promise.all([ + resolveUntilTransaction({ + block_hash: Buffer.from(requestBody.untilBlock, 'hex'), + dbTx, + }), + requestBody.after == null + ? Promise.resolve(undefined) + : resolvePageStart({ + after_block: Buffer.from(requestBody.after.block, 'hex'), + after_tx: Buffer.from(requestBody.after.tx, 'hex'), + dbTx, + }), + ]); + if (until == null) { + return genErrorMessage(Errors.BlockHashNotFound, { + untilBlock: requestBody.untilBlock, + }); + } + if (requestBody.after != null && pageStart == null) { + return genErrorMessage(Errors.PageStartNotFound, { + blockHash: requestBody.after.block, + txHash: requestBody.after.tx, + }); + } + + return await dexMeanPrices({ + after: pageStart, + until, + dbTx, + addresses: addressTypes.exactAddress.map(addr => Buffer.from(addr, 'hex')), + reverseMap: addressTypes.reverseMap, + assetPairs: requestBody.assetPairs, + limit: requestBody.limit ?? DEX_PRICE_LIMIT.RESPONSE, + }); + } + ); + if ('code' in meanPrices) { + expectType>(true); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return errorResponse(StatusCodes.CONFLICT, meanPrices); + } + + return meanPrices; + } +} diff --git a/webserver/server/app/models/dex/sqlDexMeanPrice.queries.ts b/webserver/server/app/models/dex/sqlDexMeanPrice.queries.ts new file mode 100644 index 00000000..8dd326c4 --- /dev/null +++ b/webserver/server/app/models/dex/sqlDexMeanPrice.queries.ts @@ -0,0 +1,85 @@ +/** Types generated for queries found in "app/models/dex/sqlDexMeanPrice.sql" */ +import { PreparedQuery } from '@pgtyped/query'; + +export type BufferArray = (Buffer)[]; + +/** 'SqlDexMeanPrice' parameters type */ +export interface ISqlDexMeanPriceParams { + addresses: BufferArray | null | void; + after_tx_id: string | null | void; + asset_name1: BufferArray | null | void; + asset_name2: BufferArray | null | void; + limit: string | null | void; + policy_id1: BufferArray | null | void; + policy_id2: BufferArray | null | void; + until_tx_id: string | null | void; +} + +/** 'SqlDexMeanPrice' return type */ +export interface ISqlDexMeanPriceResult { + address: Buffer; + amount1: string; + amount2: string; + asset_name1: Buffer | null; + asset_name2: Buffer | null; + policy_id1: Buffer | null; + policy_id2: Buffer | null; + tx_hash: Buffer; +} + +/** 'SqlDexMeanPrice' query type */ +export interface ISqlDexMeanPriceQuery { + params: ISqlDexMeanPriceParams; + result: ISqlDexMeanPriceResult; +} + +const sqlDexMeanPriceIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true,"addresses":true,"until_tx_id":true,"after_tx_id":true,"limit":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]},{"name":"addresses","required":false,"transform":{"type":"scalar"},"locs":[{"a":1157,"b":1166}]},{"name":"until_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":1476,"b":1487}]},{"name":"after_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":1522,"b":1533}]},{"name":"limit","required":false,"transform":{"type":"scalar"},"locs":[{"a":1592,"b":1597}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n \"Transaction\".hash AS tx_hash,\n \"Address\".payload AS address,\n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"DexMeanPrice\".amount1,\n \"DexMeanPrice\".amount2\nFROM \"DexMeanPrice\"\nJOIN \"Transaction\" ON \"Transaction\".id = \"DexMeanPrice\".tx_id\nJOIN \"Address\" ON \"Address\".id = \"DexMeanPrice\".address_id\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"DexMeanPrice\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"DexMeanPrice\".asset2_id\nWHERE\n \"Address\".payload = ANY (:addresses)\n AND\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n AND\n \"DexMeanPrice\".tx_id <= (:until_tx_id)\n AND\n \"DexMeanPrice\".tx_id > (:after_tx_id)\nORDER BY \"DexMeanPrice\".tx_id, \"DexMeanPrice\".id\nLIMIT (:limit)"}; + +/** + * Query generated from SQL: + * ``` + * WITH "AssetPairs" AS ( + * SELECT policy_id1, asset_name1, policy_id2, asset_name2 + * FROM + * unnest( + * + * (:policy_id1)::bytea[], + * (:asset_name1)::bytea[], + * (:policy_id2)::bytea[], + * (:asset_name2)::bytea[] + * ) x(policy_id1, asset_name1, policy_id2, asset_name2) + * ) + * SELECT + * "Transaction".hash AS tx_hash, + * "Address".payload AS address, + * "Asset1".policy_id AS "policy_id1?", + * "Asset1".asset_name AS "asset_name1?", + * "Asset2".policy_id AS "policy_id2?", + * "Asset2".asset_name AS "asset_name2?", + * "DexMeanPrice".amount1, + * "DexMeanPrice".amount2 + * FROM "DexMeanPrice" + * JOIN "Transaction" ON "Transaction".id = "DexMeanPrice".tx_id + * JOIN "Address" ON "Address".id = "DexMeanPrice".address_id + * LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "DexMeanPrice".asset1_id + * LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "DexMeanPrice".asset2_id + * WHERE + * "Address".payload = ANY (:addresses) + * AND + * ( + * COALESCE("Asset1".policy_id, ''::bytea), + * COALESCE("Asset1".asset_name, ''::bytea), + * COALESCE("Asset2".policy_id, ''::bytea), + * COALESCE("Asset2".asset_name, ''::bytea) + * ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") + * AND + * "DexMeanPrice".tx_id <= (:until_tx_id) + * AND + * "DexMeanPrice".tx_id > (:after_tx_id) + * ORDER BY "DexMeanPrice".tx_id, "DexMeanPrice".id + * LIMIT (:limit) + * ``` + */ +export const sqlDexMeanPrice = new PreparedQuery(sqlDexMeanPriceIR); + + diff --git a/webserver/server/app/models/dex/sqlDexMeanPrice.sql b/webserver/server/app/models/dex/sqlDexMeanPrice.sql new file mode 100644 index 00000000..c3f28cb2 --- /dev/null +++ b/webserver/server/app/models/dex/sqlDexMeanPrice.sql @@ -0,0 +1,45 @@ +/* @name sqlDexMeanPrice */ +WITH "AssetPairs" AS ( + SELECT policy_id1, asset_name1, policy_id2, asset_name2 + FROM + unnest( + /* + Aparrently, we can't make pgtyped understand that these are actually (bytea | NULL)[]. + We will pass in ('', '') instead of (NULL, NULL) for ADA and do the NULL->'' conversion + below when filtering the assets (see the COALESCE). + */ + (:policy_id1)::bytea[], + (:asset_name1)::bytea[], + (:policy_id2)::bytea[], + (:asset_name2)::bytea[] + ) x(policy_id1, asset_name1, policy_id2, asset_name2) +) +SELECT + "Transaction".hash AS tx_hash, + "Address".payload AS address, + "Asset1".policy_id AS "policy_id1?", + "Asset1".asset_name AS "asset_name1?", + "Asset2".policy_id AS "policy_id2?", + "Asset2".asset_name AS "asset_name2?", + "DexMeanPrice".amount1, + "DexMeanPrice".amount2 +FROM "DexMeanPrice" +JOIN "Transaction" ON "Transaction".id = "DexMeanPrice".tx_id +JOIN "Address" ON "Address".id = "DexMeanPrice".address_id +LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "DexMeanPrice".asset1_id +LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "DexMeanPrice".asset2_id +WHERE + "Address".payload = ANY (:addresses) + AND + ( + COALESCE("Asset1".policy_id, ''::bytea), + COALESCE("Asset1".asset_name, ''::bytea), + COALESCE("Asset2".policy_id, ''::bytea), + COALESCE("Asset2".asset_name, ''::bytea) + ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") + AND + "DexMeanPrice".tx_id <= (:until_tx_id) + AND + "DexMeanPrice".tx_id > (:after_tx_id) +ORDER BY "DexMeanPrice".tx_id, "DexMeanPrice".id +LIMIT (:limit); diff --git a/webserver/server/app/models/zapatos/schema.d.ts b/webserver/server/app/models/zapatos/schema.d.ts index e6e500ff..6da82c2c 100644 --- a/webserver/server/app/models/zapatos/schema.d.ts +++ b/webserver/server/app/models/zapatos/schema.d.ts @@ -1,9 +1,9 @@ /* ** DON'T EDIT THIS FILE ** -It's been generated by Zapatos (v5.0.2), and is liable to be overwritten +It's been generated by Zapatos (v5.0.4), and is liable to be overwritten Zapatos: https://jawj.github.io/zapatos/ -Copyright (C) 2020 - 2021 George MacKerron +Copyright (C) 2020 - 2022 George MacKerron Released under the MIT licence: see LICENCE file */ @@ -40,6 +40,12 @@ declare module 'zapatos/schema' { * - `NOT NULL`, no default */ payload: Buffer; + /** + * **Address.first_tx** + * - `int8` in database + * - `NOT NULL`, no default + */ + first_tx: db.Int8String; } export interface JSONSelectable { /** @@ -54,6 +60,12 @@ declare module 'zapatos/schema' { * - `NOT NULL`, no default */ payload: db.ByteArrayString; + /** + * **Address.first_tx** + * - `int8` in database + * - `NOT NULL`, no default + */ + first_tx: number; } export interface Whereable { /** @@ -68,6 +80,12 @@ declare module 'zapatos/schema' { * - `NOT NULL`, no default */ payload?: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; + /** + * **Address.first_tx** + * - `int8` in database + * - `NOT NULL`, no default + */ + first_tx?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; } export interface Insertable { /** @@ -82,6 +100,12 @@ declare module 'zapatos/schema' { * - `NOT NULL`, no default */ payload: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment; + /** + * **Address.first_tx** + * - `int8` in database + * - `NOT NULL`, no default + */ + first_tx: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment; } export interface Updatable { /** @@ -96,11 +120,17 @@ declare module 'zapatos/schema' { * - `NOT NULL`, no default */ payload?: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment | db.SQLFragment | db.SQLFragment>; + /** + * **Address.first_tx** + * - `int8` in database + * - `NOT NULL`, no default + */ + first_tx?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.SQLFragment | db.SQLFragment>; } export type UniqueIndex = 'Address_payload_key' | 'Address_pkey'; export type Column = keyof Selectable; export type OnlyCols = Pick; - export type SQLExpression = db.GenericSQLExpression | db.ColumnNames | db.ColumnValues | Table | Whereable | Column; + export type SQLExpression = Table | db.ColumnNames | db.ColumnValues | Whereable | Column | db.ParentColumn | db.GenericSQLExpression; export type SQL = SQLExpression | SQLExpression[]; } @@ -213,7 +243,7 @@ declare module 'zapatos/schema' { export type UniqueIndex = 'address_credential-pk'; export type Column = keyof Selectable; export type OnlyCols = Pick; - export type SQLExpression = db.GenericSQLExpression | db.ColumnNames | db.ColumnValues | Table | Whereable | Column; + export type SQLExpression = Table | db.ColumnNames | db.ColumnValues | Whereable | Column | db.ParentColumn | db.GenericSQLExpression; export type SQL = SQLExpression | SQLExpression[]; } @@ -326,7 +356,7 @@ declare module 'zapatos/schema' { export type UniqueIndex = 'asset_mint-pk'; export type Column = keyof Selectable; export type OnlyCols = Pick; - export type SQLExpression = db.GenericSQLExpression | db.ColumnNames | db.ColumnValues | Table | Whereable | Column; + export type SQLExpression = Table | db.ColumnNames | db.ColumnValues | Whereable | Column | db.ParentColumn | db.GenericSQLExpression; export type SQL = SQLExpression | SQLExpression[]; } @@ -529,7 +559,7 @@ declare module 'zapatos/schema' { export type UniqueIndex = 'Block_hash_key' | 'Block_pkey'; export type Column = keyof Selectable; export type OnlyCols = Pick; - export type SQLExpression = db.GenericSQLExpression | db.ColumnNames | db.ColumnValues | Table | Whereable | Column; + export type SQLExpression = Table | db.ColumnNames | db.ColumnValues | Whereable | Column | db.ParentColumn | db.GenericSQLExpression; export type SQL = SQLExpression | SQLExpression[]; } @@ -559,6 +589,12 @@ declare module 'zapatos/schema' { */ asset_id: db.Int8String; /** + * **Cip25Entry.version** + * - `text` in database + * - `NOT NULL`, no default + */ + version: string; + /** * **Cip25Entry.payload** * - `bytea` in database * - `NOT NULL`, no default @@ -585,6 +621,12 @@ declare module 'zapatos/schema' { */ asset_id: number; /** + * **Cip25Entry.version** + * - `text` in database + * - `NOT NULL`, no default + */ + version: string; + /** * **Cip25Entry.payload** * - `bytea` in database * - `NOT NULL`, no default @@ -611,6 +653,12 @@ declare module 'zapatos/schema' { */ asset_id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; /** + * **Cip25Entry.version** + * - `text` in database + * - `NOT NULL`, no default + */ + version?: string | db.Parameter | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; + /** * **Cip25Entry.payload** * - `bytea` in database * - `NOT NULL`, no default @@ -637,6 +685,12 @@ declare module 'zapatos/schema' { */ asset_id: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment; /** + * **Cip25Entry.version** + * - `text` in database + * - `NOT NULL`, no default + */ + version: string | db.Parameter | db.SQLFragment; + /** * **Cip25Entry.payload** * - `bytea` in database * - `NOT NULL`, no default @@ -663,6 +717,12 @@ declare module 'zapatos/schema' { */ asset_id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.SQLFragment | db.SQLFragment>; /** + * **Cip25Entry.version** + * - `text` in database + * - `NOT NULL`, no default + */ + version?: string | db.Parameter | db.SQLFragment | db.SQLFragment | db.SQLFragment>; + /** * **Cip25Entry.payload** * - `bytea` in database * - `NOT NULL`, no default @@ -672,7 +732,240 @@ declare module 'zapatos/schema' { export type UniqueIndex = 'Cip25Entry_pkey'; export type Column = keyof Selectable; export type OnlyCols = Pick; - export type SQLExpression = db.GenericSQLExpression | db.ColumnNames | db.ColumnValues | Table | Whereable | Column; + export type SQLExpression = Table | db.ColumnNames | db.ColumnValues | Whereable | Column | db.ParentColumn | db.GenericSQLExpression; + export type SQL = SQLExpression | SQLExpression[]; + } + + /** + * **DexMeanPrice** + * - Table in database + */ + export namespace DexMeanPrice { + export type Table = 'DexMeanPrice'; + export interface Selectable { + /** + * **DexMeanPrice.id** + * - `int8` in database + * - `NOT NULL`, default: `nextval('"DexMeanPrice_id_seq"'::regclass)` + */ + id: db.Int8String; + /** + * **DexMeanPrice.tx_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + tx_id: db.Int8String; + /** + * **DexMeanPrice.address_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + address_id: db.Int8String; + /** + * **DexMeanPrice.asset1_id** + * - `int8` in database + * - Nullable, no default + */ + asset1_id: db.Int8String | null; + /** + * **DexMeanPrice.asset2_id** + * - `int8` in database + * - Nullable, no default + */ + asset2_id: db.Int8String | null; + /** + * **DexMeanPrice.amount1** + * - `int8` in database + * - `NOT NULL`, no default + */ + amount1: db.Int8String; + /** + * **DexMeanPrice.amount2** + * - `int8` in database + * - `NOT NULL`, no default + */ + amount2: db.Int8String; + } + export interface JSONSelectable { + /** + * **DexMeanPrice.id** + * - `int8` in database + * - `NOT NULL`, default: `nextval('"DexMeanPrice_id_seq"'::regclass)` + */ + id: number; + /** + * **DexMeanPrice.tx_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + tx_id: number; + /** + * **DexMeanPrice.address_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + address_id: number; + /** + * **DexMeanPrice.asset1_id** + * - `int8` in database + * - Nullable, no default + */ + asset1_id: number | null; + /** + * **DexMeanPrice.asset2_id** + * - `int8` in database + * - Nullable, no default + */ + asset2_id: number | null; + /** + * **DexMeanPrice.amount1** + * - `int8` in database + * - `NOT NULL`, no default + */ + amount1: number; + /** + * **DexMeanPrice.amount2** + * - `int8` in database + * - `NOT NULL`, no default + */ + amount2: number; + } + export interface Whereable { + /** + * **DexMeanPrice.id** + * - `int8` in database + * - `NOT NULL`, default: `nextval('"DexMeanPrice_id_seq"'::regclass)` + */ + id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; + /** + * **DexMeanPrice.tx_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + tx_id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; + /** + * **DexMeanPrice.address_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + address_id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; + /** + * **DexMeanPrice.asset1_id** + * - `int8` in database + * - Nullable, no default + */ + asset1_id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; + /** + * **DexMeanPrice.asset2_id** + * - `int8` in database + * - Nullable, no default + */ + asset2_id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; + /** + * **DexMeanPrice.amount1** + * - `int8` in database + * - `NOT NULL`, no default + */ + amount1?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; + /** + * **DexMeanPrice.amount2** + * - `int8` in database + * - `NOT NULL`, no default + */ + amount2?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; + } + export interface Insertable { + /** + * **DexMeanPrice.id** + * - `int8` in database + * - `NOT NULL`, default: `nextval('"DexMeanPrice_id_seq"'::regclass)` + */ + id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.DefaultType | db.SQLFragment; + /** + * **DexMeanPrice.tx_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + tx_id: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment; + /** + * **DexMeanPrice.address_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + address_id: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment; + /** + * **DexMeanPrice.asset1_id** + * - `int8` in database + * - Nullable, no default + */ + asset1_id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | null | db.DefaultType | db.SQLFragment; + /** + * **DexMeanPrice.asset2_id** + * - `int8` in database + * - Nullable, no default + */ + asset2_id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | null | db.DefaultType | db.SQLFragment; + /** + * **DexMeanPrice.amount1** + * - `int8` in database + * - `NOT NULL`, no default + */ + amount1: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment; + /** + * **DexMeanPrice.amount2** + * - `int8` in database + * - `NOT NULL`, no default + */ + amount2: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment; + } + export interface Updatable { + /** + * **DexMeanPrice.id** + * - `int8` in database + * - `NOT NULL`, default: `nextval('"DexMeanPrice_id_seq"'::regclass)` + */ + id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.DefaultType | db.SQLFragment | db.SQLFragment | db.DefaultType | db.SQLFragment>; + /** + * **DexMeanPrice.tx_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + tx_id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.SQLFragment | db.SQLFragment>; + /** + * **DexMeanPrice.address_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + address_id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.SQLFragment | db.SQLFragment>; + /** + * **DexMeanPrice.asset1_id** + * - `int8` in database + * - Nullable, no default + */ + asset1_id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | null | db.DefaultType | db.SQLFragment | db.SQLFragment | null | db.DefaultType | db.SQLFragment>; + /** + * **DexMeanPrice.asset2_id** + * - `int8` in database + * - Nullable, no default + */ + asset2_id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | null | db.DefaultType | db.SQLFragment | db.SQLFragment | null | db.DefaultType | db.SQLFragment>; + /** + * **DexMeanPrice.amount1** + * - `int8` in database + * - `NOT NULL`, no default + */ + amount1?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.SQLFragment | db.SQLFragment>; + /** + * **DexMeanPrice.amount2** + * - `int8` in database + * - `NOT NULL`, no default + */ + amount2?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.SQLFragment | db.SQLFragment>; + } + export type UniqueIndex = 'DexMeanPrice_pkey'; + export type Column = keyof Selectable; + export type OnlyCols = Pick; + export type SQLExpression = Table | db.ColumnNames | db.ColumnValues | Whereable | Column | db.ParentColumn | db.GenericSQLExpression; export type SQL = SQLExpression | SQLExpression[]; } @@ -680,112 +973,368 @@ declare module 'zapatos/schema' { * **NativeAsset** * - Table in database */ - export namespace NativeAsset { - export type Table = 'NativeAsset'; + export namespace NativeAsset { + export type Table = 'NativeAsset'; + export interface Selectable { + /** + * **NativeAsset.id** + * - `int8` in database + * - `NOT NULL`, default: `nextval('"NativeAsset_id_seq"'::regclass)` + */ + id: db.Int8String; + /** + * **NativeAsset.policy_id** + * - `bytea` in database + * - `NOT NULL`, no default + */ + policy_id: Buffer; + /** + * **NativeAsset.asset_name** + * - `bytea` in database + * - `NOT NULL`, no default + */ + asset_name: Buffer; + /** + * **NativeAsset.cip14_fingerprint** + * - `bytea` in database + * - `NOT NULL`, no default + */ + cip14_fingerprint: Buffer; + /** + * **NativeAsset.first_tx** + * - `int8` in database + * - `NOT NULL`, no default + */ + first_tx: db.Int8String; + } + export interface JSONSelectable { + /** + * **NativeAsset.id** + * - `int8` in database + * - `NOT NULL`, default: `nextval('"NativeAsset_id_seq"'::regclass)` + */ + id: number; + /** + * **NativeAsset.policy_id** + * - `bytea` in database + * - `NOT NULL`, no default + */ + policy_id: db.ByteArrayString; + /** + * **NativeAsset.asset_name** + * - `bytea` in database + * - `NOT NULL`, no default + */ + asset_name: db.ByteArrayString; + /** + * **NativeAsset.cip14_fingerprint** + * - `bytea` in database + * - `NOT NULL`, no default + */ + cip14_fingerprint: db.ByteArrayString; + /** + * **NativeAsset.first_tx** + * - `int8` in database + * - `NOT NULL`, no default + */ + first_tx: number; + } + export interface Whereable { + /** + * **NativeAsset.id** + * - `int8` in database + * - `NOT NULL`, default: `nextval('"NativeAsset_id_seq"'::regclass)` + */ + id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; + /** + * **NativeAsset.policy_id** + * - `bytea` in database + * - `NOT NULL`, no default + */ + policy_id?: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; + /** + * **NativeAsset.asset_name** + * - `bytea` in database + * - `NOT NULL`, no default + */ + asset_name?: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; + /** + * **NativeAsset.cip14_fingerprint** + * - `bytea` in database + * - `NOT NULL`, no default + */ + cip14_fingerprint?: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; + /** + * **NativeAsset.first_tx** + * - `int8` in database + * - `NOT NULL`, no default + */ + first_tx?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; + } + export interface Insertable { + /** + * **NativeAsset.id** + * - `int8` in database + * - `NOT NULL`, default: `nextval('"NativeAsset_id_seq"'::regclass)` + */ + id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.DefaultType | db.SQLFragment; + /** + * **NativeAsset.policy_id** + * - `bytea` in database + * - `NOT NULL`, no default + */ + policy_id: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment; + /** + * **NativeAsset.asset_name** + * - `bytea` in database + * - `NOT NULL`, no default + */ + asset_name: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment; + /** + * **NativeAsset.cip14_fingerprint** + * - `bytea` in database + * - `NOT NULL`, no default + */ + cip14_fingerprint: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment; + /** + * **NativeAsset.first_tx** + * - `int8` in database + * - `NOT NULL`, no default + */ + first_tx: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment; + } + export interface Updatable { + /** + * **NativeAsset.id** + * - `int8` in database + * - `NOT NULL`, default: `nextval('"NativeAsset_id_seq"'::regclass)` + */ + id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.DefaultType | db.SQLFragment | db.SQLFragment | db.DefaultType | db.SQLFragment>; + /** + * **NativeAsset.policy_id** + * - `bytea` in database + * - `NOT NULL`, no default + */ + policy_id?: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment | db.SQLFragment | db.SQLFragment>; + /** + * **NativeAsset.asset_name** + * - `bytea` in database + * - `NOT NULL`, no default + */ + asset_name?: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment | db.SQLFragment | db.SQLFragment>; + /** + * **NativeAsset.cip14_fingerprint** + * - `bytea` in database + * - `NOT NULL`, no default + */ + cip14_fingerprint?: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment | db.SQLFragment | db.SQLFragment>; + /** + * **NativeAsset.first_tx** + * - `int8` in database + * - `NOT NULL`, no default + */ + first_tx?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.SQLFragment | db.SQLFragment>; + } + export type UniqueIndex = 'NativeAsset_pkey' | 'index-native_asset-pair'; + export type Column = keyof Selectable; + export type OnlyCols = Pick; + export type SQLExpression = Table | db.ColumnNames | db.ColumnValues | Whereable | Column | db.ParentColumn | db.GenericSQLExpression; + export type SQL = SQLExpression | SQLExpression[]; + } + + /** + * **PlutusData** + * - Table in database + */ + export namespace PlutusData { + export type Table = 'PlutusData'; + export interface Selectable { + /** + * **PlutusData.id** + * - `int8` in database + * - `NOT NULL`, default: `nextval('"PlutusData_id_seq"'::regclass)` + */ + id: db.Int8String; + /** + * **PlutusData.data** + * - `bytea` in database + * - `NOT NULL`, no default + */ + data: Buffer; + } + export interface JSONSelectable { + /** + * **PlutusData.id** + * - `int8` in database + * - `NOT NULL`, default: `nextval('"PlutusData_id_seq"'::regclass)` + */ + id: number; + /** + * **PlutusData.data** + * - `bytea` in database + * - `NOT NULL`, no default + */ + data: db.ByteArrayString; + } + export interface Whereable { + /** + * **PlutusData.id** + * - `int8` in database + * - `NOT NULL`, default: `nextval('"PlutusData_id_seq"'::regclass)` + */ + id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; + /** + * **PlutusData.data** + * - `bytea` in database + * - `NOT NULL`, no default + */ + data?: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; + } + export interface Insertable { + /** + * **PlutusData.id** + * - `int8` in database + * - `NOT NULL`, default: `nextval('"PlutusData_id_seq"'::regclass)` + */ + id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.DefaultType | db.SQLFragment; + /** + * **PlutusData.data** + * - `bytea` in database + * - `NOT NULL`, no default + */ + data: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment; + } + export interface Updatable { + /** + * **PlutusData.id** + * - `int8` in database + * - `NOT NULL`, default: `nextval('"PlutusData_id_seq"'::regclass)` + */ + id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.DefaultType | db.SQLFragment | db.SQLFragment | db.DefaultType | db.SQLFragment>; + /** + * **PlutusData.data** + * - `bytea` in database + * - `NOT NULL`, no default + */ + data?: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment | db.SQLFragment | db.SQLFragment>; + } + export type UniqueIndex = 'PlutusData_pkey'; + export type Column = keyof Selectable; + export type OnlyCols = Pick; + export type SQLExpression = Table | db.ColumnNames | db.ColumnValues | Whereable | Column | db.ParentColumn | db.GenericSQLExpression; + export type SQL = SQLExpression | SQLExpression[]; + } + + /** + * **PlutusDataHash** + * - Table in database + */ + export namespace PlutusDataHash { + export type Table = 'PlutusDataHash'; export interface Selectable { /** - * **NativeAsset.id** + * **PlutusDataHash.id** * - `int8` in database - * - `NOT NULL`, default: `nextval('"NativeAsset_id_seq"'::regclass)` + * - `NOT NULL`, default: `nextval('"PlutusDataHash_id_seq"'::regclass)` */ id: db.Int8String; /** - * **NativeAsset.policy_id** + * **PlutusDataHash.hash** * - `bytea` in database * - `NOT NULL`, no default */ - policy_id: Buffer; + hash: Buffer; /** - * **NativeAsset.asset_name** - * - `bytea` in database + * **PlutusDataHash.first_tx** + * - `int8` in database * - `NOT NULL`, no default */ - asset_name: Buffer; + first_tx: db.Int8String; } export interface JSONSelectable { /** - * **NativeAsset.id** + * **PlutusDataHash.id** * - `int8` in database - * - `NOT NULL`, default: `nextval('"NativeAsset_id_seq"'::regclass)` + * - `NOT NULL`, default: `nextval('"PlutusDataHash_id_seq"'::regclass)` */ id: number; /** - * **NativeAsset.policy_id** + * **PlutusDataHash.hash** * - `bytea` in database * - `NOT NULL`, no default */ - policy_id: db.ByteArrayString; + hash: db.ByteArrayString; /** - * **NativeAsset.asset_name** - * - `bytea` in database + * **PlutusDataHash.first_tx** + * - `int8` in database * - `NOT NULL`, no default */ - asset_name: db.ByteArrayString; + first_tx: number; } export interface Whereable { /** - * **NativeAsset.id** + * **PlutusDataHash.id** * - `int8` in database - * - `NOT NULL`, default: `nextval('"NativeAsset_id_seq"'::regclass)` + * - `NOT NULL`, default: `nextval('"PlutusDataHash_id_seq"'::regclass)` */ id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; /** - * **NativeAsset.policy_id** + * **PlutusDataHash.hash** * - `bytea` in database * - `NOT NULL`, no default */ - policy_id?: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; + hash?: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; /** - * **NativeAsset.asset_name** - * - `bytea` in database + * **PlutusDataHash.first_tx** + * - `int8` in database * - `NOT NULL`, no default */ - asset_name?: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; + first_tx?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; } export interface Insertable { /** - * **NativeAsset.id** + * **PlutusDataHash.id** * - `int8` in database - * - `NOT NULL`, default: `nextval('"NativeAsset_id_seq"'::regclass)` + * - `NOT NULL`, default: `nextval('"PlutusDataHash_id_seq"'::regclass)` */ id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.DefaultType | db.SQLFragment; /** - * **NativeAsset.policy_id** + * **PlutusDataHash.hash** * - `bytea` in database * - `NOT NULL`, no default */ - policy_id: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment; + hash: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment; /** - * **NativeAsset.asset_name** - * - `bytea` in database + * **PlutusDataHash.first_tx** + * - `int8` in database * - `NOT NULL`, no default */ - asset_name: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment; + first_tx: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment; } export interface Updatable { /** - * **NativeAsset.id** + * **PlutusDataHash.id** * - `int8` in database - * - `NOT NULL`, default: `nextval('"NativeAsset_id_seq"'::regclass)` + * - `NOT NULL`, default: `nextval('"PlutusDataHash_id_seq"'::regclass)` */ id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.DefaultType | db.SQLFragment | db.SQLFragment | db.DefaultType | db.SQLFragment>; /** - * **NativeAsset.policy_id** + * **PlutusDataHash.hash** * - `bytea` in database * - `NOT NULL`, no default */ - policy_id?: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment | db.SQLFragment | db.SQLFragment>; + hash?: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment | db.SQLFragment | db.SQLFragment>; /** - * **NativeAsset.asset_name** - * - `bytea` in database + * **PlutusDataHash.first_tx** + * - `int8` in database * - `NOT NULL`, no default */ - asset_name?: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment | db.SQLFragment | db.SQLFragment>; + first_tx?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.SQLFragment | db.SQLFragment>; } - export type UniqueIndex = 'NativeAsset_pkey' | 'index-native_asset-pair'; + export type UniqueIndex = 'PlutusDataHash_pkey' | 'index-plutus_data_hash-hash'; export type Column = keyof Selectable; export type OnlyCols = Pick; - export type SQLExpression = db.GenericSQLExpression | db.ColumnNames | db.ColumnValues | Table | Whereable | Column; + export type SQLExpression = Table | db.ColumnNames | db.ColumnValues | Whereable | Column | db.ParentColumn | db.GenericSQLExpression; export type SQL = SQLExpression | SQLExpression[]; } @@ -808,6 +1357,12 @@ declare module 'zapatos/schema' { * - `NOT NULL`, no default */ credential: Buffer; + /** + * **StakeCredential.first_tx** + * - `int8` in database + * - `NOT NULL`, no default + */ + first_tx: db.Int8String; } export interface JSONSelectable { /** @@ -822,6 +1377,12 @@ declare module 'zapatos/schema' { * - `NOT NULL`, no default */ credential: db.ByteArrayString; + /** + * **StakeCredential.first_tx** + * - `int8` in database + * - `NOT NULL`, no default + */ + first_tx: number; } export interface Whereable { /** @@ -836,6 +1397,12 @@ declare module 'zapatos/schema' { * - `NOT NULL`, no default */ credential?: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; + /** + * **StakeCredential.first_tx** + * - `int8` in database + * - `NOT NULL`, no default + */ + first_tx?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; } export interface Insertable { /** @@ -850,6 +1417,12 @@ declare module 'zapatos/schema' { * - `NOT NULL`, no default */ credential: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment; + /** + * **StakeCredential.first_tx** + * - `int8` in database + * - `NOT NULL`, no default + */ + first_tx: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment; } export interface Updatable { /** @@ -864,11 +1437,17 @@ declare module 'zapatos/schema' { * - `NOT NULL`, no default */ credential?: (db.ByteArrayString | Buffer) | db.Parameter<(db.ByteArrayString | Buffer)> | db.SQLFragment | db.SQLFragment | db.SQLFragment>; + /** + * **StakeCredential.first_tx** + * - `int8` in database + * - `NOT NULL`, no default + */ + first_tx?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.SQLFragment | db.SQLFragment>; } export type UniqueIndex = 'StakeCredential_credential_key' | 'StakeCredential_pkey'; export type Column = keyof Selectable; export type OnlyCols = Pick; - export type SQLExpression = db.GenericSQLExpression | db.ColumnNames | db.ColumnValues | Table | Whereable | Column; + export type SQLExpression = Table | db.ColumnNames | db.ColumnValues | Whereable | Column | db.ParentColumn | db.GenericSQLExpression; export type SQL = SQLExpression | SQLExpression[]; } @@ -1071,7 +1650,7 @@ declare module 'zapatos/schema' { export type UniqueIndex = 'Transaction_hash_key' | 'Transaction_pkey'; export type Column = keyof Selectable; export type OnlyCols = Pick; - export type SQLExpression = db.GenericSQLExpression | db.ColumnNames | db.ColumnValues | Table | Whereable | Column; + export type SQLExpression = Table | db.ColumnNames | db.ColumnValues | Whereable | Column | db.ParentColumn | db.GenericSQLExpression; export type SQL = SQLExpression | SQLExpression[]; } @@ -1101,6 +1680,12 @@ declare module 'zapatos/schema' { */ tx_id: db.Int8String; /** + * **TransactionInput.address_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + address_id: db.Int8String; + /** * **TransactionInput.input_index** * - `int4` in database * - `NOT NULL`, no default @@ -1127,6 +1712,12 @@ declare module 'zapatos/schema' { */ tx_id: number; /** + * **TransactionInput.address_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + address_id: number; + /** * **TransactionInput.input_index** * - `int4` in database * - `NOT NULL`, no default @@ -1153,6 +1744,12 @@ declare module 'zapatos/schema' { */ tx_id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; /** + * **TransactionInput.address_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + address_id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; + /** * **TransactionInput.input_index** * - `int4` in database * - `NOT NULL`, no default @@ -1179,6 +1776,12 @@ declare module 'zapatos/schema' { */ tx_id: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment; /** + * **TransactionInput.address_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + address_id: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment; + /** * **TransactionInput.input_index** * - `int4` in database * - `NOT NULL`, no default @@ -1205,6 +1808,12 @@ declare module 'zapatos/schema' { */ tx_id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.SQLFragment | db.SQLFragment>; /** + * **TransactionInput.address_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + address_id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.SQLFragment | db.SQLFragment>; + /** * **TransactionInput.input_index** * - `int4` in database * - `NOT NULL`, no default @@ -1214,7 +1823,7 @@ declare module 'zapatos/schema' { export type UniqueIndex = 'TransactionInput_pkey'; export type Column = keyof Selectable; export type OnlyCols = Pick; - export type SQLExpression = db.GenericSQLExpression | db.ColumnNames | db.ColumnValues | Table | Whereable | Column; + export type SQLExpression = Table | db.ColumnNames | db.ColumnValues | Whereable | Column | db.ParentColumn | db.GenericSQLExpression; export type SQL = SQLExpression | SQLExpression[]; } @@ -1357,7 +1966,7 @@ declare module 'zapatos/schema' { export type UniqueIndex = 'TransactionMetadata_pkey'; export type Column = keyof Selectable; export type OnlyCols = Pick; - export type SQLExpression = db.GenericSQLExpression | db.ColumnNames | db.ColumnValues | Table | Whereable | Column; + export type SQLExpression = Table | db.ColumnNames | db.ColumnValues | Whereable | Column | db.ParentColumn | db.GenericSQLExpression; export type SQL = SQLExpression | SQLExpression[]; } @@ -1530,7 +2139,180 @@ declare module 'zapatos/schema' { export type UniqueIndex = 'TransactionOutput_pkey'; export type Column = keyof Selectable; export type OnlyCols = Pick; - export type SQLExpression = db.GenericSQLExpression | db.ColumnNames | db.ColumnValues | Table | Whereable | Column; + export type SQLExpression = Table | db.ColumnNames | db.ColumnValues | Whereable | Column | db.ParentColumn | db.GenericSQLExpression; + export type SQL = SQLExpression | SQLExpression[]; + } + + /** + * **TransactionReferenceInput** + * - Table in database + */ + export namespace TransactionReferenceInput { + export type Table = 'TransactionReferenceInput'; + export interface Selectable { + /** + * **TransactionReferenceInput.id** + * - `int8` in database + * - `NOT NULL`, default: `nextval('"TransactionReferenceInput_id_seq"'::regclass)` + */ + id: db.Int8String; + /** + * **TransactionReferenceInput.utxo_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + utxo_id: db.Int8String; + /** + * **TransactionReferenceInput.tx_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + tx_id: db.Int8String; + /** + * **TransactionReferenceInput.address_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + address_id: db.Int8String; + /** + * **TransactionReferenceInput.input_index** + * - `int4` in database + * - `NOT NULL`, no default + */ + input_index: number; + } + export interface JSONSelectable { + /** + * **TransactionReferenceInput.id** + * - `int8` in database + * - `NOT NULL`, default: `nextval('"TransactionReferenceInput_id_seq"'::regclass)` + */ + id: number; + /** + * **TransactionReferenceInput.utxo_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + utxo_id: number; + /** + * **TransactionReferenceInput.tx_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + tx_id: number; + /** + * **TransactionReferenceInput.address_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + address_id: number; + /** + * **TransactionReferenceInput.input_index** + * - `int4` in database + * - `NOT NULL`, no default + */ + input_index: number; + } + export interface Whereable { + /** + * **TransactionReferenceInput.id** + * - `int8` in database + * - `NOT NULL`, default: `nextval('"TransactionReferenceInput_id_seq"'::regclass)` + */ + id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; + /** + * **TransactionReferenceInput.utxo_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + utxo_id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; + /** + * **TransactionReferenceInput.tx_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + tx_id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; + /** + * **TransactionReferenceInput.address_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + address_id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; + /** + * **TransactionReferenceInput.input_index** + * - `int4` in database + * - `NOT NULL`, no default + */ + input_index?: number | db.Parameter | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; + } + export interface Insertable { + /** + * **TransactionReferenceInput.id** + * - `int8` in database + * - `NOT NULL`, default: `nextval('"TransactionReferenceInput_id_seq"'::regclass)` + */ + id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.DefaultType | db.SQLFragment; + /** + * **TransactionReferenceInput.utxo_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + utxo_id: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment; + /** + * **TransactionReferenceInput.tx_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + tx_id: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment; + /** + * **TransactionReferenceInput.address_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + address_id: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment; + /** + * **TransactionReferenceInput.input_index** + * - `int4` in database + * - `NOT NULL`, no default + */ + input_index: number | db.Parameter | db.SQLFragment; + } + export interface Updatable { + /** + * **TransactionReferenceInput.id** + * - `int8` in database + * - `NOT NULL`, default: `nextval('"TransactionReferenceInput_id_seq"'::regclass)` + */ + id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.DefaultType | db.SQLFragment | db.SQLFragment | db.DefaultType | db.SQLFragment>; + /** + * **TransactionReferenceInput.utxo_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + utxo_id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.SQLFragment | db.SQLFragment>; + /** + * **TransactionReferenceInput.tx_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + tx_id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.SQLFragment | db.SQLFragment>; + /** + * **TransactionReferenceInput.address_id** + * - `int8` in database + * - `NOT NULL`, no default + */ + address_id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.SQLFragment | db.SQLFragment>; + /** + * **TransactionReferenceInput.input_index** + * - `int4` in database + * - `NOT NULL`, no default + */ + input_index?: number | db.Parameter | db.SQLFragment | db.SQLFragment | db.SQLFragment>; + } + export type UniqueIndex = 'TransactionReferenceInput_pkey'; + export type Column = keyof Selectable; + export type OnlyCols = Pick; + export type SQLExpression = Table | db.ColumnNames | db.ColumnValues | Whereable | Column | db.ParentColumn | db.GenericSQLExpression; export type SQL = SQLExpression | SQLExpression[]; } @@ -1554,12 +2336,6 @@ declare module 'zapatos/schema' { */ tx_id: db.Int8String; /** - * **TxCredentialRelation.block_id** - * - `int4` in database - * - `NOT NULL`, no default - */ - block_id: number; - /** * **TxCredentialRelation.relation** * - `int4` in database * - `NOT NULL`, no default @@ -1580,12 +2356,6 @@ declare module 'zapatos/schema' { */ tx_id: number; /** - * **TxCredentialRelation.block_id** - * - `int4` in database - * - `NOT NULL`, no default - */ - block_id: number; - /** * **TxCredentialRelation.relation** * - `int4` in database * - `NOT NULL`, no default @@ -1606,12 +2376,6 @@ declare module 'zapatos/schema' { */ tx_id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; /** - * **TxCredentialRelation.block_id** - * - `int4` in database - * - `NOT NULL`, no default - */ - block_id?: number | db.Parameter | db.SQLFragment | db.ParentColumn | db.SQLFragment | db.SQLFragment | db.ParentColumn>; - /** * **TxCredentialRelation.relation** * - `int4` in database * - `NOT NULL`, no default @@ -1632,12 +2396,6 @@ declare module 'zapatos/schema' { */ tx_id: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment; /** - * **TxCredentialRelation.block_id** - * - `int4` in database - * - `NOT NULL`, no default - */ - block_id: number | db.Parameter | db.SQLFragment; - /** * **TxCredentialRelation.relation** * - `int4` in database * - `NOT NULL`, no default @@ -1658,12 +2416,6 @@ declare module 'zapatos/schema' { */ tx_id?: (number | db.Int8String) | db.Parameter<(number | db.Int8String)> | db.SQLFragment | db.SQLFragment | db.SQLFragment>; /** - * **TxCredentialRelation.block_id** - * - `int4` in database - * - `NOT NULL`, no default - */ - block_id?: number | db.Parameter | db.SQLFragment | db.SQLFragment | db.SQLFragment>; - /** * **TxCredentialRelation.relation** * - `int4` in database * - `NOT NULL`, no default @@ -1673,25 +2425,25 @@ declare module 'zapatos/schema' { export type UniqueIndex = 'tx_credential-pk'; export type Column = keyof Selectable; export type OnlyCols = Pick; - export type SQLExpression = db.GenericSQLExpression | db.ColumnNames | db.ColumnValues | Table | Whereable | Column; + export type SQLExpression = Table | db.ColumnNames | db.ColumnValues | Whereable | Column | db.ParentColumn | db.GenericSQLExpression; export type SQL = SQLExpression | SQLExpression[]; } /* === cross-table types === */ - export type Table = Address.Table | AddressCredentialRelation.Table | AssetMint.Table | Block.Table | Cip25Entry.Table | NativeAsset.Table | StakeCredential.Table | Transaction.Table | TransactionInput.Table | TransactionMetadata.Table | TransactionOutput.Table | TxCredentialRelation.Table; - export type Selectable = Address.Selectable | AddressCredentialRelation.Selectable | AssetMint.Selectable | Block.Selectable | Cip25Entry.Selectable | NativeAsset.Selectable | StakeCredential.Selectable | Transaction.Selectable | TransactionInput.Selectable | TransactionMetadata.Selectable | TransactionOutput.Selectable | TxCredentialRelation.Selectable; - export type JSONSelectable = Address.JSONSelectable | AddressCredentialRelation.JSONSelectable | AssetMint.JSONSelectable | Block.JSONSelectable | Cip25Entry.JSONSelectable | NativeAsset.JSONSelectable | StakeCredential.JSONSelectable | Transaction.JSONSelectable | TransactionInput.JSONSelectable | TransactionMetadata.JSONSelectable | TransactionOutput.JSONSelectable | TxCredentialRelation.JSONSelectable; - export type Whereable = Address.Whereable | AddressCredentialRelation.Whereable | AssetMint.Whereable | Block.Whereable | Cip25Entry.Whereable | NativeAsset.Whereable | StakeCredential.Whereable | Transaction.Whereable | TransactionInput.Whereable | TransactionMetadata.Whereable | TransactionOutput.Whereable | TxCredentialRelation.Whereable; - export type Insertable = Address.Insertable | AddressCredentialRelation.Insertable | AssetMint.Insertable | Block.Insertable | Cip25Entry.Insertable | NativeAsset.Insertable | StakeCredential.Insertable | Transaction.Insertable | TransactionInput.Insertable | TransactionMetadata.Insertable | TransactionOutput.Insertable | TxCredentialRelation.Insertable; - export type Updatable = Address.Updatable | AddressCredentialRelation.Updatable | AssetMint.Updatable | Block.Updatable | Cip25Entry.Updatable | NativeAsset.Updatable | StakeCredential.Updatable | Transaction.Updatable | TransactionInput.Updatable | TransactionMetadata.Updatable | TransactionOutput.Updatable | TxCredentialRelation.Updatable; - export type UniqueIndex = Address.UniqueIndex | AddressCredentialRelation.UniqueIndex | AssetMint.UniqueIndex | Block.UniqueIndex | Cip25Entry.UniqueIndex | NativeAsset.UniqueIndex | StakeCredential.UniqueIndex | Transaction.UniqueIndex | TransactionInput.UniqueIndex | TransactionMetadata.UniqueIndex | TransactionOutput.UniqueIndex | TxCredentialRelation.UniqueIndex; - export type Column = Address.Column | AddressCredentialRelation.Column | AssetMint.Column | Block.Column | Cip25Entry.Column | NativeAsset.Column | StakeCredential.Column | Transaction.Column | TransactionInput.Column | TransactionMetadata.Column | TransactionOutput.Column | TxCredentialRelation.Column; - export type AllBaseTables = [Address.Table, AddressCredentialRelation.Table, AssetMint.Table, Block.Table, Cip25Entry.Table, NativeAsset.Table, StakeCredential.Table, Transaction.Table, TransactionInput.Table, TransactionMetadata.Table, TransactionOutput.Table, TxCredentialRelation.Table]; + export type Table = Address.Table | AddressCredentialRelation.Table | AssetMint.Table | Block.Table | Cip25Entry.Table | DexMeanPrice.Table | NativeAsset.Table | PlutusData.Table | PlutusDataHash.Table | StakeCredential.Table | Transaction.Table | TransactionInput.Table | TransactionMetadata.Table | TransactionOutput.Table | TransactionReferenceInput.Table | TxCredentialRelation.Table; + export type Selectable = Address.Selectable | AddressCredentialRelation.Selectable | AssetMint.Selectable | Block.Selectable | Cip25Entry.Selectable | DexMeanPrice.Selectable | NativeAsset.Selectable | PlutusData.Selectable | PlutusDataHash.Selectable | StakeCredential.Selectable | Transaction.Selectable | TransactionInput.Selectable | TransactionMetadata.Selectable | TransactionOutput.Selectable | TransactionReferenceInput.Selectable | TxCredentialRelation.Selectable; + export type JSONSelectable = Address.JSONSelectable | AddressCredentialRelation.JSONSelectable | AssetMint.JSONSelectable | Block.JSONSelectable | Cip25Entry.JSONSelectable | DexMeanPrice.JSONSelectable | NativeAsset.JSONSelectable | PlutusData.JSONSelectable | PlutusDataHash.JSONSelectable | StakeCredential.JSONSelectable | Transaction.JSONSelectable | TransactionInput.JSONSelectable | TransactionMetadata.JSONSelectable | TransactionOutput.JSONSelectable | TransactionReferenceInput.JSONSelectable | TxCredentialRelation.JSONSelectable; + export type Whereable = Address.Whereable | AddressCredentialRelation.Whereable | AssetMint.Whereable | Block.Whereable | Cip25Entry.Whereable | DexMeanPrice.Whereable | NativeAsset.Whereable | PlutusData.Whereable | PlutusDataHash.Whereable | StakeCredential.Whereable | Transaction.Whereable | TransactionInput.Whereable | TransactionMetadata.Whereable | TransactionOutput.Whereable | TransactionReferenceInput.Whereable | TxCredentialRelation.Whereable; + export type Insertable = Address.Insertable | AddressCredentialRelation.Insertable | AssetMint.Insertable | Block.Insertable | Cip25Entry.Insertable | DexMeanPrice.Insertable | NativeAsset.Insertable | PlutusData.Insertable | PlutusDataHash.Insertable | StakeCredential.Insertable | Transaction.Insertable | TransactionInput.Insertable | TransactionMetadata.Insertable | TransactionOutput.Insertable | TransactionReferenceInput.Insertable | TxCredentialRelation.Insertable; + export type Updatable = Address.Updatable | AddressCredentialRelation.Updatable | AssetMint.Updatable | Block.Updatable | Cip25Entry.Updatable | DexMeanPrice.Updatable | NativeAsset.Updatable | PlutusData.Updatable | PlutusDataHash.Updatable | StakeCredential.Updatable | Transaction.Updatable | TransactionInput.Updatable | TransactionMetadata.Updatable | TransactionOutput.Updatable | TransactionReferenceInput.Updatable | TxCredentialRelation.Updatable; + export type UniqueIndex = Address.UniqueIndex | AddressCredentialRelation.UniqueIndex | AssetMint.UniqueIndex | Block.UniqueIndex | Cip25Entry.UniqueIndex | DexMeanPrice.UniqueIndex | NativeAsset.UniqueIndex | PlutusData.UniqueIndex | PlutusDataHash.UniqueIndex | StakeCredential.UniqueIndex | Transaction.UniqueIndex | TransactionInput.UniqueIndex | TransactionMetadata.UniqueIndex | TransactionOutput.UniqueIndex | TransactionReferenceInput.UniqueIndex | TxCredentialRelation.UniqueIndex; + export type Column = Address.Column | AddressCredentialRelation.Column | AssetMint.Column | Block.Column | Cip25Entry.Column | DexMeanPrice.Column | NativeAsset.Column | PlutusData.Column | PlutusDataHash.Column | StakeCredential.Column | Transaction.Column | TransactionInput.Column | TransactionMetadata.Column | TransactionOutput.Column | TransactionReferenceInput.Column | TxCredentialRelation.Column; + export type AllBaseTables = [Address.Table, AddressCredentialRelation.Table, AssetMint.Table, Block.Table, Cip25Entry.Table, DexMeanPrice.Table, NativeAsset.Table, PlutusData.Table, PlutusDataHash.Table, StakeCredential.Table, Transaction.Table, TransactionInput.Table, TransactionMetadata.Table, TransactionOutput.Table, TransactionReferenceInput.Table, TxCredentialRelation.Table]; export type AllForeignTables = []; export type AllViews = []; export type AllMaterializedViews = []; - export type AllTablesAndViews = [Address.Table, AddressCredentialRelation.Table, AssetMint.Table, Block.Table, Cip25Entry.Table, NativeAsset.Table, StakeCredential.Table, Transaction.Table, TransactionInput.Table, TransactionMetadata.Table, TransactionOutput.Table, TxCredentialRelation.Table]; + export type AllTablesAndViews = [Address.Table, AddressCredentialRelation.Table, AssetMint.Table, Block.Table, Cip25Entry.Table, DexMeanPrice.Table, NativeAsset.Table, PlutusData.Table, PlutusDataHash.Table, StakeCredential.Table, Transaction.Table, TransactionInput.Table, TransactionMetadata.Table, TransactionOutput.Table, TransactionReferenceInput.Table, TxCredentialRelation.Table]; export type SelectableForTable = { @@ -1700,12 +2452,16 @@ declare module 'zapatos/schema' { AssetMint: AssetMint.Selectable; Block: Block.Selectable; Cip25Entry: Cip25Entry.Selectable; + DexMeanPrice: DexMeanPrice.Selectable; NativeAsset: NativeAsset.Selectable; + PlutusData: PlutusData.Selectable; + PlutusDataHash: PlutusDataHash.Selectable; StakeCredential: StakeCredential.Selectable; Transaction: Transaction.Selectable; TransactionInput: TransactionInput.Selectable; TransactionMetadata: TransactionMetadata.Selectable; TransactionOutput: TransactionOutput.Selectable; + TransactionReferenceInput: TransactionReferenceInput.Selectable; TxCredentialRelation: TxCredentialRelation.Selectable; }[T]; @@ -1715,12 +2471,16 @@ declare module 'zapatos/schema' { AssetMint: AssetMint.JSONSelectable; Block: Block.JSONSelectable; Cip25Entry: Cip25Entry.JSONSelectable; + DexMeanPrice: DexMeanPrice.JSONSelectable; NativeAsset: NativeAsset.JSONSelectable; + PlutusData: PlutusData.JSONSelectable; + PlutusDataHash: PlutusDataHash.JSONSelectable; StakeCredential: StakeCredential.JSONSelectable; Transaction: Transaction.JSONSelectable; TransactionInput: TransactionInput.JSONSelectable; TransactionMetadata: TransactionMetadata.JSONSelectable; TransactionOutput: TransactionOutput.JSONSelectable; + TransactionReferenceInput: TransactionReferenceInput.JSONSelectable; TxCredentialRelation: TxCredentialRelation.JSONSelectable; }[T]; @@ -1730,12 +2490,16 @@ declare module 'zapatos/schema' { AssetMint: AssetMint.Whereable; Block: Block.Whereable; Cip25Entry: Cip25Entry.Whereable; + DexMeanPrice: DexMeanPrice.Whereable; NativeAsset: NativeAsset.Whereable; + PlutusData: PlutusData.Whereable; + PlutusDataHash: PlutusDataHash.Whereable; StakeCredential: StakeCredential.Whereable; Transaction: Transaction.Whereable; TransactionInput: TransactionInput.Whereable; TransactionMetadata: TransactionMetadata.Whereable; TransactionOutput: TransactionOutput.Whereable; + TransactionReferenceInput: TransactionReferenceInput.Whereable; TxCredentialRelation: TxCredentialRelation.Whereable; }[T]; @@ -1745,12 +2509,16 @@ declare module 'zapatos/schema' { AssetMint: AssetMint.Insertable; Block: Block.Insertable; Cip25Entry: Cip25Entry.Insertable; + DexMeanPrice: DexMeanPrice.Insertable; NativeAsset: NativeAsset.Insertable; + PlutusData: PlutusData.Insertable; + PlutusDataHash: PlutusDataHash.Insertable; StakeCredential: StakeCredential.Insertable; Transaction: Transaction.Insertable; TransactionInput: TransactionInput.Insertable; TransactionMetadata: TransactionMetadata.Insertable; TransactionOutput: TransactionOutput.Insertable; + TransactionReferenceInput: TransactionReferenceInput.Insertable; TxCredentialRelation: TxCredentialRelation.Insertable; }[T]; @@ -1760,12 +2528,16 @@ declare module 'zapatos/schema' { AssetMint: AssetMint.Updatable; Block: Block.Updatable; Cip25Entry: Cip25Entry.Updatable; + DexMeanPrice: DexMeanPrice.Updatable; NativeAsset: NativeAsset.Updatable; + PlutusData: PlutusData.Updatable; + PlutusDataHash: PlutusDataHash.Updatable; StakeCredential: StakeCredential.Updatable; Transaction: Transaction.Updatable; TransactionInput: TransactionInput.Updatable; TransactionMetadata: TransactionMetadata.Updatable; TransactionOutput: TransactionOutput.Updatable; + TransactionReferenceInput: TransactionReferenceInput.Updatable; TxCredentialRelation: TxCredentialRelation.Updatable; }[T]; @@ -1775,12 +2547,16 @@ declare module 'zapatos/schema' { AssetMint: AssetMint.UniqueIndex; Block: Block.UniqueIndex; Cip25Entry: Cip25Entry.UniqueIndex; + DexMeanPrice: DexMeanPrice.UniqueIndex; NativeAsset: NativeAsset.UniqueIndex; + PlutusData: PlutusData.UniqueIndex; + PlutusDataHash: PlutusDataHash.UniqueIndex; StakeCredential: StakeCredential.UniqueIndex; Transaction: Transaction.UniqueIndex; TransactionInput: TransactionInput.UniqueIndex; TransactionMetadata: TransactionMetadata.UniqueIndex; TransactionOutput: TransactionOutput.UniqueIndex; + TransactionReferenceInput: TransactionReferenceInput.UniqueIndex; TxCredentialRelation: TxCredentialRelation.UniqueIndex; }[T]; @@ -1790,12 +2566,16 @@ declare module 'zapatos/schema' { AssetMint: AssetMint.Column; Block: Block.Column; Cip25Entry: Cip25Entry.Column; + DexMeanPrice: DexMeanPrice.Column; NativeAsset: NativeAsset.Column; + PlutusData: PlutusData.Column; + PlutusDataHash: PlutusDataHash.Column; StakeCredential: StakeCredential.Column; Transaction: Transaction.Column; TransactionInput: TransactionInput.Column; TransactionMetadata: TransactionMetadata.Column; TransactionOutput: TransactionOutput.Column; + TransactionReferenceInput: TransactionReferenceInput.Column; TxCredentialRelation: TxCredentialRelation.Column; }[T]; @@ -1805,12 +2585,16 @@ declare module 'zapatos/schema' { AssetMint: AssetMint.SQL; Block: Block.SQL; Cip25Entry: Cip25Entry.SQL; + DexMeanPrice: DexMeanPrice.SQL; NativeAsset: NativeAsset.SQL; + PlutusData: PlutusData.SQL; + PlutusDataHash: PlutusDataHash.SQL; StakeCredential: StakeCredential.SQL; Transaction: Transaction.SQL; TransactionInput: TransactionInput.SQL; TransactionMetadata: TransactionMetadata.SQL; TransactionOutput: TransactionOutput.SQL; + TransactionReferenceInput: TransactionReferenceInput.SQL; TxCredentialRelation: TxCredentialRelation.SQL; }[T]; diff --git a/webserver/server/app/services/DexMeanPrice.ts b/webserver/server/app/services/DexMeanPrice.ts new file mode 100644 index 00000000..abc99ce6 --- /dev/null +++ b/webserver/server/app/services/DexMeanPrice.ts @@ -0,0 +1,55 @@ +import type { Asset, DexMeanPriceResponse } from '../../../shared/models/DexMeanPrice'; +import type { PoolClient } from 'pg'; +import type { TransactionPaginationType } from './PaginationService'; +import { sqlDexMeanPrice } from '../models/dex/sqlDexMeanPrice.queries'; + +function parseAssetItem(s: string | undefined | null): Buffer { + // For the sake of the query, we represent ADA as ('', '') instead of (NULL, NULL). + // (see sqlDexMeanPrice.queries.sql for details) + return Buffer.from(s ?? "", 'hex'); +} + +function serializeAsset(policyId: Buffer | null, assetName: Buffer | null): Asset { + if (policyId === null && assetName === null) { + return null; + } + if (policyId !== null && assetName !== null) { + return { + policyId: policyId.toString('hex'), + assetName: assetName.toString('hex'), + }; + } + throw new Error('Invalid asset query response'); // should be unreachable +} + +export async function dexMeanPrices( + request: TransactionPaginationType & { + dbTx: PoolClient; + addresses: Buffer[]; + reverseMap: Map>; + assetPairs: {asset1: Asset, asset2: Asset}[]; + limit: number; + } +): Promise { + if (request.addresses.length === 0 || request.assetPairs.length === 0) return { meanPrices: [] }; + const meanPrices = await sqlDexMeanPrice.run({ + after_tx_id: (request.after?.tx_id ?? -1)?.toString(), + until_tx_id: request.until.tx_id.toString(), + addresses: request.addresses, + policy_id1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.policyId)), + asset_name1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.assetName)), + policy_id2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.policyId)), + asset_name2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.assetName)), + limit: request.limit.toString(), + }, request.dbTx); + return { + meanPrices: meanPrices.map(result => ({ + tx_hash: result.tx_hash.toString('hex'), + address: [...(request.reverseMap.get(result.address.toString('hex')) ?? [])][0], + asset1: serializeAsset(result.policy_id1, result.asset_name1), + asset2: serializeAsset(result.policy_id2, result.asset_name2), + amount1: result.amount1, + amount2: result.amount2, + })), + }; +} diff --git a/webserver/shared/constants.ts b/webserver/shared/constants.ts index ff58ceee..dcc104d3 100644 --- a/webserver/shared/constants.ts +++ b/webserver/shared/constants.ts @@ -20,3 +20,9 @@ export const CREDENTIAL_LIMIT = { export const BLOCK_LIMIT = { OFFSET: 21600, // k parameter }; + +export const DEX_PRICE_LIMIT = { + REQUEST_ADDRESSES: 100, + REQUEST_ASSET_PAIRS: 100, + RESPONSE: 1000, +}; diff --git a/webserver/shared/errors.ts b/webserver/shared/errors.ts index 9f9cd702..8953a6cf 100644 --- a/webserver/shared/errors.ts +++ b/webserver/shared/errors.ts @@ -11,6 +11,7 @@ export enum ErrorCodes { OffsetBlockNotFound = 7, AssetLimitExceeded = 8, CredentialLimitExceeded = 9, + AssetPairLimitExceeded = 10, } export type ErrorShape = { @@ -79,6 +80,12 @@ export const Errors = { detailsGen: (details: { limit: number; found: number }) => `Limit of ${details.limit}, found ${details.found}`, }, + AssetPairLimitExceeded: { + code: ErrorCodes.AssetPairLimitExceeded, + prefix: "Exceeded request asset pair limit.", + detailsGen: (details: { limit: number; found: number }) => + `Limit of ${details.limit}, found ${details.found}`, + }, } as const; export function genErrorMessage( diff --git a/webserver/shared/models/DexMeanPrice.ts b/webserver/shared/models/DexMeanPrice.ts new file mode 100644 index 00000000..2809034b --- /dev/null +++ b/webserver/shared/models/DexMeanPrice.ts @@ -0,0 +1,33 @@ +import { Address } from "./Address"; +import { Pagination } from "./common"; +import { AssetName, PolicyId } from "./PolicyIdAssetMap"; + +export type Asset = { + policyId: PolicyId; + assetName: AssetName; +} | null; + +/** + * @example "2042352568679" + */ +type Amount = string; // uint64 + +export type DexMeanPrice = { + tx_hash: string; + address: Address; + asset1: Asset; + asset2: Asset; + amount1: Amount; + amount2: Amount; +} + +export type DexMeanPriceRequest = { + addresses: Address[], + assetPairs: {asset1: Asset, asset2: Asset}[]; + /** Defaults to `DEX_PRICE_LIMIT.RESPONSE` */ + limit?: number; +} & Pagination; + +export type DexMeanPriceResponse = { + meanPrices: DexMeanPrice[]; +}; diff --git a/webserver/shared/models/PolicyIdAssetMap.ts b/webserver/shared/models/PolicyIdAssetMap.ts index 3ec17f7f..a25ae333 100644 --- a/webserver/shared/models/PolicyIdAssetMap.ts +++ b/webserver/shared/models/PolicyIdAssetMap.ts @@ -2,12 +2,12 @@ * @pattern [0-9a-fA-F]{56} * @example "b863bc7369f46136ac1048adb2fa7dae3af944c3bbb2be2f216a8d4f" */ -type PolicyId = string; +export type PolicyId = string; /** * @pattern [0-9a-fA-F]{0,64} * @example "42657272794e617679" */ -type AssetName = string; +export type AssetName = string; /** * @pattern [0-9a-fA-F]* * @example "a365636f6c6f72672330303030383065696d616765783a697066733a2f2f697066732f516d534b593167317a5375506b3536635869324b38524e766961526b44485633505a756a7474663755676b343379646e616d656a4265727279204e617679" diff --git a/webserver/shared/routes.ts b/webserver/shared/routes.ts index 0cb593fd..03360d9a 100644 --- a/webserver/shared/routes.ts +++ b/webserver/shared/routes.ts @@ -7,6 +7,7 @@ import type { CredentialAddressRequest, CredentialAddressResponse, } from "./models/CredentialAddress"; +import { DexMeanPriceRequest, DexMeanPriceResponse } from "./models/DexMeanPrice"; import { Cip25Response, PolicyIdAssetMapType } from "./models/PolicyIdAssetMap"; import type { TransactionHistoryRequest, @@ -24,6 +25,7 @@ export enum Routes { credentialAddress = "credential/address", blockLatest = "block/latest", metadataNft = "metadata/nft", + dexMeanPrice = "dex/mean-price", } export type EndpointTypes = { @@ -57,4 +59,9 @@ export type EndpointTypes = { input: CredentialAddressRequest; response: CredentialAddressResponse; }; + [Routes.dexMeanPrice]: { + name: typeof Routes.dexMeanPrice; + input: DexMeanPriceRequest; + response: DexMeanPriceResponse; + }; }; From e06587d50e793b084b67c28c163c765568b15d92 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Mon, 31 Oct 2022 12:17:45 +0100 Subject: [PATCH 06/41] Add DexSwap entity and migration --- indexer/entity/src/dex_swap.rs | 66 ++++++++++++++ indexer/entity/src/lib.rs | 1 + indexer/entity/src/prelude.rs | 4 + indexer/migration/src/lib.rs | 2 + .../m20221031_000015_create_dex_swap_table.rs | 89 +++++++++++++++++++ 5 files changed, 162 insertions(+) create mode 100644 indexer/entity/src/dex_swap.rs create mode 100644 indexer/migration/src/m20221031_000015_create_dex_swap_table.rs diff --git a/indexer/entity/src/dex_swap.rs b/indexer/entity/src/dex_swap.rs new file mode 100644 index 00000000..802a28fc --- /dev/null +++ b/indexer/entity/src/dex_swap.rs @@ -0,0 +1,66 @@ +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize, Serialize)] +#[sea_orm(table_name = "DexSwap")] +pub struct Model { + #[sea_orm(primary_key, column_type = "BigInteger")] + pub id: i64, + #[sea_orm(column_type = "BigInteger")] + pub tx_id: i64, + #[sea_orm(column_type = "BigInteger")] + pub address_id: i64, + #[sea_orm(column_type = "BigInteger", nullable)] + pub asset1_id: Option, + #[sea_orm(column_type = "BigInteger", nullable)] + pub asset2_id: Option, + #[sea_orm(column_type = "BigUnsigned")] + pub amount1: u64, + #[sea_orm(column_type = "BigUnsigned")] + pub amount2: u64, + pub direction: bool, +} + +#[derive(Copy, Clone, Debug, DeriveRelation, EnumIter)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::transaction::Entity", + from = "Column::TxId", + to = "super::transaction::Column::Id" + )] + Transaction, + #[sea_orm( + belongs_to = "super::address::Entity", + from = "Column::AddressId", + to = "super::address::Column::Id" + )] + Address, + #[sea_orm( + belongs_to = "super::native_asset::Entity", + from = "Column::Asset1Id", + to = "super::native_asset::Column::Id" + )] + Asset1, + #[sea_orm( + belongs_to = "super::native_asset::Entity", + from = "Column::Asset2Id", + to = "super::native_asset::Column::Id" + )] + Asset2, +} + +// TODO: figure out why this isn't automatically handle by the macros above +impl Related for Entity { + fn to() -> RelationDef { + Relation::Transaction.def() + } +} + +// TODO: figure out why this isn't automatically handle by the macros above +impl Related for Entity { + fn to() -> RelationDef { + Relation::Address.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/indexer/entity/src/lib.rs b/indexer/entity/src/lib.rs index 9d5aec03..8c2b3b3e 100644 --- a/indexer/entity/src/lib.rs +++ b/indexer/entity/src/lib.rs @@ -12,6 +12,7 @@ pub use sea_orm; pub mod asset_mint; pub mod cip25_entry; pub mod dex_mean_price; +pub mod dex_swap; pub mod native_asset; pub mod plutus_data; pub mod plutus_data_hash; diff --git a/indexer/entity/src/prelude.rs b/indexer/entity/src/prelude.rs index e527f913..d720c974 100644 --- a/indexer/entity/src/prelude.rs +++ b/indexer/entity/src/prelude.rs @@ -24,6 +24,10 @@ pub use super::dex_mean_price::{ Model as DexMeanPriceModel, PrimaryKey as DexMeanPricePrimaryKey, Relation as DexMeanPriceRelation, }; +pub use super::dex_swap::{ + ActiveModel as DexSwapActiveModel, Column as DexSwapColumn, Entity as DexSwap, + Model as DexSwapModel, PrimaryKey as DexSwapPrimaryKey, Relation as DexSwapRelation, +}; pub use super::native_asset::{ ActiveModel as NativeAssetActiveModel, Column as NativeAssetColumn, Entity as NativeAsset, Model as NativeAssetModel, PrimaryKey as NativeAssetPrimaryKey, diff --git a/indexer/migration/src/lib.rs b/indexer/migration/src/lib.rs index 434acee9..a58ee818 100644 --- a/indexer/migration/src/lib.rs +++ b/indexer/migration/src/lib.rs @@ -16,6 +16,7 @@ mod m20220528_000011_create_plutus_data_hash_table; mod m20220528_000012_create_plutus_data_table; mod m20220808_000013_create_transaction_reference_input_table; mod m20221020_000014_create_dex_mean_price_table; +mod m20221031_000015_create_dex_swap_table; pub struct Migrator; @@ -39,6 +40,7 @@ impl MigratorTrait for Migrator { Box::new(m20220528_000012_create_plutus_data_table::Migration), Box::new(m20220808_000013_create_transaction_reference_input_table::Migration), Box::new(m20221020_000014_create_dex_mean_price_table::Migration), + Box::new(m20221031_000015_create_dex_swap_table::Migration), ] } } diff --git a/indexer/migration/src/m20221031_000015_create_dex_swap_table.rs b/indexer/migration/src/m20221031_000015_create_dex_swap_table.rs new file mode 100644 index 00000000..353612d1 --- /dev/null +++ b/indexer/migration/src/m20221031_000015_create_dex_swap_table.rs @@ -0,0 +1,89 @@ +use sea_schema::migration::prelude::*; + +use entity::dex_swap::*; +use entity::prelude::{ + Address, AddressColumn, NativeAsset, NativeAssetColumn, Transaction, TransactionColumn, +}; + +pub struct Migration; + +impl MigrationName for Migration { + fn name(&self) -> &str { + "m20221031_000015_create_dex_swap_table" + } +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Entity) + .if_not_exists() + .col( + ColumnDef::new(Column::Id) + .big_integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(Column::TxId).big_integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fk-dex_swap-tx_id") + .from(Entity, Column::TxId) + .to(Transaction, TransactionColumn::Id) + .on_delete(ForeignKeyAction::Cascade), + ) + .col(ColumnDef::new(Column::AddressId).big_integer().not_null()) + .foreign_key( + ForeignKey::create() + .name("fk-dex_swap-address_id") + .from(Entity, Column::AddressId) + .to(Address, AddressColumn::Id) + .on_delete(ForeignKeyAction::Cascade), + ) + .col(ColumnDef::new(Column::Asset1Id).big_integer()) + .foreign_key( + ForeignKey::create() + .name("fk-dex_swap-asset1_id") + .from(Entity, Column::Asset1Id) + .to(NativeAsset, NativeAssetColumn::Id) + .on_delete(ForeignKeyAction::Cascade), + ) + .col(ColumnDef::new(Column::Asset2Id).big_integer()) + .foreign_key( + ForeignKey::create() + .name("fk-dex_swap-asset2_id") + .from(Entity, Column::Asset2Id) + .to(NativeAsset, NativeAssetColumn::Id) + .on_delete(ForeignKeyAction::Cascade), + ) + .col(ColumnDef::new(Column::Amount1).big_unsigned().not_null()) + .col(ColumnDef::new(Column::Amount2).big_unsigned().not_null()) + .col(ColumnDef::new(Column::Direction).boolean().not_null()) + .to_owned(), + ) + .await?; + + manager + .create_index( + Index::create() + .table(Entity) + .name("index-dex_swap-address-native_asset1-native_asset2-transaction") + .col(Column::AddressId) + .col(Column::Asset1Id) + .col(Column::Asset2Id) + .col(Column::TxId) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Entity).to_owned()) + .await + } +} From 67685cf68cadad3b9f128e1da87883df8936266c Mon Sep 17 00:00:00 2001 From: David Misiak Date: Tue, 1 Nov 2022 15:54:37 +0100 Subject: [PATCH 07/41] Store used inputs to outputs map --- indexer/tasks/src/dsl/execution_context.rs | 1 + indexer/tasks/src/era_common.rs | 20 ++++---- .../src/multiera/multiera_reference_inputs.rs | 4 +- .../src/multiera/multiera_used_inputs.rs | 49 ++++++++++++------- 4 files changed, 44 insertions(+), 30 deletions(-) diff --git a/indexer/tasks/src/dsl/execution_context.rs b/indexer/tasks/src/dsl/execution_context.rs index 76995e8d..4ae0a3cc 100644 --- a/indexer/tasks/src/dsl/execution_context.rs +++ b/indexer/tasks/src/dsl/execution_context.rs @@ -29,6 +29,7 @@ macro_rules! data_to_type { (multiera_metadata) => { Vec }; (multiera_outputs) => { Vec }; (multiera_used_inputs) => { Vec }; + (multiera_used_inputs_to_outputs_map) => { BTreeMap, BTreeMap> }; (multiera_assets) => { Vec }; } diff --git a/indexer/tasks/src/era_common.rs b/indexer/tasks/src/era_common.rs index 59eabc92..58fb88ab 100644 --- a/indexer/tasks/src/era_common.rs +++ b/indexer/tasks/src/era_common.rs @@ -165,22 +165,22 @@ pub async fn get_outputs_for_inputs( .collect()) } -pub fn gen_input_to_output_map<'a>( - outputs_for_inputs: &'a [(TransactionOutputModel, TransactionModel)], -) -> BTreeMap<&'a Vec, BTreeMap> { +pub fn gen_input_to_output_map( + outputs_for_inputs: &[(TransactionOutputModel, TransactionModel)], +) -> BTreeMap, BTreeMap> { let mut input_to_output_map = - BTreeMap::<&Vec, BTreeMap>::default(); + BTreeMap::, BTreeMap>::default(); for output in outputs_for_inputs { input_to_output_map - .entry(&output.1.hash) + .entry(output.1.hash.clone()) .and_modify(|output_index_map| { // note: we can insert right away instead of doing a 2nd lookup // because the pair is unique - output_index_map.insert(output.0.output_index as i64, &output.0); + output_index_map.insert(output.0.output_index as i64, output.0.clone()); }) .or_insert({ - let mut output_index_map = BTreeMap::::default(); - output_index_map.insert(output.0.output_index as i64, &output.0); + let mut output_index_map = BTreeMap::::default(); + output_index_map.insert(output.0.output_index as i64, output.0.clone()); output_index_map }); } @@ -190,7 +190,7 @@ pub fn gen_input_to_output_map<'a>( pub async fn insert_inputs( inputs: &[(Vec, i64)], - input_to_output_map: &BTreeMap<&Vec, BTreeMap>, + input_to_output_map: &BTreeMap, BTreeMap>, txn: &DatabaseTransaction, ) -> Result, DbErr> { // avoid querying the DB if there were no inputs @@ -208,7 +208,7 @@ pub async fn insert_inputs( Some(outputs) => outputs, None => panic!("Failed to find transaction {}", &hex::encode(input.hash())), }; - let output = tx_outputs[&(input.index() as i64)]; + let output = &tx_outputs[&(input.index() as i64)]; TransactionInputActiveModel { utxo_id: Set(output.id), address_id: Set(output.address_id), diff --git a/indexer/tasks/src/multiera/multiera_reference_inputs.rs b/indexer/tasks/src/multiera/multiera_reference_inputs.rs index 7fe95b07..ac576393 100644 --- a/indexer/tasks/src/multiera/multiera_reference_inputs.rs +++ b/indexer/tasks/src/multiera/multiera_reference_inputs.rs @@ -106,7 +106,7 @@ async fn handle_input( pub async fn insert_reference_inputs( inputs: &[(Vec, i64)], - input_to_output_map: &BTreeMap<&Vec, BTreeMap>, + input_to_output_map: &BTreeMap, BTreeMap>, txn: &DatabaseTransaction, ) -> Result, DbErr> { // avoid querying the DB if there were no inputs @@ -120,7 +120,7 @@ pub async fn insert_reference_inputs( .iter() .flat_map(|pair| pair.0.iter().enumerate().zip(std::iter::repeat(pair.1))) .map(|((idx, input), tx_id)| { - let output = input_to_output_map[&input.hash().to_vec()][&(input.index() as i64)]; + let output = &input_to_output_map[&input.hash().to_vec()][&(input.index() as i64)]; TransactionReferenceInputActiveModel { utxo_id: Set(output.id), address_id: Set(output.address_id), diff --git a/indexer/tasks/src/multiera/multiera_used_inputs.rs b/indexer/tasks/src/multiera/multiera_used_inputs.rs index da186364..99d56b41 100644 --- a/indexer/tasks/src/multiera/multiera_used_inputs.rs +++ b/indexer/tasks/src/multiera/multiera_used_inputs.rs @@ -24,7 +24,7 @@ carp_task! { era multiera; dependencies [MultieraOutputTask]; read [multiera_txs]; - write [vkey_relation_map, multiera_used_inputs]; + write [vkey_relation_map, multiera_used_inputs, multiera_used_inputs_to_outputs_map]; should_add_task |block, _properties| { // txs always have at least one input (even if tx fails) !block.1.is_empty() @@ -37,7 +37,8 @@ carp_task! { task.config.readonly ); merge_result |previous_data, result| { - *previous_data.multiera_used_inputs = result; + *previous_data.multiera_used_inputs = result.0; + *previous_data.multiera_used_inputs_to_outputs_map = result.1; }; } @@ -52,7 +53,13 @@ async fn handle_input( multiera_txs: &[TransactionModel], vkey_relation_map: &mut RelationMap, readonly: bool, -) -> Result, DbErr> { +) -> Result< + ( + Vec, + BTreeMap, BTreeMap>, + ), + DbErr, +> { let mut queued_inputs = QueuedInputs::default(); let txs = block.1.txs(); @@ -73,7 +80,7 @@ async fn handle_input( } match queued_inputs.is_empty() { - true => Ok(vec![]), + true => Ok((vec![], BTreeMap::default())), false => { let outputs_for_inputs = crate::era_common::get_outputs_for_inputs(&queued_inputs, db_tx).await?; @@ -93,21 +100,27 @@ async fn handle_input( TxCredentialRelationValue::InputStake, ); if readonly { - Ok(input_from_pointer( - db_tx, - queued_inputs - .iter() - .flat_map(|pair| pair.0.iter().enumerate().zip(std::iter::repeat(pair.1))) - .map(|((idx, _), tx_id)| (tx_id, idx)) - .collect::>() - .as_slice(), - ) - .await?) + Ok(( + input_from_pointer( + db_tx, + queued_inputs + .iter() + .flat_map(|pair| { + pair.0.iter().enumerate().zip(std::iter::repeat(pair.1)) + }) + .map(|((idx, _), tx_id)| (tx_id, idx)) + .collect::>() + .as_slice(), + ) + .await?, + input_to_output_map, + )) } else { - Ok( + Ok(( crate::era_common::insert_inputs(&queued_inputs, &input_to_output_map, db_tx) .await?, - ) + input_to_output_map, + )) } } } @@ -117,7 +130,7 @@ pub fn add_input_relations( vkey_relation_map: &mut RelationMap, inputs: &[(Vec, i64)], outputs: &[&TransactionOutputModel], - input_to_output_map: &BTreeMap<&Vec, BTreeMap>, + input_to_output_map: &BTreeMap, BTreeMap>, input_relation: TxCredentialRelationValue, input_stake_relation: TxCredentialRelationValue, ) { @@ -126,7 +139,7 @@ pub fn add_input_relations( for input in input_tx_pair.0.iter() { match input_to_output_map.get(&input.hash().to_vec()) { Some(entry_for_tx) => { - let output_id = entry_for_tx[&(input.index() as i64)]; + let output_id = &entry_for_tx[&(input.index() as i64)]; output_to_input_tx.insert(output_id.id, input_tx_pair.1); } None => { From 0148593c4d0000849d65923e8bd934a0af2c2353 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Thu, 3 Nov 2022 20:36:38 +0100 Subject: [PATCH 08/41] Store era in output query result --- Cargo.lock | 1 + indexer/tasks/Cargo.toml | 1 + indexer/tasks/src/dsl/execution_context.rs | 3 +- indexer/tasks/src/era_common.rs | 91 +++++++++++++------ .../src/multiera/multiera_reference_inputs.rs | 10 +- .../src/multiera/multiera_unused_input.rs | 2 +- .../src/multiera/multiera_used_inputs.rs | 12 +-- .../multiera_wingriders_v1_mean_price.rs | 8 +- 8 files changed, 80 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 637c87a4..5e4b28e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2857,6 +2857,7 @@ dependencies = [ "nameof", "pallas 0.14.0-alpha.4", "paste", + "sea-orm", "serde", "shred", "tokio", diff --git a/indexer/tasks/Cargo.toml b/indexer/tasks/Cargo.toml index 7e6b16d7..5049d028 100644 --- a/indexer/tasks/Cargo.toml +++ b/indexer/tasks/Cargo.toml @@ -25,6 +25,7 @@ urlencoding = "2.1.0" serde = { version = "1.0", features = ["derive"] } tracing-subscriber = "0.3.9" tracing = "0.1.31" +sea-orm = { git = "https://github.com/dcSpark/sea-orm", branch = "insert-many-returning", features = [ "macros" ], default-features = false } [features] default = ["build_rust_task"] diff --git a/indexer/tasks/src/dsl/execution_context.rs b/indexer/tasks/src/dsl/execution_context.rs index 4ae0a3cc..f04dac08 100644 --- a/indexer/tasks/src/dsl/execution_context.rs +++ b/indexer/tasks/src/dsl/execution_context.rs @@ -1,3 +1,4 @@ +pub use crate::era_common::OutputWithTxData; pub use entity::{ prelude::*, sea_orm::{prelude::*, DatabaseTransaction}, @@ -29,7 +30,7 @@ macro_rules! data_to_type { (multiera_metadata) => { Vec }; (multiera_outputs) => { Vec }; (multiera_used_inputs) => { Vec }; - (multiera_used_inputs_to_outputs_map) => { BTreeMap, BTreeMap> }; + (multiera_used_inputs_to_outputs_map) => { BTreeMap, BTreeMap> }; (multiera_assets) => { Vec }; } diff --git a/indexer/tasks/src/era_common.rs b/indexer/tasks/src/era_common.rs index 58fb88ab..a736c24f 100644 --- a/indexer/tasks/src/era_common.rs +++ b/indexer/tasks/src/era_common.rs @@ -3,7 +3,8 @@ use std::collections::BTreeSet; use entity::{ prelude::*, sea_orm::{ - entity::*, prelude::*, ColumnTrait, Condition, DatabaseTransaction, QueryOrder, Set, + entity::*, prelude::*, ColumnTrait, Condition, DatabaseTransaction, FromQueryResult, + QueryOrder, QuerySelect, Set, }, }; use std::collections::BTreeMap; @@ -113,10 +114,17 @@ pub async fn insert_addresses( Ok(result_map) } +#[derive(Clone)] +pub struct OutputWithTxData { + pub model: TransactionOutputModel, + pub tx_hash: Vec, + pub era: i32, +} + pub async fn get_outputs_for_inputs( inputs: &[(Vec, i64)], txn: &DatabaseTransaction, -) -> Result, DbErr> { +) -> Result, DbErr> { // avoid querying the DB if there were no inputs let has_input = inputs.iter().any(|input| !input.0.is_empty()); if !has_input { @@ -137,16 +145,36 @@ pub async fn get_outputs_for_inputs( ); } - let mut tx_outputs = TransactionOutput::find() - .inner_join(Transaction) + #[derive(FromQueryResult)] + pub struct QueryOutputResult { + id: i64, + payload: Vec, + address_id: i64, + tx_id: i64, + output_index: i32, + tx_hash: Vec, + era: i32, + } + + let tx_outputs = TransactionOutput::find() + .select_only() + .column(TransactionOutputColumn::Id) + .column(TransactionOutputColumn::Payload) + .column(TransactionOutputColumn::AddressId) + .column(TransactionOutputColumn::TxId) + .column(TransactionOutputColumn::OutputIndex) + .column_as(TransactionColumn::Hash, "tx_hash") + .column(BlockColumn::Era) + .join( + entity::sea_orm::JoinType::InnerJoin, + TransactionOutputRelation::Transaction.def(), + ) + .join( + entity::sea_orm::JoinType::InnerJoin, + TransactionRelation::Block.def(), + ) .filter(output_conditions) - .select_with(Transaction) - // TODO: we only actually need these columns, but sea-orm returns the full join - // .column(TransactionOutputColumn::Id) - // .column(TransactionOutputColumn::OutputIndex) - // .column(TransactionOutputColumn::Payload) - // .column(TransactionColumn::Hash) - // .column(TransactionColumn::Id) + .into_model::() // note: we can use "all" because all utxos are unique so we know: // 1) there won't be duplicates in the result set // 2) the # results == # of outputs in the filter @@ -154,33 +182,36 @@ pub async fn get_outputs_for_inputs( .await?; Ok(tx_outputs - .drain(..) - // is a one-to-one mapping so it's safe to flatten this - .map(|(output, txs)| { - if txs.len() > 1 { - panic!(); - } - (output, txs[0].clone()) + .iter() + .map(|output| OutputWithTxData { + model: TransactionOutputModel { + id: output.id, + payload: output.payload.clone(), + address_id: output.address_id, + tx_id: output.tx_id.clone(), + output_index: output.output_index, + }, + tx_hash: output.tx_hash.clone(), + era: output.era, }) - .collect()) + .collect::>()) } pub fn gen_input_to_output_map( - outputs_for_inputs: &[(TransactionOutputModel, TransactionModel)], -) -> BTreeMap, BTreeMap> { - let mut input_to_output_map = - BTreeMap::, BTreeMap>::default(); + outputs_for_inputs: &[OutputWithTxData], +) -> BTreeMap, BTreeMap> { + let mut input_to_output_map = BTreeMap::, BTreeMap>::default(); for output in outputs_for_inputs { input_to_output_map - .entry(output.1.hash.clone()) + .entry(output.tx_hash.clone()) .and_modify(|output_index_map| { // note: we can insert right away instead of doing a 2nd lookup // because the pair is unique - output_index_map.insert(output.0.output_index as i64, output.0.clone()); + output_index_map.insert(output.model.output_index as i64, output.clone()); }) .or_insert({ - let mut output_index_map = BTreeMap::::default(); - output_index_map.insert(output.0.output_index as i64, output.0.clone()); + let mut output_index_map = BTreeMap::::default(); + output_index_map.insert(output.model.output_index as i64, output.clone()); output_index_map }); } @@ -190,7 +221,7 @@ pub fn gen_input_to_output_map( pub async fn insert_inputs( inputs: &[(Vec, i64)], - input_to_output_map: &BTreeMap, BTreeMap>, + input_to_output_map: &BTreeMap, BTreeMap>, txn: &DatabaseTransaction, ) -> Result, DbErr> { // avoid querying the DB if there were no inputs @@ -210,8 +241,8 @@ pub async fn insert_inputs( }; let output = &tx_outputs[&(input.index() as i64)]; TransactionInputActiveModel { - utxo_id: Set(output.id), - address_id: Set(output.address_id), + utxo_id: Set(output.model.id), + address_id: Set(output.model.address_id), tx_id: Set(tx_id), input_index: Set(idx as i32), ..Default::default() diff --git a/indexer/tasks/src/multiera/multiera_reference_inputs.rs b/indexer/tasks/src/multiera/multiera_reference_inputs.rs index ac576393..664bdffd 100644 --- a/indexer/tasks/src/multiera/multiera_reference_inputs.rs +++ b/indexer/tasks/src/multiera/multiera_reference_inputs.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; -use crate::config::ReadonlyConfig::ReadonlyConfig; use crate::types::TxCredentialRelationValue; +use crate::{config::ReadonlyConfig::ReadonlyConfig, era_common::OutputWithTxData}; use cardano_multiplatform_lib::{ address::{BaseAddress, EnterpriseAddress, PointerAddress, RewardAddress}, byron::ByronAddress, @@ -79,7 +79,7 @@ async fn handle_input( &queued_inputs, outputs_for_inputs .iter() - .map(|(output, _)| output) + .map(|output| &output.model) .collect::>() .as_slice(), &input_to_output_map, @@ -106,7 +106,7 @@ async fn handle_input( pub async fn insert_reference_inputs( inputs: &[(Vec, i64)], - input_to_output_map: &BTreeMap, BTreeMap>, + input_to_output_map: &BTreeMap, BTreeMap>, txn: &DatabaseTransaction, ) -> Result, DbErr> { // avoid querying the DB if there were no inputs @@ -122,8 +122,8 @@ pub async fn insert_reference_inputs( .map(|((idx, input), tx_id)| { let output = &input_to_output_map[&input.hash().to_vec()][&(input.index() as i64)]; TransactionReferenceInputActiveModel { - utxo_id: Set(output.id), - address_id: Set(output.address_id), + utxo_id: Set(output.model.id), + address_id: Set(output.model.address_id), tx_id: Set(tx_id), input_index: Set(idx as i32), ..Default::default() diff --git a/indexer/tasks/src/multiera/multiera_unused_input.rs b/indexer/tasks/src/multiera/multiera_unused_input.rs index e9ba744c..e1cfbb1e 100644 --- a/indexer/tasks/src/multiera/multiera_unused_input.rs +++ b/indexer/tasks/src/multiera/multiera_unused_input.rs @@ -72,7 +72,7 @@ async fn handle_unused_input( &queued_unused_inputs, outputs_for_inputs .iter() - .map(|(output, _)| output) + .map(|output| &output.model) .collect::>() .as_slice(), &input_to_output_map, diff --git a/indexer/tasks/src/multiera/multiera_used_inputs.rs b/indexer/tasks/src/multiera/multiera_used_inputs.rs index 99d56b41..a9aacce6 100644 --- a/indexer/tasks/src/multiera/multiera_used_inputs.rs +++ b/indexer/tasks/src/multiera/multiera_used_inputs.rs @@ -1,8 +1,8 @@ use std::collections::BTreeMap; -use crate::config::ReadonlyConfig::ReadonlyConfig; use crate::era_common::input_from_pointer; use crate::types::TxCredentialRelationValue; +use crate::{config::ReadonlyConfig::ReadonlyConfig, era_common::OutputWithTxData}; use cardano_multiplatform_lib::{ address::{BaseAddress, EnterpriseAddress, PointerAddress, RewardAddress}, byron::ByronAddress, @@ -56,7 +56,7 @@ async fn handle_input( ) -> Result< ( Vec, - BTreeMap, BTreeMap>, + BTreeMap, BTreeMap>, ), DbErr, > { @@ -92,7 +92,7 @@ async fn handle_input( &queued_inputs, outputs_for_inputs .iter() - .map(|(output, _)| output) + .map(|output| &output.model) .collect::>() .as_slice(), &input_to_output_map, @@ -130,7 +130,7 @@ pub fn add_input_relations( vkey_relation_map: &mut RelationMap, inputs: &[(Vec, i64)], outputs: &[&TransactionOutputModel], - input_to_output_map: &BTreeMap, BTreeMap>, + input_to_output_map: &BTreeMap, BTreeMap>, input_relation: TxCredentialRelationValue, input_stake_relation: TxCredentialRelationValue, ) { @@ -139,8 +139,8 @@ pub fn add_input_relations( for input in input_tx_pair.0.iter() { match input_to_output_map.get(&input.hash().to_vec()) { Some(entry_for_tx) => { - let output_id = &entry_for_tx[&(input.index() as i64)]; - output_to_input_tx.insert(output_id.id, input_tx_pair.1); + let output = &entry_for_tx[&(input.index() as i64)]; + output_to_input_tx.insert(output.model.id, input_tx_pair.1); } None => { panic!("tx: {} index:{}", input.hash(), input.index()); diff --git a/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs index 1750d3b9..a2afbbf3 100644 --- a/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs +++ b/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs @@ -3,6 +3,7 @@ use std::collections::BTreeSet; use super::utils::common::{ get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, }; +use super::utils::constants::{WR_V1_POOL_FIXED_ADA, WR_V1_POOL_SCRIPT_HASH}; use super::{multiera_address::MultieraAddressTask, utils::common::asset_from_pair}; use crate::dsl::task_macro::*; use crate::{config::EmptyConfig::EmptyConfig, types::AssetPair}; @@ -12,9 +13,6 @@ use pallas::ledger::{ traverse::{MultiEraBlock, MultiEraTx}, }; -const POOL_SCRIPT_HASH: &str = "e6c90a5923713af5786963dee0fdffd830ca7e0c86a041d9e5833e91"; -const POOL_FIXED_ADA: u64 = 3_000_000; // every pool UTXO holds this amount of ADA - carp_task! { name MultieraWingRidersV1MeanPriceTask; configuration EmptyConfig; @@ -111,7 +109,7 @@ fn queue_mean_price(queued_prices: &mut Vec, tx: &MultiEraTx, t for output in tx .outputs() .iter() - .find(|o| get_sheley_payment_hash(o.address()).as_deref() == Some(POOL_SCRIPT_HASH)) + .find(|o| get_sheley_payment_hash(o.address()).as_deref() == Some(WR_V1_POOL_SCRIPT_HASH)) { // Remark: The datum that corresponds to the pool output's datum hash should be present // in tx.plutus_data() @@ -140,7 +138,7 @@ fn queue_mean_price(queued_prices: &mut Vec, tx: &MultiEraTx, t let get_fixed_ada = |pair: &AssetPair| -> u64 { if pair.is_none() { - POOL_FIXED_ADA + WR_V1_POOL_FIXED_ADA } else { 0 } From b377868e694a7df00941840dcf77bd32ea885144 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Tue, 8 Nov 2022 15:06:24 +0100 Subject: [PATCH 09/41] Extract common dex utils --- .../multiera_wingriders_v1_mean_price.rs | 30 +++++++------------ indexer/tasks/src/multiera/utils/dex.rs | 22 ++++++++++++++ indexer/tasks/src/multiera/utils/mod.rs | 1 + 3 files changed, 34 insertions(+), 19 deletions(-) create mode 100644 indexer/tasks/src/multiera/utils/dex.rs diff --git a/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs index a2afbbf3..7c1db686 100644 --- a/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs +++ b/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs @@ -3,7 +3,9 @@ use std::collections::BTreeSet; use super::utils::common::{ get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, }; -use super::utils::constants::{WR_V1_POOL_FIXED_ADA, WR_V1_POOL_SCRIPT_HASH}; +use super::utils::dex::{ + build_asset, reduce_ada_amount, WR_V1_POOL_FIXED_ADA, WR_V1_POOL_SCRIPT_HASH, +}; use super::{multiera_address::MultieraAddressTask, utils::common::asset_from_pair}; use crate::dsl::task_macro::*; use crate::{config::EmptyConfig::EmptyConfig, types::AssetPair}; @@ -126,25 +128,15 @@ fn queue_mean_price(queued_prices: &mut Vec, tx: &MultiEraTx, t .to_string(); hex::decode(item).unwrap() }; - let get_asset = |policy_id: Vec, asset_name: Vec| { - if policy_id.is_empty() && asset_name.is_empty() { - None - } else { - Some((policy_id, asset_name)) - } - }; - let asset1 = get_asset(get_asset_item(0, 0), get_asset_item(0, 1)); - let asset2 = get_asset(get_asset_item(1, 0), get_asset_item(1, 1)); + let asset1 = build_asset(get_asset_item(0, 0), get_asset_item(0, 1)); + let asset2 = build_asset(get_asset_item(1, 0), get_asset_item(1, 1)); - let get_fixed_ada = |pair: &AssetPair| -> u64 { - if pair.is_none() { - WR_V1_POOL_FIXED_ADA - } else { - 0 - } - }; - let amount1 = get_asset_amount(output, &asset1) - treasury1 - get_fixed_ada(&asset1); - let amount2 = get_asset_amount(output, &asset2) - treasury2 - get_fixed_ada(&asset2); + let amount1 = get_asset_amount(output, &asset1) + - treasury1 + - reduce_ada_amount(&asset1, WR_V1_POOL_FIXED_ADA); + let amount2 = get_asset_amount(output, &asset2) + - treasury2 + - reduce_ada_amount(&asset2, WR_V1_POOL_FIXED_ADA); queued_prices.push(QueuedMeanPrice { tx_id, diff --git a/indexer/tasks/src/multiera/utils/dex.rs b/indexer/tasks/src/multiera/utils/dex.rs new file mode 100644 index 00000000..4a6bcf7d --- /dev/null +++ b/indexer/tasks/src/multiera/utils/dex.rs @@ -0,0 +1,22 @@ +use crate::types::AssetPair; + +pub const WR_V1_POOL_SCRIPT_HASH: &str = "e6c90a5923713af5786963dee0fdffd830ca7e0c86a041d9e5833e91"; +pub const WR_V1_POOL_FIXED_ADA: u64 = 3_000_000; // every pool UTXO holds this amount of ADA +pub const WR_V1_SWAP_IN_ADA: u64 = 4_000_000; // oil ADA + agent fee +pub const WR_V1_SWAP_OUT_ADA: u64 = 2_000_000; // oil ADA + +pub fn build_asset(policy_id: Vec, asset_name: Vec) -> AssetPair { + if policy_id.is_empty() && asset_name.is_empty() { + None + } else { + Some((policy_id, asset_name)) + } +} + +pub fn reduce_ada_amount(pair: &AssetPair, amount: u64) -> u64 { + if pair.is_none() { + amount + } else { + 0 + } +} diff --git a/indexer/tasks/src/multiera/utils/mod.rs b/indexer/tasks/src/multiera/utils/mod.rs index 76cbd331..1b3cc364 100644 --- a/indexer/tasks/src/multiera/utils/mod.rs +++ b/indexer/tasks/src/multiera/utils/mod.rs @@ -1,3 +1,4 @@ pub mod cip25_parse; pub mod common; +pub mod dex; pub mod user_asset; From 277fd4f893929c01486b7f5f930468fac079ba7f Mon Sep 17 00:00:00 2001 From: David Misiak Date: Tue, 8 Nov 2022 15:14:52 +0100 Subject: [PATCH 10/41] Add MultieraWingRidersV1SwapTask --- indexer/tasks/src/multiera/mod.rs | 1 + .../multiera/multiera_wingriders_v1_swap.rs | 237 ++++++++++++++++++ indexer/tasks/src/types.rs | 15 ++ 3 files changed, 253 insertions(+) create mode 100644 indexer/tasks/src/multiera/multiera_wingriders_v1_swap.rs diff --git a/indexer/tasks/src/multiera/mod.rs b/indexer/tasks/src/multiera/mod.rs index 61bced77..04dd907d 100644 --- a/indexer/tasks/src/multiera/mod.rs +++ b/indexer/tasks/src/multiera/mod.rs @@ -14,5 +14,6 @@ pub mod multiera_unused_input; pub mod multiera_used_inputs; pub mod multiera_used_outputs; pub mod multiera_wingriders_v1_mean_price; +pub mod multiera_wingriders_v1_swap; pub mod relation_map; pub mod utils; diff --git a/indexer/tasks/src/multiera/multiera_wingriders_v1_swap.rs b/indexer/tasks/src/multiera/multiera_wingriders_v1_swap.rs new file mode 100644 index 00000000..68e79924 --- /dev/null +++ b/indexer/tasks/src/multiera/multiera_wingriders_v1_swap.rs @@ -0,0 +1,237 @@ +use std::collections::BTreeSet; + +use super::multiera_used_inputs::MultieraUsedInputTask; +use super::utils::common::{ + asset_from_pair, get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, +}; +use super::utils::dex::{ + build_asset, reduce_ada_amount, WR_V1_POOL_FIXED_ADA, WR_V1_POOL_SCRIPT_HASH, + WR_V1_SWAP_IN_ADA, WR_V1_SWAP_OUT_ADA, +}; +use crate::dsl::task_macro::*; +use crate::era_common::get_outputs_for_inputs; +use crate::era_common::OutputWithTxData; +use crate::types::DexSwapDirection; +use crate::{config::EmptyConfig::EmptyConfig, types::AssetPair}; +use entity::sea_orm::{DatabaseTransaction, Set}; +use pallas::ledger::traverse::MultiEraOutput; +use pallas::ledger::{ + primitives::ToCanonicalJson, + traverse::{MultiEraBlock, MultiEraTx}, +}; + +carp_task! { + name MultieraWingRidersV1SwapTask; + configuration EmptyConfig; + doc "Adds WingRiders V1 swaps to the database"; + era multiera; + dependencies [MultieraUsedInputTask]; + read [multiera_txs, multiera_addresses, multiera_used_inputs_to_outputs_map]; + write []; + should_add_task |block, _properties| { + block.1.txs().iter().any(|tx| tx.outputs().len() > 0) + }; + execute |previous_data, task| handle_swap( + task.db_tx, + task.block, + &previous_data.multiera_txs, + &previous_data.multiera_addresses, + &previous_data.multiera_used_inputs_to_outputs_map, + ); + merge_result |previous_data, _result| { + }; +} + +struct QueuedSwap { + tx_id: i64, + address: Vec, // pallas::crypto::hash::Hash<32> + asset1: AssetPair, + asset2: AssetPair, + amount1: u64, + amount2: u64, + direction: DexSwapDirection, +} + +async fn handle_swap( + db_tx: &DatabaseTransaction, + block: BlockInfo<'_, MultiEraBlock<'_>>, + multiera_txs: &[TransactionModel], + multiera_addresses: &BTreeMap, AddressInBlock>, + multiera_used_inputs_to_outputs_map: &BTreeMap, BTreeMap>, +) -> Result<(), DbErr> { + // 1) Parse swaps + let mut queued_swaps = Vec::::default(); + for (tx_body, cardano_transaction) in block.1.txs().iter().zip(multiera_txs) { + if cardano_transaction.is_valid { + queue_swap( + &mut queued_swaps, + tx_body, + cardano_transaction.id, + multiera_used_inputs_to_outputs_map, + ); + } + } + + if queued_swaps.is_empty() { + return Ok(()); + } + + // 2) Remove asset duplicates to build a list of all the to query for. + // ADA is ignored, it's not in the NativeAsset DB table + let mut unique_tokens = BTreeSet::<&(Vec, Vec)>::default(); + for p in &queued_swaps { + if let Some(pair) = &p.asset1 { + unique_tokens.insert(&pair); + } + if let Some(pair) = &p.asset2 { + unique_tokens.insert(&pair); + } + } + + // 3) Query for asset ids + // TODO use the query result from mean price task? + let found_assets = asset_from_pair( + db_tx, + &unique_tokens + .iter() + .map(|(policy_id, asset_name)| (policy_id.clone(), asset_name.clone())) + .collect::>(), + ) + .await?; + let mut asset_pair_to_id_map = found_assets + .into_iter() + .map(|asset| (Some((asset.policy_id, asset.asset_name)), Some(asset.id))) + .collect::>(); + asset_pair_to_id_map.insert(None, None); // ADA + + // 4) Add mean prices to DB + DexSwap::insert_many(queued_swaps.iter().map(|price| DexSwapActiveModel { + tx_id: Set(price.tx_id), + address_id: Set(multiera_addresses[&price.address].model.id), + asset1_id: Set(asset_pair_to_id_map[&price.asset1]), + asset2_id: Set(asset_pair_to_id_map[&price.asset2]), + amount1: Set(price.amount1), + amount2: Set(price.amount2), + direction: Set(price.direction.into()), + ..Default::default() + })) + .exec(db_tx) + .await?; + + Ok(()) +} + +fn queue_swap( + queued_swaps: &mut Vec, + tx: &MultiEraTx, + tx_id: i64, + multiera_used_inputs_to_outputs_map: &BTreeMap, BTreeMap>, +) { + // Find the pool address (Note: there should be at most one pool output) + for pool_output in tx + .outputs() + .iter() + .find(|o| get_sheley_payment_hash(o.address()).as_deref() == Some(WR_V1_POOL_SCRIPT_HASH)) + { + let redeemers = tx.redeemers().unwrap(); + + // Get pool input from redemeers + let pool_input = redeemers[0].data.to_json()["fields"][0]["int"].as_i64(); + if pool_input.is_none() { + return; + } + let pool_input = pool_input.unwrap(); + + // Find main redemeer + if let Some(redeemer) = redeemers.iter().find(|&r| r.index as i64 == pool_input) { + let redeemer = redeemer.data.to_json(); + + // Extract input list from redemeer + let redeemer_map: Vec = redeemer["fields"][2]["list"] + .as_array() + .unwrap() + .iter() + .map(|r| r["int"].as_i64().unwrap() as usize) + .collect(); + // Find main transaction + let parent = redeemer["fields"][0]["int"].as_i64().unwrap() as usize; + // Restore inputs + let inputs: Vec = tx + .inputs() + .iter() + .map(|i| { + let output = &multiera_used_inputs_to_outputs_map[&i.hash().to_vec()] + [&(i.index() as i64)]; + MultiEraOutput::decode( + (output.era as u16).try_into().unwrap(), + &output.model.payload, + ) + .unwrap() + }) + .collect::>(); + // Zip outputs with redemeer index + for (output, redeemer) in tx.outputs().iter().skip(1).zip(redeemer_map) { + // pair input with output + let input = inputs[redeemer].clone(); + + // get information about swap from pool plutus data + let parent_datum = get_plutus_datum_for_output(&inputs[parent], &tx.plutus_data()) + .unwrap() + .to_json(); + let parse_asset_item = |i, j| { + let item = parent_datum["fields"][1]["fields"][0]["fields"][i]["fields"][j] + ["bytes"] + .as_str() + .unwrap() + .to_string(); + hex::decode(item).unwrap() + }; + let asset1 = build_asset(parse_asset_item(0, 0), parse_asset_item(0, 1)); + let asset2 = build_asset(parse_asset_item(1, 0), parse_asset_item(1, 1)); + + // get actual plutus datum + let input_datum = get_plutus_datum_for_output(&input, &tx.plutus_data()) + .unwrap() + .to_json(); + // identify operation: 0 = swap + let operation = input_datum["fields"][1]["constructor"].as_i64().unwrap(); + if operation != 0 { + tracing::info!("Operation is not a swap"); + continue; + } + let direction = input_datum["fields"][1]["fields"][0]["constructor"] + .as_i64() + .unwrap(); + + let amount1; + let amount2; + if direction == 0 { + amount1 = get_asset_amount(&input, &asset1) + - reduce_ada_amount(&asset1, WR_V1_SWAP_IN_ADA); + amount2 = get_asset_amount(&output, &asset2) + - reduce_ada_amount(&asset2, WR_V1_SWAP_OUT_ADA); + } else { + amount1 = get_asset_amount(&output, &asset1) + - reduce_ada_amount(&asset1, WR_V1_SWAP_OUT_ADA); + amount2 = get_asset_amount(&input, &asset2) + - reduce_ada_amount(&asset2, WR_V1_SWAP_IN_ADA); + } + queued_swaps.push(QueuedSwap { + tx_id, + address: pool_output.address().unwrap().to_vec(), + asset1, + asset2, + amount1, + amount2, + direction: if direction == 0 { + DexSwapDirection::SellAsset1 + } else { + DexSwapDirection::BuyAsset1 + }, + }) + } + } else { + tracing::info!("Redeemer not found"); + } + } +} diff --git a/indexer/tasks/src/types.rs b/indexer/tasks/src/types.rs index f2acf9ef..d25ee00d 100644 --- a/indexer/tasks/src/types.rs +++ b/indexer/tasks/src/types.rs @@ -71,3 +71,18 @@ impl From for i32 { // ADA = None, token = Some((policy_id, asset_name)) pub type AssetPair = Option<(Vec, Vec)>; + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum DexSwapDirection { + BuyAsset1, + SellAsset1, +} + +impl From for bool { + fn from(item: DexSwapDirection) -> Self { + match item { + DexSwapDirection::BuyAsset1 => false, + DexSwapDirection::SellAsset1 => true, + } + } +} From 66442371790b0223c990b32675c3c30a7ce11b9d Mon Sep 17 00:00:00 2001 From: David Misiak Date: Tue, 8 Nov 2022 15:29:31 +0100 Subject: [PATCH 11/41] Update dex_prices.toml execution plan --- indexer/execution_plans/dex_prices.toml | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/indexer/execution_plans/dex_prices.toml b/indexer/execution_plans/dex_prices.toml index 8e385e33..8e492ac4 100644 --- a/indexer/execution_plans/dex_prices.toml +++ b/indexer/execution_plans/dex_prices.toml @@ -30,32 +30,17 @@ readonly=false [MultieraTransactionTask] readonly=false -[MultieraMetadataTask] -readonly=false - [MultieraAddressTask] [MultieraOutputTask] readonly=false -[MultieraReferenceInputTask] -readonly=false - [MultieraUsedInputTask] readonly=false -[MultieraUnusedInputTask] - -[MultieraStakeCredentialTask] - -[MultieraAddressCredentialRelationTask] -readonly=false - -[MultieraTxCredentialRelationTask] - [MultieraAssetMintTask] readonly=false -[MultieraCip25EntryTask] - [MultieraWingRidersV1MeanPriceTask] + +[MultieraWingRidersV1SwapTask] From 61c4f8368ba6b80b9780daab398c8c1c9051128c Mon Sep 17 00:00:00 2001 From: David Misiak Date: Wed, 9 Nov 2022 17:05:19 +0100 Subject: [PATCH 12/41] Fix output era parsing bug --- Cargo.lock | 1 + indexer/entity/Cargo.toml | 1 + indexer/entity/src/block.rs | 30 +++++++++++++++++++ indexer/tasks/src/era_common.rs | 8 +++-- .../multiera/multiera_wingriders_v1_swap.rs | 10 ++----- 5 files changed, 41 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5e4b28e7..f33d3889 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -794,6 +794,7 @@ checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" name = "entity" version = "0.0.0" dependencies = [ + "pallas 0.14.0-alpha.4", "sea-orm", "serde", ] diff --git a/indexer/entity/Cargo.toml b/indexer/entity/Cargo.toml index 43d99c86..e48c13bd 100644 --- a/indexer/entity/Cargo.toml +++ b/indexer/entity/Cargo.toml @@ -12,3 +12,4 @@ sea-orm = { git = "https://github.com/dcSpark/sea-orm", branch = "insert-many-re "macros", ], default-features = false } serde = "1.0.136" +pallas = "0.14.0-alpha.4" diff --git a/indexer/entity/src/block.rs b/indexer/entity/src/block.rs index ebea7415..490c40cd 100644 --- a/indexer/entity/src/block.rs +++ b/indexer/entity/src/block.rs @@ -1,3 +1,4 @@ +use pallas::ledger::traverse::Era; use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; @@ -43,3 +44,32 @@ impl From for i32 { } } } + +impl TryFrom for EraValue { + type Error = (); + + fn try_from(value: i32) -> Result { + match value { + 0 => Ok(EraValue::Byron), + 1 => Ok(EraValue::Shelley), + 2 => Ok(EraValue::Allegra), + 3 => Ok(EraValue::Mary), + 4 => Ok(EraValue::Alonzo), + 5 => Ok(EraValue::Babbage), + _ => Err(()), + } + } +} + +impl From for Era { + fn from(item: EraValue) -> Self { + match item { + EraValue::Byron => Era::Byron, + EraValue::Shelley => Era::Shelley, + EraValue::Allegra => Era::Allegra, + EraValue::Mary => Era::Mary, + EraValue::Alonzo => Era::Alonzo, + EraValue::Babbage => Era::Babbage, + } + } +} diff --git a/indexer/tasks/src/era_common.rs b/indexer/tasks/src/era_common.rs index a736c24f..19c74251 100644 --- a/indexer/tasks/src/era_common.rs +++ b/indexer/tasks/src/era_common.rs @@ -1,12 +1,14 @@ use std::collections::BTreeSet; use entity::{ + block::EraValue, prelude::*, sea_orm::{ entity::*, prelude::*, ColumnTrait, Condition, DatabaseTransaction, FromQueryResult, QueryOrder, QuerySelect, Set, }, }; +use pallas::ledger::traverse::Era; use std::collections::BTreeMap; static ADDRESS_TRUNCATE: usize = 500; // 1000 in hex @@ -118,7 +120,7 @@ pub async fn insert_addresses( pub struct OutputWithTxData { pub model: TransactionOutputModel, pub tx_hash: Vec, - pub era: i32, + pub era: Era, } pub async fn get_outputs_for_inputs( @@ -192,7 +194,9 @@ pub async fn get_outputs_for_inputs( output_index: output.output_index, }, tx_hash: output.tx_hash.clone(), - era: output.era, + era: >::try_into(output.era) + .unwrap() + .into(), }) .collect::>()) } diff --git a/indexer/tasks/src/multiera/multiera_wingriders_v1_swap.rs b/indexer/tasks/src/multiera/multiera_wingriders_v1_swap.rs index 68e79924..b35a03c3 100644 --- a/indexer/tasks/src/multiera/multiera_wingriders_v1_swap.rs +++ b/indexer/tasks/src/multiera/multiera_wingriders_v1_swap.rs @@ -14,7 +14,7 @@ use crate::era_common::OutputWithTxData; use crate::types::DexSwapDirection; use crate::{config::EmptyConfig::EmptyConfig, types::AssetPair}; use entity::sea_orm::{DatabaseTransaction, Set}; -use pallas::ledger::traverse::MultiEraOutput; +use pallas::ledger::traverse::{Era, MultiEraOutput}; use pallas::ledger::{ primitives::ToCanonicalJson, traverse::{MultiEraBlock, MultiEraTx}, @@ -162,11 +162,7 @@ fn queue_swap( .map(|i| { let output = &multiera_used_inputs_to_outputs_map[&i.hash().to_vec()] [&(i.index() as i64)]; - MultiEraOutput::decode( - (output.era as u16).try_into().unwrap(), - &output.model.payload, - ) - .unwrap() + MultiEraOutput::decode(output.era, &output.model.payload).unwrap() }) .collect::>(); // Zip outputs with redemeer index @@ -196,7 +192,7 @@ fn queue_swap( // identify operation: 0 = swap let operation = input_datum["fields"][1]["constructor"].as_i64().unwrap(); if operation != 0 { - tracing::info!("Operation is not a swap"); + tracing::debug!("Operation is not a swap"); continue; } let direction = input_datum["fields"][1]["fields"][0]["constructor"] From 523e24182e40cb380d4029e8046c72505b2860ca Mon Sep 17 00:00:00 2001 From: David Misiak Date: Thu, 10 Nov 2022 15:26:33 +0100 Subject: [PATCH 13/41] Extract pool output and redeemers more carefully --- .../multiera_wingriders_v1_mean_price.rs | 73 +++++++++++-------- .../multiera/multiera_wingriders_v1_swap.rs | 11 ++- 2 files changed, 47 insertions(+), 37 deletions(-) diff --git a/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs index 7c1db686..84aeb3fb 100644 --- a/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs +++ b/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs @@ -10,9 +10,10 @@ use super::{multiera_address::MultieraAddressTask, utils::common::asset_from_pai use crate::dsl::task_macro::*; use crate::{config::EmptyConfig::EmptyConfig, types::AssetPair}; use entity::sea_orm::{DatabaseTransaction, Set}; +use pallas::ledger::primitives::alonzo; use pallas::ledger::{ primitives::ToCanonicalJson, - traverse::{MultiEraBlock, MultiEraTx}, + traverse::{MultiEraBlock, MultiEraOutput, MultiEraTx}, }; carp_task! { @@ -106,9 +107,9 @@ async fn handle_mean_price( Ok(()) } -fn queue_mean_price(queued_prices: &mut Vec, tx: &MultiEraTx, tx_id: i64) { - // Find the pool address (Note: there should be at most one pool output) - for output in tx +pub fn get_pool_output<'b>(tx: &'b MultiEraTx) -> Option<(MultiEraOutput<'b>, alonzo::PlutusData)> { + // Note: there should be at most one pool output + if let Some(output) = tx .outputs() .iter() .find(|o| get_sheley_payment_hash(o.address()).as_deref() == Some(WR_V1_POOL_SCRIPT_HASH)) @@ -116,36 +117,46 @@ fn queue_mean_price(queued_prices: &mut Vec, tx: &MultiEraTx, t // Remark: The datum that corresponds to the pool output's datum hash should be present // in tx.plutus_data() if let Some(datum) = get_plutus_datum_for_output(output, &tx.plutus_data()) { - let datum = datum.to_json(); + Some((output.clone(), datum)) + } else { + None + } + } else { + None + } +} - let treasury1 = datum["fields"][1]["fields"][2]["int"].as_u64().unwrap(); - let treasury2 = datum["fields"][1]["fields"][3]["int"].as_u64().unwrap(); +fn queue_mean_price(queued_prices: &mut Vec, tx: &MultiEraTx, tx_id: i64) { + if let Some((output, datum)) = get_pool_output(tx) { + let datum = datum.to_json(); - let get_asset_item = |i, j| { - let item = datum["fields"][1]["fields"][0]["fields"][i]["fields"][j]["bytes"] - .as_str() - .unwrap() - .to_string(); - hex::decode(item).unwrap() - }; - let asset1 = build_asset(get_asset_item(0, 0), get_asset_item(0, 1)); - let asset2 = build_asset(get_asset_item(1, 0), get_asset_item(1, 1)); + let treasury1 = datum["fields"][1]["fields"][2]["int"].as_u64().unwrap(); + let treasury2 = datum["fields"][1]["fields"][3]["int"].as_u64().unwrap(); - let amount1 = get_asset_amount(output, &asset1) - - treasury1 - - reduce_ada_amount(&asset1, WR_V1_POOL_FIXED_ADA); - let amount2 = get_asset_amount(output, &asset2) - - treasury2 - - reduce_ada_amount(&asset2, WR_V1_POOL_FIXED_ADA); + let get_asset_item = |i, j| { + let item = datum["fields"][1]["fields"][0]["fields"][i]["fields"][j]["bytes"] + .as_str() + .unwrap() + .to_string(); + hex::decode(item).unwrap() + }; + let asset1 = build_asset(get_asset_item(0, 0), get_asset_item(0, 1)); + let asset2 = build_asset(get_asset_item(1, 0), get_asset_item(1, 1)); - queued_prices.push(QueuedMeanPrice { - tx_id, - address: output.address().unwrap().to_vec(), - asset1, - asset2, - amount1, - amount2, - }); - } + let amount1 = get_asset_amount(&output, &asset1) + - treasury1 + - reduce_ada_amount(&asset1, WR_V1_POOL_FIXED_ADA); + let amount2 = get_asset_amount(&output, &asset2) + - treasury2 + - reduce_ada_amount(&asset2, WR_V1_POOL_FIXED_ADA); + + queued_prices.push(QueuedMeanPrice { + tx_id, + address: output.address().unwrap().to_vec(), + asset1, + asset2, + amount1, + amount2, + }); } } diff --git a/indexer/tasks/src/multiera/multiera_wingriders_v1_swap.rs b/indexer/tasks/src/multiera/multiera_wingriders_v1_swap.rs index b35a03c3..c2435fa6 100644 --- a/indexer/tasks/src/multiera/multiera_wingriders_v1_swap.rs +++ b/indexer/tasks/src/multiera/multiera_wingriders_v1_swap.rs @@ -1,6 +1,7 @@ use std::collections::BTreeSet; use super::multiera_used_inputs::MultieraUsedInputTask; +use super::multiera_wingriders_v1_mean_price::get_pool_output; use super::utils::common::{ asset_from_pair, get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, }; @@ -127,12 +128,10 @@ fn queue_swap( tx_id: i64, multiera_used_inputs_to_outputs_map: &BTreeMap, BTreeMap>, ) { - // Find the pool address (Note: there should be at most one pool output) - for pool_output in tx - .outputs() - .iter() - .find(|o| get_sheley_payment_hash(o.address()).as_deref() == Some(WR_V1_POOL_SCRIPT_HASH)) - { + if let Some((pool_output, _)) = get_pool_output(tx) { + if tx.redeemers().is_none() || tx.redeemers().unwrap().len() == 0 { + return; + } let redeemers = tx.redeemers().unwrap(); // Get pool input from redemeers From 798b24e822a638dc39e93a88f0e713e50fc469f0 Mon Sep 17 00:00:00 2001 From: Michal Vido Date: Tue, 8 Nov 2022 14:44:47 +0100 Subject: [PATCH 14/41] Merged MinSwap and SundaeSwap meanprice tasks --- indexer/execution_plans/dex_prices.toml | 4 + indexer/tasks/src/multiera/mod.rs | 2 + .../multiera_minswap_v1_mean_price.rs | 151 ++++++++++++++++++ .../multiera_sundaeswap_v1_mean_price.rs | 150 +++++++++++++++++ 4 files changed, 307 insertions(+) create mode 100644 indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs create mode 100644 indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs diff --git a/indexer/execution_plans/dex_prices.toml b/indexer/execution_plans/dex_prices.toml index 8e492ac4..fda7e985 100644 --- a/indexer/execution_plans/dex_prices.toml +++ b/indexer/execution_plans/dex_prices.toml @@ -43,4 +43,8 @@ readonly=false [MultieraWingRidersV1MeanPriceTask] +[MultieraMinSwapV1MeanPriceTask] + +[MultieraSundaeSwapV1MeanPriceTask] + [MultieraWingRidersV1SwapTask] diff --git a/indexer/tasks/src/multiera/mod.rs b/indexer/tasks/src/multiera/mod.rs index 04dd907d..f4184e27 100644 --- a/indexer/tasks/src/multiera/mod.rs +++ b/indexer/tasks/src/multiera/mod.rs @@ -6,8 +6,10 @@ pub mod multiera_cip25entry; pub mod multiera_datum; pub mod multiera_executor; pub mod multiera_metadata; +pub mod multiera_minswap_v1_mean_price; pub mod multiera_reference_inputs; pub mod multiera_stake_credentials; +pub mod multiera_sundaeswap_v1_mean_price; pub mod multiera_tx_credential_relations; pub mod multiera_txs; pub mod multiera_unused_input; diff --git a/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs new file mode 100644 index 00000000..a6a1439f --- /dev/null +++ b/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs @@ -0,0 +1,151 @@ +use std::collections::BTreeSet; + +use super::utils::common::{ + get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, +}; +use super::{multiera_address::MultieraAddressTask, utils::common::asset_from_pair}; +use crate::dsl::task_macro::*; +use crate::{config::EmptyConfig::EmptyConfig, types::AssetPair}; +use entity::sea_orm::{DatabaseTransaction, Set}; +use pallas::ledger::{ + primitives::ToCanonicalJson, + traverse::{MultiEraBlock, MultiEraTx}, +}; + +const POOL_SCRIPT_HASH: &str = "e1317b152faac13426e6a83e06ff88a4d62cce3c1634ab0a5ec13309"; +const POOL_SCRIPT_HASH2: &str = "57c8e718c201fba10a9da1748d675b54281d3b1b983c5d1687fc7317"; + +carp_task! { + name MultieraMinSwapV1MeanPriceTask; + configuration EmptyConfig; + doc "Adds Minswap V1 mean price updates to the database"; + era multiera; + dependencies [MultieraAddressTask]; + read [multiera_txs, multiera_addresses]; + write []; + should_add_task |block, _properties| { + block.1.txs().iter().any(|tx| tx.outputs().len() > 0) + }; + execute |previous_data, task| handle_mean_price( + task.db_tx, + task.block, + &previous_data.multiera_txs, + &previous_data.multiera_addresses, + ); + merge_result |previous_data, _result| { + }; +} + +struct QueuedMeanPrice { + tx_id: i64, + address: Vec, // pallas::crypto::hash::Hash<32> + asset1: AssetPair, + asset2: AssetPair, + amount1: u64, + amount2: u64, +} + +async fn handle_mean_price( + db_tx: &DatabaseTransaction, + block: BlockInfo<'_, MultiEraBlock<'_>>, + multiera_txs: &[TransactionModel], + multiera_addresses: &BTreeMap, AddressInBlock>, +) -> Result<(), DbErr> { + // 1) Parse mean prices + let mut queued_prices = Vec::::default(); + for (tx_body, cardano_transaction) in block.1.txs().iter().zip(multiera_txs) { + if cardano_transaction.is_valid { + queue_mean_price(&mut queued_prices, tx_body, cardano_transaction.id); + } + } + + if queued_prices.is_empty() { + return Ok(()); + } + + // 2) Remove asset duplicates to build a list of all the to query for. + // ADA is ignored, it's not in the NativeAsset DB table + let mut unique_tokens = BTreeSet::<&(Vec, Vec)>::default(); + for p in &queued_prices { + if let Some(pair) = &p.asset1 { + unique_tokens.insert(&pair); + } + if let Some(pair) = &p.asset2 { + unique_tokens.insert(&pair); + } + } + + // 3) Query for asset ids + let found_assets = asset_from_pair( + db_tx, + &unique_tokens + .iter() + .map(|(policy_id, asset_name)| (policy_id.clone(), asset_name.clone())) + .collect::>(), + ) + .await?; + let mut asset_pair_to_id_map = found_assets + .into_iter() + .map(|asset| (Some((asset.policy_id, asset.asset_name)), Some(asset.id))) + .collect::>(); + asset_pair_to_id_map.insert(None, None); // ADA + + // 4) Add mean prices to DB + DexMeanPrice::insert_many(queued_prices.iter().map(|price| DexMeanPriceActiveModel { + tx_id: Set(price.tx_id), + address_id: Set(multiera_addresses[&price.address].model.id), + asset1_id: Set(asset_pair_to_id_map[&price.asset1]), + asset2_id: Set(asset_pair_to_id_map[&price.asset2]), + amount1: Set(price.amount1), + amount2: Set(price.amount2), + ..Default::default() + })) + .exec(db_tx) + .await?; + + Ok(()) +} + +fn queue_mean_price(queued_prices: &mut Vec, tx: &MultiEraTx, tx_id: i64) { + // Find the pool address (Note: there should be at most one pool output) + for output in tx.outputs().iter().find(|o| { + get_sheley_payment_hash(o.address()).as_deref() == Some(POOL_SCRIPT_HASH) + || get_sheley_payment_hash(o.address()).as_deref() == Some(POOL_SCRIPT_HASH2) + }) { + // Remark: The datum that corresponds to the pool output's datum hash should be present + // in tx.plutus_data() + if let Some(datum) = get_plutus_datum_for_output(output, &tx.plutus_data()) { + let datum = datum.to_json(); + + let get_asset_item = |i, j| { + let item = datum["fields"][i]["fields"][j]["bytes"] + .as_str() + .unwrap() + .to_string(); + hex::decode(item).unwrap() + }; + let get_asset = |policy_id: Vec, asset_name: Vec| { + if policy_id.is_empty() && asset_name.is_empty() { + None + } else { + Some((policy_id, asset_name)) + } + }; + // extract plutus + let asset1 = get_asset(get_asset_item(0, 0), get_asset_item(0, 1)); + let asset2 = get_asset(get_asset_item(1, 0), get_asset_item(1, 1)); + + let amount1 = get_asset_amount(output, &asset1); + let amount2 = get_asset_amount(output, &asset2); + + queued_prices.push(QueuedMeanPrice { + tx_id, + address: output.address().unwrap().to_vec(), + asset1, + asset2, + amount1, + amount2, + }); + } + } +} diff --git a/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs new file mode 100644 index 00000000..17712d04 --- /dev/null +++ b/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs @@ -0,0 +1,150 @@ +use std::collections::BTreeSet; + +use super::utils::common::{ + get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, +}; +use super::{multiera_address::MultieraAddressTask, utils::common::asset_from_pair}; +use crate::dsl::task_macro::*; +use crate::{config::EmptyConfig::EmptyConfig, types::AssetPair}; +use entity::sea_orm::{DatabaseTransaction, Set}; +use pallas::ledger::{ + primitives::ToCanonicalJson, + traverse::{MultiEraBlock, MultiEraTx}, +}; + +const POOL_SCRIPT_HASH: &str = "4020e7fc2de75a0729c3cc3af715b34d98381e0cdbcfa99c950bc3ac"; + +carp_task! { + name MultieraSundaeSwapV1MeanPriceTask; + configuration EmptyConfig; + doc "Adds SundaeSwap V1 mean price updates to the database"; + era multiera; + dependencies [MultieraAddressTask]; + read [multiera_txs, multiera_addresses]; + write []; + should_add_task |block, _properties| { + block.1.txs().iter().any(|tx| tx.outputs().len() > 0) + }; + execute |previous_data, task| handle_mean_price( + task.db_tx, + task.block, + &previous_data.multiera_txs, + &previous_data.multiera_addresses, + ); + merge_result |previous_data, _result| { + }; +} + +struct QueuedMeanPrice { + tx_id: i64, + address: Vec, // pallas::crypto::hash::Hash<32> + asset1: AssetPair, + asset2: AssetPair, + amount1: u64, + amount2: u64, +} + +async fn handle_mean_price( + db_tx: &DatabaseTransaction, + block: BlockInfo<'_, MultiEraBlock<'_>>, + multiera_txs: &[TransactionModel], + multiera_addresses: &BTreeMap, AddressInBlock>, +) -> Result<(), DbErr> { + // 1) Parse mean prices + let mut queued_prices = Vec::::default(); + for (tx_body, cardano_transaction) in block.1.txs().iter().zip(multiera_txs) { + if cardano_transaction.is_valid { + queue_mean_price(&mut queued_prices, tx_body, cardano_transaction.id); + } + } + + if queued_prices.is_empty() { + return Ok(()); + } + + // 2) Remove asset duplicates to build a list of all the to query for. + // ADA is ignored, it's not in the NativeAsset DB table + let mut unique_tokens = BTreeSet::<&(Vec, Vec)>::default(); + for p in &queued_prices { + if let Some(pair) = &p.asset1 { + unique_tokens.insert(&pair); + } + if let Some(pair) = &p.asset2 { + unique_tokens.insert(&pair); + } + } + + // 3) Query for asset ids + let found_assets = asset_from_pair( + db_tx, + &unique_tokens + .iter() + .map(|(policy_id, asset_name)| (policy_id.clone(), asset_name.clone())) + .collect::>(), + ) + .await?; + let mut asset_pair_to_id_map = found_assets + .into_iter() + .map(|asset| (Some((asset.policy_id, asset.asset_name)), Some(asset.id))) + .collect::>(); + asset_pair_to_id_map.insert(None, None); // ADA + + // 4) Add mean prices to DB + DexMeanPrice::insert_many(queued_prices.iter().map(|price| DexMeanPriceActiveModel { + tx_id: Set(price.tx_id), + address_id: Set(multiera_addresses[&price.address].model.id), + asset1_id: Set(asset_pair_to_id_map[&price.asset1]), + asset2_id: Set(asset_pair_to_id_map[&price.asset2]), + amount1: Set(price.amount1), + amount2: Set(price.amount2), + ..Default::default() + })) + .exec(db_tx) + .await?; + + Ok(()) +} + +fn queue_mean_price(queued_prices: &mut Vec, tx: &MultiEraTx, tx_id: i64) { + // Find the pool address (Note: there should be at most one pool output) + for output in tx + .outputs() + .iter() + .find(|o| get_sheley_payment_hash(o.address()).as_deref() == Some(POOL_SCRIPT_HASH)) + { + // Remark: The datum that corresponds to the pool output's datum hash should be present + // in tx.plutus_data() + if let Some(datum) = get_plutus_datum_for_output(output, &tx.plutus_data()) { + let datum = datum.to_json(); + + let get_asset_item = |i, j| { + let item = datum["fields"][0]["fields"][i]["fields"][j]["bytes"] + .as_str() + .unwrap() + .to_string(); + hex::decode(item).unwrap() + }; + let get_asset = |policy_id: Vec, asset_name: Vec| { + if policy_id.is_empty() && asset_name.is_empty() { + None + } else { + Some((policy_id, asset_name)) + } + }; + let asset1 = get_asset(get_asset_item(0, 0), get_asset_item(0, 1)); + let asset2 = get_asset(get_asset_item(1, 0), get_asset_item(1, 1)); + + let amount1 = get_asset_amount(output, &asset1); + let amount2 = get_asset_amount(output, &asset2); + + queued_prices.push(QueuedMeanPrice { + tx_id, + address: output.address().unwrap().to_vec(), + asset1, + asset2, + amount1, + amount2, + }); + } + } +} From 2ecb40531c889b9520c079f91f51de0b151d96dc Mon Sep 17 00:00:00 2001 From: Michal Vido Date: Tue, 8 Nov 2022 15:02:34 +0100 Subject: [PATCH 15/41] extracting QueuedMeanPrice struct --- .../src/multiera/multiera_minswap_v1_mean_price.rs | 11 +---------- .../src/multiera/multiera_sundaeswap_v1_mean_price.rs | 11 +---------- .../src/multiera/multiera_wingriders_v1_mean_price.rs | 11 +---------- indexer/tasks/src/multiera/utils/common.rs | 9 +++++++++ 4 files changed, 12 insertions(+), 30 deletions(-) diff --git a/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs index a6a1439f..ed1e048e 100644 --- a/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs +++ b/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs @@ -1,7 +1,7 @@ use std::collections::BTreeSet; use super::utils::common::{ - get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, + get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, QueuedMeanPrice, }; use super::{multiera_address::MultieraAddressTask, utils::common::asset_from_pair}; use crate::dsl::task_macro::*; @@ -36,15 +36,6 @@ carp_task! { }; } -struct QueuedMeanPrice { - tx_id: i64, - address: Vec, // pallas::crypto::hash::Hash<32> - asset1: AssetPair, - asset2: AssetPair, - amount1: u64, - amount2: u64, -} - async fn handle_mean_price( db_tx: &DatabaseTransaction, block: BlockInfo<'_, MultiEraBlock<'_>>, diff --git a/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs index 17712d04..4127b5b7 100644 --- a/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs +++ b/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs @@ -1,7 +1,7 @@ use std::collections::BTreeSet; use super::utils::common::{ - get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, + get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, QueuedMeanPrice, }; use super::{multiera_address::MultieraAddressTask, utils::common::asset_from_pair}; use crate::dsl::task_macro::*; @@ -35,15 +35,6 @@ carp_task! { }; } -struct QueuedMeanPrice { - tx_id: i64, - address: Vec, // pallas::crypto::hash::Hash<32> - asset1: AssetPair, - asset2: AssetPair, - amount1: u64, - amount2: u64, -} - async fn handle_mean_price( db_tx: &DatabaseTransaction, block: BlockInfo<'_, MultiEraBlock<'_>>, diff --git a/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs index 84aeb3fb..43f2b3f2 100644 --- a/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs +++ b/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs @@ -1,7 +1,7 @@ use std::collections::BTreeSet; use super::utils::common::{ - get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, + get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, QueuedMeanPrice, }; use super::utils::dex::{ build_asset, reduce_ada_amount, WR_V1_POOL_FIXED_ADA, WR_V1_POOL_SCRIPT_HASH, @@ -37,15 +37,6 @@ carp_task! { }; } -struct QueuedMeanPrice { - tx_id: i64, - address: Vec, // pallas::crypto::hash::Hash<32> - asset1: AssetPair, - asset2: AssetPair, - amount1: u64, - amount2: u64, -} - async fn handle_mean_price( db_tx: &DatabaseTransaction, block: BlockInfo<'_, MultiEraBlock<'_>>, diff --git a/indexer/tasks/src/multiera/utils/common.rs b/indexer/tasks/src/multiera/utils/common.rs index 393bb08d..5a3b1265 100644 --- a/indexer/tasks/src/multiera/utils/common.rs +++ b/indexer/tasks/src/multiera/utils/common.rs @@ -74,3 +74,12 @@ pub async fn asset_from_pair( .await?; Ok(assets) } + +pub struct QueuedMeanPrice { + pub tx_id: i64, + pub address: Vec, // pallas::crypto::hash::Hash<32> + pub asset1: AssetPair, + pub asset2: AssetPair, + pub amount1: u64, + pub amount2: u64, +} From bf9db5b196daf439a4c41352984e0c80051aecbe Mon Sep 17 00:00:00 2001 From: Michal Vido Date: Wed, 9 Nov 2022 18:23:41 +0100 Subject: [PATCH 16/41] implementing Dex trait --- .../multiera_minswap_v1_mean_price.rs | 144 ++++++------------ .../multiera_sundaeswap_v1_mean_price.rs | 142 +++++------------ .../multiera_wingriders_v1_mean_price.rs | 93 +++++------ indexer/tasks/src/multiera/utils/common.rs | 9 -- indexer/tasks/src/multiera/utils/dex.rs | 136 ++++++++++++++++- 5 files changed, 255 insertions(+), 269 deletions(-) diff --git a/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs index ed1e048e..402a913d 100644 --- a/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs +++ b/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs @@ -1,7 +1,7 @@ use std::collections::BTreeSet; - +use super::utils::dex::{handle_mean_price, QueuedMeanPrice, Dex, MinSwapV1, PoolType}; use super::utils::common::{ - get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, QueuedMeanPrice, + get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, }; use super::{multiera_address::MultieraAddressTask, utils::common::asset_from_pair}; use crate::dsl::task_macro::*; @@ -31,112 +31,54 @@ carp_task! { task.block, &previous_data.multiera_txs, &previous_data.multiera_addresses, + PoolType::MinSwapV1, ); merge_result |previous_data, _result| { }; } -async fn handle_mean_price( - db_tx: &DatabaseTransaction, - block: BlockInfo<'_, MultiEraBlock<'_>>, - multiera_txs: &[TransactionModel], - multiera_addresses: &BTreeMap, AddressInBlock>, -) -> Result<(), DbErr> { - // 1) Parse mean prices - let mut queued_prices = Vec::::default(); - for (tx_body, cardano_transaction) in block.1.txs().iter().zip(multiera_txs) { - if cardano_transaction.is_valid { - queue_mean_price(&mut queued_prices, tx_body, cardano_transaction.id); - } - } - - if queued_prices.is_empty() { - return Ok(()); - } +impl Dex for MinSwapV1 { + fn queue_mean_price(&self, queued_prices: &mut Vec, tx: &MultiEraTx, tx_id: i64) { + // Find the pool address (Note: there should be at most one pool output) + for output in tx.outputs().iter().find(|o| { + get_sheley_payment_hash(o.address()).as_deref() == Some(POOL_SCRIPT_HASH) + || get_sheley_payment_hash(o.address()).as_deref() == Some(POOL_SCRIPT_HASH2) + }) { + // Remark: The datum that corresponds to the pool output's datum hash should be present + // in tx.plutus_data() + if let Some(datum) = get_plutus_datum_for_output(output, &tx.plutus_data()) { + let datum = datum.to_json(); - // 2) Remove asset duplicates to build a list of all the to query for. - // ADA is ignored, it's not in the NativeAsset DB table - let mut unique_tokens = BTreeSet::<&(Vec, Vec)>::default(); - for p in &queued_prices { - if let Some(pair) = &p.asset1 { - unique_tokens.insert(&pair); - } - if let Some(pair) = &p.asset2 { - unique_tokens.insert(&pair); - } - } + let get_asset_item = |i, j| { + let item = datum["fields"][i]["fields"][j]["bytes"] + .as_str() + .unwrap() + .to_string(); + hex::decode(item).unwrap() + }; + let get_asset = |policy_id: Vec, asset_name: Vec| { + if policy_id.is_empty() && asset_name.is_empty() { + None + } else { + Some((policy_id, asset_name)) + } + }; + // extract plutus + let asset1 = get_asset(get_asset_item(0, 0), get_asset_item(0, 1)); + let asset2 = get_asset(get_asset_item(1, 0), get_asset_item(1, 1)); - // 3) Query for asset ids - let found_assets = asset_from_pair( - db_tx, - &unique_tokens - .iter() - .map(|(policy_id, asset_name)| (policy_id.clone(), asset_name.clone())) - .collect::>(), - ) - .await?; - let mut asset_pair_to_id_map = found_assets - .into_iter() - .map(|asset| (Some((asset.policy_id, asset.asset_name)), Some(asset.id))) - .collect::>(); - asset_pair_to_id_map.insert(None, None); // ADA + let amount1 = get_asset_amount(output, &asset1); + let amount2 = get_asset_amount(output, &asset2); - // 4) Add mean prices to DB - DexMeanPrice::insert_many(queued_prices.iter().map(|price| DexMeanPriceActiveModel { - tx_id: Set(price.tx_id), - address_id: Set(multiera_addresses[&price.address].model.id), - asset1_id: Set(asset_pair_to_id_map[&price.asset1]), - asset2_id: Set(asset_pair_to_id_map[&price.asset2]), - amount1: Set(price.amount1), - amount2: Set(price.amount2), - ..Default::default() - })) - .exec(db_tx) - .await?; - - Ok(()) -} - -fn queue_mean_price(queued_prices: &mut Vec, tx: &MultiEraTx, tx_id: i64) { - // Find the pool address (Note: there should be at most one pool output) - for output in tx.outputs().iter().find(|o| { - get_sheley_payment_hash(o.address()).as_deref() == Some(POOL_SCRIPT_HASH) - || get_sheley_payment_hash(o.address()).as_deref() == Some(POOL_SCRIPT_HASH2) - }) { - // Remark: The datum that corresponds to the pool output's datum hash should be present - // in tx.plutus_data() - if let Some(datum) = get_plutus_datum_for_output(output, &tx.plutus_data()) { - let datum = datum.to_json(); - - let get_asset_item = |i, j| { - let item = datum["fields"][i]["fields"][j]["bytes"] - .as_str() - .unwrap() - .to_string(); - hex::decode(item).unwrap() - }; - let get_asset = |policy_id: Vec, asset_name: Vec| { - if policy_id.is_empty() && asset_name.is_empty() { - None - } else { - Some((policy_id, asset_name)) - } - }; - // extract plutus - let asset1 = get_asset(get_asset_item(0, 0), get_asset_item(0, 1)); - let asset2 = get_asset(get_asset_item(1, 0), get_asset_item(1, 1)); - - let amount1 = get_asset_amount(output, &asset1); - let amount2 = get_asset_amount(output, &asset2); - - queued_prices.push(QueuedMeanPrice { - tx_id, - address: output.address().unwrap().to_vec(), - asset1, - asset2, - amount1, - amount2, - }); + queued_prices.push(QueuedMeanPrice { + tx_id, + address: output.address().unwrap().to_vec(), + asset1, + asset2, + amount1, + amount2, + }); + } } } -} +} \ No newline at end of file diff --git a/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs index 4127b5b7..bf72ba79 100644 --- a/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs +++ b/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs @@ -1,7 +1,7 @@ use std::collections::BTreeSet; - +use super::utils::dex::{handle_mean_price, QueuedMeanPrice, Dex, SundaeSwapV1, PoolType}; use super::utils::common::{ - get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, QueuedMeanPrice, + get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, }; use super::{multiera_address::MultieraAddressTask, utils::common::asset_from_pair}; use crate::dsl::task_macro::*; @@ -30,112 +30,54 @@ carp_task! { task.block, &previous_data.multiera_txs, &previous_data.multiera_addresses, + PoolType::SundaeSwapV1, ); merge_result |previous_data, _result| { }; } -async fn handle_mean_price( - db_tx: &DatabaseTransaction, - block: BlockInfo<'_, MultiEraBlock<'_>>, - multiera_txs: &[TransactionModel], - multiera_addresses: &BTreeMap, AddressInBlock>, -) -> Result<(), DbErr> { - // 1) Parse mean prices - let mut queued_prices = Vec::::default(); - for (tx_body, cardano_transaction) in block.1.txs().iter().zip(multiera_txs) { - if cardano_transaction.is_valid { - queue_mean_price(&mut queued_prices, tx_body, cardano_transaction.id); - } - } - - if queued_prices.is_empty() { - return Ok(()); - } - - // 2) Remove asset duplicates to build a list of all the to query for. - // ADA is ignored, it's not in the NativeAsset DB table - let mut unique_tokens = BTreeSet::<&(Vec, Vec)>::default(); - for p in &queued_prices { - if let Some(pair) = &p.asset1 { - unique_tokens.insert(&pair); - } - if let Some(pair) = &p.asset2 { - unique_tokens.insert(&pair); - } - } - - // 3) Query for asset ids - let found_assets = asset_from_pair( - db_tx, - &unique_tokens +impl Dex for SundaeSwapV1 { + fn queue_mean_price(&self, queued_prices: &mut Vec, tx: &MultiEraTx, tx_id: i64) { + // Find the pool address (Note: there should be at most one pool output) + for output in tx + .outputs() .iter() - .map(|(policy_id, asset_name)| (policy_id.clone(), asset_name.clone())) - .collect::>(), - ) - .await?; - let mut asset_pair_to_id_map = found_assets - .into_iter() - .map(|asset| (Some((asset.policy_id, asset.asset_name)), Some(asset.id))) - .collect::>(); - asset_pair_to_id_map.insert(None, None); // ADA + .find(|o| get_sheley_payment_hash(o.address()).as_deref() == Some(POOL_SCRIPT_HASH)) + { + // Remark: The datum that corresponds to the pool output's datum hash should be present + // in tx.plutus_data() + if let Some(datum) = get_plutus_datum_for_output(output, &tx.plutus_data()) { + let datum = datum.to_json(); - // 4) Add mean prices to DB - DexMeanPrice::insert_many(queued_prices.iter().map(|price| DexMeanPriceActiveModel { - tx_id: Set(price.tx_id), - address_id: Set(multiera_addresses[&price.address].model.id), - asset1_id: Set(asset_pair_to_id_map[&price.asset1]), - asset2_id: Set(asset_pair_to_id_map[&price.asset2]), - amount1: Set(price.amount1), - amount2: Set(price.amount2), - ..Default::default() - })) - .exec(db_tx) - .await?; - - Ok(()) -} + let get_asset_item = |i, j| { + let item = datum["fields"][0]["fields"][i]["fields"][j]["bytes"] + .as_str() + .unwrap() + .to_string(); + hex::decode(item).unwrap() + }; + let get_asset = |policy_id: Vec, asset_name: Vec| { + if policy_id.is_empty() && asset_name.is_empty() { + None + } else { + Some((policy_id, asset_name)) + } + }; + let asset1 = get_asset(get_asset_item(0, 0), get_asset_item(0, 1)); + let asset2 = get_asset(get_asset_item(1, 0), get_asset_item(1, 1)); -fn queue_mean_price(queued_prices: &mut Vec, tx: &MultiEraTx, tx_id: i64) { - // Find the pool address (Note: there should be at most one pool output) - for output in tx - .outputs() - .iter() - .find(|o| get_sheley_payment_hash(o.address()).as_deref() == Some(POOL_SCRIPT_HASH)) - { - // Remark: The datum that corresponds to the pool output's datum hash should be present - // in tx.plutus_data() - if let Some(datum) = get_plutus_datum_for_output(output, &tx.plutus_data()) { - let datum = datum.to_json(); + let amount1 = get_asset_amount(output, &asset1); + let amount2 = get_asset_amount(output, &asset2); - let get_asset_item = |i, j| { - let item = datum["fields"][0]["fields"][i]["fields"][j]["bytes"] - .as_str() - .unwrap() - .to_string(); - hex::decode(item).unwrap() - }; - let get_asset = |policy_id: Vec, asset_name: Vec| { - if policy_id.is_empty() && asset_name.is_empty() { - None - } else { - Some((policy_id, asset_name)) - } - }; - let asset1 = get_asset(get_asset_item(0, 0), get_asset_item(0, 1)); - let asset2 = get_asset(get_asset_item(1, 0), get_asset_item(1, 1)); - - let amount1 = get_asset_amount(output, &asset1); - let amount2 = get_asset_amount(output, &asset2); - - queued_prices.push(QueuedMeanPrice { - tx_id, - address: output.address().unwrap().to_vec(), - asset1, - asset2, - amount1, - amount2, - }); + queued_prices.push(QueuedMeanPrice { + tx_id, + address: output.address().unwrap().to_vec(), + asset1, + asset2, + amount1, + amount2, + }); + } } } -} +} \ No newline at end of file diff --git a/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs index 43f2b3f2..1754a20e 100644 --- a/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs +++ b/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs @@ -1,7 +1,7 @@ use std::collections::BTreeSet; - +use super::utils::dex::{handle_mean_price, QueuedMeanPrice, Dex, WingRidersV1, PoolType}; use super::utils::common::{ - get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, QueuedMeanPrice, + get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, }; use super::utils::dex::{ build_asset, reduce_ada_amount, WR_V1_POOL_FIXED_ADA, WR_V1_POOL_SCRIPT_HASH, @@ -32,70 +32,47 @@ carp_task! { task.block, &previous_data.multiera_txs, &previous_data.multiera_addresses, + PoolType::WingRidersV1, ); merge_result |previous_data, _result| { }; } -async fn handle_mean_price( - db_tx: &DatabaseTransaction, - block: BlockInfo<'_, MultiEraBlock<'_>>, - multiera_txs: &[TransactionModel], - multiera_addresses: &BTreeMap, AddressInBlock>, -) -> Result<(), DbErr> { - // 1) Parse mean prices - let mut queued_prices = Vec::::default(); - for (tx_body, cardano_transaction) in block.1.txs().iter().zip(multiera_txs) { - if cardano_transaction.is_valid { - queue_mean_price(&mut queued_prices, tx_body, cardano_transaction.id); - } - } +impl Dex for WingRidersV1 { + fn queue_mean_price(&self, queued_prices: &mut Vec, tx: &MultiEraTx, tx_id: i64) { + // Find the pool address (Note: there should be at most one pool output) + for output in tx + .outputs() + .iter() + .find(|o| get_sheley_payment_hash(o.address()).as_deref() == Some(WR_V1_POOL_SCRIPT_HASH)) + { + // Remark: The datum that corresponds to the pool output's datum hash should be present + // in tx.plutus_data() + if let Some(datum) = get_plutus_datum_for_output(output, &tx.plutus_data()) { + let datum = datum.to_json(); - if queued_prices.is_empty() { - return Ok(()); - } + let treasury1 = datum["fields"][1]["fields"][2]["int"].as_u64().unwrap(); + let treasury2 = datum["fields"][1]["fields"][3]["int"].as_u64().unwrap(); - // 2) Remove asset duplicates to build a list of all the to query for. - // ADA is ignored, it's not in the NativeAsset DB table - let mut unique_tokens = BTreeSet::<&(Vec, Vec)>::default(); - for p in &queued_prices { - if let Some(pair) = &p.asset1 { - unique_tokens.insert(&pair); - } - if let Some(pair) = &p.asset2 { - unique_tokens.insert(&pair); + let get_asset_item = |i, j| { + let item = datum["fields"][1]["fields"][0]["fields"][i]["fields"][j]["bytes"] + .as_str() + .unwrap() + .to_string(); + hex::decode(item).unwrap() + }; + let get_asset = |policy_id: Vec, asset_name: Vec| { + if policy_id.is_empty() && asset_name.is_empty() { + None + } else { + Some((policy_id, asset_name)) + } + }; + let asset1 = get_asset(get_asset_item(0, 0), get_asset_item(0, 1)); + let asset2 = get_asset(get_asset_item(1, 0), get_asset_item(1, 1)); + } } } - - // 3) Query for asset ids - let found_assets = asset_from_pair( - db_tx, - &unique_tokens - .iter() - .map(|(policy_id, asset_name)| (policy_id.clone(), asset_name.clone())) - .collect::>(), - ) - .await?; - let mut asset_pair_to_id_map = found_assets - .into_iter() - .map(|asset| (Some((asset.policy_id, asset.asset_name)), Some(asset.id))) - .collect::>(); - asset_pair_to_id_map.insert(None, None); // ADA - - // 4) Add mean prices to DB - DexMeanPrice::insert_many(queued_prices.iter().map(|price| DexMeanPriceActiveModel { - tx_id: Set(price.tx_id), - address_id: Set(multiera_addresses[&price.address].model.id), - asset1_id: Set(asset_pair_to_id_map[&price.asset1]), - asset2_id: Set(asset_pair_to_id_map[&price.asset2]), - amount1: Set(price.amount1), - amount2: Set(price.amount2), - ..Default::default() - })) - .exec(db_tx) - .await?; - - Ok(()) } pub fn get_pool_output<'b>(tx: &'b MultiEraTx) -> Option<(MultiEraOutput<'b>, alonzo::PlutusData)> { @@ -150,4 +127,4 @@ fn queue_mean_price(queued_prices: &mut Vec, tx: &MultiEraTx, t amount2, }); } -} +} \ No newline at end of file diff --git a/indexer/tasks/src/multiera/utils/common.rs b/indexer/tasks/src/multiera/utils/common.rs index 5a3b1265..393bb08d 100644 --- a/indexer/tasks/src/multiera/utils/common.rs +++ b/indexer/tasks/src/multiera/utils/common.rs @@ -74,12 +74,3 @@ pub async fn asset_from_pair( .await?; Ok(assets) } - -pub struct QueuedMeanPrice { - pub tx_id: i64, - pub address: Vec, // pallas::crypto::hash::Hash<32> - pub asset1: AssetPair, - pub asset2: AssetPair, - pub amount1: u64, - pub amount2: u64, -} diff --git a/indexer/tasks/src/multiera/utils/dex.rs b/indexer/tasks/src/multiera/utils/dex.rs index 4a6bcf7d..3c24ce2b 100644 --- a/indexer/tasks/src/multiera/utils/dex.rs +++ b/indexer/tasks/src/multiera/utils/dex.rs @@ -1,10 +1,144 @@ -use crate::types::AssetPair; +use std::collections::{BTreeMap, BTreeSet}; +use entity::sea_orm::{DatabaseTransaction, Set}; +use pallas::ledger::traverse::{MultiEraBlock, MultiEraTx}; +use crate::dsl::task_macro::*; + +use crate::{types::AssetPair, dsl::database_task::BlockInfo}; + +use super::common::asset_from_pair; pub const WR_V1_POOL_SCRIPT_HASH: &str = "e6c90a5923713af5786963dee0fdffd830ca7e0c86a041d9e5833e91"; pub const WR_V1_POOL_FIXED_ADA: u64 = 3_000_000; // every pool UTXO holds this amount of ADA pub const WR_V1_SWAP_IN_ADA: u64 = 4_000_000; // oil ADA + agent fee pub const WR_V1_SWAP_OUT_ADA: u64 = 2_000_000; // oil ADA + +pub struct QueuedMeanPrice { + pub tx_id: i64, + pub address: Vec, // pallas::crypto::hash::Hash<32> + pub asset1: AssetPair, + pub asset2: AssetPair, + pub amount1: u64, + pub amount2: u64, +} + +pub trait Dex { + fn queue_mean_price( + &self, + queued_prices: &mut Vec, + tx: &MultiEraTx, + tx_id: i64, + ); +} + +#[derive(Debug, PartialEq, Eq)] +pub struct WingRidersV1; +#[derive(Debug, PartialEq, Eq)] +pub struct MinSwapV1; +#[derive(Debug, PartialEq, Eq)] +pub struct SundaeSwapV1; +#[derive(Debug, PartialEq, Eq)] +pub struct Empty; + + +impl Dex for Empty { + fn queue_mean_price( + &self, + _queued_prices: &mut Vec, + _tx: &MultiEraTx, + _tx_id: i64, + ) { + unimplemented!(); + } +} + +#[derive(Debug, PartialEq, Eq)] +pub enum PoolType { + WingRidersV1, + SundaeSwapV1, + MinSwapV1, + MinSwapV2, +} + +struct PoolConfig { + pub pool_type: PoolType, +} + +impl PoolConfig { + fn as_trait(&self) -> &dyn Dex { + match &self.pool_type { + PoolType::WingRidersV1 => &WingRidersV1 {}, + PoolType::MinSwapV1 => &MinSwapV1 {}, + PoolType::SundaeSwapV1 => &SundaeSwapV1 {}, + _ => &Empty {}, + } + } +} + +pub async fn handle_mean_price( + db_tx: &DatabaseTransaction, + block: BlockInfo<'_, MultiEraBlock<'_>>, + multiera_txs: &[TransactionModel], + multiera_addresses: &BTreeMap, AddressInBlock>, + pool_type: PoolType, +) -> Result<(), DbErr> { + // 1) Parse mean prices + let pool = PoolConfig {pool_type}; + let mean_value_trait = pool.as_trait(); + let mut queued_prices = Vec::::default(); + for (tx_body, cardano_transaction) in block.1.txs().iter().zip(multiera_txs) { + if cardano_transaction.is_valid { + mean_value_trait.queue_mean_price(&mut queued_prices, tx_body, cardano_transaction.id); + } + } + + if queued_prices.is_empty() { + return Ok(()); + } + + // 2) Remove asset duplicates to build a list of all the to query for. + // ADA is ignored, it's not in the NativeAsset DB table + let mut unique_tokens = BTreeSet::<&(Vec, Vec)>::default(); + for p in &queued_prices { + if let Some(pair) = &p.asset1 { + unique_tokens.insert(&pair); + } + if let Some(pair) = &p.asset2 { + unique_tokens.insert(&pair); + } + } + + // 3) Query for asset ids + let found_assets = asset_from_pair( + db_tx, + &unique_tokens + .iter() + .map(|(policy_id, asset_name)| (policy_id.clone(), asset_name.clone())) + .collect::>(), + ) + .await?; + let mut asset_pair_to_id_map = found_assets + .into_iter() + .map(|asset| (Some((asset.policy_id, asset.asset_name)), Some(asset.id))) + .collect::>(); + asset_pair_to_id_map.insert(None, None); // ADA + + // 4) Add mean prices to DB + DexMeanPrice::insert_many(queued_prices.iter().map(|price| DexMeanPriceActiveModel { + tx_id: Set(price.tx_id), + address_id: Set(multiera_addresses[&price.address].model.id), + asset1_id: Set(asset_pair_to_id_map[&price.asset1]), + asset2_id: Set(asset_pair_to_id_map[&price.asset2]), + amount1: Set(price.amount1), + amount2: Set(price.amount2), + ..Default::default() + })) + .exec(db_tx) + .await?; + + Ok(()) +} + pub fn build_asset(policy_id: Vec, asset_name: Vec) -> AssetPair { if policy_id.is_empty() && asset_name.is_empty() { None From 5dd7f7dd83a9247ce9c2caf0441a351cd4d5ffc2 Mon Sep 17 00:00:00 2001 From: Michal Vido Date: Wed, 9 Nov 2022 18:46:03 +0100 Subject: [PATCH 17/41] resolving future conflicts with CARP-39 --- .../multiera_minswap_v1_mean_price.rs | 28 ++++++------- .../multiera_sundaeswap_v1_mean_price.rs | 27 ++++++------ .../multiera_wingriders_v1_mean_price.rs | 42 ++++++++++++++----- indexer/tasks/src/multiera/utils/dex.rs | 25 ++++++----- 4 files changed, 72 insertions(+), 50 deletions(-) diff --git a/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs index 402a913d..c5df9dd3 100644 --- a/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs +++ b/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs @@ -1,8 +1,9 @@ -use std::collections::BTreeSet; -use super::utils::dex::{handle_mean_price, QueuedMeanPrice, Dex, MinSwapV1, PoolType}; use super::utils::common::{ get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, }; +use super::utils::dex::{ + build_asset, handle_mean_price, Dex, MinSwapV1, PoolType, QueuedMeanPrice, +}; use super::{multiera_address::MultieraAddressTask, utils::common::asset_from_pair}; use crate::dsl::task_macro::*; use crate::{config::EmptyConfig::EmptyConfig, types::AssetPair}; @@ -11,6 +12,7 @@ use pallas::ledger::{ primitives::ToCanonicalJson, traverse::{MultiEraBlock, MultiEraTx}, }; +use std::collections::BTreeSet; const POOL_SCRIPT_HASH: &str = "e1317b152faac13426e6a83e06ff88a4d62cce3c1634ab0a5ec13309"; const POOL_SCRIPT_HASH2: &str = "57c8e718c201fba10a9da1748d675b54281d3b1b983c5d1687fc7317"; @@ -38,7 +40,12 @@ carp_task! { } impl Dex for MinSwapV1 { - fn queue_mean_price(&self, queued_prices: &mut Vec, tx: &MultiEraTx, tx_id: i64) { + fn queue_mean_price( + &self, + queued_prices: &mut Vec, + tx: &MultiEraTx, + tx_id: i64, + ) { // Find the pool address (Note: there should be at most one pool output) for output in tx.outputs().iter().find(|o| { get_sheley_payment_hash(o.address()).as_deref() == Some(POOL_SCRIPT_HASH) @@ -56,16 +63,9 @@ impl Dex for MinSwapV1 { .to_string(); hex::decode(item).unwrap() }; - let get_asset = |policy_id: Vec, asset_name: Vec| { - if policy_id.is_empty() && asset_name.is_empty() { - None - } else { - Some((policy_id, asset_name)) - } - }; - // extract plutus - let asset1 = get_asset(get_asset_item(0, 0), get_asset_item(0, 1)); - let asset2 = get_asset(get_asset_item(1, 0), get_asset_item(1, 1)); + + let asset1 = build_asset(get_asset_item(0, 0), get_asset_item(0, 1)); + let asset2 = build_asset(get_asset_item(1, 0), get_asset_item(1, 1)); let amount1 = get_asset_amount(output, &asset1); let amount2 = get_asset_amount(output, &asset2); @@ -81,4 +81,4 @@ impl Dex for MinSwapV1 { } } } -} \ No newline at end of file +} diff --git a/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs index bf72ba79..49f348b2 100644 --- a/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs +++ b/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs @@ -1,8 +1,9 @@ -use std::collections::BTreeSet; -use super::utils::dex::{handle_mean_price, QueuedMeanPrice, Dex, SundaeSwapV1, PoolType}; use super::utils::common::{ get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, }; +use super::utils::dex::{ + build_asset, handle_mean_price, Dex, PoolType, QueuedMeanPrice, SundaeSwapV1, +}; use super::{multiera_address::MultieraAddressTask, utils::common::asset_from_pair}; use crate::dsl::task_macro::*; use crate::{config::EmptyConfig::EmptyConfig, types::AssetPair}; @@ -11,6 +12,7 @@ use pallas::ledger::{ primitives::ToCanonicalJson, traverse::{MultiEraBlock, MultiEraTx}, }; +use std::collections::BTreeSet; const POOL_SCRIPT_HASH: &str = "4020e7fc2de75a0729c3cc3af715b34d98381e0cdbcfa99c950bc3ac"; @@ -37,7 +39,12 @@ carp_task! { } impl Dex for SundaeSwapV1 { - fn queue_mean_price(&self, queued_prices: &mut Vec, tx: &MultiEraTx, tx_id: i64) { + fn queue_mean_price( + &self, + queued_prices: &mut Vec, + tx: &MultiEraTx, + tx_id: i64, + ) { // Find the pool address (Note: there should be at most one pool output) for output in tx .outputs() @@ -56,15 +63,9 @@ impl Dex for SundaeSwapV1 { .to_string(); hex::decode(item).unwrap() }; - let get_asset = |policy_id: Vec, asset_name: Vec| { - if policy_id.is_empty() && asset_name.is_empty() { - None - } else { - Some((policy_id, asset_name)) - } - }; - let asset1 = get_asset(get_asset_item(0, 0), get_asset_item(0, 1)); - let asset2 = get_asset(get_asset_item(1, 0), get_asset_item(1, 1)); + + let asset1 = build_asset(get_asset_item(0, 0), get_asset_item(0, 1)); + let asset2 = build_asset(get_asset_item(1, 0), get_asset_item(1, 1)); let amount1 = get_asset_amount(output, &asset1); let amount2 = get_asset_amount(output, &asset2); @@ -80,4 +81,4 @@ impl Dex for SundaeSwapV1 { } } } -} \ No newline at end of file +} diff --git a/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs index 1754a20e..baef14a5 100644 --- a/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs +++ b/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs @@ -1,10 +1,10 @@ -use std::collections::BTreeSet; -use super::utils::dex::{handle_mean_price, QueuedMeanPrice, Dex, WingRidersV1, PoolType}; use super::utils::common::{ get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, }; use super::utils::dex::{ - build_asset, reduce_ada_amount, WR_V1_POOL_FIXED_ADA, WR_V1_POOL_SCRIPT_HASH, + WR_V1_POOL_FIXED_ADA, WR_V1_POOL_SCRIPT_HASH, + build_asset, handle_mean_price, reduce_ada_amount, Dex, PoolType, QueuedMeanPrice, + WingRidersV1, }; use super::{multiera_address::MultieraAddressTask, utils::common::asset_from_pair}; use crate::dsl::task_macro::*; @@ -15,6 +15,7 @@ use pallas::ledger::{ primitives::ToCanonicalJson, traverse::{MultiEraBlock, MultiEraOutput, MultiEraTx}, }; +use std::collections::BTreeSet; carp_task! { name MultieraWingRidersV1MeanPriceTask; @@ -39,7 +40,12 @@ carp_task! { } impl Dex for WingRidersV1 { - fn queue_mean_price(&self, queued_prices: &mut Vec, tx: &MultiEraTx, tx_id: i64) { + fn queue_mean_price( + &self, + queued_prices: &mut Vec, + tx: &MultiEraTx, + tx_id: i64, + ) { // Find the pool address (Note: there should be at most one pool output) for output in tx .outputs() @@ -61,15 +67,31 @@ impl Dex for WingRidersV1 { .to_string(); hex::decode(item).unwrap() }; - let get_asset = |policy_id: Vec, asset_name: Vec| { - if policy_id.is_empty() && asset_name.is_empty() { - None + let asset1 = build_asset(get_asset_item(0, 0), get_asset_item(0, 1)); + let asset2 = build_asset(get_asset_item(1, 0), get_asset_item(1, 1)); + + let get_fixed_ada = |pair: &AssetPair| -> u64 { + if pair.is_none() { + WR_V1_POOL_FIXED_ADA } else { - Some((policy_id, asset_name)) + 0 } }; - let asset1 = get_asset(get_asset_item(0, 0), get_asset_item(0, 1)); - let asset2 = get_asset(get_asset_item(1, 0), get_asset_item(1, 1)); + let amount1 = get_asset_amount(output, &asset1) + - treasury1 + - reduce_ada_amount(&asset1, WR_V1_POOL_FIXED_ADA); + let amount2 = get_asset_amount(output, &asset2) + - treasury2 + - reduce_ada_amount(&asset2, WR_V1_POOL_FIXED_ADA); + + queued_prices.push(QueuedMeanPrice { + tx_id, + address: output.address().unwrap().to_vec(), + asset1, + asset2, + amount1, + amount2, + }); } } } diff --git a/indexer/tasks/src/multiera/utils/dex.rs b/indexer/tasks/src/multiera/utils/dex.rs index 3c24ce2b..06587826 100644 --- a/indexer/tasks/src/multiera/utils/dex.rs +++ b/indexer/tasks/src/multiera/utils/dex.rs @@ -1,18 +1,18 @@ -use std::collections::{BTreeMap, BTreeSet}; +use crate::dsl::task_macro::*; use entity::sea_orm::{DatabaseTransaction, Set}; use pallas::ledger::traverse::{MultiEraBlock, MultiEraTx}; -use crate::dsl::task_macro::*; +use std::collections::{BTreeMap, BTreeSet}; -use crate::{types::AssetPair, dsl::database_task::BlockInfo}; +use crate::{dsl::database_task::BlockInfo, types::AssetPair}; use super::common::asset_from_pair; pub const WR_V1_POOL_SCRIPT_HASH: &str = "e6c90a5923713af5786963dee0fdffd830ca7e0c86a041d9e5833e91"; pub const WR_V1_POOL_FIXED_ADA: u64 = 3_000_000; // every pool UTXO holds this amount of ADA + pub const WR_V1_SWAP_IN_ADA: u64 = 4_000_000; // oil ADA + agent fee pub const WR_V1_SWAP_OUT_ADA: u64 = 2_000_000; // oil ADA - pub struct QueuedMeanPrice { pub tx_id: i64, pub address: Vec, // pallas::crypto::hash::Hash<32> @@ -40,14 +40,13 @@ pub struct SundaeSwapV1; #[derive(Debug, PartialEq, Eq)] pub struct Empty; - impl Dex for Empty { fn queue_mean_price( - &self, - _queued_prices: &mut Vec, - _tx: &MultiEraTx, - _tx_id: i64, - ) { + &self, + _queued_prices: &mut Vec, + _tx: &MultiEraTx, + _tx_id: i64, + ) { unimplemented!(); } } @@ -64,7 +63,7 @@ struct PoolConfig { pub pool_type: PoolType, } -impl PoolConfig { +impl PoolConfig { fn as_trait(&self) -> &dyn Dex { match &self.pool_type { PoolType::WingRidersV1 => &WingRidersV1 {}, @@ -83,7 +82,7 @@ pub async fn handle_mean_price( pool_type: PoolType, ) -> Result<(), DbErr> { // 1) Parse mean prices - let pool = PoolConfig {pool_type}; + let pool = PoolConfig { pool_type }; let mean_value_trait = pool.as_trait(); let mut queued_prices = Vec::::default(); for (tx_body, cardano_transaction) in block.1.txs().iter().zip(multiera_txs) { @@ -153,4 +152,4 @@ pub fn reduce_ada_amount(pair: &AssetPair, amount: u64) -> u64 { } else { 0 } -} +} \ No newline at end of file From 9669ab36dea04fc6e9c943c010c058691c0db3d4 Mon Sep 17 00:00:00 2001 From: Michal Vido Date: Wed, 9 Nov 2022 18:57:02 +0100 Subject: [PATCH 18/41] moving constants to utils/dex.rs --- .../src/multiera/multiera_minswap_v1_mean_price.rs | 8 +++----- .../src/multiera/multiera_sundaeswap_v1_mean_price.rs | 11 ++++------- indexer/tasks/src/multiera/utils/dex.rs | 5 +++++ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs index c5df9dd3..14338cfd 100644 --- a/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs +++ b/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs @@ -3,6 +3,7 @@ use super::utils::common::{ }; use super::utils::dex::{ build_asset, handle_mean_price, Dex, MinSwapV1, PoolType, QueuedMeanPrice, + MS_V1_POOL_SCRIPT_HASH1, MS_V1_POOL_SCRIPT_HASH2, }; use super::{multiera_address::MultieraAddressTask, utils::common::asset_from_pair}; use crate::dsl::task_macro::*; @@ -14,9 +15,6 @@ use pallas::ledger::{ }; use std::collections::BTreeSet; -const POOL_SCRIPT_HASH: &str = "e1317b152faac13426e6a83e06ff88a4d62cce3c1634ab0a5ec13309"; -const POOL_SCRIPT_HASH2: &str = "57c8e718c201fba10a9da1748d675b54281d3b1b983c5d1687fc7317"; - carp_task! { name MultieraMinSwapV1MeanPriceTask; configuration EmptyConfig; @@ -48,8 +46,8 @@ impl Dex for MinSwapV1 { ) { // Find the pool address (Note: there should be at most one pool output) for output in tx.outputs().iter().find(|o| { - get_sheley_payment_hash(o.address()).as_deref() == Some(POOL_SCRIPT_HASH) - || get_sheley_payment_hash(o.address()).as_deref() == Some(POOL_SCRIPT_HASH2) + get_sheley_payment_hash(o.address()).as_deref() == Some(MS_V1_POOL_SCRIPT_HASH1) + || get_sheley_payment_hash(o.address()).as_deref() == Some(MS_V1_POOL_SCRIPT_HASH2) }) { // Remark: The datum that corresponds to the pool output's datum hash should be present // in tx.plutus_data() diff --git a/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs index 49f348b2..6e1d6232 100644 --- a/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs +++ b/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs @@ -3,6 +3,7 @@ use super::utils::common::{ }; use super::utils::dex::{ build_asset, handle_mean_price, Dex, PoolType, QueuedMeanPrice, SundaeSwapV1, + SS_V1_POOL_SCRIPT_HASH, }; use super::{multiera_address::MultieraAddressTask, utils::common::asset_from_pair}; use crate::dsl::task_macro::*; @@ -14,8 +15,6 @@ use pallas::ledger::{ }; use std::collections::BTreeSet; -const POOL_SCRIPT_HASH: &str = "4020e7fc2de75a0729c3cc3af715b34d98381e0cdbcfa99c950bc3ac"; - carp_task! { name MultieraSundaeSwapV1MeanPriceTask; configuration EmptyConfig; @@ -46,11 +45,9 @@ impl Dex for SundaeSwapV1 { tx_id: i64, ) { // Find the pool address (Note: there should be at most one pool output) - for output in tx - .outputs() - .iter() - .find(|o| get_sheley_payment_hash(o.address()).as_deref() == Some(POOL_SCRIPT_HASH)) - { + for output in tx.outputs().iter().find(|o| { + get_sheley_payment_hash(o.address()).as_deref() == Some(SS_V1_POOL_SCRIPT_HASH) + }) { // Remark: The datum that corresponds to the pool output's datum hash should be present // in tx.plutus_data() if let Some(datum) = get_plutus_datum_for_output(output, &tx.plutus_data()) { diff --git a/indexer/tasks/src/multiera/utils/dex.rs b/indexer/tasks/src/multiera/utils/dex.rs index 06587826..8b9edd30 100644 --- a/indexer/tasks/src/multiera/utils/dex.rs +++ b/indexer/tasks/src/multiera/utils/dex.rs @@ -9,6 +9,11 @@ use super::common::asset_from_pair; pub const WR_V1_POOL_SCRIPT_HASH: &str = "e6c90a5923713af5786963dee0fdffd830ca7e0c86a041d9e5833e91"; pub const WR_V1_POOL_FIXED_ADA: u64 = 3_000_000; // every pool UTXO holds this amount of ADA +pub const MS_V1_POOL_SCRIPT_HASH1: &str = + "e1317b152faac13426e6a83e06ff88a4d62cce3c1634ab0a5ec13309"; +pub const MS_V1_POOL_SCRIPT_HASH2: &str = + "57c8e718c201fba10a9da1748d675b54281d3b1b983c5d1687fc7317"; +pub const SS_V1_POOL_SCRIPT_HASH: &str = "4020e7fc2de75a0729c3cc3af715b34d98381e0cdbcfa99c950bc3ac"; pub const WR_V1_SWAP_IN_ADA: u64 = 4_000_000; // oil ADA + agent fee pub const WR_V1_SWAP_OUT_ADA: u64 = 2_000_000; // oil ADA From ecd8ec7d589ede19d808a568edf30da115f41c95 Mon Sep 17 00:00:00 2001 From: Michal Vido Date: Wed, 9 Nov 2022 19:00:06 +0100 Subject: [PATCH 19/41] tiny clean up --- .../src/multiera/multiera_wingriders_v1_mean_price.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs index baef14a5..8571d7b1 100644 --- a/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs +++ b/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs @@ -70,13 +70,6 @@ impl Dex for WingRidersV1 { let asset1 = build_asset(get_asset_item(0, 0), get_asset_item(0, 1)); let asset2 = build_asset(get_asset_item(1, 0), get_asset_item(1, 1)); - let get_fixed_ada = |pair: &AssetPair| -> u64 { - if pair.is_none() { - WR_V1_POOL_FIXED_ADA - } else { - 0 - } - }; let amount1 = get_asset_amount(output, &asset1) - treasury1 - reduce_ada_amount(&asset1, WR_V1_POOL_FIXED_ADA); From 188b4f6e66759c2879d96ff626412f58399bd37a Mon Sep 17 00:00:00 2001 From: David Misiak Date: Fri, 11 Nov 2022 16:51:23 +0100 Subject: [PATCH 20/41] Extract get_pool_output_and_datum --- .../multiera_minswap_v1_mean_price.rs | 56 +++++++++---------- .../multiera_sundaeswap_v1_mean_price.rs | 54 ++++++++---------- .../multiera_wingriders_v1_mean_price.rs | 38 +------------ .../multiera/multiera_wingriders_v1_swap.rs | 7 +-- indexer/tasks/src/multiera/utils/dex.rs | 32 ++++++++++- 5 files changed, 83 insertions(+), 104 deletions(-) diff --git a/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs index 14338cfd..1c63edea 100644 --- a/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs +++ b/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs @@ -2,8 +2,8 @@ use super::utils::common::{ get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, }; use super::utils::dex::{ - build_asset, handle_mean_price, Dex, MinSwapV1, PoolType, QueuedMeanPrice, - MS_V1_POOL_SCRIPT_HASH1, MS_V1_POOL_SCRIPT_HASH2, + build_asset, get_pool_output_and_datum, handle_mean_price, Dex, MinSwapV1, PoolType, + QueuedMeanPrice, MS_V1_POOL_SCRIPT_HASH1, MS_V1_POOL_SCRIPT_HASH2, }; use super::{multiera_address::MultieraAddressTask, utils::common::asset_from_pair}; use crate::dsl::task_macro::*; @@ -44,39 +44,33 @@ impl Dex for MinSwapV1 { tx: &MultiEraTx, tx_id: i64, ) { - // Find the pool address (Note: there should be at most one pool output) - for output in tx.outputs().iter().find(|o| { - get_sheley_payment_hash(o.address()).as_deref() == Some(MS_V1_POOL_SCRIPT_HASH1) - || get_sheley_payment_hash(o.address()).as_deref() == Some(MS_V1_POOL_SCRIPT_HASH2) - }) { - // Remark: The datum that corresponds to the pool output's datum hash should be present - // in tx.plutus_data() - if let Some(datum) = get_plutus_datum_for_output(output, &tx.plutus_data()) { - let datum = datum.to_json(); + if let Some((output, datum)) = + get_pool_output_and_datum(tx, &vec![MS_V1_POOL_SCRIPT_HASH1, MS_V1_POOL_SCRIPT_HASH2]) + { + let datum = datum.to_json(); - let get_asset_item = |i, j| { - let item = datum["fields"][i]["fields"][j]["bytes"] - .as_str() - .unwrap() - .to_string(); - hex::decode(item).unwrap() - }; + let get_asset_item = |i, j| { + let item = datum["fields"][i]["fields"][j]["bytes"] + .as_str() + .unwrap() + .to_string(); + hex::decode(item).unwrap() + }; - let asset1 = build_asset(get_asset_item(0, 0), get_asset_item(0, 1)); - let asset2 = build_asset(get_asset_item(1, 0), get_asset_item(1, 1)); + let asset1 = build_asset(get_asset_item(0, 0), get_asset_item(0, 1)); + let asset2 = build_asset(get_asset_item(1, 0), get_asset_item(1, 1)); - let amount1 = get_asset_amount(output, &asset1); - let amount2 = get_asset_amount(output, &asset2); + let amount1 = get_asset_amount(&output, &asset1); + let amount2 = get_asset_amount(&output, &asset2); - queued_prices.push(QueuedMeanPrice { - tx_id, - address: output.address().unwrap().to_vec(), - asset1, - asset2, - amount1, - amount2, - }); - } + queued_prices.push(QueuedMeanPrice { + tx_id, + address: output.address().unwrap().to_vec(), + asset1, + asset2, + amount1, + amount2, + }); } } } diff --git a/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs index 6e1d6232..201c6216 100644 --- a/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs +++ b/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs @@ -2,8 +2,8 @@ use super::utils::common::{ get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, }; use super::utils::dex::{ - build_asset, handle_mean_price, Dex, PoolType, QueuedMeanPrice, SundaeSwapV1, - SS_V1_POOL_SCRIPT_HASH, + build_asset, get_pool_output_and_datum, handle_mean_price, Dex, PoolType, QueuedMeanPrice, + SundaeSwapV1, SS_V1_POOL_SCRIPT_HASH, }; use super::{multiera_address::MultieraAddressTask, utils::common::asset_from_pair}; use crate::dsl::task_macro::*; @@ -44,38 +44,32 @@ impl Dex for SundaeSwapV1 { tx: &MultiEraTx, tx_id: i64, ) { - // Find the pool address (Note: there should be at most one pool output) - for output in tx.outputs().iter().find(|o| { - get_sheley_payment_hash(o.address()).as_deref() == Some(SS_V1_POOL_SCRIPT_HASH) - }) { - // Remark: The datum that corresponds to the pool output's datum hash should be present - // in tx.plutus_data() - if let Some(datum) = get_plutus_datum_for_output(output, &tx.plutus_data()) { - let datum = datum.to_json(); + if let Some((output, datum)) = get_pool_output_and_datum(tx, &vec![SS_V1_POOL_SCRIPT_HASH]) + { + let datum = datum.to_json(); - let get_asset_item = |i, j| { - let item = datum["fields"][0]["fields"][i]["fields"][j]["bytes"] - .as_str() - .unwrap() - .to_string(); - hex::decode(item).unwrap() - }; + let get_asset_item = |i, j| { + let item = datum["fields"][0]["fields"][i]["fields"][j]["bytes"] + .as_str() + .unwrap() + .to_string(); + hex::decode(item).unwrap() + }; - let asset1 = build_asset(get_asset_item(0, 0), get_asset_item(0, 1)); - let asset2 = build_asset(get_asset_item(1, 0), get_asset_item(1, 1)); + let asset1 = build_asset(get_asset_item(0, 0), get_asset_item(0, 1)); + let asset2 = build_asset(get_asset_item(1, 0), get_asset_item(1, 1)); - let amount1 = get_asset_amount(output, &asset1); - let amount2 = get_asset_amount(output, &asset2); + let amount1 = get_asset_amount(&output, &asset1); + let amount2 = get_asset_amount(&output, &asset2); - queued_prices.push(QueuedMeanPrice { - tx_id, - address: output.address().unwrap().to_vec(), - asset1, - asset2, - amount1, - amount2, - }); - } + queued_prices.push(QueuedMeanPrice { + tx_id, + address: output.address().unwrap().to_vec(), + asset1, + asset2, + amount1, + amount2, + }); } } } diff --git a/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs index 8571d7b1..7c04357a 100644 --- a/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs +++ b/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs @@ -2,9 +2,10 @@ use super::utils::common::{ get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, }; use super::utils::dex::{ - WR_V1_POOL_FIXED_ADA, WR_V1_POOL_SCRIPT_HASH, + WR_V1_POOL_SCRIPT_HASH, WR_V1_POOL_FIXED_ADA, build_asset, handle_mean_price, reduce_ada_amount, Dex, PoolType, QueuedMeanPrice, WingRidersV1, + get_pool_output_and_datum }; use super::{multiera_address::MultieraAddressTask, utils::common::asset_from_pair}; use crate::dsl::task_macro::*; @@ -108,38 +109,3 @@ pub fn get_pool_output<'b>(tx: &'b MultiEraTx) -> Option<(MultiEraOutput<'b>, al None } } - -fn queue_mean_price(queued_prices: &mut Vec, tx: &MultiEraTx, tx_id: i64) { - if let Some((output, datum)) = get_pool_output(tx) { - let datum = datum.to_json(); - - let treasury1 = datum["fields"][1]["fields"][2]["int"].as_u64().unwrap(); - let treasury2 = datum["fields"][1]["fields"][3]["int"].as_u64().unwrap(); - - let get_asset_item = |i, j| { - let item = datum["fields"][1]["fields"][0]["fields"][i]["fields"][j]["bytes"] - .as_str() - .unwrap() - .to_string(); - hex::decode(item).unwrap() - }; - let asset1 = build_asset(get_asset_item(0, 0), get_asset_item(0, 1)); - let asset2 = build_asset(get_asset_item(1, 0), get_asset_item(1, 1)); - - let amount1 = get_asset_amount(&output, &asset1) - - treasury1 - - reduce_ada_amount(&asset1, WR_V1_POOL_FIXED_ADA); - let amount2 = get_asset_amount(&output, &asset2) - - treasury2 - - reduce_ada_amount(&asset2, WR_V1_POOL_FIXED_ADA); - - queued_prices.push(QueuedMeanPrice { - tx_id, - address: output.address().unwrap().to_vec(), - asset1, - asset2, - amount1, - amount2, - }); - } -} \ No newline at end of file diff --git a/indexer/tasks/src/multiera/multiera_wingriders_v1_swap.rs b/indexer/tasks/src/multiera/multiera_wingriders_v1_swap.rs index c2435fa6..e03f6fdb 100644 --- a/indexer/tasks/src/multiera/multiera_wingriders_v1_swap.rs +++ b/indexer/tasks/src/multiera/multiera_wingriders_v1_swap.rs @@ -1,13 +1,12 @@ use std::collections::BTreeSet; use super::multiera_used_inputs::MultieraUsedInputTask; -use super::multiera_wingriders_v1_mean_price::get_pool_output; use super::utils::common::{ asset_from_pair, get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, }; use super::utils::dex::{ - build_asset, reduce_ada_amount, WR_V1_POOL_FIXED_ADA, WR_V1_POOL_SCRIPT_HASH, - WR_V1_SWAP_IN_ADA, WR_V1_SWAP_OUT_ADA, + build_asset, get_pool_output_and_datum, reduce_ada_amount, WR_V1_POOL_FIXED_ADA, + WR_V1_POOL_SCRIPT_HASH, WR_V1_SWAP_IN_ADA, WR_V1_SWAP_OUT_ADA, }; use crate::dsl::task_macro::*; use crate::era_common::get_outputs_for_inputs; @@ -128,7 +127,7 @@ fn queue_swap( tx_id: i64, multiera_used_inputs_to_outputs_map: &BTreeMap, BTreeMap>, ) { - if let Some((pool_output, _)) = get_pool_output(tx) { + if let Some((pool_output, _)) = get_pool_output_and_datum(tx, &vec![WR_V1_POOL_SCRIPT_HASH]) { if tx.redeemers().is_none() || tx.redeemers().unwrap().len() == 0 { return; } diff --git a/indexer/tasks/src/multiera/utils/dex.rs b/indexer/tasks/src/multiera/utils/dex.rs index 8b9edd30..4b5254ff 100644 --- a/indexer/tasks/src/multiera/utils/dex.rs +++ b/indexer/tasks/src/multiera/utils/dex.rs @@ -1,11 +1,14 @@ use crate::dsl::task_macro::*; use entity::sea_orm::{DatabaseTransaction, Set}; -use pallas::ledger::traverse::{MultiEraBlock, MultiEraTx}; +use pallas::ledger::{ + primitives::alonzo, + traverse::{MultiEraBlock, MultiEraOutput, MultiEraTx}, +}; use std::collections::{BTreeMap, BTreeSet}; use crate::{dsl::database_task::BlockInfo, types::AssetPair}; -use super::common::asset_from_pair; +use super::common::{asset_from_pair, get_plutus_datum_for_output, get_sheley_payment_hash}; pub const WR_V1_POOL_SCRIPT_HASH: &str = "e6c90a5923713af5786963dee0fdffd830ca7e0c86a041d9e5833e91"; pub const WR_V1_POOL_FIXED_ADA: u64 = 3_000_000; // every pool UTXO holds this amount of ADA @@ -157,4 +160,27 @@ pub fn reduce_ada_amount(pair: &AssetPair, amount: u64) -> u64 { } else { 0 } -} \ No newline at end of file +} + +pub fn get_pool_output_and_datum<'b>( + tx: &'b MultiEraTx, + pool_hashes: &[&str], +) -> Option<(MultiEraOutput<'b>, alonzo::PlutusData)> { + let pool_hashes = pool_hashes.iter().map(|&s| Some(s)).collect::>(); + // Note: there should be at most one pool output + if let Some(output) = tx + .outputs() + .iter() + .find(|o| pool_hashes.contains(&get_sheley_payment_hash(o.address()).as_deref())) + { + // Remark: The datum that corresponds to the pool output's datum hash should be present + // in tx.plutus_data() + if let Some(datum) = get_plutus_datum_for_output(output, &tx.plutus_data()) { + Some((output.clone(), datum)) + } else { + None + } + } else { + None + } +} From ba7c20c68fa063722874492d6906928b292b88ef Mon Sep 17 00:00:00 2001 From: David Misiak Date: Fri, 11 Nov 2022 17:38:54 +0100 Subject: [PATCH 21/41] Extract dex-related code --- .../multiera/{utils/dex.rs => dex/common.rs} | 148 ++++++++++-- indexer/tasks/src/multiera/dex/minswap_v1.rs | 58 +++++ indexer/tasks/src/multiera/dex/mod.rs | 4 + .../tasks/src/multiera/dex/sundaeswap_v1.rs | 57 +++++ .../tasks/src/multiera/dex/wingriders_v1.rs | 174 +++++++++++++++ indexer/tasks/src/multiera/mod.rs | 1 + .../multiera_minswap_v1_mean_price.rs | 56 +---- .../multiera_sundaeswap_v1_mean_price.rs | 55 +---- .../multiera_wingriders_v1_mean_price.rs | 73 ++---- .../multiera/multiera_wingriders_v1_swap.rs | 210 +----------------- indexer/tasks/src/multiera/utils/mod.rs | 1 - 11 files changed, 442 insertions(+), 395 deletions(-) rename indexer/tasks/src/multiera/{utils/dex.rs => dex/common.rs} (61%) create mode 100644 indexer/tasks/src/multiera/dex/minswap_v1.rs create mode 100644 indexer/tasks/src/multiera/dex/mod.rs create mode 100644 indexer/tasks/src/multiera/dex/sundaeswap_v1.rs create mode 100644 indexer/tasks/src/multiera/dex/wingriders_v1.rs diff --git a/indexer/tasks/src/multiera/utils/dex.rs b/indexer/tasks/src/multiera/dex/common.rs similarity index 61% rename from indexer/tasks/src/multiera/utils/dex.rs rename to indexer/tasks/src/multiera/dex/common.rs index 4b5254ff..cb3da8cc 100644 --- a/indexer/tasks/src/multiera/utils/dex.rs +++ b/indexer/tasks/src/multiera/dex/common.rs @@ -1,4 +1,10 @@ -use crate::dsl::task_macro::*; +use crate::{ + dsl::task_macro::*, + multiera::utils::common::{ + asset_from_pair, get_plutus_datum_for_output, get_sheley_payment_hash, + }, + types::DexSwapDirection, +}; use entity::sea_orm::{DatabaseTransaction, Set}; use pallas::ledger::{ primitives::alonzo, @@ -8,8 +14,6 @@ use std::collections::{BTreeMap, BTreeSet}; use crate::{dsl::database_task::BlockInfo, types::AssetPair}; -use super::common::{asset_from_pair, get_plutus_datum_for_output, get_sheley_payment_hash}; - pub const WR_V1_POOL_SCRIPT_HASH: &str = "e6c90a5923713af5786963dee0fdffd830ca7e0c86a041d9e5833e91"; pub const WR_V1_POOL_FIXED_ADA: u64 = 3_000_000; // every pool UTXO holds this amount of ADA pub const MS_V1_POOL_SCRIPT_HASH1: &str = @@ -21,6 +25,29 @@ pub const SS_V1_POOL_SCRIPT_HASH: &str = "4020e7fc2de75a0729c3cc3af715b34d98381e pub const WR_V1_SWAP_IN_ADA: u64 = 4_000_000; // oil ADA + agent fee pub const WR_V1_SWAP_OUT_ADA: u64 = 2_000_000; // oil ADA +pub fn get_pool_output_and_datum<'b>( + tx: &'b MultiEraTx, + pool_hashes: &[&str], +) -> Option<(MultiEraOutput<'b>, alonzo::PlutusData)> { + let pool_hashes = pool_hashes.iter().map(|&s| Some(s)).collect::>(); + // Note: there should be at most one pool output + if let Some(output) = tx + .outputs() + .iter() + .find(|o| pool_hashes.contains(&get_sheley_payment_hash(o.address()).as_deref())) + { + // Remark: The datum that corresponds to the pool output's datum hash should be present + // in tx.plutus_data() + if let Some(datum) = get_plutus_datum_for_output(output, &tx.plutus_data()) { + Some((output.clone(), datum)) + } else { + None + } + } else { + None + } +} + pub struct QueuedMeanPrice { pub tx_id: i64, pub address: Vec, // pallas::crypto::hash::Hash<32> @@ -30,6 +57,16 @@ pub struct QueuedMeanPrice { pub amount2: u64, } +pub struct QueuedSwap { + pub tx_id: i64, + pub address: Vec, // pallas::crypto::hash::Hash<32> + pub asset1: AssetPair, + pub asset2: AssetPair, + pub amount1: u64, + pub amount2: u64, + pub direction: DexSwapDirection, +} + pub trait Dex { fn queue_mean_price( &self, @@ -37,6 +74,14 @@ pub trait Dex { tx: &MultiEraTx, tx_id: i64, ); + + fn queue_swap( + &self, + queued_swaps: &mut Vec, + tx: &MultiEraTx, + tx_id: i64, + multiera_used_inputs_to_outputs_map: &BTreeMap, BTreeMap>, + ); } #[derive(Debug, PartialEq, Eq)] @@ -57,6 +102,16 @@ impl Dex for Empty { ) { unimplemented!(); } + + fn queue_swap( + &self, + _queued_swaps: &mut Vec, + _tx: &MultiEraTx, + _tx_id: i64, + _multiera_used_inputs_to_outputs_map: &BTreeMap, BTreeMap>, + ) { + unimplemented!(); + } } #[derive(Debug, PartialEq, Eq)] @@ -162,25 +217,74 @@ pub fn reduce_ada_amount(pair: &AssetPair, amount: u64) -> u64 { } } -pub fn get_pool_output_and_datum<'b>( - tx: &'b MultiEraTx, - pool_hashes: &[&str], -) -> Option<(MultiEraOutput<'b>, alonzo::PlutusData)> { - let pool_hashes = pool_hashes.iter().map(|&s| Some(s)).collect::>(); - // Note: there should be at most one pool output - if let Some(output) = tx - .outputs() - .iter() - .find(|o| pool_hashes.contains(&get_sheley_payment_hash(o.address()).as_deref())) - { - // Remark: The datum that corresponds to the pool output's datum hash should be present - // in tx.plutus_data() - if let Some(datum) = get_plutus_datum_for_output(output, &tx.plutus_data()) { - Some((output.clone(), datum)) - } else { - None +pub async fn handle_swap( + db_tx: &DatabaseTransaction, + block: BlockInfo<'_, MultiEraBlock<'_>>, + multiera_txs: &[TransactionModel], + multiera_addresses: &BTreeMap, AddressInBlock>, + multiera_used_inputs_to_outputs_map: &BTreeMap, BTreeMap>, + pool_type: PoolType, +) -> Result<(), DbErr> { + // 1) Parse swaps + let pool = PoolConfig { pool_type }; + let swap_trait = pool.as_trait(); + let mut queued_swaps = Vec::::default(); + for (tx_body, cardano_transaction) in block.1.txs().iter().zip(multiera_txs) { + if cardano_transaction.is_valid { + swap_trait.queue_swap( + &mut queued_swaps, + tx_body, + cardano_transaction.id, + multiera_used_inputs_to_outputs_map, + ); + } + } + + if queued_swaps.is_empty() { + return Ok(()); + } + + // 2) Remove asset duplicates to build a list of all the to query for. + // ADA is ignored, it's not in the NativeAsset DB table + let mut unique_tokens = BTreeSet::<&(Vec, Vec)>::default(); + for p in &queued_swaps { + if let Some(pair) = &p.asset1 { + unique_tokens.insert(&pair); + } + if let Some(pair) = &p.asset2 { + unique_tokens.insert(&pair); } - } else { - None } + + // 3) Query for asset ids + // TODO use the query result from mean price task? + let found_assets = asset_from_pair( + db_tx, + &unique_tokens + .iter() + .map(|(policy_id, asset_name)| (policy_id.clone(), asset_name.clone())) + .collect::>(), + ) + .await?; + let mut asset_pair_to_id_map = found_assets + .into_iter() + .map(|asset| (Some((asset.policy_id, asset.asset_name)), Some(asset.id))) + .collect::>(); + asset_pair_to_id_map.insert(None, None); // ADA + + // 4) Add mean prices to DB + DexSwap::insert_many(queued_swaps.iter().map(|price| DexSwapActiveModel { + tx_id: Set(price.tx_id), + address_id: Set(multiera_addresses[&price.address].model.id), + asset1_id: Set(asset_pair_to_id_map[&price.asset1]), + asset2_id: Set(asset_pair_to_id_map[&price.asset2]), + amount1: Set(price.amount1), + amount2: Set(price.amount2), + direction: Set(price.direction.into()), + ..Default::default() + })) + .exec(db_tx) + .await?; + + Ok(()) } diff --git a/indexer/tasks/src/multiera/dex/minswap_v1.rs b/indexer/tasks/src/multiera/dex/minswap_v1.rs new file mode 100644 index 00000000..223b9a21 --- /dev/null +++ b/indexer/tasks/src/multiera/dex/minswap_v1.rs @@ -0,0 +1,58 @@ +use std::collections::BTreeMap; + +use pallas::ledger::{primitives::ToCanonicalJson, traverse::MultiEraTx}; + +use crate::{era_common::OutputWithTxData, multiera::utils::common::get_asset_amount}; + +use super::common::{ + build_asset, get_pool_output_and_datum, Dex, MinSwapV1, QueuedMeanPrice, QueuedSwap, + MS_V1_POOL_SCRIPT_HASH1, MS_V1_POOL_SCRIPT_HASH2, +}; + +impl Dex for MinSwapV1 { + fn queue_mean_price( + &self, + queued_prices: &mut Vec, + tx: &MultiEraTx, + tx_id: i64, + ) { + if let Some((output, datum)) = + get_pool_output_and_datum(tx, &vec![MS_V1_POOL_SCRIPT_HASH1, MS_V1_POOL_SCRIPT_HASH2]) + { + let datum = datum.to_json(); + + let get_asset_item = |i, j| { + let item = datum["fields"][i]["fields"][j]["bytes"] + .as_str() + .unwrap() + .to_string(); + hex::decode(item).unwrap() + }; + + let asset1 = build_asset(get_asset_item(0, 0), get_asset_item(0, 1)); + let asset2 = build_asset(get_asset_item(1, 0), get_asset_item(1, 1)); + + let amount1 = get_asset_amount(&output, &asset1); + let amount2 = get_asset_amount(&output, &asset2); + + queued_prices.push(QueuedMeanPrice { + tx_id, + address: output.address().unwrap().to_vec(), + asset1, + asset2, + amount1, + amount2, + }); + } + } + + fn queue_swap( + &self, + _queued_swaps: &mut Vec, + _tx: &MultiEraTx, + _tx_id: i64, + _multiera_used_inputs_to_outputs_map: &BTreeMap, BTreeMap>, + ) { + unimplemented!() + } +} diff --git a/indexer/tasks/src/multiera/dex/mod.rs b/indexer/tasks/src/multiera/dex/mod.rs new file mode 100644 index 00000000..024db66f --- /dev/null +++ b/indexer/tasks/src/multiera/dex/mod.rs @@ -0,0 +1,4 @@ +pub mod common; +pub mod minswap_v1; +pub mod sundaeswap_v1; +pub mod wingriders_v1; diff --git a/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs b/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs new file mode 100644 index 00000000..d84724d3 --- /dev/null +++ b/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs @@ -0,0 +1,57 @@ +use std::collections::BTreeMap; + +use pallas::ledger::{primitives::ToCanonicalJson, traverse::MultiEraTx}; + +use crate::{era_common::OutputWithTxData, multiera::utils::common::get_asset_amount}; + +use super::common::{ + build_asset, get_pool_output_and_datum, Dex, QueuedMeanPrice, QueuedSwap, SundaeSwapV1, + SS_V1_POOL_SCRIPT_HASH, +}; + +impl Dex for SundaeSwapV1 { + fn queue_mean_price( + &self, + queued_prices: &mut Vec, + tx: &MultiEraTx, + tx_id: i64, + ) { + if let Some((output, datum)) = get_pool_output_and_datum(tx, &vec![SS_V1_POOL_SCRIPT_HASH]) + { + let datum = datum.to_json(); + + let get_asset_item = |i, j| { + let item = datum["fields"][0]["fields"][i]["fields"][j]["bytes"] + .as_str() + .unwrap() + .to_string(); + hex::decode(item).unwrap() + }; + + let asset1 = build_asset(get_asset_item(0, 0), get_asset_item(0, 1)); + let asset2 = build_asset(get_asset_item(1, 0), get_asset_item(1, 1)); + + let amount1 = get_asset_amount(&output, &asset1); + let amount2 = get_asset_amount(&output, &asset2); + + queued_prices.push(QueuedMeanPrice { + tx_id, + address: output.address().unwrap().to_vec(), + asset1, + asset2, + amount1, + amount2, + }); + } + } + + fn queue_swap( + &self, + _queued_swaps: &mut Vec, + _tx: &MultiEraTx, + _tx_id: i64, + _multiera_used_inputs_to_outputs_map: &BTreeMap, BTreeMap>, + ) { + unimplemented!() + } +} diff --git a/indexer/tasks/src/multiera/dex/wingriders_v1.rs b/indexer/tasks/src/multiera/dex/wingriders_v1.rs new file mode 100644 index 00000000..378ac11f --- /dev/null +++ b/indexer/tasks/src/multiera/dex/wingriders_v1.rs @@ -0,0 +1,174 @@ +use std::collections::BTreeMap; + +use pallas::ledger::{ + primitives::ToCanonicalJson, + traverse::{MultiEraOutput, MultiEraTx}, +}; + +use crate::{ + era_common::OutputWithTxData, + multiera::utils::common::{get_asset_amount, get_plutus_datum_for_output}, + types::DexSwapDirection, +}; + +use super::common::{ + build_asset, get_pool_output_and_datum, reduce_ada_amount, Dex, QueuedMeanPrice, QueuedSwap, + WingRidersV1, WR_V1_POOL_FIXED_ADA, WR_V1_POOL_SCRIPT_HASH, WR_V1_SWAP_IN_ADA, + WR_V1_SWAP_OUT_ADA, +}; + +impl Dex for WingRidersV1 { + fn queue_mean_price( + &self, + queued_prices: &mut Vec, + tx: &MultiEraTx, + tx_id: i64, + ) { + if let Some((output, datum)) = get_pool_output_and_datum(tx, &vec![WR_V1_POOL_SCRIPT_HASH]) + { + let datum = datum.to_json(); + + let treasury1 = datum["fields"][1]["fields"][2]["int"].as_u64().unwrap(); + let treasury2 = datum["fields"][1]["fields"][3]["int"].as_u64().unwrap(); + + let get_asset_item = |i, j| { + let item = datum["fields"][1]["fields"][0]["fields"][i]["fields"][j]["bytes"] + .as_str() + .unwrap() + .to_string(); + hex::decode(item).unwrap() + }; + + let asset1 = build_asset(get_asset_item(0, 0), get_asset_item(0, 1)); + let asset2 = build_asset(get_asset_item(1, 0), get_asset_item(1, 1)); + + let amount1 = get_asset_amount(&output, &asset1) + - treasury1 + - reduce_ada_amount(&asset1, WR_V1_POOL_FIXED_ADA); + let amount2 = get_asset_amount(&output, &asset2) + - treasury2 + - reduce_ada_amount(&asset2, WR_V1_POOL_FIXED_ADA); + + queued_prices.push(QueuedMeanPrice { + tx_id, + address: output.address().unwrap().to_vec(), + asset1, + asset2, + amount1, + amount2, + }); + } + } + + fn queue_swap( + &self, + queued_swaps: &mut Vec, + tx: &MultiEraTx, + tx_id: i64, + multiera_used_inputs_to_outputs_map: &BTreeMap, BTreeMap>, + ) { + if let Some((pool_output, _)) = get_pool_output_and_datum(tx, &vec![WR_V1_POOL_SCRIPT_HASH]) + { + if tx.redeemers().is_none() || tx.redeemers().unwrap().len() == 0 { + return; + } + let redeemers = tx.redeemers().unwrap(); + + // Get pool input from redemeers + let pool_input = redeemers[0].data.to_json()["fields"][0]["int"].as_i64(); + if pool_input.is_none() { + return; + } + let pool_input = pool_input.unwrap(); + + // Find main redemeer + if let Some(redeemer) = redeemers.iter().find(|&r| r.index as i64 == pool_input) { + let redeemer = redeemer.data.to_json(); + + // Extract input list from redemeer + let redeemer_map: Vec = redeemer["fields"][2]["list"] + .as_array() + .unwrap() + .iter() + .map(|r| r["int"].as_i64().unwrap() as usize) + .collect(); + // Find main transaction + let parent = redeemer["fields"][0]["int"].as_i64().unwrap() as usize; + // Restore inputs + let inputs: Vec = tx + .inputs() + .iter() + .map(|i| { + let output = &multiera_used_inputs_to_outputs_map[&i.hash().to_vec()] + [&(i.index() as i64)]; + MultiEraOutput::decode(output.era, &output.model.payload).unwrap() + }) + .collect::>(); + // Zip outputs with redemeer index + for (output, redeemer) in tx.outputs().iter().skip(1).zip(redeemer_map) { + // pair input with output + let input = inputs[redeemer].clone(); + + // get information about swap from pool plutus data + let parent_datum = + get_plutus_datum_for_output(&inputs[parent], &tx.plutus_data()) + .unwrap() + .to_json(); + let parse_asset_item = |i, j| { + let item = parent_datum["fields"][1]["fields"][0]["fields"][i]["fields"][j] + ["bytes"] + .as_str() + .unwrap() + .to_string(); + hex::decode(item).unwrap() + }; + let asset1 = build_asset(parse_asset_item(0, 0), parse_asset_item(0, 1)); + let asset2 = build_asset(parse_asset_item(1, 0), parse_asset_item(1, 1)); + + // get actual plutus datum + let input_datum = get_plutus_datum_for_output(&input, &tx.plutus_data()) + .unwrap() + .to_json(); + // identify operation: 0 = swap + let operation = input_datum["fields"][1]["constructor"].as_i64().unwrap(); + if operation != 0 { + tracing::debug!("Operation is not a swap"); + continue; + } + let direction = input_datum["fields"][1]["fields"][0]["constructor"] + .as_i64() + .unwrap(); + + let amount1; + let amount2; + if direction == 0 { + amount1 = get_asset_amount(&input, &asset1) + - reduce_ada_amount(&asset1, WR_V1_SWAP_IN_ADA); + amount2 = get_asset_amount(&output, &asset2) + - reduce_ada_amount(&asset2, WR_V1_SWAP_OUT_ADA); + } else { + amount1 = get_asset_amount(&output, &asset1) + - reduce_ada_amount(&asset1, WR_V1_SWAP_OUT_ADA); + amount2 = get_asset_amount(&input, &asset2) + - reduce_ada_amount(&asset2, WR_V1_SWAP_IN_ADA); + } + queued_swaps.push(QueuedSwap { + tx_id, + address: pool_output.address().unwrap().to_vec(), + asset1, + asset2, + amount1, + amount2, + direction: if direction == 0 { + DexSwapDirection::SellAsset1 + } else { + DexSwapDirection::BuyAsset1 + }, + }) + } + } else { + tracing::info!("Redeemer not found"); + } + } + } +} diff --git a/indexer/tasks/src/multiera/mod.rs b/indexer/tasks/src/multiera/mod.rs index f4184e27..669069ab 100644 --- a/indexer/tasks/src/multiera/mod.rs +++ b/indexer/tasks/src/multiera/mod.rs @@ -1,3 +1,4 @@ +pub mod dex; pub mod multiera_address; pub mod multiera_address_credential_relations; pub mod multiera_asset_mint; diff --git a/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs index 1c63edea..8dc90e18 100644 --- a/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs +++ b/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs @@ -1,19 +1,7 @@ -use super::utils::common::{ - get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, -}; -use super::utils::dex::{ - build_asset, get_pool_output_and_datum, handle_mean_price, Dex, MinSwapV1, PoolType, - QueuedMeanPrice, MS_V1_POOL_SCRIPT_HASH1, MS_V1_POOL_SCRIPT_HASH2, -}; -use super::{multiera_address::MultieraAddressTask, utils::common::asset_from_pair}; +use super::dex::common::{handle_mean_price, PoolType}; +use super::multiera_address::MultieraAddressTask; +use crate::config::EmptyConfig::EmptyConfig; use crate::dsl::task_macro::*; -use crate::{config::EmptyConfig::EmptyConfig, types::AssetPair}; -use entity::sea_orm::{DatabaseTransaction, Set}; -use pallas::ledger::{ - primitives::ToCanonicalJson, - traverse::{MultiEraBlock, MultiEraTx}, -}; -use std::collections::BTreeSet; carp_task! { name MultieraMinSwapV1MeanPriceTask; @@ -36,41 +24,3 @@ carp_task! { merge_result |previous_data, _result| { }; } - -impl Dex for MinSwapV1 { - fn queue_mean_price( - &self, - queued_prices: &mut Vec, - tx: &MultiEraTx, - tx_id: i64, - ) { - if let Some((output, datum)) = - get_pool_output_and_datum(tx, &vec![MS_V1_POOL_SCRIPT_HASH1, MS_V1_POOL_SCRIPT_HASH2]) - { - let datum = datum.to_json(); - - let get_asset_item = |i, j| { - let item = datum["fields"][i]["fields"][j]["bytes"] - .as_str() - .unwrap() - .to_string(); - hex::decode(item).unwrap() - }; - - let asset1 = build_asset(get_asset_item(0, 0), get_asset_item(0, 1)); - let asset2 = build_asset(get_asset_item(1, 0), get_asset_item(1, 1)); - - let amount1 = get_asset_amount(&output, &asset1); - let amount2 = get_asset_amount(&output, &asset2); - - queued_prices.push(QueuedMeanPrice { - tx_id, - address: output.address().unwrap().to_vec(), - asset1, - asset2, - amount1, - amount2, - }); - } - } -} diff --git a/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs index 201c6216..3b7b3eb6 100644 --- a/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs +++ b/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs @@ -1,19 +1,7 @@ -use super::utils::common::{ - get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, -}; -use super::utils::dex::{ - build_asset, get_pool_output_and_datum, handle_mean_price, Dex, PoolType, QueuedMeanPrice, - SundaeSwapV1, SS_V1_POOL_SCRIPT_HASH, -}; -use super::{multiera_address::MultieraAddressTask, utils::common::asset_from_pair}; +use super::dex::common::{handle_mean_price, PoolType}; +use super::multiera_address::MultieraAddressTask; +use crate::config::EmptyConfig::EmptyConfig; use crate::dsl::task_macro::*; -use crate::{config::EmptyConfig::EmptyConfig, types::AssetPair}; -use entity::sea_orm::{DatabaseTransaction, Set}; -use pallas::ledger::{ - primitives::ToCanonicalJson, - traverse::{MultiEraBlock, MultiEraTx}, -}; -use std::collections::BTreeSet; carp_task! { name MultieraSundaeSwapV1MeanPriceTask; @@ -36,40 +24,3 @@ carp_task! { merge_result |previous_data, _result| { }; } - -impl Dex for SundaeSwapV1 { - fn queue_mean_price( - &self, - queued_prices: &mut Vec, - tx: &MultiEraTx, - tx_id: i64, - ) { - if let Some((output, datum)) = get_pool_output_and_datum(tx, &vec![SS_V1_POOL_SCRIPT_HASH]) - { - let datum = datum.to_json(); - - let get_asset_item = |i, j| { - let item = datum["fields"][0]["fields"][i]["fields"][j]["bytes"] - .as_str() - .unwrap() - .to_string(); - hex::decode(item).unwrap() - }; - - let asset1 = build_asset(get_asset_item(0, 0), get_asset_item(0, 1)); - let asset2 = build_asset(get_asset_item(1, 0), get_asset_item(1, 1)); - - let amount1 = get_asset_amount(&output, &asset1); - let amount2 = get_asset_amount(&output, &asset2); - - queued_prices.push(QueuedMeanPrice { - tx_id, - address: output.address().unwrap().to_vec(), - asset1, - asset2, - amount1, - amount2, - }); - } - } -} diff --git a/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs index 7c04357a..cb9ae057 100644 --- a/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs +++ b/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs @@ -1,22 +1,26 @@ use super::utils::common::{ get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, }; -use super::utils::dex::{ +use crate::multiera::dex::common::{ WR_V1_POOL_SCRIPT_HASH, WR_V1_POOL_FIXED_ADA, build_asset, handle_mean_price, reduce_ada_amount, Dex, PoolType, QueuedMeanPrice, WingRidersV1, - get_pool_output_and_datum + get_pool_output_and_datum }; -use super::{multiera_address::MultieraAddressTask, utils::common::asset_from_pair}; -use crate::dsl::task_macro::*; -use crate::{config::EmptyConfig::EmptyConfig, types::AssetPair}; -use entity::sea_orm::{DatabaseTransaction, Set}; use pallas::ledger::primitives::alonzo; +use super::{multiera_address::MultieraAddressTask, utils::common::asset_from_pair}; +use crate::config::EmptyConfig::EmptyConfig; +/* +use super::dex::common::{handle_mean_price, PoolType}; +use super::multiera_address::MultieraAddressTask; + +*/ + use pallas::ledger::{ - primitives::ToCanonicalJson, - traverse::{MultiEraBlock, MultiEraOutput, MultiEraTx}, + primitives::{alonzo::Certificate, Fragment}, + traverse::{MultiEraBlock, MultiEraCert, MultiEraOutput, MultiEraTx}, }; -use std::collections::BTreeSet; +use crate::dsl::task_macro::*; carp_task! { name MultieraWingRidersV1MeanPriceTask; @@ -40,57 +44,6 @@ carp_task! { }; } -impl Dex for WingRidersV1 { - fn queue_mean_price( - &self, - queued_prices: &mut Vec, - tx: &MultiEraTx, - tx_id: i64, - ) { - // Find the pool address (Note: there should be at most one pool output) - for output in tx - .outputs() - .iter() - .find(|o| get_sheley_payment_hash(o.address()).as_deref() == Some(WR_V1_POOL_SCRIPT_HASH)) - { - // Remark: The datum that corresponds to the pool output's datum hash should be present - // in tx.plutus_data() - if let Some(datum) = get_plutus_datum_for_output(output, &tx.plutus_data()) { - let datum = datum.to_json(); - - let treasury1 = datum["fields"][1]["fields"][2]["int"].as_u64().unwrap(); - let treasury2 = datum["fields"][1]["fields"][3]["int"].as_u64().unwrap(); - - let get_asset_item = |i, j| { - let item = datum["fields"][1]["fields"][0]["fields"][i]["fields"][j]["bytes"] - .as_str() - .unwrap() - .to_string(); - hex::decode(item).unwrap() - }; - let asset1 = build_asset(get_asset_item(0, 0), get_asset_item(0, 1)); - let asset2 = build_asset(get_asset_item(1, 0), get_asset_item(1, 1)); - - let amount1 = get_asset_amount(output, &asset1) - - treasury1 - - reduce_ada_amount(&asset1, WR_V1_POOL_FIXED_ADA); - let amount2 = get_asset_amount(output, &asset2) - - treasury2 - - reduce_ada_amount(&asset2, WR_V1_POOL_FIXED_ADA); - - queued_prices.push(QueuedMeanPrice { - tx_id, - address: output.address().unwrap().to_vec(), - asset1, - asset2, - amount1, - amount2, - }); - } - } - } -} - pub fn get_pool_output<'b>(tx: &'b MultiEraTx) -> Option<(MultiEraOutput<'b>, alonzo::PlutusData)> { // Note: there should be at most one pool output if let Some(output) = tx diff --git a/indexer/tasks/src/multiera/multiera_wingriders_v1_swap.rs b/indexer/tasks/src/multiera/multiera_wingriders_v1_swap.rs index e03f6fdb..d5692f44 100644 --- a/indexer/tasks/src/multiera/multiera_wingriders_v1_swap.rs +++ b/indexer/tasks/src/multiera/multiera_wingriders_v1_swap.rs @@ -1,24 +1,7 @@ -use std::collections::BTreeSet; - +use super::dex::common::{handle_swap, PoolType}; use super::multiera_used_inputs::MultieraUsedInputTask; -use super::utils::common::{ - asset_from_pair, get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, -}; -use super::utils::dex::{ - build_asset, get_pool_output_and_datum, reduce_ada_amount, WR_V1_POOL_FIXED_ADA, - WR_V1_POOL_SCRIPT_HASH, WR_V1_SWAP_IN_ADA, WR_V1_SWAP_OUT_ADA, -}; +use crate::config::EmptyConfig::EmptyConfig; use crate::dsl::task_macro::*; -use crate::era_common::get_outputs_for_inputs; -use crate::era_common::OutputWithTxData; -use crate::types::DexSwapDirection; -use crate::{config::EmptyConfig::EmptyConfig, types::AssetPair}; -use entity::sea_orm::{DatabaseTransaction, Set}; -use pallas::ledger::traverse::{Era, MultiEraOutput}; -use pallas::ledger::{ - primitives::ToCanonicalJson, - traverse::{MultiEraBlock, MultiEraTx}, -}; carp_task! { name MultieraWingRidersV1SwapTask; @@ -37,195 +20,8 @@ carp_task! { &previous_data.multiera_txs, &previous_data.multiera_addresses, &previous_data.multiera_used_inputs_to_outputs_map, + PoolType::WingRidersV1, ); merge_result |previous_data, _result| { }; } - -struct QueuedSwap { - tx_id: i64, - address: Vec, // pallas::crypto::hash::Hash<32> - asset1: AssetPair, - asset2: AssetPair, - amount1: u64, - amount2: u64, - direction: DexSwapDirection, -} - -async fn handle_swap( - db_tx: &DatabaseTransaction, - block: BlockInfo<'_, MultiEraBlock<'_>>, - multiera_txs: &[TransactionModel], - multiera_addresses: &BTreeMap, AddressInBlock>, - multiera_used_inputs_to_outputs_map: &BTreeMap, BTreeMap>, -) -> Result<(), DbErr> { - // 1) Parse swaps - let mut queued_swaps = Vec::::default(); - for (tx_body, cardano_transaction) in block.1.txs().iter().zip(multiera_txs) { - if cardano_transaction.is_valid { - queue_swap( - &mut queued_swaps, - tx_body, - cardano_transaction.id, - multiera_used_inputs_to_outputs_map, - ); - } - } - - if queued_swaps.is_empty() { - return Ok(()); - } - - // 2) Remove asset duplicates to build a list of all the to query for. - // ADA is ignored, it's not in the NativeAsset DB table - let mut unique_tokens = BTreeSet::<&(Vec, Vec)>::default(); - for p in &queued_swaps { - if let Some(pair) = &p.asset1 { - unique_tokens.insert(&pair); - } - if let Some(pair) = &p.asset2 { - unique_tokens.insert(&pair); - } - } - - // 3) Query for asset ids - // TODO use the query result from mean price task? - let found_assets = asset_from_pair( - db_tx, - &unique_tokens - .iter() - .map(|(policy_id, asset_name)| (policy_id.clone(), asset_name.clone())) - .collect::>(), - ) - .await?; - let mut asset_pair_to_id_map = found_assets - .into_iter() - .map(|asset| (Some((asset.policy_id, asset.asset_name)), Some(asset.id))) - .collect::>(); - asset_pair_to_id_map.insert(None, None); // ADA - - // 4) Add mean prices to DB - DexSwap::insert_many(queued_swaps.iter().map(|price| DexSwapActiveModel { - tx_id: Set(price.tx_id), - address_id: Set(multiera_addresses[&price.address].model.id), - asset1_id: Set(asset_pair_to_id_map[&price.asset1]), - asset2_id: Set(asset_pair_to_id_map[&price.asset2]), - amount1: Set(price.amount1), - amount2: Set(price.amount2), - direction: Set(price.direction.into()), - ..Default::default() - })) - .exec(db_tx) - .await?; - - Ok(()) -} - -fn queue_swap( - queued_swaps: &mut Vec, - tx: &MultiEraTx, - tx_id: i64, - multiera_used_inputs_to_outputs_map: &BTreeMap, BTreeMap>, -) { - if let Some((pool_output, _)) = get_pool_output_and_datum(tx, &vec![WR_V1_POOL_SCRIPT_HASH]) { - if tx.redeemers().is_none() || tx.redeemers().unwrap().len() == 0 { - return; - } - let redeemers = tx.redeemers().unwrap(); - - // Get pool input from redemeers - let pool_input = redeemers[0].data.to_json()["fields"][0]["int"].as_i64(); - if pool_input.is_none() { - return; - } - let pool_input = pool_input.unwrap(); - - // Find main redemeer - if let Some(redeemer) = redeemers.iter().find(|&r| r.index as i64 == pool_input) { - let redeemer = redeemer.data.to_json(); - - // Extract input list from redemeer - let redeemer_map: Vec = redeemer["fields"][2]["list"] - .as_array() - .unwrap() - .iter() - .map(|r| r["int"].as_i64().unwrap() as usize) - .collect(); - // Find main transaction - let parent = redeemer["fields"][0]["int"].as_i64().unwrap() as usize; - // Restore inputs - let inputs: Vec = tx - .inputs() - .iter() - .map(|i| { - let output = &multiera_used_inputs_to_outputs_map[&i.hash().to_vec()] - [&(i.index() as i64)]; - MultiEraOutput::decode(output.era, &output.model.payload).unwrap() - }) - .collect::>(); - // Zip outputs with redemeer index - for (output, redeemer) in tx.outputs().iter().skip(1).zip(redeemer_map) { - // pair input with output - let input = inputs[redeemer].clone(); - - // get information about swap from pool plutus data - let parent_datum = get_plutus_datum_for_output(&inputs[parent], &tx.plutus_data()) - .unwrap() - .to_json(); - let parse_asset_item = |i, j| { - let item = parent_datum["fields"][1]["fields"][0]["fields"][i]["fields"][j] - ["bytes"] - .as_str() - .unwrap() - .to_string(); - hex::decode(item).unwrap() - }; - let asset1 = build_asset(parse_asset_item(0, 0), parse_asset_item(0, 1)); - let asset2 = build_asset(parse_asset_item(1, 0), parse_asset_item(1, 1)); - - // get actual plutus datum - let input_datum = get_plutus_datum_for_output(&input, &tx.plutus_data()) - .unwrap() - .to_json(); - // identify operation: 0 = swap - let operation = input_datum["fields"][1]["constructor"].as_i64().unwrap(); - if operation != 0 { - tracing::debug!("Operation is not a swap"); - continue; - } - let direction = input_datum["fields"][1]["fields"][0]["constructor"] - .as_i64() - .unwrap(); - - let amount1; - let amount2; - if direction == 0 { - amount1 = get_asset_amount(&input, &asset1) - - reduce_ada_amount(&asset1, WR_V1_SWAP_IN_ADA); - amount2 = get_asset_amount(&output, &asset2) - - reduce_ada_amount(&asset2, WR_V1_SWAP_OUT_ADA); - } else { - amount1 = get_asset_amount(&output, &asset1) - - reduce_ada_amount(&asset1, WR_V1_SWAP_OUT_ADA); - amount2 = get_asset_amount(&input, &asset2) - - reduce_ada_amount(&asset2, WR_V1_SWAP_IN_ADA); - } - queued_swaps.push(QueuedSwap { - tx_id, - address: pool_output.address().unwrap().to_vec(), - asset1, - asset2, - amount1, - amount2, - direction: if direction == 0 { - DexSwapDirection::SellAsset1 - } else { - DexSwapDirection::BuyAsset1 - }, - }) - } - } else { - tracing::info!("Redeemer not found"); - } - } -} diff --git a/indexer/tasks/src/multiera/utils/mod.rs b/indexer/tasks/src/multiera/utils/mod.rs index 1b3cc364..76cbd331 100644 --- a/indexer/tasks/src/multiera/utils/mod.rs +++ b/indexer/tasks/src/multiera/utils/mod.rs @@ -1,4 +1,3 @@ pub mod cip25_parse; pub mod common; -pub mod dex; pub mod user_asset; From 3d1b798004203b9fa04d604391cbd25720259270 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Mon, 14 Nov 2022 16:25:49 +0100 Subject: [PATCH 22/41] Indent tasks consistently --- .../multiera_minswap_v1_mean_price.rs | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs index 8dc90e18..5bb3c973 100644 --- a/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs +++ b/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs @@ -4,23 +4,23 @@ use crate::config::EmptyConfig::EmptyConfig; use crate::dsl::task_macro::*; carp_task! { - name MultieraMinSwapV1MeanPriceTask; - configuration EmptyConfig; - doc "Adds Minswap V1 mean price updates to the database"; - era multiera; - dependencies [MultieraAddressTask]; - read [multiera_txs, multiera_addresses]; - write []; - should_add_task |block, _properties| { - block.1.txs().iter().any(|tx| tx.outputs().len() > 0) - }; - execute |previous_data, task| handle_mean_price( - task.db_tx, - task.block, - &previous_data.multiera_txs, - &previous_data.multiera_addresses, - PoolType::MinSwapV1, - ); - merge_result |previous_data, _result| { - }; + name MultieraMinSwapV1MeanPriceTask; + configuration EmptyConfig; + doc "Adds Minswap V1 mean price updates to the database"; + era multiera; + dependencies [MultieraAddressTask]; + read [multiera_txs, multiera_addresses]; + write []; + should_add_task |block, _properties| { + block.1.txs().iter().any(|tx| tx.outputs().len() > 0) + }; + execute |previous_data, task| handle_mean_price( + task.db_tx, + task.block, + &previous_data.multiera_txs, + &previous_data.multiera_addresses, + PoolType::MinSwapV1, + ); + merge_result |previous_data, _result| { + }; } From 384539dad0c1161e5d333d05b8be5507a219f934 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Mon, 14 Nov 2022 16:26:11 +0100 Subject: [PATCH 23/41] Add minswap and sundaeswap swap tasks --- .../src/multiera/multiera_minswap_v1_swap.rs | 27 +++++++++++++++++++ .../multiera/multiera_sundaeswap_v1_swap.rs | 27 +++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 indexer/tasks/src/multiera/multiera_minswap_v1_swap.rs create mode 100644 indexer/tasks/src/multiera/multiera_sundaeswap_v1_swap.rs diff --git a/indexer/tasks/src/multiera/multiera_minswap_v1_swap.rs b/indexer/tasks/src/multiera/multiera_minswap_v1_swap.rs new file mode 100644 index 00000000..82c8965e --- /dev/null +++ b/indexer/tasks/src/multiera/multiera_minswap_v1_swap.rs @@ -0,0 +1,27 @@ +use super::dex::common::{handle_swap, PoolType}; +use super::multiera_used_inputs::MultieraUsedInputTask; +use crate::config::EmptyConfig::EmptyConfig; +use crate::dsl::task_macro::*; + +carp_task! { + name MultieraMinSwapV1SwapTask; + configuration EmptyConfig; + doc "Adds Minswap V1 swaps to the database"; + era multiera; + dependencies [MultieraUsedInputTask]; + read [multiera_txs, multiera_addresses, multiera_used_inputs_to_outputs_map]; + write []; + should_add_task |block, _properties| { + block.1.txs().iter().any(|tx| tx.outputs().len() > 0) + }; + execute |previous_data, task| handle_swap( + task.db_tx, + task.block, + &previous_data.multiera_txs, + &previous_data.multiera_addresses, + &previous_data.multiera_used_inputs_to_outputs_map, + PoolType::MinSwapV1, + ); + merge_result |previous_data, _result| { + }; +} diff --git a/indexer/tasks/src/multiera/multiera_sundaeswap_v1_swap.rs b/indexer/tasks/src/multiera/multiera_sundaeswap_v1_swap.rs new file mode 100644 index 00000000..c8746609 --- /dev/null +++ b/indexer/tasks/src/multiera/multiera_sundaeswap_v1_swap.rs @@ -0,0 +1,27 @@ +use super::dex::common::{handle_swap, PoolType}; +use super::multiera_used_inputs::MultieraUsedInputTask; +use crate::config::EmptyConfig::EmptyConfig; +use crate::dsl::task_macro::*; + +carp_task! { + name MultieraSundaeSwapV1SwapTask; + configuration EmptyConfig; + doc "Adds SundaeSwap V1 swaps to the database"; + era multiera; + dependencies [MultieraUsedInputTask]; + read [multiera_txs, multiera_addresses, multiera_used_inputs_to_outputs_map]; + write []; + should_add_task |block, _properties| { + block.1.txs().iter().any(|tx| tx.outputs().len() > 0) + }; + execute |previous_data, task| handle_swap( + task.db_tx, + task.block, + &previous_data.multiera_txs, + &previous_data.multiera_addresses, + &previous_data.multiera_used_inputs_to_outputs_map, + PoolType::SundaeSwapV1, + ); + merge_result |previous_data, _result| { + }; +} From c277ca6d2ef712fd40d71d3414f0ac52150e49c5 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Mon, 14 Nov 2022 16:32:27 +0100 Subject: [PATCH 24/41] Move dex constants to implementation files --- indexer/tasks/src/multiera/dex/minswap_v1.rs | 8 ++++-- .../tasks/src/multiera/dex/sundaeswap_v1.rs | 8 +++--- .../tasks/src/multiera/dex/wingriders_v1.rs | 26 ++++++++++--------- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/indexer/tasks/src/multiera/dex/minswap_v1.rs b/indexer/tasks/src/multiera/dex/minswap_v1.rs index 223b9a21..6b4beb03 100644 --- a/indexer/tasks/src/multiera/dex/minswap_v1.rs +++ b/indexer/tasks/src/multiera/dex/minswap_v1.rs @@ -6,9 +6,13 @@ use crate::{era_common::OutputWithTxData, multiera::utils::common::get_asset_amo use super::common::{ build_asset, get_pool_output_and_datum, Dex, MinSwapV1, QueuedMeanPrice, QueuedSwap, - MS_V1_POOL_SCRIPT_HASH1, MS_V1_POOL_SCRIPT_HASH2, }; +pub const POOL_SCRIPT_HASH1: &str = "e1317b152faac13426e6a83e06ff88a4d62cce3c1634ab0a5ec13309"; +pub const POOL_SCRIPT_HASH2: &str = "57c8e718c201fba10a9da1748d675b54281d3b1b983c5d1687fc7317"; +pub const SWAP_IN_ADA: u64 = 4_000_000; // oil ADA + agent fee +pub const SWAP_OUT_ADA: u64 = 2_000_000; // oil ADA + impl Dex for MinSwapV1 { fn queue_mean_price( &self, @@ -17,7 +21,7 @@ impl Dex for MinSwapV1 { tx_id: i64, ) { if let Some((output, datum)) = - get_pool_output_and_datum(tx, &vec![MS_V1_POOL_SCRIPT_HASH1, MS_V1_POOL_SCRIPT_HASH2]) + get_pool_output_and_datum(tx, &vec![POOL_SCRIPT_HASH1, POOL_SCRIPT_HASH2]) { let datum = datum.to_json(); diff --git a/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs b/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs index d84724d3..7ea0e754 100644 --- a/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs +++ b/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs @@ -6,9 +6,12 @@ use crate::{era_common::OutputWithTxData, multiera::utils::common::get_asset_amo use super::common::{ build_asset, get_pool_output_and_datum, Dex, QueuedMeanPrice, QueuedSwap, SundaeSwapV1, - SS_V1_POOL_SCRIPT_HASH, }; +pub const POOL_SCRIPT_HASH: &str = "4020e7fc2de75a0729c3cc3af715b34d98381e0cdbcfa99c950bc3ac"; +pub const SWAP_IN_ADA: u64 = 4_500_000; // oil ADA + agent fee +pub const SWAP_OUT_ADA: u64 = 2_000_000; // oil ADA + impl Dex for SundaeSwapV1 { fn queue_mean_price( &self, @@ -16,8 +19,7 @@ impl Dex for SundaeSwapV1 { tx: &MultiEraTx, tx_id: i64, ) { - if let Some((output, datum)) = get_pool_output_and_datum(tx, &vec![SS_V1_POOL_SCRIPT_HASH]) - { + if let Some((output, datum)) = get_pool_output_and_datum(tx, &vec![POOL_SCRIPT_HASH]) { let datum = datum.to_json(); let get_asset_item = |i, j| { diff --git a/indexer/tasks/src/multiera/dex/wingriders_v1.rs b/indexer/tasks/src/multiera/dex/wingriders_v1.rs index 378ac11f..4f352497 100644 --- a/indexer/tasks/src/multiera/dex/wingriders_v1.rs +++ b/indexer/tasks/src/multiera/dex/wingriders_v1.rs @@ -13,10 +13,14 @@ use crate::{ use super::common::{ build_asset, get_pool_output_and_datum, reduce_ada_amount, Dex, QueuedMeanPrice, QueuedSwap, - WingRidersV1, WR_V1_POOL_FIXED_ADA, WR_V1_POOL_SCRIPT_HASH, WR_V1_SWAP_IN_ADA, - WR_V1_SWAP_OUT_ADA, + WingRidersV1, }; +const POOL_SCRIPT_HASH: &str = "e6c90a5923713af5786963dee0fdffd830ca7e0c86a041d9e5833e91"; +const POOL_FIXED_ADA: u64 = 3_000_000; // every pool UTXO holds this amount of ADA +const SWAP_IN_ADA: u64 = 4_000_000; // oil ADA + agent fee +const SWAP_OUT_ADA: u64 = 2_000_000; // oil ADA + impl Dex for WingRidersV1 { fn queue_mean_price( &self, @@ -24,8 +28,7 @@ impl Dex for WingRidersV1 { tx: &MultiEraTx, tx_id: i64, ) { - if let Some((output, datum)) = get_pool_output_and_datum(tx, &vec![WR_V1_POOL_SCRIPT_HASH]) - { + if let Some((output, datum)) = get_pool_output_and_datum(tx, &vec![POOL_SCRIPT_HASH]) { let datum = datum.to_json(); let treasury1 = datum["fields"][1]["fields"][2]["int"].as_u64().unwrap(); @@ -44,10 +47,10 @@ impl Dex for WingRidersV1 { let amount1 = get_asset_amount(&output, &asset1) - treasury1 - - reduce_ada_amount(&asset1, WR_V1_POOL_FIXED_ADA); + - reduce_ada_amount(&asset1, POOL_FIXED_ADA); let amount2 = get_asset_amount(&output, &asset2) - treasury2 - - reduce_ada_amount(&asset2, WR_V1_POOL_FIXED_ADA); + - reduce_ada_amount(&asset2, POOL_FIXED_ADA); queued_prices.push(QueuedMeanPrice { tx_id, @@ -67,8 +70,7 @@ impl Dex for WingRidersV1 { tx_id: i64, multiera_used_inputs_to_outputs_map: &BTreeMap, BTreeMap>, ) { - if let Some((pool_output, _)) = get_pool_output_and_datum(tx, &vec![WR_V1_POOL_SCRIPT_HASH]) - { + if let Some((pool_output, _)) = get_pool_output_and_datum(tx, &vec![POOL_SCRIPT_HASH]) { if tx.redeemers().is_none() || tx.redeemers().unwrap().len() == 0 { return; } @@ -143,14 +145,14 @@ impl Dex for WingRidersV1 { let amount2; if direction == 0 { amount1 = get_asset_amount(&input, &asset1) - - reduce_ada_amount(&asset1, WR_V1_SWAP_IN_ADA); + - reduce_ada_amount(&asset1, SWAP_IN_ADA); amount2 = get_asset_amount(&output, &asset2) - - reduce_ada_amount(&asset2, WR_V1_SWAP_OUT_ADA); + - reduce_ada_amount(&asset2, SWAP_OUT_ADA); } else { amount1 = get_asset_amount(&output, &asset1) - - reduce_ada_amount(&asset1, WR_V1_SWAP_OUT_ADA); + - reduce_ada_amount(&asset1, SWAP_OUT_ADA); amount2 = get_asset_amount(&input, &asset2) - - reduce_ada_amount(&asset2, WR_V1_SWAP_IN_ADA); + - reduce_ada_amount(&asset2, SWAP_IN_ADA); } queued_swaps.push(QueuedSwap { tx_id, From 6cd9c84ab81e9bac8d144dc15c76e32d4045a2cf Mon Sep 17 00:00:00 2001 From: David Misiak Date: Tue, 15 Nov 2022 23:11:04 +0100 Subject: [PATCH 25/41] Implement minswap and sundaeswap swap tasks --- indexer/tasks/src/multiera/dex/common.rs | 74 +++++++--- indexer/tasks/src/multiera/dex/minswap_v1.rs | 139 ++++++++++++++++-- .../tasks/src/multiera/dex/sundaeswap_v1.rs | 134 +++++++++++++++-- .../tasks/src/multiera/dex/wingriders_v1.rs | 22 ++- indexer/tasks/src/multiera/mod.rs | 2 + 5 files changed, 325 insertions(+), 46 deletions(-) diff --git a/indexer/tasks/src/multiera/dex/common.rs b/indexer/tasks/src/multiera/dex/common.rs index cb3da8cc..a55bba57 100644 --- a/indexer/tasks/src/multiera/dex/common.rs +++ b/indexer/tasks/src/multiera/dex/common.rs @@ -6,9 +6,12 @@ use crate::{ types::DexSwapDirection, }; use entity::sea_orm::{DatabaseTransaction, Set}; -use pallas::ledger::{ - primitives::alonzo, - traverse::{MultiEraBlock, MultiEraOutput, MultiEraTx}, +use pallas::{ + codec::utils::KeepRaw, + ledger::{ + primitives::alonzo, + traverse::{MultiEraBlock, MultiEraOutput, MultiEraTx}, + }, }; use std::collections::{BTreeMap, BTreeSet}; @@ -25,27 +28,52 @@ pub const SS_V1_POOL_SCRIPT_HASH: &str = "4020e7fc2de75a0729c3cc3af715b34d98381e pub const WR_V1_SWAP_IN_ADA: u64 = 4_000_000; // oil ADA + agent fee pub const WR_V1_SWAP_OUT_ADA: u64 = 2_000_000; // oil ADA -pub fn get_pool_output_and_datum<'b>( - tx: &'b MultiEraTx, - pool_hashes: &[&str], -) -> Option<(MultiEraOutput<'b>, alonzo::PlutusData)> { - let pool_hashes = pool_hashes.iter().map(|&s| Some(s)).collect::>(); - // Note: there should be at most one pool output - if let Some(output) = tx - .outputs() +/// Returns an output and it's datum only if the output's payment hash is in `payment_hashes` +/// and the plutus datum is known. +pub fn filter_outputs_and_datums_by_hash<'b>( + outputs: &[MultiEraOutput<'b>], + payment_hashes: &[&str], + plutus_data: &Vec<&KeepRaw>, +) -> Vec<(MultiEraOutput<'b>, alonzo::PlutusData)> { + let payment_hashes = payment_hashes.iter().map(|&s| Some(s)).collect::>(); + outputs .iter() - .find(|o| pool_hashes.contains(&get_sheley_payment_hash(o.address()).as_deref())) - { - // Remark: The datum that corresponds to the pool output's datum hash should be present - // in tx.plutus_data() - if let Some(datum) = get_plutus_datum_for_output(output, &tx.plutus_data()) { - Some((output.clone(), datum)) - } else { - None - } - } else { - None - } + .filter_map(|o| { + if payment_hashes.contains(&get_sheley_payment_hash(o.address()).as_deref()) { + if let Some(datum) = get_plutus_datum_for_output(&o, plutus_data) { + Some((o.clone(), datum)) + } else { + None + } + } else { + None + } + }) + .collect::>() +} + +/// Returns an output and it's datum only if the output's address is in `addresses` +/// and the plutus datum is known. +pub fn filter_outputs_and_datums_by_address<'b>( + outputs: &[MultiEraOutput<'b>], + addresses: &[&str], + plutus_data: &Vec<&KeepRaw>, +) -> Vec<(MultiEraOutput<'b>, alonzo::PlutusData)> { + let addresses = addresses.iter().map(|&s| Some(s)).collect::>(); + outputs + .iter() + .filter_map(|o| { + if addresses.contains(&o.address().ok().and_then(|a| a.to_bech32().ok()).as_deref()) { + if let Some(datum) = get_plutus_datum_for_output(&o, plutus_data) { + Some((o.clone(), datum)) + } else { + None + } + } else { + None + } + }) + .collect::>() } pub struct QueuedMeanPrice { diff --git a/indexer/tasks/src/multiera/dex/minswap_v1.rs b/indexer/tasks/src/multiera/dex/minswap_v1.rs index 6b4beb03..d7251840 100644 --- a/indexer/tasks/src/multiera/dex/minswap_v1.rs +++ b/indexer/tasks/src/multiera/dex/minswap_v1.rs @@ -1,15 +1,25 @@ use std::collections::BTreeMap; -use pallas::ledger::{primitives::ToCanonicalJson, traverse::MultiEraTx}; +use pallas::ledger::{ + addresses::Address, + primitives::ToCanonicalJson, + traverse::{MultiEraOutput, MultiEraTx}, +}; -use crate::{era_common::OutputWithTxData, multiera::utils::common::get_asset_amount}; +use crate::{ + era_common::OutputWithTxData, multiera::utils::common::get_asset_amount, + types::DexSwapDirection, +}; use super::common::{ - build_asset, get_pool_output_and_datum, Dex, MinSwapV1, QueuedMeanPrice, QueuedSwap, + build_asset, filter_outputs_and_datums_by_address, filter_outputs_and_datums_by_hash, + reduce_ada_amount, Dex, MinSwapV1, QueuedMeanPrice, QueuedSwap, }; pub const POOL_SCRIPT_HASH1: &str = "e1317b152faac13426e6a83e06ff88a4d62cce3c1634ab0a5ec13309"; pub const POOL_SCRIPT_HASH2: &str = "57c8e718c201fba10a9da1748d675b54281d3b1b983c5d1687fc7317"; +pub const BATCH_ORDER_ADDRESS1: &str = "addr1wyx22z2s4kasd3w976pnjf9xdty88epjqfvgkmfnscpd0rg3z8y6v"; +pub const BATCH_ORDER_ADDRESS2: &str = "addr1wxn9efv2f6w82hagxqtn62ju4m293tqvw0uhmdl64ch8uwc0h43gt"; pub const SWAP_IN_ADA: u64 = 4_000_000; // oil ADA + agent fee pub const SWAP_OUT_ADA: u64 = 2_000_000; // oil ADA @@ -20,8 +30,13 @@ impl Dex for MinSwapV1 { tx: &MultiEraTx, tx_id: i64, ) { - if let Some((output, datum)) = - get_pool_output_and_datum(tx, &vec![POOL_SCRIPT_HASH1, POOL_SCRIPT_HASH2]) + // Note: there should be at most one pool output + if let Some((output, datum)) = filter_outputs_and_datums_by_hash( + &tx.outputs(), + &vec![POOL_SCRIPT_HASH1, POOL_SCRIPT_HASH2], + &tx.plutus_data(), + ) + .get(0) { let datum = datum.to_json(); @@ -52,11 +67,115 @@ impl Dex for MinSwapV1 { fn queue_swap( &self, - _queued_swaps: &mut Vec, - _tx: &MultiEraTx, - _tx_id: i64, - _multiera_used_inputs_to_outputs_map: &BTreeMap, BTreeMap>, + queued_swaps: &mut Vec, + tx: &MultiEraTx, + tx_id: i64, + multiera_used_inputs_to_outputs_map: &BTreeMap, BTreeMap>, ) { - unimplemented!() + // Note: there should be at most one pool output + if let Some((main_output, main_datum)) = filter_outputs_and_datums_by_hash( + &tx.outputs(), + &vec![POOL_SCRIPT_HASH1, POOL_SCRIPT_HASH2], + &tx.plutus_data(), + ) + .get(0) + { + let main_datum = main_datum.to_json(); + let mut free_utxos: Vec = tx.outputs(); + + // Extract asset information from plutus data of pool input + let parse_asset_item = |i, j| { + let item = main_datum["fields"][i]["fields"][j]["bytes"] + .as_str() + .unwrap() + .to_string(); + hex::decode(item).unwrap() + }; + let asset1 = build_asset(parse_asset_item(0, 0), parse_asset_item(0, 1)); + let asset2 = build_asset(parse_asset_item(1, 0), parse_asset_item(1, 1)); + + let inputs: Vec = tx + .inputs() + .iter() + .map(|i| { + let output = &multiera_used_inputs_to_outputs_map[&i.hash().to_vec()] + [&(i.index() as i64)]; + MultiEraOutput::decode(output.era, &output.model.payload).unwrap() + }) + .collect::>(); + for (input, input_datum) in filter_outputs_and_datums_by_address( + &inputs, + &vec![BATCH_ORDER_ADDRESS1, BATCH_ORDER_ADDRESS2], + &tx.plutus_data(), + ) { + let input_datum = input_datum.to_json(); + + // identify operation: 0 = swap + let operation = input_datum["fields"][3]["constructor"].as_i64().unwrap(); + if operation != 0 { + tracing::debug!("Operation is not a swap"); + continue; + } + + let parse_asset_item = |i, j| { + let item = input_datum["fields"][3]["fields"][i]["fields"][j]["bytes"] + .as_str() + .unwrap() + .to_string(); + hex::decode(item).unwrap() + }; + let target_asset = build_asset(parse_asset_item(0, 0), parse_asset_item(0, 1)); + + // Get transaction output + let output_address_items = vec![ + String::from("01"), // mainnet + input_datum["fields"][1]["fields"][0]["fields"][0]["bytes"] + .as_str() + .unwrap() + .to_string(), + input_datum["fields"][1]["fields"][1]["fields"][0]["fields"][0]["fields"][0] + ["bytes"] + .as_str() + .unwrap() + .to_string(), + ]; + let output_address = Address::from_hex(&output_address_items.join("")).unwrap(); + + // Get coresponding UTxO with result + let utxo_pos = free_utxos + .iter() + .position(|o| o.address().ok() == Some(output_address.clone())) + .unwrap(); + let utxo = free_utxos[utxo_pos].clone(); + free_utxos.remove(utxo_pos); + + // Get amount and direction + let amount1; + let amount2; + let direction; + if target_asset == asset2 { + amount1 = + get_asset_amount(&input, &asset1) - reduce_ada_amount(&asset1, SWAP_IN_ADA); + amount2 = + get_asset_amount(&utxo, &asset2) - reduce_ada_amount(&asset2, SWAP_OUT_ADA); + direction = DexSwapDirection::BuyAsset1; + } else { + amount1 = + get_asset_amount(&utxo, &asset1) - reduce_ada_amount(&asset1, SWAP_OUT_ADA); + amount2 = + get_asset_amount(&input, &asset2) - reduce_ada_amount(&asset2, SWAP_IN_ADA); + direction = DexSwapDirection::SellAsset1; + } + queued_swaps.push(QueuedSwap { + tx_id, + address: main_output.address().unwrap().to_vec(), + asset1: asset1.clone(), + asset2: asset2.clone(), + amount1, + amount2, + direction, + }) + } + } } } diff --git a/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs b/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs index 7ea0e754..fe457fb9 100644 --- a/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs +++ b/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs @@ -1,14 +1,23 @@ use std::collections::BTreeMap; -use pallas::ledger::{primitives::ToCanonicalJson, traverse::MultiEraTx}; +use pallas::ledger::{ + addresses::Address, + primitives::ToCanonicalJson, + traverse::{MultiEraOutput, MultiEraTx}, +}; -use crate::{era_common::OutputWithTxData, multiera::utils::common::get_asset_amount}; +use crate::{ + era_common::OutputWithTxData, multiera::utils::common::get_asset_amount, + types::DexSwapDirection, +}; use super::common::{ - build_asset, get_pool_output_and_datum, Dex, QueuedMeanPrice, QueuedSwap, SundaeSwapV1, + build_asset, filter_outputs_and_datums_by_address, filter_outputs_and_datums_by_hash, + reduce_ada_amount, Dex, QueuedMeanPrice, QueuedSwap, SundaeSwapV1, }; pub const POOL_SCRIPT_HASH: &str = "4020e7fc2de75a0729c3cc3af715b34d98381e0cdbcfa99c950bc3ac"; +pub const REQUEST_SCRIPT_HASH: &str = "ba158766c1bae60e2117ee8987621441fac66a5e0fb9c7aca58cf20a"; pub const SWAP_IN_ADA: u64 = 4_500_000; // oil ADA + agent fee pub const SWAP_OUT_ADA: u64 = 2_000_000; // oil ADA @@ -19,7 +28,14 @@ impl Dex for SundaeSwapV1 { tx: &MultiEraTx, tx_id: i64, ) { - if let Some((output, datum)) = get_pool_output_and_datum(tx, &vec![POOL_SCRIPT_HASH]) { + // Note: there should be at most one pool output + if let Some((output, datum)) = filter_outputs_and_datums_by_hash( + &tx.outputs(), + &vec![POOL_SCRIPT_HASH], + &tx.plutus_data(), + ) + .get(0) + { let datum = datum.to_json(); let get_asset_item = |i, j| { @@ -49,11 +65,111 @@ impl Dex for SundaeSwapV1 { fn queue_swap( &self, - _queued_swaps: &mut Vec, - _tx: &MultiEraTx, - _tx_id: i64, - _multiera_used_inputs_to_outputs_map: &BTreeMap, BTreeMap>, + queued_swaps: &mut Vec, + tx: &MultiEraTx, + tx_id: i64, + multiera_used_inputs_to_outputs_map: &BTreeMap, BTreeMap>, ) { - unimplemented!() + // Note: there should be at most one pool output + if let Some((main_output, main_datum)) = filter_outputs_and_datums_by_hash( + &tx.outputs(), + &vec![POOL_SCRIPT_HASH], + &tx.plutus_data(), + ) + .get(0) + { + let main_datum = main_datum.to_json(); + let mut free_utxos: Vec = tx.outputs(); + + // Extract asset information from plutus data of pool input + let parse_asset_item = |i, j| { + let item = main_datum["fields"][0]["fields"][i]["fields"][j]["bytes"] + .as_str() + .unwrap() + .to_string(); + hex::decode(item).unwrap() + }; + let asset1 = build_asset(parse_asset_item(0, 0), parse_asset_item(0, 1)); + let asset2 = build_asset(parse_asset_item(1, 0), parse_asset_item(1, 1)); + + let inputs: Vec = tx + .inputs() + .iter() + .map(|i| { + let output = &multiera_used_inputs_to_outputs_map[&i.hash().to_vec()] + [&(i.index() as i64)]; + MultiEraOutput::decode(output.era, &output.model.payload).unwrap() + }) + .collect::>(); + for (input, input_datum) in filter_outputs_and_datums_by_hash( + &inputs, + &vec![REQUEST_SCRIPT_HASH], + &tx.plutus_data(), + ) { + let input_datum = input_datum.to_json(); + + // identify operation: 0 = swap + let operation = input_datum["fields"][3]["constructor"].as_i64().unwrap(); + if operation != 0 { + tracing::debug!("Operation is not a swap"); + continue; + } + + // Get transaction output + let output_address_items = vec![ + String::from("01"), // mainnet + input_datum["fields"][1]["fields"][0]["fields"][0]["fields"][0]["fields"][0] + ["bytes"] + .as_str() + .unwrap() + .to_string(), + input_datum["fields"][1]["fields"][0]["fields"][0]["fields"][1]["fields"][0] + ["fields"][0]["fields"][0]["bytes"] + .as_str() + .unwrap() + .to_string(), + ]; + let output_address = Address::from_hex(&output_address_items.join("")).unwrap(); + + // Get coresponding UTxO with result + let utxo_pos = free_utxos + .iter() + .position(|o| o.address().ok() == Some(output_address.clone())) + .unwrap(); + let utxo = free_utxos[utxo_pos].clone(); + free_utxos.remove(utxo_pos); + + // Get amount and direction + let amount1; + let amount2; + let direction = input_datum["fields"][3]["fields"][0]["constructor"] + .as_i64() + .unwrap(); + if direction == 0 { + amount1 = + get_asset_amount(&input, &asset1) - reduce_ada_amount(&asset1, SWAP_IN_ADA); + amount2 = + get_asset_amount(&utxo, &asset2) - reduce_ada_amount(&asset2, SWAP_OUT_ADA); + } else { + amount1 = + get_asset_amount(&utxo, &asset1) - reduce_ada_amount(&asset1, SWAP_OUT_ADA); + amount2 = + get_asset_amount(&input, &asset2) - reduce_ada_amount(&asset2, SWAP_IN_ADA); + } + queued_swaps.push(QueuedSwap { + tx_id, + address: main_output.address().unwrap().to_vec(), + asset1: asset1.clone(), + asset2: asset2.clone(), + amount1, + amount2, + direction: if direction == 0 { + DexSwapDirection::BuyAsset1 + } else { + DexSwapDirection::SellAsset1 + }, + }) + } + } } } diff --git a/indexer/tasks/src/multiera/dex/wingriders_v1.rs b/indexer/tasks/src/multiera/dex/wingriders_v1.rs index 4f352497..539aa2cf 100644 --- a/indexer/tasks/src/multiera/dex/wingriders_v1.rs +++ b/indexer/tasks/src/multiera/dex/wingriders_v1.rs @@ -12,8 +12,8 @@ use crate::{ }; use super::common::{ - build_asset, get_pool_output_and_datum, reduce_ada_amount, Dex, QueuedMeanPrice, QueuedSwap, - WingRidersV1, + build_asset, filter_outputs_and_datums_by_hash, reduce_ada_amount, Dex, QueuedMeanPrice, + QueuedSwap, WingRidersV1, }; const POOL_SCRIPT_HASH: &str = "e6c90a5923713af5786963dee0fdffd830ca7e0c86a041d9e5833e91"; @@ -28,7 +28,14 @@ impl Dex for WingRidersV1 { tx: &MultiEraTx, tx_id: i64, ) { - if let Some((output, datum)) = get_pool_output_and_datum(tx, &vec![POOL_SCRIPT_HASH]) { + // Note: there should be at most one pool output + if let Some((output, datum)) = filter_outputs_and_datums_by_hash( + &tx.outputs(), + &vec![POOL_SCRIPT_HASH], + &tx.plutus_data(), + ) + .get(0) + { let datum = datum.to_json(); let treasury1 = datum["fields"][1]["fields"][2]["int"].as_u64().unwrap(); @@ -70,7 +77,14 @@ impl Dex for WingRidersV1 { tx_id: i64, multiera_used_inputs_to_outputs_map: &BTreeMap, BTreeMap>, ) { - if let Some((pool_output, _)) = get_pool_output_and_datum(tx, &vec![POOL_SCRIPT_HASH]) { + // Note: there should be at most one pool output + if let Some((pool_output, _)) = filter_outputs_and_datums_by_hash( + &tx.outputs(), + &vec![POOL_SCRIPT_HASH], + &tx.plutus_data(), + ) + .get(0) + { if tx.redeemers().is_none() || tx.redeemers().unwrap().len() == 0 { return; } diff --git a/indexer/tasks/src/multiera/mod.rs b/indexer/tasks/src/multiera/mod.rs index 669069ab..0ccf716b 100644 --- a/indexer/tasks/src/multiera/mod.rs +++ b/indexer/tasks/src/multiera/mod.rs @@ -8,9 +8,11 @@ pub mod multiera_datum; pub mod multiera_executor; pub mod multiera_metadata; pub mod multiera_minswap_v1_mean_price; +pub mod multiera_minswap_v1_swap; pub mod multiera_reference_inputs; pub mod multiera_stake_credentials; pub mod multiera_sundaeswap_v1_mean_price; +pub mod multiera_sundaeswap_v1_swap; pub mod multiera_tx_credential_relations; pub mod multiera_txs; pub mod multiera_unused_input; From 220c30ea34e6426a836042dd53f65e08b5e5e20a Mon Sep 17 00:00:00 2001 From: David Misiak Date: Tue, 15 Nov 2022 23:13:15 +0100 Subject: [PATCH 26/41] Use consistent helper function name --- indexer/tasks/src/multiera/dex/minswap_v1.rs | 7 +++---- indexer/tasks/src/multiera/dex/sundaeswap_v1.rs | 7 +++---- indexer/tasks/src/multiera/dex/wingriders_v1.rs | 7 +++---- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/indexer/tasks/src/multiera/dex/minswap_v1.rs b/indexer/tasks/src/multiera/dex/minswap_v1.rs index d7251840..e6e7dd81 100644 --- a/indexer/tasks/src/multiera/dex/minswap_v1.rs +++ b/indexer/tasks/src/multiera/dex/minswap_v1.rs @@ -40,16 +40,15 @@ impl Dex for MinSwapV1 { { let datum = datum.to_json(); - let get_asset_item = |i, j| { + let parse_asset_item = |i, j| { let item = datum["fields"][i]["fields"][j]["bytes"] .as_str() .unwrap() .to_string(); hex::decode(item).unwrap() }; - - let asset1 = build_asset(get_asset_item(0, 0), get_asset_item(0, 1)); - let asset2 = build_asset(get_asset_item(1, 0), get_asset_item(1, 1)); + let asset1 = build_asset(parse_asset_item(0, 0), parse_asset_item(0, 1)); + let asset2 = build_asset(parse_asset_item(1, 0), parse_asset_item(1, 1)); let amount1 = get_asset_amount(&output, &asset1); let amount2 = get_asset_amount(&output, &asset2); diff --git a/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs b/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs index fe457fb9..0f4ca65a 100644 --- a/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs +++ b/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs @@ -38,16 +38,15 @@ impl Dex for SundaeSwapV1 { { let datum = datum.to_json(); - let get_asset_item = |i, j| { + let parse_asset_item = |i, j| { let item = datum["fields"][0]["fields"][i]["fields"][j]["bytes"] .as_str() .unwrap() .to_string(); hex::decode(item).unwrap() }; - - let asset1 = build_asset(get_asset_item(0, 0), get_asset_item(0, 1)); - let asset2 = build_asset(get_asset_item(1, 0), get_asset_item(1, 1)); + let asset1 = build_asset(parse_asset_item(0, 0), parse_asset_item(0, 1)); + let asset2 = build_asset(parse_asset_item(1, 0), parse_asset_item(1, 1)); let amount1 = get_asset_amount(&output, &asset1); let amount2 = get_asset_amount(&output, &asset2); diff --git a/indexer/tasks/src/multiera/dex/wingriders_v1.rs b/indexer/tasks/src/multiera/dex/wingriders_v1.rs index 539aa2cf..4b8e6384 100644 --- a/indexer/tasks/src/multiera/dex/wingriders_v1.rs +++ b/indexer/tasks/src/multiera/dex/wingriders_v1.rs @@ -41,16 +41,15 @@ impl Dex for WingRidersV1 { let treasury1 = datum["fields"][1]["fields"][2]["int"].as_u64().unwrap(); let treasury2 = datum["fields"][1]["fields"][3]["int"].as_u64().unwrap(); - let get_asset_item = |i, j| { + let parse_asset_item = |i, j| { let item = datum["fields"][1]["fields"][0]["fields"][i]["fields"][j]["bytes"] .as_str() .unwrap() .to_string(); hex::decode(item).unwrap() }; - - let asset1 = build_asset(get_asset_item(0, 0), get_asset_item(0, 1)); - let asset2 = build_asset(get_asset_item(1, 0), get_asset_item(1, 1)); + let asset1 = build_asset(parse_asset_item(0, 0), parse_asset_item(0, 1)); + let asset2 = build_asset(parse_asset_item(1, 0), parse_asset_item(1, 1)); let amount1 = get_asset_amount(&output, &asset1) - treasury1 From 397b43f956596e5fca8957068c1865e1c3266e72 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Tue, 15 Nov 2022 23:13:46 +0100 Subject: [PATCH 27/41] Add all dex tasks to dex_prices.toml execution plan --- indexer/execution_plans/dex_prices.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/indexer/execution_plans/dex_prices.toml b/indexer/execution_plans/dex_prices.toml index fda7e985..88b3b3fc 100644 --- a/indexer/execution_plans/dex_prices.toml +++ b/indexer/execution_plans/dex_prices.toml @@ -45,6 +45,11 @@ readonly=false [MultieraMinSwapV1MeanPriceTask] +[MultieraMinSwapV1SwapTask] + [MultieraSundaeSwapV1MeanPriceTask] [MultieraWingRidersV1SwapTask] + +[MultieraSundaeSwapV1SwapTask] + From afc16e7e36238e4a1dff9ace2cfec005a012c660 Mon Sep 17 00:00:00 2001 From: David Misiak Date: Wed, 16 Nov 2022 18:56:27 +0100 Subject: [PATCH 28/41] Unwrap swap data carefully --- indexer/tasks/src/multiera/dex/common.rs | 30 ++- indexer/tasks/src/multiera/dex/minswap_v1.rs | 47 ++-- .../tasks/src/multiera/dex/sundaeswap_v1.rs | 41 ++-- .../tasks/src/multiera/dex/wingriders_v1.rs | 209 +++++++++--------- 4 files changed, 180 insertions(+), 147 deletions(-) diff --git a/indexer/tasks/src/multiera/dex/common.rs b/indexer/tasks/src/multiera/dex/common.rs index a55bba57..2053c4cb 100644 --- a/indexer/tasks/src/multiera/dex/common.rs +++ b/indexer/tasks/src/multiera/dex/common.rs @@ -101,7 +101,7 @@ pub trait Dex { queued_prices: &mut Vec, tx: &MultiEraTx, tx_id: i64, - ); + ) -> Result<(), String>; fn queue_swap( &self, @@ -109,7 +109,7 @@ pub trait Dex { tx: &MultiEraTx, tx_id: i64, multiera_used_inputs_to_outputs_map: &BTreeMap, BTreeMap>, - ); + ) -> Result<(), String>; } #[derive(Debug, PartialEq, Eq)] @@ -127,7 +127,7 @@ impl Dex for Empty { _queued_prices: &mut Vec, _tx: &MultiEraTx, _tx_id: i64, - ) { + ) -> Result<(), String> { unimplemented!(); } @@ -137,7 +137,7 @@ impl Dex for Empty { _tx: &MultiEraTx, _tx_id: i64, _multiera_used_inputs_to_outputs_map: &BTreeMap, BTreeMap>, - ) { + ) -> Result<(), String> { unimplemented!(); } } @@ -178,7 +178,18 @@ pub async fn handle_mean_price( let mut queued_prices = Vec::::default(); for (tx_body, cardano_transaction) in block.1.txs().iter().zip(multiera_txs) { if cardano_transaction.is_valid { - mean_value_trait.queue_mean_price(&mut queued_prices, tx_body, cardano_transaction.id); + let result = mean_value_trait.queue_mean_price( + &mut queued_prices, + tx_body, + cardano_transaction.id, + ); + if result.is_err() { + tracing::warn!( + "Failed to parse mean price for tx {}: {}", + cardano_transaction.id, + result.err().unwrap(), + ); + } } } @@ -259,12 +270,19 @@ pub async fn handle_swap( let mut queued_swaps = Vec::::default(); for (tx_body, cardano_transaction) in block.1.txs().iter().zip(multiera_txs) { if cardano_transaction.is_valid { - swap_trait.queue_swap( + let result = swap_trait.queue_swap( &mut queued_swaps, tx_body, cardano_transaction.id, multiera_used_inputs_to_outputs_map, ); + if result.is_err() { + tracing::warn!( + "Failed to parse swaps for tx {}: {}", + cardano_transaction.id, + result.err().unwrap() + ); + } } } diff --git a/indexer/tasks/src/multiera/dex/minswap_v1.rs b/indexer/tasks/src/multiera/dex/minswap_v1.rs index e6e7dd81..5ab43896 100644 --- a/indexer/tasks/src/multiera/dex/minswap_v1.rs +++ b/indexer/tasks/src/multiera/dex/minswap_v1.rs @@ -29,7 +29,7 @@ impl Dex for MinSwapV1 { queued_prices: &mut Vec, tx: &MultiEraTx, tx_id: i64, - ) { + ) -> Result<(), String> { // Note: there should be at most one pool output if let Some((output, datum)) = filter_outputs_and_datums_by_hash( &tx.outputs(), @@ -40,15 +40,15 @@ impl Dex for MinSwapV1 { { let datum = datum.to_json(); - let parse_asset_item = |i, j| { + let parse_asset_item = |i, j| -> Result, &str> { let item = datum["fields"][i]["fields"][j]["bytes"] .as_str() - .unwrap() + .ok_or("Failed to parse asset item")? .to_string(); - hex::decode(item).unwrap() + hex::decode(item).map_err(|_e| "Failed to parse asset item") }; - let asset1 = build_asset(parse_asset_item(0, 0), parse_asset_item(0, 1)); - let asset2 = build_asset(parse_asset_item(1, 0), parse_asset_item(1, 1)); + let asset1 = build_asset(parse_asset_item(0, 0)?, parse_asset_item(0, 1)?); + let asset2 = build_asset(parse_asset_item(1, 0)?, parse_asset_item(1, 1)?); let amount1 = get_asset_amount(&output, &asset1); let amount2 = get_asset_amount(&output, &asset2); @@ -62,6 +62,7 @@ impl Dex for MinSwapV1 { amount2, }); } + Ok(()) } fn queue_swap( @@ -70,7 +71,7 @@ impl Dex for MinSwapV1 { tx: &MultiEraTx, tx_id: i64, multiera_used_inputs_to_outputs_map: &BTreeMap, BTreeMap>, - ) { + ) -> Result<(), String> { // Note: there should be at most one pool output if let Some((main_output, main_datum)) = filter_outputs_and_datums_by_hash( &tx.outputs(), @@ -83,15 +84,15 @@ impl Dex for MinSwapV1 { let mut free_utxos: Vec = tx.outputs(); // Extract asset information from plutus data of pool input - let parse_asset_item = |i, j| { + let parse_asset_item = |i, j| -> Result, &str> { let item = main_datum["fields"][i]["fields"][j]["bytes"] .as_str() - .unwrap() + .ok_or("Failed to parse asset item")? .to_string(); - hex::decode(item).unwrap() + hex::decode(item).map_err(|_e| "Failed to parse asset item") }; - let asset1 = build_asset(parse_asset_item(0, 0), parse_asset_item(0, 1)); - let asset2 = build_asset(parse_asset_item(1, 0), parse_asset_item(1, 1)); + let asset1 = build_asset(parse_asset_item(0, 0)?, parse_asset_item(0, 1)?); + let asset2 = build_asset(parse_asset_item(1, 0)?, parse_asset_item(1, 1)?); let inputs: Vec = tx .inputs() @@ -110,41 +111,44 @@ impl Dex for MinSwapV1 { let input_datum = input_datum.to_json(); // identify operation: 0 = swap - let operation = input_datum["fields"][3]["constructor"].as_i64().unwrap(); + let operation = input_datum["fields"][3]["constructor"] + .as_i64() + .ok_or("Failed to parse operation")?; if operation != 0 { tracing::debug!("Operation is not a swap"); continue; } - let parse_asset_item = |i, j| { + let parse_asset_item = |i, j| -> Result, &str> { let item = input_datum["fields"][3]["fields"][i]["fields"][j]["bytes"] .as_str() - .unwrap() + .ok_or("Failed to parse asset item")? .to_string(); - hex::decode(item).unwrap() + hex::decode(item).map_err(|_e| "Failed to parse asset item") }; - let target_asset = build_asset(parse_asset_item(0, 0), parse_asset_item(0, 1)); + let target_asset = build_asset(parse_asset_item(0, 0)?, parse_asset_item(0, 1)?); // Get transaction output let output_address_items = vec![ String::from("01"), // mainnet input_datum["fields"][1]["fields"][0]["fields"][0]["bytes"] .as_str() - .unwrap() + .ok_or("Failed to parse output address item")? .to_string(), input_datum["fields"][1]["fields"][1]["fields"][0]["fields"][0]["fields"][0] ["bytes"] .as_str() - .unwrap() + .ok_or("Failed to parse output address item")? .to_string(), ]; - let output_address = Address::from_hex(&output_address_items.join("")).unwrap(); + let output_address = Address::from_hex(&output_address_items.join("")) + .map_err(|_e| "Failed to parse output address")?; // Get coresponding UTxO with result let utxo_pos = free_utxos .iter() .position(|o| o.address().ok() == Some(output_address.clone())) - .unwrap(); + .ok_or("Failed to find utxo")?; let utxo = free_utxos[utxo_pos].clone(); free_utxos.remove(utxo_pos); @@ -176,5 +180,6 @@ impl Dex for MinSwapV1 { }) } } + Ok(()) } } diff --git a/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs b/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs index 0f4ca65a..68ec326d 100644 --- a/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs +++ b/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs @@ -27,7 +27,7 @@ impl Dex for SundaeSwapV1 { queued_prices: &mut Vec, tx: &MultiEraTx, tx_id: i64, - ) { + ) -> Result<(), String> { // Note: there should be at most one pool output if let Some((output, datum)) = filter_outputs_and_datums_by_hash( &tx.outputs(), @@ -38,15 +38,15 @@ impl Dex for SundaeSwapV1 { { let datum = datum.to_json(); - let parse_asset_item = |i, j| { + let parse_asset_item = |i, j| -> Result, &str> { let item = datum["fields"][0]["fields"][i]["fields"][j]["bytes"] .as_str() - .unwrap() + .ok_or("Failed to parse asset item")? .to_string(); - hex::decode(item).unwrap() + hex::decode(item).map_err(|_e| "Failed to parse asset item") }; - let asset1 = build_asset(parse_asset_item(0, 0), parse_asset_item(0, 1)); - let asset2 = build_asset(parse_asset_item(1, 0), parse_asset_item(1, 1)); + let asset1 = build_asset(parse_asset_item(0, 0)?, parse_asset_item(0, 1)?); + let asset2 = build_asset(parse_asset_item(1, 0)?, parse_asset_item(1, 1)?); let amount1 = get_asset_amount(&output, &asset1); let amount2 = get_asset_amount(&output, &asset2); @@ -60,6 +60,7 @@ impl Dex for SundaeSwapV1 { amount2, }); } + Ok(()) } fn queue_swap( @@ -68,7 +69,7 @@ impl Dex for SundaeSwapV1 { tx: &MultiEraTx, tx_id: i64, multiera_used_inputs_to_outputs_map: &BTreeMap, BTreeMap>, - ) { + ) -> Result<(), String> { // Note: there should be at most one pool output if let Some((main_output, main_datum)) = filter_outputs_and_datums_by_hash( &tx.outputs(), @@ -81,15 +82,15 @@ impl Dex for SundaeSwapV1 { let mut free_utxos: Vec = tx.outputs(); // Extract asset information from plutus data of pool input - let parse_asset_item = |i, j| { + let parse_asset_item = |i, j| -> Result, &str> { let item = main_datum["fields"][0]["fields"][i]["fields"][j]["bytes"] .as_str() - .unwrap() + .ok_or("Failed to parse asset item")? .to_string(); - hex::decode(item).unwrap() + hex::decode(item).map_err(|_e| "Failed to parse asset item") }; - let asset1 = build_asset(parse_asset_item(0, 0), parse_asset_item(0, 1)); - let asset2 = build_asset(parse_asset_item(1, 0), parse_asset_item(1, 1)); + let asset1 = build_asset(parse_asset_item(0, 0)?, parse_asset_item(0, 1)?); + let asset2 = build_asset(parse_asset_item(1, 0)?, parse_asset_item(1, 1)?); let inputs: Vec = tx .inputs() @@ -108,7 +109,9 @@ impl Dex for SundaeSwapV1 { let input_datum = input_datum.to_json(); // identify operation: 0 = swap - let operation = input_datum["fields"][3]["constructor"].as_i64().unwrap(); + let operation = input_datum["fields"][3]["constructor"] + .as_i64() + .ok_or("Failed to parse operation")?; if operation != 0 { tracing::debug!("Operation is not a swap"); continue; @@ -120,21 +123,22 @@ impl Dex for SundaeSwapV1 { input_datum["fields"][1]["fields"][0]["fields"][0]["fields"][0]["fields"][0] ["bytes"] .as_str() - .unwrap() + .ok_or("Failed to parse output address item")? .to_string(), input_datum["fields"][1]["fields"][0]["fields"][0]["fields"][1]["fields"][0] ["fields"][0]["fields"][0]["bytes"] .as_str() - .unwrap() + .ok_or("Failed to parse output address item")? .to_string(), ]; - let output_address = Address::from_hex(&output_address_items.join("")).unwrap(); + let output_address = Address::from_hex(&output_address_items.join("")) + .map_err(|_e| "Failed to parse output address")?; // Get coresponding UTxO with result let utxo_pos = free_utxos .iter() .position(|o| o.address().ok() == Some(output_address.clone())) - .unwrap(); + .ok_or("Failed to find utxo")?; let utxo = free_utxos[utxo_pos].clone(); free_utxos.remove(utxo_pos); @@ -143,7 +147,7 @@ impl Dex for SundaeSwapV1 { let amount2; let direction = input_datum["fields"][3]["fields"][0]["constructor"] .as_i64() - .unwrap(); + .ok_or("Failed to parse direction")?; if direction == 0 { amount1 = get_asset_amount(&input, &asset1) - reduce_ada_amount(&asset1, SWAP_IN_ADA); @@ -170,5 +174,6 @@ impl Dex for SundaeSwapV1 { }) } } + Ok(()) } } diff --git a/indexer/tasks/src/multiera/dex/wingriders_v1.rs b/indexer/tasks/src/multiera/dex/wingriders_v1.rs index 4b8e6384..93645fb0 100644 --- a/indexer/tasks/src/multiera/dex/wingriders_v1.rs +++ b/indexer/tasks/src/multiera/dex/wingriders_v1.rs @@ -27,7 +27,7 @@ impl Dex for WingRidersV1 { queued_prices: &mut Vec, tx: &MultiEraTx, tx_id: i64, - ) { + ) -> Result<(), String> { // Note: there should be at most one pool output if let Some((output, datum)) = filter_outputs_and_datums_by_hash( &tx.outputs(), @@ -38,18 +38,22 @@ impl Dex for WingRidersV1 { { let datum = datum.to_json(); - let treasury1 = datum["fields"][1]["fields"][2]["int"].as_u64().unwrap(); - let treasury2 = datum["fields"][1]["fields"][3]["int"].as_u64().unwrap(); + let treasury1 = datum["fields"][1]["fields"][2]["int"] + .as_u64() + .ok_or("Failed to parse treasury1")?; + let treasury2 = datum["fields"][1]["fields"][3]["int"] + .as_u64() + .ok_or("Failed to parse treasury2")?; - let parse_asset_item = |i, j| { + let parse_asset_item = |i, j| -> Result, &str> { let item = datum["fields"][1]["fields"][0]["fields"][i]["fields"][j]["bytes"] .as_str() - .unwrap() + .ok_or("Failed to parse asset item")? .to_string(); - hex::decode(item).unwrap() + hex::decode(item).map_err(|_e| "Failed to parse asset item") }; - let asset1 = build_asset(parse_asset_item(0, 0), parse_asset_item(0, 1)); - let asset2 = build_asset(parse_asset_item(1, 0), parse_asset_item(1, 1)); + let asset1 = build_asset(parse_asset_item(0, 0)?, parse_asset_item(0, 1)?); + let asset2 = build_asset(parse_asset_item(1, 0)?, parse_asset_item(1, 1)?); let amount1 = get_asset_amount(&output, &asset1) - treasury1 @@ -67,6 +71,7 @@ impl Dex for WingRidersV1 { amount2, }); } + Ok(()) } fn queue_swap( @@ -75,7 +80,7 @@ impl Dex for WingRidersV1 { tx: &MultiEraTx, tx_id: i64, multiera_used_inputs_to_outputs_map: &BTreeMap, BTreeMap>, - ) { + ) -> Result<(), String> { // Note: there should be at most one pool output if let Some((pool_output, _)) = filter_outputs_and_datums_by_hash( &tx.outputs(), @@ -84,106 +89,106 @@ impl Dex for WingRidersV1 { ) .get(0) { - if tx.redeemers().is_none() || tx.redeemers().unwrap().len() == 0 { - return; - } - let redeemers = tx.redeemers().unwrap(); + let redeemers = tx.redeemers().ok_or("No redeemers")?; // Get pool input from redemeers - let pool_input = redeemers[0].data.to_json()["fields"][0]["int"].as_i64(); - if pool_input.is_none() { - return; - } - let pool_input = pool_input.unwrap(); + let pool_input_redeemer = redeemers.get(0).ok_or("No redeemers")?; + let pool_input = pool_input_redeemer.data.to_json()["fields"][0]["int"] + .as_i64() + .ok_or("Failed to parse pool input index")?; // Find main redemeer - if let Some(redeemer) = redeemers.iter().find(|&r| r.index as i64 == pool_input) { - let redeemer = redeemer.data.to_json(); - - // Extract input list from redemeer - let redeemer_map: Vec = redeemer["fields"][2]["list"] - .as_array() + let redeemer = redeemers + .iter() + .find(|&r| r.index as i64 == pool_input) + .ok_or("Failed to find main redeemer")?; + let redeemer = redeemer.data.to_json(); + + // Extract input list from redemeer + let redeemer_map: Vec = redeemer["fields"][2]["list"] + .as_array() + .ok_or("Failed to parse redeemer map")? + .iter() + .map(|r| r["int"].as_i64().unwrap() as usize) + .collect(); + // Find main transaction + let parent = redeemer["fields"][0]["int"] + .as_i64() + .ok_or("Failed to parse main transaction")? as usize; + // Restore inputs + let inputs: Vec = tx + .inputs() + .iter() + .map(|i| { + let output = &multiera_used_inputs_to_outputs_map[&i.hash().to_vec()] + [&(i.index() as i64)]; + MultiEraOutput::decode(output.era, &output.model.payload).unwrap() + }) + .collect::>(); + // Zip outputs with redemeer index + for (output, redeemer) in tx.outputs().iter().skip(1).zip(redeemer_map) { + // pair input with output + let input = inputs.get(redeemer).ok_or("Failed to pair output")?.clone(); + + // get information about swap from pool plutus data + let parent_datum = get_plutus_datum_for_output(&inputs[parent], &tx.plutus_data()) .unwrap() - .iter() - .map(|r| r["int"].as_i64().unwrap() as usize) - .collect(); - // Find main transaction - let parent = redeemer["fields"][0]["int"].as_i64().unwrap() as usize; - // Restore inputs - let inputs: Vec = tx - .inputs() - .iter() - .map(|i| { - let output = &multiera_used_inputs_to_outputs_map[&i.hash().to_vec()] - [&(i.index() as i64)]; - MultiEraOutput::decode(output.era, &output.model.payload).unwrap() - }) - .collect::>(); - // Zip outputs with redemeer index - for (output, redeemer) in tx.outputs().iter().skip(1).zip(redeemer_map) { - // pair input with output - let input = inputs[redeemer].clone(); - - // get information about swap from pool plutus data - let parent_datum = - get_plutus_datum_for_output(&inputs[parent], &tx.plutus_data()) - .unwrap() - .to_json(); - let parse_asset_item = |i, j| { - let item = parent_datum["fields"][1]["fields"][0]["fields"][i]["fields"][j] - ["bytes"] - .as_str() - .unwrap() - .to_string(); - hex::decode(item).unwrap() - }; - let asset1 = build_asset(parse_asset_item(0, 0), parse_asset_item(0, 1)); - let asset2 = build_asset(parse_asset_item(1, 0), parse_asset_item(1, 1)); - - // get actual plutus datum - let input_datum = get_plutus_datum_for_output(&input, &tx.plutus_data()) - .unwrap() - .to_json(); - // identify operation: 0 = swap - let operation = input_datum["fields"][1]["constructor"].as_i64().unwrap(); - if operation != 0 { - tracing::debug!("Operation is not a swap"); - continue; - } - let direction = input_datum["fields"][1]["fields"][0]["constructor"] - .as_i64() - .unwrap(); - - let amount1; - let amount2; - if direction == 0 { - amount1 = get_asset_amount(&input, &asset1) - - reduce_ada_amount(&asset1, SWAP_IN_ADA); - amount2 = get_asset_amount(&output, &asset2) - - reduce_ada_amount(&asset2, SWAP_OUT_ADA); - } else { - amount1 = get_asset_amount(&output, &asset1) - - reduce_ada_amount(&asset1, SWAP_OUT_ADA); - amount2 = get_asset_amount(&input, &asset2) - - reduce_ada_amount(&asset2, SWAP_IN_ADA); - } - queued_swaps.push(QueuedSwap { - tx_id, - address: pool_output.address().unwrap().to_vec(), - asset1, - asset2, - amount1, - amount2, - direction: if direction == 0 { - DexSwapDirection::SellAsset1 - } else { - DexSwapDirection::BuyAsset1 - }, - }) + .to_json(); + let parse_asset_item = |i, j| -> Result, &str> { + let item = parent_datum["fields"][1]["fields"][0]["fields"][i]["fields"][j] + ["bytes"] + .as_str() + .ok_or("Failed to parse asset item")? + .to_string(); + hex::decode(item).map_err(|_e| "Failed to parse asset item") + }; + let asset1 = build_asset(parse_asset_item(0, 0)?, parse_asset_item(0, 1)?); + let asset2 = build_asset(parse_asset_item(1, 0)?, parse_asset_item(1, 1)?); + + // get actual plutus datum + let input_datum = get_plutus_datum_for_output(&input, &tx.plutus_data()) + .unwrap() + .to_json(); + // identify operation: 0 = swap + let operation = input_datum["fields"][1]["constructor"] + .as_i64() + .ok_or("Failed to parse operation")?; + if operation != 0 { + tracing::debug!("Operation is not a swap"); + continue; } - } else { - tracing::info!("Redeemer not found"); + let direction = input_datum["fields"][1]["fields"][0]["constructor"] + .as_i64() + .ok_or("Failed to parse direction")?; + + let amount1; + let amount2; + if direction == 0 { + amount1 = + get_asset_amount(&input, &asset1) - reduce_ada_amount(&asset1, SWAP_IN_ADA); + amount2 = get_asset_amount(&output, &asset2) + - reduce_ada_amount(&asset2, SWAP_OUT_ADA); + } else { + amount1 = get_asset_amount(&output, &asset1) + - reduce_ada_amount(&asset1, SWAP_OUT_ADA); + amount2 = + get_asset_amount(&input, &asset2) - reduce_ada_amount(&asset2, SWAP_IN_ADA); + } + queued_swaps.push(QueuedSwap { + tx_id, + address: pool_output.address().unwrap().to_vec(), + asset1, + asset2, + amount1, + amount2, + direction: if direction == 0 { + DexSwapDirection::SellAsset1 + } else { + DexSwapDirection::BuyAsset1 + }, + }) } } + Ok(()) } } From 5e3adfd6c1995c427ef0330002a1c99fb1094f1b Mon Sep 17 00:00:00 2001 From: Miksa Date: Wed, 16 Nov 2022 15:23:18 +0100 Subject: [PATCH 29/41] Add endpoint for swap --- .../app/controllers/DexSwapController.ts | 116 ++++++++++++++++++ .../app/models/dex/sqlDexSwap.queries.ts | 87 +++++++++++++ .../server/app/models/dex/sqlDexSwap.sql | 46 +++++++ webserver/server/app/services/DexSwap.ts | 57 +++++++++ webserver/shared/models/DexSwap.ts | 35 ++++++ webserver/shared/routes.ts | 7 ++ 6 files changed, 348 insertions(+) create mode 100644 webserver/server/app/controllers/DexSwapController.ts create mode 100644 webserver/server/app/models/dex/sqlDexSwap.queries.ts create mode 100644 webserver/server/app/models/dex/sqlDexSwap.sql create mode 100644 webserver/server/app/services/DexSwap.ts create mode 100644 webserver/shared/models/DexSwap.ts diff --git a/webserver/server/app/controllers/DexSwapController.ts b/webserver/server/app/controllers/DexSwapController.ts new file mode 100644 index 00000000..a29afa8e --- /dev/null +++ b/webserver/server/app/controllers/DexSwapController.ts @@ -0,0 +1,116 @@ +import { Body, Controller, TsoaResponse, Res, Post, Route, SuccessResponse } from 'tsoa'; +import { StatusCodes } from 'http-status-codes'; +import { DEX_PRICE_LIMIT } from '../../../shared/constants'; +import tx from 'pg-tx'; +import pool from '../services/PgPoolSingleton'; +import { resolvePageStart, resolveUntilTransaction } from '../services/PaginationService'; +import type { ErrorShape } from '../../../shared/errors'; +import { genErrorMessage } from '../../../shared/errors'; +import { Errors } from '../../../shared/errors'; +import { expectType } from 'tsd'; +import type { EndpointTypes } from '../../../shared/routes'; +import { Routes } from '../../../shared/routes'; +import { getAddressTypes } from '../models/utils'; +import type { DexSwapResponse } from '../../../shared/models/DexSwap'; +import { dexSwap } from '../services/DexSwap'; + +const route = Routes.dexSwap; + +@Route('dex/swap-price') +export class DexSwapController extends Controller { + /** + * Gets the swap prices for the given liquidity pool addresses and asset pairs. + */ + @SuccessResponse(`${StatusCodes.OK}`) + @Post() + public async dexSwap( + @Body() + requestBody: EndpointTypes[typeof route]['input'], + @Res() + errorResponse: TsoaResponse< + StatusCodes.BAD_REQUEST | StatusCodes.CONFLICT | StatusCodes.UNPROCESSABLE_ENTITY, + ErrorShape + > + ): Promise { + if (requestBody.addresses.length > DEX_PRICE_LIMIT.REQUEST_ADDRESSES) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return errorResponse( + StatusCodes.BAD_REQUEST, + genErrorMessage(Errors.AddressLimitExceeded, { + limit: DEX_PRICE_LIMIT.REQUEST_ADDRESSES, + found: requestBody.addresses.length, + }) + ); + } + const addressTypes = getAddressTypes(requestBody.addresses); + if (addressTypes.invalid.length > 0) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return errorResponse( + StatusCodes.UNPROCESSABLE_ENTITY, + genErrorMessage(Errors.IncorrectAddressFormat, { + addresses: addressTypes.invalid, + }) + ); + } + + if (requestBody.assetPairs.length > DEX_PRICE_LIMIT.REQUEST_ASSET_PAIRS) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return errorResponse( + StatusCodes.BAD_REQUEST, + genErrorMessage(Errors.AssetPairLimitExceeded, { + limit: DEX_PRICE_LIMIT.REQUEST_ASSET_PAIRS, + found: requestBody.assetPairs.length, + }) + ); + } + + // note: we use a SQL transaction to make sure the pagination check works properly + // otherwise, a rollback could happen between getting the pagination info and the history query + const swapPrices = await tx( + pool, + async dbTx => { + const [until, pageStart] = await Promise.all([ + resolveUntilTransaction({ + block_hash: Buffer.from(requestBody.untilBlock, 'hex'), + dbTx, + }), + requestBody.after == null + ? Promise.resolve(undefined) + : resolvePageStart({ + after_block: Buffer.from(requestBody.after.block, 'hex'), + after_tx: Buffer.from(requestBody.after.tx, 'hex'), + dbTx, + }), + ]); + if (until == null) { + return genErrorMessage(Errors.BlockHashNotFound, { + untilBlock: requestBody.untilBlock, + }); + } + if (requestBody.after != null && pageStart == null) { + return genErrorMessage(Errors.PageStartNotFound, { + blockHash: requestBody.after.block, + txHash: requestBody.after.tx, + }); + } + + return await dexSwap({ + after: pageStart, + until, + dbTx, + addresses: addressTypes.exactAddress.map(addr => Buffer.from(addr, 'hex')), + reverseMap: addressTypes.reverseMap, + assetPairs: requestBody.assetPairs, + limit: requestBody.limit ?? DEX_PRICE_LIMIT.RESPONSE, + }); + } + ); + if ('code' in swapPrices) { + expectType>(true); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return errorResponse(StatusCodes.CONFLICT, swapPrices); + } + + return swapPrices; + } +} diff --git a/webserver/server/app/models/dex/sqlDexSwap.queries.ts b/webserver/server/app/models/dex/sqlDexSwap.queries.ts new file mode 100644 index 00000000..e3cf6435 --- /dev/null +++ b/webserver/server/app/models/dex/sqlDexSwap.queries.ts @@ -0,0 +1,87 @@ +/** Types generated for queries found in "app/models/dex/sqlDexSwap.sql" */ +import { PreparedQuery } from '@pgtyped/query'; + +export type BufferArray = (Buffer)[]; + +/** 'SqlDexSwap' parameters type */ +export interface ISqlDexSwapParams { + addresses: BufferArray | null | void; + after_tx_id: string | null | void; + asset_name1: BufferArray | null | void; + asset_name2: BufferArray | null | void; + limit: string | null | void; + policy_id1: BufferArray | null | void; + policy_id2: BufferArray | null | void; + until_tx_id: string | null | void; +} + +/** 'SqlDexSwap' return type */ +export interface ISqlDexSwapResult { + address: Buffer; + amount1: string; + amount2: string; + asset_name1: Buffer | null; + asset_name2: Buffer | null; + direction: boolean; + policy_id1: Buffer | null; + policy_id2: Buffer | null; + tx_hash: Buffer; +} + +/** 'SqlDexSwap' query type */ +export interface ISqlDexSwapQuery { + params: ISqlDexSwapParams; + result: ISqlDexSwapResult; +} + +const sqlDexSwapIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true,"addresses":true,"until_tx_id":true,"after_tx_id":true,"limit":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]},{"name":"addresses","required":false,"transform":{"type":"scalar"},"locs":[{"a":1145,"b":1154}]},{"name":"until_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":1459,"b":1470}]},{"name":"after_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":1500,"b":1511}]},{"name":"limit","required":false,"transform":{"type":"scalar"},"locs":[{"a":1560,"b":1565}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n \"Transaction\".hash AS tx_hash,\n \"Address\".payload AS address,\n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"DexSwap\".amount1,\n \"DexSwap\".amount2,\n \"DexSwap\".direction\nFROM \"DexSwap\"\nJOIN \"Transaction\" ON \"Transaction\".id = \"DexSwap\".tx_id\nJOIN \"Address\" ON \"Address\".id = \"DexSwap\".address_id\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"DexSwap\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"DexSwap\".asset2_id\nWHERE\n \"Address\".payload = ANY (:addresses)\n AND\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n AND\n \"DexSwap\".tx_id <= (:until_tx_id)\n AND\n \"DexSwap\".tx_id > (:after_tx_id)\nORDER BY \"DexSwap\".tx_id, \"DexSwap\".id\nLIMIT (:limit)"}; + +/** + * Query generated from SQL: + * ``` + * WITH "AssetPairs" AS ( + * SELECT policy_id1, asset_name1, policy_id2, asset_name2 + * FROM + * unnest( + * + * (:policy_id1)::bytea[], + * (:asset_name1)::bytea[], + * (:policy_id2)::bytea[], + * (:asset_name2)::bytea[] + * ) x(policy_id1, asset_name1, policy_id2, asset_name2) + * ) + * SELECT + * "Transaction".hash AS tx_hash, + * "Address".payload AS address, + * "Asset1".policy_id AS "policy_id1?", + * "Asset1".asset_name AS "asset_name1?", + * "Asset2".policy_id AS "policy_id2?", + * "Asset2".asset_name AS "asset_name2?", + * "DexSwap".amount1, + * "DexSwap".amount2, + * "DexSwap".direction + * FROM "DexSwap" + * JOIN "Transaction" ON "Transaction".id = "DexSwap".tx_id + * JOIN "Address" ON "Address".id = "DexSwap".address_id + * LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "DexSwap".asset1_id + * LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "DexSwap".asset2_id + * WHERE + * "Address".payload = ANY (:addresses) + * AND + * ( + * COALESCE("Asset1".policy_id, ''::bytea), + * COALESCE("Asset1".asset_name, ''::bytea), + * COALESCE("Asset2".policy_id, ''::bytea), + * COALESCE("Asset2".asset_name, ''::bytea) + * ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") + * AND + * "DexSwap".tx_id <= (:until_tx_id) + * AND + * "DexSwap".tx_id > (:after_tx_id) + * ORDER BY "DexSwap".tx_id, "DexSwap".id + * LIMIT (:limit) + * ``` + */ +export const sqlDexSwap = new PreparedQuery(sqlDexSwapIR); + + diff --git a/webserver/server/app/models/dex/sqlDexSwap.sql b/webserver/server/app/models/dex/sqlDexSwap.sql new file mode 100644 index 00000000..aafa5292 --- /dev/null +++ b/webserver/server/app/models/dex/sqlDexSwap.sql @@ -0,0 +1,46 @@ +/* @name sqlDexSwap */ +WITH "AssetPairs" AS ( + SELECT policy_id1, asset_name1, policy_id2, asset_name2 + FROM + unnest( + /* + Aparrently, we can't make pgtyped understand that these are actually (bytea | NULL)[]. + We will pass in ('', '') instead of (NULL, NULL) for ADA and do the NULL->'' conversion + below when filtering the assets (see the COALESCE). + */ + (:policy_id1)::bytea[], + (:asset_name1)::bytea[], + (:policy_id2)::bytea[], + (:asset_name2)::bytea[] + ) x(policy_id1, asset_name1, policy_id2, asset_name2) +) +SELECT + "Transaction".hash AS tx_hash, + "Address".payload AS address, + "Asset1".policy_id AS "policy_id1?", + "Asset1".asset_name AS "asset_name1?", + "Asset2".policy_id AS "policy_id2?", + "Asset2".asset_name AS "asset_name2?", + "DexSwap".amount1, + "DexSwap".amount2, + "DexSwap".direction +FROM "DexSwap" +JOIN "Transaction" ON "Transaction".id = "DexSwap".tx_id +JOIN "Address" ON "Address".id = "DexSwap".address_id +LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "DexSwap".asset1_id +LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "DexSwap".asset2_id +WHERE + "Address".payload = ANY (:addresses) + AND + ( + COALESCE("Asset1".policy_id, ''::bytea), + COALESCE("Asset1".asset_name, ''::bytea), + COALESCE("Asset2".policy_id, ''::bytea), + COALESCE("Asset2".asset_name, ''::bytea) + ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") + AND + "DexSwap".tx_id <= (:until_tx_id) + AND + "DexSwap".tx_id > (:after_tx_id) +ORDER BY "DexSwap".tx_id, "DexSwap".id +LIMIT (:limit); diff --git a/webserver/server/app/services/DexSwap.ts b/webserver/server/app/services/DexSwap.ts new file mode 100644 index 00000000..821a13fb --- /dev/null +++ b/webserver/server/app/services/DexSwap.ts @@ -0,0 +1,57 @@ +import type { Asset, DexSwapResponse } from '../../../shared/models/DexSwap'; +import type { PoolClient } from 'pg'; +import type { TransactionPaginationType } from './PaginationService'; +import { sqlDexSwap } from '../models/dex/sqlDexSwap.queries'; + +function parseAssetItem(s: string | undefined | null): Buffer { + // For the sake of the query, we represent ADA as ('', '') instead of (NULL, NULL). + // (see sqlDexMeanPrice.queries.sql for details) + return Buffer.from(s ?? "", 'hex'); +} + +function serializeAsset(policyId: Buffer | null, assetName: Buffer | null): Asset { + if (policyId === null && assetName === null) { + return null; + } + if (policyId !== null && assetName !== null) { + return { + policyId: policyId.toString('hex'), + assetName: assetName.toString('hex'), + }; + } + throw new Error('Invalid asset query response'); // should be unreachable +} + +export async function dexSwap( + request: TransactionPaginationType & { + dbTx: PoolClient; + addresses: Buffer[]; + reverseMap: Map>; + assetPairs: {asset1: Asset, asset2: Asset}[]; + limit: number; + } +): Promise { + if (request.addresses.length === 0 || request.assetPairs.length === 0) return { swap: [] }; + + const swap = await sqlDexSwap.run({ + after_tx_id: (request.after?.tx_id ?? -1)?.toString(), + until_tx_id: request.until.tx_id.toString(), + addresses: request.addresses, + policy_id1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.policyId)), + asset_name1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.assetName)), + policy_id2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.policyId)), + asset_name2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.assetName)), + limit: request.limit.toString(), + }, request.dbTx); + return { + swap: swap.map(result => ({ + tx_hash: result.tx_hash.toString('hex'), + address: [...(request.reverseMap.get(result.address.toString('hex')) ?? [])][0], + asset1: serializeAsset(result.policy_id1, result.asset_name1), + asset2: serializeAsset(result.policy_id2, result.asset_name2), + amount1: result.amount1, + amount2: result.amount2, + direction: (result.direction ? 'buy' : 'sell') + })), + }; +} diff --git a/webserver/shared/models/DexSwap.ts b/webserver/shared/models/DexSwap.ts new file mode 100644 index 00000000..5ead606e --- /dev/null +++ b/webserver/shared/models/DexSwap.ts @@ -0,0 +1,35 @@ +import { Address } from "./Address"; +import { Pagination } from "./common"; +import { AssetName, PolicyId } from "./PolicyIdAssetMap"; + +export type Asset = { + policyId: PolicyId; + assetName: AssetName; +} | null; + +/** + * @example "2042352568679" + */ +type Amount = string; // uint64 +type Direction = string; + +export type DexSwap = { + tx_hash: string; + address: Address; + asset1: Asset; + asset2: Asset; + amount1: Amount; + amount2: Amount; + direction: Direction; +} + +export type DexSwapRequest = { + addresses: Address[], + assetPairs: {asset1: Asset, asset2: Asset}[]; + /** Defaults to `DEX_PRICE_LIMIT.RESPONSE` */ + limit?: number; +} & Pagination; + +export type DexSwapResponse = { + swap: DexSwap[]; +}; diff --git a/webserver/shared/routes.ts b/webserver/shared/routes.ts index 03360d9a..d3b15bfb 100644 --- a/webserver/shared/routes.ts +++ b/webserver/shared/routes.ts @@ -8,6 +8,7 @@ import type { CredentialAddressResponse, } from "./models/CredentialAddress"; import { DexMeanPriceRequest, DexMeanPriceResponse } from "./models/DexMeanPrice"; +import { DexSwapRequest, DexSwapResponse } from "./models/DexSwap"; import { Cip25Response, PolicyIdAssetMapType } from "./models/PolicyIdAssetMap"; import type { TransactionHistoryRequest, @@ -26,6 +27,7 @@ export enum Routes { blockLatest = "block/latest", metadataNft = "metadata/nft", dexMeanPrice = "dex/mean-price", + dexSwap = "dex/swap", } export type EndpointTypes = { @@ -64,4 +66,9 @@ export type EndpointTypes = { input: DexMeanPriceRequest; response: DexMeanPriceResponse; }; + [Routes.dexSwap]: { + name: typeof Routes.dexSwap; + input: DexSwapRequest; + response: DexSwapResponse; + }; }; From 2f7b1f2a1af7fb04e62c98831aff60d1a3e48eb3 Mon Sep 17 00:00:00 2001 From: Miksa Date: Wed, 16 Nov 2022 15:31:36 +0100 Subject: [PATCH 30/41] Add last price endpoint --- .../app/controllers/DexLastPriceController.ts | 65 ++++++++++++++ .../models/dex/sqlDexLastPriceMean.queries.ts | 80 +++++++++++++++++ .../app/models/dex/sqlDexLastPriceMean.sql | 45 ++++++++++ .../models/dex/sqlDexLastPriceSwap.queries.ts | 87 +++++++++++++++++++ .../app/models/dex/sqlDexLastPriceSwap.sql | 51 +++++++++++ webserver/server/app/models/utils.ts | 2 +- webserver/server/app/services/DexLastPrice.ts | 57 ++++++++++++ webserver/server/app/services/DexMeanPrice.ts | 20 +---- webserver/server/app/services/DexSwap.ts | 20 +---- webserver/server/app/services/utils.ts | 21 +++++ webserver/shared/models/DexLastPrice.ts | 35 ++++++++ webserver/shared/routes.ts | 7 ++ 12 files changed, 451 insertions(+), 39 deletions(-) create mode 100644 webserver/server/app/controllers/DexLastPriceController.ts create mode 100644 webserver/server/app/models/dex/sqlDexLastPriceMean.queries.ts create mode 100644 webserver/server/app/models/dex/sqlDexLastPriceMean.sql create mode 100644 webserver/server/app/models/dex/sqlDexLastPriceSwap.queries.ts create mode 100644 webserver/server/app/models/dex/sqlDexLastPriceSwap.sql create mode 100644 webserver/server/app/services/DexLastPrice.ts create mode 100644 webserver/server/app/services/utils.ts create mode 100644 webserver/shared/models/DexLastPrice.ts diff --git a/webserver/server/app/controllers/DexLastPriceController.ts b/webserver/server/app/controllers/DexLastPriceController.ts new file mode 100644 index 00000000..e586cbeb --- /dev/null +++ b/webserver/server/app/controllers/DexLastPriceController.ts @@ -0,0 +1,65 @@ +import { Body, Controller, TsoaResponse, Res, Post, Route, SuccessResponse } from 'tsoa'; +import { StatusCodes } from 'http-status-codes'; +import { DEX_PRICE_LIMIT } from '../../../shared/constants'; +import tx from 'pg-tx'; +import pool from '../services/PgPoolSingleton'; +import type { ErrorShape } from '../../../shared/errors'; +import { genErrorMessage } from '../../../shared/errors'; +import { Errors } from '../../../shared/errors'; +import { expectType } from 'tsd'; +import type { EndpointTypes } from '../../../shared/routes'; +import { Routes } from '../../../shared/routes'; +import type { DexLastPriceResponse } from '../../../shared/models/DexLastPrice'; +import { dexLastPrice } from '../services/DexLastPrice'; + + +const route = Routes.dexLastPrice; + +@Route('dex/last-price') +export class DexLastPriceController extends Controller { + /** + * Gets the swap prices for the given liquidity pool addresses and asset pairs. + */ + @SuccessResponse(`${StatusCodes.OK}`) + @Post() + public async dexLastPrice( + @Body() + requestBody: EndpointTypes[typeof route]['input'], + @Res() + errorResponse: TsoaResponse< + StatusCodes.BAD_REQUEST | StatusCodes.CONFLICT | StatusCodes.UNPROCESSABLE_ENTITY, + ErrorShape + > + ): Promise { + if (requestBody.assetPairs.length > DEX_PRICE_LIMIT.REQUEST_ASSET_PAIRS) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return errorResponse( + StatusCodes.BAD_REQUEST, + genErrorMessage(Errors.AssetPairLimitExceeded, { + limit: DEX_PRICE_LIMIT.REQUEST_ASSET_PAIRS, + found: requestBody.assetPairs.length, + }) + ); + } + + // note: we use a SQL transaction to make sure the pagination check works properly + // otherwise, a rollback could happen between getting the pagination info and the history query + const lastPrices = await tx( + pool, + async dbTx => { + return await dexLastPrice({ + dbTx, + assetPairs: requestBody.assetPairs, + type: requestBody.type + }); + } + ); + if ('code' in lastPrices) { + expectType>(true); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return errorResponse(StatusCodes.CONFLICT, lastPrices); + } + + return lastPrices; + } +} diff --git a/webserver/server/app/models/dex/sqlDexLastPriceMean.queries.ts b/webserver/server/app/models/dex/sqlDexLastPriceMean.queries.ts new file mode 100644 index 00000000..82d0527d --- /dev/null +++ b/webserver/server/app/models/dex/sqlDexLastPriceMean.queries.ts @@ -0,0 +1,80 @@ +/** Types generated for queries found in "app/models/dex/sqlDexLastPriceMean.sql" */ +import { PreparedQuery } from '@pgtyped/query'; + +export type BufferArray = (Buffer)[]; + +/** 'SqlDexLastPriceMean' parameters type */ +export interface ISqlDexLastPriceMeanParams { + asset_name1: BufferArray | null | void; + asset_name2: BufferArray | null | void; + policy_id1: BufferArray | null | void; + policy_id2: BufferArray | null | void; +} + +/** 'SqlDexLastPriceMean' return type */ +export interface ISqlDexLastPriceMeanResult { + amount1: string; + amount2: string; + asset_name1: Buffer | null; + asset_name2: Buffer | null; + dex: string | null; + policy_id1: Buffer | null; + policy_id2: Buffer | null; +} + +/** 'SqlDexLastPriceMean' query type */ +export interface ISqlDexLastPriceMeanQuery { + params: ISqlDexLastPriceMeanParams; + result: ISqlDexLastPriceMeanResult; +} + +const sqlDexLastPriceMeanIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n DISTINCT ON(\"DexMeanPrice\".address_id)\n\n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"DexMeanPrice\".amount1,\n \"DexMeanPrice\".amount2,\n 'WingRiders' || address_id as dex\nFROM \"DexMeanPrice\"\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"DexMeanPrice\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"DexMeanPrice\".asset2_id\nWHERE\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n -- Add swap for another direction\n OR\n (\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea),\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\nORDER BY \"DexMeanPrice\".address_id, \"DexMeanPrice\".tx_id, \"DexMeanPrice\".id"}; + +/** + * Query generated from SQL: + * ``` + * WITH "AssetPairs" AS ( + * SELECT policy_id1, asset_name1, policy_id2, asset_name2 + * FROM + * unnest( + * + * (:policy_id1)::bytea[], + * (:asset_name1)::bytea[], + * (:policy_id2)::bytea[], + * (:asset_name2)::bytea[] + * ) x(policy_id1, asset_name1, policy_id2, asset_name2) + * ) + * SELECT + * DISTINCT ON("DexMeanPrice".address_id) + * + * "Asset1".policy_id AS "policy_id1?", + * "Asset1".asset_name AS "asset_name1?", + * "Asset2".policy_id AS "policy_id2?", + * "Asset2".asset_name AS "asset_name2?", + * "DexMeanPrice".amount1, + * "DexMeanPrice".amount2, + * 'WingRiders' || address_id as dex + * FROM "DexMeanPrice" + * LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "DexMeanPrice".asset1_id + * LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "DexMeanPrice".asset2_id + * WHERE + * ( + * COALESCE("Asset1".policy_id, ''::bytea), + * COALESCE("Asset1".asset_name, ''::bytea), + * COALESCE("Asset2".policy_id, ''::bytea), + * COALESCE("Asset2".asset_name, ''::bytea) + * ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") + * -- Add swap for another direction + * OR + * ( + * COALESCE("Asset2".policy_id, ''::bytea), + * COALESCE("Asset2".asset_name, ''::bytea), + * COALESCE("Asset1".policy_id, ''::bytea), + * COALESCE("Asset1".asset_name, ''::bytea) + * ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") + * ORDER BY "DexMeanPrice".address_id, "DexMeanPrice".tx_id, "DexMeanPrice".id + * ``` + */ +export const sqlDexLastPriceMean = new PreparedQuery(sqlDexLastPriceMeanIR); + + diff --git a/webserver/server/app/models/dex/sqlDexLastPriceMean.sql b/webserver/server/app/models/dex/sqlDexLastPriceMean.sql new file mode 100644 index 00000000..759f488a --- /dev/null +++ b/webserver/server/app/models/dex/sqlDexLastPriceMean.sql @@ -0,0 +1,45 @@ +/* @name sqlDexLastPriceMean */ +WITH "AssetPairs" AS ( + SELECT policy_id1, asset_name1, policy_id2, asset_name2 + FROM + unnest( + /* + Aparrently, we can't make pgtyped understand that these are actually (bytea | NULL)[]. + We will pass in ('', '') instead of (NULL, NULL) for ADA and do the NULL->'' conversion + below when filtering the assets (see the COALESCE). + */ + (:policy_id1)::bytea[], + (:asset_name1)::bytea[], + (:policy_id2)::bytea[], + (:asset_name2)::bytea[] + ) x(policy_id1, asset_name1, policy_id2, asset_name2) +) +SELECT + DISTINCT ON("DexMeanPrice".address_id) + + "Asset1".policy_id AS "policy_id1?", + "Asset1".asset_name AS "asset_name1?", + "Asset2".policy_id AS "policy_id2?", + "Asset2".asset_name AS "asset_name2?", + "DexMeanPrice".amount1, + "DexMeanPrice".amount2, + 'WingRiders' || address_id as dex +FROM "DexMeanPrice" +LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "DexMeanPrice".asset1_id +LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "DexMeanPrice".asset2_id +WHERE + ( + COALESCE("Asset1".policy_id, ''::bytea), + COALESCE("Asset1".asset_name, ''::bytea), + COALESCE("Asset2".policy_id, ''::bytea), + COALESCE("Asset2".asset_name, ''::bytea) + ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") + -- Add swap for another direction + OR + ( + COALESCE("Asset2".policy_id, ''::bytea), + COALESCE("Asset2".asset_name, ''::bytea), + COALESCE("Asset1".policy_id, ''::bytea), + COALESCE("Asset1".asset_name, ''::bytea) + ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") +ORDER BY "DexMeanPrice".address_id, "DexMeanPrice".tx_id, "DexMeanPrice".id; diff --git a/webserver/server/app/models/dex/sqlDexLastPriceSwap.queries.ts b/webserver/server/app/models/dex/sqlDexLastPriceSwap.queries.ts new file mode 100644 index 00000000..b341a77d --- /dev/null +++ b/webserver/server/app/models/dex/sqlDexLastPriceSwap.queries.ts @@ -0,0 +1,87 @@ +/** Types generated for queries found in "app/models/dex/sqlDexLastPriceSwap.sql" */ +import { PreparedQuery } from '@pgtyped/query'; + +export type BufferArray = (Buffer)[]; + +/** 'SqlDexLastPriceSwap' parameters type */ +export interface ISqlDexLastPriceSwapParams { + asset_name1: BufferArray | null | void; + asset_name2: BufferArray | null | void; + direction: boolean | null | void; + policy_id1: BufferArray | null | void; + policy_id2: BufferArray | null | void; +} + +/** 'SqlDexLastPriceSwap' return type */ +export interface ISqlDexLastPriceSwapResult { + amount1: string; + amount2: string; + asset_name1: Buffer | null; + asset_name2: Buffer | null; + dex: string | null; + policy_id1: Buffer | null; + policy_id2: Buffer | null; +} + +/** 'SqlDexLastPriceSwap' query type */ +export interface ISqlDexLastPriceSwapQuery { + params: ISqlDexLastPriceSwapParams; + result: ISqlDexLastPriceSwapResult; +} + +const sqlDexLastPriceSwapIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true,"direction":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]},{"name":"direction","required":false,"transform":{"type":"scalar"},"locs":[{"a":1307,"b":1316},{"a":1678,"b":1687}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n DISTINCT ON(\"DexSwap\".address_id)\n\n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"DexSwap\".amount1,\n \"DexSwap\".amount2,\n 'WingRiders' || address_id as dex\nFROM \"DexSwap\"\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"DexSwap\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"DexSwap\".asset2_id\nWHERE\n (\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n AND \"DexSwap\".direction = :direction\n )\n -- Add swap for another direction\n OR\n (\n (\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea),\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n AND \"DexSwap\".direction != :direction\n )\nORDER BY \"DexSwap\".address_id, \"DexSwap\".tx_id, \"DexSwap\".id"}; + +/** + * Query generated from SQL: + * ``` + * WITH "AssetPairs" AS ( + * SELECT policy_id1, asset_name1, policy_id2, asset_name2 + * FROM + * unnest( + * + * (:policy_id1)::bytea[], + * (:asset_name1)::bytea[], + * (:policy_id2)::bytea[], + * (:asset_name2)::bytea[] + * ) x(policy_id1, asset_name1, policy_id2, asset_name2) + * ) + * SELECT + * DISTINCT ON("DexSwap".address_id) + * + * "Asset1".policy_id AS "policy_id1?", + * "Asset1".asset_name AS "asset_name1?", + * "Asset2".policy_id AS "policy_id2?", + * "Asset2".asset_name AS "asset_name2?", + * "DexSwap".amount1, + * "DexSwap".amount2, + * 'WingRiders' || address_id as dex + * FROM "DexSwap" + * LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "DexSwap".asset1_id + * LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "DexSwap".asset2_id + * WHERE + * ( + * ( + * COALESCE("Asset1".policy_id, ''::bytea), + * COALESCE("Asset1".asset_name, ''::bytea), + * COALESCE("Asset2".policy_id, ''::bytea), + * COALESCE("Asset2".asset_name, ''::bytea) + * ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") + * AND "DexSwap".direction = :direction + * ) + * -- Add swap for another direction + * OR + * ( + * ( + * COALESCE("Asset2".policy_id, ''::bytea), + * COALESCE("Asset2".asset_name, ''::bytea), + * COALESCE("Asset1".policy_id, ''::bytea), + * COALESCE("Asset1".asset_name, ''::bytea) + * ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") + * AND "DexSwap".direction != :direction + * ) + * ORDER BY "DexSwap".address_id, "DexSwap".tx_id, "DexSwap".id + * ``` + */ +export const sqlDexLastPriceSwap = new PreparedQuery(sqlDexLastPriceSwapIR); + + diff --git a/webserver/server/app/models/dex/sqlDexLastPriceSwap.sql b/webserver/server/app/models/dex/sqlDexLastPriceSwap.sql new file mode 100644 index 00000000..83553ac5 --- /dev/null +++ b/webserver/server/app/models/dex/sqlDexLastPriceSwap.sql @@ -0,0 +1,51 @@ +/* @name sqlDexLastPriceSwap */ +WITH "AssetPairs" AS ( + SELECT policy_id1, asset_name1, policy_id2, asset_name2 + FROM + unnest( + /* + Aparrently, we can't make pgtyped understand that these are actually (bytea | NULL)[]. + We will pass in ('', '') instead of (NULL, NULL) for ADA and do the NULL->'' conversion + below when filtering the assets (see the COALESCE). + */ + (:policy_id1)::bytea[], + (:asset_name1)::bytea[], + (:policy_id2)::bytea[], + (:asset_name2)::bytea[] + ) x(policy_id1, asset_name1, policy_id2, asset_name2) +) +SELECT + DISTINCT ON("DexSwap".address_id) + + "Asset1".policy_id AS "policy_id1?", + "Asset1".asset_name AS "asset_name1?", + "Asset2".policy_id AS "policy_id2?", + "Asset2".asset_name AS "asset_name2?", + "DexSwap".amount1, + "DexSwap".amount2, + 'WingRiders' || address_id as dex +FROM "DexSwap" +LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "DexSwap".asset1_id +LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "DexSwap".asset2_id +WHERE + ( + ( + COALESCE("Asset1".policy_id, ''::bytea), + COALESCE("Asset1".asset_name, ''::bytea), + COALESCE("Asset2".policy_id, ''::bytea), + COALESCE("Asset2".asset_name, ''::bytea) + ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") + AND "DexSwap".direction = :direction + ) + -- Add swap for another direction + OR + ( + ( + COALESCE("Asset2".policy_id, ''::bytea), + COALESCE("Asset2".asset_name, ''::bytea), + COALESCE("Asset1".policy_id, ''::bytea), + COALESCE("Asset1".asset_name, ''::bytea) + ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") + AND "DexSwap".direction != :direction + ) +ORDER BY "DexSwap".address_id, "DexSwap".tx_id, "DexSwap".id; diff --git a/webserver/server/app/models/utils.ts b/webserver/server/app/models/utils.ts index ab7c54b2..4fdc9478 100644 --- a/webserver/server/app/models/utils.ts +++ b/webserver/server/app/models/utils.ts @@ -133,4 +133,4 @@ export function getAsExactAddressHex(address: string): undefined | string { } } catch (_e) {} return undefined; -} +} \ No newline at end of file diff --git a/webserver/server/app/services/DexLastPrice.ts b/webserver/server/app/services/DexLastPrice.ts new file mode 100644 index 00000000..5273f7e7 --- /dev/null +++ b/webserver/server/app/services/DexLastPrice.ts @@ -0,0 +1,57 @@ +import { Asset, PriceType, DexLastPriceResponse } from '../../../shared/models/DexLastPrice'; +import type { PoolClient } from 'pg'; +import { sqlDexLastPriceSwap } from '../models/dex/sqlDexLastPriceSwap.queries'; +import { sqlDexLastPriceMean } from '../models/dex/sqlDexLastPriceMean.queries'; +import { parseAssetItem, serializeAsset} from './utils'; + + +export async function dexLastPrice( + request: { + dbTx: PoolClient; + assetPairs: {asset1: Asset, asset2: Asset}[]; + type: PriceType; + } +): Promise { + if (request.assetPairs.length === 0) return { lastPrice: [] }; + + + const lastPrice = await (async () => { + switch(request.type) { + case PriceType.Mean: + return await sqlDexLastPriceMean.run({ + policy_id1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.policyId)), + asset_name1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.assetName)), + policy_id2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.policyId)), + asset_name2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.assetName)), + }, request.dbTx); + + case PriceType.Sell: + return await sqlDexLastPriceSwap.run({ + policy_id1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.policyId)), + asset_name1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.assetName)), + policy_id2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.policyId)), + asset_name2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.assetName)), + direction: false + }, request.dbTx); + + case PriceType.Buy: + return await sqlDexLastPriceSwap.run({ + policy_id1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.policyId)), + asset_name1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.assetName)), + policy_id2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.policyId)), + asset_name2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.assetName)), + direction: true + }, request.dbTx); + } + })(); + + return { + lastPrice: lastPrice.map(result => ({ + asset1: serializeAsset(result.policy_id1, result.asset_name1), + asset2: serializeAsset(result.policy_id2, result.asset_name2), + amount1: result.amount1, + amount2: result.amount2, + dex: String(result.dex), + })), + }; +} diff --git a/webserver/server/app/services/DexMeanPrice.ts b/webserver/server/app/services/DexMeanPrice.ts index abc99ce6..bacabee3 100644 --- a/webserver/server/app/services/DexMeanPrice.ts +++ b/webserver/server/app/services/DexMeanPrice.ts @@ -2,25 +2,7 @@ import type { Asset, DexMeanPriceResponse } from '../../../shared/models/DexMean import type { PoolClient } from 'pg'; import type { TransactionPaginationType } from './PaginationService'; import { sqlDexMeanPrice } from '../models/dex/sqlDexMeanPrice.queries'; - -function parseAssetItem(s: string | undefined | null): Buffer { - // For the sake of the query, we represent ADA as ('', '') instead of (NULL, NULL). - // (see sqlDexMeanPrice.queries.sql for details) - return Buffer.from(s ?? "", 'hex'); -} - -function serializeAsset(policyId: Buffer | null, assetName: Buffer | null): Asset { - if (policyId === null && assetName === null) { - return null; - } - if (policyId !== null && assetName !== null) { - return { - policyId: policyId.toString('hex'), - assetName: assetName.toString('hex'), - }; - } - throw new Error('Invalid asset query response'); // should be unreachable -} +import { parseAssetItem, serializeAsset} from './utils'; export async function dexMeanPrices( request: TransactionPaginationType & { diff --git a/webserver/server/app/services/DexSwap.ts b/webserver/server/app/services/DexSwap.ts index 821a13fb..c0f73989 100644 --- a/webserver/server/app/services/DexSwap.ts +++ b/webserver/server/app/services/DexSwap.ts @@ -2,25 +2,7 @@ import type { Asset, DexSwapResponse } from '../../../shared/models/DexSwap'; import type { PoolClient } from 'pg'; import type { TransactionPaginationType } from './PaginationService'; import { sqlDexSwap } from '../models/dex/sqlDexSwap.queries'; - -function parseAssetItem(s: string | undefined | null): Buffer { - // For the sake of the query, we represent ADA as ('', '') instead of (NULL, NULL). - // (see sqlDexMeanPrice.queries.sql for details) - return Buffer.from(s ?? "", 'hex'); -} - -function serializeAsset(policyId: Buffer | null, assetName: Buffer | null): Asset { - if (policyId === null && assetName === null) { - return null; - } - if (policyId !== null && assetName !== null) { - return { - policyId: policyId.toString('hex'), - assetName: assetName.toString('hex'), - }; - } - throw new Error('Invalid asset query response'); // should be unreachable -} +import { parseAssetItem, serializeAsset} from './utils'; export async function dexSwap( request: TransactionPaginationType & { diff --git a/webserver/server/app/services/utils.ts b/webserver/server/app/services/utils.ts new file mode 100644 index 00000000..813b3391 --- /dev/null +++ b/webserver/server/app/services/utils.ts @@ -0,0 +1,21 @@ +import type { Asset } from '../../../shared/models/DexMeanPrice'; + +export function parseAssetItem(s: string | undefined | null): Buffer { + // For the sake of the query, we represent ADA as ('', '') instead of (NULL, NULL). + // (see sqlDexMeanPrice.queries.sql for details) + return Buffer.from(s ?? "", 'hex'); + } + + export function serializeAsset(policyId: Buffer | null, assetName: Buffer | null): Asset { + if (policyId === null && assetName === null) { + return null; + } + if (policyId !== null && assetName !== null) { + return { + policyId: policyId.toString('hex'), + assetName: assetName.toString('hex'), + }; + } + throw new Error('Invalid asset query response'); // should be unreachable + } + \ No newline at end of file diff --git a/webserver/shared/models/DexLastPrice.ts b/webserver/shared/models/DexLastPrice.ts new file mode 100644 index 00000000..3b5e57ce --- /dev/null +++ b/webserver/shared/models/DexLastPrice.ts @@ -0,0 +1,35 @@ +import { AssetName, PolicyId } from "./PolicyIdAssetMap"; + +export type Asset = { + policyId: PolicyId; + assetName: AssetName; +} | null; + +/** + * @example "2042352568679" + */ +type Amount = string; // uint64 +type Dex = string; // enum + +export type DexLastPrice = { + asset1: Asset; + asset2: Asset; + amount1: Amount; + amount2: Amount; + dex: Dex; +}; + +export enum PriceType { + Buy = "buy", + Sell = "sell", + Mean = "mean", +}; + +export type DexLastPriceRequest = { + assetPairs: {asset1: Asset, asset2: Asset}[]; + type: PriceType; +}; + +export type DexLastPriceResponse = { + lastPrice: DexLastPrice[]; +}; diff --git a/webserver/shared/routes.ts b/webserver/shared/routes.ts index d3b15bfb..1d7b099f 100644 --- a/webserver/shared/routes.ts +++ b/webserver/shared/routes.ts @@ -9,6 +9,7 @@ import type { } from "./models/CredentialAddress"; import { DexMeanPriceRequest, DexMeanPriceResponse } from "./models/DexMeanPrice"; import { DexSwapRequest, DexSwapResponse } from "./models/DexSwap"; +import { DexLastPriceRequest, DexLastPriceResponse } from "./models/DexLastPrice"; import { Cip25Response, PolicyIdAssetMapType } from "./models/PolicyIdAssetMap"; import type { TransactionHistoryRequest, @@ -28,6 +29,7 @@ export enum Routes { metadataNft = "metadata/nft", dexMeanPrice = "dex/mean-price", dexSwap = "dex/swap", + dexLastPrice = "dex/last-price" } export type EndpointTypes = { @@ -71,4 +73,9 @@ export type EndpointTypes = { input: DexSwapRequest; response: DexSwapResponse; }; + [Routes.dexLastPrice]: { + name: typeof Routes.dexLastPrice; + input: DexLastPriceRequest; + response: DexLastPriceResponse; + }; }; From de8cea662e3cb9e60a2353ed37d6072a410d9065 Mon Sep 17 00:00:00 2001 From: Miksa Date: Wed, 16 Nov 2022 16:09:49 +0100 Subject: [PATCH 31/41] Fix direction on grouping --- webserver/server/app/models/dex/sqlDexLastPriceMean.sql | 2 +- webserver/server/app/models/dex/sqlDexLastPriceSwap.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/webserver/server/app/models/dex/sqlDexLastPriceMean.sql b/webserver/server/app/models/dex/sqlDexLastPriceMean.sql index 759f488a..5d0f4513 100644 --- a/webserver/server/app/models/dex/sqlDexLastPriceMean.sql +++ b/webserver/server/app/models/dex/sqlDexLastPriceMean.sql @@ -42,4 +42,4 @@ WHERE COALESCE("Asset1".policy_id, ''::bytea), COALESCE("Asset1".asset_name, ''::bytea) ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") -ORDER BY "DexMeanPrice".address_id, "DexMeanPrice".tx_id, "DexMeanPrice".id; +ORDER BY "DexMeanPrice".address_id, "DexMeanPrice".tx_id DESC, "DexMeanPrice".id DESC; diff --git a/webserver/server/app/models/dex/sqlDexLastPriceSwap.sql b/webserver/server/app/models/dex/sqlDexLastPriceSwap.sql index 83553ac5..db872998 100644 --- a/webserver/server/app/models/dex/sqlDexLastPriceSwap.sql +++ b/webserver/server/app/models/dex/sqlDexLastPriceSwap.sql @@ -48,4 +48,4 @@ WHERE ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") AND "DexSwap".direction != :direction ) -ORDER BY "DexSwap".address_id, "DexSwap".tx_id, "DexSwap".id; +ORDER BY "DexSwap".address_id, "DexSwap".tx_id DESC, "DexSwap".id DESC; From fd940cef1076905dcf713a00675e8a919bcd058a Mon Sep 17 00:00:00 2001 From: Miksa Date: Wed, 16 Nov 2022 17:27:43 +0100 Subject: [PATCH 32/41] Add dex to migration and output --- .env | 5 +++-- indexer/entity/src/dex_mean_price.rs | 1 + indexer/entity/src/dex_swap.rs | 18 ++++++++++++++++++ ...21020_000014_create_dex_mean_price_table.rs | 1 + .../m20221031_000015_create_dex_swap_table.rs | 1 + indexer/tasks/src/multiera/dex/common.rs | 17 ++++++++++++++++- indexer/tasks/src/multiera/dex/minswap_v1.rs | 4 +++- .../tasks/src/multiera/dex/sundaeswap_v1.rs | 4 +++- .../tasks/src/multiera/dex/wingriders_v1.rs | 6 ++++-- .../models/dex/sqlDexLastPriceMean.queries.ts | 8 ++++---- .../app/models/dex/sqlDexLastPriceMean.sql | 2 +- .../models/dex/sqlDexLastPriceSwap.queries.ts | 8 ++++---- .../app/models/dex/sqlDexLastPriceSwap.sql | 2 +- webserver/server/app/services/DexLastPrice.ts | 11 ++++++++++- 14 files changed, 70 insertions(+), 18 deletions(-) diff --git a/.env b/.env index e71c8986..b5f12368 100644 --- a/.env +++ b/.env @@ -1,12 +1,13 @@ NETWORK=mainnet # mainnet/preview/preprod/testnet # SOCKET=relays-new.cardano-mainnet.iohk.io:3001 # SOCKET=relays-new.cardano-testnet.iohkdev.io:3001 -SOCKET="$(realpath ../cardano-node/bin/\\.pipe\\cardano-node)" +SOCKET=/mnt/nvme/carp/node-ipc/node.socket POSTGRES_HOST=localhost POSTGRES_PORT=5432 -PGUSER=carp +PGUSER=postgres POSTGRES_DB=carp_mainnet PGPASSFILE="$(realpath secrets/.pgpass)" +PGPASSWORD=postgres # note: PGPASSWORD isn't required to run carp # since it will be parsed from the PGPASSFILE instead # as this command will gracefully fallback to PGPASSFILE if no password is specified diff --git a/indexer/entity/src/dex_mean_price.rs b/indexer/entity/src/dex_mean_price.rs index 5232b3a1..0bb76936 100644 --- a/indexer/entity/src/dex_mean_price.rs +++ b/indexer/entity/src/dex_mean_price.rs @@ -10,6 +10,7 @@ pub struct Model { pub tx_id: i64, #[sea_orm(column_type = "BigInteger")] pub address_id: i64, + pub dex: i32, #[sea_orm(column_type = "BigInteger", nullable)] pub asset1_id: Option, #[sea_orm(column_type = "BigInteger", nullable)] diff --git a/indexer/entity/src/dex_swap.rs b/indexer/entity/src/dex_swap.rs index 802a28fc..689d769a 100644 --- a/indexer/entity/src/dex_swap.rs +++ b/indexer/entity/src/dex_swap.rs @@ -10,6 +10,7 @@ pub struct Model { pub tx_id: i64, #[sea_orm(column_type = "BigInteger")] pub address_id: i64, + pub dex: i32, #[sea_orm(column_type = "BigInteger", nullable)] pub asset1_id: Option, #[sea_orm(column_type = "BigInteger", nullable)] @@ -64,3 +65,20 @@ impl Related for Entity { } impl ActiveModelBehavior for ActiveModel {} + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum Dex { + WingRidersV1, + SundaeSwapV1, + MinSwapV1, +} + +impl From for i32 { + fn from(item: Dex) -> Self { + match item { + Dex::WingRidersV1 => 0, + Dex::SundaeSwapV1 => 1, + Dex::MinSwapV1 => 2, + } + } +} diff --git a/indexer/migration/src/m20221020_000014_create_dex_mean_price_table.rs b/indexer/migration/src/m20221020_000014_create_dex_mean_price_table.rs index 5c7117c2..a3b03c88 100644 --- a/indexer/migration/src/m20221020_000014_create_dex_mean_price_table.rs +++ b/indexer/migration/src/m20221020_000014_create_dex_mean_price_table.rs @@ -44,6 +44,7 @@ impl MigrationTrait for Migration { .to(Address, AddressColumn::Id) .on_delete(ForeignKeyAction::Cascade), ) + .col(ColumnDef::new(Column::Dex).big_integer().not_null()) .col(ColumnDef::new(Column::Asset1Id).big_integer()) .foreign_key( ForeignKey::create() diff --git a/indexer/migration/src/m20221031_000015_create_dex_swap_table.rs b/indexer/migration/src/m20221031_000015_create_dex_swap_table.rs index 353612d1..5a2de8ff 100644 --- a/indexer/migration/src/m20221031_000015_create_dex_swap_table.rs +++ b/indexer/migration/src/m20221031_000015_create_dex_swap_table.rs @@ -44,6 +44,7 @@ impl MigrationTrait for Migration { .to(Address, AddressColumn::Id) .on_delete(ForeignKeyAction::Cascade), ) + .col(ColumnDef::new(Column::Dex).big_integer().not_null()) .col(ColumnDef::new(Column::Asset1Id).big_integer()) .foreign_key( ForeignKey::create() diff --git a/indexer/tasks/src/multiera/dex/common.rs b/indexer/tasks/src/multiera/dex/common.rs index 2053c4cb..99f382e2 100644 --- a/indexer/tasks/src/multiera/dex/common.rs +++ b/indexer/tasks/src/multiera/dex/common.rs @@ -79,6 +79,7 @@ pub fn filter_outputs_and_datums_by_address<'b>( pub struct QueuedMeanPrice { pub tx_id: i64, pub address: Vec, // pallas::crypto::hash::Hash<32> + pub pool_type: PoolType, pub asset1: AssetPair, pub asset2: AssetPair, pub amount1: u64, @@ -88,6 +89,7 @@ pub struct QueuedMeanPrice { pub struct QueuedSwap { pub tx_id: i64, pub address: Vec, // pallas::crypto::hash::Hash<32> + pub pool_type: PoolType, pub asset1: AssetPair, pub asset2: AssetPair, pub amount1: u64, @@ -142,7 +144,7 @@ impl Dex for Empty { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum PoolType { WingRidersV1, SundaeSwapV1, @@ -150,6 +152,17 @@ pub enum PoolType { MinSwapV2, } +impl From for i32 { + fn from(item: PoolType) -> Self { + match item { + PoolType::WingRidersV1 => 0, + PoolType::SundaeSwapV1 => 1, + PoolType::MinSwapV1 => 2, + PoolType::MinSwapV2 => 3, + } + } +} + struct PoolConfig { pub pool_type: PoolType, } @@ -228,6 +241,7 @@ pub async fn handle_mean_price( DexMeanPrice::insert_many(queued_prices.iter().map(|price| DexMeanPriceActiveModel { tx_id: Set(price.tx_id), address_id: Set(multiera_addresses[&price.address].model.id), + dex: Set(i32::from(price.pool_type.clone())), asset1_id: Set(asset_pair_to_id_map[&price.asset1]), asset2_id: Set(asset_pair_to_id_map[&price.asset2]), amount1: Set(price.amount1), @@ -322,6 +336,7 @@ pub async fn handle_swap( DexSwap::insert_many(queued_swaps.iter().map(|price| DexSwapActiveModel { tx_id: Set(price.tx_id), address_id: Set(multiera_addresses[&price.address].model.id), + dex: Set(i32::from(price.pool_type.clone())), asset1_id: Set(asset_pair_to_id_map[&price.asset1]), asset2_id: Set(asset_pair_to_id_map[&price.asset2]), amount1: Set(price.amount1), diff --git a/indexer/tasks/src/multiera/dex/minswap_v1.rs b/indexer/tasks/src/multiera/dex/minswap_v1.rs index 5ab43896..0de1e161 100644 --- a/indexer/tasks/src/multiera/dex/minswap_v1.rs +++ b/indexer/tasks/src/multiera/dex/minswap_v1.rs @@ -13,7 +13,7 @@ use crate::{ use super::common::{ build_asset, filter_outputs_and_datums_by_address, filter_outputs_and_datums_by_hash, - reduce_ada_amount, Dex, MinSwapV1, QueuedMeanPrice, QueuedSwap, + reduce_ada_amount, Dex, MinSwapV1, PoolType, QueuedMeanPrice, QueuedSwap, }; pub const POOL_SCRIPT_HASH1: &str = "e1317b152faac13426e6a83e06ff88a4d62cce3c1634ab0a5ec13309"; @@ -56,6 +56,7 @@ impl Dex for MinSwapV1 { queued_prices.push(QueuedMeanPrice { tx_id, address: output.address().unwrap().to_vec(), + pool_type: PoolType::MinSwapV1, asset1, asset2, amount1, @@ -172,6 +173,7 @@ impl Dex for MinSwapV1 { queued_swaps.push(QueuedSwap { tx_id, address: main_output.address().unwrap().to_vec(), + pool_type: PoolType::MinSwapV1, asset1: asset1.clone(), asset2: asset2.clone(), amount1, diff --git a/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs b/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs index 68ec326d..daf53526 100644 --- a/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs +++ b/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs @@ -13,7 +13,7 @@ use crate::{ use super::common::{ build_asset, filter_outputs_and_datums_by_address, filter_outputs_and_datums_by_hash, - reduce_ada_amount, Dex, QueuedMeanPrice, QueuedSwap, SundaeSwapV1, + reduce_ada_amount, Dex, PoolType, QueuedMeanPrice, QueuedSwap, SundaeSwapV1, }; pub const POOL_SCRIPT_HASH: &str = "4020e7fc2de75a0729c3cc3af715b34d98381e0cdbcfa99c950bc3ac"; @@ -54,6 +54,7 @@ impl Dex for SundaeSwapV1 { queued_prices.push(QueuedMeanPrice { tx_id, address: output.address().unwrap().to_vec(), + pool_type: PoolType::SundaeSwapV1, asset1, asset2, amount1, @@ -162,6 +163,7 @@ impl Dex for SundaeSwapV1 { queued_swaps.push(QueuedSwap { tx_id, address: main_output.address().unwrap().to_vec(), + pool_type: PoolType::SundaeSwapV1, asset1: asset1.clone(), asset2: asset2.clone(), amount1, diff --git a/indexer/tasks/src/multiera/dex/wingriders_v1.rs b/indexer/tasks/src/multiera/dex/wingriders_v1.rs index 93645fb0..19769590 100644 --- a/indexer/tasks/src/multiera/dex/wingriders_v1.rs +++ b/indexer/tasks/src/multiera/dex/wingriders_v1.rs @@ -12,8 +12,8 @@ use crate::{ }; use super::common::{ - build_asset, filter_outputs_and_datums_by_hash, reduce_ada_amount, Dex, QueuedMeanPrice, - QueuedSwap, WingRidersV1, + build_asset, filter_outputs_and_datums_by_hash, reduce_ada_amount, Dex, PoolType, + QueuedMeanPrice, QueuedSwap, WingRidersV1, }; const POOL_SCRIPT_HASH: &str = "e6c90a5923713af5786963dee0fdffd830ca7e0c86a041d9e5833e91"; @@ -65,6 +65,7 @@ impl Dex for WingRidersV1 { queued_prices.push(QueuedMeanPrice { tx_id, address: output.address().unwrap().to_vec(), + pool_type: PoolType::WingRidersV1, asset1, asset2, amount1, @@ -177,6 +178,7 @@ impl Dex for WingRidersV1 { queued_swaps.push(QueuedSwap { tx_id, address: pool_output.address().unwrap().to_vec(), + pool_type: PoolType::WingRidersV1, asset1, asset2, amount1, diff --git a/webserver/server/app/models/dex/sqlDexLastPriceMean.queries.ts b/webserver/server/app/models/dex/sqlDexLastPriceMean.queries.ts index 82d0527d..66a1a275 100644 --- a/webserver/server/app/models/dex/sqlDexLastPriceMean.queries.ts +++ b/webserver/server/app/models/dex/sqlDexLastPriceMean.queries.ts @@ -17,7 +17,7 @@ export interface ISqlDexLastPriceMeanResult { amount2: string; asset_name1: Buffer | null; asset_name2: Buffer | null; - dex: string | null; + dex: string; policy_id1: Buffer | null; policy_id2: Buffer | null; } @@ -28,7 +28,7 @@ export interface ISqlDexLastPriceMeanQuery { result: ISqlDexLastPriceMeanResult; } -const sqlDexLastPriceMeanIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n DISTINCT ON(\"DexMeanPrice\".address_id)\n\n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"DexMeanPrice\".amount1,\n \"DexMeanPrice\".amount2,\n 'WingRiders' || address_id as dex\nFROM \"DexMeanPrice\"\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"DexMeanPrice\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"DexMeanPrice\".asset2_id\nWHERE\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n -- Add swap for another direction\n OR\n (\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea),\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\nORDER BY \"DexMeanPrice\".address_id, \"DexMeanPrice\".tx_id, \"DexMeanPrice\".id"}; +const sqlDexLastPriceMeanIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n DISTINCT ON(\"DexMeanPrice\".address_id)\n\n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"DexMeanPrice\".amount1,\n \"DexMeanPrice\".amount2,\n \"DexMeanPrice\".dex\nFROM \"DexMeanPrice\"\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"DexMeanPrice\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"DexMeanPrice\".asset2_id\nWHERE\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n -- Add swap for another direction\n OR\n (\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea),\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\nORDER BY \"DexMeanPrice\".address_id, \"DexMeanPrice\".tx_id DESC, \"DexMeanPrice\".id DESC"}; /** * Query generated from SQL: @@ -53,7 +53,7 @@ const sqlDexLastPriceMeanIR: any = {"usedParamSet":{"policy_id1":true,"asset_nam * "Asset2".asset_name AS "asset_name2?", * "DexMeanPrice".amount1, * "DexMeanPrice".amount2, - * 'WingRiders' || address_id as dex + * "DexMeanPrice".dex * FROM "DexMeanPrice" * LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "DexMeanPrice".asset1_id * LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "DexMeanPrice".asset2_id @@ -72,7 +72,7 @@ const sqlDexLastPriceMeanIR: any = {"usedParamSet":{"policy_id1":true,"asset_nam * COALESCE("Asset1".policy_id, ''::bytea), * COALESCE("Asset1".asset_name, ''::bytea) * ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") - * ORDER BY "DexMeanPrice".address_id, "DexMeanPrice".tx_id, "DexMeanPrice".id + * ORDER BY "DexMeanPrice".address_id, "DexMeanPrice".tx_id DESC, "DexMeanPrice".id DESC * ``` */ export const sqlDexLastPriceMean = new PreparedQuery(sqlDexLastPriceMeanIR); diff --git a/webserver/server/app/models/dex/sqlDexLastPriceMean.sql b/webserver/server/app/models/dex/sqlDexLastPriceMean.sql index 5d0f4513..3363efac 100644 --- a/webserver/server/app/models/dex/sqlDexLastPriceMean.sql +++ b/webserver/server/app/models/dex/sqlDexLastPriceMean.sql @@ -23,7 +23,7 @@ SELECT "Asset2".asset_name AS "asset_name2?", "DexMeanPrice".amount1, "DexMeanPrice".amount2, - 'WingRiders' || address_id as dex + "DexMeanPrice".dex FROM "DexMeanPrice" LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "DexMeanPrice".asset1_id LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "DexMeanPrice".asset2_id diff --git a/webserver/server/app/models/dex/sqlDexLastPriceSwap.queries.ts b/webserver/server/app/models/dex/sqlDexLastPriceSwap.queries.ts index b341a77d..442907c0 100644 --- a/webserver/server/app/models/dex/sqlDexLastPriceSwap.queries.ts +++ b/webserver/server/app/models/dex/sqlDexLastPriceSwap.queries.ts @@ -18,7 +18,7 @@ export interface ISqlDexLastPriceSwapResult { amount2: string; asset_name1: Buffer | null; asset_name2: Buffer | null; - dex: string | null; + dex: string; policy_id1: Buffer | null; policy_id2: Buffer | null; } @@ -29,7 +29,7 @@ export interface ISqlDexLastPriceSwapQuery { result: ISqlDexLastPriceSwapResult; } -const sqlDexLastPriceSwapIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true,"direction":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]},{"name":"direction","required":false,"transform":{"type":"scalar"},"locs":[{"a":1307,"b":1316},{"a":1678,"b":1687}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n DISTINCT ON(\"DexSwap\".address_id)\n\n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"DexSwap\".amount1,\n \"DexSwap\".amount2,\n 'WingRiders' || address_id as dex\nFROM \"DexSwap\"\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"DexSwap\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"DexSwap\".asset2_id\nWHERE\n (\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n AND \"DexSwap\".direction = :direction\n )\n -- Add swap for another direction\n OR\n (\n (\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea),\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n AND \"DexSwap\".direction != :direction\n )\nORDER BY \"DexSwap\".address_id, \"DexSwap\".tx_id, \"DexSwap\".id"}; +const sqlDexLastPriceSwapIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true,"direction":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]},{"name":"direction","required":false,"transform":{"type":"scalar"},"locs":[{"a":1287,"b":1296},{"a":1658,"b":1667}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n DISTINCT ON(\"DexSwap\".address_id)\n\n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"DexSwap\".amount1,\n \"DexSwap\".amount2,\n \"DexSwap\".dex\nFROM \"DexSwap\"\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"DexSwap\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"DexSwap\".asset2_id\nWHERE\n (\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n AND \"DexSwap\".direction = :direction\n )\n -- Add swap for another direction\n OR\n (\n (\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea),\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n AND \"DexSwap\".direction != :direction\n )\nORDER BY \"DexSwap\".address_id, \"DexSwap\".tx_id DESC, \"DexSwap\".id DESC"}; /** * Query generated from SQL: @@ -54,7 +54,7 @@ const sqlDexLastPriceSwapIR: any = {"usedParamSet":{"policy_id1":true,"asset_nam * "Asset2".asset_name AS "asset_name2?", * "DexSwap".amount1, * "DexSwap".amount2, - * 'WingRiders' || address_id as dex + * "DexSwap".dex * FROM "DexSwap" * LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "DexSwap".asset1_id * LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "DexSwap".asset2_id @@ -79,7 +79,7 @@ const sqlDexLastPriceSwapIR: any = {"usedParamSet":{"policy_id1":true,"asset_nam * ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") * AND "DexSwap".direction != :direction * ) - * ORDER BY "DexSwap".address_id, "DexSwap".tx_id, "DexSwap".id + * ORDER BY "DexSwap".address_id, "DexSwap".tx_id DESC, "DexSwap".id DESC * ``` */ export const sqlDexLastPriceSwap = new PreparedQuery(sqlDexLastPriceSwapIR); diff --git a/webserver/server/app/models/dex/sqlDexLastPriceSwap.sql b/webserver/server/app/models/dex/sqlDexLastPriceSwap.sql index db872998..685c487a 100644 --- a/webserver/server/app/models/dex/sqlDexLastPriceSwap.sql +++ b/webserver/server/app/models/dex/sqlDexLastPriceSwap.sql @@ -23,7 +23,7 @@ SELECT "Asset2".asset_name AS "asset_name2?", "DexSwap".amount1, "DexSwap".amount2, - 'WingRiders' || address_id as dex + "DexSwap".dex FROM "DexSwap" LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "DexSwap".asset1_id LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "DexSwap".asset2_id diff --git a/webserver/server/app/services/DexLastPrice.ts b/webserver/server/app/services/DexLastPrice.ts index 5273f7e7..6d1c3032 100644 --- a/webserver/server/app/services/DexLastPrice.ts +++ b/webserver/server/app/services/DexLastPrice.ts @@ -5,6 +5,15 @@ import { sqlDexLastPriceMean } from '../models/dex/sqlDexLastPriceMean.queries'; import { parseAssetItem, serializeAsset} from './utils'; +function dexToString(dex: string): string { + switch(dex) { + case '0': return 'WingRiders'; + case '1': return 'SundaeSwap'; + case '2': return 'MinSwap'; + } + return 'Unknown'; +} + export async function dexLastPrice( request: { dbTx: PoolClient; @@ -51,7 +60,7 @@ export async function dexLastPrice( asset2: serializeAsset(result.policy_id2, result.asset_name2), amount1: result.amount1, amount2: result.amount2, - dex: String(result.dex), + dex: dexToString(result.dex) })), }; } From bcad44547808d754012f5f84bcea634e38a93ec1 Mon Sep 17 00:00:00 2001 From: Martin Miksanik Date: Thu, 17 Nov 2022 15:13:20 +0100 Subject: [PATCH 33/41] Replace address - dex and add enums --- .../app/controllers/DexLastPriceController.ts | 2 +- .../app/controllers/DexMeanPriceController.ts | 27 ++------------ .../app/controllers/DexSwapController.ts | 27 ++------------ .../app/models/dex/sqlDexMeanPrice.queries.ts | 32 +++++++++++------ .../server/app/models/dex/sqlDexMeanPrice.sql | 24 ++++++++----- .../app/models/dex/sqlDexSwap.queries.ts | 35 ++++++++++++------- .../server/app/models/dex/sqlDexSwap.sql | 27 +++++++++----- webserver/server/app/services/DexLastPrice.ts | 14 ++------ webserver/server/app/services/DexMeanPrice.ts | 13 +++---- webserver/server/app/services/DexSwap.ts | 14 ++++---- webserver/server/app/services/utils.ts | 21 ++++++++++- webserver/shared/models/DexLastPrice.ts | 2 +- webserver/shared/models/DexMeanPrice.ts | 7 ++-- webserver/shared/models/DexSwap.ts | 9 +++-- webserver/shared/models/common.ts | 12 +++++++ 15 files changed, 140 insertions(+), 126 deletions(-) diff --git a/webserver/server/app/controllers/DexLastPriceController.ts b/webserver/server/app/controllers/DexLastPriceController.ts index e586cbeb..30a4815a 100644 --- a/webserver/server/app/controllers/DexLastPriceController.ts +++ b/webserver/server/app/controllers/DexLastPriceController.ts @@ -18,7 +18,7 @@ const route = Routes.dexLastPrice; @Route('dex/last-price') export class DexLastPriceController extends Controller { /** - * Gets the swap prices for the given liquidity pool addresses and asset pairs. + * Gets the swap prices for the given liquidity pool and asset pairs. */ @SuccessResponse(`${StatusCodes.OK}`) @Post() diff --git a/webserver/server/app/controllers/DexMeanPriceController.ts b/webserver/server/app/controllers/DexMeanPriceController.ts index 80877b79..5338f2ac 100644 --- a/webserver/server/app/controllers/DexMeanPriceController.ts +++ b/webserver/server/app/controllers/DexMeanPriceController.ts @@ -10,7 +10,6 @@ import { Errors } from '../../../shared/errors'; import { expectType } from 'tsd'; import type { EndpointTypes } from '../../../shared/routes'; import { Routes } from '../../../shared/routes'; -import { getAddressTypes } from '../models/utils'; import type { DexMeanPriceResponse } from '../../../shared/models/DexMeanPrice'; import { dexMeanPrices } from '../services/DexMeanPrice'; @@ -19,7 +18,7 @@ const route = Routes.dexMeanPrice; @Route('dex/mean-price') export class DexMeanPriceController extends Controller { /** - * Gets the mean prices for the given liquidity pool addresses and asset pairs. + * Gets the mean prices for the given liquidity pool and asset pairs. */ @SuccessResponse(`${StatusCodes.OK}`) @Post() @@ -32,27 +31,6 @@ export class DexMeanPriceController extends Controller { ErrorShape > ): Promise { - if (requestBody.addresses.length > DEX_PRICE_LIMIT.REQUEST_ADDRESSES) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return errorResponse( - StatusCodes.BAD_REQUEST, - genErrorMessage(Errors.AddressLimitExceeded, { - limit: DEX_PRICE_LIMIT.REQUEST_ADDRESSES, - found: requestBody.addresses.length, - }) - ); - } - const addressTypes = getAddressTypes(requestBody.addresses); - if (addressTypes.invalid.length > 0) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return errorResponse( - StatusCodes.UNPROCESSABLE_ENTITY, - genErrorMessage(Errors.IncorrectAddressFormat, { - addresses: addressTypes.invalid, - }) - ); - } - if (requestBody.assetPairs.length > DEX_PRICE_LIMIT.REQUEST_ASSET_PAIRS) { // eslint-disable-next-line @typescript-eslint/no-unsafe-return return errorResponse( @@ -98,8 +76,7 @@ export class DexMeanPriceController extends Controller { after: pageStart, until, dbTx, - addresses: addressTypes.exactAddress.map(addr => Buffer.from(addr, 'hex')), - reverseMap: addressTypes.reverseMap, + dexes: requestBody.dexes, assetPairs: requestBody.assetPairs, limit: requestBody.limit ?? DEX_PRICE_LIMIT.RESPONSE, }); diff --git a/webserver/server/app/controllers/DexSwapController.ts b/webserver/server/app/controllers/DexSwapController.ts index a29afa8e..746f5567 100644 --- a/webserver/server/app/controllers/DexSwapController.ts +++ b/webserver/server/app/controllers/DexSwapController.ts @@ -10,7 +10,6 @@ import { Errors } from '../../../shared/errors'; import { expectType } from 'tsd'; import type { EndpointTypes } from '../../../shared/routes'; import { Routes } from '../../../shared/routes'; -import { getAddressTypes } from '../models/utils'; import type { DexSwapResponse } from '../../../shared/models/DexSwap'; import { dexSwap } from '../services/DexSwap'; @@ -19,7 +18,7 @@ const route = Routes.dexSwap; @Route('dex/swap-price') export class DexSwapController extends Controller { /** - * Gets the swap prices for the given liquidity pool addresses and asset pairs. + * Gets the swap prices for the given liquidity pool and asset pairs. */ @SuccessResponse(`${StatusCodes.OK}`) @Post() @@ -32,27 +31,6 @@ export class DexSwapController extends Controller { ErrorShape > ): Promise { - if (requestBody.addresses.length > DEX_PRICE_LIMIT.REQUEST_ADDRESSES) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return errorResponse( - StatusCodes.BAD_REQUEST, - genErrorMessage(Errors.AddressLimitExceeded, { - limit: DEX_PRICE_LIMIT.REQUEST_ADDRESSES, - found: requestBody.addresses.length, - }) - ); - } - const addressTypes = getAddressTypes(requestBody.addresses); - if (addressTypes.invalid.length > 0) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return errorResponse( - StatusCodes.UNPROCESSABLE_ENTITY, - genErrorMessage(Errors.IncorrectAddressFormat, { - addresses: addressTypes.invalid, - }) - ); - } - if (requestBody.assetPairs.length > DEX_PRICE_LIMIT.REQUEST_ASSET_PAIRS) { // eslint-disable-next-line @typescript-eslint/no-unsafe-return return errorResponse( @@ -98,9 +76,8 @@ export class DexSwapController extends Controller { after: pageStart, until, dbTx, - addresses: addressTypes.exactAddress.map(addr => Buffer.from(addr, 'hex')), - reverseMap: addressTypes.reverseMap, assetPairs: requestBody.assetPairs, + dexes: requestBody.dexes, limit: requestBody.limit ?? DEX_PRICE_LIMIT.RESPONSE, }); } diff --git a/webserver/server/app/models/dex/sqlDexMeanPrice.queries.ts b/webserver/server/app/models/dex/sqlDexMeanPrice.queries.ts index 8dd326c4..87b7a92b 100644 --- a/webserver/server/app/models/dex/sqlDexMeanPrice.queries.ts +++ b/webserver/server/app/models/dex/sqlDexMeanPrice.queries.ts @@ -3,12 +3,14 @@ import { PreparedQuery } from '@pgtyped/query'; export type BufferArray = (Buffer)[]; +export type stringArray = (string)[]; + /** 'SqlDexMeanPrice' parameters type */ export interface ISqlDexMeanPriceParams { - addresses: BufferArray | null | void; after_tx_id: string | null | void; asset_name1: BufferArray | null | void; asset_name2: BufferArray | null | void; + dexes: stringArray | null | void; limit: string | null | void; policy_id1: BufferArray | null | void; policy_id2: BufferArray | null | void; @@ -17,11 +19,11 @@ export interface ISqlDexMeanPriceParams { /** 'SqlDexMeanPrice' return type */ export interface ISqlDexMeanPriceResult { - address: Buffer; amount1: string; amount2: string; asset_name1: Buffer | null; asset_name2: Buffer | null; + dex: string; policy_id1: Buffer | null; policy_id2: Buffer | null; tx_hash: Buffer; @@ -33,7 +35,7 @@ export interface ISqlDexMeanPriceQuery { result: ISqlDexMeanPriceResult; } -const sqlDexMeanPriceIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true,"addresses":true,"until_tx_id":true,"after_tx_id":true,"limit":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]},{"name":"addresses","required":false,"transform":{"type":"scalar"},"locs":[{"a":1157,"b":1166}]},{"name":"until_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":1476,"b":1487}]},{"name":"after_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":1522,"b":1533}]},{"name":"limit","required":false,"transform":{"type":"scalar"},"locs":[{"a":1592,"b":1597}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n \"Transaction\".hash AS tx_hash,\n \"Address\".payload AS address,\n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"DexMeanPrice\".amount1,\n \"DexMeanPrice\".amount2\nFROM \"DexMeanPrice\"\nJOIN \"Transaction\" ON \"Transaction\".id = \"DexMeanPrice\".tx_id\nJOIN \"Address\" ON \"Address\".id = \"DexMeanPrice\".address_id\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"DexMeanPrice\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"DexMeanPrice\".asset2_id\nWHERE\n \"Address\".payload = ANY (:addresses)\n AND\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n AND\n \"DexMeanPrice\".tx_id <= (:until_tx_id)\n AND\n \"DexMeanPrice\".tx_id > (:after_tx_id)\nORDER BY \"DexMeanPrice\".tx_id, \"DexMeanPrice\".id\nLIMIT (:limit)"}; +const sqlDexMeanPriceIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true,"dexes":true,"until_tx_id":true,"after_tx_id":true,"limit":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]},{"name":"dexes","required":false,"transform":{"type":"scalar"},"locs":[{"a":1096,"b":1101}]},{"name":"until_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":1718,"b":1729}]},{"name":"after_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":1764,"b":1775}]},{"name":"limit","required":false,"transform":{"type":"scalar"},"locs":[{"a":1834,"b":1839}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n \"Transaction\".hash AS tx_hash,\n \"DexMeanPrice\".dex as dex,\n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"DexMeanPrice\".amount1,\n \"DexMeanPrice\".amount2\nFROM \"DexMeanPrice\"\nJOIN \"Transaction\" ON \"Transaction\".id = \"DexMeanPrice\".tx_id\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"DexMeanPrice\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"DexMeanPrice\".asset2_id\nWHERE\n \"DexMeanPrice\".dex = ANY (:dexes)\n AND\n (\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n OR\n (\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea),\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n )\n AND\n \"DexMeanPrice\".tx_id <= (:until_tx_id)\n AND\n \"DexMeanPrice\".tx_id > (:after_tx_id)\nORDER BY \"DexMeanPrice\".tx_id, \"DexMeanPrice\".id\nLIMIT (:limit)"}; /** * Query generated from SQL: @@ -51,7 +53,7 @@ const sqlDexMeanPriceIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1": * ) * SELECT * "Transaction".hash AS tx_hash, - * "Address".payload AS address, + * "DexMeanPrice".dex as dex, * "Asset1".policy_id AS "policy_id1?", * "Asset1".asset_name AS "asset_name1?", * "Asset2".policy_id AS "policy_id2?", @@ -60,18 +62,26 @@ const sqlDexMeanPriceIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1": * "DexMeanPrice".amount2 * FROM "DexMeanPrice" * JOIN "Transaction" ON "Transaction".id = "DexMeanPrice".tx_id - * JOIN "Address" ON "Address".id = "DexMeanPrice".address_id * LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "DexMeanPrice".asset1_id * LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "DexMeanPrice".asset2_id * WHERE - * "Address".payload = ANY (:addresses) + * "DexMeanPrice".dex = ANY (:dexes) * AND * ( - * COALESCE("Asset1".policy_id, ''::bytea), - * COALESCE("Asset1".asset_name, ''::bytea), - * COALESCE("Asset2".policy_id, ''::bytea), - * COALESCE("Asset2".asset_name, ''::bytea) - * ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") + * ( + * COALESCE("Asset1".policy_id, ''::bytea), + * COALESCE("Asset1".asset_name, ''::bytea), + * COALESCE("Asset2".policy_id, ''::bytea), + * COALESCE("Asset2".asset_name, ''::bytea) + * ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") + * OR + * ( + * COALESCE("Asset2".policy_id, ''::bytea), + * COALESCE("Asset2".asset_name, ''::bytea), + * COALESCE("Asset1".policy_id, ''::bytea), + * COALESCE("Asset1".asset_name, ''::bytea) + * ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") + * ) * AND * "DexMeanPrice".tx_id <= (:until_tx_id) * AND diff --git a/webserver/server/app/models/dex/sqlDexMeanPrice.sql b/webserver/server/app/models/dex/sqlDexMeanPrice.sql index c3f28cb2..52fe5a0a 100644 --- a/webserver/server/app/models/dex/sqlDexMeanPrice.sql +++ b/webserver/server/app/models/dex/sqlDexMeanPrice.sql @@ -16,7 +16,7 @@ WITH "AssetPairs" AS ( ) SELECT "Transaction".hash AS tx_hash, - "Address".payload AS address, + "DexMeanPrice".dex as dex, "Asset1".policy_id AS "policy_id1?", "Asset1".asset_name AS "asset_name1?", "Asset2".policy_id AS "policy_id2?", @@ -25,18 +25,26 @@ SELECT "DexMeanPrice".amount2 FROM "DexMeanPrice" JOIN "Transaction" ON "Transaction".id = "DexMeanPrice".tx_id -JOIN "Address" ON "Address".id = "DexMeanPrice".address_id LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "DexMeanPrice".asset1_id LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "DexMeanPrice".asset2_id WHERE - "Address".payload = ANY (:addresses) + "DexMeanPrice".dex = ANY (:dexes) AND ( - COALESCE("Asset1".policy_id, ''::bytea), - COALESCE("Asset1".asset_name, ''::bytea), - COALESCE("Asset2".policy_id, ''::bytea), - COALESCE("Asset2".asset_name, ''::bytea) - ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") + ( + COALESCE("Asset1".policy_id, ''::bytea), + COALESCE("Asset1".asset_name, ''::bytea), + COALESCE("Asset2".policy_id, ''::bytea), + COALESCE("Asset2".asset_name, ''::bytea) + ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") + OR + ( + COALESCE("Asset2".policy_id, ''::bytea), + COALESCE("Asset2".asset_name, ''::bytea), + COALESCE("Asset1".policy_id, ''::bytea), + COALESCE("Asset1".asset_name, ''::bytea) + ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") + ) AND "DexMeanPrice".tx_id <= (:until_tx_id) AND diff --git a/webserver/server/app/models/dex/sqlDexSwap.queries.ts b/webserver/server/app/models/dex/sqlDexSwap.queries.ts index e3cf6435..916d191f 100644 --- a/webserver/server/app/models/dex/sqlDexSwap.queries.ts +++ b/webserver/server/app/models/dex/sqlDexSwap.queries.ts @@ -3,12 +3,14 @@ import { PreparedQuery } from '@pgtyped/query'; export type BufferArray = (Buffer)[]; +export type stringArray = (string)[]; + /** 'SqlDexSwap' parameters type */ export interface ISqlDexSwapParams { - addresses: BufferArray | null | void; after_tx_id: string | null | void; asset_name1: BufferArray | null | void; asset_name2: BufferArray | null | void; + dexes: stringArray | null | void; limit: string | null | void; policy_id1: BufferArray | null | void; policy_id2: BufferArray | null | void; @@ -17,11 +19,11 @@ export interface ISqlDexSwapParams { /** 'SqlDexSwap' return type */ export interface ISqlDexSwapResult { - address: Buffer; amount1: string; amount2: string; asset_name1: Buffer | null; asset_name2: Buffer | null; + dex: string; direction: boolean; policy_id1: Buffer | null; policy_id2: Buffer | null; @@ -34,7 +36,7 @@ export interface ISqlDexSwapQuery { result: ISqlDexSwapResult; } -const sqlDexSwapIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true,"addresses":true,"until_tx_id":true,"after_tx_id":true,"limit":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]},{"name":"addresses","required":false,"transform":{"type":"scalar"},"locs":[{"a":1145,"b":1154}]},{"name":"until_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":1459,"b":1470}]},{"name":"after_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":1500,"b":1511}]},{"name":"limit","required":false,"transform":{"type":"scalar"},"locs":[{"a":1560,"b":1565}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n \"Transaction\".hash AS tx_hash,\n \"Address\".payload AS address,\n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"DexSwap\".amount1,\n \"DexSwap\".amount2,\n \"DexSwap\".direction\nFROM \"DexSwap\"\nJOIN \"Transaction\" ON \"Transaction\".id = \"DexSwap\".tx_id\nJOIN \"Address\" ON \"Address\".id = \"DexSwap\".address_id\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"DexSwap\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"DexSwap\".asset2_id\nWHERE\n \"Address\".payload = ANY (:addresses)\n AND\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n AND\n \"DexSwap\".tx_id <= (:until_tx_id)\n AND\n \"DexSwap\".tx_id > (:after_tx_id)\nORDER BY \"DexSwap\".tx_id, \"DexSwap\".id\nLIMIT (:limit)"}; +const sqlDexSwapIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true,"dexes":true,"until_tx_id":true,"after_tx_id":true,"limit":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]},{"name":"dexes","required":false,"transform":{"type":"scalar"},"locs":[{"a":1128,"b":1133}]},{"name":"until_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":1745,"b":1756}]},{"name":"after_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":1786,"b":1797}]},{"name":"limit","required":false,"transform":{"type":"scalar"},"locs":[{"a":1846,"b":1851}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n \"Transaction\".hash AS tx_hash, \n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"DexSwap\".amount1,\n \"DexSwap\".amount2,\n \"DexSwap\".direction,\n \"DexSwap\".dex\nFROM \"DexSwap\"\nJOIN \"Transaction\" ON \"Transaction\".id = \"DexSwap\".tx_id\nJOIN \"Address\" ON \"Address\".id = \"DexSwap\".address_id\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"DexSwap\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"DexSwap\".asset2_id\nWHERE\n \"DexSwap\".dex = ANY (:dexes)\n AND\n (\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n OR\n (\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea),\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n )\n AND\n \"DexSwap\".tx_id <= (:until_tx_id)\n AND\n \"DexSwap\".tx_id > (:after_tx_id)\nORDER BY \"DexSwap\".tx_id, \"DexSwap\".id\nLIMIT (:limit)"}; /** * Query generated from SQL: @@ -51,29 +53,38 @@ const sqlDexSwapIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true, * ) x(policy_id1, asset_name1, policy_id2, asset_name2) * ) * SELECT - * "Transaction".hash AS tx_hash, - * "Address".payload AS address, + * "Transaction".hash AS tx_hash, * "Asset1".policy_id AS "policy_id1?", * "Asset1".asset_name AS "asset_name1?", * "Asset2".policy_id AS "policy_id2?", * "Asset2".asset_name AS "asset_name2?", * "DexSwap".amount1, * "DexSwap".amount2, - * "DexSwap".direction + * "DexSwap".direction, + * "DexSwap".dex * FROM "DexSwap" * JOIN "Transaction" ON "Transaction".id = "DexSwap".tx_id * JOIN "Address" ON "Address".id = "DexSwap".address_id * LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "DexSwap".asset1_id * LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "DexSwap".asset2_id * WHERE - * "Address".payload = ANY (:addresses) + * "DexSwap".dex = ANY (:dexes) * AND * ( - * COALESCE("Asset1".policy_id, ''::bytea), - * COALESCE("Asset1".asset_name, ''::bytea), - * COALESCE("Asset2".policy_id, ''::bytea), - * COALESCE("Asset2".asset_name, ''::bytea) - * ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") + * ( + * COALESCE("Asset1".policy_id, ''::bytea), + * COALESCE("Asset1".asset_name, ''::bytea), + * COALESCE("Asset2".policy_id, ''::bytea), + * COALESCE("Asset2".asset_name, ''::bytea) + * ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") + * OR + * ( + * COALESCE("Asset2".policy_id, ''::bytea), + * COALESCE("Asset2".asset_name, ''::bytea), + * COALESCE("Asset1".policy_id, ''::bytea), + * COALESCE("Asset1".asset_name, ''::bytea) + * ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") + * ) * AND * "DexSwap".tx_id <= (:until_tx_id) * AND diff --git a/webserver/server/app/models/dex/sqlDexSwap.sql b/webserver/server/app/models/dex/sqlDexSwap.sql index aafa5292..ee66fcb1 100644 --- a/webserver/server/app/models/dex/sqlDexSwap.sql +++ b/webserver/server/app/models/dex/sqlDexSwap.sql @@ -15,29 +15,38 @@ WITH "AssetPairs" AS ( ) x(policy_id1, asset_name1, policy_id2, asset_name2) ) SELECT - "Transaction".hash AS tx_hash, - "Address".payload AS address, + "Transaction".hash AS tx_hash, "Asset1".policy_id AS "policy_id1?", "Asset1".asset_name AS "asset_name1?", "Asset2".policy_id AS "policy_id2?", "Asset2".asset_name AS "asset_name2?", "DexSwap".amount1, "DexSwap".amount2, - "DexSwap".direction + "DexSwap".direction, + "DexSwap".dex FROM "DexSwap" JOIN "Transaction" ON "Transaction".id = "DexSwap".tx_id JOIN "Address" ON "Address".id = "DexSwap".address_id LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "DexSwap".asset1_id LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "DexSwap".asset2_id WHERE - "Address".payload = ANY (:addresses) + "DexSwap".dex = ANY (:dexes) AND ( - COALESCE("Asset1".policy_id, ''::bytea), - COALESCE("Asset1".asset_name, ''::bytea), - COALESCE("Asset2".policy_id, ''::bytea), - COALESCE("Asset2".asset_name, ''::bytea) - ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") + ( + COALESCE("Asset1".policy_id, ''::bytea), + COALESCE("Asset1".asset_name, ''::bytea), + COALESCE("Asset2".policy_id, ''::bytea), + COALESCE("Asset2".asset_name, ''::bytea) + ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") + OR + ( + COALESCE("Asset2".policy_id, ''::bytea), + COALESCE("Asset2".asset_name, ''::bytea), + COALESCE("Asset1".policy_id, ''::bytea), + COALESCE("Asset1".asset_name, ''::bytea) + ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") + ) AND "DexSwap".tx_id <= (:until_tx_id) AND diff --git a/webserver/server/app/services/DexLastPrice.ts b/webserver/server/app/services/DexLastPrice.ts index 6d1c3032..d859758b 100644 --- a/webserver/server/app/services/DexLastPrice.ts +++ b/webserver/server/app/services/DexLastPrice.ts @@ -1,19 +1,11 @@ import { Asset, PriceType, DexLastPriceResponse } from '../../../shared/models/DexLastPrice'; +import { Dex } from '../../../shared/models/common'; import type { PoolClient } from 'pg'; import { sqlDexLastPriceSwap } from '../models/dex/sqlDexLastPriceSwap.queries'; import { sqlDexLastPriceMean } from '../models/dex/sqlDexLastPriceMean.queries'; -import { parseAssetItem, serializeAsset} from './utils'; +import { parseAssetItem, serializeAsset, valueToDex} from './utils'; -function dexToString(dex: string): string { - switch(dex) { - case '0': return 'WingRiders'; - case '1': return 'SundaeSwap'; - case '2': return 'MinSwap'; - } - return 'Unknown'; -} - export async function dexLastPrice( request: { dbTx: PoolClient; @@ -60,7 +52,7 @@ export async function dexLastPrice( asset2: serializeAsset(result.policy_id2, result.asset_name2), amount1: result.amount1, amount2: result.amount2, - dex: dexToString(result.dex) + dex: valueToDex(result.dex) })), }; } diff --git a/webserver/server/app/services/DexMeanPrice.ts b/webserver/server/app/services/DexMeanPrice.ts index bacabee3..4604a1c2 100644 --- a/webserver/server/app/services/DexMeanPrice.ts +++ b/webserver/server/app/services/DexMeanPrice.ts @@ -1,33 +1,34 @@ import type { Asset, DexMeanPriceResponse } from '../../../shared/models/DexMeanPrice'; +import { Dex } from '../../../shared/models/common'; import type { PoolClient } from 'pg'; import type { TransactionPaginationType } from './PaginationService'; import { sqlDexMeanPrice } from '../models/dex/sqlDexMeanPrice.queries'; -import { parseAssetItem, serializeAsset} from './utils'; +import { parseAssetItem, serializeAsset, valueToDex, dexToValue } from './utils'; export async function dexMeanPrices( request: TransactionPaginationType & { dbTx: PoolClient; - addresses: Buffer[]; - reverseMap: Map>; + dexes: Array; assetPairs: {asset1: Asset, asset2: Asset}[]; limit: number; } ): Promise { - if (request.addresses.length === 0 || request.assetPairs.length === 0) return { meanPrices: [] }; + if (request.assetPairs.length === 0) return { meanPrices: [] }; const meanPrices = await sqlDexMeanPrice.run({ after_tx_id: (request.after?.tx_id ?? -1)?.toString(), until_tx_id: request.until.tx_id.toString(), - addresses: request.addresses, + dexes: request.dexes.map(dex => dexToValue(dex)), policy_id1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.policyId)), asset_name1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.assetName)), policy_id2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.policyId)), asset_name2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.assetName)), limit: request.limit.toString(), }, request.dbTx); + return { meanPrices: meanPrices.map(result => ({ tx_hash: result.tx_hash.toString('hex'), - address: [...(request.reverseMap.get(result.address.toString('hex')) ?? [])][0], + dex: valueToDex(result.dex), asset1: serializeAsset(result.policy_id1, result.asset_name1), asset2: serializeAsset(result.policy_id2, result.asset_name2), amount1: result.amount1, diff --git a/webserver/server/app/services/DexSwap.ts b/webserver/server/app/services/DexSwap.ts index c0f73989..5b7d7267 100644 --- a/webserver/server/app/services/DexSwap.ts +++ b/webserver/server/app/services/DexSwap.ts @@ -2,23 +2,23 @@ import type { Asset, DexSwapResponse } from '../../../shared/models/DexSwap'; import type { PoolClient } from 'pg'; import type { TransactionPaginationType } from './PaginationService'; import { sqlDexSwap } from '../models/dex/sqlDexSwap.queries'; -import { parseAssetItem, serializeAsset} from './utils'; +import { parseAssetItem, serializeAsset, valueToDex, dexToValue} from './utils'; +import { Dex, Direction } from '../../../shared/models/common'; export async function dexSwap( request: TransactionPaginationType & { dbTx: PoolClient; - addresses: Buffer[]; - reverseMap: Map>; + dexes: Array; assetPairs: {asset1: Asset, asset2: Asset}[]; limit: number; } ): Promise { - if (request.addresses.length === 0 || request.assetPairs.length === 0) return { swap: [] }; + if (request.assetPairs.length === 0) return { swap: [] }; const swap = await sqlDexSwap.run({ after_tx_id: (request.after?.tx_id ?? -1)?.toString(), until_tx_id: request.until.tx_id.toString(), - addresses: request.addresses, + dexes: request.dexes.map(dex => dexToValue(dex)), policy_id1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.policyId)), asset_name1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.assetName)), policy_id2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.policyId)), @@ -28,12 +28,12 @@ export async function dexSwap( return { swap: swap.map(result => ({ tx_hash: result.tx_hash.toString('hex'), - address: [...(request.reverseMap.get(result.address.toString('hex')) ?? [])][0], + dex: valueToDex(result.dex), asset1: serializeAsset(result.policy_id1, result.asset_name1), asset2: serializeAsset(result.policy_id2, result.asset_name2), amount1: result.amount1, amount2: result.amount2, - direction: (result.direction ? 'buy' : 'sell') + direction: (result.direction ? Direction.Buy : Direction.Sell) })), }; } diff --git a/webserver/server/app/services/utils.ts b/webserver/server/app/services/utils.ts index 813b3391..a4583d10 100644 --- a/webserver/server/app/services/utils.ts +++ b/webserver/server/app/services/utils.ts @@ -1,4 +1,5 @@ import type { Asset } from '../../../shared/models/DexMeanPrice'; +import { Dex } from '../../../shared/models/common'; export function parseAssetItem(s: string | undefined | null): Buffer { // For the sake of the query, we represent ADA as ('', '') instead of (NULL, NULL). @@ -18,4 +19,22 @@ export function parseAssetItem(s: string | undefined | null): Buffer { } throw new Error('Invalid asset query response'); // should be unreachable } - \ No newline at end of file + + +export function valueToDex(dex: string) { + switch(dex) { + case '0': return Dex.WingRiders; + case '1': return Dex.SundaeSwap; + case '2': return Dex.MinSwap; + } + return Dex.Unknown; +} + +export function dexToValue(dex: Dex) { + switch(dex) { + case Dex.WingRiders: return '0'; + case Dex.SundaeSwap: return '1'; + case Dex.MinSwap: return '2'; + } + return '-1'; +} \ No newline at end of file diff --git a/webserver/shared/models/DexLastPrice.ts b/webserver/shared/models/DexLastPrice.ts index 3b5e57ce..c0af5100 100644 --- a/webserver/shared/models/DexLastPrice.ts +++ b/webserver/shared/models/DexLastPrice.ts @@ -1,4 +1,5 @@ import { AssetName, PolicyId } from "./PolicyIdAssetMap"; +import { Dex } from "./common"; export type Asset = { policyId: PolicyId; @@ -9,7 +10,6 @@ export type Asset = { * @example "2042352568679" */ type Amount = string; // uint64 -type Dex = string; // enum export type DexLastPrice = { asset1: Asset; diff --git a/webserver/shared/models/DexMeanPrice.ts b/webserver/shared/models/DexMeanPrice.ts index 2809034b..0a5c3f14 100644 --- a/webserver/shared/models/DexMeanPrice.ts +++ b/webserver/shared/models/DexMeanPrice.ts @@ -1,5 +1,4 @@ -import { Address } from "./Address"; -import { Pagination } from "./common"; +import { Dex, Pagination } from "./common"; import { AssetName, PolicyId } from "./PolicyIdAssetMap"; export type Asset = { @@ -14,7 +13,7 @@ type Amount = string; // uint64 export type DexMeanPrice = { tx_hash: string; - address: Address; + dex: Dex; asset1: Asset; asset2: Asset; amount1: Amount; @@ -22,8 +21,8 @@ export type DexMeanPrice = { } export type DexMeanPriceRequest = { - addresses: Address[], assetPairs: {asset1: Asset, asset2: Asset}[]; + dexes: Array, /** Defaults to `DEX_PRICE_LIMIT.RESPONSE` */ limit?: number; } & Pagination; diff --git a/webserver/shared/models/DexSwap.ts b/webserver/shared/models/DexSwap.ts index 5ead606e..3b62799c 100644 --- a/webserver/shared/models/DexSwap.ts +++ b/webserver/shared/models/DexSwap.ts @@ -1,5 +1,4 @@ -import { Address } from "./Address"; -import { Pagination } from "./common"; +import { Pagination, Dex, Direction } from "./common"; import { AssetName, PolicyId } from "./PolicyIdAssetMap"; export type Asset = { @@ -11,11 +10,11 @@ export type Asset = { * @example "2042352568679" */ type Amount = string; // uint64 -type Direction = string; + export type DexSwap = { tx_hash: string; - address: Address; + dex: Dex; asset1: Asset; asset2: Asset; amount1: Amount; @@ -24,7 +23,7 @@ export type DexSwap = { } export type DexSwapRequest = { - addresses: Address[], + dexes: Array, assetPairs: {asset1: Asset, asset2: Asset}[]; /** Defaults to `DEX_PRICE_LIMIT.RESPONSE` */ limit?: number; diff --git a/webserver/shared/models/common.ts b/webserver/shared/models/common.ts index 5d313009..18081431 100644 --- a/webserver/shared/models/common.ts +++ b/webserver/shared/models/common.ts @@ -89,3 +89,15 @@ export type PageInfo = { hasNextPage: boolean; }; }; + +export enum Direction { + Buy = 'buy', + Sell = 'sell', +}; + +export enum Dex { + WingRiders = 'WingRiders', + SundaeSwap = 'SundaeSwap', + MinSwap = 'MinSwap', + Unknown = 'Unknown', +}; From 7101eec9bf133b7e175c76f6f3b457024305b0bb Mon Sep 17 00:00:00 2001 From: Martin Miksanik Date: Thu, 17 Nov 2022 15:24:03 +0100 Subject: [PATCH 34/41] Repair trait for dexes --- indexer/tasks/src/multiera/dex/common.rs | 45 +++++++++---------- indexer/tasks/src/multiera/dex/minswap_v1.rs | 6 +-- .../tasks/src/multiera/dex/sundaeswap_v1.rs | 6 +-- .../tasks/src/multiera/dex/wingriders_v1.rs | 6 +-- .../multiera_minswap_v1_mean_price.rs | 4 +- .../src/multiera/multiera_minswap_v1_swap.rs | 4 +- .../multiera_sundaeswap_v1_mean_price.rs | 4 +- .../multiera/multiera_sundaeswap_v1_swap.rs | 4 +- .../multiera_wingriders_v1_mean_price.rs | 12 ++--- .../multiera/multiera_wingriders_v1_swap.rs | 4 +- 10 files changed, 42 insertions(+), 53 deletions(-) diff --git a/indexer/tasks/src/multiera/dex/common.rs b/indexer/tasks/src/multiera/dex/common.rs index 99f382e2..ea29f967 100644 --- a/indexer/tasks/src/multiera/dex/common.rs +++ b/indexer/tasks/src/multiera/dex/common.rs @@ -79,7 +79,7 @@ pub fn filter_outputs_and_datums_by_address<'b>( pub struct QueuedMeanPrice { pub tx_id: i64, pub address: Vec, // pallas::crypto::hash::Hash<32> - pub pool_type: PoolType, + pub dex_type: DexType, pub asset1: AssetPair, pub asset2: AssetPair, pub amount1: u64, @@ -89,7 +89,7 @@ pub struct QueuedMeanPrice { pub struct QueuedSwap { pub tx_id: i64, pub address: Vec, // pallas::crypto::hash::Hash<32> - pub pool_type: PoolType, + pub dex_type: DexType, pub asset1: AssetPair, pub asset2: AssetPair, pub amount1: u64, @@ -145,34 +145,30 @@ impl Dex for Empty { } #[derive(Clone, Debug, PartialEq, Eq)] -pub enum PoolType { +pub enum DexType { WingRidersV1, SundaeSwapV1, MinSwapV1, MinSwapV2, } -impl From for i32 { - fn from(item: PoolType) -> Self { +impl From for i32 { + fn from(item: DexType) -> Self { match item { - PoolType::WingRidersV1 => 0, - PoolType::SundaeSwapV1 => 1, - PoolType::MinSwapV1 => 2, - PoolType::MinSwapV2 => 3, + DexType::WingRidersV1 => 0, + DexType::SundaeSwapV1 => 1, + DexType::MinSwapV1 => 2, + DexType::MinSwapV2 => 3, } } } -struct PoolConfig { - pub pool_type: PoolType, -} - -impl PoolConfig { +impl DexType { fn as_trait(&self) -> &dyn Dex { - match &self.pool_type { - PoolType::WingRidersV1 => &WingRidersV1 {}, - PoolType::MinSwapV1 => &MinSwapV1 {}, - PoolType::SundaeSwapV1 => &SundaeSwapV1 {}, + match &self { + DexType::WingRidersV1 => &WingRidersV1 {}, + DexType::MinSwapV1 => &MinSwapV1 {}, + DexType::SundaeSwapV1 => &SundaeSwapV1 {}, _ => &Empty {}, } } @@ -183,10 +179,10 @@ pub async fn handle_mean_price( block: BlockInfo<'_, MultiEraBlock<'_>>, multiera_txs: &[TransactionModel], multiera_addresses: &BTreeMap, AddressInBlock>, - pool_type: PoolType, + pool_type: DexType, ) -> Result<(), DbErr> { // 1) Parse mean prices - let pool = PoolConfig { pool_type }; + let pool = pool_type; let mean_value_trait = pool.as_trait(); let mut queued_prices = Vec::::default(); for (tx_body, cardano_transaction) in block.1.txs().iter().zip(multiera_txs) { @@ -241,7 +237,7 @@ pub async fn handle_mean_price( DexMeanPrice::insert_many(queued_prices.iter().map(|price| DexMeanPriceActiveModel { tx_id: Set(price.tx_id), address_id: Set(multiera_addresses[&price.address].model.id), - dex: Set(i32::from(price.pool_type.clone())), + dex: Set(i32::from(price.dex_type.clone())), asset1_id: Set(asset_pair_to_id_map[&price.asset1]), asset2_id: Set(asset_pair_to_id_map[&price.asset2]), amount1: Set(price.amount1), @@ -276,11 +272,10 @@ pub async fn handle_swap( multiera_txs: &[TransactionModel], multiera_addresses: &BTreeMap, AddressInBlock>, multiera_used_inputs_to_outputs_map: &BTreeMap, BTreeMap>, - pool_type: PoolType, + dex_type: DexType, ) -> Result<(), DbErr> { // 1) Parse swaps - let pool = PoolConfig { pool_type }; - let swap_trait = pool.as_trait(); + let swap_trait = dex_type.as_trait(); let mut queued_swaps = Vec::::default(); for (tx_body, cardano_transaction) in block.1.txs().iter().zip(multiera_txs) { if cardano_transaction.is_valid { @@ -336,7 +331,7 @@ pub async fn handle_swap( DexSwap::insert_many(queued_swaps.iter().map(|price| DexSwapActiveModel { tx_id: Set(price.tx_id), address_id: Set(multiera_addresses[&price.address].model.id), - dex: Set(i32::from(price.pool_type.clone())), + dex: Set(i32::from(price.dex_type.clone())), asset1_id: Set(asset_pair_to_id_map[&price.asset1]), asset2_id: Set(asset_pair_to_id_map[&price.asset2]), amount1: Set(price.amount1), diff --git a/indexer/tasks/src/multiera/dex/minswap_v1.rs b/indexer/tasks/src/multiera/dex/minswap_v1.rs index 0de1e161..f1e48011 100644 --- a/indexer/tasks/src/multiera/dex/minswap_v1.rs +++ b/indexer/tasks/src/multiera/dex/minswap_v1.rs @@ -13,7 +13,7 @@ use crate::{ use super::common::{ build_asset, filter_outputs_and_datums_by_address, filter_outputs_and_datums_by_hash, - reduce_ada_amount, Dex, MinSwapV1, PoolType, QueuedMeanPrice, QueuedSwap, + reduce_ada_amount, Dex, DexType, MinSwapV1, QueuedMeanPrice, QueuedSwap, }; pub const POOL_SCRIPT_HASH1: &str = "e1317b152faac13426e6a83e06ff88a4d62cce3c1634ab0a5ec13309"; @@ -56,7 +56,7 @@ impl Dex for MinSwapV1 { queued_prices.push(QueuedMeanPrice { tx_id, address: output.address().unwrap().to_vec(), - pool_type: PoolType::MinSwapV1, + dex_type: DexType::MinSwapV1, asset1, asset2, amount1, @@ -173,7 +173,7 @@ impl Dex for MinSwapV1 { queued_swaps.push(QueuedSwap { tx_id, address: main_output.address().unwrap().to_vec(), - pool_type: PoolType::MinSwapV1, + dex_type: DexType::MinSwapV1, asset1: asset1.clone(), asset2: asset2.clone(), amount1, diff --git a/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs b/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs index daf53526..caf2270c 100644 --- a/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs +++ b/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs @@ -13,7 +13,7 @@ use crate::{ use super::common::{ build_asset, filter_outputs_and_datums_by_address, filter_outputs_and_datums_by_hash, - reduce_ada_amount, Dex, PoolType, QueuedMeanPrice, QueuedSwap, SundaeSwapV1, + reduce_ada_amount, Dex, DexType, QueuedMeanPrice, QueuedSwap, SundaeSwapV1, }; pub const POOL_SCRIPT_HASH: &str = "4020e7fc2de75a0729c3cc3af715b34d98381e0cdbcfa99c950bc3ac"; @@ -54,7 +54,7 @@ impl Dex for SundaeSwapV1 { queued_prices.push(QueuedMeanPrice { tx_id, address: output.address().unwrap().to_vec(), - pool_type: PoolType::SundaeSwapV1, + dex_type: DexType::SundaeSwapV1, asset1, asset2, amount1, @@ -163,7 +163,7 @@ impl Dex for SundaeSwapV1 { queued_swaps.push(QueuedSwap { tx_id, address: main_output.address().unwrap().to_vec(), - pool_type: PoolType::SundaeSwapV1, + dex_type: DexType::SundaeSwapV1, asset1: asset1.clone(), asset2: asset2.clone(), amount1, diff --git a/indexer/tasks/src/multiera/dex/wingriders_v1.rs b/indexer/tasks/src/multiera/dex/wingriders_v1.rs index 19769590..e66a8e56 100644 --- a/indexer/tasks/src/multiera/dex/wingriders_v1.rs +++ b/indexer/tasks/src/multiera/dex/wingriders_v1.rs @@ -12,7 +12,7 @@ use crate::{ }; use super::common::{ - build_asset, filter_outputs_and_datums_by_hash, reduce_ada_amount, Dex, PoolType, + build_asset, filter_outputs_and_datums_by_hash, reduce_ada_amount, Dex, DexType, QueuedMeanPrice, QueuedSwap, WingRidersV1, }; @@ -65,7 +65,7 @@ impl Dex for WingRidersV1 { queued_prices.push(QueuedMeanPrice { tx_id, address: output.address().unwrap().to_vec(), - pool_type: PoolType::WingRidersV1, + dex_type: DexType::WingRidersV1, asset1, asset2, amount1, @@ -178,7 +178,7 @@ impl Dex for WingRidersV1 { queued_swaps.push(QueuedSwap { tx_id, address: pool_output.address().unwrap().to_vec(), - pool_type: PoolType::WingRidersV1, + dex_type: DexType::WingRidersV1, asset1, asset2, amount1, diff --git a/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs index 5bb3c973..47f16713 100644 --- a/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs +++ b/indexer/tasks/src/multiera/multiera_minswap_v1_mean_price.rs @@ -1,4 +1,4 @@ -use super::dex::common::{handle_mean_price, PoolType}; +use super::dex::common::{handle_mean_price, DexType}; use super::multiera_address::MultieraAddressTask; use crate::config::EmptyConfig::EmptyConfig; use crate::dsl::task_macro::*; @@ -19,7 +19,7 @@ carp_task! { task.block, &previous_data.multiera_txs, &previous_data.multiera_addresses, - PoolType::MinSwapV1, + DexType::MinSwapV1, ); merge_result |previous_data, _result| { }; diff --git a/indexer/tasks/src/multiera/multiera_minswap_v1_swap.rs b/indexer/tasks/src/multiera/multiera_minswap_v1_swap.rs index 82c8965e..807db920 100644 --- a/indexer/tasks/src/multiera/multiera_minswap_v1_swap.rs +++ b/indexer/tasks/src/multiera/multiera_minswap_v1_swap.rs @@ -1,4 +1,4 @@ -use super::dex::common::{handle_swap, PoolType}; +use super::dex::common::{handle_swap, DexType}; use super::multiera_used_inputs::MultieraUsedInputTask; use crate::config::EmptyConfig::EmptyConfig; use crate::dsl::task_macro::*; @@ -20,7 +20,7 @@ carp_task! { &previous_data.multiera_txs, &previous_data.multiera_addresses, &previous_data.multiera_used_inputs_to_outputs_map, - PoolType::MinSwapV1, + DexType::MinSwapV1, ); merge_result |previous_data, _result| { }; diff --git a/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs index 3b7b3eb6..e3e458f2 100644 --- a/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs +++ b/indexer/tasks/src/multiera/multiera_sundaeswap_v1_mean_price.rs @@ -1,4 +1,4 @@ -use super::dex::common::{handle_mean_price, PoolType}; +use super::dex::common::{handle_mean_price, DexType}; use super::multiera_address::MultieraAddressTask; use crate::config::EmptyConfig::EmptyConfig; use crate::dsl::task_macro::*; @@ -19,7 +19,7 @@ carp_task! { task.block, &previous_data.multiera_txs, &previous_data.multiera_addresses, - PoolType::SundaeSwapV1, + DexType::SundaeSwapV1, ); merge_result |previous_data, _result| { }; diff --git a/indexer/tasks/src/multiera/multiera_sundaeswap_v1_swap.rs b/indexer/tasks/src/multiera/multiera_sundaeswap_v1_swap.rs index c8746609..2481b02c 100644 --- a/indexer/tasks/src/multiera/multiera_sundaeswap_v1_swap.rs +++ b/indexer/tasks/src/multiera/multiera_sundaeswap_v1_swap.rs @@ -1,4 +1,4 @@ -use super::dex::common::{handle_swap, PoolType}; +use super::dex::common::{handle_swap, DexType}; use super::multiera_used_inputs::MultieraUsedInputTask; use crate::config::EmptyConfig::EmptyConfig; use crate::dsl::task_macro::*; @@ -20,7 +20,7 @@ carp_task! { &previous_data.multiera_txs, &previous_data.multiera_addresses, &previous_data.multiera_used_inputs_to_outputs_map, - PoolType::SundaeSwapV1, + DexType::SundaeSwapV1, ); merge_result |previous_data, _result| { }; diff --git a/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs index cb9ae057..10b183b1 100644 --- a/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs +++ b/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs @@ -3,18 +3,12 @@ use super::utils::common::{ }; use crate::multiera::dex::common::{ WR_V1_POOL_SCRIPT_HASH, WR_V1_POOL_FIXED_ADA, - build_asset, handle_mean_price, reduce_ada_amount, Dex, PoolType, QueuedMeanPrice, - WingRidersV1, - get_pool_output_and_datum + build_asset, handle_mean_price, reduce_ada_amount, Dex, DexType, QueuedMeanPrice, + WingRidersV1 }; use pallas::ledger::primitives::alonzo; use super::{multiera_address::MultieraAddressTask, utils::common::asset_from_pair}; use crate::config::EmptyConfig::EmptyConfig; -/* -use super::dex::common::{handle_mean_price, PoolType}; -use super::multiera_address::MultieraAddressTask; - -*/ use pallas::ledger::{ primitives::{alonzo::Certificate, Fragment}, @@ -38,7 +32,7 @@ carp_task! { task.block, &previous_data.multiera_txs, &previous_data.multiera_addresses, - PoolType::WingRidersV1, + DexType::WingRidersV1, ); merge_result |previous_data, _result| { }; diff --git a/indexer/tasks/src/multiera/multiera_wingriders_v1_swap.rs b/indexer/tasks/src/multiera/multiera_wingriders_v1_swap.rs index d5692f44..a856e50f 100644 --- a/indexer/tasks/src/multiera/multiera_wingriders_v1_swap.rs +++ b/indexer/tasks/src/multiera/multiera_wingriders_v1_swap.rs @@ -1,4 +1,4 @@ -use super::dex::common::{handle_swap, PoolType}; +use super::dex::common::{handle_swap, DexType}; use super::multiera_used_inputs::MultieraUsedInputTask; use crate::config::EmptyConfig::EmptyConfig; use crate::dsl::task_macro::*; @@ -20,7 +20,7 @@ carp_task! { &previous_data.multiera_txs, &previous_data.multiera_addresses, &previous_data.multiera_used_inputs_to_outputs_map, - PoolType::WingRidersV1, + DexType::WingRidersV1, ); merge_result |previous_data, _result| { }; From d6ad2987d16b6a029554d99912f339e5cb73d4ea Mon Sep 17 00:00:00 2001 From: Martin Miksanik Date: Thu, 17 Nov 2022 15:30:21 +0100 Subject: [PATCH 35/41] Fix eslint --- webserver/server/app/services/DexLastPrice.ts | 4 ++-- webserver/server/app/services/DexMeanPrice.ts | 2 +- webserver/server/app/services/DexSwap.ts | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/webserver/server/app/services/DexLastPrice.ts b/webserver/server/app/services/DexLastPrice.ts index d859758b..9d8f2bef 100644 --- a/webserver/server/app/services/DexLastPrice.ts +++ b/webserver/server/app/services/DexLastPrice.ts @@ -1,6 +1,6 @@ -import { Asset, PriceType, DexLastPriceResponse } from '../../../shared/models/DexLastPrice'; -import { Dex } from '../../../shared/models/common'; +import type { Asset, DexLastPriceResponse } from '../../../shared/models/DexLastPrice'; import type { PoolClient } from 'pg'; +import { PriceType } from '../../../shared/models/DexLastPrice'; import { sqlDexLastPriceSwap } from '../models/dex/sqlDexLastPriceSwap.queries'; import { sqlDexLastPriceMean } from '../models/dex/sqlDexLastPriceMean.queries'; import { parseAssetItem, serializeAsset, valueToDex} from './utils'; diff --git a/webserver/server/app/services/DexMeanPrice.ts b/webserver/server/app/services/DexMeanPrice.ts index 4604a1c2..5f601040 100644 --- a/webserver/server/app/services/DexMeanPrice.ts +++ b/webserver/server/app/services/DexMeanPrice.ts @@ -1,5 +1,5 @@ import type { Asset, DexMeanPriceResponse } from '../../../shared/models/DexMeanPrice'; -import { Dex } from '../../../shared/models/common'; +import type { Dex } from '../../../shared/models/common'; import type { PoolClient } from 'pg'; import type { TransactionPaginationType } from './PaginationService'; import { sqlDexMeanPrice } from '../models/dex/sqlDexMeanPrice.queries'; diff --git a/webserver/server/app/services/DexSwap.ts b/webserver/server/app/services/DexSwap.ts index 5b7d7267..5f3a3850 100644 --- a/webserver/server/app/services/DexSwap.ts +++ b/webserver/server/app/services/DexSwap.ts @@ -1,9 +1,10 @@ import type { Asset, DexSwapResponse } from '../../../shared/models/DexSwap'; import type { PoolClient } from 'pg'; import type { TransactionPaginationType } from './PaginationService'; +import type { Dex } from '../../../shared/models/common'; import { sqlDexSwap } from '../models/dex/sqlDexSwap.queries'; import { parseAssetItem, serializeAsset, valueToDex, dexToValue} from './utils'; -import { Dex, Direction } from '../../../shared/models/common'; +import { Direction } from '../../../shared/models/common'; export async function dexSwap( request: TransactionPaginationType & { From e5e7e30a91eea8d749bd3e7d0f703452a769c493 Mon Sep 17 00:00:00 2001 From: Martin Miksanik Date: Thu, 17 Nov 2022 15:34:02 +0100 Subject: [PATCH 36/41] Replace address_id to dex in queries --- .../server/app/models/dex/sqlDexLastPriceMean.queries.ts | 6 +++--- webserver/server/app/models/dex/sqlDexLastPriceMean.sql | 4 ++-- .../server/app/models/dex/sqlDexLastPriceSwap.queries.ts | 6 +++--- webserver/server/app/models/dex/sqlDexLastPriceSwap.sql | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/webserver/server/app/models/dex/sqlDexLastPriceMean.queries.ts b/webserver/server/app/models/dex/sqlDexLastPriceMean.queries.ts index 66a1a275..a1ef6f5a 100644 --- a/webserver/server/app/models/dex/sqlDexLastPriceMean.queries.ts +++ b/webserver/server/app/models/dex/sqlDexLastPriceMean.queries.ts @@ -28,7 +28,7 @@ export interface ISqlDexLastPriceMeanQuery { result: ISqlDexLastPriceMeanResult; } -const sqlDexLastPriceMeanIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n DISTINCT ON(\"DexMeanPrice\".address_id)\n\n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"DexMeanPrice\".amount1,\n \"DexMeanPrice\".amount2,\n \"DexMeanPrice\".dex\nFROM \"DexMeanPrice\"\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"DexMeanPrice\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"DexMeanPrice\".asset2_id\nWHERE\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n -- Add swap for another direction\n OR\n (\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea),\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\nORDER BY \"DexMeanPrice\".address_id, \"DexMeanPrice\".tx_id DESC, \"DexMeanPrice\".id DESC"}; +const sqlDexLastPriceMeanIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n DISTINCT ON(\"DexMeanPrice\".dex)\n\n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"DexMeanPrice\".amount1,\n \"DexMeanPrice\".amount2,\n \"DexMeanPrice\".dex\nFROM \"DexMeanPrice\"\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"DexMeanPrice\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"DexMeanPrice\".asset2_id\nWHERE\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n -- Add swap for another direction\n OR\n (\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea),\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\nORDER BY \"DexMeanPrice\".dex, \"DexMeanPrice\".tx_id DESC, \"DexMeanPrice\".id DESC"}; /** * Query generated from SQL: @@ -45,7 +45,7 @@ const sqlDexLastPriceMeanIR: any = {"usedParamSet":{"policy_id1":true,"asset_nam * ) x(policy_id1, asset_name1, policy_id2, asset_name2) * ) * SELECT - * DISTINCT ON("DexMeanPrice".address_id) + * DISTINCT ON("DexMeanPrice".dex) * * "Asset1".policy_id AS "policy_id1?", * "Asset1".asset_name AS "asset_name1?", @@ -72,7 +72,7 @@ const sqlDexLastPriceMeanIR: any = {"usedParamSet":{"policy_id1":true,"asset_nam * COALESCE("Asset1".policy_id, ''::bytea), * COALESCE("Asset1".asset_name, ''::bytea) * ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") - * ORDER BY "DexMeanPrice".address_id, "DexMeanPrice".tx_id DESC, "DexMeanPrice".id DESC + * ORDER BY "DexMeanPrice".dex, "DexMeanPrice".tx_id DESC, "DexMeanPrice".id DESC * ``` */ export const sqlDexLastPriceMean = new PreparedQuery(sqlDexLastPriceMeanIR); diff --git a/webserver/server/app/models/dex/sqlDexLastPriceMean.sql b/webserver/server/app/models/dex/sqlDexLastPriceMean.sql index 3363efac..cbd08ed8 100644 --- a/webserver/server/app/models/dex/sqlDexLastPriceMean.sql +++ b/webserver/server/app/models/dex/sqlDexLastPriceMean.sql @@ -15,7 +15,7 @@ WITH "AssetPairs" AS ( ) x(policy_id1, asset_name1, policy_id2, asset_name2) ) SELECT - DISTINCT ON("DexMeanPrice".address_id) + DISTINCT ON("DexMeanPrice".dex) "Asset1".policy_id AS "policy_id1?", "Asset1".asset_name AS "asset_name1?", @@ -42,4 +42,4 @@ WHERE COALESCE("Asset1".policy_id, ''::bytea), COALESCE("Asset1".asset_name, ''::bytea) ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") -ORDER BY "DexMeanPrice".address_id, "DexMeanPrice".tx_id DESC, "DexMeanPrice".id DESC; +ORDER BY "DexMeanPrice".dex, "DexMeanPrice".tx_id DESC, "DexMeanPrice".id DESC; diff --git a/webserver/server/app/models/dex/sqlDexLastPriceSwap.queries.ts b/webserver/server/app/models/dex/sqlDexLastPriceSwap.queries.ts index 442907c0..32da4b48 100644 --- a/webserver/server/app/models/dex/sqlDexLastPriceSwap.queries.ts +++ b/webserver/server/app/models/dex/sqlDexLastPriceSwap.queries.ts @@ -29,7 +29,7 @@ export interface ISqlDexLastPriceSwapQuery { result: ISqlDexLastPriceSwapResult; } -const sqlDexLastPriceSwapIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true,"direction":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]},{"name":"direction","required":false,"transform":{"type":"scalar"},"locs":[{"a":1287,"b":1296},{"a":1658,"b":1667}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n DISTINCT ON(\"DexSwap\".address_id)\n\n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"DexSwap\".amount1,\n \"DexSwap\".amount2,\n \"DexSwap\".dex\nFROM \"DexSwap\"\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"DexSwap\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"DexSwap\".asset2_id\nWHERE\n (\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n AND \"DexSwap\".direction = :direction\n )\n -- Add swap for another direction\n OR\n (\n (\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea),\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n AND \"DexSwap\".direction != :direction\n )\nORDER BY \"DexSwap\".address_id, \"DexSwap\".tx_id DESC, \"DexSwap\".id DESC"}; +const sqlDexLastPriceSwapIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true,"direction":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]},{"name":"direction","required":false,"transform":{"type":"scalar"},"locs":[{"a":1280,"b":1289},{"a":1651,"b":1660}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n DISTINCT ON(\"DexSwap\".dex)\n\n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"DexSwap\".amount1,\n \"DexSwap\".amount2,\n \"DexSwap\".dex\nFROM \"DexSwap\"\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"DexSwap\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"DexSwap\".asset2_id\nWHERE\n (\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n AND \"DexSwap\".direction = :direction\n )\n -- Add swap for another direction\n OR\n (\n (\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea),\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n AND \"DexSwap\".direction != :direction\n )\nORDER BY \"DexSwap\".dex, \"DexSwap\".tx_id DESC, \"DexSwap\".id DESC"}; /** * Query generated from SQL: @@ -46,7 +46,7 @@ const sqlDexLastPriceSwapIR: any = {"usedParamSet":{"policy_id1":true,"asset_nam * ) x(policy_id1, asset_name1, policy_id2, asset_name2) * ) * SELECT - * DISTINCT ON("DexSwap".address_id) + * DISTINCT ON("DexSwap".dex) * * "Asset1".policy_id AS "policy_id1?", * "Asset1".asset_name AS "asset_name1?", @@ -79,7 +79,7 @@ const sqlDexLastPriceSwapIR: any = {"usedParamSet":{"policy_id1":true,"asset_nam * ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") * AND "DexSwap".direction != :direction * ) - * ORDER BY "DexSwap".address_id, "DexSwap".tx_id DESC, "DexSwap".id DESC + * ORDER BY "DexSwap".dex, "DexSwap".tx_id DESC, "DexSwap".id DESC * ``` */ export const sqlDexLastPriceSwap = new PreparedQuery(sqlDexLastPriceSwapIR); diff --git a/webserver/server/app/models/dex/sqlDexLastPriceSwap.sql b/webserver/server/app/models/dex/sqlDexLastPriceSwap.sql index 685c487a..e2422d0a 100644 --- a/webserver/server/app/models/dex/sqlDexLastPriceSwap.sql +++ b/webserver/server/app/models/dex/sqlDexLastPriceSwap.sql @@ -15,7 +15,7 @@ WITH "AssetPairs" AS ( ) x(policy_id1, asset_name1, policy_id2, asset_name2) ) SELECT - DISTINCT ON("DexSwap".address_id) + DISTINCT ON("DexSwap".dex) "Asset1".policy_id AS "policy_id1?", "Asset1".asset_name AS "asset_name1?", @@ -48,4 +48,4 @@ WHERE ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") AND "DexSwap".direction != :direction ) -ORDER BY "DexSwap".address_id, "DexSwap".tx_id DESC, "DexSwap".id DESC; +ORDER BY "DexSwap".dex, "DexSwap".tx_id DESC, "DexSwap".id DESC; From f197dfcc3fccf7fa50a27cfbd6df3dcd71cb8238 Mon Sep 17 00:00:00 2001 From: Martin Miksanik Date: Thu, 17 Nov 2022 16:17:05 +0100 Subject: [PATCH 37/41] Cargo fmt --- .env | 5 +- indexer/entity/src/dex_mean_price.rs | 2 +- indexer/entity/src/dex_swap.rs | 2 +- ...1020_000014_create_dex_mean_price_table.rs | 2 +- .../m20221031_000015_create_dex_swap_table.rs | 2 +- indexer/tasks/src/multiera/dex/common.rs | 78 ++++++++++--------- .../multiera_wingriders_v1_mean_price.rs | 35 ++------- indexer/tasks/src/multiera/utils/common.rs | 2 +- webserver/server/app/services/DexLastPrice.ts | 3 +- webserver/server/app/services/DexMeanPrice.ts | 4 +- webserver/server/app/services/DexSwap.ts | 3 +- webserver/server/app/services/utils.ts | 2 +- webserver/shared/models/DexLastPrice.ts | 8 +- webserver/shared/models/DexMeanPrice.ts | 8 +- webserver/shared/models/DexSwap.ts | 8 +- webserver/shared/models/common.ts | 7 ++ 16 files changed, 72 insertions(+), 99 deletions(-) diff --git a/.env b/.env index b5f12368..e71c8986 100644 --- a/.env +++ b/.env @@ -1,13 +1,12 @@ NETWORK=mainnet # mainnet/preview/preprod/testnet # SOCKET=relays-new.cardano-mainnet.iohk.io:3001 # SOCKET=relays-new.cardano-testnet.iohkdev.io:3001 -SOCKET=/mnt/nvme/carp/node-ipc/node.socket +SOCKET="$(realpath ../cardano-node/bin/\\.pipe\\cardano-node)" POSTGRES_HOST=localhost POSTGRES_PORT=5432 -PGUSER=postgres +PGUSER=carp POSTGRES_DB=carp_mainnet PGPASSFILE="$(realpath secrets/.pgpass)" -PGPASSWORD=postgres # note: PGPASSWORD isn't required to run carp # since it will be parsed from the PGPASSFILE instead # as this command will gracefully fallback to PGPASSFILE if no password is specified diff --git a/indexer/entity/src/dex_mean_price.rs b/indexer/entity/src/dex_mean_price.rs index 0bb76936..93a494bf 100644 --- a/indexer/entity/src/dex_mean_price.rs +++ b/indexer/entity/src/dex_mean_price.rs @@ -1,7 +1,7 @@ use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Deserialize, Serialize)] #[sea_orm(table_name = "DexMeanPrice")] pub struct Model { #[sea_orm(primary_key, column_type = "BigInteger")] diff --git a/indexer/entity/src/dex_swap.rs b/indexer/entity/src/dex_swap.rs index 689d769a..80bd079d 100644 --- a/indexer/entity/src/dex_swap.rs +++ b/indexer/entity/src/dex_swap.rs @@ -1,7 +1,7 @@ use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Deserialize, Serialize)] #[sea_orm(table_name = "DexSwap")] pub struct Model { #[sea_orm(primary_key, column_type = "BigInteger")] diff --git a/indexer/migration/src/m20221020_000014_create_dex_mean_price_table.rs b/indexer/migration/src/m20221020_000014_create_dex_mean_price_table.rs index a3b03c88..f712cf1c 100644 --- a/indexer/migration/src/m20221020_000014_create_dex_mean_price_table.rs +++ b/indexer/migration/src/m20221020_000014_create_dex_mean_price_table.rs @@ -72,7 +72,7 @@ impl MigrationTrait for Migration { Index::create() .table(Entity) .name("index-dex_mean_price-address-native_asset1-native_asset2-transaction") - .col(Column::AddressId) + .col(Column::Dex) .col(Column::Asset1Id) .col(Column::Asset2Id) .col(Column::TxId) diff --git a/indexer/migration/src/m20221031_000015_create_dex_swap_table.rs b/indexer/migration/src/m20221031_000015_create_dex_swap_table.rs index 5a2de8ff..c308e628 100644 --- a/indexer/migration/src/m20221031_000015_create_dex_swap_table.rs +++ b/indexer/migration/src/m20221031_000015_create_dex_swap_table.rs @@ -73,7 +73,7 @@ impl MigrationTrait for Migration { Index::create() .table(Entity) .name("index-dex_swap-address-native_asset1-native_asset2-transaction") - .col(Column::AddressId) + .col(Column::Dex) .col(Column::Asset1Id) .col(Column::Asset2Id) .col(Column::TxId) diff --git a/indexer/tasks/src/multiera/dex/common.rs b/indexer/tasks/src/multiera/dex/common.rs index ea29f967..1f3346e5 100644 --- a/indexer/tasks/src/multiera/dex/common.rs +++ b/indexer/tasks/src/multiera/dex/common.rs @@ -1,7 +1,8 @@ +use crate::{dsl::database_task::BlockInfo, types::AssetPair}; use crate::{ dsl::task_macro::*, multiera::utils::common::{ - asset_from_pair, get_plutus_datum_for_output, get_sheley_payment_hash, + asset_from_pair, get_plutus_datum_for_output, get_shelley_payment_hash, }, types::DexSwapDirection, }; @@ -15,19 +16,6 @@ use pallas::{ }; use std::collections::{BTreeMap, BTreeSet}; -use crate::{dsl::database_task::BlockInfo, types::AssetPair}; - -pub const WR_V1_POOL_SCRIPT_HASH: &str = "e6c90a5923713af5786963dee0fdffd830ca7e0c86a041d9e5833e91"; -pub const WR_V1_POOL_FIXED_ADA: u64 = 3_000_000; // every pool UTXO holds this amount of ADA -pub const MS_V1_POOL_SCRIPT_HASH1: &str = - "e1317b152faac13426e6a83e06ff88a4d62cce3c1634ab0a5ec13309"; -pub const MS_V1_POOL_SCRIPT_HASH2: &str = - "57c8e718c201fba10a9da1748d675b54281d3b1b983c5d1687fc7317"; -pub const SS_V1_POOL_SCRIPT_HASH: &str = "4020e7fc2de75a0729c3cc3af715b34d98381e0cdbcfa99c950bc3ac"; - -pub const WR_V1_SWAP_IN_ADA: u64 = 4_000_000; // oil ADA + agent fee -pub const WR_V1_SWAP_OUT_ADA: u64 = 2_000_000; // oil ADA - /// Returns an output and it's datum only if the output's payment hash is in `payment_hashes` /// and the plutus datum is known. pub fn filter_outputs_and_datums_by_hash<'b>( @@ -39,7 +27,7 @@ pub fn filter_outputs_and_datums_by_hash<'b>( outputs .iter() .filter_map(|o| { - if payment_hashes.contains(&get_sheley_payment_hash(o.address()).as_deref()) { + if payment_hashes.contains(&get_shelley_payment_hash(o.address()).as_deref()) { if let Some(datum) = get_plutus_datum_for_output(&o, plutus_data) { Some((o.clone(), datum)) } else { @@ -234,16 +222,25 @@ pub async fn handle_mean_price( asset_pair_to_id_map.insert(None, None); // ADA // 4) Add mean prices to DB - DexMeanPrice::insert_many(queued_prices.iter().map(|price| DexMeanPriceActiveModel { - tx_id: Set(price.tx_id), - address_id: Set(multiera_addresses[&price.address].model.id), - dex: Set(i32::from(price.dex_type.clone())), - asset1_id: Set(asset_pair_to_id_map[&price.asset1]), - asset2_id: Set(asset_pair_to_id_map[&price.asset2]), - amount1: Set(price.amount1), - amount2: Set(price.amount2), - ..Default::default() - })) + DexMeanPrice::insert_many( + queued_prices + .iter() + .filter(|price| { + // In the unlikely case that an asset is not in the DB, skip this price update + asset_pair_to_id_map.contains_key(&price.asset1) + && asset_pair_to_id_map.contains_key(&price.asset2) + }) + .map(|price| DexMeanPriceActiveModel { + tx_id: Set(price.tx_id), + address_id: Set(multiera_addresses[&price.address].model.id), + dex: Set(i32::from(price.dex_type.clone())), + asset1_id: Set(asset_pair_to_id_map[&price.asset1]), + asset2_id: Set(asset_pair_to_id_map[&price.asset2]), + amount1: Set(price.amount1), + amount2: Set(price.amount2), + ..Default::default() + }), + ) .exec(db_tx) .await?; @@ -328,17 +325,26 @@ pub async fn handle_swap( asset_pair_to_id_map.insert(None, None); // ADA // 4) Add mean prices to DB - DexSwap::insert_many(queued_swaps.iter().map(|price| DexSwapActiveModel { - tx_id: Set(price.tx_id), - address_id: Set(multiera_addresses[&price.address].model.id), - dex: Set(i32::from(price.dex_type.clone())), - asset1_id: Set(asset_pair_to_id_map[&price.asset1]), - asset2_id: Set(asset_pair_to_id_map[&price.asset2]), - amount1: Set(price.amount1), - amount2: Set(price.amount2), - direction: Set(price.direction.into()), - ..Default::default() - })) + DexSwap::insert_many( + queued_swaps + .iter() + .filter(|price| { + // In the unlikely case that an asset is not in the DB, skip this price update + asset_pair_to_id_map.contains_key(&price.asset1) + && asset_pair_to_id_map.contains_key(&price.asset2) + }) + .map(|price| DexSwapActiveModel { + tx_id: Set(price.tx_id), + address_id: Set(multiera_addresses[&price.address].model.id), + dex: Set(i32::from(price.dex_type.clone())), + asset1_id: Set(asset_pair_to_id_map[&price.asset1]), + asset2_id: Set(asset_pair_to_id_map[&price.asset2]), + amount1: Set(price.amount1), + amount2: Set(price.amount2), + direction: Set(price.direction.into()), + ..Default::default() + }), + ) .exec(db_tx) .await?; diff --git a/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs b/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs index 10b183b1..2ccb0828 100644 --- a/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs +++ b/indexer/tasks/src/multiera/multiera_wingriders_v1_mean_price.rs @@ -1,20 +1,16 @@ use super::utils::common::{ - get_asset_amount, get_plutus_datum_for_output, get_sheley_payment_hash, + get_asset_amount, get_plutus_datum_for_output, get_shelley_payment_hash, }; -use crate::multiera::dex::common::{ - WR_V1_POOL_SCRIPT_HASH, WR_V1_POOL_FIXED_ADA, - build_asset, handle_mean_price, reduce_ada_amount, Dex, DexType, QueuedMeanPrice, - WingRidersV1 -}; -use pallas::ledger::primitives::alonzo; use super::{multiera_address::MultieraAddressTask, utils::common::asset_from_pair}; use crate::config::EmptyConfig::EmptyConfig; +use crate::multiera::dex::common::{handle_mean_price, DexType}; +use pallas::ledger::primitives::alonzo; +use crate::dsl::task_macro::*; use pallas::ledger::{ - primitives::{alonzo::Certificate, Fragment}, - traverse::{MultiEraBlock, MultiEraCert, MultiEraOutput, MultiEraTx}, + primitives::{alonzo::Certificate, Fragment}, + traverse::{MultiEraBlock, MultiEraCert, MultiEraOutput, MultiEraTx}, }; -use crate::dsl::task_macro::*; carp_task! { name MultieraWingRidersV1MeanPriceTask; @@ -37,22 +33,3 @@ carp_task! { merge_result |previous_data, _result| { }; } - -pub fn get_pool_output<'b>(tx: &'b MultiEraTx) -> Option<(MultiEraOutput<'b>, alonzo::PlutusData)> { - // Note: there should be at most one pool output - if let Some(output) = tx - .outputs() - .iter() - .find(|o| get_sheley_payment_hash(o.address()).as_deref() == Some(WR_V1_POOL_SCRIPT_HASH)) - { - // Remark: The datum that corresponds to the pool output's datum hash should be present - // in tx.plutus_data() - if let Some(datum) = get_plutus_datum_for_output(output, &tx.plutus_data()) { - Some((output.clone(), datum)) - } else { - None - } - } else { - None - } -} diff --git a/indexer/tasks/src/multiera/utils/common.rs b/indexer/tasks/src/multiera/utils/common.rs index 393bb08d..631b1129 100644 --- a/indexer/tasks/src/multiera/utils/common.rs +++ b/indexer/tasks/src/multiera/utils/common.rs @@ -16,7 +16,7 @@ use pallas::{ use crate::types::AssetPair; -pub fn get_sheley_payment_hash( +pub fn get_shelley_payment_hash( address: Result, ) -> Option { if let Ok(addresses::Address::Shelley(shelley_address)) = address { diff --git a/webserver/server/app/services/DexLastPrice.ts b/webserver/server/app/services/DexLastPrice.ts index 9d8f2bef..f5a10c2b 100644 --- a/webserver/server/app/services/DexLastPrice.ts +++ b/webserver/server/app/services/DexLastPrice.ts @@ -1,4 +1,5 @@ -import type { Asset, DexLastPriceResponse } from '../../../shared/models/DexLastPrice'; +import type { DexLastPriceResponse } from '../../../shared/models/DexLastPrice'; +import type { Asset } from '../../../shared/models/common'; import type { PoolClient } from 'pg'; import { PriceType } from '../../../shared/models/DexLastPrice'; import { sqlDexLastPriceSwap } from '../models/dex/sqlDexLastPriceSwap.queries'; diff --git a/webserver/server/app/services/DexMeanPrice.ts b/webserver/server/app/services/DexMeanPrice.ts index 5f601040..af3ae0f2 100644 --- a/webserver/server/app/services/DexMeanPrice.ts +++ b/webserver/server/app/services/DexMeanPrice.ts @@ -1,5 +1,5 @@ -import type { Asset, DexMeanPriceResponse } from '../../../shared/models/DexMeanPrice'; -import type { Dex } from '../../../shared/models/common'; +import type { DexMeanPriceResponse } from '../../../shared/models/DexMeanPrice'; +import type { Dex, Asset } from '../../../shared/models/common'; import type { PoolClient } from 'pg'; import type { TransactionPaginationType } from './PaginationService'; import { sqlDexMeanPrice } from '../models/dex/sqlDexMeanPrice.queries'; diff --git a/webserver/server/app/services/DexSwap.ts b/webserver/server/app/services/DexSwap.ts index 5f3a3850..91946b5c 100644 --- a/webserver/server/app/services/DexSwap.ts +++ b/webserver/server/app/services/DexSwap.ts @@ -1,4 +1,5 @@ -import type { Asset, DexSwapResponse } from '../../../shared/models/DexSwap'; +import type { DexSwapResponse } from '../../../shared/models/DexSwap'; +import type { Asset } from '../../../shared/models/common'; import type { PoolClient } from 'pg'; import type { TransactionPaginationType } from './PaginationService'; import type { Dex } from '../../../shared/models/common'; diff --git a/webserver/server/app/services/utils.ts b/webserver/server/app/services/utils.ts index a4583d10..cca56fe2 100644 --- a/webserver/server/app/services/utils.ts +++ b/webserver/server/app/services/utils.ts @@ -1,4 +1,4 @@ -import type { Asset } from '../../../shared/models/DexMeanPrice'; +import type { Asset } from '../../../shared/models/common'; import { Dex } from '../../../shared/models/common'; export function parseAssetItem(s: string | undefined | null): Buffer { diff --git a/webserver/shared/models/DexLastPrice.ts b/webserver/shared/models/DexLastPrice.ts index c0af5100..0d58a816 100644 --- a/webserver/shared/models/DexLastPrice.ts +++ b/webserver/shared/models/DexLastPrice.ts @@ -1,10 +1,4 @@ -import { AssetName, PolicyId } from "./PolicyIdAssetMap"; -import { Dex } from "./common"; - -export type Asset = { - policyId: PolicyId; - assetName: AssetName; -} | null; +import { Dex, Asset } from "./common"; /** * @example "2042352568679" diff --git a/webserver/shared/models/DexMeanPrice.ts b/webserver/shared/models/DexMeanPrice.ts index 0a5c3f14..06f7d73e 100644 --- a/webserver/shared/models/DexMeanPrice.ts +++ b/webserver/shared/models/DexMeanPrice.ts @@ -1,10 +1,4 @@ -import { Dex, Pagination } from "./common"; -import { AssetName, PolicyId } from "./PolicyIdAssetMap"; - -export type Asset = { - policyId: PolicyId; - assetName: AssetName; -} | null; +import { Dex, Pagination, Asset } from "./common"; /** * @example "2042352568679" diff --git a/webserver/shared/models/DexSwap.ts b/webserver/shared/models/DexSwap.ts index 3b62799c..dc7c157b 100644 --- a/webserver/shared/models/DexSwap.ts +++ b/webserver/shared/models/DexSwap.ts @@ -1,10 +1,4 @@ -import { Pagination, Dex, Direction } from "./common"; -import { AssetName, PolicyId } from "./PolicyIdAssetMap"; - -export type Asset = { - policyId: PolicyId; - assetName: AssetName; -} | null; +import { Pagination, Dex, Direction, Asset } from "./common"; /** * @example "2042352568679" diff --git a/webserver/shared/models/common.ts b/webserver/shared/models/common.ts index 18081431..ffccdeab 100644 --- a/webserver/shared/models/common.ts +++ b/webserver/shared/models/common.ts @@ -1,3 +1,5 @@ +import { AssetName, PolicyId } from "./PolicyIdAssetMap"; + /** * Filter which uses of the address are considered relevant for the query. * @@ -101,3 +103,8 @@ export enum Dex { MinSwap = 'MinSwap', Unknown = 'Unknown', }; + +export type Asset = { + policyId: PolicyId; + assetName: AssetName; +} | null; From 113a608b1703d67b13d51ab812cdef286555cf9c Mon Sep 17 00:00:00 2001 From: Martin Miksanik Date: Thu, 17 Nov 2022 18:41:02 +0100 Subject: [PATCH 38/41] Fix Clippy problems --- indexer/tasks/src/era_common.rs | 2 +- indexer/tasks/src/multiera/dex/common.rs | 24 +++++---------- indexer/tasks/src/multiera/dex/minswap_v1.rs | 14 ++++----- .../tasks/src/multiera/dex/sundaeswap_v1.rs | 29 ++++++++----------- .../tasks/src/multiera/dex/wingriders_v1.rs | 26 +++++++---------- indexer/tasks/src/multiera/relation_map.rs | 2 +- indexer/tasks/src/multiera/utils/common.rs | 4 +-- 7 files changed, 41 insertions(+), 60 deletions(-) diff --git a/indexer/tasks/src/era_common.rs b/indexer/tasks/src/era_common.rs index 19c74251..896b22d8 100644 --- a/indexer/tasks/src/era_common.rs +++ b/indexer/tasks/src/era_common.rs @@ -190,7 +190,7 @@ pub async fn get_outputs_for_inputs( id: output.id, payload: output.payload.clone(), address_id: output.address_id, - tx_id: output.tx_id.clone(), + tx_id: output.tx_id, output_index: output.output_index, }, tx_hash: output.tx_hash.clone(), diff --git a/indexer/tasks/src/multiera/dex/common.rs b/indexer/tasks/src/multiera/dex/common.rs index 1f3346e5..2145c123 100644 --- a/indexer/tasks/src/multiera/dex/common.rs +++ b/indexer/tasks/src/multiera/dex/common.rs @@ -21,18 +21,14 @@ use std::collections::{BTreeMap, BTreeSet}; pub fn filter_outputs_and_datums_by_hash<'b>( outputs: &[MultiEraOutput<'b>], payment_hashes: &[&str], - plutus_data: &Vec<&KeepRaw>, + plutus_data: &[&KeepRaw], ) -> Vec<(MultiEraOutput<'b>, alonzo::PlutusData)> { let payment_hashes = payment_hashes.iter().map(|&s| Some(s)).collect::>(); outputs .iter() .filter_map(|o| { if payment_hashes.contains(&get_shelley_payment_hash(o.address()).as_deref()) { - if let Some(datum) = get_plutus_datum_for_output(&o, plutus_data) { - Some((o.clone(), datum)) - } else { - None - } + get_plutus_datum_for_output(o, plutus_data).map(|datum| (o.clone(), datum)) } else { None } @@ -45,18 +41,14 @@ pub fn filter_outputs_and_datums_by_hash<'b>( pub fn filter_outputs_and_datums_by_address<'b>( outputs: &[MultiEraOutput<'b>], addresses: &[&str], - plutus_data: &Vec<&KeepRaw>, + plutus_data: &[&KeepRaw], ) -> Vec<(MultiEraOutput<'b>, alonzo::PlutusData)> { let addresses = addresses.iter().map(|&s| Some(s)).collect::>(); outputs .iter() .filter_map(|o| { if addresses.contains(&o.address().ok().and_then(|a| a.to_bech32().ok()).as_deref()) { - if let Some(datum) = get_plutus_datum_for_output(&o, plutus_data) { - Some((o.clone(), datum)) - } else { - None - } + get_plutus_datum_for_output(o, plutus_data).map(|datum| (o.clone(), datum)) } else { None } @@ -199,10 +191,10 @@ pub async fn handle_mean_price( let mut unique_tokens = BTreeSet::<&(Vec, Vec)>::default(); for p in &queued_prices { if let Some(pair) = &p.asset1 { - unique_tokens.insert(&pair); + unique_tokens.insert(pair); } if let Some(pair) = &p.asset2 { - unique_tokens.insert(&pair); + unique_tokens.insert(pair); } } @@ -301,10 +293,10 @@ pub async fn handle_swap( let mut unique_tokens = BTreeSet::<&(Vec, Vec)>::default(); for p in &queued_swaps { if let Some(pair) = &p.asset1 { - unique_tokens.insert(&pair); + unique_tokens.insert(pair); } if let Some(pair) = &p.asset2 { - unique_tokens.insert(&pair); + unique_tokens.insert(pair); } } diff --git a/indexer/tasks/src/multiera/dex/minswap_v1.rs b/indexer/tasks/src/multiera/dex/minswap_v1.rs index f1e48011..900ccf59 100644 --- a/indexer/tasks/src/multiera/dex/minswap_v1.rs +++ b/indexer/tasks/src/multiera/dex/minswap_v1.rs @@ -33,7 +33,7 @@ impl Dex for MinSwapV1 { // Note: there should be at most one pool output if let Some((output, datum)) = filter_outputs_and_datums_by_hash( &tx.outputs(), - &vec![POOL_SCRIPT_HASH1, POOL_SCRIPT_HASH2], + &[POOL_SCRIPT_HASH1, POOL_SCRIPT_HASH2], &tx.plutus_data(), ) .get(0) @@ -50,8 +50,8 @@ impl Dex for MinSwapV1 { let asset1 = build_asset(parse_asset_item(0, 0)?, parse_asset_item(0, 1)?); let asset2 = build_asset(parse_asset_item(1, 0)?, parse_asset_item(1, 1)?); - let amount1 = get_asset_amount(&output, &asset1); - let amount2 = get_asset_amount(&output, &asset2); + let amount1 = get_asset_amount(output, &asset1); + let amount2 = get_asset_amount(output, &asset2); queued_prices.push(QueuedMeanPrice { tx_id, @@ -76,7 +76,7 @@ impl Dex for MinSwapV1 { // Note: there should be at most one pool output if let Some((main_output, main_datum)) = filter_outputs_and_datums_by_hash( &tx.outputs(), - &vec![POOL_SCRIPT_HASH1, POOL_SCRIPT_HASH2], + &[POOL_SCRIPT_HASH1, POOL_SCRIPT_HASH2], &tx.plutus_data(), ) .get(0) @@ -106,7 +106,7 @@ impl Dex for MinSwapV1 { .collect::>(); for (input, input_datum) in filter_outputs_and_datums_by_address( &inputs, - &vec![BATCH_ORDER_ADDRESS1, BATCH_ORDER_ADDRESS2], + &[BATCH_ORDER_ADDRESS1, BATCH_ORDER_ADDRESS2], &tx.plutus_data(), ) { let input_datum = input_datum.to_json(); @@ -162,13 +162,13 @@ impl Dex for MinSwapV1 { get_asset_amount(&input, &asset1) - reduce_ada_amount(&asset1, SWAP_IN_ADA); amount2 = get_asset_amount(&utxo, &asset2) - reduce_ada_amount(&asset2, SWAP_OUT_ADA); - direction = DexSwapDirection::BuyAsset1; + direction = DexSwapDirection::SellAsset1; } else { amount1 = get_asset_amount(&utxo, &asset1) - reduce_ada_amount(&asset1, SWAP_OUT_ADA); amount2 = get_asset_amount(&input, &asset2) - reduce_ada_amount(&asset2, SWAP_IN_ADA); - direction = DexSwapDirection::SellAsset1; + direction = DexSwapDirection::BuyAsset1; } queued_swaps.push(QueuedSwap { tx_id, diff --git a/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs b/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs index caf2270c..a04990e4 100644 --- a/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs +++ b/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs @@ -29,12 +29,9 @@ impl Dex for SundaeSwapV1 { tx_id: i64, ) -> Result<(), String> { // Note: there should be at most one pool output - if let Some((output, datum)) = filter_outputs_and_datums_by_hash( - &tx.outputs(), - &vec![POOL_SCRIPT_HASH], - &tx.plutus_data(), - ) - .get(0) + if let Some((output, datum)) = + filter_outputs_and_datums_by_hash(&tx.outputs(), &[POOL_SCRIPT_HASH], &tx.plutus_data()) + .get(0) { let datum = datum.to_json(); @@ -48,8 +45,8 @@ impl Dex for SundaeSwapV1 { let asset1 = build_asset(parse_asset_item(0, 0)?, parse_asset_item(0, 1)?); let asset2 = build_asset(parse_asset_item(1, 0)?, parse_asset_item(1, 1)?); - let amount1 = get_asset_amount(&output, &asset1); - let amount2 = get_asset_amount(&output, &asset2); + let amount1 = get_asset_amount(output, &asset1); + let amount2 = get_asset_amount(output, &asset2); queued_prices.push(QueuedMeanPrice { tx_id, @@ -72,12 +69,9 @@ impl Dex for SundaeSwapV1 { multiera_used_inputs_to_outputs_map: &BTreeMap, BTreeMap>, ) -> Result<(), String> { // Note: there should be at most one pool output - if let Some((main_output, main_datum)) = filter_outputs_and_datums_by_hash( - &tx.outputs(), - &vec![POOL_SCRIPT_HASH], - &tx.plutus_data(), - ) - .get(0) + if let Some((main_output, main_datum)) = + filter_outputs_and_datums_by_hash(&tx.outputs(), &[POOL_SCRIPT_HASH], &tx.plutus_data()) + .get(0) { let main_datum = main_datum.to_json(); let mut free_utxos: Vec = tx.outputs(); @@ -104,7 +98,7 @@ impl Dex for SundaeSwapV1 { .collect::>(); for (input, input_datum) in filter_outputs_and_datums_by_hash( &inputs, - &vec![REQUEST_SCRIPT_HASH], + &[REQUEST_SCRIPT_HASH], &tx.plutus_data(), ) { let input_datum = input_datum.to_json(); @@ -149,6 +143,7 @@ impl Dex for SundaeSwapV1 { let direction = input_datum["fields"][3]["fields"][0]["constructor"] .as_i64() .ok_or("Failed to parse direction")?; + if direction == 0 { amount1 = get_asset_amount(&input, &asset1) - reduce_ada_amount(&asset1, SWAP_IN_ADA); @@ -169,9 +164,9 @@ impl Dex for SundaeSwapV1 { amount1, amount2, direction: if direction == 0 { - DexSwapDirection::BuyAsset1 - } else { DexSwapDirection::SellAsset1 + } else { + DexSwapDirection::BuyAsset1 }, }) } diff --git a/indexer/tasks/src/multiera/dex/wingriders_v1.rs b/indexer/tasks/src/multiera/dex/wingriders_v1.rs index e66a8e56..c4deae70 100644 --- a/indexer/tasks/src/multiera/dex/wingriders_v1.rs +++ b/indexer/tasks/src/multiera/dex/wingriders_v1.rs @@ -29,12 +29,9 @@ impl Dex for WingRidersV1 { tx_id: i64, ) -> Result<(), String> { // Note: there should be at most one pool output - if let Some((output, datum)) = filter_outputs_and_datums_by_hash( - &tx.outputs(), - &vec![POOL_SCRIPT_HASH], - &tx.plutus_data(), - ) - .get(0) + if let Some((output, datum)) = + filter_outputs_and_datums_by_hash(&tx.outputs(), &[POOL_SCRIPT_HASH], &tx.plutus_data()) + .get(0) { let datum = datum.to_json(); @@ -55,10 +52,10 @@ impl Dex for WingRidersV1 { let asset1 = build_asset(parse_asset_item(0, 0)?, parse_asset_item(0, 1)?); let asset2 = build_asset(parse_asset_item(1, 0)?, parse_asset_item(1, 1)?); - let amount1 = get_asset_amount(&output, &asset1) + let amount1 = get_asset_amount(output, &asset1) - treasury1 - reduce_ada_amount(&asset1, POOL_FIXED_ADA); - let amount2 = get_asset_amount(&output, &asset2) + let amount2 = get_asset_amount(output, &asset2) - treasury2 - reduce_ada_amount(&asset2, POOL_FIXED_ADA); @@ -83,12 +80,9 @@ impl Dex for WingRidersV1 { multiera_used_inputs_to_outputs_map: &BTreeMap, BTreeMap>, ) -> Result<(), String> { // Note: there should be at most one pool output - if let Some((pool_output, _)) = filter_outputs_and_datums_by_hash( - &tx.outputs(), - &vec![POOL_SCRIPT_HASH], - &tx.plutus_data(), - ) - .get(0) + if let Some((pool_output, _)) = + filter_outputs_and_datums_by_hash(&tx.outputs(), &[POOL_SCRIPT_HASH], &tx.plutus_data()) + .get(0) { let redeemers = tx.redeemers().ok_or("No redeemers")?; @@ -167,10 +161,10 @@ impl Dex for WingRidersV1 { if direction == 0 { amount1 = get_asset_amount(&input, &asset1) - reduce_ada_amount(&asset1, SWAP_IN_ADA); - amount2 = get_asset_amount(&output, &asset2) + amount2 = get_asset_amount(output, &asset2) - reduce_ada_amount(&asset2, SWAP_OUT_ADA); } else { - amount1 = get_asset_amount(&output, &asset1) + amount1 = get_asset_amount(output, &asset1) - reduce_ada_amount(&asset1, SWAP_OUT_ADA); amount2 = get_asset_amount(&input, &asset2) - reduce_ada_amount(&asset2, SWAP_IN_ADA); diff --git a/indexer/tasks/src/multiera/relation_map.rs b/indexer/tasks/src/multiera/relation_map.rs index d889ce14..89adf791 100644 --- a/indexer/tasks/src/multiera/relation_map.rs +++ b/indexer/tasks/src/multiera/relation_map.rs @@ -29,7 +29,7 @@ impl RelationMap { } pub fn for_transaction(&mut self, tx_id: i64) -> &mut BTreeMap, i32> { - self.0.entry(tx_id).or_insert(BTreeMap::new()) + self.0.entry(tx_id).or_default() } pub fn add_relation( diff --git a/indexer/tasks/src/multiera/utils/common.rs b/indexer/tasks/src/multiera/utils/common.rs index 631b1129..20f8817d 100644 --- a/indexer/tasks/src/multiera/utils/common.rs +++ b/indexer/tasks/src/multiera/utils/common.rs @@ -20,7 +20,7 @@ pub fn get_shelley_payment_hash( address: Result, ) -> Option { if let Ok(addresses::Address::Shelley(shelley_address)) = address { - Some(hex::encode(shelley_address.payment().as_hash().to_vec())) + Some(hex::encode(shelley_address.payment().as_hash())) } else { None } @@ -42,7 +42,7 @@ pub fn get_asset_amount(output: &MultiEraOutput, pair: &AssetPair) -> u64 { pub fn get_plutus_datum_for_output( output: &MultiEraOutput, - plutus_data: &Vec<&KeepRaw>, + plutus_data: &[&KeepRaw], ) -> Option { match output.datum() { Some(DatumOption::Data(datum)) => Some(datum.0), From 095f0eca6a363db517245fa96da81140eee4ac97 Mon Sep 17 00:00:00 2001 From: Martin Miksanik Date: Tue, 29 Nov 2022 19:40:33 +0100 Subject: [PATCH 39/41] Replace dex tables with view --- indexer/entity/src/dex_mean_price.rs | 66 -------------- indexer/entity/src/dex_swap.rs | 34 +++---- indexer/entity/src/lib.rs | 1 - indexer/entity/src/prelude.rs | 5 -- indexer/execution_plans/dex_prices.toml | 4 +- indexer/migration/src/lib.rs | 8 +- ...1020_000014_create_dex_mean_price_table.rs | 89 ------------------- ...s => m20221031_000014_create_dex_table.rs} | 24 +++-- .../src/m20221031_000015_create_dex_views.rs | 45 ++++++++++ indexer/tasks/src/multiera/dex/common.rs | 14 +-- indexer/tasks/src/multiera/dex/minswap_v1.rs | 15 ++-- .../tasks/src/multiera/dex/sundaeswap_v1.rs | 14 ++- .../tasks/src/multiera/dex/wingriders_v1.rs | 18 ++-- indexer/tasks/src/types.rs | 15 ---- .../app/controllers/DexLastPriceController.ts | 25 ++---- .../models/dex/sqlDexLastPriceMean.queries.ts | 6 +- .../models/dex/sqlDexLastPriceSwap.queries.ts | 14 +-- .../app/models/dex/sqlDexLastPriceSwap.sql | 4 +- .../app/models/dex/sqlDexMeanPrice.queries.ts | 6 +- .../app/models/dex/sqlDexSwap.queries.ts | 12 +-- .../server/app/models/dex/sqlDexSwap.sql | 2 +- webserver/server/app/services/DexLastPrice.ts | 10 +-- webserver/server/app/services/DexMeanPrice.ts | 6 +- webserver/server/app/services/DexSwap.ts | 8 +- webserver/server/app/services/utils.ts | 3 +- webserver/shared/models/DexLastPrice.ts | 7 +- webserver/shared/models/DexMeanPrice.ts | 6 +- webserver/shared/models/DexSwap.ts | 8 +- webserver/shared/models/common.ts | 7 +- 29 files changed, 161 insertions(+), 315 deletions(-) delete mode 100644 indexer/entity/src/dex_mean_price.rs delete mode 100644 indexer/migration/src/m20221020_000014_create_dex_mean_price_table.rs rename indexer/migration/src/{m20221031_000015_create_dex_swap_table.rs => m20221031_000014_create_dex_table.rs} (81%) create mode 100644 indexer/migration/src/m20221031_000015_create_dex_views.rs diff --git a/indexer/entity/src/dex_mean_price.rs b/indexer/entity/src/dex_mean_price.rs deleted file mode 100644 index 93a494bf..00000000 --- a/indexer/entity/src/dex_mean_price.rs +++ /dev/null @@ -1,66 +0,0 @@ -use sea_orm::entity::prelude::*; -use serde::{Deserialize, Serialize}; - -#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Deserialize, Serialize)] -#[sea_orm(table_name = "DexMeanPrice")] -pub struct Model { - #[sea_orm(primary_key, column_type = "BigInteger")] - pub id: i64, - #[sea_orm(column_type = "BigInteger")] - pub tx_id: i64, - #[sea_orm(column_type = "BigInteger")] - pub address_id: i64, - pub dex: i32, - #[sea_orm(column_type = "BigInteger", nullable)] - pub asset1_id: Option, - #[sea_orm(column_type = "BigInteger", nullable)] - pub asset2_id: Option, - #[sea_orm(column_type = "BigUnsigned")] - pub amount1: u64, - #[sea_orm(column_type = "BigUnsigned")] - pub amount2: u64, -} - -#[derive(Copy, Clone, Debug, DeriveRelation, EnumIter)] -pub enum Relation { - #[sea_orm( - belongs_to = "super::transaction::Entity", - from = "Column::TxId", - to = "super::transaction::Column::Id" - )] - Transaction, - #[sea_orm( - belongs_to = "super::address::Entity", - from = "Column::AddressId", - to = "super::address::Column::Id" - )] - Address, - #[sea_orm( - belongs_to = "super::native_asset::Entity", - from = "Column::Asset1Id", - to = "super::native_asset::Column::Id" - )] - Asset1, - #[sea_orm( - belongs_to = "super::native_asset::Entity", - from = "Column::Asset2Id", - to = "super::native_asset::Column::Id" - )] - Asset2, -} - -// TODO: figure out why this isn't automatically handle by the macros above -impl Related for Entity { - fn to() -> RelationDef { - Relation::Transaction.def() - } -} - -// TODO: figure out why this isn't automatically handle by the macros above -impl Related for Entity { - fn to() -> RelationDef { - Relation::Address.def() - } -} - -impl ActiveModelBehavior for ActiveModel {} diff --git a/indexer/entity/src/dex_swap.rs b/indexer/entity/src/dex_swap.rs index 80bd079d..23d07f71 100644 --- a/indexer/entity/src/dex_swap.rs +++ b/indexer/entity/src/dex_swap.rs @@ -2,7 +2,7 @@ use sea_orm::entity::prelude::*; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel, Deserialize, Serialize)] -#[sea_orm(table_name = "DexSwap")] +#[sea_orm(table_name = "Dex")] pub struct Model { #[sea_orm(primary_key, column_type = "BigInteger")] pub id: i64, @@ -19,7 +19,7 @@ pub struct Model { pub amount1: u64, #[sea_orm(column_type = "BigUnsigned")] pub amount2: u64, - pub direction: bool, + pub operation: i32, } #[derive(Copy, Clone, Debug, DeriveRelation, EnumIter)] @@ -50,20 +50,6 @@ pub enum Relation { Asset2, } -// TODO: figure out why this isn't automatically handle by the macros above -impl Related for Entity { - fn to() -> RelationDef { - Relation::Transaction.def() - } -} - -// TODO: figure out why this isn't automatically handle by the macros above -impl Related for Entity { - fn to() -> RelationDef { - Relation::Address.def() - } -} - impl ActiveModelBehavior for ActiveModel {} #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] @@ -82,3 +68,19 @@ impl From for i32 { } } } + +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub enum Operation { + Sell, + Buy, + Mean, +} +impl From for i32 { + fn from(item: Operation) -> Self { + match item { + Operation::Sell => 0, + Operation::Buy => 1, + Operation::Mean => 2, + } + } +} diff --git a/indexer/entity/src/lib.rs b/indexer/entity/src/lib.rs index 8c2b3b3e..3cb250a0 100644 --- a/indexer/entity/src/lib.rs +++ b/indexer/entity/src/lib.rs @@ -11,7 +11,6 @@ pub mod tx_credential; pub use sea_orm; pub mod asset_mint; pub mod cip25_entry; -pub mod dex_mean_price; pub mod dex_swap; pub mod native_asset; pub mod plutus_data; diff --git a/indexer/entity/src/prelude.rs b/indexer/entity/src/prelude.rs index d720c974..ae559ca9 100644 --- a/indexer/entity/src/prelude.rs +++ b/indexer/entity/src/prelude.rs @@ -19,11 +19,6 @@ pub use super::cip25_entry::{ ActiveModel as Cip25EntryActiveModel, Column as Cip25EntryColumn, Entity as Cip25Entry, Model as Cip25EntryModel, PrimaryKey as Cip25EntryPrimaryKey, Relation as Cip25EntryRelation, }; -pub use super::dex_mean_price::{ - ActiveModel as DexMeanPriceActiveModel, Column as DexMeanPriceColumn, Entity as DexMeanPrice, - Model as DexMeanPriceModel, PrimaryKey as DexMeanPricePrimaryKey, - Relation as DexMeanPriceRelation, -}; pub use super::dex_swap::{ ActiveModel as DexSwapActiveModel, Column as DexSwapColumn, Entity as DexSwap, Model as DexSwapModel, PrimaryKey as DexSwapPrimaryKey, Relation as DexSwapRelation, diff --git a/indexer/execution_plans/dex_prices.toml b/indexer/execution_plans/dex_prices.toml index 88b3b3fc..66c5e962 100644 --- a/indexer/execution_plans/dex_prices.toml +++ b/indexer/execution_plans/dex_prices.toml @@ -45,11 +45,11 @@ readonly=false [MultieraMinSwapV1MeanPriceTask] -[MultieraMinSwapV1SwapTask] - [MultieraSundaeSwapV1MeanPriceTask] [MultieraWingRidersV1SwapTask] +[MultieraMinSwapV1SwapTask] + [MultieraSundaeSwapV1SwapTask] diff --git a/indexer/migration/src/lib.rs b/indexer/migration/src/lib.rs index a58ee818..d7b4d3c9 100644 --- a/indexer/migration/src/lib.rs +++ b/indexer/migration/src/lib.rs @@ -15,8 +15,8 @@ mod m20220520_000010_create_cip25_entry_table; mod m20220528_000011_create_plutus_data_hash_table; mod m20220528_000012_create_plutus_data_table; mod m20220808_000013_create_transaction_reference_input_table; -mod m20221020_000014_create_dex_mean_price_table; -mod m20221031_000015_create_dex_swap_table; +mod m20221031_000014_create_dex_table; +mod m20221031_000015_create_dex_views; pub struct Migrator; @@ -39,8 +39,8 @@ impl MigratorTrait for Migrator { Box::new(m20220528_000011_create_plutus_data_hash_table::Migration), Box::new(m20220528_000012_create_plutus_data_table::Migration), Box::new(m20220808_000013_create_transaction_reference_input_table::Migration), - Box::new(m20221020_000014_create_dex_mean_price_table::Migration), - Box::new(m20221031_000015_create_dex_swap_table::Migration), + Box::new(m20221031_000014_create_dex_table::Migration), + Box::new(m20221031_000015_create_dex_views::Migration), ] } } diff --git a/indexer/migration/src/m20221020_000014_create_dex_mean_price_table.rs b/indexer/migration/src/m20221020_000014_create_dex_mean_price_table.rs deleted file mode 100644 index f712cf1c..00000000 --- a/indexer/migration/src/m20221020_000014_create_dex_mean_price_table.rs +++ /dev/null @@ -1,89 +0,0 @@ -use sea_schema::migration::prelude::*; - -use entity::dex_mean_price::*; -use entity::prelude::{ - Address, AddressColumn, NativeAsset, NativeAssetColumn, Transaction, TransactionColumn, -}; - -pub struct Migration; - -impl MigrationName for Migration { - fn name(&self) -> &str { - "m20221020_000014_create_dex_mean_price_table" - } -} - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(Entity) - .if_not_exists() - .col( - ColumnDef::new(Column::Id) - .big_integer() - .not_null() - .auto_increment() - .primary_key(), - ) - .col(ColumnDef::new(Column::TxId).big_integer().not_null()) - .foreign_key( - ForeignKey::create() - .name("fk-dex_mean_price-tx_id") - .from(Entity, Column::TxId) - .to(Transaction, TransactionColumn::Id) - .on_delete(ForeignKeyAction::Cascade), - ) - .col(ColumnDef::new(Column::AddressId).big_integer().not_null()) - .foreign_key( - ForeignKey::create() - .name("fk-dex_mean_price-address_id") - .from(Entity, Column::AddressId) - .to(Address, AddressColumn::Id) - .on_delete(ForeignKeyAction::Cascade), - ) - .col(ColumnDef::new(Column::Dex).big_integer().not_null()) - .col(ColumnDef::new(Column::Asset1Id).big_integer()) - .foreign_key( - ForeignKey::create() - .name("fk-dex_mean_price-asset1_id") - .from(Entity, Column::Asset1Id) - .to(NativeAsset, NativeAssetColumn::Id) - .on_delete(ForeignKeyAction::Cascade), - ) - .col(ColumnDef::new(Column::Asset2Id).big_integer()) - .foreign_key( - ForeignKey::create() - .name("fk-dex_mean_price-asset2_id") - .from(Entity, Column::Asset2Id) - .to(NativeAsset, NativeAssetColumn::Id) - .on_delete(ForeignKeyAction::Cascade), - ) - .col(ColumnDef::new(Column::Amount1).big_unsigned().not_null()) - .col(ColumnDef::new(Column::Amount2).big_unsigned().not_null()) - .to_owned(), - ) - .await?; - - manager - .create_index( - Index::create() - .table(Entity) - .name("index-dex_mean_price-address-native_asset1-native_asset2-transaction") - .col(Column::Dex) - .col(Column::Asset1Id) - .col(Column::Asset2Id) - .col(Column::TxId) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_table(Table::drop().table(Entity).to_owned()) - .await - } -} diff --git a/indexer/migration/src/m20221031_000015_create_dex_swap_table.rs b/indexer/migration/src/m20221031_000014_create_dex_table.rs similarity index 81% rename from indexer/migration/src/m20221031_000015_create_dex_swap_table.rs rename to indexer/migration/src/m20221031_000014_create_dex_table.rs index c308e628..20997d33 100644 --- a/indexer/migration/src/m20221031_000015_create_dex_swap_table.rs +++ b/indexer/migration/src/m20221031_000014_create_dex_table.rs @@ -9,7 +9,7 @@ pub struct Migration; impl MigrationName for Migration { fn name(&self) -> &str { - "m20221031_000015_create_dex_swap_table" + "m20221031_000014_create_dex_table" } } @@ -31,7 +31,7 @@ impl MigrationTrait for Migration { .col(ColumnDef::new(Column::TxId).big_integer().not_null()) .foreign_key( ForeignKey::create() - .name("fk-dex_swap-tx_id") + .name("fk-dex-tx_id") .from(Entity, Column::TxId) .to(Transaction, TransactionColumn::Id) .on_delete(ForeignKeyAction::Cascade), @@ -39,7 +39,7 @@ impl MigrationTrait for Migration { .col(ColumnDef::new(Column::AddressId).big_integer().not_null()) .foreign_key( ForeignKey::create() - .name("fk-dex_swap-address_id") + .name("fk-dex-address_id") .from(Entity, Column::AddressId) .to(Address, AddressColumn::Id) .on_delete(ForeignKeyAction::Cascade), @@ -48,7 +48,7 @@ impl MigrationTrait for Migration { .col(ColumnDef::new(Column::Asset1Id).big_integer()) .foreign_key( ForeignKey::create() - .name("fk-dex_swap-asset1_id") + .name("fk-dex-asset1_id") .from(Entity, Column::Asset1Id) .to(NativeAsset, NativeAssetColumn::Id) .on_delete(ForeignKeyAction::Cascade), @@ -56,14 +56,14 @@ impl MigrationTrait for Migration { .col(ColumnDef::new(Column::Asset2Id).big_integer()) .foreign_key( ForeignKey::create() - .name("fk-dex_swap-asset2_id") + .name("fk-dex-asset2_id") .from(Entity, Column::Asset2Id) .to(NativeAsset, NativeAssetColumn::Id) .on_delete(ForeignKeyAction::Cascade), ) .col(ColumnDef::new(Column::Amount1).big_unsigned().not_null()) .col(ColumnDef::new(Column::Amount2).big_unsigned().not_null()) - .col(ColumnDef::new(Column::Direction).boolean().not_null()) + .col(ColumnDef::new(Column::Operation).big_integer().not_null()) .to_owned(), ) .await?; @@ -72,13 +72,23 @@ impl MigrationTrait for Migration { .create_index( Index::create() .table(Entity) - .name("index-dex_swap-address-native_asset1-native_asset2-transaction") + .name("index-dex-address-native_asset1-native_asset2-transaction") .col(Column::Dex) .col(Column::Asset1Id) .col(Column::Asset2Id) .col(Column::TxId) .to_owned(), ) + .await?; + + manager + .create_index( + Index::create() + .table(Entity) + .name("index-dex-operation") + .col(Column::Operation) + .to_owned(), + ) .await } diff --git a/indexer/migration/src/m20221031_000015_create_dex_views.rs b/indexer/migration/src/m20221031_000015_create_dex_views.rs new file mode 100644 index 00000000..ce8fe670 --- /dev/null +++ b/indexer/migration/src/m20221031_000015_create_dex_views.rs @@ -0,0 +1,45 @@ +use sea_orm::{ConnectionTrait, Statement}; +use sea_schema::migration::prelude::*; + +pub struct Migration; + +impl MigrationName for Migration { + fn name(&self) -> &str { + "m20221031_000015_create_dex_views" + } +} + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + let sql = r#" + CREATE VIEW "DexSwap" AS SELECT + id, tx_id, address_id, dex, asset1_id, asset2_id, amount1, amount2, operation + FROM "Dex" WHERE "operation" IN (0, 1); + "#; + let stmt = Statement::from_string(manager.get_database_backend(), sql.to_owned()); + manager.get_connection().execute(stmt).await?; + + let sql = r#" + CREATE VIEW "DexMeanPrice" AS SELECT + id, tx_id, address_id, dex, asset1_id, asset2_id, amount1, amount2 + FROM "Dex" WHERE "operation" = 2; + "#; + let stmt = Statement::from_string(manager.get_database_backend(), sql.to_owned()); + manager.get_connection().execute(stmt).await.map(|_| ()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + let sql = r#" + DROP VIEW IF EXISTS "DexSwap"; + "#; + let stmt = Statement::from_string(manager.get_database_backend(), sql.to_owned()); + manager.get_connection().execute(stmt).await?; + + let sql = r#" + DROP VIEW IF EXISTS "DexMeanPrice"; + "#; + let stmt = Statement::from_string(manager.get_database_backend(), sql.to_owned()); + manager.get_connection().execute(stmt).await.map(|_| ()) + } +} diff --git a/indexer/tasks/src/multiera/dex/common.rs b/indexer/tasks/src/multiera/dex/common.rs index 2145c123..752eadac 100644 --- a/indexer/tasks/src/multiera/dex/common.rs +++ b/indexer/tasks/src/multiera/dex/common.rs @@ -4,8 +4,8 @@ use crate::{ multiera::utils::common::{ asset_from_pair, get_plutus_datum_for_output, get_shelley_payment_hash, }, - types::DexSwapDirection, }; +use entity::dex_swap::Operation; use entity::sea_orm::{DatabaseTransaction, Set}; use pallas::{ codec::utils::KeepRaw, @@ -74,7 +74,7 @@ pub struct QueuedSwap { pub asset2: AssetPair, pub amount1: u64, pub amount2: u64, - pub direction: DexSwapDirection, + pub operation: Operation, } pub trait Dex { @@ -214,7 +214,7 @@ pub async fn handle_mean_price( asset_pair_to_id_map.insert(None, None); // ADA // 4) Add mean prices to DB - DexMeanPrice::insert_many( + DexSwap::insert_many( queued_prices .iter() .filter(|price| { @@ -222,7 +222,7 @@ pub async fn handle_mean_price( asset_pair_to_id_map.contains_key(&price.asset1) && asset_pair_to_id_map.contains_key(&price.asset2) }) - .map(|price| DexMeanPriceActiveModel { + .map(|price| DexSwapActiveModel { tx_id: Set(price.tx_id), address_id: Set(multiera_addresses[&price.address].model.id), dex: Set(i32::from(price.dex_type.clone())), @@ -230,6 +230,7 @@ pub async fn handle_mean_price( asset2_id: Set(asset_pair_to_id_map[&price.asset2]), amount1: Set(price.amount1), amount2: Set(price.amount2), + operation: Set(Operation::Mean.into()), ..Default::default() }), ) @@ -301,7 +302,6 @@ pub async fn handle_swap( } // 3) Query for asset ids - // TODO use the query result from mean price task? let found_assets = asset_from_pair( db_tx, &unique_tokens @@ -328,12 +328,12 @@ pub async fn handle_swap( .map(|price| DexSwapActiveModel { tx_id: Set(price.tx_id), address_id: Set(multiera_addresses[&price.address].model.id), - dex: Set(i32::from(price.dex_type.clone())), + dex: Set(price.dex_type.clone().into()), asset1_id: Set(asset_pair_to_id_map[&price.asset1]), asset2_id: Set(asset_pair_to_id_map[&price.asset2]), amount1: Set(price.amount1), amount2: Set(price.amount2), - direction: Set(price.direction.into()), + operation: Set(price.operation.into()), ..Default::default() }), ) diff --git a/indexer/tasks/src/multiera/dex/minswap_v1.rs b/indexer/tasks/src/multiera/dex/minswap_v1.rs index 900ccf59..44f077cc 100644 --- a/indexer/tasks/src/multiera/dex/minswap_v1.rs +++ b/indexer/tasks/src/multiera/dex/minswap_v1.rs @@ -6,15 +6,12 @@ use pallas::ledger::{ traverse::{MultiEraOutput, MultiEraTx}, }; -use crate::{ - era_common::OutputWithTxData, multiera::utils::common::get_asset_amount, - types::DexSwapDirection, -}; - use super::common::{ build_asset, filter_outputs_and_datums_by_address, filter_outputs_and_datums_by_hash, reduce_ada_amount, Dex, DexType, MinSwapV1, QueuedMeanPrice, QueuedSwap, }; +use crate::{era_common::OutputWithTxData, multiera::utils::common::get_asset_amount}; +use entity::dex_swap::Operation; pub const POOL_SCRIPT_HASH1: &str = "e1317b152faac13426e6a83e06ff88a4d62cce3c1634ab0a5ec13309"; pub const POOL_SCRIPT_HASH2: &str = "57c8e718c201fba10a9da1748d675b54281d3b1b983c5d1687fc7317"; @@ -156,19 +153,19 @@ impl Dex for MinSwapV1 { // Get amount and direction let amount1; let amount2; - let direction; + let operation; if target_asset == asset2 { amount1 = get_asset_amount(&input, &asset1) - reduce_ada_amount(&asset1, SWAP_IN_ADA); amount2 = get_asset_amount(&utxo, &asset2) - reduce_ada_amount(&asset2, SWAP_OUT_ADA); - direction = DexSwapDirection::SellAsset1; + operation = Operation::Sell; } else { amount1 = get_asset_amount(&utxo, &asset1) - reduce_ada_amount(&asset1, SWAP_OUT_ADA); amount2 = get_asset_amount(&input, &asset2) - reduce_ada_amount(&asset2, SWAP_IN_ADA); - direction = DexSwapDirection::BuyAsset1; + operation = Operation::Buy } queued_swaps.push(QueuedSwap { tx_id, @@ -178,7 +175,7 @@ impl Dex for MinSwapV1 { asset2: asset2.clone(), amount1, amount2, - direction, + operation, }) } } diff --git a/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs b/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs index a04990e4..4c2347ce 100644 --- a/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs +++ b/indexer/tasks/src/multiera/dex/sundaeswap_v1.rs @@ -6,15 +6,12 @@ use pallas::ledger::{ traverse::{MultiEraOutput, MultiEraTx}, }; -use crate::{ - era_common::OutputWithTxData, multiera::utils::common::get_asset_amount, - types::DexSwapDirection, -}; - use super::common::{ build_asset, filter_outputs_and_datums_by_address, filter_outputs_and_datums_by_hash, reduce_ada_amount, Dex, DexType, QueuedMeanPrice, QueuedSwap, SundaeSwapV1, }; +use crate::{era_common::OutputWithTxData, multiera::utils::common::get_asset_amount}; +use entity::dex_swap::Operation; pub const POOL_SCRIPT_HASH: &str = "4020e7fc2de75a0729c3cc3af715b34d98381e0cdbcfa99c950bc3ac"; pub const REQUEST_SCRIPT_HASH: &str = "ba158766c1bae60e2117ee8987621441fac66a5e0fb9c7aca58cf20a"; @@ -163,10 +160,9 @@ impl Dex for SundaeSwapV1 { asset2: asset2.clone(), amount1, amount2, - direction: if direction == 0 { - DexSwapDirection::SellAsset1 - } else { - DexSwapDirection::BuyAsset1 + operation: match direction == 0 { + true => Operation::Sell, + false => Operation::Buy, }, }) } diff --git a/indexer/tasks/src/multiera/dex/wingriders_v1.rs b/indexer/tasks/src/multiera/dex/wingriders_v1.rs index c4deae70..68c01c85 100644 --- a/indexer/tasks/src/multiera/dex/wingriders_v1.rs +++ b/indexer/tasks/src/multiera/dex/wingriders_v1.rs @@ -5,16 +5,15 @@ use pallas::ledger::{ traverse::{MultiEraOutput, MultiEraTx}, }; -use crate::{ - era_common::OutputWithTxData, - multiera::utils::common::{get_asset_amount, get_plutus_datum_for_output}, - types::DexSwapDirection, -}; - use super::common::{ build_asset, filter_outputs_and_datums_by_hash, reduce_ada_amount, Dex, DexType, QueuedMeanPrice, QueuedSwap, WingRidersV1, }; +use crate::{ + era_common::OutputWithTxData, + multiera::utils::common::{get_asset_amount, get_plutus_datum_for_output}, +}; +use entity::dex_swap::Operation; const POOL_SCRIPT_HASH: &str = "e6c90a5923713af5786963dee0fdffd830ca7e0c86a041d9e5833e91"; const POOL_FIXED_ADA: u64 = 3_000_000; // every pool UTXO holds this amount of ADA @@ -177,10 +176,9 @@ impl Dex for WingRidersV1 { asset2, amount1, amount2, - direction: if direction == 0 { - DexSwapDirection::SellAsset1 - } else { - DexSwapDirection::BuyAsset1 + operation: match direction == 0 { + true => Operation::Sell, + false => Operation::Buy, }, }) } diff --git a/indexer/tasks/src/types.rs b/indexer/tasks/src/types.rs index d25ee00d..f2acf9ef 100644 --- a/indexer/tasks/src/types.rs +++ b/indexer/tasks/src/types.rs @@ -71,18 +71,3 @@ impl From for i32 { // ADA = None, token = Some((policy_id, asset_name)) pub type AssetPair = Option<(Vec, Vec)>; - -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub enum DexSwapDirection { - BuyAsset1, - SellAsset1, -} - -impl From for bool { - fn from(item: DexSwapDirection) -> Self { - match item { - DexSwapDirection::BuyAsset1 => false, - DexSwapDirection::SellAsset1 => true, - } - } -} diff --git a/webserver/server/app/controllers/DexLastPriceController.ts b/webserver/server/app/controllers/DexLastPriceController.ts index 30a4815a..216a604e 100644 --- a/webserver/server/app/controllers/DexLastPriceController.ts +++ b/webserver/server/app/controllers/DexLastPriceController.ts @@ -1,15 +1,12 @@ import { Body, Controller, TsoaResponse, Res, Post, Route, SuccessResponse } from 'tsoa'; import { StatusCodes } from 'http-status-codes'; import { DEX_PRICE_LIMIT } from '../../../shared/constants'; -import tx from 'pg-tx'; import pool from '../services/PgPoolSingleton'; import type { ErrorShape } from '../../../shared/errors'; import { genErrorMessage } from '../../../shared/errors'; import { Errors } from '../../../shared/errors'; -import { expectType } from 'tsd'; import type { EndpointTypes } from '../../../shared/routes'; import { Routes } from '../../../shared/routes'; -import type { DexLastPriceResponse } from '../../../shared/models/DexLastPrice'; import { dexLastPrice } from '../services/DexLastPrice'; @@ -42,23 +39,11 @@ export class DexLastPriceController extends Controller { ); } - // note: we use a SQL transaction to make sure the pagination check works properly - // otherwise, a rollback could happen between getting the pagination info and the history query - const lastPrices = await tx( - pool, - async dbTx => { - return await dexLastPrice({ - dbTx, - assetPairs: requestBody.assetPairs, - type: requestBody.type - }); - } - ); - if ('code' in lastPrices) { - expectType>(true); - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return errorResponse(StatusCodes.CONFLICT, lastPrices); - } + const lastPrices = await dexLastPrice({ + dbTx: await pool.connect(), + assetPairs: requestBody.assetPairs, + type: requestBody.type + }); return lastPrices; } diff --git a/webserver/server/app/models/dex/sqlDexLastPriceMean.queries.ts b/webserver/server/app/models/dex/sqlDexLastPriceMean.queries.ts index a1ef6f5a..e2770170 100644 --- a/webserver/server/app/models/dex/sqlDexLastPriceMean.queries.ts +++ b/webserver/server/app/models/dex/sqlDexLastPriceMean.queries.ts @@ -13,11 +13,11 @@ export interface ISqlDexLastPriceMeanParams { /** 'SqlDexLastPriceMean' return type */ export interface ISqlDexLastPriceMeanResult { - amount1: string; - amount2: string; + amount1: string | null; + amount2: string | null; asset_name1: Buffer | null; asset_name2: Buffer | null; - dex: string; + dex: string | null; policy_id1: Buffer | null; policy_id2: Buffer | null; } diff --git a/webserver/server/app/models/dex/sqlDexLastPriceSwap.queries.ts b/webserver/server/app/models/dex/sqlDexLastPriceSwap.queries.ts index 32da4b48..37f4c2c5 100644 --- a/webserver/server/app/models/dex/sqlDexLastPriceSwap.queries.ts +++ b/webserver/server/app/models/dex/sqlDexLastPriceSwap.queries.ts @@ -7,18 +7,18 @@ export type BufferArray = (Buffer)[]; export interface ISqlDexLastPriceSwapParams { asset_name1: BufferArray | null | void; asset_name2: BufferArray | null | void; - direction: boolean | null | void; + operation: string | null | void; policy_id1: BufferArray | null | void; policy_id2: BufferArray | null | void; } /** 'SqlDexLastPriceSwap' return type */ export interface ISqlDexLastPriceSwapResult { - amount1: string; - amount2: string; + amount1: string | null; + amount2: string | null; asset_name1: Buffer | null; asset_name2: Buffer | null; - dex: string; + dex: string | null; policy_id1: Buffer | null; policy_id2: Buffer | null; } @@ -29,7 +29,7 @@ export interface ISqlDexLastPriceSwapQuery { result: ISqlDexLastPriceSwapResult; } -const sqlDexLastPriceSwapIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true,"direction":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]},{"name":"direction","required":false,"transform":{"type":"scalar"},"locs":[{"a":1280,"b":1289},{"a":1651,"b":1660}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n DISTINCT ON(\"DexSwap\".dex)\n\n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"DexSwap\".amount1,\n \"DexSwap\".amount2,\n \"DexSwap\".dex\nFROM \"DexSwap\"\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"DexSwap\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"DexSwap\".asset2_id\nWHERE\n (\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n AND \"DexSwap\".direction = :direction\n )\n -- Add swap for another direction\n OR\n (\n (\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea),\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n AND \"DexSwap\".direction != :direction\n )\nORDER BY \"DexSwap\".dex, \"DexSwap\".tx_id DESC, \"DexSwap\".id DESC"}; +const sqlDexLastPriceSwapIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true,"operation":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]},{"name":"operation","required":false,"transform":{"type":"scalar"},"locs":[{"a":1280,"b":1289},{"a":1651,"b":1660}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n DISTINCT ON(\"DexSwap\".dex)\n\n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"DexSwap\".amount1,\n \"DexSwap\".amount2,\n \"DexSwap\".dex\nFROM \"DexSwap\"\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"DexSwap\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"DexSwap\".asset2_id\nWHERE\n (\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n AND \"DexSwap\".operation = :operation\n )\n -- Add swap for another direction\n OR\n (\n (\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea),\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n AND \"DexSwap\".operation != :operation\n )\nORDER BY \"DexSwap\".dex, \"DexSwap\".tx_id DESC, \"DexSwap\".id DESC"}; /** * Query generated from SQL: @@ -66,7 +66,7 @@ const sqlDexLastPriceSwapIR: any = {"usedParamSet":{"policy_id1":true,"asset_nam * COALESCE("Asset2".policy_id, ''::bytea), * COALESCE("Asset2".asset_name, ''::bytea) * ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") - * AND "DexSwap".direction = :direction + * AND "DexSwap".operation = :operation * ) * -- Add swap for another direction * OR @@ -77,7 +77,7 @@ const sqlDexLastPriceSwapIR: any = {"usedParamSet":{"policy_id1":true,"asset_nam * COALESCE("Asset1".policy_id, ''::bytea), * COALESCE("Asset1".asset_name, ''::bytea) * ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") - * AND "DexSwap".direction != :direction + * AND "DexSwap".operation != :operation * ) * ORDER BY "DexSwap".dex, "DexSwap".tx_id DESC, "DexSwap".id DESC * ``` diff --git a/webserver/server/app/models/dex/sqlDexLastPriceSwap.sql b/webserver/server/app/models/dex/sqlDexLastPriceSwap.sql index e2422d0a..8800d3d7 100644 --- a/webserver/server/app/models/dex/sqlDexLastPriceSwap.sql +++ b/webserver/server/app/models/dex/sqlDexLastPriceSwap.sql @@ -35,7 +35,7 @@ WHERE COALESCE("Asset2".policy_id, ''::bytea), COALESCE("Asset2".asset_name, ''::bytea) ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") - AND "DexSwap".direction = :direction + AND "DexSwap".operation = :operation ) -- Add swap for another direction OR @@ -46,6 +46,6 @@ WHERE COALESCE("Asset1".policy_id, ''::bytea), COALESCE("Asset1".asset_name, ''::bytea) ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") - AND "DexSwap".direction != :direction + AND "DexSwap".operation != :operation ) ORDER BY "DexSwap".dex, "DexSwap".tx_id DESC, "DexSwap".id DESC; diff --git a/webserver/server/app/models/dex/sqlDexMeanPrice.queries.ts b/webserver/server/app/models/dex/sqlDexMeanPrice.queries.ts index 87b7a92b..0995fa0b 100644 --- a/webserver/server/app/models/dex/sqlDexMeanPrice.queries.ts +++ b/webserver/server/app/models/dex/sqlDexMeanPrice.queries.ts @@ -19,11 +19,11 @@ export interface ISqlDexMeanPriceParams { /** 'SqlDexMeanPrice' return type */ export interface ISqlDexMeanPriceResult { - amount1: string; - amount2: string; + amount1: string | null; + amount2: string | null; asset_name1: Buffer | null; asset_name2: Buffer | null; - dex: string; + dex: string | null; policy_id1: Buffer | null; policy_id2: Buffer | null; tx_hash: Buffer; diff --git a/webserver/server/app/models/dex/sqlDexSwap.queries.ts b/webserver/server/app/models/dex/sqlDexSwap.queries.ts index 916d191f..8835e4da 100644 --- a/webserver/server/app/models/dex/sqlDexSwap.queries.ts +++ b/webserver/server/app/models/dex/sqlDexSwap.queries.ts @@ -19,12 +19,12 @@ export interface ISqlDexSwapParams { /** 'SqlDexSwap' return type */ export interface ISqlDexSwapResult { - amount1: string; - amount2: string; + amount1: string | null; + amount2: string | null; asset_name1: Buffer | null; asset_name2: Buffer | null; - dex: string; - direction: boolean; + dex: string | null; + operation: string | null; policy_id1: Buffer | null; policy_id2: Buffer | null; tx_hash: Buffer; @@ -36,7 +36,7 @@ export interface ISqlDexSwapQuery { result: ISqlDexSwapResult; } -const sqlDexSwapIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true,"dexes":true,"until_tx_id":true,"after_tx_id":true,"limit":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]},{"name":"dexes","required":false,"transform":{"type":"scalar"},"locs":[{"a":1128,"b":1133}]},{"name":"until_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":1745,"b":1756}]},{"name":"after_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":1786,"b":1797}]},{"name":"limit","required":false,"transform":{"type":"scalar"},"locs":[{"a":1846,"b":1851}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n \"Transaction\".hash AS tx_hash, \n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"DexSwap\".amount1,\n \"DexSwap\".amount2,\n \"DexSwap\".direction,\n \"DexSwap\".dex\nFROM \"DexSwap\"\nJOIN \"Transaction\" ON \"Transaction\".id = \"DexSwap\".tx_id\nJOIN \"Address\" ON \"Address\".id = \"DexSwap\".address_id\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"DexSwap\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"DexSwap\".asset2_id\nWHERE\n \"DexSwap\".dex = ANY (:dexes)\n AND\n (\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n OR\n (\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea),\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n )\n AND\n \"DexSwap\".tx_id <= (:until_tx_id)\n AND\n \"DexSwap\".tx_id > (:after_tx_id)\nORDER BY \"DexSwap\".tx_id, \"DexSwap\".id\nLIMIT (:limit)"}; +const sqlDexSwapIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true,"dexes":true,"until_tx_id":true,"after_tx_id":true,"limit":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]},{"name":"dexes","required":false,"transform":{"type":"scalar"},"locs":[{"a":1128,"b":1133}]},{"name":"until_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":1745,"b":1756}]},{"name":"after_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":1786,"b":1797}]},{"name":"limit","required":false,"transform":{"type":"scalar"},"locs":[{"a":1846,"b":1851}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n \"Transaction\".hash AS tx_hash, \n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"DexSwap\".amount1,\n \"DexSwap\".amount2,\n \"DexSwap\".operation,\n \"DexSwap\".dex\nFROM \"DexSwap\"\nJOIN \"Transaction\" ON \"Transaction\".id = \"DexSwap\".tx_id\nJOIN \"Address\" ON \"Address\".id = \"DexSwap\".address_id\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"DexSwap\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"DexSwap\".asset2_id\nWHERE\n \"DexSwap\".dex = ANY (:dexes)\n AND\n (\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n OR\n (\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea),\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n )\n AND\n \"DexSwap\".tx_id <= (:until_tx_id)\n AND\n \"DexSwap\".tx_id > (:after_tx_id)\nORDER BY \"DexSwap\".tx_id, \"DexSwap\".id\nLIMIT (:limit)"}; /** * Query generated from SQL: @@ -60,7 +60,7 @@ const sqlDexSwapIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true, * "Asset2".asset_name AS "asset_name2?", * "DexSwap".amount1, * "DexSwap".amount2, - * "DexSwap".direction, + * "DexSwap".operation, * "DexSwap".dex * FROM "DexSwap" * JOIN "Transaction" ON "Transaction".id = "DexSwap".tx_id diff --git a/webserver/server/app/models/dex/sqlDexSwap.sql b/webserver/server/app/models/dex/sqlDexSwap.sql index ee66fcb1..22ad0601 100644 --- a/webserver/server/app/models/dex/sqlDexSwap.sql +++ b/webserver/server/app/models/dex/sqlDexSwap.sql @@ -22,7 +22,7 @@ SELECT "Asset2".asset_name AS "asset_name2?", "DexSwap".amount1, "DexSwap".amount2, - "DexSwap".direction, + "DexSwap".operation, "DexSwap".dex FROM "DexSwap" JOIN "Transaction" ON "Transaction".id = "DexSwap".tx_id diff --git a/webserver/server/app/services/DexLastPrice.ts b/webserver/server/app/services/DexLastPrice.ts index f5a10c2b..295f3097 100644 --- a/webserver/server/app/services/DexLastPrice.ts +++ b/webserver/server/app/services/DexLastPrice.ts @@ -33,7 +33,7 @@ export async function dexLastPrice( asset_name1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.assetName)), policy_id2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.policyId)), asset_name2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.assetName)), - direction: false + operation: '0' }, request.dbTx); case PriceType.Buy: @@ -42,7 +42,7 @@ export async function dexLastPrice( asset_name1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.assetName)), policy_id2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.policyId)), asset_name2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.assetName)), - direction: true + operation: '1' }, request.dbTx); } })(); @@ -51,9 +51,9 @@ export async function dexLastPrice( lastPrice: lastPrice.map(result => ({ asset1: serializeAsset(result.policy_id1, result.asset_name1), asset2: serializeAsset(result.policy_id2, result.asset_name2), - amount1: result.amount1, - amount2: result.amount2, - dex: valueToDex(result.dex) + amount1: result.amount1 || '0', + amount2: result.amount2 || '0', + dex: valueToDex(result.dex || '-1') })), }; } diff --git a/webserver/server/app/services/DexMeanPrice.ts b/webserver/server/app/services/DexMeanPrice.ts index af3ae0f2..f8282066 100644 --- a/webserver/server/app/services/DexMeanPrice.ts +++ b/webserver/server/app/services/DexMeanPrice.ts @@ -28,11 +28,11 @@ export async function dexMeanPrices( return { meanPrices: meanPrices.map(result => ({ tx_hash: result.tx_hash.toString('hex'), - dex: valueToDex(result.dex), + dex: valueToDex(result.dex || '-1'), asset1: serializeAsset(result.policy_id1, result.asset_name1), asset2: serializeAsset(result.policy_id2, result.asset_name2), - amount1: result.amount1, - amount2: result.amount2, + amount1: result.amount1 || '0', + amount2: result.amount2 || '0', })), }; } diff --git a/webserver/server/app/services/DexSwap.ts b/webserver/server/app/services/DexSwap.ts index 91946b5c..2917c029 100644 --- a/webserver/server/app/services/DexSwap.ts +++ b/webserver/server/app/services/DexSwap.ts @@ -30,12 +30,12 @@ export async function dexSwap( return { swap: swap.map(result => ({ tx_hash: result.tx_hash.toString('hex'), - dex: valueToDex(result.dex), + dex: valueToDex(result.dex || '-1'), asset1: serializeAsset(result.policy_id1, result.asset_name1), asset2: serializeAsset(result.policy_id2, result.asset_name2), - amount1: result.amount1, - amount2: result.amount2, - direction: (result.direction ? Direction.Buy : Direction.Sell) + amount1: result.amount1 || '0', + amount2: result.amount2 || '0', + direction: (result.operation ? Direction.Buy : Direction.Sell) })), }; } diff --git a/webserver/server/app/services/utils.ts b/webserver/server/app/services/utils.ts index cca56fe2..36c44b54 100644 --- a/webserver/server/app/services/utils.ts +++ b/webserver/server/app/services/utils.ts @@ -27,7 +27,7 @@ export function valueToDex(dex: string) { case '1': return Dex.SundaeSwap; case '2': return Dex.MinSwap; } - return Dex.Unknown; + throw new Error(`Unsupported dex: '${dex}'`); } export function dexToValue(dex: Dex) { @@ -36,5 +36,4 @@ export function dexToValue(dex: Dex) { case Dex.SundaeSwap: return '1'; case Dex.MinSwap: return '2'; } - return '-1'; } \ No newline at end of file diff --git a/webserver/shared/models/DexLastPrice.ts b/webserver/shared/models/DexLastPrice.ts index 0d58a816..ee04dd8f 100644 --- a/webserver/shared/models/DexLastPrice.ts +++ b/webserver/shared/models/DexLastPrice.ts @@ -1,9 +1,4 @@ -import { Dex, Asset } from "./common"; - -/** - * @example "2042352568679" - */ -type Amount = string; // uint64 +import { Dex, Asset, Amount } from "./common"; export type DexLastPrice = { asset1: Asset; diff --git a/webserver/shared/models/DexMeanPrice.ts b/webserver/shared/models/DexMeanPrice.ts index 06f7d73e..7427fb8e 100644 --- a/webserver/shared/models/DexMeanPrice.ts +++ b/webserver/shared/models/DexMeanPrice.ts @@ -1,9 +1,5 @@ -import { Dex, Pagination, Asset } from "./common"; +import { Dex, Pagination, Asset, Amount } from "./common"; -/** - * @example "2042352568679" - */ -type Amount = string; // uint64 export type DexMeanPrice = { tx_hash: string; diff --git a/webserver/shared/models/DexSwap.ts b/webserver/shared/models/DexSwap.ts index dc7c157b..e0b8a135 100644 --- a/webserver/shared/models/DexSwap.ts +++ b/webserver/shared/models/DexSwap.ts @@ -1,10 +1,4 @@ -import { Pagination, Dex, Direction, Asset } from "./common"; - -/** - * @example "2042352568679" - */ -type Amount = string; // uint64 - +import { Pagination, Dex, Direction, Asset, Amount } from "./common"; export type DexSwap = { tx_hash: string; diff --git a/webserver/shared/models/common.ts b/webserver/shared/models/common.ts index ffccdeab..8d943cfa 100644 --- a/webserver/shared/models/common.ts +++ b/webserver/shared/models/common.ts @@ -101,10 +101,15 @@ export enum Dex { WingRiders = 'WingRiders', SundaeSwap = 'SundaeSwap', MinSwap = 'MinSwap', - Unknown = 'Unknown', }; export type Asset = { policyId: PolicyId; assetName: AssetName; } | null; + +/** + * @pattern [0-9]+ + * @example: "2042352568679" + */ +export type Amount = string; \ No newline at end of file From 62bdb7b95cecc678aaf70bc73c19342e724e5925 Mon Sep 17 00:00:00 2001 From: Martin Miksanik Date: Wed, 30 Nov 2022 09:44:02 +0100 Subject: [PATCH 40/41] Fix null values from views --- webserver/server/app/services/DexLastPrice.ts | 66 +++++++++---------- webserver/server/app/services/DexMeanPrice.ts | 8 +-- webserver/server/app/services/DexSwap.ts | 10 +-- webserver/shared/models/common.ts | 4 +- 4 files changed, 44 insertions(+), 44 deletions(-) diff --git a/webserver/server/app/services/DexLastPrice.ts b/webserver/server/app/services/DexLastPrice.ts index 295f3097..541fc89f 100644 --- a/webserver/server/app/services/DexLastPrice.ts +++ b/webserver/server/app/services/DexLastPrice.ts @@ -4,56 +4,56 @@ import type { PoolClient } from 'pg'; import { PriceType } from '../../../shared/models/DexLastPrice'; import { sqlDexLastPriceSwap } from '../models/dex/sqlDexLastPriceSwap.queries'; import { sqlDexLastPriceMean } from '../models/dex/sqlDexLastPriceMean.queries'; -import { parseAssetItem, serializeAsset, valueToDex} from './utils'; +import { parseAssetItem, serializeAsset, valueToDex } from './utils'; export async function dexLastPrice( request: { dbTx: PoolClient; - assetPairs: {asset1: Asset, asset2: Asset}[]; + assetPairs: { asset1: Asset, asset2: Asset }[]; type: PriceType; } ): Promise { if (request.assetPairs.length === 0) return { lastPrice: [] }; - - const lastPrice = await (async () => { - switch(request.type) { - case PriceType.Mean: - return await sqlDexLastPriceMean.run({ - policy_id1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.policyId)), - asset_name1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.assetName)), - policy_id2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.policyId)), - asset_name2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.assetName)), - }, request.dbTx); - case PriceType.Sell: - return await sqlDexLastPriceSwap.run({ - policy_id1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.policyId)), - asset_name1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.assetName)), - policy_id2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.policyId)), - asset_name2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.assetName)), - operation: '0' - }, request.dbTx); + const lastPrice = await (async () => { + switch (request.type) { + case PriceType.Mean: + return await sqlDexLastPriceMean.run({ + policy_id1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.policyId)), + asset_name1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.assetName)), + policy_id2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.policyId)), + asset_name2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.assetName)), + }, request.dbTx); - case PriceType.Buy: - return await sqlDexLastPriceSwap.run({ - policy_id1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.policyId)), - asset_name1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.assetName)), - policy_id2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.policyId)), - asset_name2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.assetName)), - operation: '1' - }, request.dbTx); - } - })(); + case PriceType.Sell: + return await sqlDexLastPriceSwap.run({ + policy_id1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.policyId)), + asset_name1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.assetName)), + policy_id2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.policyId)), + asset_name2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.assetName)), + operation: '0' + }, request.dbTx); + + case PriceType.Buy: + return await sqlDexLastPriceSwap.run({ + policy_id1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.policyId)), + asset_name1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.assetName)), + policy_id2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.policyId)), + asset_name2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.assetName)), + operation: '1' + }, request.dbTx); + } + })(); return { lastPrice: lastPrice.map(result => ({ asset1: serializeAsset(result.policy_id1, result.asset_name1), asset2: serializeAsset(result.policy_id2, result.asset_name2), - amount1: result.amount1 || '0', - amount2: result.amount2 || '0', - dex: valueToDex(result.dex || '-1') + amount1: result.amount1 ?? '0', + amount2: result.amount2 ?? '0', + dex: valueToDex(result.dex ?? '-1') })), }; } diff --git a/webserver/server/app/services/DexMeanPrice.ts b/webserver/server/app/services/DexMeanPrice.ts index f8282066..2bf7cbd9 100644 --- a/webserver/server/app/services/DexMeanPrice.ts +++ b/webserver/server/app/services/DexMeanPrice.ts @@ -9,7 +9,7 @@ export async function dexMeanPrices( request: TransactionPaginationType & { dbTx: PoolClient; dexes: Array; - assetPairs: {asset1: Asset, asset2: Asset}[]; + assetPairs: { asset1: Asset, asset2: Asset }[]; limit: number; } ): Promise { @@ -28,11 +28,11 @@ export async function dexMeanPrices( return { meanPrices: meanPrices.map(result => ({ tx_hash: result.tx_hash.toString('hex'), - dex: valueToDex(result.dex || '-1'), + dex: valueToDex(result.dex ?? '-1'), asset1: serializeAsset(result.policy_id1, result.asset_name1), asset2: serializeAsset(result.policy_id2, result.asset_name2), - amount1: result.amount1 || '0', - amount2: result.amount2 || '0', + amount1: result.amount1 ?? '0', + amount2: result.amount2 ?? '0', })), }; } diff --git a/webserver/server/app/services/DexSwap.ts b/webserver/server/app/services/DexSwap.ts index 2917c029..a4fcc6d5 100644 --- a/webserver/server/app/services/DexSwap.ts +++ b/webserver/server/app/services/DexSwap.ts @@ -4,14 +4,14 @@ import type { PoolClient } from 'pg'; import type { TransactionPaginationType } from './PaginationService'; import type { Dex } from '../../../shared/models/common'; import { sqlDexSwap } from '../models/dex/sqlDexSwap.queries'; -import { parseAssetItem, serializeAsset, valueToDex, dexToValue} from './utils'; +import { parseAssetItem, serializeAsset, valueToDex, dexToValue } from './utils'; import { Direction } from '../../../shared/models/common'; export async function dexSwap( request: TransactionPaginationType & { dbTx: PoolClient; dexes: Array; - assetPairs: {asset1: Asset, asset2: Asset}[]; + assetPairs: { asset1: Asset, asset2: Asset }[]; limit: number; } ): Promise { @@ -30,11 +30,11 @@ export async function dexSwap( return { swap: swap.map(result => ({ tx_hash: result.tx_hash.toString('hex'), - dex: valueToDex(result.dex || '-1'), + dex: valueToDex(result.dex ?? '-1'), asset1: serializeAsset(result.policy_id1, result.asset_name1), asset2: serializeAsset(result.policy_id2, result.asset_name2), - amount1: result.amount1 || '0', - amount2: result.amount2 || '0', + amount1: result.amount1 ?? '0', + amount2: result.amount2 ?? '0', direction: (result.operation ? Direction.Buy : Direction.Sell) })), }; diff --git a/webserver/shared/models/common.ts b/webserver/shared/models/common.ts index 8d943cfa..4f6d8729 100644 --- a/webserver/shared/models/common.ts +++ b/webserver/shared/models/common.ts @@ -109,7 +109,7 @@ export type Asset = { } | null; /** - * @pattern [0-9]+ - * @example: "2042352568679" + * @pattern [1-9][0-9]* + * @example "2042352568679" */ export type Amount = string; \ No newline at end of file From 2cb5ab2ac965536ab3e0bb6567e22f9624aca18b Mon Sep 17 00:00:00 2001 From: Martin Miksanik Date: Thu, 1 Dec 2022 14:44:14 +0100 Subject: [PATCH 41/41] Remove views and add documentation for mean swap values --- docs/bin/openapi.json | 496 +++++++++++++++++- indexer/migration/src/lib.rs | 2 - .../src/m20221031_000015_create_dex_views.rs | 45 -- indexer/tasks/src/multiera/dex/common.rs | 2 + .../app/controllers/DexLastPriceController.ts | 1 + .../app/controllers/DexMeanPriceController.ts | 1 + .../app/models/dex/sqlDexLastPrice.queries.ts | 88 ++++ ...xLastPriceSwap.sql => sqlDexLastPrice.sql} | 22 +- .../models/dex/sqlDexLastPriceMean.queries.ts | 80 --- .../app/models/dex/sqlDexLastPriceMean.sql | 45 -- .../models/dex/sqlDexLastPriceSwap.queries.ts | 87 --- .../app/models/dex/sqlDexMeanPrice.queries.ts | 32 +- .../server/app/models/dex/sqlDexMeanPrice.sql | 24 +- .../app/models/dex/sqlDexSwap.queries.ts | 36 +- .../server/app/models/dex/sqlDexSwap.sql | 26 +- webserver/server/app/services/DexLastPrice.ts | 23 +- webserver/server/app/services/DexMeanPrice.ts | 6 +- webserver/shared/models/DexLastPrice.ts | 3 + 18 files changed, 673 insertions(+), 346 deletions(-) delete mode 100644 indexer/migration/src/m20221031_000015_create_dex_views.rs create mode 100644 webserver/server/app/models/dex/sqlDexLastPrice.queries.ts rename webserver/server/app/models/dex/{sqlDexLastPriceSwap.sql => sqlDexLastPrice.sql} (75%) delete mode 100644 webserver/server/app/models/dex/sqlDexLastPriceMean.queries.ts delete mode 100644 webserver/server/app/models/dex/sqlDexLastPriceMean.sql delete mode 100644 webserver/server/app/models/dex/sqlDexLastPriceSwap.queries.ts diff --git a/docs/bin/openapi.json b/docs/bin/openapi.json index f46e5c5c..80107ca2 100644 --- a/docs/bin/openapi.json +++ b/docs/bin/openapi.json @@ -282,6 +282,315 @@ } ] }, + "PolicyId": { + "type": "string", + "example": "b863bc7369f46136ac1048adb2fa7dae3af944c3bbb2be2f216a8d4f", + "pattern": "[0-9a-fA-F]{56}" + }, + "AssetName": { + "type": "string", + "example": "42657272794e617679", + "pattern": "[0-9a-fA-F]{0,64}" + }, + "Asset": { + "properties": { + "assetName": { + "$ref": "#/components/schemas/AssetName" + }, + "policyId": { + "$ref": "#/components/schemas/PolicyId" + } + }, + "required": [ + "assetName", + "policyId" + ], + "type": "object", + "nullable": true + }, + "Amount": { + "type": "string", + "example": "2042352568679", + "pattern": "[1-9][0-9]*" + }, + "Dex": { + "enum": [ + "WingRiders", + "SundaeSwap", + "MinSwap" + ], + "type": "string" + }, + "DexLastPrice": { + "properties": { + "dex": { + "$ref": "#/components/schemas/Dex" + }, + "amount2": { + "$ref": "#/components/schemas/Amount" + }, + "amount1": { + "$ref": "#/components/schemas/Amount" + }, + "asset2": { + "$ref": "#/components/schemas/Asset" + }, + "asset1": { + "$ref": "#/components/schemas/Asset" + } + }, + "required": [ + "dex", + "amount2", + "amount1", + "asset2", + "asset1" + ], + "type": "object" + }, + "DexLastPriceResponse": { + "properties": { + "lastPrice": { + "items": { + "$ref": "#/components/schemas/DexLastPrice" + }, + "type": "array" + } + }, + "required": [ + "lastPrice" + ], + "type": "object" + }, + "PriceType": { + "enum": [ + "buy", + "sell", + "mean" + ], + "type": "string" + }, + "DexLastPriceRequest": { + "properties": { + "type": { + "$ref": "#/components/schemas/PriceType" + }, + "assetPairs": { + "items": { + "properties": { + "asset2": { + "$ref": "#/components/schemas/Asset" + }, + "asset1": { + "$ref": "#/components/schemas/Asset" + } + }, + "required": [ + "asset2", + "asset1" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "type", + "assetPairs" + ], + "type": "object" + }, + "DexMeanPrice": { + "properties": { + "amount2": { + "$ref": "#/components/schemas/Amount" + }, + "amount1": { + "$ref": "#/components/schemas/Amount" + }, + "asset2": { + "$ref": "#/components/schemas/Asset" + }, + "asset1": { + "$ref": "#/components/schemas/Asset" + }, + "dex": { + "$ref": "#/components/schemas/Dex" + }, + "tx_hash": { + "type": "string" + } + }, + "required": [ + "amount2", + "amount1", + "asset2", + "asset1", + "dex", + "tx_hash" + ], + "type": "object" + }, + "DexMeanPriceResponse": { + "properties": { + "meanPrices": { + "items": { + "$ref": "#/components/schemas/DexMeanPrice" + }, + "type": "array" + } + }, + "required": [ + "meanPrices" + ], + "type": "object" + }, + "DexMeanPriceRequest": { + "allOf": [ + { + "properties": { + "limit": { + "type": "number", + "format": "double", + "description": "Defaults to `DEX_PRICE_LIMIT.RESPONSE`" + }, + "dexes": { + "items": { + "$ref": "#/components/schemas/Dex" + }, + "type": "array" + }, + "assetPairs": { + "items": { + "properties": { + "asset2": { + "$ref": "#/components/schemas/Asset" + }, + "asset1": { + "$ref": "#/components/schemas/Asset" + } + }, + "required": [ + "asset2", + "asset1" + ], + "type": "object" + }, + "type": "array" + } + }, + "required": [ + "dexes", + "assetPairs" + ], + "type": "object" + }, + { + "$ref": "#/components/schemas/Pagination" + } + ] + }, + "Direction": { + "enum": [ + "buy", + "sell" + ], + "type": "string" + }, + "DexSwap": { + "properties": { + "direction": { + "$ref": "#/components/schemas/Direction" + }, + "amount2": { + "$ref": "#/components/schemas/Amount" + }, + "amount1": { + "$ref": "#/components/schemas/Amount" + }, + "asset2": { + "$ref": "#/components/schemas/Asset" + }, + "asset1": { + "$ref": "#/components/schemas/Asset" + }, + "dex": { + "$ref": "#/components/schemas/Dex" + }, + "tx_hash": { + "type": "string" + } + }, + "required": [ + "direction", + "amount2", + "amount1", + "asset2", + "asset1", + "dex", + "tx_hash" + ], + "type": "object" + }, + "DexSwapResponse": { + "properties": { + "swap": { + "items": { + "$ref": "#/components/schemas/DexSwap" + }, + "type": "array" + } + }, + "required": [ + "swap" + ], + "type": "object" + }, + "DexSwapRequest": { + "allOf": [ + { + "properties": { + "limit": { + "type": "number", + "format": "double", + "description": "Defaults to `DEX_PRICE_LIMIT.RESPONSE`" + }, + "assetPairs": { + "items": { + "properties": { + "asset2": { + "$ref": "#/components/schemas/Asset" + }, + "asset1": { + "$ref": "#/components/schemas/Asset" + } + }, + "required": [ + "asset2", + "asset1" + ], + "type": "object" + }, + "type": "array" + }, + "dexes": { + "items": { + "$ref": "#/components/schemas/Dex" + }, + "type": "array" + } + }, + "required": [ + "assetPairs", + "dexes" + ], + "type": "object" + }, + { + "$ref": "#/components/schemas/Pagination" + } + ] + }, "Cip25Metadata": { "type": "string", "example": "a365636f6c6f72672330303030383065696d616765783a697066733a2f2f697066732f516d534b593167317a5375506b3536635869324b38524e766961526b44485633505a756a7474663755676b343379646e616d656a4265727279204e617679", @@ -311,11 +620,6 @@ ], "type": "object" }, - "AssetName": { - "type": "string", - "example": "42657272794e617679", - "pattern": "[0-9a-fA-F]{0,64}" - }, "PolicyIdAssetMapType": { "properties": { "assets": { @@ -533,7 +837,7 @@ }, "info": { "title": "carp", - "version": "2.0.0", + "version": "2.0.3", "description": "API for the Postgres database generated by Carp", "license": { "name": "MIT" @@ -704,6 +1008,186 @@ } } }, + "/dex/last-price": { + "post": { + "operationId": "DexLastPrice", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DexLastPriceResponse" + } + } + } + }, + "400": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorShape" + } + } + } + }, + "409": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorShape" + } + } + } + }, + "422": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorShape" + } + } + } + } + }, + "description": "Gets the swap prices for the given liquidity pool and asset pairs.\nOperation \"mean\" is not AVG from the last values, but the remaining amount of assets on the pool output", + "security": [], + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DexLastPriceRequest" + } + } + } + } + } + }, + "/dex/mean-price": { + "post": { + "operationId": "DexMeanPrice", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DexMeanPriceResponse" + } + } + } + }, + "400": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorShape" + } + } + } + }, + "409": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorShape" + } + } + } + }, + "422": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorShape" + } + } + } + } + }, + "description": "Gets the mean prices for the given liquidity pool and asset pairs.\nMean is not AVG from the last values, but the remaining amount of assets on the pool output", + "security": [], + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DexMeanPriceRequest" + } + } + } + } + } + }, + "/dex/swap-price": { + "post": { + "operationId": "DexSwap", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DexSwapResponse" + } + } + } + }, + "400": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorShape" + } + } + } + }, + "409": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorShape" + } + } + } + }, + "422": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorShape" + } + } + } + } + }, + "description": "Gets the swap prices for the given liquidity pool and asset pairs.", + "security": [], + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/DexSwapRequest" + } + } + } + } + } + }, "/metadata/nft": { "post": { "operationId": "MetadataNft", diff --git a/indexer/migration/src/lib.rs b/indexer/migration/src/lib.rs index d7b4d3c9..f3778177 100644 --- a/indexer/migration/src/lib.rs +++ b/indexer/migration/src/lib.rs @@ -16,7 +16,6 @@ mod m20220528_000011_create_plutus_data_hash_table; mod m20220528_000012_create_plutus_data_table; mod m20220808_000013_create_transaction_reference_input_table; mod m20221031_000014_create_dex_table; -mod m20221031_000015_create_dex_views; pub struct Migrator; @@ -40,7 +39,6 @@ impl MigratorTrait for Migrator { Box::new(m20220528_000012_create_plutus_data_table::Migration), Box::new(m20220808_000013_create_transaction_reference_input_table::Migration), Box::new(m20221031_000014_create_dex_table::Migration), - Box::new(m20221031_000015_create_dex_views::Migration), ] } } diff --git a/indexer/migration/src/m20221031_000015_create_dex_views.rs b/indexer/migration/src/m20221031_000015_create_dex_views.rs deleted file mode 100644 index ce8fe670..00000000 --- a/indexer/migration/src/m20221031_000015_create_dex_views.rs +++ /dev/null @@ -1,45 +0,0 @@ -use sea_orm::{ConnectionTrait, Statement}; -use sea_schema::migration::prelude::*; - -pub struct Migration; - -impl MigrationName for Migration { - fn name(&self) -> &str { - "m20221031_000015_create_dex_views" - } -} - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - let sql = r#" - CREATE VIEW "DexSwap" AS SELECT - id, tx_id, address_id, dex, asset1_id, asset2_id, amount1, amount2, operation - FROM "Dex" WHERE "operation" IN (0, 1); - "#; - let stmt = Statement::from_string(manager.get_database_backend(), sql.to_owned()); - manager.get_connection().execute(stmt).await?; - - let sql = r#" - CREATE VIEW "DexMeanPrice" AS SELECT - id, tx_id, address_id, dex, asset1_id, asset2_id, amount1, amount2 - FROM "Dex" WHERE "operation" = 2; - "#; - let stmt = Statement::from_string(manager.get_database_backend(), sql.to_owned()); - manager.get_connection().execute(stmt).await.map(|_| ()) - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - let sql = r#" - DROP VIEW IF EXISTS "DexSwap"; - "#; - let stmt = Statement::from_string(manager.get_database_backend(), sql.to_owned()); - manager.get_connection().execute(stmt).await?; - - let sql = r#" - DROP VIEW IF EXISTS "DexMeanPrice"; - "#; - let stmt = Statement::from_string(manager.get_database_backend(), sql.to_owned()); - manager.get_connection().execute(stmt).await.map(|_| ()) - } -} diff --git a/indexer/tasks/src/multiera/dex/common.rs b/indexer/tasks/src/multiera/dex/common.rs index 752eadac..4d0d8e88 100644 --- a/indexer/tasks/src/multiera/dex/common.rs +++ b/indexer/tasks/src/multiera/dex/common.rs @@ -78,6 +78,7 @@ pub struct QueuedSwap { } pub trait Dex { + /// Handle the rest of the assets on the pool address fn queue_mean_price( &self, queued_prices: &mut Vec, @@ -85,6 +86,7 @@ pub trait Dex { tx_id: i64, ) -> Result<(), String>; + /// Handle amount of each swap operation fn queue_swap( &self, queued_swaps: &mut Vec, diff --git a/webserver/server/app/controllers/DexLastPriceController.ts b/webserver/server/app/controllers/DexLastPriceController.ts index 216a604e..6b937dab 100644 --- a/webserver/server/app/controllers/DexLastPriceController.ts +++ b/webserver/server/app/controllers/DexLastPriceController.ts @@ -16,6 +16,7 @@ const route = Routes.dexLastPrice; export class DexLastPriceController extends Controller { /** * Gets the swap prices for the given liquidity pool and asset pairs. + * Operation "mean" is not AVG from the last values, but the remaining amount of assets on the pool output */ @SuccessResponse(`${StatusCodes.OK}`) @Post() diff --git a/webserver/server/app/controllers/DexMeanPriceController.ts b/webserver/server/app/controllers/DexMeanPriceController.ts index 5338f2ac..58b0d758 100644 --- a/webserver/server/app/controllers/DexMeanPriceController.ts +++ b/webserver/server/app/controllers/DexMeanPriceController.ts @@ -19,6 +19,7 @@ const route = Routes.dexMeanPrice; export class DexMeanPriceController extends Controller { /** * Gets the mean prices for the given liquidity pool and asset pairs. + * Mean is not AVG from the last values, but the remaining amount of assets on the pool output */ @SuccessResponse(`${StatusCodes.OK}`) @Post() diff --git a/webserver/server/app/models/dex/sqlDexLastPrice.queries.ts b/webserver/server/app/models/dex/sqlDexLastPrice.queries.ts new file mode 100644 index 00000000..f1a24f81 --- /dev/null +++ b/webserver/server/app/models/dex/sqlDexLastPrice.queries.ts @@ -0,0 +1,88 @@ +/** Types generated for queries found in "app/models/dex/sqlDexLastPrice.sql" */ +import { PreparedQuery } from '@pgtyped/query'; + +export type BufferArray = (Buffer)[]; + +/** 'SqlDexLastPrice' parameters type */ +export interface ISqlDexLastPriceParams { + asset_name1: BufferArray | null | void; + asset_name2: BufferArray | null | void; + operation1: string | null | void; + operation2: string | null | void; + policy_id1: BufferArray | null | void; + policy_id2: BufferArray | null | void; +} + +/** 'SqlDexLastPrice' return type */ +export interface ISqlDexLastPriceResult { + amount1: string; + amount2: string; + asset_name1: Buffer | null; + asset_name2: Buffer | null; + dex: string; + policy_id1: Buffer | null; + policy_id2: Buffer | null; +} + +/** 'SqlDexLastPrice' query type */ +export interface ISqlDexLastPriceQuery { + params: ISqlDexLastPriceParams; + result: ISqlDexLastPriceResult; +} + +const sqlDexLastPriceIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true,"operation1":true,"operation2":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]},{"name":"operation1","required":false,"transform":{"type":"scalar"},"locs":[{"a":1248,"b":1258}]},{"name":"operation2","required":false,"transform":{"type":"scalar"},"locs":[{"a":1615,"b":1625}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n DISTINCT ON(\"Dex\".dex)\n\n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"Dex\".amount1,\n \"Dex\".amount2,\n \"Dex\".dex\nFROM \"Dex\"\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"Dex\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"Dex\".asset2_id\nWHERE\n (\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n AND \"Dex\".operation = :operation1\n )\n -- Add swap for another direction\n OR\n (\n (\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea),\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n AND \"Dex\".operation = :operation2\n )\nORDER BY \"Dex\".dex, \"Dex\".tx_id DESC, \"Dex\".id DESC"}; + +/** + * Query generated from SQL: + * ``` + * WITH "AssetPairs" AS ( + * SELECT policy_id1, asset_name1, policy_id2, asset_name2 + * FROM + * unnest( + * + * (:policy_id1)::bytea[], + * (:asset_name1)::bytea[], + * (:policy_id2)::bytea[], + * (:asset_name2)::bytea[] + * ) x(policy_id1, asset_name1, policy_id2, asset_name2) + * ) + * SELECT + * DISTINCT ON("Dex".dex) + * + * "Asset1".policy_id AS "policy_id1?", + * "Asset1".asset_name AS "asset_name1?", + * "Asset2".policy_id AS "policy_id2?", + * "Asset2".asset_name AS "asset_name2?", + * "Dex".amount1, + * "Dex".amount2, + * "Dex".dex + * FROM "Dex" + * LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "Dex".asset1_id + * LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "Dex".asset2_id + * WHERE + * ( + * ( + * COALESCE("Asset1".policy_id, ''::bytea), + * COALESCE("Asset1".asset_name, ''::bytea), + * COALESCE("Asset2".policy_id, ''::bytea), + * COALESCE("Asset2".asset_name, ''::bytea) + * ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") + * AND "Dex".operation = :operation1 + * ) + * -- Add swap for another direction + * OR + * ( + * ( + * COALESCE("Asset2".policy_id, ''::bytea), + * COALESCE("Asset2".asset_name, ''::bytea), + * COALESCE("Asset1".policy_id, ''::bytea), + * COALESCE("Asset1".asset_name, ''::bytea) + * ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") + * AND "Dex".operation = :operation2 + * ) + * ORDER BY "Dex".dex, "Dex".tx_id DESC, "Dex".id DESC + * ``` + */ +export const sqlDexLastPrice = new PreparedQuery(sqlDexLastPriceIR); + + diff --git a/webserver/server/app/models/dex/sqlDexLastPriceSwap.sql b/webserver/server/app/models/dex/sqlDexLastPrice.sql similarity index 75% rename from webserver/server/app/models/dex/sqlDexLastPriceSwap.sql rename to webserver/server/app/models/dex/sqlDexLastPrice.sql index 8800d3d7..2ac79e54 100644 --- a/webserver/server/app/models/dex/sqlDexLastPriceSwap.sql +++ b/webserver/server/app/models/dex/sqlDexLastPrice.sql @@ -1,4 +1,4 @@ -/* @name sqlDexLastPriceSwap */ +/* @name sqlDexLastPrice */ WITH "AssetPairs" AS ( SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM @@ -15,18 +15,18 @@ WITH "AssetPairs" AS ( ) x(policy_id1, asset_name1, policy_id2, asset_name2) ) SELECT - DISTINCT ON("DexSwap".dex) + DISTINCT ON("Dex".dex) "Asset1".policy_id AS "policy_id1?", "Asset1".asset_name AS "asset_name1?", "Asset2".policy_id AS "policy_id2?", "Asset2".asset_name AS "asset_name2?", - "DexSwap".amount1, - "DexSwap".amount2, - "DexSwap".dex -FROM "DexSwap" -LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "DexSwap".asset1_id -LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "DexSwap".asset2_id + "Dex".amount1, + "Dex".amount2, + "Dex".dex +FROM "Dex" +LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "Dex".asset1_id +LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "Dex".asset2_id WHERE ( ( @@ -35,7 +35,7 @@ WHERE COALESCE("Asset2".policy_id, ''::bytea), COALESCE("Asset2".asset_name, ''::bytea) ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") - AND "DexSwap".operation = :operation + AND "Dex".operation = :operation1 ) -- Add swap for another direction OR @@ -46,6 +46,6 @@ WHERE COALESCE("Asset1".policy_id, ''::bytea), COALESCE("Asset1".asset_name, ''::bytea) ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") - AND "DexSwap".operation != :operation + AND "Dex".operation = :operation2 ) -ORDER BY "DexSwap".dex, "DexSwap".tx_id DESC, "DexSwap".id DESC; +ORDER BY "Dex".dex, "Dex".tx_id DESC, "Dex".id DESC; diff --git a/webserver/server/app/models/dex/sqlDexLastPriceMean.queries.ts b/webserver/server/app/models/dex/sqlDexLastPriceMean.queries.ts deleted file mode 100644 index e2770170..00000000 --- a/webserver/server/app/models/dex/sqlDexLastPriceMean.queries.ts +++ /dev/null @@ -1,80 +0,0 @@ -/** Types generated for queries found in "app/models/dex/sqlDexLastPriceMean.sql" */ -import { PreparedQuery } from '@pgtyped/query'; - -export type BufferArray = (Buffer)[]; - -/** 'SqlDexLastPriceMean' parameters type */ -export interface ISqlDexLastPriceMeanParams { - asset_name1: BufferArray | null | void; - asset_name2: BufferArray | null | void; - policy_id1: BufferArray | null | void; - policy_id2: BufferArray | null | void; -} - -/** 'SqlDexLastPriceMean' return type */ -export interface ISqlDexLastPriceMeanResult { - amount1: string | null; - amount2: string | null; - asset_name1: Buffer | null; - asset_name2: Buffer | null; - dex: string | null; - policy_id1: Buffer | null; - policy_id2: Buffer | null; -} - -/** 'SqlDexLastPriceMean' query type */ -export interface ISqlDexLastPriceMeanQuery { - params: ISqlDexLastPriceMeanParams; - result: ISqlDexLastPriceMeanResult; -} - -const sqlDexLastPriceMeanIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n DISTINCT ON(\"DexMeanPrice\".dex)\n\n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"DexMeanPrice\".amount1,\n \"DexMeanPrice\".amount2,\n \"DexMeanPrice\".dex\nFROM \"DexMeanPrice\"\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"DexMeanPrice\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"DexMeanPrice\".asset2_id\nWHERE\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n -- Add swap for another direction\n OR\n (\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea),\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\nORDER BY \"DexMeanPrice\".dex, \"DexMeanPrice\".tx_id DESC, \"DexMeanPrice\".id DESC"}; - -/** - * Query generated from SQL: - * ``` - * WITH "AssetPairs" AS ( - * SELECT policy_id1, asset_name1, policy_id2, asset_name2 - * FROM - * unnest( - * - * (:policy_id1)::bytea[], - * (:asset_name1)::bytea[], - * (:policy_id2)::bytea[], - * (:asset_name2)::bytea[] - * ) x(policy_id1, asset_name1, policy_id2, asset_name2) - * ) - * SELECT - * DISTINCT ON("DexMeanPrice".dex) - * - * "Asset1".policy_id AS "policy_id1?", - * "Asset1".asset_name AS "asset_name1?", - * "Asset2".policy_id AS "policy_id2?", - * "Asset2".asset_name AS "asset_name2?", - * "DexMeanPrice".amount1, - * "DexMeanPrice".amount2, - * "DexMeanPrice".dex - * FROM "DexMeanPrice" - * LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "DexMeanPrice".asset1_id - * LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "DexMeanPrice".asset2_id - * WHERE - * ( - * COALESCE("Asset1".policy_id, ''::bytea), - * COALESCE("Asset1".asset_name, ''::bytea), - * COALESCE("Asset2".policy_id, ''::bytea), - * COALESCE("Asset2".asset_name, ''::bytea) - * ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") - * -- Add swap for another direction - * OR - * ( - * COALESCE("Asset2".policy_id, ''::bytea), - * COALESCE("Asset2".asset_name, ''::bytea), - * COALESCE("Asset1".policy_id, ''::bytea), - * COALESCE("Asset1".asset_name, ''::bytea) - * ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") - * ORDER BY "DexMeanPrice".dex, "DexMeanPrice".tx_id DESC, "DexMeanPrice".id DESC - * ``` - */ -export const sqlDexLastPriceMean = new PreparedQuery(sqlDexLastPriceMeanIR); - - diff --git a/webserver/server/app/models/dex/sqlDexLastPriceMean.sql b/webserver/server/app/models/dex/sqlDexLastPriceMean.sql deleted file mode 100644 index cbd08ed8..00000000 --- a/webserver/server/app/models/dex/sqlDexLastPriceMean.sql +++ /dev/null @@ -1,45 +0,0 @@ -/* @name sqlDexLastPriceMean */ -WITH "AssetPairs" AS ( - SELECT policy_id1, asset_name1, policy_id2, asset_name2 - FROM - unnest( - /* - Aparrently, we can't make pgtyped understand that these are actually (bytea | NULL)[]. - We will pass in ('', '') instead of (NULL, NULL) for ADA and do the NULL->'' conversion - below when filtering the assets (see the COALESCE). - */ - (:policy_id1)::bytea[], - (:asset_name1)::bytea[], - (:policy_id2)::bytea[], - (:asset_name2)::bytea[] - ) x(policy_id1, asset_name1, policy_id2, asset_name2) -) -SELECT - DISTINCT ON("DexMeanPrice".dex) - - "Asset1".policy_id AS "policy_id1?", - "Asset1".asset_name AS "asset_name1?", - "Asset2".policy_id AS "policy_id2?", - "Asset2".asset_name AS "asset_name2?", - "DexMeanPrice".amount1, - "DexMeanPrice".amount2, - "DexMeanPrice".dex -FROM "DexMeanPrice" -LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "DexMeanPrice".asset1_id -LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "DexMeanPrice".asset2_id -WHERE - ( - COALESCE("Asset1".policy_id, ''::bytea), - COALESCE("Asset1".asset_name, ''::bytea), - COALESCE("Asset2".policy_id, ''::bytea), - COALESCE("Asset2".asset_name, ''::bytea) - ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") - -- Add swap for another direction - OR - ( - COALESCE("Asset2".policy_id, ''::bytea), - COALESCE("Asset2".asset_name, ''::bytea), - COALESCE("Asset1".policy_id, ''::bytea), - COALESCE("Asset1".asset_name, ''::bytea) - ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") -ORDER BY "DexMeanPrice".dex, "DexMeanPrice".tx_id DESC, "DexMeanPrice".id DESC; diff --git a/webserver/server/app/models/dex/sqlDexLastPriceSwap.queries.ts b/webserver/server/app/models/dex/sqlDexLastPriceSwap.queries.ts deleted file mode 100644 index 37f4c2c5..00000000 --- a/webserver/server/app/models/dex/sqlDexLastPriceSwap.queries.ts +++ /dev/null @@ -1,87 +0,0 @@ -/** Types generated for queries found in "app/models/dex/sqlDexLastPriceSwap.sql" */ -import { PreparedQuery } from '@pgtyped/query'; - -export type BufferArray = (Buffer)[]; - -/** 'SqlDexLastPriceSwap' parameters type */ -export interface ISqlDexLastPriceSwapParams { - asset_name1: BufferArray | null | void; - asset_name2: BufferArray | null | void; - operation: string | null | void; - policy_id1: BufferArray | null | void; - policy_id2: BufferArray | null | void; -} - -/** 'SqlDexLastPriceSwap' return type */ -export interface ISqlDexLastPriceSwapResult { - amount1: string | null; - amount2: string | null; - asset_name1: Buffer | null; - asset_name2: Buffer | null; - dex: string | null; - policy_id1: Buffer | null; - policy_id2: Buffer | null; -} - -/** 'SqlDexLastPriceSwap' query type */ -export interface ISqlDexLastPriceSwapQuery { - params: ISqlDexLastPriceSwapParams; - result: ISqlDexLastPriceSwapResult; -} - -const sqlDexLastPriceSwapIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true,"operation":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]},{"name":"operation","required":false,"transform":{"type":"scalar"},"locs":[{"a":1280,"b":1289},{"a":1651,"b":1660}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n DISTINCT ON(\"DexSwap\".dex)\n\n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"DexSwap\".amount1,\n \"DexSwap\".amount2,\n \"DexSwap\".dex\nFROM \"DexSwap\"\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"DexSwap\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"DexSwap\".asset2_id\nWHERE\n (\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n AND \"DexSwap\".operation = :operation\n )\n -- Add swap for another direction\n OR\n (\n (\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea),\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n AND \"DexSwap\".operation != :operation\n )\nORDER BY \"DexSwap\".dex, \"DexSwap\".tx_id DESC, \"DexSwap\".id DESC"}; - -/** - * Query generated from SQL: - * ``` - * WITH "AssetPairs" AS ( - * SELECT policy_id1, asset_name1, policy_id2, asset_name2 - * FROM - * unnest( - * - * (:policy_id1)::bytea[], - * (:asset_name1)::bytea[], - * (:policy_id2)::bytea[], - * (:asset_name2)::bytea[] - * ) x(policy_id1, asset_name1, policy_id2, asset_name2) - * ) - * SELECT - * DISTINCT ON("DexSwap".dex) - * - * "Asset1".policy_id AS "policy_id1?", - * "Asset1".asset_name AS "asset_name1?", - * "Asset2".policy_id AS "policy_id2?", - * "Asset2".asset_name AS "asset_name2?", - * "DexSwap".amount1, - * "DexSwap".amount2, - * "DexSwap".dex - * FROM "DexSwap" - * LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "DexSwap".asset1_id - * LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "DexSwap".asset2_id - * WHERE - * ( - * ( - * COALESCE("Asset1".policy_id, ''::bytea), - * COALESCE("Asset1".asset_name, ''::bytea), - * COALESCE("Asset2".policy_id, ''::bytea), - * COALESCE("Asset2".asset_name, ''::bytea) - * ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") - * AND "DexSwap".operation = :operation - * ) - * -- Add swap for another direction - * OR - * ( - * ( - * COALESCE("Asset2".policy_id, ''::bytea), - * COALESCE("Asset2".asset_name, ''::bytea), - * COALESCE("Asset1".policy_id, ''::bytea), - * COALESCE("Asset1".asset_name, ''::bytea) - * ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") - * AND "DexSwap".operation != :operation - * ) - * ORDER BY "DexSwap".dex, "DexSwap".tx_id DESC, "DexSwap".id DESC - * ``` - */ -export const sqlDexLastPriceSwap = new PreparedQuery(sqlDexLastPriceSwapIR); - - diff --git a/webserver/server/app/models/dex/sqlDexMeanPrice.queries.ts b/webserver/server/app/models/dex/sqlDexMeanPrice.queries.ts index 0995fa0b..9d801055 100644 --- a/webserver/server/app/models/dex/sqlDexMeanPrice.queries.ts +++ b/webserver/server/app/models/dex/sqlDexMeanPrice.queries.ts @@ -19,11 +19,11 @@ export interface ISqlDexMeanPriceParams { /** 'SqlDexMeanPrice' return type */ export interface ISqlDexMeanPriceResult { - amount1: string | null; - amount2: string | null; + amount1: string; + amount2: string; asset_name1: Buffer | null; asset_name2: Buffer | null; - dex: string | null; + dex: string; policy_id1: Buffer | null; policy_id2: Buffer | null; tx_hash: Buffer; @@ -35,7 +35,7 @@ export interface ISqlDexMeanPriceQuery { result: ISqlDexMeanPriceResult; } -const sqlDexMeanPriceIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true,"dexes":true,"until_tx_id":true,"after_tx_id":true,"limit":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]},{"name":"dexes","required":false,"transform":{"type":"scalar"},"locs":[{"a":1096,"b":1101}]},{"name":"until_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":1718,"b":1729}]},{"name":"after_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":1764,"b":1775}]},{"name":"limit","required":false,"transform":{"type":"scalar"},"locs":[{"a":1834,"b":1839}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n \"Transaction\".hash AS tx_hash,\n \"DexMeanPrice\".dex as dex,\n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"DexMeanPrice\".amount1,\n \"DexMeanPrice\".amount2\nFROM \"DexMeanPrice\"\nJOIN \"Transaction\" ON \"Transaction\".id = \"DexMeanPrice\".tx_id\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"DexMeanPrice\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"DexMeanPrice\".asset2_id\nWHERE\n \"DexMeanPrice\".dex = ANY (:dexes)\n AND\n (\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n OR\n (\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea),\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n )\n AND\n \"DexMeanPrice\".tx_id <= (:until_tx_id)\n AND\n \"DexMeanPrice\".tx_id > (:after_tx_id)\nORDER BY \"DexMeanPrice\".tx_id, \"DexMeanPrice\".id\nLIMIT (:limit)"}; +const sqlDexMeanPriceIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true,"dexes":true,"until_tx_id":true,"after_tx_id":true,"limit":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]},{"name":"dexes","required":false,"transform":{"type":"scalar"},"locs":[{"a":1024,"b":1029}]},{"name":"until_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":1665,"b":1676}]},{"name":"after_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":1702,"b":1713}]},{"name":"limit","required":false,"transform":{"type":"scalar"},"locs":[{"a":1754,"b":1759}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n \"Transaction\".hash AS tx_hash,\n \"Dex\".dex as dex,\n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"Dex\".amount1,\n \"Dex\".amount2\nFROM \"Dex\"\nJOIN \"Transaction\" ON \"Transaction\".id = \"Dex\".tx_id\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"Dex\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"Dex\".asset2_id\nWHERE\n \"Dex\".dex = ANY (:dexes)\n AND\n (\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n OR\n (\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea),\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n )\n AND\n \"Dex\".operation = 2\n AND\n \"Dex\".tx_id <= (:until_tx_id)\n AND\n \"Dex\".tx_id > (:after_tx_id)\nORDER BY \"Dex\".tx_id, \"Dex\".id\nLIMIT (:limit)"}; /** * Query generated from SQL: @@ -53,19 +53,19 @@ const sqlDexMeanPriceIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1": * ) * SELECT * "Transaction".hash AS tx_hash, - * "DexMeanPrice".dex as dex, + * "Dex".dex as dex, * "Asset1".policy_id AS "policy_id1?", * "Asset1".asset_name AS "asset_name1?", * "Asset2".policy_id AS "policy_id2?", * "Asset2".asset_name AS "asset_name2?", - * "DexMeanPrice".amount1, - * "DexMeanPrice".amount2 - * FROM "DexMeanPrice" - * JOIN "Transaction" ON "Transaction".id = "DexMeanPrice".tx_id - * LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "DexMeanPrice".asset1_id - * LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "DexMeanPrice".asset2_id + * "Dex".amount1, + * "Dex".amount2 + * FROM "Dex" + * JOIN "Transaction" ON "Transaction".id = "Dex".tx_id + * LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "Dex".asset1_id + * LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "Dex".asset2_id * WHERE - * "DexMeanPrice".dex = ANY (:dexes) + * "Dex".dex = ANY (:dexes) * AND * ( * ( @@ -83,10 +83,12 @@ const sqlDexMeanPriceIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1": * ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") * ) * AND - * "DexMeanPrice".tx_id <= (:until_tx_id) + * "Dex".operation = 2 * AND - * "DexMeanPrice".tx_id > (:after_tx_id) - * ORDER BY "DexMeanPrice".tx_id, "DexMeanPrice".id + * "Dex".tx_id <= (:until_tx_id) + * AND + * "Dex".tx_id > (:after_tx_id) + * ORDER BY "Dex".tx_id, "Dex".id * LIMIT (:limit) * ``` */ diff --git a/webserver/server/app/models/dex/sqlDexMeanPrice.sql b/webserver/server/app/models/dex/sqlDexMeanPrice.sql index 52fe5a0a..61d8c556 100644 --- a/webserver/server/app/models/dex/sqlDexMeanPrice.sql +++ b/webserver/server/app/models/dex/sqlDexMeanPrice.sql @@ -16,19 +16,19 @@ WITH "AssetPairs" AS ( ) SELECT "Transaction".hash AS tx_hash, - "DexMeanPrice".dex as dex, + "Dex".dex as dex, "Asset1".policy_id AS "policy_id1?", "Asset1".asset_name AS "asset_name1?", "Asset2".policy_id AS "policy_id2?", "Asset2".asset_name AS "asset_name2?", - "DexMeanPrice".amount1, - "DexMeanPrice".amount2 -FROM "DexMeanPrice" -JOIN "Transaction" ON "Transaction".id = "DexMeanPrice".tx_id -LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "DexMeanPrice".asset1_id -LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "DexMeanPrice".asset2_id + "Dex".amount1, + "Dex".amount2 +FROM "Dex" +JOIN "Transaction" ON "Transaction".id = "Dex".tx_id +LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "Dex".asset1_id +LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "Dex".asset2_id WHERE - "DexMeanPrice".dex = ANY (:dexes) + "Dex".dex = ANY (:dexes) AND ( ( @@ -46,8 +46,10 @@ WHERE ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") ) AND - "DexMeanPrice".tx_id <= (:until_tx_id) + "Dex".operation = 2 AND - "DexMeanPrice".tx_id > (:after_tx_id) -ORDER BY "DexMeanPrice".tx_id, "DexMeanPrice".id + "Dex".tx_id <= (:until_tx_id) + AND + "Dex".tx_id > (:after_tx_id) +ORDER BY "Dex".tx_id, "Dex".id LIMIT (:limit); diff --git a/webserver/server/app/models/dex/sqlDexSwap.queries.ts b/webserver/server/app/models/dex/sqlDexSwap.queries.ts index 8835e4da..3ae3526f 100644 --- a/webserver/server/app/models/dex/sqlDexSwap.queries.ts +++ b/webserver/server/app/models/dex/sqlDexSwap.queries.ts @@ -19,12 +19,12 @@ export interface ISqlDexSwapParams { /** 'SqlDexSwap' return type */ export interface ISqlDexSwapResult { - amount1: string | null; - amount2: string | null; + amount1: string; + amount2: string; asset_name1: Buffer | null; asset_name2: Buffer | null; - dex: string | null; - operation: string | null; + dex: string; + operation: string; policy_id1: Buffer | null; policy_id2: Buffer | null; tx_hash: Buffer; @@ -36,7 +36,7 @@ export interface ISqlDexSwapQuery { result: ISqlDexSwapResult; } -const sqlDexSwapIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true,"dexes":true,"until_tx_id":true,"after_tx_id":true,"limit":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]},{"name":"dexes","required":false,"transform":{"type":"scalar"},"locs":[{"a":1128,"b":1133}]},{"name":"until_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":1745,"b":1756}]},{"name":"after_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":1786,"b":1797}]},{"name":"limit","required":false,"transform":{"type":"scalar"},"locs":[{"a":1846,"b":1851}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n \"Transaction\".hash AS tx_hash, \n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"DexSwap\".amount1,\n \"DexSwap\".amount2,\n \"DexSwap\".operation,\n \"DexSwap\".dex\nFROM \"DexSwap\"\nJOIN \"Transaction\" ON \"Transaction\".id = \"DexSwap\".tx_id\nJOIN \"Address\" ON \"Address\".id = \"DexSwap\".address_id\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"DexSwap\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"DexSwap\".asset2_id\nWHERE\n \"DexSwap\".dex = ANY (:dexes)\n AND\n (\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n OR\n (\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea),\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n )\n AND\n \"DexSwap\".tx_id <= (:until_tx_id)\n AND\n \"DexSwap\".tx_id > (:after_tx_id)\nORDER BY \"DexSwap\".tx_id, \"DexSwap\".id\nLIMIT (:limit)"}; +const sqlDexSwapIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true,"policy_id2":true,"asset_name2":true,"dexes":true,"until_tx_id":true,"after_tx_id":true,"limit":true},"params":[{"name":"policy_id1","required":false,"transform":{"type":"scalar"},"locs":[{"a":370,"b":380}]},{"name":"asset_name1","required":false,"transform":{"type":"scalar"},"locs":[{"a":400,"b":411}]},{"name":"policy_id2","required":false,"transform":{"type":"scalar"},"locs":[{"a":431,"b":441}]},{"name":"asset_name2","required":false,"transform":{"type":"scalar"},"locs":[{"a":461,"b":472}]},{"name":"dexes","required":false,"transform":{"type":"scalar"},"locs":[{"a":1088,"b":1093}]},{"name":"until_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":1701,"b":1712}]},{"name":"after_tx_id","required":false,"transform":{"type":"scalar"},"locs":[{"a":1738,"b":1749}]},{"name":"limit","required":false,"transform":{"type":"scalar"},"locs":[{"a":1790,"b":1795}]}],"statement":"WITH \"AssetPairs\" AS (\n SELECT policy_id1, asset_name1, policy_id2, asset_name2\n FROM\n unnest(\n \n (:policy_id1)::bytea[],\n (:asset_name1)::bytea[],\n (:policy_id2)::bytea[],\n (:asset_name2)::bytea[]\n ) x(policy_id1, asset_name1, policy_id2, asset_name2)\n)\nSELECT\n \"Transaction\".hash AS tx_hash, \n \"Asset1\".policy_id AS \"policy_id1?\",\n \"Asset1\".asset_name AS \"asset_name1?\",\n \"Asset2\".policy_id AS \"policy_id2?\",\n \"Asset2\".asset_name AS \"asset_name2?\",\n \"Dex\".amount1,\n \"Dex\".amount2,\n \"Dex\".operation,\n \"Dex\".dex\nFROM \"Dex\"\nJOIN \"Transaction\" ON \"Transaction\".id = \"Dex\".tx_id\nJOIN \"Address\" ON \"Address\".id = \"Dex\".address_id\nLEFT JOIN \"NativeAsset\" as \"Asset1\" ON \"Asset1\".id = \"Dex\".asset1_id\nLEFT JOIN \"NativeAsset\" as \"Asset2\" ON \"Asset2\".id = \"Dex\".asset2_id\nWHERE\n \"Dex\".dex = ANY (:dexes)\n AND\n (\n (\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea),\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n OR\n (\n COALESCE(\"Asset2\".policy_id, ''::bytea),\n COALESCE(\"Asset2\".asset_name, ''::bytea),\n COALESCE(\"Asset1\".policy_id, ''::bytea),\n COALESCE(\"Asset1\".asset_name, ''::bytea)\n ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM \"AssetPairs\")\n )\n AND\n \"Dex\".tx_id <= (:until_tx_id)\n AND\n \"Dex\".tx_id > (:after_tx_id)\nORDER BY \"Dex\".tx_id, \"Dex\".id\nLIMIT (:limit)"}; /** * Query generated from SQL: @@ -58,17 +58,17 @@ const sqlDexSwapIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true, * "Asset1".asset_name AS "asset_name1?", * "Asset2".policy_id AS "policy_id2?", * "Asset2".asset_name AS "asset_name2?", - * "DexSwap".amount1, - * "DexSwap".amount2, - * "DexSwap".operation, - * "DexSwap".dex - * FROM "DexSwap" - * JOIN "Transaction" ON "Transaction".id = "DexSwap".tx_id - * JOIN "Address" ON "Address".id = "DexSwap".address_id - * LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "DexSwap".asset1_id - * LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "DexSwap".asset2_id + * "Dex".amount1, + * "Dex".amount2, + * "Dex".operation, + * "Dex".dex + * FROM "Dex" + * JOIN "Transaction" ON "Transaction".id = "Dex".tx_id + * JOIN "Address" ON "Address".id = "Dex".address_id + * LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "Dex".asset1_id + * LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "Dex".asset2_id * WHERE - * "DexSwap".dex = ANY (:dexes) + * "Dex".dex = ANY (:dexes) * AND * ( * ( @@ -86,10 +86,10 @@ const sqlDexSwapIR: any = {"usedParamSet":{"policy_id1":true,"asset_name1":true, * ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") * ) * AND - * "DexSwap".tx_id <= (:until_tx_id) + * "Dex".tx_id <= (:until_tx_id) * AND - * "DexSwap".tx_id > (:after_tx_id) - * ORDER BY "DexSwap".tx_id, "DexSwap".id + * "Dex".tx_id > (:after_tx_id) + * ORDER BY "Dex".tx_id, "Dex".id * LIMIT (:limit) * ``` */ diff --git a/webserver/server/app/models/dex/sqlDexSwap.sql b/webserver/server/app/models/dex/sqlDexSwap.sql index 22ad0601..b66f48af 100644 --- a/webserver/server/app/models/dex/sqlDexSwap.sql +++ b/webserver/server/app/models/dex/sqlDexSwap.sql @@ -20,17 +20,17 @@ SELECT "Asset1".asset_name AS "asset_name1?", "Asset2".policy_id AS "policy_id2?", "Asset2".asset_name AS "asset_name2?", - "DexSwap".amount1, - "DexSwap".amount2, - "DexSwap".operation, - "DexSwap".dex -FROM "DexSwap" -JOIN "Transaction" ON "Transaction".id = "DexSwap".tx_id -JOIN "Address" ON "Address".id = "DexSwap".address_id -LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "DexSwap".asset1_id -LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "DexSwap".asset2_id + "Dex".amount1, + "Dex".amount2, + "Dex".operation, + "Dex".dex +FROM "Dex" +JOIN "Transaction" ON "Transaction".id = "Dex".tx_id +JOIN "Address" ON "Address".id = "Dex".address_id +LEFT JOIN "NativeAsset" as "Asset1" ON "Asset1".id = "Dex".asset1_id +LEFT JOIN "NativeAsset" as "Asset2" ON "Asset2".id = "Dex".asset2_id WHERE - "DexSwap".dex = ANY (:dexes) + "Dex".dex = ANY (:dexes) AND ( ( @@ -48,8 +48,8 @@ WHERE ) IN (SELECT policy_id1, asset_name1, policy_id2, asset_name2 FROM "AssetPairs") ) AND - "DexSwap".tx_id <= (:until_tx_id) + "Dex".tx_id <= (:until_tx_id) AND - "DexSwap".tx_id > (:after_tx_id) -ORDER BY "DexSwap".tx_id, "DexSwap".id + "Dex".tx_id > (:after_tx_id) +ORDER BY "Dex".tx_id, "Dex".id LIMIT (:limit); diff --git a/webserver/server/app/services/DexLastPrice.ts b/webserver/server/app/services/DexLastPrice.ts index 541fc89f..3b70c006 100644 --- a/webserver/server/app/services/DexLastPrice.ts +++ b/webserver/server/app/services/DexLastPrice.ts @@ -2,8 +2,7 @@ import type { DexLastPriceResponse } from '../../../shared/models/DexLastPrice'; import type { Asset } from '../../../shared/models/common'; import type { PoolClient } from 'pg'; import { PriceType } from '../../../shared/models/DexLastPrice'; -import { sqlDexLastPriceSwap } from '../models/dex/sqlDexLastPriceSwap.queries'; -import { sqlDexLastPriceMean } from '../models/dex/sqlDexLastPriceMean.queries'; +import { sqlDexLastPrice } from '../models/dex/sqlDexLastPrice.queries'; import { parseAssetItem, serializeAsset, valueToDex } from './utils'; @@ -20,29 +19,33 @@ export async function dexLastPrice( const lastPrice = await (async () => { switch (request.type) { case PriceType.Mean: - return await sqlDexLastPriceMean.run({ + return await sqlDexLastPrice.run({ policy_id1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.policyId)), asset_name1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.assetName)), policy_id2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.policyId)), asset_name2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.assetName)), + operation1: '2', + operation2: '2' }, request.dbTx); case PriceType.Sell: - return await sqlDexLastPriceSwap.run({ + return await sqlDexLastPrice.run({ policy_id1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.policyId)), asset_name1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.assetName)), policy_id2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.policyId)), asset_name2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.assetName)), - operation: '0' + operation1: '0', + operation2: '1' }, request.dbTx); case PriceType.Buy: - return await sqlDexLastPriceSwap.run({ + return await sqlDexLastPrice.run({ policy_id1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.policyId)), asset_name1: request.assetPairs.map(pair => parseAssetItem(pair.asset1?.assetName)), policy_id2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.policyId)), asset_name2: request.assetPairs.map(pair => parseAssetItem(pair.asset2?.assetName)), - operation: '1' + operation1: '1', + operation2: '0' }, request.dbTx); } })(); @@ -51,9 +54,9 @@ export async function dexLastPrice( lastPrice: lastPrice.map(result => ({ asset1: serializeAsset(result.policy_id1, result.asset_name1), asset2: serializeAsset(result.policy_id2, result.asset_name2), - amount1: result.amount1 ?? '0', - amount2: result.amount2 ?? '0', - dex: valueToDex(result.dex ?? '-1') + amount1: result.amount1, + amount2: result.amount2, + dex: valueToDex(result.dex) })), }; } diff --git a/webserver/server/app/services/DexMeanPrice.ts b/webserver/server/app/services/DexMeanPrice.ts index 2bf7cbd9..1ae3cc03 100644 --- a/webserver/server/app/services/DexMeanPrice.ts +++ b/webserver/server/app/services/DexMeanPrice.ts @@ -28,11 +28,11 @@ export async function dexMeanPrices( return { meanPrices: meanPrices.map(result => ({ tx_hash: result.tx_hash.toString('hex'), - dex: valueToDex(result.dex ?? '-1'), + dex: valueToDex(result.dex), asset1: serializeAsset(result.policy_id1, result.asset_name1), asset2: serializeAsset(result.policy_id2, result.asset_name2), - amount1: result.amount1 ?? '0', - amount2: result.amount2 ?? '0', + amount1: result.amount1, + amount2: result.amount2, })), }; } diff --git a/webserver/shared/models/DexLastPrice.ts b/webserver/shared/models/DexLastPrice.ts index ee04dd8f..8b43ae86 100644 --- a/webserver/shared/models/DexLastPrice.ts +++ b/webserver/shared/models/DexLastPrice.ts @@ -11,6 +11,9 @@ export type DexLastPrice = { export enum PriceType { Buy = "buy", Sell = "sell", + /** + * Mean is not AVG from the last values, but the remaining amount of assets on the pool output + */ Mean = "mean", };