Skip to content

Commit

Permalink
Update @nimiq/fastspot-api && @nimiq/libswap packages to support USDT
Browse files Browse the repository at this point in the history
  • Loading branch information
sisou committed Oct 31, 2024
1 parent 9d66a53 commit 92aaf4b
Show file tree
Hide file tree
Showing 11 changed files with 88 additions and 71 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@
"@formatjs/intl-displaynames": "^3.3.4",
"@linusborg/vue-simple-portal": "^0.1.4",
"@nimiq/electrum-client": "https://github.com/nimiq/electrum-client#build",
"@nimiq/fastspot-api": "^1.8.0",
"@nimiq/fastspot-api": "^1.10.0",
"@nimiq/hub-api": "^1.8.0",
"@nimiq/iqons": "^1.5.2",
"@nimiq/libswap": "^1.3.0",
"@nimiq/libswap": "^1.4.0",
"@nimiq/oasis-api": "^1.1.1",
"@nimiq/oasis-bank-list": "https://github.com/nimiq/oasis-bank-list#main",
"@nimiq/rpc": "^0.4.1",
Expand Down
7 changes: 5 additions & 2 deletions src/components/modals/BuyCryptoModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,7 @@ import {
cancelSwap,
getSwap,
Swap,
Contract,
} from '@nimiq/fastspot-api';
import {
getHtlc,
Expand Down Expand Up @@ -741,7 +742,8 @@ export default defineComponent({
let oasisHtlc: OasisHtlc;
try {
// TODO: Retry getting the HTLC if first time fails
oasisHtlc = await getHtlc(confirmedSwap.contracts[SwapAsset.EUR]!.htlc.address);
const contract = confirmedSwap.contracts[SwapAsset.EUR] as Contract<SwapAsset.EUR>;
oasisHtlc = await getHtlc(contract.htlc.address);
if (oasisHtlc.status !== HtlcStatus.PENDING) {
throw new Error(`UNEXPECTED: OASIS HTLC is not 'pending' but '${oasisHtlc.status}'`);
}
Expand Down Expand Up @@ -883,7 +885,8 @@ export default defineComponent({
function onPaid() {
if (!swap.value!.fundingInstructions || swap.value!.fundingInstructions.type !== 'sepa') {
// We are in a test environment
sandboxMockClearHtlc(swap.value!.contracts.EUR!.htlc.address);
const contract = swap.value!.contracts[SwapAsset.EUR] as Contract<SwapAsset.EUR>;
sandboxMockClearHtlc(contract.htlc.address);
}
if (!swap.value!.stateEnteredAt) {
Expand Down
4 changes: 3 additions & 1 deletion src/components/modals/SellCryptoModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ import {
createSwap,
cancelSwap,
getSwap,
Contract,
} from '@nimiq/fastspot-api';
import {
getHtlc,
Expand Down Expand Up @@ -789,7 +790,8 @@ export default defineComponent({
});
// Fetch OASIS HTLC to get clearing instructions
const oasisHtlc = await getHtlc(confirmedSwap.contracts[SwapAsset.EUR]!.htlc.address);
const contract = confirmedSwap.contracts[SwapAsset.EUR] as Contract<SwapAsset.EUR>;
const oasisHtlc = await getHtlc(contract.htlc.address);
if (oasisHtlc.status !== HtlcStatus.PENDING && oasisHtlc.status !== HtlcStatus.CLEARED) {
const error = new Error(`UNEXPECTED: OASIS HTLC is not 'pending'/'cleared' but '${oasisHtlc.status}'`);
if (config.reportToSentry) captureException(error);
Expand Down
4 changes: 2 additions & 2 deletions src/components/modals/UsdcTransactionModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ import GroundedArrowDownIcon from '../icons/GroundedArrowDownIcon.vue';
import Avatar from '../Avatar.vue';
import InteractiveShortAddress from '../InteractiveShortAddress.vue';
import TransactionDetailOasisPayoutStatus from '../TransactionDetailOasisPayoutStatus.vue';
import { SwapUsdcData } from '../../stores/Swaps';
import { SwapErc20Data } from '../../stores/Swaps';
import { useTransactionsStore, Transaction as NimTransaction } from '../../stores/Transactions';
import { useBtcTransactionsStore, Transaction as BtcTransaction } from '../../stores/BtcTransactions';
import { isProxyData, ProxyType } from '../../lib/ProxyDetection';
Expand Down Expand Up @@ -468,7 +468,7 @@ export default defineComponent({
);
async function refundHtlc() {
const htlcDetails = (swapInfo.value?.in as SwapUsdcData | undefined)?.htlc;
const htlcDetails = (swapInfo.value?.in as SwapErc20Data | undefined)?.htlc;
if (!htlcDetails) {
alert('Unexpected: unknown HTLC refund details'); // eslint-disable-line no-alert
return;
Expand Down
8 changes: 4 additions & 4 deletions src/components/swap/SwapBalanceBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ import BitcoinIcon from '../icons/BitcoinIcon.vue';
import CurvedLine from '../icons/SwapBalanceBar/CurvedLine.vue';
import SlideHint from '../icons/SwapBalanceBar/SlideHint.vue';
import { getColorClass } from '../../lib/AddressColor';
import { assetToCurrency } from '../../lib/swap/utils/Assets';
import { assetToCurrency, SupportedSwapAsset } from '../../lib/swap/utils/Assets';
import { usePolygonAddressStore } from '../../stores/PolygonAddress';
import UsdcIcon from '../icons/UsdcIcon.vue';
import UsdtIcon from '../icons/UsdtIcon.vue';
Expand All @@ -158,11 +158,11 @@ export default defineComponent({
name: 'swap-balance-bar',
props: {
leftAsset: {
type: String as () => SwapAsset,
type: String as () => SupportedSwapAsset,
required: true,
},
rightAsset: {
type: String as () => SwapAsset,
type: String as () => SupportedSwapAsset,
required: true,
},
newLeftBalance: {
Expand Down Expand Up @@ -373,7 +373,7 @@ export default defineComponent({
[SwapAsset.BTC]: 8,
[SwapAsset.USDC]: 6, // For TS completeness
[SwapAsset.USDC_MATIC]: 6,
// [SwapAsset.USDT]: 6,
[SwapAsset.USDT]: 6,
[SwapAsset.EUR]: 2, // For TS completeness
} as const;
Expand Down
68 changes: 38 additions & 30 deletions src/components/swap/SwapModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ import {
RequestAsset,
getSwap,
Swap,
Contract,
} from '@nimiq/fastspot-api';
import { captureException } from '@sentry/vue';
import type { BigNumber } from 'ethers';
Expand Down Expand Up @@ -320,7 +321,7 @@ import { useKycStore } from '../../stores/Kyc';
import { usePolygonAddressStore } from '../../stores/PolygonAddress';
import { useAccountSettingsStore } from '../../stores/AccountSettings';
import { calculateDisplayedDecimals } from '../../lib/NumberFormatting';
import { assetToCurrency } from '../../lib/swap/utils/Assets';
import { assetToCurrency, SupportedSwapAsset } from '../../lib/swap/utils/Assets';
import AddressList from '../AddressList.vue';
import SwapAnimation from './SwapAnimation.vue';
import SendModalFooter from '../SendModalFooter.vue';
Expand Down Expand Up @@ -386,20 +387,20 @@ export default defineComponent({
const leftAsset = ref(
activeAccountInfo.value?.type === AccountType.LEDGER
? SwapAsset.NIM
: props.pair.split('-')[0] as SwapAsset,
: props.pair.split('-')[0] as SupportedSwapAsset,
);
const rightAsset = ref(
activeAccountInfo.value?.type === AccountType.LEDGER
? SwapAsset.BTC
: props.pair.split('-')[1] as SwapAsset,
: props.pair.split('-')[1] as SupportedSwapAsset,
);
const swapHasBtc = computed(() => leftAsset.value === SwapAsset.BTC || rightAsset.value === SwapAsset.BTC);
const swapHasUsdc = computed(
() => leftAsset.value === SwapAsset.USDC_MATIC || rightAsset.value === SwapAsset.USDC_MATIC,
);
const fixedAsset = ref<SwapAsset>(leftAsset.value);
const fixedAsset = ref<SupportedSwapAsset>(leftAsset.value);
const disabledAssetError = computed(() => {
if (!config.fastspot.enabled) return i18n.t('Crypto swaps are currently under maintenance.') as string;
Expand All @@ -421,6 +422,7 @@ export default defineComponent({
const {
activeAddress: activeUsdcAddress,
accountUsdcBalance,
accountUsdtBridgedBalance,
} = usePolygonAddressStore();
const { stablecoin } = useAccountSettingsStore();
const { exchangeRates, currency, state: fiat$ } = useFiatStore();
Expand Down Expand Up @@ -473,17 +475,18 @@ export default defineComponent({
[SwapAsset.BTC]: 8,
[SwapAsset.USDC]: 6, // For TS completeness
[SwapAsset.USDC_MATIC]: 6,
[SwapAsset.USDT]: 6,
[SwapAsset.EUR]: 2, // For TS completeness
} as const;
function effectiveDecimals(asset: SwapAsset) {
function effectiveDecimals(asset: SupportedSwapAsset) {
return {
...DECIMALS,
[SwapAsset.BTC]: btcUnit.value.decimals,
}[asset];
}
function capDecimals(amount: number, asset: SwapAsset) {
function capDecimals(amount: number, asset: SupportedSwapAsset) {
if (!amount) return 0;
const numberSign = amount / Math.abs(amount); // 1 or -1
Expand Down Expand Up @@ -1088,12 +1091,13 @@ export default defineComponent({
const isLimitReached = ref(false);
function accountBalance(asset: SwapAsset): number { // eslint-disable-line consistent-return
function accountBalance(asset: SupportedSwapAsset): number { // eslint-disable-line consistent-return
switch (asset) { // eslint-disable-line default-case
case SwapAsset.NIM: return activeAddressInfo.value?.balance ?? 0;
case SwapAsset.BTC: return accountBtcBalance.value;
case SwapAsset.USDC: return 0; // not supported for swapping
case SwapAsset.USDC_MATIC: return accountUsdcBalance.value;
case SwapAsset.USDT: return accountUsdtBridgedBalance.value;
case SwapAsset.EUR: return 0;
}
}
Expand Down Expand Up @@ -1150,15 +1154,15 @@ export default defineComponent({
}
}
function otherAsset(asset: SwapAsset) {
function otherAsset(asset: SupportedSwapAsset) {
if (asset === leftAsset.value) return rightAsset.value;
if (asset === rightAsset.value) return leftAsset.value;
throw new Error(`Cannot get other asset to ${asset} as it's not currently selected`);
}
// If user only has one asset, then we know that there is only one available operation,
// so we show only one icon: '-' or '+' depending on the asset
function getPlaceholder(asset: SwapAsset) {
function getPlaceholder(asset: SupportedSwapAsset) {
if (!accountBalance(otherAsset(asset))) {
return '- 0';
}
Expand All @@ -1168,7 +1172,7 @@ export default defineComponent({
return '± 0';
}
function onFocus(asset: SwapAsset, input: HTMLInputElement) {
function onFocus(asset: SupportedSwapAsset, input: HTMLInputElement) {
// If user has 0 assets in the other asset than the one selected, the input should start with a - symbol
// If user has already changed the input, do nothing
Expand Down Expand Up @@ -1312,11 +1316,11 @@ export default defineComponent({
const data = swap.value || estimate.value;
const feeAmount = (data.from.amount - data.from.serviceNetworkFee) * data.serviceFeePercentage;
return (Math.max(0, feeAmount) / 10 ** DECIMALS[data.from.asset])
* (exchangeRates.value[assetToCurrency(data.from.asset)][currency.value] || 0);
return (Math.max(0, feeAmount) / 10 ** DECIMALS[data.from.asset as SupportedSwapAsset])
* (exchangeRates.value[assetToCurrency(data.from.asset as SupportedSwapAsset)][currency.value] || 0);
});
const feeFiat = (asset: SwapAsset) => {
const feeFiat = (asset: SupportedSwapAsset) => {
if (leftAsset.value === asset) return myLeftFeeFiat.value + serviceLeftFeeFiat.value;
if (rightAsset.value === asset) return myRightFeeFiat.value + serviceRightFeeFiat.value;
return undefined;
Expand All @@ -1338,8 +1342,8 @@ export default defineComponent({
const data = swap.value || estimate.value;
const fromAmount = data.from.amount - data.from.serviceNetworkFee;
const fromFiat = (fromAmount / 10 ** DECIMALS[data.from.asset])
* (exchangeRates.value[assetToCurrency(data.from.asset)][currency.value] || 0);
const fromFiat = (fromAmount / 10 ** DECIMALS[data.from.asset as SupportedSwapAsset])
* (exchangeRates.value[assetToCurrency(data.from.asset as SupportedSwapAsset)][currency.value] || 0);
return (totalFeeFiat.value / fromFiat) >= 0.3;
});
Expand Down Expand Up @@ -1433,8 +1437,8 @@ export default defineComponent({
}
swapSuggestion = await createSwap(
from as RequestAsset<SwapAsset>, // Need to force one of the function signatures
to as SwapAsset,
from as RequestAsset<SupportedSwapAsset>, // Need to force one of the function signatures
to as SupportedSwapAsset,
);
// Update local fees with latest feePerUnit values
Expand Down Expand Up @@ -1741,8 +1745,12 @@ export default defineComponent({
layout: 'slider',
direction: leftAsset.value === fund.type ? 'left-to-right' : 'right-to-left',
fiatCurrency: currency.value,
fundingFiatRate: exchangeRates.value[assetToCurrency(fund.type as SwapAsset)][currency.value]!,
redeemingFiatRate: exchangeRates.value[assetToCurrency(redeem.type as SwapAsset)][currency.value]!,
fundingFiatRate: exchangeRates.value[assetToCurrency(
fund.type as SupportedSwapAsset,
)][currency.value]!,
redeemingFiatRate: exchangeRates.value[assetToCurrency(
redeem.type as SupportedSwapAsset,
)][currency.value]!,
fundFees: {
processing: 0,
redeeming: swapSuggestion.from.serviceNetworkFee,
Expand Down Expand Up @@ -1793,10 +1801,10 @@ export default defineComponent({
}
const fundingSignedTx = signedTransactions[
assetToCurrency(fund.type as SwapAsset) as keyof SetupSwapResult
assetToCurrency(fund.type as SupportedSwapAsset) as keyof SetupSwapResult
] as SignedTransaction | SignedBtcTransaction | SignedPolygonTransaction;
const redeemingSignedTx = signedTransactions[
assetToCurrency(redeem.type as SwapAsset) as keyof SetupSwapResult
assetToCurrency(redeem.type as SupportedSwapAsset) as keyof SetupSwapResult
] as SignedTransaction | SignedBtcTransaction | SignedPolygonTransaction;
if (!fundingSignedTx || !redeemingSignedTx) {
Expand Down Expand Up @@ -1844,7 +1852,7 @@ export default defineComponent({
} catch (error) {
if (config.reportToSentry) captureException(error);
else console.error(error); // eslint-disable-line no-console
swapError.value = context.root.$t('Invalid swap state, swap aborted!');
swapError.value = context.root.$t('Invalid swap state, swap aborted!') as string;
cancelSwap({ id: swapId } as PreSwap);
currentlySigning.value = false;
updateEstimate();
Expand All @@ -1861,7 +1869,7 @@ export default defineComponent({
const nimHtlcAddress = confirmedSwap.from.asset === SwapAsset.NIM
? signedTransactions.nim!.raw.recipient
: signedTransactions.nim!.raw.sender;
confirmedSwap.contracts[SwapAsset.NIM]!.htlc.address = nimHtlcAddress;
(confirmedSwap.contracts[SwapAsset.NIM] as Contract<SwapAsset.NIM>).htlc.address = nimHtlcAddress;
}
setActiveSwap({
Expand Down Expand Up @@ -1950,7 +1958,7 @@ export default defineComponent({
onClose();
}
function onSwapBalanceBarChange(swapInfo: { asset: SwapAsset, amount: number }) {
function onSwapBalanceBarChange(swapInfo: { asset: SupportedSwapAsset, amount: number }) {
const { asset, amount } = swapInfo;
// Only cap decimals on the amount when not the whole address/account balance is used
Expand Down Expand Up @@ -1978,7 +1986,7 @@ export default defineComponent({
const { hasBitcoinAddresses, hasPolygonAddresses } = useAccountStore();
// Only allow swapping between assets that have a balance in one of the sides of the swap.
function getButtonGroupOptions(otherSide: SwapAsset) {
function getButtonGroupOptions(otherSide: SupportedSwapAsset) {
const otherAssetBalance = accountBalance(otherSide);
return getWalletEnabledAssets().reduce((result, asset) => {
if (
Expand All @@ -1989,7 +1997,7 @@ export default defineComponent({
return {
...result,
[asset]: {
label: assetToCurrency(asset).toUpperCase(),
label: assetToCurrency(asset as SupportedSwapAsset).toUpperCase(),
// Note that currencies which are disabled in Fastspot, are not disabled in the button group,
// but instead show a maintenance message in the footer.
disabled: (
Expand All @@ -1998,7 +2006,7 @@ export default defineComponent({
|| (asset === SwapAsset.USDC_MATIC && !hasPolygonAddresses.value)
) || (
// Asset pair has no balance to swap.
!otherAssetBalance && !accountBalance(asset)
!otherAssetBalance && !accountBalance(asset as SupportedSwapAsset)
),
},
};
Expand All @@ -2008,15 +2016,15 @@ export default defineComponent({
const leftButtonGroupOptions = computed(() => getButtonGroupOptions(rightAsset.value));
const rightButtonGroupOptions = computed(() => getButtonGroupOptions(leftAsset.value));
function setLeftAsset(asset: SwapAsset) {
function setLeftAsset(asset: SupportedSwapAsset) {
if (rightAsset.value === asset) {
rightAsset.value = leftAsset.value;
}
leftAsset.value = asset;
context.root.$router.replace(`/swap/${leftAsset.value}-${rightAsset.value}`);
}
function setRightAsset(asset: SwapAsset) {
function setRightAsset(asset: SupportedSwapAsset) {
if (leftAsset.value === asset) {
leftAsset.value = rightAsset.value;
}
Expand Down Expand Up @@ -2051,7 +2059,7 @@ export default defineComponent({
console.warn('No swap found'); // eslint-disable-line no-console
return;
}
const usdcHtlc = swap.value.contracts[SwapAsset.USDC_MATIC];
const usdcHtlc = swap.value.contracts[SwapAsset.USDC_MATIC] as Contract<SwapAsset.USDC_MATIC> | undefined;
if (!usdcHtlc) {
console.warn('No USDC HTLC found in swap', swap.value); // eslint-disable-line no-console
return;
Expand Down
8 changes: 5 additions & 3 deletions src/components/swap/SwapNotification.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ import {
} from '@nimiq/oasis-api';
import { SwapHandler, Swap as GenericSwap, SwapAsset, Client, Transaction } from '@nimiq/libswap';
import type { ForwardRequest } from '@opengsn/common/dist/EIP712/ForwardRequest';
import { Event as PolygonEvent, EventType as PolygonEventType } from '@nimiq/libswap/dist/src/UsdcAssetAdapter';
import { Event as PolygonEvent, EventType as PolygonEventType } from '@nimiq/libswap/dist/src/Erc20AssetAdapter';
import { captureException } from '@sentry/vue';
import MaximizeIcon from '../icons/MaximizeIcon.vue';
import { useSwapsStore, SwapState, ActiveSwap, SwapEurData, SwapErrorAction } from '../../stores/Swaps';
Expand Down Expand Up @@ -796,14 +796,16 @@ export default defineComponent({
SwapAsset.NIM,
SwapAsset.BTC,
SwapAsset.USDC_MATIC,
SwapAsset.USDT,
];
const fiatCurrencies = [
SwapAsset.EUR,
];
const fromAsset = activeSwap.value.from.asset;
const toAsset = activeSwap.value.to.asset;
// Convert from Fastspot SwapAsset to LibSwap SwapAsset
const fromAsset = activeSwap.value.from.asset as SwapAsset;
const toAsset = activeSwap.value.to.asset as SwapAsset;
if (cryptoCurrencies.includes(fromAsset) && cryptoCurrencies.includes(toAsset)) {
context.root.$router.push('/swap');
Expand Down
Loading

0 comments on commit 92aaf4b

Please sign in to comment.