Skip to content

Commit

Permalink
Transaction stores: remove code duplication for detecting fiat swaps
Browse files Browse the repository at this point in the history
  • Loading branch information
danimoh committed Apr 22, 2024
1 parent 47b1857 commit 57991e6
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 202 deletions.
24 changes: 11 additions & 13 deletions src/lib/swap/utils/Assets.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
import { SwapAsset } from '@nimiq/fastspot-api';
import { CryptoCurrency, FiatCurrency } from '../../Constants';

export function assetToCurrency(asset: SwapAsset): string { // eslint-disable-line consistent-return
switch (asset) { // eslint-disable-line default-case
case SwapAsset.NIM:
return CryptoCurrency.NIM;
case SwapAsset.BTC:
return CryptoCurrency.BTC;
case SwapAsset.USDC:
case SwapAsset.USDC_MATIC:
return CryptoCurrency.USDC;
case SwapAsset.EUR:
return FiatCurrency.EUR;
// The default case is unnecessary as TypeScript throws a compiler error if a case is missing
}
export function assetToCurrency(asset: Exclude<SwapAsset, SwapAsset.EUR>): CryptoCurrency;
export function assetToCurrency(asset: SwapAsset.EUR): FiatCurrency;
export function assetToCurrency(asset: SwapAsset): CryptoCurrency | FiatCurrency;
export function assetToCurrency(asset: SwapAsset): CryptoCurrency | FiatCurrency {
return {
[SwapAsset.NIM]: CryptoCurrency.NIM,
[SwapAsset.BTC]: CryptoCurrency.BTC,
[SwapAsset.USDC]: CryptoCurrency.USDC,
[SwapAsset.USDC_MATIC]: CryptoCurrency.USDC,
[SwapAsset.EUR]: FiatCurrency.EUR,
}[asset];
}
69 changes: 5 additions & 64 deletions src/stores/BtcTransactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ import Vue from 'vue';
import { createStore } from 'pinia';
import { getHistoricExchangeRates, isHistorySupportedFiatCurrency } from '@nimiq/utils';
import { TransactionDetails, PlainOutput, TransactionState } from '@nimiq/electrum-client';
import { getContract, SwapAsset } from '@nimiq/fastspot-api';
import { SwapAsset } from '@nimiq/fastspot-api';
import { useFiatStore } from './Fiat';
import { CryptoCurrency, FiatCurrency, FIAT_API_PROVIDER_TX_HISTORY, FIAT_PRICE_UNAVAILABLE } from '../lib/Constants';
import { useBtcAddressStore } from './BtcAddress';
import { isHtlcFunding, isHtlcRefunding, isHtlcSettlement, HTLC_ADDRESS_LENGTH } from '../lib/BtcHtlcDetection';
import { useSwapsStore } from './Swaps';
import { getEurPerCrypto, getFiatFees } from '../lib/swap/utils/Functions';

export type Transaction = Omit<TransactionDetails, 'outputs'> & {
addresses: string[],
Expand Down Expand Up @@ -215,7 +214,7 @@ export const useBtcTransactionsStore = createStore({
// Note: this method should not modify the transaction itself or the transaction store, to avoid race conditions with
// other methods that do so.
async function detectSwap(transaction: Transaction) {
const { state: swaps$, addFundingData, addSettlementData } = useSwapsStore();
const { state: swaps$, addFundingData, addSettlementData, detectSwapFiatCounterpart } = useSwapsStore();
if (swaps$.swapByTransaction[transaction.transactionHash]) return; // already known

const [fundingData, refundingData, settlementData] = await Promise.all([
Expand Down Expand Up @@ -244,37 +243,8 @@ async function detectSwap(transaction: Transaction) {
const htlcAddress = transaction.outputs
.find((output) => output.address?.length === HTLC_ADDRESS_LENGTH)!
.address;
if (!swaps$.swaps[fundingData.hash].out && htlcAddress) {
// Check this swap with the Fastspot API to detect if this was a EUR swap
const contractWithEstimate = await getContract(SwapAsset.BTC, htlcAddress).catch(() => undefined);
if (contractWithEstimate?.to.asset === SwapAsset.EUR) {
const exchangeRate = {
[CryptoCurrency.BTC]: {
[FiatCurrency.EUR]: getEurPerCrypto(
SwapAsset.BTC,
contractWithEstimate,
),
},
};
const fiatFees = getFiatFees(
contractWithEstimate,
CryptoCurrency.BTC,
exchangeRate,
FiatCurrency.EUR,
null,
);

addSettlementData(fundingData.hash, {
asset: SwapAsset.EUR,
amount: contractWithEstimate.to.amount,
// We cannot get bank info or EUR HTLC details from this.
}, {
fees: {
totalFee: fiatFees.funding.total,
asset: SwapAsset.EUR,
},
});
}
if (htlcAddress) {
await detectSwapFiatCounterpart(htlcAddress, fundingData.hash, 'settlement', SwapAsset.BTC);
}
}

Expand All @@ -295,35 +265,6 @@ async function detectSwap(transaction: Transaction) {
outputIndex: settlementData.outputIndex,
});

if (!swaps$.swaps[settlementData.hash].in) {
// Check this swap with the Fastspot API to detect if this was a EUR swap
const contractWithEstimate = await getContract(SwapAsset.BTC, transaction.inputs[0].address!)
.catch(() => undefined);
if (contractWithEstimate?.from.asset === SwapAsset.EUR) {
const exchangeRate = {
[CryptoCurrency.BTC]: {
[FiatCurrency.EUR]: getEurPerCrypto(SwapAsset.BTC, contractWithEstimate),
},
};
const fiatFees = getFiatFees(
contractWithEstimate,
CryptoCurrency.BTC,
exchangeRate,
FiatCurrency.EUR,
null,
);

addFundingData(settlementData.hash, {
asset: SwapAsset.EUR,
amount: contractWithEstimate.from.amount,
// We cannot get bank info or EUR HTLC details from this.
}, {
fees: {
totalFee: fiatFees.settlement.total,
asset: SwapAsset.EUR,
},
});
}
}
await detectSwapFiatCounterpart(transaction.inputs[0].address!, settlementData.hash, 'funding', SwapAsset.BTC);
}
}
46 changes: 45 additions & 1 deletion src/stores/Swaps.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { createStore } from 'pinia';
import { TransactionDetails as BtcTransactionDetails } from '@nimiq/electrum-client';
import { Swap as SwapObject, SwapAsset } from '@nimiq/fastspot-api';
import { Swap as SwapObject, SwapAsset, getContract } from '@nimiq/fastspot-api';
import { DeniedReason, Htlc as OasisHtlc, SepaClearingInstruction, SettlementStatus } from '@nimiq/oasis-api';
import { FiatCurrency } from '../lib/Constants';
import { assetToCurrency } from '../lib/swap/utils/Assets';
import { getEurPerCrypto, getFiatFees } from '../lib/swap/utils/Functions';
import { Transaction as PolygonTransaction } from './UsdcTransactions';

export enum SwapState {
Expand Down Expand Up @@ -172,5 +175,46 @@ export const useSwapsStore = createStore({
setPromoBoxVisible(visible: boolean) {
this.state.promoBoxVisible = visible;
},

async detectSwapFiatCounterpart(
contractAddress: string,
hashRoot: string,
type: 'funding' | 'settlement',
otherAsset: Exclude<SwapAsset, SwapAsset.EUR>,
) {
// Check swap with the Fastspot API to detect if it was a EUR swap.
const [addSwapData, swapProp, contractProp, feesProp] = type === 'funding'
? [this.addFundingData.bind(this), 'in', 'from', 'settlement'] as const
: [this.addSettlementData.bind(this), 'out', 'to', 'funding'] as const;
const otherCurrency = assetToCurrency(otherAsset);
const swap = this.state.swaps[hashRoot];
if (!swap || !!swap[swapProp]) return; // not a swap, or counterpart already known
const contractWithEstimate = await getContract(otherAsset, contractAddress).catch(() => undefined);
if (contractWithEstimate?.[contractProp].asset !== SwapAsset.EUR) return; // No contract or no EUR

const exchangeRate = {
[otherCurrency]: {
[FiatCurrency.EUR]: getEurPerCrypto(otherAsset, contractWithEstimate),
},
};
const fees = getFiatFees(
contractWithEstimate,
otherCurrency,
exchangeRate,
FiatCurrency.EUR,
null,
);

addSwapData(hashRoot, {
asset: SwapAsset.EUR,
amount: contractWithEstimate[contractProp].amount,
// We cannot get bank info or EUR HTLC details from this.
}, {
fees: {
totalFee: fees[feesProp].total,
asset: SwapAsset.EUR,
},
});
},
},
});
65 changes: 4 additions & 61 deletions src/stores/Transactions.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import Vue from 'vue';
import { getHistoricExchangeRates, isHistorySupportedFiatCurrency } from '@nimiq/utils';
import { getContract, SwapAsset } from '@nimiq/fastspot-api';
import { SwapAsset } from '@nimiq/fastspot-api';
import { createStore } from 'pinia';
import Config from 'config';
import { useFiatStore } from './Fiat';
import { CryptoCurrency, FiatCurrency, FIAT_API_PROVIDER_TX_HISTORY, FIAT_PRICE_UNAVAILABLE } from '../lib/Constants';
import { detectProxyTransactions, cleanupKnownProxyTransactions } from '../lib/ProxyDetection';
import { useSwapsStore } from './Swaps';
import { getNetworkClient } from '../network';
import { getEurPerCrypto, getFiatFees } from '../lib/swap/utils/Functions';
import { AddressInfo, useAddressStore } from './Address';

export type Transaction = ReturnType<Nimiq.Client.TransactionDetails['toPlain']> & {
Expand Down Expand Up @@ -223,7 +222,7 @@ export const useTransactionsStore = createStore({
// Note: this method should not modify the transaction itself or the transaction store, to avoid race conditions with
// other methods that do so.
async function detectSwap(transaction: Transaction, knownTransactions: Transaction[]) {
const { state: swaps$, addFundingData, addSettlementData } = useSwapsStore();
const { state: swaps$, addFundingData, addSettlementData, detectSwapFiatCounterpart } = useSwapsStore();
if (swaps$.swapByTransaction[transaction.transactionHash]) return; // already known

// HTLC Creation
Expand All @@ -248,35 +247,7 @@ async function detectSwap(transaction: Transaction, knownTransactions: Transacti
},
});

if (!swaps$.swaps[fundingData.hashRoot].out) {
// Check this swap with the Fastspot API to detect if this was a EUR swap
const contractWithEstimate = await getContract(SwapAsset.NIM, transaction.recipient).catch(() => undefined);
if (contractWithEstimate?.to.asset === SwapAsset.EUR) {
const exchangeRate = {
[CryptoCurrency.NIM]: {
[FiatCurrency.EUR]: getEurPerCrypto(SwapAsset.NIM, contractWithEstimate),
},
};
const fiatFees = getFiatFees(
contractWithEstimate,
CryptoCurrency.NIM,
exchangeRate,
FiatCurrency.EUR,
null,
);

addSettlementData(fundingData.hashRoot, {
asset: SwapAsset.EUR,
amount: contractWithEstimate.to.amount,
// We cannot get bank info or EUR HTLC details from this.
}, {
fees: {
totalFee: fiatFees.funding.total,
asset: SwapAsset.EUR,
},
});
}
}
await detectSwapFiatCounterpart(transaction.recipient, fundingData.hashRoot, 'settlement', SwapAsset.NIM);
}

// HTLC Refunding
Expand Down Expand Up @@ -334,34 +305,6 @@ async function detectSwap(transaction: Transaction, knownTransactions: Transacti
transactionHash: transaction.transactionHash,
});

if (!swaps$.swaps[settlementData.hashRoot].in) {
// Check this swap with the Fastspot API to detect if this was a EUR swap
const contractWithEstimate = await getContract(SwapAsset.NIM, transaction.sender).catch(() => undefined);
if (contractWithEstimate?.from.asset === SwapAsset.EUR) {
const exchangeRate = {
[CryptoCurrency.NIM]: {
[FiatCurrency.EUR]: getEurPerCrypto(SwapAsset.NIM, contractWithEstimate),
},
};
const fiatFees = getFiatFees(
contractWithEstimate,
CryptoCurrency.NIM,
exchangeRate,
FiatCurrency.EUR,
null,
);

addFundingData(settlementData.hashRoot, {
asset: SwapAsset.EUR,
amount: contractWithEstimate.from.amount,
// We cannot get bank info or EUR HTLC details from this.
}, {
fees: {
totalFee: fiatFees.settlement.total,
asset: SwapAsset.EUR,
},
});
}
}
await detectSwapFiatCounterpart(transaction.sender, settlementData.hashRoot, 'funding', SwapAsset.NIM);
}
}
67 changes: 4 additions & 63 deletions src/stores/UsdcTransactions.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import Vue from 'vue';
import { getHistoricExchangeRates, isHistorySupportedFiatCurrency } from '@nimiq/utils';
import { getContract, SwapAsset } from '@nimiq/fastspot-api';
import { SwapAsset } from '@nimiq/fastspot-api';
import { createStore } from 'pinia';
import config from 'config';
import { useFiatStore } from './Fiat';
import { CryptoCurrency, FiatCurrency, FIAT_API_PROVIDER_TX_HISTORY, FIAT_PRICE_UNAVAILABLE } from '../lib/Constants';
import { useSwapsStore } from './Swaps';
// import { getPolygonClient } from '../ethers';
import { getEurPerCrypto, getFiatFees } from '../lib/swap/utils/Functions';

type HtlcOpenEvent = {
name: 'Open',
Expand Down Expand Up @@ -230,7 +229,7 @@ export const useUsdcTransactionsStore = createStore({
// Note: this method should not modify the transaction itself or the transaction store, to avoid race conditions with
// other methods that do so.
async function detectSwap(transaction: Transaction, knownTransactions: Transaction[]) {
const { state: swaps$, addFundingData, addSettlementData } = useSwapsStore();
const { state: swaps$, addFundingData, addSettlementData, detectSwapFiatCounterpart } = useSwapsStore();
if (swaps$.swapByTransaction[transaction.transactionHash]) return; // already known
const asset = transaction.token === config.usdc.nativeUsdcContract ? SwapAsset.USDC_MATIC : SwapAsset.USDC;

Expand All @@ -248,36 +247,7 @@ async function detectSwap(transaction: Transaction, knownTransactions: Transacti
},
});

if (!swaps$.swaps[hashRoot].out) {
// Check this swap with the Fastspot API to detect if this was a EUR swap
const contractWithEstimate = await getContract(asset, transaction.event.id.substring(2))
.catch(() => undefined);
if (contractWithEstimate?.to.asset === SwapAsset.EUR) {
const exchangeRate = {
[CryptoCurrency.USDC]: {
[FiatCurrency.EUR]: getEurPerCrypto(asset, contractWithEstimate),
},
};
const fiatFees = getFiatFees(
contractWithEstimate,
CryptoCurrency.USDC,
exchangeRate,
FiatCurrency.EUR,
null,
);

addSettlementData(hashRoot, {
asset: SwapAsset.EUR,
amount: contractWithEstimate.to.amount,
// We cannot get bank info or EUR HTLC details from this.
}, {
fees: {
totalFee: fiatFees.funding.total,
asset: SwapAsset.EUR,
},
});
}
}
await detectSwapFiatCounterpart(transaction.event.id.substring(2), hashRoot, 'settlement', asset);
}

// HTLC Refunding
Expand Down Expand Up @@ -316,35 +286,6 @@ async function detectSwap(transaction: Transaction, knownTransactions: Transacti
transactionHash: transaction.transactionHash,
});

if (!swaps$.swaps[hashRoot].in) {
// Check this swap with the Fastspot API to detect if this was a EUR swap
const contractWithEstimate = await getContract(asset, transaction.event.id.substring(2))
.catch(() => undefined);
if (contractWithEstimate?.from.asset === SwapAsset.EUR) {
const exchangeRate = {
[CryptoCurrency.USDC]: {
[FiatCurrency.EUR]: getEurPerCrypto(asset, contractWithEstimate),
},
};
const fiatFees = getFiatFees(
contractWithEstimate,
CryptoCurrency.USDC,
exchangeRate,
FiatCurrency.EUR,
null,
);

addFundingData(hashRoot, {
asset: SwapAsset.EUR,
amount: contractWithEstimate.from.amount,
// We cannot get bank info or EUR HTLC details from this.
}, {
fees: {
totalFee: fiatFees.settlement.total,
asset: SwapAsset.EUR,
},
});
}
}
await detectSwapFiatCounterpart(transaction.event.id.substring(2), hashRoot, 'funding', asset);
}
}

0 comments on commit 57991e6

Please sign in to comment.