diff --git a/.gitignore b/.gitignore
index 0b7d2b9038..07ddb82ff7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -80,3 +80,5 @@ coverage/
# .vscode
**/extensions.json
+
+apps/test/
\ No newline at end of file
diff --git a/apps/wallet-mobile/package.json b/apps/wallet-mobile/package.json
index 531456418a..684d656617 100644
--- a/apps/wallet-mobile/package.json
+++ b/apps/wallet-mobile/package.json
@@ -222,6 +222,7 @@
"@wdio/mocha-framework": "^7.16.6",
"@wdio/selenium-standalone-service": "^7.25.1",
"@wdio/spec-reporter": "^7.16.4",
+ "@yoroi/types": "1.0.4",
"babel-eslint": "^10.1.0",
"babel-jest": "^29.2.1",
"babel-loader": "8.2.2",
diff --git a/apps/wallet-mobile/src/Catalyst/hooks.ts b/apps/wallet-mobile/src/Catalyst/hooks.ts
index 9e205f7eb1..326e3c2278 100644
--- a/apps/wallet-mobile/src/Catalyst/hooks.ts
+++ b/apps/wallet-mobile/src/Catalyst/hooks.ts
@@ -1,15 +1,18 @@
+import {Balance} from '@yoroi/types'
import {useEffect, useState} from 'react'
import {YoroiWallet} from '../yoroi-wallets/cardano/types'
import {CATALYST, isHaskellShelley} from '../yoroi-wallets/cardano/utils'
import {useBalances} from '../yoroi-wallets/hooks'
-import {Quantity} from '../yoroi-wallets/types/yoroi'
import {Amounts, Quantities} from '../yoroi-wallets/utils'
export const useCanVote = (wallet: YoroiWallet) => {
const balances = useBalances(wallet)
const primaryAmount = Amounts.getAmount(balances, '')
- const sufficientFunds = Quantities.isGreaterThan(primaryAmount.quantity, CATALYST.MIN_ADA.toString() as Quantity)
+ const sufficientFunds = Quantities.isGreaterThan(
+ primaryAmount.quantity,
+ CATALYST.MIN_ADA.toString() as Balance.Quantity,
+ )
return {
canVote: !wallet.isReadOnly && isHaskellShelley(wallet.walletImplementationId),
diff --git a/apps/wallet-mobile/src/Dashboard/StakePoolInfos.tsx b/apps/wallet-mobile/src/Dashboard/StakePoolInfos.tsx
index 5e33d48ba8..820d8a9214 100644
--- a/apps/wallet-mobile/src/Dashboard/StakePoolInfos.tsx
+++ b/apps/wallet-mobile/src/Dashboard/StakePoolInfos.tsx
@@ -1,3 +1,4 @@
+import {Balance} from '@yoroi/types'
import BigNumber from 'bignumber.js'
import React from 'react'
import {ActivityIndicator, StyleSheet, View} from 'react-native'
@@ -5,7 +6,7 @@ import {useQuery, useQueryClient, UseQueryOptions} from 'react-query'
import {useSelectedWallet} from '../SelectedWallet'
import {YoroiWallet} from '../yoroi-wallets/cardano/types'
-import {Quantity, StakingInfo, YoroiUnsignedTx} from '../yoroi-wallets/types'
+import {StakingInfo, YoroiUnsignedTx} from '../yoroi-wallets/types'
import {Quantities} from '../yoroi-wallets/utils'
import {StakePoolInfo} from './StakePoolInfo'
@@ -93,8 +94,8 @@ export const useStakingTx = (
const stakingUtxos = await wallet.getAllUtxosForKey()
const amountToDelegate = Quantities.sum([
- ...stakingUtxos.map((utxo) => utxo.amount as Quantity),
- accountState.remainingAmount as Quantity,
+ ...stakingUtxos.map((utxo) => utxo.amount as Balance.Quantity),
+ accountState.remainingAmount as Balance.Quantity,
])
return wallet.createDelegationTx(poolId, new BigNumber(amountToDelegate))
diff --git a/apps/wallet-mobile/src/NftDetails/NftDetails.tsx b/apps/wallet-mobile/src/NftDetails/NftDetails.tsx
index a208f41918..805b309da2 100644
--- a/apps/wallet-mobile/src/NftDetails/NftDetails.tsx
+++ b/apps/wallet-mobile/src/NftDetails/NftDetails.tsx
@@ -1,4 +1,5 @@
import {RouteProp, useRoute} from '@react-navigation/native'
+import {Balance} from '@yoroi/types'
import React, {ReactNode, useState} from 'react'
import {defineMessages, useIntl} from 'react-intl'
import {Dimensions, Linking, StyleSheet, TouchableOpacity, View} from 'react-native'
@@ -16,7 +17,6 @@ import {useSelectedWallet} from '../SelectedWallet'
import {COLORS} from '../theme'
import {getNetworkConfigById} from '../yoroi-wallets/cardano/networks'
import {useNft} from '../yoroi-wallets/hooks'
-import {TokenInfo} from '../yoroi-wallets/types'
import {isRecord, isString} from '../yoroi-wallets/utils'
export const NftDetails = () => {
@@ -68,7 +68,7 @@ export const NftDetails = () => {
)
}
-const UnModeratedNftImage = ({nft}: {nft: TokenInfo}) => {
+const UnModeratedNftImage = ({nft}: {nft: Balance.TokenInfo}) => {
const navigateTo = useNavigateTo()
return (
navigateTo.nftZoom(nft.id)} style={styles.imageWrapper}>
@@ -77,7 +77,7 @@ const UnModeratedNftImage = ({nft}: {nft: TokenInfo}) => {
)
}
-const ModeratedNftImage = ({nft}: {nft: TokenInfo}) => {
+const ModeratedNftImage = ({nft}: {nft: Balance.TokenInfo}) => {
const wallet = useSelectedWallet()
const navigateTo = useNavigateTo()
const {status} = useModeratedNftImage({wallet, fingerprint: nft.fingerprint})
@@ -114,7 +114,7 @@ const MetadataRow = ({title, copyText, children}: {title: string; children: Reac
)
}
-const NftOverview = ({nft}: {nft: TokenInfo}) => {
+const NftOverview = ({nft}: {nft: Balance.TokenInfo}) => {
const strings = useStrings()
const wallet = useSelectedWallet()
const config = getNetworkConfigById(wallet.networkId)
@@ -195,7 +195,7 @@ const HR = () => (
/>
)
-const NftMetadata = ({nft}: {nft: TokenInfo}) => {
+const NftMetadata = ({nft}: {nft: Balance.TokenInfo}) => {
const strings = useStrings()
const stringifiedMetadata = JSON.stringify(nft.metadatas.mintNft, undefined, 2)
diff --git a/apps/wallet-mobile/src/Nfts/Nfts.tsx b/apps/wallet-mobile/src/Nfts/Nfts.tsx
index 8e779972d2..d9e4e908b5 100644
--- a/apps/wallet-mobile/src/Nfts/Nfts.tsx
+++ b/apps/wallet-mobile/src/Nfts/Nfts.tsx
@@ -1,3 +1,4 @@
+import {Balance} from '@yoroi/types'
import React, {ReactNode} from 'react'
import {defineMessages, useIntl} from 'react-intl'
import {RefreshControl, ScrollView, StyleSheet, Text, View} from 'react-native'
@@ -8,7 +9,6 @@ import {useMetrics} from '../metrics/metricsManager'
import {useSearch, useSearchOnNavBar} from '../Search/SearchContext'
import {useSelectedWallet} from '../SelectedWallet'
import {useNfts} from '../yoroi-wallets/hooks'
-import {TokenInfo} from '../yoroi-wallets/types'
import {filterNfts, useTrackNftGallerySearchActivated} from './filterNfts'
import {useNavigateTo} from './navigation'
import {NoNftsScreen} from './NoNftsScreen'
@@ -195,7 +195,7 @@ const LoadingScreen = ({nftsCount}: {nftsCount: number}) => {
)
}
-const byName = ({name: A}: TokenInfo, {name: B}: TokenInfo) => A.localeCompare(B)
+const byName = ({name: A}: Balance.TokenInfo, {name: B}: Balance.TokenInfo) => A.localeCompare(B)
const styles = StyleSheet.create({
safeAreaView: {
diff --git a/apps/wallet-mobile/src/Nfts/filterNfts.test.ts b/apps/wallet-mobile/src/Nfts/filterNfts.test.ts
index 81b67b80e2..9e66c1733c 100644
--- a/apps/wallet-mobile/src/Nfts/filterNfts.test.ts
+++ b/apps/wallet-mobile/src/Nfts/filterNfts.test.ts
@@ -1,5 +1,6 @@
+import {Balance} from '@yoroi/types'
+
import {nft} from '../yoroi-wallets/mocks'
-import {TokenInfo} from '../yoroi-wallets/types'
import {filterNfts} from './filterNfts'
describe('filterNfts', () => {
@@ -7,7 +8,7 @@ describe('filterNfts', () => {
const boredMonkey = {...nft, id: '1', fingerprint: 'fakefingerprint2', name: 'Bored Monkey #4567'}
const appleBlocks = {...nft, id: '2', fingerprint: 'fakefingerprint3', name: 'Apple Blocks #7890'}
- const nfts: TokenInfo[] = [cryptoWolf, boredMonkey, appleBlocks]
+ const nfts: Balance.TokenInfo[] = [cryptoWolf, boredMonkey, appleBlocks]
it('filters NFTs correctly with case-insensitive search term', () => {
const filteredNfts = filterNfts('APple bLOcks', nfts)
diff --git a/apps/wallet-mobile/src/Nfts/filterNfts.ts b/apps/wallet-mobile/src/Nfts/filterNfts.ts
index fb8a0d3010..20d36f9878 100644
--- a/apps/wallet-mobile/src/Nfts/filterNfts.ts
+++ b/apps/wallet-mobile/src/Nfts/filterNfts.ts
@@ -1,9 +1,9 @@
+import {Balance} from '@yoroi/types'
import React from 'react'
import {useMetrics} from '../metrics/metricsManager'
-import {TokenInfo} from '../yoroi-wallets/types'
-export const filterNfts = (searchTerm: string, nfts: TokenInfo[]): TokenInfo[] => {
+export const filterNfts = (searchTerm: string, nfts: Balance.TokenInfo[]): Balance.TokenInfo[] => {
const searchTermLowerCase = searchTerm.toLowerCase()
const filteredNfts =
searchTermLowerCase.length > 0 ? nfts.filter((nft) => nft.name?.toLowerCase().includes(searchTermLowerCase)) : nfts
diff --git a/apps/wallet-mobile/src/Staking/DelegationConfirmation/DelegationConfirmation.tsx b/apps/wallet-mobile/src/Staking/DelegationConfirmation/DelegationConfirmation.tsx
index 3e9bcf64a9..e01e60c25c 100644
--- a/apps/wallet-mobile/src/Staking/DelegationConfirmation/DelegationConfirmation.tsx
+++ b/apps/wallet-mobile/src/Staking/DelegationConfirmation/DelegationConfirmation.tsx
@@ -1,4 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
+import {Balance} from '@yoroi/types'
import React, {useEffect, useState} from 'react'
import {defineMessages, useIntl} from 'react-intl'
import {ScrollView, StyleSheet, View, ViewProps} from 'react-native'
@@ -15,7 +16,6 @@ import {useSelectedWallet} from '../../SelectedWallet'
import {COLORS} from '../../theme'
import {NETWORKS} from '../../yoroi-wallets/cardano/networks'
import {NUMBERS} from '../../yoroi-wallets/cardano/numbers'
-import {Quantity} from '../../yoroi-wallets/types'
import {Amounts, Entries, Quantities} from '../../yoroi-wallets/utils'
type Params = StakingCenterRoutes['delegation-confirmation']
@@ -167,10 +167,10 @@ const messages = defineMessages({
* TODO: based on https://staking.cardano.org/en/calculator/
* needs to be update per-network
*/
-const approximateReward = (stakedQuantity: Quantity): Quantity => {
+const approximateReward = (stakedQuantity: Balance.Quantity): Balance.Quantity => {
return Quantities.quotient(
Quantities.product([stakedQuantity, `${NETWORKS.HASKELL_SHELLEY.PER_EPOCH_PERCENTAGE_REWARD}`]),
- NUMBERS.EPOCH_REWARD_DENOMINATOR.toString() as Quantity,
+ NUMBERS.EPOCH_REWARD_DENOMINATOR.toString() as Balance.Quantity,
)
}
diff --git a/apps/wallet-mobile/src/TxHistory/AssetList/AssetList.tsx b/apps/wallet-mobile/src/TxHistory/AssetList/AssetList.tsx
index aa365c4cbe..9691043551 100644
--- a/apps/wallet-mobile/src/TxHistory/AssetList/AssetList.tsx
+++ b/apps/wallet-mobile/src/TxHistory/AssetList/AssetList.tsx
@@ -1,4 +1,5 @@
import {FlashList, FlashListProps} from '@shopify/flash-list'
+import {Balance} from '@yoroi/types'
import React from 'react'
import {defineMessages, useIntl} from 'react-intl'
import {Alert, Linking, StyleSheet, TouchableOpacity, View} from 'react-native'
@@ -11,11 +12,10 @@ import {usePrivacyMode} from '../../Settings/PrivacyMode/PrivacyMode'
import {sortTokenInfos} from '../../utils'
import {getNetworkConfigById} from '../../yoroi-wallets/cardano/networks'
import {useBalances, useTokenInfos} from '../../yoroi-wallets/hooks'
-import {TokenInfo} from '../../yoroi-wallets/types'
import {Amounts} from '../../yoroi-wallets/utils'
import {ActionsBanner} from './ActionsBanner'
-type ListProps = FlashListProps
+type ListProps = FlashListProps
type Props = Partial & {
onScroll: ListProps['onScroll']
refreshing: boolean
diff --git a/apps/wallet-mobile/src/TxHistory/PairedBalance.tsx b/apps/wallet-mobile/src/TxHistory/PairedBalance.tsx
index da59ea54b4..88d64e085b 100644
--- a/apps/wallet-mobile/src/TxHistory/PairedBalance.tsx
+++ b/apps/wallet-mobile/src/TxHistory/PairedBalance.tsx
@@ -1,3 +1,4 @@
+import {Balance} from '@yoroi/types'
import * as React from 'react'
import {defineMessages, useIntl} from 'react-intl'
import {StyleSheet, Text} from 'react-native'
@@ -8,12 +9,12 @@ import {useSelectedWallet} from '../SelectedWallet'
import {useCurrencyContext} from '../Settings/Currency'
import {COLORS} from '../theme'
import {useExchangeRate} from '../yoroi-wallets/hooks'
-import {CurrencySymbol, YoroiAmount} from '../yoroi-wallets/types'
+import {CurrencySymbol} from '../yoroi-wallets/types'
import {Quantities} from '../yoroi-wallets/utils'
type Props = {
privacyMode?: PrivacyMode
- amount: YoroiAmount
+ amount: Balance.Amount
}
export const PairedBalance = React.forwardRef(({privacyMode, amount}, ref) => {
const {currency} = useCurrencyContext()
@@ -30,13 +31,13 @@ export const PairedBalance = React.forwardRef(({privacyMod
),
}}
>
-
+
)
})
const hiddenPairedTotal = '*.**'
-const Balance = ({privacyMode, amount}: Props) => {
+const PairedAmount = ({privacyMode, amount}: Props) => {
const wallet = useSelectedWallet()
const {currency, config} = useCurrencyContext()
const rate = useExchangeRate({wallet, to: currency})
diff --git a/apps/wallet-mobile/src/components/AmountItem/AmountItem.tsx b/apps/wallet-mobile/src/components/AmountItem/AmountItem.tsx
index e03ba165e6..91da218ea8 100644
--- a/apps/wallet-mobile/src/components/AmountItem/AmountItem.tsx
+++ b/apps/wallet-mobile/src/components/AmountItem/AmountItem.tsx
@@ -1,3 +1,4 @@
+import {Balance} from '@yoroi/types'
import * as React from 'react'
import {StyleSheet, View, ViewProps} from 'react-native'
@@ -7,13 +8,12 @@ import {PairedBalance} from '../../TxHistory/PairedBalance'
import {isEmptyString} from '../../utils'
import {YoroiWallet} from '../../yoroi-wallets/cardano/types'
import {useTokenInfo} from '../../yoroi-wallets/hooks'
-import {YoroiAmount} from '../../yoroi-wallets/types'
import {Quantities} from '../../yoroi-wallets/utils'
import {Boundary, Placeholder, Text, TokenIcon} from '..'
export type AmountItemProps = {
wallet: YoroiWallet
- amount: YoroiAmount
+ amount: Balance.Amount
style?: ViewProps['style']
privacyMode?: PrivacyMode
}
diff --git a/apps/wallet-mobile/src/components/NftImageGallery/NftImageGallery.tsx b/apps/wallet-mobile/src/components/NftImageGallery/NftImageGallery.tsx
index c6b8de743e..5dd9691c6d 100644
--- a/apps/wallet-mobile/src/components/NftImageGallery/NftImageGallery.tsx
+++ b/apps/wallet-mobile/src/components/NftImageGallery/NftImageGallery.tsx
@@ -1,4 +1,5 @@
import {FlashList, FlashListProps} from '@shopify/flash-list'
+import {Balance} from '@yoroi/types'
import React from 'react'
import {Dimensions, StyleSheet, TouchableOpacity, TouchableOpacityProps, View} from 'react-native'
import SkeletonPlaceholder from 'react-native-skeleton-placeholder'
@@ -6,7 +7,6 @@ import SkeletonPlaceholder from 'react-native-skeleton-placeholder'
import {features} from '../../features'
import {useModeratedNftImage} from '../../Nfts/hooks'
import {useSelectedWallet} from '../../SelectedWallet/Context'
-import {TokenInfo} from '../../yoroi-wallets/types'
import {Icon} from '../Icon'
import {NftPreview} from '../NftPreview'
import {Spacer} from '../Spacer'
@@ -21,12 +21,12 @@ export const SkeletonGallery = ({amount}: {amount: number}) => {
}
type Props = {
- nfts: TokenInfo[]
+ nfts: Balance.TokenInfo[]
onSelect: (id: string) => void
onRefresh: () => void
isRefreshing: boolean
- bounces?: FlashListProps['bounces']
- ListEmptyComponent?: FlashListProps['ListEmptyComponent']
+ bounces?: FlashListProps['bounces']
+ ListEmptyComponent?: FlashListProps['ListEmptyComponent']
withVerticalPadding?: boolean
readOnly?: boolean
}
@@ -60,7 +60,7 @@ export const NftImageGallery = ({
}
type ModeratedImageProps = TouchableOpacityProps & {
- nft: TokenInfo
+ nft: Balance.TokenInfo
}
const UnModeratedImage = ({nft, ...props}: ModeratedImageProps) => {
return (
@@ -112,11 +112,11 @@ const ModeratedImage = ({nft, ...props}: ModeratedImageProps) => {
)
}
-function BlockedNft({nft}: {nft: TokenInfo}) {
+function BlockedNft({nft}: {nft: Balance.TokenInfo}) {
return
}
-function PlaceholderNft({nft}: {nft: TokenInfo}) {
+function PlaceholderNft({nft}: {nft: Balance.TokenInfo}) {
return (
@@ -137,11 +137,11 @@ function PlaceholderNft({nft}: {nft: TokenInfo}) {
)
}
-function ManualReviewNft({nft}: {nft: TokenInfo}) {
+function ManualReviewNft({nft}: {nft: Balance.TokenInfo}) {
return
}
-function RequiresConsentNft({nft}: {nft: TokenInfo}) {
+function RequiresConsentNft({nft}: {nft: Balance.TokenInfo}) {
return (
@@ -166,7 +166,7 @@ function RequiresConsentNft({nft}: {nft: TokenInfo}) {
)
}
-function ApprovedNft({nft}: {nft: TokenInfo}) {
+function ApprovedNft({nft}: {nft: Balance.TokenInfo}) {
return (
diff --git a/apps/wallet-mobile/src/components/NftPreview/NftPreview.tsx b/apps/wallet-mobile/src/components/NftPreview/NftPreview.tsx
index 0d890eee32..b5c46e7bf3 100644
--- a/apps/wallet-mobile/src/components/NftPreview/NftPreview.tsx
+++ b/apps/wallet-mobile/src/components/NftPreview/NftPreview.tsx
@@ -1,3 +1,4 @@
+import {Balance} from '@yoroi/types'
import React, {useEffect, useState} from 'react'
import {ErrorBoundary} from 'react-error-boundary'
import {Image, ImageResizeMode, ImageStyle, StyleProp, View} from 'react-native'
@@ -6,7 +7,6 @@ import {SvgUri} from 'react-native-svg'
import placeholder from '../../assets/img/nft-placeholder.png'
import {getNftFilenameMediaType, getNftMainImageMediaType, isSvgMediaType} from '../../yoroi-wallets/cardano/nfts'
-import {TokenInfo} from '../../yoroi-wallets/types'
import {isString} from '../../yoroi-wallets/utils'
export const NftPreview = ({
@@ -19,7 +19,7 @@ export const NftPreview = ({
resizeMode,
blurRadius,
}: {
- nft: TokenInfo
+ nft: Balance.TokenInfo
showPlaceholder?: boolean
style?: StyleProp
showThumbnail?: boolean
diff --git a/apps/wallet-mobile/src/components/TokenIcon/ModeratedNftIcon.tsx b/apps/wallet-mobile/src/components/TokenIcon/ModeratedNftIcon.tsx
index d559cfd136..18d4704be0 100644
--- a/apps/wallet-mobile/src/components/TokenIcon/ModeratedNftIcon.tsx
+++ b/apps/wallet-mobile/src/components/TokenIcon/ModeratedNftIcon.tsx
@@ -1,12 +1,13 @@
+import {Balance} from '@yoroi/types'
import React from 'react'
import {StyleSheet, View} from 'react-native'
-import {TokenInfo, YoroiNftModerationStatus} from '../../yoroi-wallets/types'
+import {YoroiNftModerationStatus} from '../../yoroi-wallets/types'
import {NftPreview} from '../NftPreview'
const ICON_SIZE = 32
-export const ModeratedNftIcon = ({nft, status}: {nft: TokenInfo; status: YoroiNftModerationStatus}) => {
+export const ModeratedNftIcon = ({nft, status}: {nft: Balance.TokenInfo; status: YoroiNftModerationStatus}) => {
if (status === 'pending') {
return
}
@@ -30,7 +31,7 @@ export const ModeratedNftIcon = ({nft, status}: {nft: TokenInfo; status: YoroiNf
return null
}
-function PlaceholderNftIcon({nft}: {nft: TokenInfo}) {
+function PlaceholderNftIcon({nft}: {nft: Balance.TokenInfo}) {
return (
}
-function ApprovedNftIcon({nft}: {nft: TokenInfo}) {
+function ApprovedNftIcon({nft}: {nft: Balance.TokenInfo}) {
return (
@@ -57,7 +58,7 @@ function ApprovedNftIcon({nft}: {nft: TokenInfo}) {
)
}
-function ConsentNftIcon({nft}: {nft: TokenInfo}) {
+function ConsentNftIcon({nft}: {nft: Balance.TokenInfo}) {
return (
void
+ amountChanged: (quantity: Balance.Quantity) => void
amountRemoved: (tokenId: string) => void
receiverChanged: (receiver: string) => void
addressChanged: (address: Address) => void
@@ -59,7 +60,7 @@ export const SendProvider = ({children, ...props}: {initialState?: Partial dispatch({type: 'yoroiUnsignedTxChanged', yoroiUnsignedTx}),
tokenSelectedChanged: (tokenId: string) => dispatch({type: 'tokenSelectedChanged', tokenId}),
- amountChanged: (quantity: Quantity) => dispatch({type: 'amountChanged', quantity}),
+ amountChanged: (quantity: Balance.Quantity) => dispatch({type: 'amountChanged', quantity}),
amountRemoved: (tokenId: string) => dispatch({type: 'amountRemoved', tokenId}),
}).current
@@ -128,7 +129,7 @@ export type TargetAction =
}
| {
type: 'amountChanged'
- quantity: Quantity
+ quantity: Balance.Quantity
}
| {
type: 'amountRemoved'
@@ -286,5 +287,5 @@ export const useSelectedSecondaryAmountsCounter = (wallet: YoroiWallet) => {
const isSecondaryAmountFilter =
(wallet: YoroiWallet) =>
- ({tokenId}: YoroiAmount) =>
+ ({tokenId}: Balance.Amount) =>
tokenId !== wallet.primaryTokenInfo.id
diff --git a/apps/wallet-mobile/src/features/Send/common/filterByFungibility.test.ts b/apps/wallet-mobile/src/features/Send/common/filterByFungibility.test.ts
index d208b28220..c45c57fd2c 100644
--- a/apps/wallet-mobile/src/features/Send/common/filterByFungibility.test.ts
+++ b/apps/wallet-mobile/src/features/Send/common/filterByFungibility.test.ts
@@ -1,9 +1,10 @@
-import {TokenInfo} from '../../../yoroi-wallets/types'
+import {Balance} from '@yoroi/types'
+
import {FungibilityFilter} from '../useCases/ListAmountsToSend/AddToken/SelectTokenFromListScreen'
import {filterByFungibility} from './filterByFungibility'
describe('filterByFungibility', () => {
- const fakeToken1: TokenInfo = {
+ const fakeToken1: Balance.TokenInfo = {
kind: 'ft',
id: 'fake-token-1',
fingerprint: 'fake-fingerprint-1',
@@ -18,7 +19,7 @@ describe('filterByFungibility', () => {
symbol: undefined,
} as const
- const fakeToken2: TokenInfo = {
+ const fakeToken2: Balance.TokenInfo = {
kind: 'ft',
id: 'fake-token-2',
fingerprint: 'fake-fingerprint-2',
@@ -33,7 +34,7 @@ describe('filterByFungibility', () => {
symbol: undefined,
} as const
- const nft1: TokenInfo = {
+ const nft1: Balance.TokenInfo = {
kind: 'nft',
id: 'fake-token-3',
fingerprint: 'fake-fingerprint-3',
@@ -48,7 +49,7 @@ describe('filterByFungibility', () => {
symbol: undefined,
} as const
- const nft2: TokenInfo = {
+ const nft2: Balance.TokenInfo = {
kind: 'nft',
id: 'fake-token-4',
fingerprint: 'fake-fingerprint-4',
@@ -63,11 +64,11 @@ describe('filterByFungibility', () => {
symbol: undefined,
} as const
- const allTokenInfos: TokenInfo[] = [fakeToken1, fakeToken2, nft1, nft2]
- const nftTokenInfos: TokenInfo[] = [nft1, nft2]
- const ftTokenInfos: TokenInfo[] = [fakeToken1, fakeToken2]
+ const allTokenInfos: Balance.TokenInfo[] = [fakeToken1, fakeToken2, nft1, nft2]
+ const nftTokenInfos: Balance.TokenInfo[] = [nft1, nft2]
+ const ftTokenInfos: Balance.TokenInfo[] = [fakeToken1, fakeToken2]
- it.each<{fungibilityFilter: FungibilityFilter; result: TokenInfo[]}>([
+ it.each<{fungibilityFilter: FungibilityFilter; result: Balance.TokenInfo[]}>([
{
fungibilityFilter: 'all',
result: allTokenInfos,
diff --git a/apps/wallet-mobile/src/features/Send/common/filterByFungibility.ts b/apps/wallet-mobile/src/features/Send/common/filterByFungibility.ts
index 8bc34fdb31..8a923097ac 100644
--- a/apps/wallet-mobile/src/features/Send/common/filterByFungibility.ts
+++ b/apps/wallet-mobile/src/features/Send/common/filterByFungibility.ts
@@ -1,9 +1,10 @@
-import {TokenInfo} from '../../../yoroi-wallets/types'
+import {Balance} from '@yoroi/types'
+
import {FungibilityFilter} from '../useCases/ListAmountsToSend/AddToken/SelectTokenFromListScreen'
export const filterByFungibility = ({fungibilityFilter}: {fungibilityFilter: FungibilityFilter}) => {
if (fungibilityFilter === 'all') return () => true
- if (fungibilityFilter === 'nft') return (tokenInfo: TokenInfo) => tokenInfo.kind === 'nft'
- if (fungibilityFilter === 'ft') return (tokenInfo: TokenInfo) => tokenInfo.kind === 'ft'
+ if (fungibilityFilter === 'nft') return (tokenInfo: Balance.TokenInfo) => tokenInfo.kind === 'nft'
+ if (fungibilityFilter === 'ft') return (tokenInfo: Balance.TokenInfo) => tokenInfo.kind === 'ft'
return () => true
}
diff --git a/apps/wallet-mobile/src/features/Send/common/filterBySearch.test.ts b/apps/wallet-mobile/src/features/Send/common/filterBySearch.test.ts
index 0178a7b66b..66acbf11cc 100644
--- a/apps/wallet-mobile/src/features/Send/common/filterBySearch.test.ts
+++ b/apps/wallet-mobile/src/features/Send/common/filterBySearch.test.ts
@@ -1,8 +1,9 @@
-import {TokenInfo} from '../../../yoroi-wallets/types'
+import {Balance} from '@yoroi/types'
+
import {filterBySearch} from './filterBySearch'
describe('filterBySearch', () => {
- const fakeToken1: TokenInfo = {
+ const fakeToken1: Balance.TokenInfo = {
id: '',
kind: 'ft',
name: 'TADANAME',
@@ -17,7 +18,7 @@ describe('filterBySearch', () => {
metadatas: {},
} as const
- const fakeToken2: TokenInfo = {
+ const fakeToken2: Balance.TokenInfo = {
kind: 'ft',
id: '698a6ea0ca99f315034072af31eaac6ec11fe8558d3f48e9775aab9d.7444524950',
fingerprint: 'asset1nvcwnq60jnm27efjm87xnhqt6alsv024tdyxjm',
@@ -32,7 +33,7 @@ describe('filterBySearch', () => {
metadatas: {},
}
- const tokenInfos: TokenInfo[] = [fakeToken1, fakeToken2]
+ const tokenInfos: Balance.TokenInfo[] = [fakeToken1, fakeToken2]
it('should return all tokenInfos if searchTerm is empty', () => {
const searchTerm = ''
diff --git a/apps/wallet-mobile/src/features/Send/common/filterBySearch.ts b/apps/wallet-mobile/src/features/Send/common/filterBySearch.ts
index 574652c433..f0db9ef219 100644
--- a/apps/wallet-mobile/src/features/Send/common/filterBySearch.ts
+++ b/apps/wallet-mobile/src/features/Send/common/filterBySearch.ts
@@ -1,10 +1,10 @@
-import {TokenInfo} from '../../../yoroi-wallets/types'
+import {Balance} from '@yoroi/types'
export const filterBySearch = (searchTerm: string) => {
const searchTermLowerCase = searchTerm.toLocaleLowerCase()
if (searchTermLowerCase.length === 0) return () => true
- return (tokenInfo: TokenInfo) => {
+ return (tokenInfo: Balance.TokenInfo) => {
if (tokenInfo.kind === 'ft') {
return (
(tokenInfo.ticker?.toLocaleLowerCase()?.includes(searchTermLowerCase) ||
diff --git a/apps/wallet-mobile/src/features/Send/useCases/ConfirmTx/Summary/SecondaryTotals.tsx b/apps/wallet-mobile/src/features/Send/useCases/ConfirmTx/Summary/SecondaryTotals.tsx
index 5d841861c7..0449594de9 100644
--- a/apps/wallet-mobile/src/features/Send/useCases/ConfirmTx/Summary/SecondaryTotals.tsx
+++ b/apps/wallet-mobile/src/features/Send/useCases/ConfirmTx/Summary/SecondaryTotals.tsx
@@ -1,3 +1,4 @@
+import {Balance} from '@yoroi/types'
import * as React from 'react'
import {StyleSheet, View} from 'react-native'
@@ -8,7 +9,7 @@ import {useSelectedWallet} from '../../../../../SelectedWallet/Context/SelectedW
import {COLORS} from '../../../../../theme/config'
import {YoroiWallet} from '../../../../../yoroi-wallets/cardano/types'
import {useToken} from '../../../../../yoroi-wallets/hooks'
-import {YoroiAmount, YoroiUnsignedTx} from '../../../../../yoroi-wallets/types/yoroi'
+import {YoroiUnsignedTx} from '../../../../../yoroi-wallets/types/yoroi'
import {Amounts, Quantities} from '../../../../../yoroi-wallets/utils/utils'
export const SecondaryTotals = ({yoroiUnsignedTx}: {yoroiUnsignedTx: YoroiUnsignedTx}) => {
@@ -29,7 +30,7 @@ export const SecondaryTotals = ({yoroiUnsignedTx}: {yoroiUnsignedTx: YoroiUnsign
)
}
-const Amount = ({amount, wallet}: {amount: YoroiAmount; wallet: YoroiWallet}) => {
+const Amount = ({amount, wallet}: {amount: Balance.Amount; wallet: YoroiWallet}) => {
const token = useToken({wallet, tokenId: amount.tokenId})
return {formatTokenWithText(amount.quantity, token)}
diff --git a/apps/wallet-mobile/src/features/Send/useCases/ListAmountsToSend/AddToken/SelectTokenFromListScreen.tsx b/apps/wallet-mobile/src/features/Send/useCases/ListAmountsToSend/AddToken/SelectTokenFromListScreen.tsx
index b92704ae14..50fdc6257b 100644
--- a/apps/wallet-mobile/src/features/Send/useCases/ListAmountsToSend/AddToken/SelectTokenFromListScreen.tsx
+++ b/apps/wallet-mobile/src/features/Send/useCases/ListAmountsToSend/AddToken/SelectTokenFromListScreen.tsx
@@ -1,5 +1,6 @@
import {useNavigation} from '@react-navigation/native'
import {FlashList} from '@shopify/flash-list'
+import {Balance} from '@yoroi/types'
import React from 'react'
import {StyleSheet, TouchableOpacity, View} from 'react-native'
@@ -15,7 +16,6 @@ import {sortTokenInfos} from '../../../../../utils'
import {YoroiWallet} from '../../../../../yoroi-wallets/cardano/types'
import {limitOfSecondaryAmountsPerTx} from '../../../../../yoroi-wallets/contants'
import {useAllTokenInfos, useBalances, useIsWalletEmpty, useNfts} from '../../../../../yoroi-wallets/hooks'
-import {TokenInfo} from '../../../../../yoroi-wallets/types'
import {Amounts, Quantities} from '../../../../../yoroi-wallets/utils'
import {filterByFungibility} from '../../../common/filterByFungibility'
import {filterBySearch} from '../../../common/filterBySearch'
@@ -147,7 +147,7 @@ const AssetList = ({canAddAmount, fungibilityFilter}: AssetListProps) => {
(
+ renderItem={({item: tokenInfo}: {item: Balance.TokenInfo}) => (
(
)
-type SelectableAssetItemProps = {disabled?: boolean; tokenInfo: TokenInfo; wallet: YoroiWallet}
+type SelectableAssetItemProps = {disabled?: boolean; tokenInfo: Balance.TokenInfo; wallet: YoroiWallet}
const SelectableAssetItem = ({tokenInfo, disabled, wallet}: SelectableAssetItemProps) => {
const {closeSearch} = useSearch()
const {tokenSelectedChanged, amountChanged} = useSend()
@@ -240,8 +240,8 @@ const ListEmptyComponent = ({
filteredTokenInfos,
allTokenInfos,
}: {
- filteredTokenInfos: Array
- allTokenInfos: Array
+ filteredTokenInfos: Array
+ allTokenInfos: Array
}) => {
const {search: assetSearchTerm, visible: isSearching} = useSearch()
const wallet = useSelectedWallet()
@@ -342,7 +342,7 @@ const useFilteredTokenInfos = ({
tokenInfos,
}: {
fungibilityFilter: FungibilityFilter
- tokenInfos: Array
+ tokenInfos: Array
}) => {
const wallet = useSelectedWallet()
const {search: assetSearchTerm, visible: isSearching} = useSearch()
@@ -376,7 +376,7 @@ const useFilteredTokenInfos = ({
const areAllTokensSelected = (selectedTokenIds: Array, tokenInfos): boolean =>
tokenInfos.every((tokenInfo) => selectedTokenIds.includes(tokenInfo.id))
-const filterOutSelected = (selectedTokenIds: Array) => (token: TokenInfo) =>
+const filterOutSelected = (selectedTokenIds: Array) => (token: Balance.TokenInfo) =>
!selectedTokenIds.includes(token.id)
const sortNfts = (nftNameA: string, nftNameB: string): number => nftNameA.localeCompare(nftNameB)
diff --git a/apps/wallet-mobile/src/features/Send/useCases/ListAmountsToSend/EditAmount/EditAmountScreen.tsx b/apps/wallet-mobile/src/features/Send/useCases/ListAmountsToSend/EditAmount/EditAmountScreen.tsx
index b652b02fb4..2d4a6b9849 100644
--- a/apps/wallet-mobile/src/features/Send/useCases/ListAmountsToSend/EditAmount/EditAmountScreen.tsx
+++ b/apps/wallet-mobile/src/features/Send/useCases/ListAmountsToSend/EditAmount/EditAmountScreen.tsx
@@ -1,3 +1,4 @@
+import {Balance} from '@yoroi/types'
import * as React from 'react'
import {
KeyboardAvoidingView,
@@ -18,7 +19,6 @@ import {PairedBalance} from '../../../../../TxHistory/PairedBalance'
import {selectFtOrThrow} from '../../../../../yoroi-wallets/cardano/utils'
import {useTokenInfo} from '../../../../../yoroi-wallets/hooks'
import {Logger} from '../../../../../yoroi-wallets/logging'
-import {Quantity} from '../../../../../yoroi-wallets/types'
import {asQuantity, editedFormatter, pastedFormatter, Quantities} from '../../../../../yoroi-wallets/utils'
import {useNavigateTo, useOverridePreviousSendTxRoute} from '../../../common/navigation'
import {useSend, useTokenQuantities} from '../../../common/SendContext'
@@ -36,7 +36,7 @@ export const EditAmountScreen = () => {
const tokenInfo = useTokenInfo({wallet, tokenId: selectedTokenId}, {select: selectFtOrThrow})
const isPrimary = tokenInfo.id === wallet.primaryTokenInfo.id
- const [quantity, setQuantity] = React.useState(initialQuantity)
+ const [quantity, setQuantity] = React.useState(initialQuantity)
const [inputValue, setInputValue] = React.useState(
Quantities.denominated(initialQuantity, tokenInfo.decimals ?? 0),
)
diff --git a/apps/wallet-mobile/src/features/Send/useCases/ListAmountsToSend/ListAmountsToSendScreen.tsx b/apps/wallet-mobile/src/features/Send/useCases/ListAmountsToSend/ListAmountsToSendScreen.tsx
index 8424f2a76e..4cdc46142d 100644
--- a/apps/wallet-mobile/src/features/Send/useCases/ListAmountsToSend/ListAmountsToSendScreen.tsx
+++ b/apps/wallet-mobile/src/features/Send/useCases/ListAmountsToSend/ListAmountsToSendScreen.tsx
@@ -1,4 +1,5 @@
import {useNavigation} from '@react-navigation/native'
+import type {Balance} from '@yoroi/types'
import * as React from 'react'
import {useLayoutEffect} from 'react'
import {defineMessages, useIntl} from 'react-intl'
@@ -17,7 +18,7 @@ import {COLORS} from '../../../../theme'
import {sortTokenInfos} from '../../../../utils'
import {YoroiWallet} from '../../../../yoroi-wallets/cardano/types'
import {useTokenInfo, useTokenInfos} from '../../../../yoroi-wallets/hooks'
-import {TokenInfo, YoroiAmount, YoroiEntry, YoroiUnsignedTx} from '../../../../yoroi-wallets/types'
+import {YoroiEntry, YoroiUnsignedTx} from '../../../../yoroi-wallets/types'
import {Amounts} from '../../../../yoroi-wallets/utils'
import {useNavigateTo, useOverridePreviousSendTxRoute} from '../../common/navigation'
import {useSend} from '../../common/SendContext'
@@ -65,7 +66,7 @@ export const ListAmountsToSendScreen = () => {
const onEdit = (tokenId: string) => {
const tokenInfo = tokenInfos.find((tokenInfo) => tokenInfo.id === tokenId)
- if (!tokenInfo || tokenInfo.kind === 'nft') return
+ if (tokenInfo?.kind === 'nft') return
tokenSelectedChanged(tokenId)
navigateTo.editAmount()
@@ -91,7 +92,7 @@ export const ListAmountsToSendScreen = () => {
(
+ renderItem={({item: {id}}: {item: Balance.TokenInfo}) => (
@@ -117,7 +118,7 @@ export const ListAmountsToSendScreen = () => {
}
type ActionableAmountProps = {
- amount: YoroiAmount
+ amount: Balance.Amount
onEdit(tokenId: string): void
onRemove(tokenId: string): void
}
diff --git a/apps/wallet-mobile/src/legacy/format.ts b/apps/wallet-mobile/src/legacy/format.ts
index 5a9b1ad641..6c70dd1965 100644
--- a/apps/wallet-mobile/src/legacy/format.ts
+++ b/apps/wallet-mobile/src/legacy/format.ts
@@ -1,11 +1,12 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import AssetFingerprint from '@emurgo/cip14-js'
+import {Balance} from '@yoroi/types'
import {BigNumber} from 'bignumber.js'
import type {IntlShape} from 'react-intl'
import {defineMessages} from 'react-intl'
import {isTokenInfo} from '../yoroi-wallets/cardano/utils'
-import {DefaultAsset, Quantity, Token, TokenInfo} from '../yoroi-wallets/types'
+import {DefaultAsset, Token} from '../yoroi-wallets/types'
import utfSymbols from './utfSymbols'
export const getTokenFingerprint = ({policyId, assetNameHex}) => {
@@ -23,18 +24,18 @@ export const decodeHexAscii = (text: string) => {
return isAscii ? String.fromCharCode(...bytes) : undefined
}
-const getTicker = (token: TokenInfo | DefaultAsset) => {
+const getTicker = (token: Balance.TokenInfo | DefaultAsset) => {
if (isTokenInfo(token)) {
return token.kind === 'ft' ? token.ticker : undefined
}
return token.metadata.ticker
}
-const getSymbol = (token: TokenInfo | DefaultAsset) => {
+const getSymbol = (token: Balance.TokenInfo | DefaultAsset) => {
const ticker = getTicker(token)
return (ticker && utfSymbols.CURRENCIES[ticker]) ?? ticker
}
-const getName = (token: TokenInfo | DefaultAsset) => {
+const getName = (token: Balance.TokenInfo | DefaultAsset) => {
if (isTokenInfo(token)) {
return token.name || token.ticker || token.fingerprint || ''
}
@@ -49,26 +50,26 @@ const getName = (token: TokenInfo | DefaultAsset) => {
)
}
-export const getDecimals = (token: TokenInfo | DefaultAsset) => {
+export const getDecimals = (token: Balance.TokenInfo | DefaultAsset) => {
if (isTokenInfo(token)) {
return token.kind === 'nft' ? 0 : token.decimals
}
return token.metadata.numberOfDecimals
}
-export const normalizeTokenAmount = (amount: Quantity, token: TokenInfo | DefaultAsset): BigNumber => {
+export const normalizeTokenAmount = (amount: Balance.Quantity, token: Balance.TokenInfo | DefaultAsset): BigNumber => {
const decimals = getDecimals(token) ?? 0
const normalizationFactor = Math.pow(10, decimals)
return new BigNumber(amount).dividedBy(normalizationFactor).decimalPlaces(decimals)
}
-export const formatTokenAmount = (amount: Quantity, token: TokenInfo | DefaultAsset): string => {
+export const formatTokenAmount = (amount: Balance.Quantity, token: Balance.TokenInfo | DefaultAsset): string => {
const decimals = getDecimals(token)
const normalized = normalizeTokenAmount(amount, token)
return normalized.toFormat(decimals)
}
-const getTokenV2Fingerprint = (token: TokenInfo | DefaultAsset): string => {
+const getTokenV2Fingerprint = (token: Balance.TokenInfo | DefaultAsset): string => {
if (isTokenInfo(token)) {
return token.fingerprint
}
@@ -78,14 +79,14 @@ const getTokenV2Fingerprint = (token: TokenInfo | DefaultAsset): string => {
})
}
-export const formatTokenWithSymbol = (amount: Quantity, token: TokenInfo | DefaultAsset): string => {
+export const formatTokenWithSymbol = (amount: Balance.Quantity, token: Balance.TokenInfo | DefaultAsset): string => {
const denomination = getSymbol(token) ?? getTokenV2Fingerprint(token)
return `${formatTokenAmount(amount, token)}${utfSymbols.NBSP}${denomination}`
}
// We assume that tickers are non-localized. If ticker doesn't exist, default
// to identifier
-export const formatTokenWithText = (amount: Quantity, token: TokenInfo | DefaultAsset) => {
+export const formatTokenWithText = (amount: Balance.Quantity, token: Balance.TokenInfo | DefaultAsset) => {
if (isTokenInfo(token)) {
switch (token.kind) {
case 'nft':
@@ -99,12 +100,12 @@ export const formatTokenWithText = (amount: Quantity, token: TokenInfo | Default
return `${formatTokenAmount(amount, token)}${utfSymbols.NBSP}${tickerOrId}`
}
-export const formatTokenWithTextWhenHidden = (text: string, token: TokenInfo | DefaultAsset) => {
+export const formatTokenWithTextWhenHidden = (text: string, token: Balance.TokenInfo | DefaultAsset) => {
const tickerOrId = getTicker(token) || getName(token) || getTokenV2Fingerprint(token)
return `${text}${utfSymbols.NBSP}${tickerOrId}`
}
-export const formatTokenInteger = (amount: Quantity, token: Token | DefaultAsset) => {
+export const formatTokenInteger = (amount: Balance.Quantity, token: Token | DefaultAsset) => {
const normalizationFactor = Math.pow(10, token.metadata.numberOfDecimals)
const bigNumber = new BigNumber(amount)
const num = bigNumber.dividedToIntegerBy(normalizationFactor)
@@ -117,7 +118,7 @@ export const formatTokenInteger = (amount: Quantity, token: Token | DefaultAsset
}
}
-export const formatTokenFractional = (amount: Quantity, token: Token | DefaultAsset) => {
+export const formatTokenFractional = (amount: Balance.Quantity, token: Token | DefaultAsset) => {
const normalizationFactor = Math.pow(10, token.metadata.numberOfDecimals)
const fractional = new BigNumber(amount).abs().modulo(normalizationFactor).dividedBy(normalizationFactor)
// remove leading '0'
@@ -134,14 +135,14 @@ export const truncateWithEllipsis = (s: string, n: number) => {
// TODO(multi-asset): consider removing these
-const formatAda = (amount: Quantity, defaultAsset: DefaultAsset) => {
+const formatAda = (amount: Balance.Quantity, defaultAsset: DefaultAsset) => {
const defaultAssetMeta = defaultAsset.metadata
const normalizationFactor = Math.pow(10, defaultAssetMeta.numberOfDecimals)
const num = new BigNumber(amount).dividedBy(normalizationFactor)
return num.toFormat(6)
}
-export const formatAdaWithText = (amount: Quantity, defaultAsset: DefaultAsset) => {
+export const formatAdaWithText = (amount: Balance.Quantity, defaultAsset: DefaultAsset) => {
const defaultAssetMeta = defaultAsset.metadata
return `${formatAda(amount, defaultAsset)}${utfSymbols.NBSP}${defaultAssetMeta.ticker}`
}
diff --git a/apps/wallet-mobile/src/metrics/helpers.ts b/apps/wallet-mobile/src/metrics/helpers.ts
index 57a3e02e3f..2b6306483a 100644
--- a/apps/wallet-mobile/src/metrics/helpers.ts
+++ b/apps/wallet-mobile/src/metrics/helpers.ts
@@ -1,8 +1,8 @@
-import {TokenInfo, YoroiAmounts} from '../yoroi-wallets/types'
+import {Balance} from '@yoroi/types'
type AssetList = {
- tokens: TokenInfo[]
- amounts: YoroiAmounts
+ tokens: Balance.TokenInfo[]
+ amounts: Balance.Amounts
}
export const assetsToSendProperties = ({tokens, amounts}: AssetList) => {
diff --git a/apps/wallet-mobile/src/utils/sorting.ts b/apps/wallet-mobile/src/utils/sorting.ts
index f1c25a4af3..96814bb664 100644
--- a/apps/wallet-mobile/src/utils/sorting.ts
+++ b/apps/wallet-mobile/src/utils/sorting.ts
@@ -1,7 +1,14 @@
+import {Balance} from '@yoroi/types'
+
import {YoroiWallet} from '../yoroi-wallets/cardano/types'
-import {TokenInfo} from '../yoroi-wallets/types'
-export const sortTokenInfos = ({wallet, tokenInfos}: {wallet: YoroiWallet; tokenInfos: TokenInfo[]}): TokenInfo[] =>
+export const sortTokenInfos = ({
+ wallet,
+ tokenInfos,
+}: {
+ wallet: YoroiWallet
+ tokenInfos: Balance.TokenInfo[]
+}): Balance.TokenInfo[] =>
tokenInfos
.sort(
alpha((tokenInfo) => {
diff --git a/apps/wallet-mobile/src/yoroi-wallets/cardano/api/metadata.test.ts b/apps/wallet-mobile/src/yoroi-wallets/cardano/api/metadata.test.ts
index 5f76ed17bb..506d4a9399 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/cardano/api/metadata.test.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/cardano/api/metadata.test.ts
@@ -1,6 +1,6 @@
-import {TokenInfo} from '../../types'
-import {parseNFTs} from './metadata'
+import {Balance} from '@yoroi/types'
+import {parseNFTs} from './metadata'
const storageUrl = 'https://example.com'
describe('parseNFTs', () => {
it('throws when given a value that is not an object', () => {
@@ -30,7 +30,7 @@ describe('parseNFTs', () => {
it('resolves with placeholder data if key 721 is present and metadata is not', () => {
const result = parseNFTs({'8e2c7604711faef7c84c91b286c7327d17df825b7f0c88ec0332c0b4.0': [{key: '721'}]}, storageUrl)
- const expectedValue: Partial = {
+ const expectedValue: Partial = {
id: '8e2c7604711faef7c84c91b286c7327d17df825b7f0c88ec0332c0b4.30',
name: '0',
}
@@ -46,7 +46,7 @@ describe('parseNFTs', () => {
},
storageUrl,
)
- const expectedValue: Partial = {
+ const expectedValue: Partial = {
id: '8e2c7604711faef7c84c91b286c7327d17df825b7f0c88ec0332c0b4.30',
name: 'Name',
}
diff --git a/apps/wallet-mobile/src/yoroi-wallets/cardano/api/metadata.ts b/apps/wallet-mobile/src/yoroi-wallets/cardano/api/metadata.ts
index 71a52661c5..7a012fb102 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/cardano/api/metadata.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/cardano/api/metadata.ts
@@ -1,13 +1,14 @@
+import {Balance} from '@yoroi/types'
import {z} from 'zod'
-import {BackendConfig, NFTAsset, TokenInfo} from '../../types'
+import {BackendConfig, NFTAsset} from '../../types'
import {createTypeGuardFromSchema, isArray, isNonNullable, isRecord} from '../../utils'
import {convertNft} from '../nfts'
import {fetchTokensSupplies} from './assetSuply'
import fetchDefault from './fetch'
import {toAssetNameHex, toPolicyId} from './utils'
-export const getNFTs = async (ids: string[], config: BackendConfig): Promise => {
+export const getNFTs = async (ids: string[], config: BackendConfig): Promise => {
if (ids.length === 0) {
return []
}
@@ -28,19 +29,19 @@ export const getNFTs = async (ids: string[], config: BackendConfig): Promise assetSupplies[nft.id] === 1)
}
-export const getNFT = async (id: string, config: BackendConfig): Promise => {
+export const getNFT = async (id: string, config: BackendConfig): Promise => {
const [nft] = await getNFTs([id], config)
return nft || null
}
-export const parseNFTs = (value: unknown, storageUrl: string): TokenInfo[] => {
+export const parseNFTs = (value: unknown, storageUrl: string): Balance.TokenInfo[] => {
if (!isRecord(value)) {
throw new Error('Invalid response. Expected to receive object when parsing NFTs')
}
const identifiers = Object.keys(value)
- const tokens: Array = identifiers.map((id) => {
+ const tokens: Array = identifiers.map((id) => {
const assets = value[id]
if (!isArray(assets)) {
return null
diff --git a/apps/wallet-mobile/src/yoroi-wallets/cardano/api/tokenRegistry.ts b/apps/wallet-mobile/src/yoroi-wallets/cardano/api/tokenRegistry.ts
index 3e19652baf..c7e3d4a4fb 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/cardano/api/tokenRegistry.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/cardano/api/tokenRegistry.ts
@@ -1,13 +1,18 @@
+import {Balance} from '@yoroi/types'
import {z} from 'zod'
import {promiseAny} from '../../../utils'
-import {BackendConfig, TokenInfo} from '../../types'
+import {BackendConfig} from '../../types'
import {createTypeGuardFromSchema} from '../../utils'
import {checkedFetch} from './fetch'
import {getNFT} from './metadata'
import {fallbackTokenInfo, tokenInfo, toTokenSubject} from './utils'
-export const getTokenInfo = async (tokenId: string, apiUrl: string, config: BackendConfig): Promise => {
+export const getTokenInfo = async (
+ tokenId: string,
+ apiUrl: string,
+ config: BackendConfig,
+): Promise => {
const nftPromise = getNFT(tokenId, config).then((nft) => {
if (!nft) throw new Error('NFT not found')
return nft
@@ -26,7 +31,7 @@ export const getTokenInfo = async (tokenId: string, apiUrl: string, config: Back
})
try {
- const result = await promiseAny([nftPromise, tokenPromise])
+ const result = await promiseAny([nftPromise, tokenPromise])
return result ?? fallbackTokenInfo(tokenId)
} catch (e) {
return fallbackTokenInfo(tokenId)
diff --git a/apps/wallet-mobile/src/yoroi-wallets/cardano/api/utils.test.ts b/apps/wallet-mobile/src/yoroi-wallets/cardano/api/utils.test.ts
index 3179e4d086..14f81d82be 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/cardano/api/utils.test.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/cardano/api/utils.test.ts
@@ -1,4 +1,5 @@
-import {TokenInfo} from '../../types'
+import {Balance} from '@yoroi/types'
+
import {TokenRegistryEntry} from './tokenRegistry'
import {
fallbackTokenInfo,
@@ -148,7 +149,7 @@ describe('api utils', () => {
},
}
- expect(tokenInfo(entry)).toEqual({
+ expect(tokenInfo(entry)).toEqual({
kind: 'ft',
id: '11111111111111111111111111111111111111111111111111111111.61737365744e616d65',
name: 'assetName',
@@ -188,7 +189,7 @@ describe('api utils', () => {
},
}
- expect(tokenInfo(entry)).toEqual({
+ expect(tokenInfo(entry)).toEqual({
kind: 'ft',
id: '11111111111111111111111111111111111111111111111111111111.61737365744e616d65',
name: 'assetName',
@@ -214,7 +215,7 @@ describe('api utils', () => {
})
it('fallback', () => {
- expect(fallbackTokenInfo('11111111111111111111111111111111111111111111111111111111')).toEqual({
+ expect(fallbackTokenInfo('11111111111111111111111111111111111111111111111111111111')).toEqual({
kind: 'ft',
id: '11111111111111111111111111111111111111111111111111111111.',
fingerprint: 'asset17jfppv3h7hnsjfqq5lyp52dyhwstfv9e4uauga',
@@ -231,7 +232,7 @@ describe('api utils', () => {
expect(
fallbackTokenInfo('1111111111111111111111111111111111111111111111111111111161737365744e616d65'),
- ).toEqual({
+ ).toEqual({
kind: 'ft',
id: '11111111111111111111111111111111111111111111111111111111.61737365744e616d65',
fingerprint: 'asset1rafllrcpcurgdkesxy9vsvh40cgz2vrndle80x',
diff --git a/apps/wallet-mobile/src/yoroi-wallets/cardano/api/utils.ts b/apps/wallet-mobile/src/yoroi-wallets/cardano/api/utils.ts
index 116a053f31..edf71912ae 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/cardano/api/utils.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/cardano/api/utils.ts
@@ -1,10 +1,11 @@
import AssetFingerprint from '@emurgo/cip14-js'
+import {Balance} from '@yoroi/types'
import {Buffer} from 'memfs/lib/internal/buffer'
-import {LegacyToken, TokenInfo} from '../../types'
+import {LegacyToken} from '../../types'
import {TokenRegistryEntry} from './tokenRegistry'
-export const tokenInfo = (entry: TokenRegistryEntry): TokenInfo => {
+export const tokenInfo = (entry: TokenRegistryEntry): Balance.TokenInfo => {
const policyId = toPolicyId(entry.subject)
const assetName = toAssetName(entry.subject)
@@ -36,7 +37,7 @@ export const tokenInfo = (entry: TokenRegistryEntry): TokenInfo => {
}
}
-export const fallbackTokenInfo = (tokenId: string): TokenInfo => {
+export const fallbackTokenInfo = (tokenId: string): Balance.TokenInfo => {
const policyId = toPolicyId(tokenId)
const assetName = toAssetName(tokenId)
@@ -79,7 +80,7 @@ export const toTokenId = (tokenIdentifier: string) => {
export const hexToUtf8 = (hex: string) => Buffer.from(hex, 'hex').toString('utf-8')
export const utf8ToHex = (text: string) => Buffer.from(text, 'utf-8').toString('hex')
-export const toTokenInfo = (token: LegacyToken): TokenInfo => {
+export const toTokenInfo = (token: LegacyToken): Balance.TokenInfo => {
const policyId = toPolicyId(token.identifier)
const assetName = toAssetName(token.identifier)
diff --git a/apps/wallet-mobile/src/yoroi-wallets/cardano/byron/ByronWallet.ts b/apps/wallet-mobile/src/yoroi-wallets/cardano/byron/ByronWallet.ts
index 7a2abc9b1e..4f2e319ef6 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/cardano/byron/ByronWallet.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/cardano/byron/ByronWallet.ts
@@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import * as yoroiLib from '@emurgo/yoroi-lib'
+import {Balance} from '@yoroi/types'
import assert from 'assert'
import {BigNumber} from 'bignumber.js'
import ExtendableError from 'es6-error'
@@ -22,11 +23,9 @@ import {
NETWORK_REGISTRY,
NetworkId,
PoolInfoRequest,
- Quantity,
RawUtxo,
StakingInfo,
TipStatusResponse,
- TokenInfo,
Transaction,
TxStatusRequest,
TxStatusResponse,
@@ -124,7 +123,7 @@ const implementationId = WALLET_IMPLEMENTATION_REGISTRY.HASKELL_BYRON
export class ByronWallet implements YoroiWallet {
readonly primaryToken: DefaultAsset
- readonly primaryTokenInfo: TokenInfo
+ readonly primaryTokenInfo: Balance.TokenInfo
readonly id: string
readonly networkId: NetworkId
readonly walletImplementationId: WalletImplementationId
@@ -648,15 +647,15 @@ export class ByronWallet implements YoroiWallet {
const stakingUtxos = await this.getAllUtxosForKey()
const amount = Quantities.sum([
- ...stakingUtxos.map((utxo) => utxo.amount as Quantity),
- accountState.remainingAmount as Quantity,
+ ...stakingUtxos.map((utxo) => utxo.amount as Balance.Quantity),
+ accountState.remainingAmount as Balance.Quantity,
])
return {
status: 'staked',
poolId: stakingStatus.poolKeyHash,
amount,
- rewards: accountState.remainingAmount as Quantity,
+ rewards: accountState.remainingAmount as Balance.Quantity,
}
}
@@ -1342,7 +1341,7 @@ const keys: Array = [
'lastGeneratedAddressIndex',
]
-export const primaryTokenInfo: Record<'mainnet' | 'testnet', TokenInfo> = {
+export const primaryTokenInfo: Record<'mainnet' | 'testnet', Balance.TokenInfo> = {
mainnet: {
id: '',
name: 'ADA',
diff --git a/apps/wallet-mobile/src/yoroi-wallets/cardano/constants/mainnet/constants.ts b/apps/wallet-mobile/src/yoroi-wallets/cardano/constants/mainnet/constants.ts
index 4c086bcf39..06df309b7d 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/cardano/constants/mainnet/constants.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/cardano/constants/mainnet/constants.ts
@@ -1,4 +1,6 @@
-import {DefaultAsset, TokenInfo} from '../../../types'
+import {Balance} from '@yoroi/types'
+
+import {DefaultAsset} from '../../../types'
import {COIN_TYPE, COINS_PER_UTXO_WORD, KEY_DEPOSIT, LINEAR_FEE, MINIMUM_UTXO_VAL, POOL_DEPOSIT} from '../common'
export * from '../common'
@@ -74,7 +76,7 @@ export const NETWORK_CONFIG = {
COINS_PER_UTXO_WORD,
} as const
-export const PRIMARY_TOKEN_INFO: TokenInfo = {
+export const PRIMARY_TOKEN_INFO: Balance.TokenInfo = {
kind: 'ft',
id: '',
name: 'ADA',
diff --git a/apps/wallet-mobile/src/yoroi-wallets/cardano/constants/testnet/constants.ts b/apps/wallet-mobile/src/yoroi-wallets/cardano/constants/testnet/constants.ts
index f5f238914a..98b03733d6 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/cardano/constants/testnet/constants.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/cardano/constants/testnet/constants.ts
@@ -1,4 +1,6 @@
-import {DefaultAsset, TokenInfo} from '../../../types'
+import {Balance} from '@yoroi/types'
+
+import {DefaultAsset} from '../../../types'
import {COIN_TYPE, COINS_PER_UTXO_WORD, KEY_DEPOSIT, LINEAR_FEE, MINIMUM_UTXO_VAL, POOL_DEPOSIT} from '../common'
export * from '../common'
@@ -72,7 +74,7 @@ export const NETWORK_CONFIG = {
COINS_PER_UTXO_WORD,
} as const
-export const PRIMARY_TOKEN_INFO: TokenInfo = {
+export const PRIMARY_TOKEN_INFO: Balance.TokenInfo = {
id: '',
name: 'TADA',
description: 'Cardano',
diff --git a/apps/wallet-mobile/src/yoroi-wallets/cardano/getMinAmounts.ts b/apps/wallet-mobile/src/yoroi-wallets/cardano/getMinAmounts.ts
index b77b7819b9..0c85119c7a 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/cardano/getMinAmounts.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/cardano/getMinAmounts.ts
@@ -1,14 +1,15 @@
import {BigNum} from '@emurgo/cross-csl-core'
+import {Balance} from '@yoroi/types'
import BigNumber from 'bignumber.js'
-import {Token, YoroiAmounts} from '../types'
+import {Token} from '../types'
import {Amounts, asQuantity, Quantities} from '../utils'
import {CardanoMobile} from '../wallets'
import {COINS_PER_UTXO_WORD} from './constants/common'
import {MultiToken} from './MultiToken'
import {cardanoValueFromMultiToken} from './utils'
-export const withMinAmounts = async (amounts: YoroiAmounts, primaryToken: Token): Promise => {
+export const withMinAmounts = async (amounts: Balance.Amounts, primaryToken: Token): Promise => {
const amountsWithPrimaryToken = withPrimaryToken(amounts, primaryToken)
const minAmounts = await getMinAmounts(amountsWithPrimaryToken, primaryToken)
@@ -18,7 +19,7 @@ export const withMinAmounts = async (amounts: YoroiAmounts, primaryToken: Token)
}))
}
-export const getMinAmounts = async (amounts: YoroiAmounts, primaryToken: Token) => {
+export const getMinAmounts = async (amounts: Balance.Amounts, primaryToken: Token) => {
const multiToken = new MultiToken(
[
{identifier: primaryToken.identifier, networkId: primaryToken.networkId, amount: new BigNumber('0')},
@@ -42,10 +43,10 @@ export const getMinAmounts = async (amounts: YoroiAmounts, primaryToken: Token)
return {
[primaryToken.identifier]: minAda,
- } as YoroiAmounts
+ } as Balance.Amounts
}
-const withPrimaryToken = (amounts: YoroiAmounts, primaryToken: Token): YoroiAmounts => {
+const withPrimaryToken = (amounts: Balance.Amounts, primaryToken: Token): Balance.Amounts => {
if (Amounts.includes(amounts, primaryToken.identifier)) return amounts
return {
diff --git a/apps/wallet-mobile/src/yoroi-wallets/cardano/nfts.test.ts b/apps/wallet-mobile/src/yoroi-wallets/cardano/nfts.test.ts
index 00474e32eb..21d5076aa7 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/cardano/nfts.test.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/cardano/nfts.test.ts
@@ -1,5 +1,6 @@
+import {Balance} from '@yoroi/types'
+
import {nft} from '../mocks'
-import {TokenInfo} from '../types'
import {convertNft, getNftFilenameMediaType} from './nfts'
describe('convertNft', () => {
@@ -265,7 +266,7 @@ describe('getNftFilenameMediaType', () => {
})
})
-const getNftWithCustomOriginalMetadata = (metadata: unknown): TokenInfo => ({
+const getNftWithCustomOriginalMetadata = (metadata: unknown): Balance.TokenInfo => ({
...nft,
metadatas: {mintNft: metadata},
})
diff --git a/apps/wallet-mobile/src/yoroi-wallets/cardano/nfts.ts b/apps/wallet-mobile/src/yoroi-wallets/cardano/nfts.ts
index 0a296469c5..c8aba27a48 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/cardano/nfts.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/cardano/nfts.ts
@@ -1,8 +1,8 @@
+import {Balance} from '@yoroi/types'
import {z} from 'zod'
import {features} from '../../features'
import {getAssetFingerprint} from '../../legacy/format'
-import {TokenInfo} from '../types'
import {createTypeGuardFromSchema, isArrayOfType, isString} from '../utils'
import {utf8ToHex} from './api/utils'
@@ -11,7 +11,7 @@ export const convertNft = (options: {
storageUrl: string
policyId: string
shortName: string
-}): TokenInfo => {
+}): Balance.TokenInfo => {
const {metadata, storageUrl, policyId, shortName} = options
const assetNameHex = utf8ToHex(shortName)
const fingerprint = getAssetFingerprint(policyId, assetNameHex)
@@ -50,12 +50,12 @@ export const isSvgMediaType = (mediaType: unknown): boolean => {
return mediaType === 'image/svg+xml'
}
-export const getNftMainImageMediaType = (nft: TokenInfo): string | undefined => {
+export const getNftMainImageMediaType = (nft: Balance.TokenInfo): string | undefined => {
const originalMetadata = nft.metadatas.mintNft
return hasMediaTypeProperty(originalMetadata) ? normalizeProperty(originalMetadata.mediaType) : undefined
}
-export const getNftFilenameMediaType = (nft: TokenInfo, filename: string): string | undefined => {
+export const getNftFilenameMediaType = (nft: Balance.TokenInfo, filename: string): string | undefined => {
const originalMetadata = nft.metadatas.mintNft
if (!hasFilesProperty(originalMetadata)) {
diff --git a/apps/wallet-mobile/src/yoroi-wallets/cardano/shelley/ShelleyWallet.ts b/apps/wallet-mobile/src/yoroi-wallets/cardano/shelley/ShelleyWallet.ts
index 0e50ecf7ab..8d0b7e7b51 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/cardano/shelley/ShelleyWallet.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/cardano/shelley/ShelleyWallet.ts
@@ -1,4 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
+import {Balance} from '@yoroi/types'
import assert from 'assert'
import {BigNumber} from 'bignumber.js'
import ExtendableError from 'es6-error'
@@ -20,14 +21,13 @@ import type {
PoolInfoRequest,
RawUtxo,
TipStatusResponse,
- TokenInfo,
Transaction,
TxStatusRequest,
TxStatusResponse,
YoroiEntry,
YoroiNftModerationStatus,
} from '../../types'
-import {Quantity, StakingInfo, YoroiSignedTx, YoroiUnsignedTx} from '../../types'
+import {StakingInfo, YoroiSignedTx, YoroiUnsignedTx} from '../../types'
import {Quantities} from '../../utils'
import {parseSafe} from '../../utils/parsing'
import {validatePassword} from '../../utils/validators'
@@ -151,7 +151,7 @@ export const makeShelleyWallet = (constants: typeof MAINNET | typeof TESTNET) =>
return class ShelleyWallet implements YoroiWallet {
readonly primaryToken: DefaultAsset = PRIMARY_TOKEN
- readonly primaryTokenInfo: TokenInfo = PRIMARY_TOKEN_INFO
+ readonly primaryTokenInfo: Balance.TokenInfo = PRIMARY_TOKEN_INFO
readonly walletImplementationId = WALLET_IMPLEMENTATION_ID
readonly networkId = NETWORK_ID
readonly id: string
@@ -566,15 +566,15 @@ export const makeShelleyWallet = (constants: typeof MAINNET | typeof TESTNET) =>
const stakingUtxos = await this.getAllUtxosForKey()
const amount = Quantities.sum([
- ...stakingUtxos.map((utxo) => utxo.amount as Quantity),
- accountState.remainingAmount as Quantity,
+ ...stakingUtxos.map((utxo) => utxo.amount as Balance.Quantity),
+ accountState.remainingAmount as Balance.Quantity,
])
return {
status: 'staked',
poolId: stakingStatus.poolKeyHash,
amount,
- rewards: accountState.remainingAmount as Quantity,
+ rewards: accountState.remainingAmount as Balance.Quantity,
}
}
diff --git a/apps/wallet-mobile/src/yoroi-wallets/cardano/shelley/tests/mainnet/ShelleyWallet.test.ts b/apps/wallet-mobile/src/yoroi-wallets/cardano/shelley/tests/mainnet/ShelleyWallet.test.ts
index 458b2a8cec..14b8a9f1b4 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/cardano/shelley/tests/mainnet/ShelleyWallet.test.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/cardano/shelley/tests/mainnet/ShelleyWallet.test.ts
@@ -1,9 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import AsyncStorage from '@react-native-async-storage/async-storage'
+import {Balance} from '@yoroi/types'
import {HWDeviceInfo} from '../../../../hw'
import {EncryptedStorage, EncryptedStorageKeys, storage} from '../../../../storage'
-import {DefaultAsset, TokenInfo} from '../../../../types'
+import {DefaultAsset} from '../../../../types'
import {WalletMeta} from '../../../../walletManager'
import {ShelleyAddressGeneratorJSON} from '../../../chain'
import {YoroiWallet} from '../../../types'
@@ -105,7 +106,7 @@ describe('ShelleyWallet', () => {
},
networkId: 1,
})
- expect(wallet.primaryTokenInfo).toEqual({
+ expect(wallet.primaryTokenInfo).toEqual({
kind: 'ft',
description: 'Cardano',
id: '',
@@ -218,7 +219,7 @@ describe('ShelleyWallet', () => {
},
networkId: 1,
})
- expect(wallet.primaryTokenInfo).toEqual({
+ expect(wallet.primaryTokenInfo).toEqual({
kind: 'ft',
description: 'Cardano',
id: '',
@@ -321,7 +322,7 @@ describe('ShelleyWallet', () => {
},
networkId: 1,
})
- expect(wallet.primaryTokenInfo).toEqual({
+ expect(wallet.primaryTokenInfo).toEqual({
kind: 'ft',
description: 'Cardano',
id: '',
diff --git a/apps/wallet-mobile/src/yoroi-wallets/cardano/shelley/tests/testnet/ShelleyWalletTestnet.test.ts b/apps/wallet-mobile/src/yoroi-wallets/cardano/shelley/tests/testnet/ShelleyWalletTestnet.test.ts
index 0de322550f..51557acf27 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/cardano/shelley/tests/testnet/ShelleyWalletTestnet.test.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/cardano/shelley/tests/testnet/ShelleyWalletTestnet.test.ts
@@ -1,9 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import AsyncStorage from '@react-native-async-storage/async-storage'
+import {Balance} from '@yoroi/types'
import {HWDeviceInfo} from '../../../../hw'
import {EncryptedStorage, EncryptedStorageKeys, storage} from '../../../../storage'
-import {DefaultAsset, TokenInfo} from '../../../../types'
+import {DefaultAsset} from '../../../../types'
import {WalletMeta} from '../../../../walletManager'
import {ShelleyAddressGeneratorJSON} from '../../../chain'
import {WalletJSON} from '../../../shelley/ShelleyWallet'
@@ -102,7 +103,7 @@ describe('ShelleyWalletTestnet', () => {
},
networkId: 300,
})
- expect(wallet.primaryTokenInfo).toEqual({
+ expect(wallet.primaryTokenInfo).toEqual({
kind: 'ft',
id: '',
name: 'TADA',
@@ -215,7 +216,7 @@ describe('ShelleyWalletTestnet', () => {
},
networkId: 300,
})
- expect(wallet.primaryTokenInfo).toEqual({
+ expect(wallet.primaryTokenInfo).toEqual({
kind: 'ft',
id: '',
name: 'TADA',
@@ -317,7 +318,7 @@ describe('ShelleyWalletTestnet', () => {
},
networkId: 300,
})
- expect(wallet.primaryTokenInfo).toEqual({
+ expect(wallet.primaryTokenInfo).toEqual({
kind: 'ft',
id: '',
name: 'TADA',
diff --git a/apps/wallet-mobile/src/yoroi-wallets/cardano/types.ts b/apps/wallet-mobile/src/yoroi-wallets/cardano/types.ts
index 91221639c2..3dae9245a0 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/cardano/types.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/cardano/types.ts
@@ -11,6 +11,7 @@ import {
TxMetadata as TxMetadataType,
UnsignedTx as UnsignedTxType,
} from '@emurgo/yoroi-lib'
+import {Balance} from '@yoroi/types'
import {BigNumber} from 'bignumber.js'
import {HWDeviceInfo} from '../hw'
@@ -38,7 +39,7 @@ import type {
TxStatusResponse,
WalletState,
} from '../types/other'
-import {DefaultAsset, TokenInfo} from '../types/tokens'
+import {DefaultAsset} from '../types/tokens'
import type {Addresses} from './chain'
export type WalletEvent =
@@ -92,7 +93,7 @@ export type YoroiWallet = {
hwDeviceInfo: null | HWDeviceInfo
isReadOnly: boolean
primaryToken: Readonly
- primaryTokenInfo: Readonly
+ primaryTokenInfo: Readonly
// Sending
createUnsignedTx(entry: YoroiEntry, metadata?: Array): Promise
@@ -157,7 +158,7 @@ export type YoroiWallet = {
get confirmationCounts(): Record
fetchTipStatus(): Promise
fetchTxStatus(request: TxStatusRequest): Promise
- fetchTokenInfo(tokenId: string): Promise
+ fetchTokenInfo(tokenId: string): Promise
// Fiat
fetchCurrentPrice(symbol: CurrencySymbol): Promise
diff --git a/apps/wallet-mobile/src/yoroi-wallets/cardano/unsignedTx/unsignedTx.test.ts b/apps/wallet-mobile/src/yoroi-wallets/cardano/unsignedTx/unsignedTx.test.ts
index 21bc24a8b6..e4264d380d 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/cardano/unsignedTx/unsignedTx.test.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/cardano/unsignedTx/unsignedTx.test.ts
@@ -1,11 +1,12 @@
+import {Balance} from '@yoroi/types'
import BigNumber from 'bignumber.js'
-import {YoroiAmounts, YoroiEntries, YoroiMetadata} from '../../types'
+import {YoroiEntries, YoroiMetadata} from '../../types'
import {CardanoTypes} from '../types'
import {toAmounts, toDisplayAddress, toEntries, toMetadata} from './unsignedTx'
describe('YoroiUnsignedTx', () => {
- it('toAmounts converts TokenEntry[] to YoroiAmounts', () => {
+ it('toAmounts converts TokenEntry[] to Balance.Amounts', () => {
const tokenEntries: Array = [
{identifier: '', networkId: 1, amount: new BigNumber('1')},
{identifier: '', networkId: 1, amount: new BigNumber('3')},
@@ -18,7 +19,7 @@ describe('YoroiUnsignedTx', () => {
'': '4',
token123: '2',
token456: '2',
- } as YoroiAmounts)
+ } as Balance.Amounts)
})
it('toMetadata converts UnsignedTx to YoroiMetadata', () => {
diff --git a/apps/wallet-mobile/src/yoroi-wallets/cardano/unsignedTx/unsignedTx.ts b/apps/wallet-mobile/src/yoroi-wallets/cardano/unsignedTx/unsignedTx.ts
index 47a2f52663..070e2ada82 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/cardano/unsignedTx/unsignedTx.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/cardano/unsignedTx/unsignedTx.ts
@@ -1,4 +1,6 @@
-import {Quantity, YoroiAmounts, YoroiEntries, YoroiMetadata, YoroiUnsignedTx, YoroiVoting} from '../../types'
+import {Balance} from '@yoroi/types'
+
+import {YoroiEntries, YoroiMetadata, YoroiUnsignedTx, YoroiVoting} from '../../types'
import {Amounts, Entries, Quantities} from '../../utils'
import {Cardano, CardanoMobile} from '../../wallets'
import {CardanoHaskellShelleyNetwork} from '../networks'
@@ -85,10 +87,10 @@ export const toAmounts = (values: Array) =>
...result,
[current.identifier]: Quantities.sum([
Amounts.getAmount(result, current.identifier).quantity || '0',
- current.amount.toString() as Quantity,
+ current.amount.toString() as Balance.Quantity,
]),
}),
- {} as YoroiAmounts,
+ {} as Balance.Amounts,
)
export const toMetadata = (metadata: ReadonlyArray) =>
@@ -119,7 +121,7 @@ const Staking = {
for (let i = 0; i < length; i++) {
const rewardAddress = await rewardAddresses.get(i)
- const amount = (await withdrawals.get(rewardAddress).then((x) => x.toStr())) as Quantity
+ const amount = (await withdrawals.get(rewardAddress).then((x) => x.toStr())) as Balance.Quantity
const address = await rewardAddress
.toAddress()
.then((address) => address.toBytes())
@@ -153,7 +155,7 @@ const Staking = {
return {
...(await result),
- [address]: {'': KEY_DEPOSIT as Quantity},
+ [address]: {'': KEY_DEPOSIT as Balance.Quantity},
}
}, Promise.resolve({} as YoroiEntries)),
@@ -179,7 +181,7 @@ const Staking = {
return {
...(await result),
- [address]: {'': KEY_DEPOSIT as Quantity},
+ [address]: {'': KEY_DEPOSIT as Balance.Quantity},
}
}, Promise.resolve({} as YoroiEntries)),
@@ -189,11 +191,11 @@ const Staking = {
}: {
balances: CardanoTypes.StakingKeyBalances
fee: YoroiUnsignedTx['fee']
- }): {[poolId: string]: YoroiAmounts} =>
+ }): {[poolId: string]: Balance.Amounts} =>
Object.entries(balances).reduce(
(result, [poolId, quantity]) => ({
...result,
- [poolId]: Amounts.diff({'': quantity} as YoroiAmounts, fee),
+ [poolId]: Amounts.diff({'': quantity} as Balance.Amounts, fee),
}),
{},
),
diff --git a/apps/wallet-mobile/src/yoroi-wallets/cardano/utils.test.ts b/apps/wallet-mobile/src/yoroi-wallets/cardano/utils.test.ts
index 028fa5a61a..704641434e 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/cardano/utils.test.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/cardano/utils.test.ts
@@ -1,6 +1,7 @@
import {SendToken} from '@emurgo/yoroi-lib'
+import {Balance} from '@yoroi/types'
-import {Token, YoroiAmounts} from '../types'
+import {Token} from '../types'
import {PRIMARY_TOKEN} from './constants/mainnet/constants'
import {toSendToken, toSendTokenList} from './utils'
@@ -8,7 +9,7 @@ describe('toSendTokenList', () => {
const asSendToken = toSendToken(PRIMARY_TOKEN)
it('converts amounts to send token list for tx (lib)', async () => {
- const amounts: YoroiAmounts = {
+ const amounts: Balance.Amounts = {
[PRIMARY_TOKEN.identifier]: '123',
[secondaryToken.identifier]: '456',
}
diff --git a/apps/wallet-mobile/src/yoroi-wallets/cardano/utils.ts b/apps/wallet-mobile/src/yoroi-wallets/cardano/utils.ts
index fbef2f2520..538bba13be 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/cardano/utils.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/cardano/utils.ts
@@ -1,6 +1,6 @@
/* eslint-disable no-empty */
-
import {SendToken} from '@emurgo/yoroi-lib'
+import {Balance} from '@yoroi/types'
import {BigNumber} from 'bignumber.js'
import {
@@ -12,8 +12,7 @@ import {
WALLET_IMPLEMENTATION_REGISTRY,
WalletImplementationId,
} from '../types/other'
-import {DefaultAsset, Token, TokenInfo} from '../types/tokens'
-import {YoroiAmount, YoroiAmounts} from '../types/yoroi'
+import {DefaultAsset, Token} from '../types/tokens'
import {Amounts} from '../utils'
import {CardanoMobile} from '../wallets'
import {toAssetNameHex, toPolicyId} from './api/utils'
@@ -275,13 +274,13 @@ export const toCardanoNetworkId = (networkId: number) => {
throw new Error('invalid network id')
}
-export const toSendTokenList = (amounts: YoroiAmounts, primaryToken: Token): Array => {
+export const toSendTokenList = (amounts: Balance.Amounts, primaryToken: Token): Array => {
return Amounts.toArray(amounts).map(toSendToken(primaryToken))
}
export const toSendToken =
(primaryToken: Token) =>
- (amount: YoroiAmount): SendToken => {
+ (amount: Balance.Amount): SendToken => {
const {tokenId, quantity} = amount
const isPrimary = tokenId === primaryToken.identifier
@@ -296,11 +295,11 @@ export const toSendToken =
}
}
-export const isTokenInfo = (token: TokenInfo | DefaultAsset): token is TokenInfo => {
- return !!(token as TokenInfo).kind
+export const isTokenInfo = (token: Balance.TokenInfo | DefaultAsset): token is Balance.TokenInfo => {
+ return !!(token as Balance.TokenInfo).kind
}
-export const selectFtOrThrow = (token: TokenInfo): TokenInfo => {
+export const selectFtOrThrow = (token: Balance.TokenInfo): Balance.TokenInfo => {
if (token.kind === 'ft') {
return token
}
diff --git a/apps/wallet-mobile/src/yoroi-wallets/hooks/index.ts b/apps/wallet-mobile/src/yoroi-wallets/hooks/index.ts
index 5f5b03e7c4..c5f82e5f69 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/hooks/index.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/hooks/index.ts
@@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import AsyncStorage, {AsyncStorageStatic} from '@react-native-async-storage/async-storage'
+import {Balance} from '@yoroi/types'
import * as React from 'react'
import {useCallback, useMemo} from 'react'
import {
@@ -22,11 +23,8 @@ import {HWDeviceInfo} from '../hw'
import {parseWalletMeta} from '../migrations/walletMeta'
import {useStorage} from '../storage'
import {
- Quantity,
- TokenInfo,
TRANSACTION_DIRECTION,
TRANSACTION_STATUS,
- YoroiAmounts,
YoroiNftModerationStatus,
YoroiSignedTx,
YoroiUnsignedTx,
@@ -130,13 +128,14 @@ export const useAssetIds = (wallet: YoroiWallet): string[] => {
*/
export const useLockedAmount = (
{wallet}: {wallet: YoroiWallet},
- options?: UseQueryOptions,
+ options?: UseQueryOptions,
) => {
const query = useQuery({
...options,
suspense: true,
queryKey: [wallet.id, 'lockedAmount'],
- queryFn: () => calcLockedDeposit(wallet.utxos, wallet.networkId).then((amount) => amount.toString() as Quantity),
+ queryFn: () =>
+ calcLockedDeposit(wallet.utxos, wallet.networkId).then((amount) => amount.toString() as Balance.Quantity),
})
React.useEffect(() => {
@@ -198,9 +197,9 @@ export const useChangeWalletName = (wallet: YoroiWallet, options: UseMutationOpt
}
}
-export const useTokenInfo = (
+export const useTokenInfo = (
{wallet, tokenId}: {wallet: YoroiWallet; tokenId: string},
- options?: UseQueryOptions,
+ options?: UseQueryOptions,
) => {
const query = useQuery({
...options,
@@ -245,7 +244,7 @@ export const useNftImageModerated = ({
export const useToken = (
{wallet, tokenId}: {wallet: YoroiWallet; tokenId: string},
- options?: UseQueryOptions,
+ options?: UseQueryOptions,
) => {
const query = useQuery({
...options,
@@ -261,7 +260,7 @@ export const useToken = (
export const useTokenInfosDetailed = (
{wallet, tokenIds}: {wallet: YoroiWallet; tokenIds: Array},
- options?: UseQueryOptions,
+ options?: UseQueryOptions,
) => {
const queries = tokenIds.map((tokenId) => ({
...options,
@@ -274,10 +273,10 @@ export const useTokenInfosDetailed = (
export const useTokenInfos = (
{wallet, tokenIds}: {wallet: YoroiWallet; tokenIds: Array},
- options?: UseQueryOptions,
+ options?: UseQueryOptions,
) => {
const results = useTokenInfosDetailed({wallet, tokenIds}, options)
- return results.reduce((result, {data}) => (data ? [...result, data] : result), [] as Array)
+ return results.reduce((result, {data}) => (data ? [...result, data] : result), [] as Array)
}
export const useAllTokenInfos = ({wallet}: {wallet: YoroiWallet}) => {
@@ -858,7 +857,7 @@ export const useExchangeRate = ({
return query.data
}
-export const useBalances = (wallet: YoroiWallet): YoroiAmounts => {
+export const useBalances = (wallet: YoroiWallet): Balance.Amounts => {
const utxos = useUtxos(wallet)
return Utxos.toAmounts(utxos, wallet.primaryTokenInfo.id)
@@ -897,10 +896,10 @@ export const useSaveMemo = (
}
}
-export const useNfts = (wallet: YoroiWallet, options: UseQueryOptions = {}) => {
+export const useNfts = (wallet: YoroiWallet, options: UseQueryOptions = {}) => {
const assetIds = useAssetIds(wallet)
const results = useTokenInfosDetailed({wallet, tokenIds: assetIds}, options)
- const nfts = results.map((r) => r.data).filter((t): t is TokenInfo => t?.kind === 'nft')
+ const nfts = results.map((r) => r.data).filter((t): t is Balance.TokenInfo => t?.kind === 'nft')
const isLoading = results.some((r) => r.isLoading)
const isError = results.some((r) => r.isError)
const error = results.find((r) => r.isError)?.error
@@ -908,7 +907,7 @@ export const useNfts = (wallet: YoroiWallet, options: UseQueryOptions {
+export const useNft = (wallet: YoroiWallet, {id}: {id: string}): Balance.TokenInfo => {
const tokenInfo = useTokenInfo({wallet, tokenId: id}, {suspense: true})
if (tokenInfo.kind !== 'nft') {
diff --git a/apps/wallet-mobile/src/yoroi-wallets/mocks/wallet.ts b/apps/wallet-mobile/src/yoroi-wallets/mocks/wallet.ts
index 4b49a28f47..84ee12874d 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/mocks/wallet.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/mocks/wallet.ts
@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-explicit-any */
import {action} from '@storybook/addon-actions'
+import {Balance} from '@yoroi/types'
import BigNumber from 'bignumber.js'
import {getTokenFingerprint} from '../../legacy/format'
@@ -14,9 +15,7 @@ import {
StakePoolInfosAndHistories,
StakingInfo,
StakingStatus,
- TokenInfo,
TransactionInfo,
- YoroiAmounts,
YoroiNftModerationStatus,
YoroiSignedTx,
YoroiUnsignedTx,
@@ -295,7 +294,7 @@ const fetchPoolInfo = {
},
}
-export const generateManyNfts = (): TokenInfo[] => {
+export const generateManyNfts = (): Balance.TokenInfo[] => {
return Array(30)
.fill(undefined)
.map((_, index) => ({
@@ -515,16 +514,16 @@ const fetchCurrentPrice = {
const fetchTokenInfo = {
success: {
- nft: async (...args): Promise => {
+ nft: async (...args): Promise => {
action('fetchTokenInfo')(...args)
return nft
},
- randomNft: async (...args): Promise => {
+ randomNft: async (...args): Promise => {
action('fetchTokenInfo')(...args)
const allNfts = generateManyNfts()
return allNfts[Math.floor(Math.random() * allNfts.length)]
},
- ft: async (...args): Promise => {
+ ft: async (...args): Promise => {
action('fetchTokenInfo')(...args)
return {
kind: 'ft',
@@ -550,7 +549,7 @@ const fetchTokenInfo = {
},
}
},
- ftNoImage: async (...args): Promise => {
+ ftNoImage: async (...args): Promise => {
action('fetchTokenInfo')(...args)
return {
kind: 'ft',
@@ -579,7 +578,7 @@ const fetchTokenInfo = {
},
loading: async (...args) => {
action('fetchTokenInfo')(...args)
- return new Promise(() => null) as unknown as TokenInfo
+ return new Promise(() => null) as unknown as Balance.TokenInfo
},
error: async (...args) => {
action('fetchTokenInfo')(...args)
@@ -620,7 +619,7 @@ const tokenEntries: Array = [
},
]
-const balances: YoroiAmounts = {
+const balances: Balance.Amounts = {
[PRIMARY_ASSET_CONSTANTS.CARDANO]: '2727363743849',
'698a6ea0ca99f315034072af31eaac6ec11fe8558d3f48e9775aab9d.7444524950': '12344',
'29d222ce763455e3d7a09a665ce554f00ac89d2e99a1a83d267170c6.4d494e': '215410',
@@ -632,7 +631,7 @@ const balances: YoroiAmounts = {
'1d129dc9c03f95a863489883914f05a52e13135994a32f0cbeacc65e.74484f444c55': '100000000000000000020',
}
-const tokenInfos: Record = {
+const tokenInfos: Record = {
'': HASKELL_SHELLEY_TESTNET.PRIMARY_TOKEN_INFO,
'698a6ea0ca99f315034072af31eaac6ec11fe8558d3f48e9775aab9d.7444524950': toTokenInfo({
networkId: 300,
@@ -841,7 +840,7 @@ const yoroiSignedTx: YoroiSignedTx & {mock: true} = {
mock: true,
}
-export const nft: TokenInfo = {
+export const nft: Balance.TokenInfo = {
kind: 'nft',
id: `8e2c7604711faef7c84c91b286c7327d17df825b7f0c88ec0332c0b4.${utf8ToHex('NFT 0')}`,
group: '8e2c7604711faef7c84c91b286c7327d17df825b7f0c88ec0332c0b4',
diff --git a/apps/wallet-mobile/src/yoroi-wallets/types/staking.ts b/apps/wallet-mobile/src/yoroi-wallets/types/staking.ts
index 3d927ae2ca..2c0fe54f66 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/types/staking.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/types/staking.ts
@@ -1,4 +1,4 @@
-import {Quantity} from './yoroi'
+import {Balance} from '@yoroi/types'
export type StakingInfo =
| {status: 'not-registered'}
@@ -6,8 +6,8 @@ export type StakingInfo =
| {
status: 'staked'
poolId: string
- amount: Quantity
- rewards: Quantity
+ amount: Balance.Quantity
+ rewards: Balance.Quantity
}
export type StakingStatus =
diff --git a/apps/wallet-mobile/src/yoroi-wallets/types/tokens.ts b/apps/wallet-mobile/src/yoroi-wallets/types/tokens.ts
index f78e6c8a0d..76e7cdbf28 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/types/tokens.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/types/tokens.ts
@@ -52,29 +52,3 @@ export type NFTAsset = {
key: '721'
metadata?: unknown
}
-
-export type TokenInfo = {
- kind: 'ft' | 'nft'
- id: string
- group: string
- fingerprint: string
- image: string | undefined
- icon: string | undefined
- decimals: number | undefined
- symbol: string | undefined
- name: string
- description: string | undefined
- ticker: string | undefined
- metadatas: {mintNft?: NftMetadata; mintFt?: FtMetadata; tokenRegistry?: FtMetadata}
-}
-
-type FtMetadata = {
- description: string | Array | undefined
- icon: string | Array | undefined
- decimals: number | undefined
- ticker: string | undefined
- url: string | undefined
- version: string | undefined
-}
-
-export type NftMetadata = unknown
diff --git a/apps/wallet-mobile/src/yoroi-wallets/types/yoroi.ts b/apps/wallet-mobile/src/yoroi-wallets/types/yoroi.ts
index 27a1bf8255..26439c563f 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/types/yoroi.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/types/yoroi.ts
@@ -1,3 +1,5 @@
+import {Balance} from '@yoroi/types'
+
import {CardanoTypes, YoroiWallet} from '../cardano/types'
import {HWDeviceInfo} from '../hw'
import {YoroiStorage} from '../storage'
@@ -13,8 +15,8 @@ export type YoroiSignedTx = YoroiTxInfo & {
export type YoroiTxInfo = {
entries: YoroiEntries
- amounts: YoroiAmounts
- fee: YoroiAmounts
+ amounts: Balance.Amounts
+ fee: Balance.Amounts
change: YoroiEntries
metadata: YoroiMetadata
staking: YoroiStaking
@@ -38,23 +40,13 @@ export type YoroiVoting = {
}
export type Address = string
-export type Quantity = `${number}`
export type TokenId = string
-export type YoroiEntries = Record
+export type YoroiEntries = Record
export type YoroiEntry = {
address: Address
- amounts: YoroiAmounts
-}
-
-export type YoroiAmounts = {
- [tokenId: TokenId]: Quantity
-}
-
-export type YoroiAmount = {
- tokenId: TokenId
- quantity: Quantity
+ amounts: Balance.Amounts
}
export type YoroiMetadata = {
diff --git a/apps/wallet-mobile/src/yoroi-wallets/utils/utils.test.ts b/apps/wallet-mobile/src/yoroi-wallets/utils/utils.test.ts
index 73b6cd398d..03682ea5fe 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/utils/utils.test.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/utils/utils.test.ts
@@ -1,31 +1,32 @@
+import {Balance} from '@yoroi/types'
import BigNumber from 'bignumber.js'
-import {Quantity, YoroiAmount, YoroiAmounts, YoroiEntries, YoroiEntry} from '../types'
+import {YoroiEntries, YoroiEntry} from '../types'
import {RawUtxo} from '../types/other'
import {Amounts, asQuantity, Entries, Quantities, Utxos} from './utils'
describe('Quantities', () => {
it('sum', () => {
- expect(Quantities.sum(['1', '2'])).toEqual('3' as Quantity)
- expect(Quantities.sum(['1', '2', '3'])).toEqual('6' as Quantity)
+ expect(Quantities.sum(['1', '2'])).toEqual('3' as Balance.Quantity)
+ expect(Quantities.sum(['1', '2', '3'])).toEqual('6' as Balance.Quantity)
})
it('diff', () => {
- expect(Quantities.diff('1', '2')).toEqual('-1' as Quantity)
- expect(Quantities.diff('3', '2')).toEqual('1' as Quantity)
+ expect(Quantities.diff('1', '2')).toEqual('-1' as Balance.Quantity)
+ expect(Quantities.diff('3', '2')).toEqual('1' as Balance.Quantity)
})
it('negated', () => {
- expect(Quantities.negated('1')).toEqual('-1' as Quantity)
- expect(Quantities.negated('-1')).toEqual('1' as Quantity)
+ expect(Quantities.negated('1')).toEqual('-1' as Balance.Quantity)
+ expect(Quantities.negated('-1')).toEqual('1' as Balance.Quantity)
})
it('product', () => {
- expect(Quantities.product(['1', '2'])).toEqual('2' as Quantity)
- expect(Quantities.product(['2', '3'])).toEqual('6' as Quantity)
+ expect(Quantities.product(['1', '2'])).toEqual('2' as Balance.Quantity)
+ expect(Quantities.product(['2', '3'])).toEqual('6' as Balance.Quantity)
})
it('quotient', () => {
- expect(Quantities.quotient('1', '2')).toEqual('0.5' as Quantity)
- expect(Quantities.quotient('2', '1')).toEqual('2' as Quantity)
+ expect(Quantities.quotient('1', '2')).toEqual('0.5' as Balance.Quantity)
+ expect(Quantities.quotient('2', '1')).toEqual('2' as Balance.Quantity)
})
it('isGreaterThan', () => {
expect(Quantities.isGreaterThan('1', '2')).toBe(false)
@@ -86,13 +87,13 @@ describe('Quantities', () => {
})
describe('Amounts', () => {
- it('sums multiple YoroiAmounts into a single YoroiAmounts', () => {
- const amounts1: YoroiAmounts = {
+ it('sums multiple Balance.Amounts into a single Balance.Amounts', () => {
+ const amounts1: Balance.Amounts = {
'': '1',
token123: '2',
token567: '-2',
}
- const amounts2: YoroiAmounts = {
+ const amounts2: Balance.Amounts = {
'': '3',
token456: '4',
}
@@ -102,16 +103,16 @@ describe('Amounts', () => {
token123: '2',
token456: '4',
token567: '-2',
- } as YoroiAmounts)
+ } as Balance.Amounts)
})
- it('diffs 2 YoroiAmounts into a single YoroiAmounts', () => {
- const amounts1: YoroiAmounts = {
+ it('diffs 2 Balance.Amounts into a single Balance.Amounts', () => {
+ const amounts1: Balance.Amounts = {
'': '1',
token123: '2',
token567: '-2',
}
- const amounts2: YoroiAmounts = {
+ const amounts2: Balance.Amounts = {
'': '3',
token456: '4',
}
@@ -121,11 +122,11 @@ describe('Amounts', () => {
token123: '2',
token456: '-4',
token567: '-2',
- } as YoroiAmounts)
+ } as Balance.Amounts)
})
- it('negate YoroiAmounts', () => {
- const amounts1: YoroiAmounts = {
+ it('negate Balance.Amounts', () => {
+ const amounts1: Balance.Amounts = {
'': '1',
token123: '2',
token567: '-2',
@@ -135,11 +136,11 @@ describe('Amounts', () => {
'': '-1',
token123: '-2',
token567: '2',
- } as YoroiAmounts)
+ } as Balance.Amounts)
})
it('getAmount', () => {
- const amounts: YoroiAmounts = {
+ const amounts: Balance.Amounts = {
'': '1',
token123: '2',
token567: '-2',
@@ -149,12 +150,12 @@ describe('Amounts', () => {
expect(Amounts.getAmount(amounts, tokenId)).toEqual({
tokenId,
quantity,
- } as YoroiAmount),
+ } as Balance.Amount),
)
})
it('includes', () => {
- const amounts: YoroiAmounts = {
+ const amounts: Balance.Amounts = {
'': '1',
token123: '2',
token567: '-2',
@@ -166,7 +167,7 @@ describe('Amounts', () => {
})
it('remove', () => {
- const amounts: YoroiAmounts = {
+ const amounts: Balance.Amounts = {
'': '123',
token123: '456',
token567: '-789',
@@ -175,11 +176,11 @@ describe('Amounts', () => {
expect(Amounts.remove(amounts, ['token123'])).toEqual({
'': '123',
token567: '-789',
- } as YoroiAmounts)
+ } as Balance.Amounts)
})
it('toArray', () => {
- const amounts: YoroiAmounts = {
+ const amounts: Balance.Amounts = {
'': '123',
token123: '456',
token567: '-789',
@@ -189,11 +190,11 @@ describe('Amounts', () => {
{tokenId: '', quantity: '123'},
{tokenId: 'token123', quantity: '456'},
{tokenId: 'token567', quantity: '-789'},
- ] as Array)
+ ] as Array)
})
it('from Array', () => {
- const amounts: Array = [
+ const amounts: Array = [
{tokenId: '', quantity: '123'},
{tokenId: 'SUN', quantity: '456'},
{tokenId: 'QWE', quantity: '789'},
@@ -203,11 +204,11 @@ describe('Amounts', () => {
'': '123',
SUN: '456',
QWE: '789',
- } as YoroiAmounts)
+ } as Balance.Amounts)
})
it('map', () => {
- const amounts: YoroiAmounts = {
+ const amounts: Balance.Amounts = {
'': '1',
SUN: '4',
QWE: '7',
@@ -222,15 +223,15 @@ describe('Amounts', () => {
'': '2',
SUN: '5',
QWE: '8',
- } as YoroiAmounts)
+ } as Balance.Amounts)
})
describe('save', () => {
it('updating when already exists', () => {
- const amounts: YoroiAmounts = {
+ const amounts: Balance.Amounts = {
updateToken: '456',
}
- const updateAmount: YoroiAmount = {
+ const updateAmount: Balance.Amount = {
tokenId: 'updateToken',
quantity: '321',
}
@@ -241,10 +242,10 @@ describe('Amounts', () => {
})
it('adding when it doesnt exist', () => {
- const amounts: YoroiAmounts = {
+ const amounts: Balance.Amounts = {
updateToken: '456',
}
- const addAmount: YoroiAmount = {
+ const addAmount: Balance.Amount = {
tokenId: 'addToken',
quantity: '789',
}
@@ -360,7 +361,7 @@ describe('Entries', () => {
'': '3',
token123: '6',
token567: '-6',
- } as YoroiAmounts)
+ } as Balance.Amounts)
})
})
@@ -372,7 +373,7 @@ describe('Utxos', () => {
expect(Utxos.toAmounts(utxos, primaryTokenId)).toEqual({
primaryTokenId: '0',
- } as YoroiAmounts)
+ } as Balance.Amounts)
})
it('Utxos without tokens', () => {
@@ -415,7 +416,7 @@ describe('Utxos', () => {
expect(Utxos.toAmounts(utxos, primaryTokenId)).toEqual({
primaryTokenId: '627690',
- } as YoroiAmounts)
+ } as Balance.Amounts)
})
it('Utxos with tokens', () => {
@@ -463,7 +464,7 @@ describe('Utxos', () => {
primaryTokenId: '67905',
token123: '15',
token567: '8',
- } as YoroiAmounts)
+ } as Balance.Amounts)
})
})
})
diff --git a/apps/wallet-mobile/src/yoroi-wallets/utils/utils.ts b/apps/wallet-mobile/src/yoroi-wallets/utils/utils.ts
index 32e7485fcf..c3b2f705f7 100644
--- a/apps/wallet-mobile/src/yoroi-wallets/utils/utils.ts
+++ b/apps/wallet-mobile/src/yoroi-wallets/utils/utils.ts
@@ -1,6 +1,7 @@
+import {Balance} from '@yoroi/types'
import BigNumber from 'bignumber.js'
-import {Quantity, RawUtxo, TokenId, YoroiAmount, YoroiAmounts, YoroiEntries, YoroiEntry} from '../types'
+import {RawUtxo, TokenId, YoroiEntries, YoroiEntry} from '../types'
export const Entries = {
first: (entries: YoroiEntries): YoroiEntry => {
@@ -23,7 +24,7 @@ export const Entries = {
toAddresses: (entries: YoroiEntries): Array => {
return Object.keys(entries)
},
- toAmounts: (entries: YoroiEntries): YoroiAmounts => {
+ toAmounts: (entries: YoroiEntries): Balance.Amounts => {
const amounts = Object.values(entries)
return Amounts.sum(amounts)
@@ -31,7 +32,7 @@ export const Entries = {
}
export const Amounts = {
- sum: (amounts: Array): YoroiAmounts => {
+ sum: (amounts: Array): Balance.Amounts => {
const entries = amounts.map((amounts) => Object.entries(amounts)).flat()
return entries.reduce(
@@ -39,33 +40,33 @@ export const Amounts = {
...result,
[tokenId]: result[tokenId] ? Quantities.sum([result[tokenId], quantity]) : quantity,
}),
- {} as YoroiAmounts,
+ {} as Balance.Amounts,
)
},
- diff: (amounts1: YoroiAmounts, amounts2: YoroiAmounts): YoroiAmounts => {
+ diff: (amounts1: Balance.Amounts, amounts2: Balance.Amounts): Balance.Amounts => {
return Amounts.sum([amounts1, Amounts.negated(amounts2)])
},
- includes: (amounts: YoroiAmounts, tokenId: string): boolean => {
+ includes: (amounts: Balance.Amounts, tokenId: string): boolean => {
return Object.keys(amounts).includes(tokenId)
},
- negated: (amounts: YoroiAmounts): YoroiAmounts => {
+ negated: (amounts: Balance.Amounts): Balance.Amounts => {
const entries = Object.entries(amounts)
const negatedEntries = entries.map(([tokenId, amount]) => [tokenId, Quantities.negated(amount)])
return Object.fromEntries(negatedEntries)
},
- remove: (amounts: YoroiAmounts, removeTokenIds: Array): YoroiAmounts => {
+ remove: (amounts: Balance.Amounts, removeTokenIds: Array): Balance.Amounts => {
const filteredEntries = Object.entries(amounts).filter(([tokenId]) => !removeTokenIds.includes(tokenId))
return Object.fromEntries(filteredEntries)
},
- getAmount: (amounts: YoroiAmounts, tokenId: string): YoroiAmount => {
+ getAmount: (amounts: Balance.Amounts, tokenId: string): Balance.Amount => {
return {
tokenId,
quantity: amounts[tokenId] || Quantities.zero,
}
},
- save: (amounts: YoroiAmounts, amount: YoroiAmount): YoroiAmounts => {
+ save: (amounts: Balance.Amounts, amount: Balance.Amount): Balance.Amounts => {
const {tokenId, quantity} = amount
return {
@@ -73,55 +74,57 @@ export const Amounts = {
[tokenId]: quantity,
}
},
- map: (amounts: YoroiAmounts, fn: (amount: YoroiAmount) => YoroiAmount): YoroiAmounts =>
+ map: (amounts: Balance.Amounts, fn: (amount: Balance.Amount) => Balance.Amount): Balance.Amounts =>
Amounts.fromArray(Amounts.toArray(amounts).map(fn)),
- toArray: (amounts: YoroiAmounts) =>
+ toArray: (amounts: Balance.Amounts) =>
Object.keys(amounts).reduce(
(result, current) => [...result, Amounts.getAmount(amounts, current)],
- [] as Array,
+ [] as Array,
),
- fromArray: (amounts: Array) =>
+ fromArray: (amounts: Array) =>
Object.fromEntries(amounts.map((amount) => [amount.tokenId, amount.quantity])),
}
export const Quantities = {
- sum: (quantities: Array) => {
- return quantities.reduce((result, current) => result.plus(current), new BigNumber(0)).toString(10) as Quantity
+ sum: (quantities: Array) => {
+ return quantities
+ .reduce((result, current) => result.plus(current), new BigNumber(0))
+ .toString(10) as Balance.Quantity
},
- max: (...quantities: Array) => {
- return BigNumber.max(...quantities).toString(10) as Quantity
+ max: (...quantities: Array) => {
+ return BigNumber.max(...quantities).toString(10) as Balance.Quantity
},
- diff: (quantity1: Quantity, quantity2: Quantity) => {
- return new BigNumber(quantity1).minus(new BigNumber(quantity2)).toString(10) as Quantity
+ diff: (quantity1: Balance.Quantity, quantity2: Balance.Quantity) => {
+ return new BigNumber(quantity1).minus(new BigNumber(quantity2)).toString(10) as Balance.Quantity
},
- negated: (quantity: Quantity) => {
- return new BigNumber(quantity).negated().toString(10) as Quantity
+ negated: (quantity: Balance.Quantity) => {
+ return new BigNumber(quantity).negated().toString(10) as Balance.Quantity
},
- product: (quantities: Array) => {
+ product: (quantities: Array) => {
return quantities.reduce((result, quantity) => {
const x = new BigNumber(result).times(new BigNumber(quantity))
- return x.toString(10) as Quantity
- }, '1' as Quantity)
+ return x.toString(10) as Balance.Quantity
+ }, '1' as Balance.Quantity)
},
- quotient: (quantity1: Quantity, quantity2: Quantity) => {
- return new BigNumber(quantity1).dividedBy(new BigNumber(quantity2)).toString(10) as Quantity
+ quotient: (quantity1: Balance.Quantity, quantity2: Balance.Quantity) => {
+ return new BigNumber(quantity1).dividedBy(new BigNumber(quantity2)).toString(10) as Balance.Quantity
},
- isGreaterThan: (quantity1: Quantity, quantity2: Quantity) => {
+ isGreaterThan: (quantity1: Balance.Quantity, quantity2: Balance.Quantity) => {
return new BigNumber(quantity1).isGreaterThan(new BigNumber(quantity2))
},
- decimalPlaces: (quantity: Quantity, precision: number) => {
- return new BigNumber(quantity).decimalPlaces(precision).toString(10) as Quantity
+ decimalPlaces: (quantity: Balance.Quantity, precision: number) => {
+ return new BigNumber(quantity).decimalPlaces(precision).toString(10) as Balance.Quantity
},
- denominated: (quantity: Quantity, denomination: number) => {
- return Quantities.quotient(quantity, new BigNumber(10).pow(denomination).toString(10) as Quantity)
+ denominated: (quantity: Balance.Quantity, denomination: number) => {
+ return Quantities.quotient(quantity, new BigNumber(10).pow(denomination).toString(10) as Balance.Quantity)
},
- integer: (quantity: Quantity, denomination: number) => {
- return new BigNumber(quantity).decimalPlaces(denomination).shiftedBy(denomination).toString(10) as Quantity
+ integer: (quantity: Balance.Quantity, denomination: number) => {
+ return new BigNumber(quantity).decimalPlaces(denomination).shiftedBy(denomination).toString(10) as Balance.Quantity
},
- zero: '0' as Quantity,
- isZero: (quantity: Quantity) => new BigNumber(quantity).isZero(),
- isAtomic: (quantity: Quantity, denomination: number) => {
+ zero: '0' as Balance.Quantity,
+ isZero: (quantity: Balance.Quantity) => new BigNumber(quantity).isZero(),
+ isAtomic: (quantity: Balance.Quantity, denomination: number) => {
const absoluteQuantity = new BigNumber(quantity).decimalPlaces(denomination).abs()
const minimalFractionalPart = new BigNumber(10).pow(new BigNumber(denomination).negated())
@@ -134,7 +137,7 @@ export const asQuantity = (value: BigNumber | number | string) => {
if (bn.isNaN() || !bn.isFinite()) {
throw new Error('Invalid quantity')
}
- return bn.toString(10) as Quantity
+ return bn.toString(10) as Balance.Quantity
}
export const Utxos = {
@@ -143,7 +146,7 @@ export const Utxos = {
(previousAmounts, currentUtxo) => {
const amounts = {
...previousAmounts,
- [primaryTokenId]: Quantities.sum([previousAmounts[primaryTokenId], currentUtxo.amount as Quantity]),
+ [primaryTokenId]: Quantities.sum([previousAmounts[primaryTokenId], currentUtxo.amount as Balance.Quantity]),
}
if (currentUtxo.assets) {
@@ -152,7 +155,7 @@ export const Utxos = {
...previousAmountsWithAssets,
[currentAsset.assetId]: Quantities.sum([
Amounts.getAmount(previousAmountsWithAssets, currentAsset.assetId).quantity,
- currentAsset.amount as Quantity,
+ currentAsset.amount as Balance.Quantity,
]),
}
}, amounts)
@@ -160,7 +163,7 @@ export const Utxos = {
return amounts
},
- {[primaryTokenId]: Quantities.zero} as YoroiAmounts,
+ {[primaryTokenId]: Quantities.zero} as Balance.Amounts,
)
},
}
diff --git a/apps/wallet-mobile/translations/messages/src/NftDetails/NftDetails.json b/apps/wallet-mobile/translations/messages/src/NftDetails/NftDetails.json
index 429b2938ec..a041075fc9 100644
--- a/apps/wallet-mobile/translations/messages/src/NftDetails/NftDetails.json
+++ b/apps/wallet-mobile/translations/messages/src/NftDetails/NftDetails.json
@@ -6,12 +6,12 @@
"start": {
"line": 280,
"column": 9,
- "index": 7824
+ "index": 7572
},
"end": {
"line": 283,
"column": 3,
- "index": 7895
+ "index": 7643
}
},
{
@@ -21,12 +21,12 @@
"start": {
"line": 284,
"column": 12,
- "index": 7909
+ "index": 7657
},
"end": {
"line": 287,
"column": 3,
- "index": 7980
+ "index": 7728
}
},
{
@@ -36,12 +36,12 @@
"start": {
"line": 288,
"column": 12,
- "index": 7994
+ "index": 7742
},
"end": {
"line": 291,
"column": 3,
- "index": 8065
+ "index": 7813
}
},
{
@@ -51,12 +51,12 @@
"start": {
"line": 292,
"column": 11,
- "index": 8078
+ "index": 7826
},
"end": {
"line": 295,
"column": 3,
- "index": 8148
+ "index": 7896
}
},
{
@@ -66,12 +66,12 @@
"start": {
"line": 296,
"column": 13,
- "index": 8163
+ "index": 7911
},
"end": {
"line": 299,
"column": 3,
- "index": 8234
+ "index": 7982
}
},
{
@@ -81,12 +81,12 @@
"start": {
"line": 300,
"column": 15,
- "index": 8251
+ "index": 7999
},
"end": {
"line": 303,
"column": 3,
- "index": 8328
+ "index": 8076
}
},
{
@@ -96,12 +96,12 @@
"start": {
"line": 304,
"column": 10,
- "index": 8340
+ "index": 8088
},
"end": {
"line": 307,
"column": 3,
- "index": 8407
+ "index": 8155
}
},
{
@@ -111,12 +111,12 @@
"start": {
"line": 308,
"column": 15,
- "index": 8424
+ "index": 8172
},
"end": {
"line": 311,
"column": 3,
- "index": 8501
+ "index": 8249
}
},
{
@@ -126,12 +126,12 @@
"start": {
"line": 312,
"column": 12,
- "index": 8515
+ "index": 8263
},
"end": {
"line": 315,
"column": 3,
- "index": 8587
+ "index": 8335
}
},
{
@@ -141,12 +141,12 @@
"start": {
"line": 316,
"column": 16,
- "index": 8605
+ "index": 8353
},
"end": {
"line": 319,
"column": 3,
- "index": 8682
+ "index": 8430
}
},
{
@@ -156,12 +156,12 @@
"start": {
"line": 320,
"column": 16,
- "index": 8700
+ "index": 8448
},
"end": {
"line": 323,
"column": 3,
- "index": 8780
+ "index": 8528
}
}
-]
\ No newline at end of file
+]
diff --git a/apps/wallet-mobile/translations/messages/src/Nfts/Nfts.json b/apps/wallet-mobile/translations/messages/src/Nfts/Nfts.json
index 11eed4d4dd..94db214a27 100644
--- a/apps/wallet-mobile/translations/messages/src/Nfts/Nfts.json
+++ b/apps/wallet-mobile/translations/messages/src/Nfts/Nfts.json
@@ -6,12 +6,12 @@
"start": {
"line": 246,
"column": 12,
- "index": 6029
+ "index": 6033
},
"end": {
"line": 249,
"column": 3,
- "index": 6102
+ "index": 6106
}
},
{
@@ -21,12 +21,12 @@
"start": {
"line": 250,
"column": 14,
- "index": 6118
+ "index": 6122
},
"end": {
"line": 253,
"column": 3,
- "index": 6189
+ "index": 6193
}
},
{
@@ -36,12 +36,12 @@
"start": {
"line": 254,
"column": 20,
- "index": 6211
+ "index": 6215
},
"end": {
"line": 257,
"column": 3,
- "index": 6304
+ "index": 6308
}
},
{
@@ -51,12 +51,12 @@
"start": {
"line": 258,
"column": 13,
- "index": 6319
+ "index": 6323
},
"end": {
"line": 261,
"column": 3,
- "index": 6407
+ "index": 6411
}
},
{
@@ -66,12 +66,12 @@
"start": {
"line": 262,
"column": 15,
- "index": 6424
+ "index": 6428
},
"end": {
"line": 265,
"column": 3,
- "index": 6504
+ "index": 6508
}
},
{
@@ -81,12 +81,12 @@
"start": {
"line": 266,
"column": 18,
- "index": 6524
+ "index": 6528
},
"end": {
"line": 269,
"column": 3,
- "index": 6626
+ "index": 6630
}
},
{
@@ -96,12 +96,12 @@
"start": {
"line": 270,
"column": 9,
- "index": 6637
+ "index": 6641
},
"end": {
"line": 273,
"column": 3,
- "index": 6712
+ "index": 6716
}
},
{
@@ -111,12 +111,12 @@
"start": {
"line": 274,
"column": 10,
- "index": 6724
+ "index": 6728
},
"end": {
"line": 277,
"column": 3,
- "index": 6799
+ "index": 6803
}
}
]
\ No newline at end of file
diff --git a/apps/wallet-mobile/translations/messages/src/Staking/DelegationConfirmation/DelegationConfirmation.json b/apps/wallet-mobile/translations/messages/src/Staking/DelegationConfirmation/DelegationConfirmation.json
index d65a3e5781..8b192d2d07 100644
--- a/apps/wallet-mobile/translations/messages/src/Staking/DelegationConfirmation/DelegationConfirmation.json
+++ b/apps/wallet-mobile/translations/messages/src/Staking/DelegationConfirmation/DelegationConfirmation.json
@@ -6,12 +6,12 @@
"start": {
"line": 147,
"column": 23,
- "index": 5310
+ "index": 5296
},
"end": {
"line": 150,
"column": 3,
- "index": 5424
+ "index": 5410
}
},
{
@@ -21,12 +21,12 @@
"start": {
"line": 151,
"column": 10,
- "index": 5436
+ "index": 5422
},
"end": {
"line": 154,
"column": 3,
- "index": 5536
+ "index": 5522
}
},
{
@@ -36,12 +36,12 @@
"start": {
"line": 155,
"column": 22,
- "index": 5560
+ "index": 5546
},
"end": {
"line": 158,
"column": 3,
- "index": 5730
+ "index": 5716
}
},
{
@@ -51,12 +51,12 @@
"start": {
"line": 159,
"column": 15,
- "index": 5747
+ "index": 5733
},
"end": {
"line": 162,
"column": 3,
- "index": 5866
+ "index": 5852
}
}
-]
\ No newline at end of file
+]
diff --git a/apps/wallet-mobile/translations/messages/src/TxHistory/AssetList/AssetList.json b/apps/wallet-mobile/translations/messages/src/TxHistory/AssetList/AssetList.json
index 02f53e561a..2fdccb0e36 100644
--- a/apps/wallet-mobile/translations/messages/src/TxHistory/AssetList/AssetList.json
+++ b/apps/wallet-mobile/translations/messages/src/TxHistory/AssetList/AssetList.json
@@ -6,12 +6,12 @@
"start": {
"line": 97,
"column": 16,
- "index": 3313
+ "index": 3171
},
"end": {
"line": 100,
"column": 3,
- "index": 3418
+ "index": 3276
}
}
]
\ No newline at end of file
diff --git a/apps/wallet-mobile/translations/messages/src/TxHistory/PairedBalance.json b/apps/wallet-mobile/translations/messages/src/TxHistory/PairedBalance.json
index 9d1c86b4e0..06e03c6bcd 100644
--- a/apps/wallet-mobile/translations/messages/src/TxHistory/PairedBalance.json
+++ b/apps/wallet-mobile/translations/messages/src/TxHistory/PairedBalance.json
@@ -6,12 +6,12 @@
"start": {
"line": 85,
"column": 22,
- "index": 2563
+ "index": 2498
},
"end": {
"line": 88,
"column": 3,
- "index": 2695
+ "index": 2630
}
}
]
\ No newline at end of file
diff --git a/apps/wallet-mobile/translations/messages/src/legacy/format.json b/apps/wallet-mobile/translations/messages/src/legacy/format.json
index c6643c9c76..64572d6a45 100644
--- a/apps/wallet-mobile/translations/messages/src/legacy/format.json
+++ b/apps/wallet-mobile/translations/messages/src/legacy/format.json
@@ -4,14 +4,14 @@
"defaultMessage": "!!!Today",
"file": "src/legacy/format.ts",
"start": {
- "line": 206,
+ "line": 207,
"column": 9,
- "index": 6715
+ "index": 6875
},
"end": {
- "line": 209,
+ "line": 210,
"column": 3,
- "index": 6782
+ "index": 6942
}
},
{
@@ -19,14 +19,14 @@
"defaultMessage": "!!!Yesterday",
"file": "src/legacy/format.ts",
"start": {
- "line": 210,
+ "line": 211,
"column": 13,
- "index": 6797
+ "index": 6957
},
"end": {
- "line": 213,
+ "line": 214,
"column": 3,
- "index": 6872
+ "index": 7032
}
},
{
@@ -34,14 +34,14 @@
"defaultMessage": "!!![Unknown asset name]",
"file": "src/legacy/format.ts",
"start": {
- "line": 214,
+ "line": 215,
"column": 20,
- "index": 6894
+ "index": 7054
},
"end": {
- "line": 217,
+ "line": 218,
"column": 3,
- "index": 6987
+ "index": 7147
}
}
-]
\ No newline at end of file
+]
diff --git a/packages/swap-react/.editorconfig b/packages/swap-react/.editorconfig
new file mode 100644
index 0000000000..65365be68e
--- /dev/null
+++ b/packages/swap-react/.editorconfig
@@ -0,0 +1,15 @@
+# EditorConfig helps developers define and maintain consistent
+# coding styles between different editors and IDEs
+# editorconfig.org
+
+root = true
+
+[*]
+
+indent_style = space
+indent_size = 2
+
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
diff --git a/packages/swap-react/.gitignore b/packages/swap-react/.gitignore
new file mode 100644
index 0000000000..75356714f9
--- /dev/null
+++ b/packages/swap-react/.gitignore
@@ -0,0 +1,70 @@
+# OSX
+#
+.DS_Store
+
+# XDE
+.expo/
+
+# VSCode
+.vscode/
+jsconfig.json
+
+# Xcode
+#
+build/
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+xcuserdata
+*.xccheckout
+*.moved-aside
+DerivedData
+*.hmap
+*.ipa
+*.xcuserstate
+project.xcworkspace
+
+# Android/IJ
+#
+.classpath
+.cxx
+.gradle
+.idea
+.project
+.settings
+local.properties
+android.iml
+
+# Cocoapods
+#
+example/ios/Pods
+
+# Ruby
+example/vendor/
+
+# node.js
+#
+node_modules/
+npm-debug.log
+yarn-debug.log
+yarn-error.log
+
+# BUCK
+buck-out/
+\.buckd/
+android/app/libs
+android/keystores/debug.keystore
+
+# Expo
+.expo/
+
+# Turborepo
+.turbo/
+
+# generated by bob
+lib/
diff --git a/packages/swap-react/.watchmanconfig b/packages/swap-react/.watchmanconfig
new file mode 100644
index 0000000000..0967ef424b
--- /dev/null
+++ b/packages/swap-react/.watchmanconfig
@@ -0,0 +1 @@
+{}
diff --git a/packages/swap-react/babel.config.js b/packages/swap-react/babel.config.js
new file mode 100644
index 0000000000..f842b77fcf
--- /dev/null
+++ b/packages/swap-react/babel.config.js
@@ -0,0 +1,3 @@
+module.exports = {
+ presets: ['module:metro-react-native-babel-preset'],
+};
diff --git a/packages/swap-react/jest.setup.js b/packages/swap-react/jest.setup.js
new file mode 100644
index 0000000000..eb2d41652a
--- /dev/null
+++ b/packages/swap-react/jest.setup.js
@@ -0,0 +1,3 @@
+jest.mock('@react-native-async-storage/async-storage', () =>
+ require('@react-native-async-storage/async-storage/jest/async-storage-mock'),
+)
diff --git a/packages/swap-react/package.json b/packages/swap-react/package.json
new file mode 100644
index 0000000000..8996ca35f6
--- /dev/null
+++ b/packages/swap-react/package.json
@@ -0,0 +1,184 @@
+{
+ "name": "@yoroi/swap-react",
+ "version": "1.0.0",
+ "description": "The Swap package of Yoroi SDK for react and browser",
+ "main": "lib/commonjs/index",
+ "browser": "lib/module/index",
+ "module": "lib/module/index",
+ "types": "lib/typescript/index.d.ts",
+ "source": "src/index",
+ "files": [
+ "src",
+ "lib",
+ "!ios/build",
+ "!android/build",
+ "!android/gradle",
+ "!android/gradlew",
+ "!android/gradlew.bat",
+ "!android/local.properties",
+ "!**/__tests__",
+ "!**/__fixtures__",
+ "!**/__mocks__",
+ "!**/.*"
+ ],
+ "scripts": {
+ "test": "jest",
+ "tsc": "tsc --noEmit",
+ "flow": ". ./scripts/flowgen.sh",
+ "clean": "del-cli lib",
+ "lint": "eslint \"**/*.{js,ts,tsx}\"",
+ "build": "yarn tsc && yarn lint && yarn test --ci --silent && yarn clean && bob build && yarn flow",
+ "prepack": "yarn build",
+ "release": "release-it"
+ },
+ "keywords": [
+ "yoroi",
+ "cardano",
+ "swap",
+ "browser",
+ "react"
+ ],
+ "repository": {
+ "type": "github",
+ "url": "https://github.com/Emurgo/yoroi.git",
+ "directory": "packages/swap-react"
+ },
+ "author": "EMURGO Fintech (https://github.com/Emurgo/yoroi)",
+ "contributors": [
+ {
+ "name": "Juliano Lazzarotto",
+ "email": "juliano@stackchain.dev"
+ }
+ ],
+ "license": "Apache-2.0",
+ "bugs": {
+ "url": "https://github.com/Emurgo/yoroi/issues"
+ },
+ "homepage": "https://github.com/Emurgo/yoroi/packages/metrics-react#readme",
+ "publishConfig": {
+ "registry": "https://registry.npmjs.org/"
+ },
+ "dependencies": {
+ "@yoroi/types": "1.0.4",
+ "immer": "^10.0.2"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0 <= 19.0.0",
+ "react-query": "^3.39.3"
+ },
+ "optionalDependencies": {
+ "@react-native-async-storage/async-storage": "^1.18.1"
+ },
+ "devDependencies": {
+ "@commitlint/config-conventional": "^17.0.2",
+ "@react-native-async-storage/async-storage": "^1.18.1",
+ "@react-native-community/eslint-config": "^3.0.2",
+ "@release-it/conventional-changelog": "^5.0.0",
+ "@testing-library/react-hooks": "^8.0.1",
+ "@types/jest": "^28.1.2",
+ "@types/react": "18.2.0",
+ "@types/react-native": "0.71.6",
+ "commitlint": "^17.0.2",
+ "del-cli": "^5.0.0",
+ "eslint": "^8.4.1",
+ "eslint-config-prettier": "^8.5.0",
+ "eslint-plugin-prettier": "^4.0.0",
+ "flowgen": "^1.21.0",
+ "jest": "^28.1.1",
+ "pod-install": "^0.1.0",
+ "prettier": "^2.0.5",
+ "react": "18.2.0",
+ "react-native": "0.71.6",
+ "react-native-builder-bob": "^0.20.4",
+ "react-query": "^3.39.3",
+ "react-test-renderer": "^18.2.0",
+ "release-it": "^15.0.0",
+ "typescript": "^4.5.2"
+ },
+ "engines": {
+ "node": ">= 16.19.0"
+ },
+ "packageManager": "^yarn@1.22.15",
+ "jest": {
+ "preset": "react-native",
+ "setupFiles": [
+ "/jest.setup.js"
+ ],
+ "modulePathIgnorePatterns": [
+ "/example/node_modules",
+ "/lib/"
+ ]
+ },
+ "commitlint": {
+ "extends": [
+ "@commitlint/config-conventional"
+ ]
+ },
+ "release-it": {
+ "git": {
+ "commitMessage": "chore: release ${version}",
+ "tagName": "v${version}"
+ },
+ "npm": {
+ "publish": true
+ },
+ "github": {
+ "release": false
+ },
+ "plugins": {
+ "@release-it/conventional-changelog": {
+ "preset": "angular"
+ }
+ }
+ },
+ "eslintConfig": {
+ "root": true,
+ "extends": [
+ "@react-native-community",
+ "prettier"
+ ],
+ "rules": {
+ "prettier/prettier": [
+ "error",
+ {
+ "quoteProps": "consistent",
+ "bracketSpacing": false,
+ "singleQuote": true,
+ "tabWidth": 2,
+ "trailingComma": "all",
+ "useTabs": false,
+ "semi": false
+ }
+ ]
+ }
+ },
+ "eslintIgnore": [
+ "node_modules/",
+ "lib/",
+ "babel.config.js",
+ "jest.setup.js"
+ ],
+ "prettier": {
+ "quoteProps": "consistent",
+ "bracketSpacing": false,
+ "singleQuote": true,
+ "tabWidth": 2,
+ "trailingComma": "all",
+ "useTabs": false,
+ "semi": false
+ },
+ "react-native-builder-bob": {
+ "source": "src",
+ "output": "lib",
+ "targets": [
+ "commonjs",
+ "module",
+ [
+ "typescript",
+ {
+ "project": "tsconfig.build.json"
+ }
+ ]
+ ]
+ }
+}
diff --git a/packages/swap-react/scripts/flowgen.sh b/packages/swap-react/scripts/flowgen.sh
new file mode 100644
index 0000000000..2638ef8d27
--- /dev/null
+++ b/packages/swap-react/scripts/flowgen.sh
@@ -0,0 +1,3 @@
+for i in $(find lib -type f -name "*.d.ts");
+ do sh -c "npx flowgen $i -o ${i%.*.*}.js.flow";
+done;
diff --git a/packages/swap-react/src/adapters/mocks.ts b/packages/swap-react/src/adapters/mocks.ts
new file mode 100644
index 0000000000..4065da6325
--- /dev/null
+++ b/packages/swap-react/src/adapters/mocks.ts
@@ -0,0 +1,81 @@
+import {Swap} from '@yoroi/types'
+
+export function makeMockSwapStorage(): Readonly {
+ const slippage: Swap.Storage['slippage'] = {
+ read: () => {
+ console.debug('[swap-react] makeMockSwapStorage slippage read')
+ return Promise.resolve(0.1)
+ },
+ remove: () => {
+ console.debug('[swap-react] makeMockSwapStorage slippage remove')
+ return Promise.resolve()
+ },
+ save: (newSlippage) => {
+ console.debug(
+ '[swap-react] makeMockSwapStorage slippage save',
+ newSlippage,
+ )
+ return Promise.resolve()
+ },
+ }
+
+ const reset: Swap.Storage['reset'] = () => {
+ console.debug('[swap-react] makeMockSwapStorage reset')
+ return Promise.all([slippage.remove])
+ }
+
+ return {
+ slippage,
+ reset,
+ } as const
+}
+
+export function makeMockSwapApiClient(options: Swap.FactoryOptions) {
+ console.debug('[swap-react] makeMockSwapApiClient', options)
+ return {
+ getOpenOrders: (stakeKeyHash: string) => {
+ console.debug(
+ '[swap-react] makeMockSwapApiClient getOpenOrders',
+ stakeKeyHash,
+ )
+ return Promise.resolve([])
+ },
+ getCancelOrderTx: (
+ orderUTxO: string,
+ collateralUTxOs: string,
+ walletAddress: string,
+ ) => {
+ console.debug(
+ '[swap-react] makeMockSwapApiClient getCancelOrderTx',
+ orderUTxO,
+ collateralUTxOs,
+ walletAddress,
+ )
+ return Promise.resolve({})
+ },
+ getOrderDatum: (order: any) => {
+ console.debug('[swap-react] makeMockSwapApiClient getOrderDatum', order)
+ return Promise.resolve({})
+ },
+ getSupportedTokens: (baseToken: string) => {
+ console.debug(
+ '[swap-react] makeMockSwapApiClient getSupportedTokens',
+ baseToken,
+ )
+ return Promise.resolve([])
+ },
+ getTokenPairPools: (sendToken: string, receiveToken: string) => {
+ console.debug(
+ '[swap-react] makeMockSwapApiClient getTokenPairPools',
+ sendToken,
+ receiveToken,
+ )
+ return Promise.resolve([])
+ },
+ } as const
+}
+
+export const swapApiBaseUrls = {
+ mainnet: 'https://onchain2.muesliswap.com/',
+ testnet: 'https://onchain2.muesliswap.com/',
+} as const
diff --git a/packages/swap-react/src/adapters/openswap.ts b/packages/swap-react/src/adapters/openswap.ts
new file mode 100644
index 0000000000..8b4d330935
--- /dev/null
+++ b/packages/swap-react/src/adapters/openswap.ts
@@ -0,0 +1,78 @@
+import {Swap} from '@yoroi/types'
+import {makeMockSwapApiClient, makeMockSwapStorage} from './mocks'
+
+const initialDeps = {
+ apiClient: makeMockSwapApiClient({
+ apiUrl: 'https://127.0.0.1:8080/api',
+ stakingKey: 'test',
+ }),
+ storage: makeMockSwapStorage(),
+} as const
+
+export function makeSwapModule(
+ {apiUrl, stakingKey}: Readonly,
+ {apiClient, storage} = initialDeps,
+) {
+ console.log(apiUrl, stakingKey, apiClient, storage)
+
+ const dexModule = () => {} // init something
+
+ const getOpenOrders = () => {
+ dexModule()
+ return [
+ {
+ id: '1',
+ type: 'limit',
+ },
+ ]
+ }
+
+ const createOrder = (
+ orderType: Swap.OrderType,
+ order: Swap.CreateOrderData,
+ ) => {
+ dexModule()
+ return {
+ orderType,
+ order,
+ }
+ }
+
+ const cancelOrder = (orderType: Swap.OrderType, order: any) => {
+ dexModule()
+ return {
+ orderType,
+ order,
+ }
+ }
+
+ const getSupportedTokens = () => {
+ dexModule()
+ return {}
+ }
+
+ const getTokenPairPools = () => {
+ dexModule()
+ return {}
+ }
+
+ return {
+ orders: {
+ create: createOrder,
+ cancel: cancelOrder,
+ list: {
+ open: getOpenOrders,
+ },
+ },
+ pairs: {
+ list: {
+ supportedByToken: getSupportedTokens,
+ },
+ },
+ pools: {
+ list: {
+ byPair: getTokenPairPools,
+ },
+ },
+ } as const
+}
diff --git a/packages/swap-react/src/adapters/storage.test.ts b/packages/swap-react/src/adapters/storage.test.ts
new file mode 100644
index 0000000000..94f478990a
--- /dev/null
+++ b/packages/swap-react/src/adapters/storage.test.ts
@@ -0,0 +1,54 @@
+import AsyncStorage from '@react-native-async-storage/async-storage'
+import {Swap} from '@yoroi/types'
+
+import {makeSwapStorage, swapStorageSlippageKey} from './storage'
+
+jest.mock('@react-native-async-storage/async-storage')
+
+const mockedAsyncStorage = AsyncStorage as jest.Mocked
+
+describe('makeSwapStorage', () => {
+ let swapStorage: Swap.Storage
+
+ beforeEach(() => {
+ swapStorage = makeSwapStorage()
+ mockedAsyncStorage.setItem.mockClear()
+ mockedAsyncStorage.getItem.mockClear()
+ mockedAsyncStorage.removeItem.mockClear()
+ })
+
+ it('should save slippage', async () => {
+ const slippage = 0.1
+ await swapStorage.slippage.save(slippage)
+ expect(mockedAsyncStorage.setItem).toHaveBeenCalledWith(
+ swapStorageSlippageKey,
+ JSON.stringify(slippage),
+ )
+ })
+
+ it('should read slippage', async () => {
+ const slippage = 0.1
+ mockedAsyncStorage.getItem.mockResolvedValue(JSON.stringify(slippage))
+ const result = await swapStorage.slippage.read()
+ expect(result).toEqual(slippage)
+ expect(mockedAsyncStorage.getItem).toHaveBeenCalledWith(
+ swapStorageSlippageKey,
+ )
+ })
+
+ it('should handle non-numeric values when reading slippage', async () => {
+ mockedAsyncStorage.getItem.mockResolvedValue(JSON.stringify('not a number'))
+ const result = await swapStorage.slippage.read()
+ expect(result).toEqual(0)
+ expect(mockedAsyncStorage.getItem).toHaveBeenCalledWith(
+ swapStorageSlippageKey,
+ )
+ })
+
+ it('should remove slippage', async () => {
+ await swapStorage.slippage.remove()
+ expect(mockedAsyncStorage.removeItem).toHaveBeenCalledWith(
+ swapStorageSlippageKey,
+ )
+ })
+})
diff --git a/packages/swap-react/src/adapters/storage.ts b/packages/swap-react/src/adapters/storage.ts
new file mode 100644
index 0000000000..e6d8904ff3
--- /dev/null
+++ b/packages/swap-react/src/adapters/storage.ts
@@ -0,0 +1,38 @@
+import AsyncStorage from '@react-native-async-storage/async-storage'
+import type {Swap} from '@yoroi/types'
+
+const initialDeps = {storage: AsyncStorage} as const
+
+export function makeSwapStorage(deps = initialDeps): Readonly {
+ return {
+ slippage: {
+ save: (slippage) =>
+ deps.storage.setItem(swapStorageSlippageKey, JSON.stringify(slippage)),
+ read: () =>
+ deps.storage
+ .getItem(swapStorageSlippageKey)
+ .then((value) => parseNumber(value) ?? 0),
+ remove: () => deps.storage.removeItem(swapStorageSlippageKey),
+ },
+ } as const as Swap.Storage
+}
+
+export const swapStorageSlippageKey = 'swap-slippage'
+
+// * === UTILS ===
+// * NOTE copied from utils it should be imported from utils package later
+const parseNumber = (data: unknown) => {
+ const parsed = parseSafe(data)
+ return isNumber(parsed) ? parsed : undefined
+}
+
+const parseSafe = (text: any) => {
+ try {
+ return JSON.parse(text) as unknown
+ } catch (_) {
+ return undefined
+ }
+}
+
+const isNumber = (data: unknown): data is number =>
+ typeof data === 'number' && !Number.isNaN(data) && Number.isFinite(data)
diff --git a/packages/swap-react/src/index.ts b/packages/swap-react/src/index.ts
new file mode 100644
index 0000000000..03912a7fd3
--- /dev/null
+++ b/packages/swap-react/src/index.ts
@@ -0,0 +1,3 @@
+export {makeSwapStorage} from './adapters/storage'
+export {makeMockSwapStorage} from './adapters/mocks'
+export {SwapProvider, useSwap} from './translators/reactjs'
diff --git a/packages/swap-react/src/translators/reactjs.test.tsx b/packages/swap-react/src/translators/reactjs.test.tsx
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/swap-react/src/translators/reactjs.tsx b/packages/swap-react/src/translators/reactjs.tsx
new file mode 100644
index 0000000000..e19ea93166
--- /dev/null
+++ b/packages/swap-react/src/translators/reactjs.tsx
@@ -0,0 +1,323 @@
+import * as React from 'react'
+import {
+ QueryKey,
+ UseMutationOptions,
+ useQuery,
+ useQueryClient,
+ useMutation,
+} from 'react-query'
+import {Balance, Swap} from '@yoroi/types'
+import {makeMockSwapStorage} from '../adapters/mocks'
+import {swapStorageSlippageKey} from '../adapters/storage'
+import {produce} from 'immer'
+
+type SwapState = Readonly<{
+ createOrder: {
+ type: Swap.OrderType
+ } & Omit
+
+ yoroiUnsignedTx: any | undefined
+}>
+
+type SwapCreateOrderActions = {
+ updateOrderType: (orderType: Swap.OrderType) => void
+ updateFromAmount: (fromAmount: Balance.Amount) => void
+ updateToAmount: (toAmount: Balance.Amount) => void
+ updateProtocol: (protocol: Swap.Protocol) => void
+ updatePoolId: (poolId: string) => void
+ updateSlippage: (slippage: number) => void
+}
+
+export enum SwapOrderActionType {
+ ChangeOrderType = 'changeOrderType',
+ ChangeAmountFrom = 'changeAmountFrom',
+ ChangeAmountTo = 'changeAmountTo',
+ ChangeProtocol = 'changeProtocol',
+ ChangePoolId = 'changePoolId',
+ ChangeSlippage = 'changeSlippage',
+}
+
+type SwapOrderAction =
+ | {type: SwapOrderActionType.ChangeOrderType; orderType: Swap.OrderType}
+ | {type: SwapOrderActionType.ChangeAmountFrom; fromAmount: Balance.Amount}
+ | {type: SwapOrderActionType.ChangeAmountTo; toAmount: Balance.Amount}
+ | {type: SwapOrderActionType.ChangeProtocol; protocol: Swap.Protocol}
+ | {type: SwapOrderActionType.ChangePoolId; poolId: string}
+ | {type: SwapOrderActionType.ChangeSlippage; slippage: number}
+
+type SwapActions = {
+ updateSwapUnsignedTx: (swapUnsignedTx: any | undefined) => void
+ reset: () => void
+}
+
+enum SwapActionType {
+ UpdateSwapUnsignedTx = 'updateSwapUnsignedTx',
+ Reset = 'reset',
+}
+
+type SwapAction =
+ | {
+ type: SwapActionType.UpdateSwapUnsignedTx
+ unsignedTx: any | undefined
+ }
+ | {type: SwapActionType.Reset}
+
+const combinedReducers = (
+ state: SwapState,
+ action: SwapOrderAction | SwapAction,
+) => {
+ return {
+ ...swapReducer(
+ {
+ ...state,
+ ...createOrderReducer(state, action as SwapOrderAction),
+ },
+ action as SwapAction,
+ ),
+ } as const
+}
+
+const defaultState: SwapState = {
+ createOrder: {
+ type: 'limit',
+ amounts: {
+ sell: {
+ quantity: '0',
+ tokenId: '',
+ },
+ buy: {
+ quantity: '0',
+ tokenId: '',
+ },
+ },
+ slippage: 0.1,
+ protocol: 'muesliswap',
+ poolId: '',
+ },
+ yoroiUnsignedTx: undefined,
+} as const
+
+const defaultSwapOrderActions: SwapCreateOrderActions = {
+ updateOrderType: (_orderType: Swap.OrderType) =>
+ console.error('[swap-react] missing initialization'),
+ updateFromAmount: (_fromAmount: Balance.Amount) =>
+ console.error('[swap-react] missing initialization'),
+ updateToAmount: (_toAmount: Balance.Amount) =>
+ console.error('[swap-react] missing initialization'),
+ updateProtocol: (_protocol: Swap.Protocol) =>
+ console.error('[swap-react] missing initialization'),
+ updateSlippage: (_slippage: number) =>
+ console.error('[swap-react] missing initialization'),
+ updatePoolId: (_poolId: string) =>
+ console.error('[swap-react] missing initialization'),
+} as const
+
+const defaultSwapActions: SwapActions = {
+ updateSwapUnsignedTx: (_swapUnsignedTx: any | undefined) =>
+ console.error('[swap-react] missing initialization'),
+ reset: () => console.error('[swap-react] missing initialization'),
+} as const
+
+const defaultActions = {
+ ...defaultSwapOrderActions,
+ ...defaultSwapActions,
+} as const
+
+const defaultStorage: Swap.Storage = makeMockSwapStorage()
+
+const initialSwapProvider: SwapProvider = {
+ ...defaultState,
+ ...defaultActions,
+ ...defaultStorage,
+}
+
+type SwapProvider = React.PropsWithChildren<
+ SwapState & SwapCreateOrderActions & SwapActions & Swap.Storage
+>
+
+const SwapContext = React.createContext(initialSwapProvider)
+
+export const SwapProvider = ({
+ children,
+ storage,
+ initialState,
+}: {
+ children: React.ReactNode
+ storage: Readonly
+ initialState?: Readonly>
+}) => {
+ const [state, dispatch] = React.useReducer(combinedReducers, {
+ ...defaultState,
+ ...initialState,
+ })
+ const actions = React.useRef({
+ updateOrderType: (orderType: Swap.OrderType) => {
+ dispatch({type: SwapOrderActionType.ChangeOrderType, orderType})
+ },
+ updateFromAmount: (fromAmount: Balance.Amount) => {
+ dispatch({type: SwapOrderActionType.ChangeAmountFrom, fromAmount})
+ },
+ updateToAmount: (toAmount: Balance.Amount) => {
+ dispatch({type: SwapOrderActionType.ChangeAmountTo, toAmount})
+ },
+ updateProtocol: (protocol: Swap.Protocol) => {
+ dispatch({type: SwapOrderActionType.ChangeProtocol, protocol})
+ },
+ updatePoolId: (poolId: string) => {
+ dispatch({type: SwapOrderActionType.ChangePoolId, poolId})
+ },
+ updateSlippage: (slippage: number) => {
+ dispatch({type: SwapOrderActionType.ChangeSlippage, slippage})
+ },
+ updateSwapUnsignedTx: (unsignedTx: any | undefined) => {
+ dispatch({
+ type: SwapActionType.UpdateSwapUnsignedTx,
+ unsignedTx: unsignedTx,
+ })
+ },
+ reset: () => {
+ dispatch({type: SwapActionType.Reset})
+ },
+ }).current
+
+ const context = React.useMemo(
+ () => ({...state, ...actions, ...storage}),
+ [state, actions, storage],
+ )
+
+ return {children}
+}
+
+function createOrderReducer(
+ state: SwapState,
+ action: SwapOrderAction,
+): SwapState['createOrder'] {
+ switch (action.type) {
+ case SwapOrderActionType.ChangeOrderType:
+ return produce(state.createOrder, (draft) => {
+ draft.type = action.orderType
+ })
+ case SwapOrderActionType.ChangeAmountFrom:
+ return produce(state.createOrder, (draft) => {
+ draft.amounts.sell = action.fromAmount
+ })
+ case SwapOrderActionType.ChangeAmountTo:
+ return produce(state.createOrder, (draft) => {
+ draft.amounts.buy = action.toAmount
+ })
+ case SwapOrderActionType.ChangeProtocol:
+ return produce(state.createOrder, (draft) => {
+ draft.protocol = action.protocol
+ })
+ case SwapOrderActionType.ChangePoolId:
+ return produce(state.createOrder, (draft) => {
+ draft.poolId = action.poolId
+ })
+ case SwapOrderActionType.ChangeSlippage:
+ return produce(state.createOrder, (draft) => {
+ draft.slippage = action.slippage
+ })
+ default:
+ return produce(state.createOrder, () => {})
+ }
+}
+const swapReducer = (state: SwapState, action: SwapAction) => {
+ switch (action.type) {
+ case SwapActionType.UpdateSwapUnsignedTx:
+ return produce(
+ state,
+ (draft) => (draft.yoroiUnsignedTx = action.unsignedTx),
+ )
+ case SwapActionType.Reset:
+ return produce(defaultState, () => {})
+ default:
+ return produce(state, () => {})
+ }
+}
+
+export const useSwap = () => {
+ const value = React.useContext(SwapContext)
+ if (!value) {
+ throw new Error('[swap-react] useSwap must be used within a SwapProvider')
+ }
+ return value
+}
+
+// * === SETTINGS ===
+// * NOTE maybe it should be moved as part of wallet settings package
+export const useSwapSlippage = () => {
+ const {slippage} = useSwap()
+ const query = useQuery({
+ suspense: true,
+ queryKey: [swapStorageSlippageKey],
+ queryFn: slippage.read,
+ })
+
+ if (query.data == null)
+ throw new Error('[swap-react] useSwapSlippage invalid state')
+
+ return query.data
+}
+
+export const useSwapSetSlippage = (
+ options?: UseMutationOptions,
+) => {
+ const {slippage} = useSwap()
+ const mutation = useMutationWithInvalidations({
+ ...options,
+ useErrorBoundary: true,
+ mutationFn: slippage.save,
+ invalidateQueries: [[swapStorageSlippageKey]],
+ })
+
+ return mutation.mutate
+}
+
+export const useSwapSettings = () => {
+ const setSlippage = useSwapSetSlippage()
+ const slippage = useSwapSlippage()
+
+ const memoizedSetSlippage = React.useCallback(
+ (newSlippage: number) =>
+ setSlippage(newSlippage, {
+ // onSuccess: metrics.enable,
+ }),
+ [setSlippage],
+ )
+
+ return React.useMemo(
+ () => ({
+ slippage,
+ setSlippage: memoizedSetSlippage,
+ }),
+ [slippage, memoizedSetSlippage],
+ )
+}
+
+// * === HOOKS ===
+// * NOTE copied from wallet-mobile it should be imported from hooks package later
+const useMutationWithInvalidations = <
+ TData = unknown,
+ TError = unknown,
+ TVariables = void,
+ TContext = unknown,
+>({
+ invalidateQueries,
+ ...options
+}: UseMutationOptions & {
+ invalidateQueries?: Array
+} = {}) => {
+ const queryClient = useQueryClient()
+
+ return useMutation({
+ ...options,
+ onMutate: (variables) => {
+ invalidateQueries?.forEach((key) => queryClient.cancelQueries(key))
+ return options?.onMutate?.(variables)
+ },
+ onSuccess: (data, variables, context) => {
+ invalidateQueries?.forEach((key) => queryClient.invalidateQueries(key))
+ return options?.onSuccess?.(data, variables, context)
+ },
+ })
+}
diff --git a/packages/swap-react/tsconfig.build.json b/packages/swap-react/tsconfig.build.json
new file mode 100644
index 0000000000..999d3f3c8d
--- /dev/null
+++ b/packages/swap-react/tsconfig.build.json
@@ -0,0 +1,5 @@
+
+{
+ "extends": "./tsconfig",
+ "exclude": ["example"]
+}
diff --git a/packages/swap-react/tsconfig.json b/packages/swap-react/tsconfig.json
new file mode 100644
index 0000000000..1cf23a1a02
--- /dev/null
+++ b/packages/swap-react/tsconfig.json
@@ -0,0 +1,24 @@
+{
+ "compilerOptions": {
+ "baseUrl": "./src",
+ "allowUnreachableCode": false,
+ "allowUnusedLabels": false,
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "jsx": "react",
+ "lib": ["esnext"],
+ "module": "esnext",
+ "moduleResolution": "node",
+ "noFallthroughCasesInSwitch": true,
+ "noImplicitReturns": true,
+ "noImplicitUseStrict": false,
+ "noStrictGenericChecks": false,
+ "noUncheckedIndexedAccess": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "resolveJsonModule": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "target": "esnext"
+ }
+}
diff --git a/packages/swap/.editorconfig b/packages/swap/.editorconfig
new file mode 100644
index 0000000000..22534a358c
--- /dev/null
+++ b/packages/swap/.editorconfig
@@ -0,0 +1,16 @@
+# EditorConfig helps developers define and maintain consistent
+# coding styles between different editors and IDEs
+# editorconfig.org
+
+root = true
+
+[*]
+
+indent_style = space
+indent_size = 2
+quote_type = single
+
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
diff --git a/packages/swap/README.md b/packages/swap/README.md
index deec96e6e9..57102956ce 100644
--- a/packages/swap/README.md
+++ b/packages/swap/README.md
@@ -1,2 +1,3 @@
# yoroi-dex
+
MuesliSwap DEX Aggregator Integration
diff --git a/packages/swap/api/config.ts b/packages/swap/api/config.ts
new file mode 100644
index 0000000000..b5dfb6e350
--- /dev/null
+++ b/packages/swap/api/config.ts
@@ -0,0 +1,28 @@
+import axios from 'axios';
+
+export const SWAP_API_ENDPOINTS = {
+ mainnet: {
+ getPools: 'https://onchain2.muesliswap.com/pools/pair',
+ getOrders: 'https://onchain2.muesliswap.com/orders/all',
+ getTokens: 'https://api.muesliswap.com/list',
+ constructSwapDatum: 'https://aggregator.muesliswap.com/constructSwapDatum',
+ cancelSwapTransaction:
+ 'https://aggregator.muesliswap.com/cancelSwapTransaction',
+ },
+ preprod: {
+ getPools: 'https://preprod.pools.muesliswap.com/pools/pair',
+ getOrders: 'https://preprod.pools.muesliswap.com/orders/all',
+ getTokens: 'https://preprod.api.muesliswap.com/list',
+ constructSwapDatum:
+ 'https://aggregator.muesliswap.com/constructTestnetSwapDatum',
+ cancelSwapTransaction:
+ 'https://aggregator.muesliswap.com/cancelTestnetSwapTransaction',
+ },
+} as const;
+
+export const axiosClient = axios.create({
+ headers: {
+ Accept: 'application/json',
+ 'Content-Type': 'application/json',
+ },
+});
diff --git a/packages/swap/api/index.ts b/packages/swap/api/index.ts
new file mode 100644
index 0000000000..8c18283fa0
--- /dev/null
+++ b/packages/swap/api/index.ts
@@ -0,0 +1,40 @@
+import { Swap } from '@yoroi/types';
+import { cancelOrder, createOrder, getOrders } from './orders';
+import { getPools } from './pools';
+import { getTokens } from './tokens';
+
+export class SwapApi implements Swap.Api {
+ constructor(public readonly network: Swap.Network) {}
+
+ public async createOrder(
+ order: Swap.CreateOrderData
+ ) {
+ return createOrder(this.network, order);
+ }
+
+ public async cancelOrder(
+ orderUTxO: string,
+ collateralUTxO: string,
+ walletAddress: string
+ ) {
+ return cancelOrder(this.network, orderUTxO, collateralUTxO, walletAddress);
+ }
+
+ public async getOrders(stakeKeyHash: string) {
+ return getOrders(this.network, stakeKeyHash);
+ }
+
+ public async getPools(
+ tokenA: Swap.BaseTokenInfo,
+ tokenB: Swap.BaseTokenInfo
+ ) {
+ return getPools(this.network, tokenA, tokenB);
+ }
+
+ public getTokens(
+ policyId = '',
+ assetName = ''
+ ) {
+ return getTokens(this.network, policyId, assetName);
+ }
+}
diff --git a/packages/swap/api/orders.spec.ts b/packages/swap/api/orders.spec.ts
new file mode 100644
index 0000000000..f5e33e7a60
--- /dev/null
+++ b/packages/swap/api/orders.spec.ts
@@ -0,0 +1,175 @@
+import { describe, expect, it, vi, Mocked } from 'vitest';
+import { createOrder, cancelOrder, getOrders } from './orders';
+import axios from 'axios';
+import { axiosClient } from './config';
+
+vi.mock('./config');
+
+const ADA_TOKEN = {
+ policyId: '',
+ assetName: '',
+};
+
+const GENS_TOKEN = {
+ policyId: 'dda5fdb1002f7389b33e036b6afee82a8189becb6cba852e8b79b4fb',
+ assetName: '0014df1047454e53',
+};
+
+const mockAxios = axiosClient as Mocked;
+
+describe('SwapOrdersApi', () => {
+ describe('getOrders', () => {
+ it('Should return orders list using staking key hash', async () => {
+ mockAxios.get.mockImplementationOnce(() =>
+ Promise.resolve({
+ data: mockedOrders,
+ status: 200,
+ })
+ );
+ const result = await getOrders(
+ 'preprod',
+ '24fd15671a17a39268b7a31e2a6703f5893f254d4568411322baeeb7'
+ );
+ expect(result).to.have.lengthOf(1);
+ });
+ });
+
+ describe('createOrder', () => {
+ it('should create order and return datum, datumHash, and contract address', async () => {
+ mockAxios.get.mockImplementationOnce(() =>
+ Promise.resolve({
+ status: 200,
+ data: mockedCreateOrderResult,
+ })
+ );
+
+ const order = await createOrder('mainnet', createOrderParams);
+
+ expect(order.contractAddress).to.eq(mockedCreateOrderResult.address);
+ expect(order.datum).to.eq(mockedCreateOrderResult.datum);
+ expect(order.datumHash).to.eq(mockedCreateOrderResult.hash);
+ });
+
+ it('should throw error for invalid order', async () => {
+ await expect(async () => {
+ mockAxios.get.mockImplementationOnce(() =>
+ Promise.resolve({
+ status: 200,
+ data: { status: 'failed', reason: 'error_message' },
+ })
+ );
+ await createOrder('preprod', createOrderParams);
+ }).rejects.toThrowError(/^error_message$/);
+ });
+
+ it('should throw generic error for invalid response', async () => {
+ await expect(async () => {
+ mockAxios.get.mockImplementationOnce(() =>
+ Promise.resolve({ status: 400 })
+ );
+ await createOrder('mainnet', createOrderParams);
+ }).rejects.toThrow('Failed to construct swap datum');
+ });
+ });
+
+ describe('cancelOrder', () => {
+ it('should cancel pending orders', async () => {
+ mockAxios.get.mockImplementationOnce(() =>
+ Promise.resolve({
+ status: 200,
+ data: { cbor: 'tx_cbor' },
+ })
+ );
+
+ const txCbor = await cancelOrder(
+ 'mainnet',
+ 'orderUtxo',
+ 'collateralUtxo',
+ 'addr1'
+ );
+
+ expect(txCbor).to.eq('tx_cbor');
+ });
+
+ it('should throw generic error for invalid response', async () => {
+ await expect(async () => {
+ mockAxios.get.mockImplementationOnce(() =>
+ Promise.resolve({ status: 400 })
+ );
+ await cancelOrder(
+ 'mainnet',
+ cancelOrderParams.utxo,
+ cancelOrderParams.collateralUTxOs,
+ cancelOrderParams.address
+ );
+ }).rejects.toThrow('Failed to cancel swap transaction');
+ });
+ });
+});
+
+const mockedOrders = [
+ {
+ from: {
+ amount: '1000000',
+ token: '.',
+ },
+ to: {
+ amount: '41372',
+ token:
+ '2adf188218a66847024664f4f63939577627a56c090f679fe366c5ee.535441424c45',
+ },
+ sender:
+ 'addr1qy0556dz9jssrrnhv0g3ga98uczdd465cut9jjs5a4k5qy3yl52kwxsh5wfx3darrc4xwql43ylj2n29dpq3xg46a6mska8vfz',
+ owner:
+ 'addr1qy0556dz9jssrrnhv0g3ga98uczdd465cut9jjs5a4k5qy3yl52kwxsh5wfx3darrc4xwql43ylj2n29dpq3xg46a6mska8vfz',
+ ownerPubKeyHash: '1f4a69a22ca1018e7763d11474a7e604d6d754c716594a14ed6d4012',
+ ownerStakeKeyHash:
+ '24fd15671a17a39268b7a31e2a6703f5893f254d4568411322baeeb7',
+ batcherFee: {
+ amount: '950000',
+ token: '.',
+ },
+ deposit: '1700000',
+ valueAttached: [
+ {
+ amount: '3650000',
+ token: '.',
+ },
+ ],
+ utxo: '1e977694e2413bd0e6105303bb44da60530cafe49b864dde8f8902b021ed86ba#0',
+ provider: 'muesliswap_v4',
+ feeField: '2650000',
+ allowPartial: true,
+ },
+];
+
+const mockedCreateOrderResult = {
+ status: 'success',
+ datum:
+ 'd8799fd8799fd8799fd8799f581c353b8bc29a15603f0b73eac44653d1bd944d92e0e0dcd5eb185164a2ffd8799fd8799fd8799f581cda22c532206a75a628778eebaf63826f9d93fbe9b4ac69a7f8e4cd78ffffffff581c353b8bc29a15603f0b73eac44653d1bd944d92e0e0dcd5eb185164a21b00000188f2408726d8799fd8799f4040ffd8799f581cdda5fdb1002f7389b33e036b6afee82a8189becb6cba852e8b79b4fb480014df1047454e53ffffffd8799fd879801a0006517affff',
+ hash: '4ae3fc5498e9d0f04daaf2ee739e41dc3f6f4119391e7274f0b3fa15aa2163ff',
+ address: 'addr1wxr2a8htmzuhj39y2gq7ftkpxv98y2g67tg8zezthgq4jkg0a4ul4',
+};
+
+const createOrderParams = {
+ address:
+ 'addr1qy0556dz9jssrrnhv0g3ga98uczdd465cut9jjs5a4k5qy3yl52kwxsh5wfx3darrc4xwql43ylj2n29dpq3xg46a6mska8vfz',
+ protocol: 'sundaeswap',
+ poolId: '14',
+ sell: {
+ ...ADA_TOKEN,
+ amount: '25000000',
+ },
+ buy: {
+ ...GENS_TOKEN,
+ amount: '50000000',
+ },
+} as const;
+
+const cancelOrderParams = {
+ utxo: '6c4b4e55301d79128071f05a018cf05b7de86bc3f92d05b6668423e220152a86',
+ collateralUTxOs:
+ '6c4b4e55301d79128071f05a018cf05b7de86bc3f92d05b6668423e220152a86',
+ address:
+ 'addr1qy0556dz9jssrrnhv0g3ga98uczdd465cut9jjs5a4k5qy3yl52kwxsh5wfx3darrc4xwql43ylj2n29dpq3xg46a6mska8vfz',
+} as const;
diff --git a/packages/swap/api/orders.ts b/packages/swap/api/orders.ts
new file mode 100644
index 0000000000..4c977a833b
--- /dev/null
+++ b/packages/swap/api/orders.ts
@@ -0,0 +1,94 @@
+import { Swap } from '@yoroi/types';
+import { SWAP_API_ENDPOINTS, axiosClient } from './config';
+
+export async function createOrder(
+ network: Swap.Network,
+ order: Swap.CreateOrderData
+): Promise {
+ const apiUrl = SWAP_API_ENDPOINTS[network].constructSwapDatum;
+ const response = await axiosClient.get<
+ | { status: 'failed'; reason?: string }
+ | { status: 'success'; hash: string; datum: string; address: string }
+ >('/', {
+ baseURL: apiUrl,
+ params: {
+ walletAddr: order.address,
+ protocol: order.protocol,
+ poolId: order.poolId,
+ sellTokenPolicyID: order.sell.policyId,
+ sellTokenNameHex: order.sell.assetName,
+ sellAmount: order.sell.amount,
+ buyTokenPolicyID: order.buy.policyId,
+ buyTokenNameHex: order.buy.assetName,
+ buyAmount: order.buy.amount,
+ },
+ });
+
+ if (response.status !== 200) {
+ throw new Error('Failed to construct swap datum', {
+ cause: response.data,
+ });
+ }
+
+ if (response.data.status === 'failed') {
+ throw new Error(response.data.reason || 'Unexpected error occurred');
+ }
+
+ return {
+ datumHash: response.data.hash,
+ datum: response.data.datum,
+ contractAddress: response.data.address,
+ };
+}
+
+/**
+ * @param orderUTxO order UTxO from the smart contract to cancel. e.g. "txhash#0"
+ * @param collateralUTxOs collateral UTxOs to use for canceling the order in cbor format.
+ * @param walletAddress address of the wallet that owns the order in cbor format.
+ * @returns an unsigned transaction to cancel the order.
+ */
+export async function cancelOrder(
+ network: Swap.Network,
+ orderUTxO: string,
+ collateralUTxO: string,
+ walletAddress: string
+): Promise {
+ const apiUrl = SWAP_API_ENDPOINTS[network].cancelSwapTransaction;
+ const response = await axiosClient.get('/', {
+ baseURL: apiUrl,
+ params: {
+ wallet: walletAddress,
+ utxo: orderUTxO,
+ collateralUTxO,
+ },
+ });
+
+ if (response.status !== 200) {
+ throw new Error('Failed to cancel swap transaction', {
+ cause: response.data,
+ });
+ }
+
+ return response.data.cbor;
+}
+
+export async function getOrders(
+ network: Swap.Network,
+ stakeKeyHash: string
+): Promise {
+ const apiUrl = SWAP_API_ENDPOINTS[network].getPools;
+ const response = await axiosClient.get('/', {
+ baseURL: apiUrl,
+ params: {
+ 'stake-key-hash': stakeKeyHash,
+ },
+ });
+
+ if (response.status !== 200) {
+ throw new Error(`Failed to get orders for ${stakeKeyHash}`, {
+ cause: response.data,
+ });
+ }
+
+ return response.data;
+}
diff --git a/packages/swap/api/pools.spec.ts b/packages/swap/api/pools.spec.ts
new file mode 100644
index 0000000000..33b31897b4
--- /dev/null
+++ b/packages/swap/api/pools.spec.ts
@@ -0,0 +1,79 @@
+import { describe, expect, it, vi, Mocked } from 'vitest';
+import { getPools } from './pools';
+import { axiosClient } from './config';
+
+vi.mock('./config.ts');
+const mockAxios = axiosClient as Mocked;
+
+describe('SwapPoolsApi', () => {
+ it('should get pools list for a given token pair', async () => {
+ mockAxios.get.mockImplementationOnce(() =>
+ Promise.resolve({
+ status: 200,
+ data: mockedPoolRes,
+ })
+ );
+
+ const result = await getPools(
+ 'mainnet',
+ getPoolsParams.sell,
+ getPoolsParams.buy
+ );
+ expect(result).to.be.of.lengthOf(1);
+ });
+
+ it('should throw error for invalid response', async () => {
+ await expect(async () => {
+ mockAxios.get.mockImplementationOnce(() =>
+ Promise.resolve({ status: 500 })
+ );
+ await getPools('preprod', getPoolsParams.sell, getPoolsParams.buy);
+ }).rejects.toThrow('Failed to fetch pools for token pair');
+ });
+});
+
+const mockedPoolRes = [
+ {
+ provider: 'minswap',
+ fee: '0.3',
+ tokenA: {
+ amount: '1233807687',
+ token: '.',
+ },
+ tokenB: {
+ amount: '780',
+ token:
+ 'e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed72.43414b45',
+ },
+ price: 1581804.726923077,
+ batcherFee: {
+ amount: '2000000',
+ token: '.',
+ },
+ depositFee: {
+ amount: '2000000',
+ token: '.',
+ },
+ deposit: 2000000,
+ utxo: '0596860b5970ef989c56f7ae38b3c0f74bb4979ac15ee994c30760f7f4d908ce#0',
+ poolId:
+ '0be55d262b29f564998ff81efe21bdc0022621c12f15af08d0f2ddb1.7339a8bcda85e2c997d9f16beddbeb3ad755f5202f5cfd9cb08db346a1292c01',
+ timestamp: '2023-05-31 07:03:41',
+ lpToken: {
+ amount: '981004',
+ token:
+ 'e4214b7cce62ac6fbba385d164df48e157eae5863521b4b67ca71d86.7339a8bcda85e2c997d9f16beddbeb3ad755f5202f5cfd9cb08db346a1292c01',
+ },
+ },
+];
+
+const getPoolsParams = {
+ sell: {
+ policyId: '',
+ assetNameHex: '',
+ },
+ buy: {
+ policyId: 'e16c2dc8ae937e8d3790c7fd7168d7b994621ba14ca11415f39fed72',
+ assetNameHex: '43414b45',
+ },
+} as const;
diff --git a/packages/swap/api/pools.ts b/packages/swap/api/pools.ts
new file mode 100644
index 0000000000..12235d11db
--- /dev/null
+++ b/packages/swap/api/pools.ts
@@ -0,0 +1,34 @@
+import { Swap } from '@yoroi/types';
+import { SWAP_API_ENDPOINTS, axiosClient } from './config';
+
+export async function getPools(
+ network: Swap.Network,
+ tokenA: Swap.BaseTokenInfo,
+ tokenB: Swap.BaseTokenInfo
+): Promise {
+ const params: { [key: string]: string } = {
+ 'policy-id1': tokenA.policyId,
+ 'policy-id2': tokenB.policyId,
+ };
+
+ if ('assetName' in tokenA) params['tokenname1'] = tokenA.assetName;
+ if ('assetName' in tokenB) params['tokenname2'] = tokenB.assetName;
+
+ // note: {tokenname-hex} will overwrites {tokenname}
+ if ('assetNameHex' in tokenA) params['tokenname-hex1'] = tokenA.assetNameHex;
+ if ('assetNameHex' in tokenB) params['tokenname-hex2'] = tokenB.assetNameHex;
+
+ const apiUrl = SWAP_API_ENDPOINTS[network].getPools;
+ const response = await axiosClient.get('', {
+ baseURL: apiUrl,
+ params,
+ });
+
+ if (response.status !== 200) {
+ throw new Error('Failed to fetch pools for token pair', {
+ cause: response.data,
+ });
+ }
+
+ return response.data;
+}
diff --git a/packages/swap/api/tokens.spec.ts b/packages/swap/api/tokens.spec.ts
new file mode 100644
index 0000000000..353e889114
--- /dev/null
+++ b/packages/swap/api/tokens.spec.ts
@@ -0,0 +1,74 @@
+import { expect, describe, it, vi, Mocked } from 'vitest';
+import { getTokens } from './tokens';
+import { axiosClient } from './config';
+
+vi.mock('./config.ts');
+const mockAxios = axiosClient as Mocked;
+
+describe('SwapTokensApi', () => {
+ it('should get all supported tokens list', async () => {
+ mockAxios.get.mockImplementationOnce(() =>
+ Promise.resolve({ status: 200, data: mockedGetTokensRes })
+ );
+ const result = await getTokens('mainnet');
+ expect(result).to.be.lengthOf(1);
+ });
+
+ it('should return empty list on preprod network', async () => {
+ expect(await getTokens('preprod')).to.be.empty;
+ });
+
+ it('should throw error for invalid response', async () => {
+ await expect(async () => {
+ mockAxios.get.mockImplementationOnce(() =>
+ Promise.resolve({ status: 500 })
+ );
+ await getTokens('mainnet');
+ }).rejects.toThrow('Failed to fetch tokens');
+ });
+});
+
+const mockedGetTokensRes = [
+ {
+ info: {
+ supply: { total: '1000000000000', circulating: null },
+ status: 'unverified',
+ image: 'ipfs://QmPzaykTy4yfutCtwv7nRUmgbQbA7euiThyy2i9fiFuDHX',
+ imageIpfsHash: 'QmPzaykTy4yfutCtwv7nRUmgbQbA7euiThyy2i9fiFuDHX',
+ symbol: 'ARGENT',
+ minting: {
+ type: 'time-lock-policy',
+ blockchain: 'cardano',
+ mintedBeforeSlotNumber: 91850718,
+ },
+ mediatype: 'image/png',
+ tokentype: 'token',
+ description: 'ARGENT Token',
+ totalsupply: 1000000000000,
+ address: {
+ policyId: 'c04f4200502a998e9eebafac0291a1f38008de3fe146d136946d8f4b',
+ name: '415247454e54',
+ },
+ decimalPlaces: 0,
+ categories: [],
+ },
+ price: {
+ volume: { base: 0, quote: 0 },
+ volumeChange: { base: 0, quote: 0 },
+ volumeTotal: { base: 0, quote: 0 },
+ volumeAggregator: {},
+ price: 0,
+ askPrice: 0,
+ bidPrice: 0,
+ priceChange: { '24h': 0, '7d': 0 },
+ quoteDecimalPlaces: 0,
+ baseDecimalPlaces: 6,
+ quoteAddress: {
+ policyId: 'c04f4200502a998e9eebafac0291a1f38008de3fe146d136946d8f4b',
+ name: '415247454e54',
+ },
+ baseAddress: { policyId: '', name: '' },
+ price10d: [],
+ },
+ },
+];
diff --git a/packages/swap/api/tokens.ts b/packages/swap/api/tokens.ts
new file mode 100644
index 0000000000..3df38337c3
--- /dev/null
+++ b/packages/swap/api/tokens.ts
@@ -0,0 +1,25 @@
+import { Swap } from '@yoroi/types';
+import { SWAP_API_ENDPOINTS, axiosClient } from './config';
+
+export async function getTokens(
+ network: Swap.Network,
+ policyId = '',
+ assetName = ''
+): Promise {
+ if (network === 'preprod') return [];
+
+ const apiUrl = SWAP_API_ENDPOINTS[network].getTokens;
+ const response = await axiosClient.get('', {
+ baseURL: apiUrl,
+ params: {
+ 'base-policy-id': policyId,
+ 'base-tokenname': assetName,
+ },
+ });
+
+ if (response.status !== 200) {
+ throw new Error('Failed to fetch tokens', { cause: response.data });
+ }
+
+ return response.data;
+}
diff --git a/packages/swap/cardano/create.ts b/packages/swap/cardano/create.ts
deleted file mode 100644
index 0f0e74e031..0000000000
--- a/packages/swap/cardano/create.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import {
- Address, TransactionBody, TransactionBuilder, TransactionOutput,
-} from "@emurgo/cross-csl-core";
-import { buildValue } from "./utils";
-
-export async function buildCreateOrderTx(txBuilder: TransactionBuilder, order: any): Promise {
- const address = await Address.fromBech32(order.contractAddress);
- const value = await buildValue(order.depositAmount);
- const txOutput = await TransactionOutput.new(address, value);
-
- await txOutput.setDataHash(order.datumHash);
- await txBuilder.addOutput(txOutput);
-
- return txBuilder.build();
-};
diff --git a/packages/swap/cardano/index.ts b/packages/swap/cardano/index.ts
deleted file mode 100644
index 1e03cceb83..0000000000
--- a/packages/swap/cardano/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './create';
diff --git a/packages/swap/cardano/utils.ts b/packages/swap/cardano/utils.ts
deleted file mode 100644
index cb6544cb9b..0000000000
--- a/packages/swap/cardano/utils.ts
+++ /dev/null
@@ -1,51 +0,0 @@
-import { AssetName, Assets, BigNum, MultiAsset, ScriptHash, Value } from "@emurgo/cross-csl-core";
-import { TokenAmount } from "../dex";
-
-export const toScripthash = (hex: string) => {
- return ScriptHash.fromBytes(toUint8Array(hex));
-};
-
-export const fromScripthash = async (instance: ScriptHash) => {
- return fromUint8Array(await instance.toBytes());
-};
-
-export const toUint8Array = (hex: string) => {
- if (hex.length % 2 !== 0) throw new Error("Invalid hex string");
-
- const bytes = new Uint8Array(hex.length / 2);
-
- for (let i = 0; i < hex.length; i += 2) {
- let value = parseInt(hex.substring(i, i + 2), 16);
- if (isNaN(value)) throw new Error("Invalid hex string");
- bytes[i / 2] = value;
- }
-
- return bytes;
-};
-
-export const fromUint8Array = (bytes: Uint8Array) => {
- const hex = new Array(bytes.length);
-
- for (let i = 0; i < bytes.length; i++) {
- hex.push(bytes[i].toString(16).padStart(2, '0'));
- }
-
- return hex.join('');
-};
-
-export const buildValue = async (token: TokenAmount) => {
- if (token.address.policyId === '' && token.address.assetName === '') {
- return await Value.new(await BigNum.fromStr(token.amount));
- }
-
- const multiasset = await MultiAsset.new();
- const assets = await Assets.new();
- await assets.insert(
- await AssetName.new(toUint8Array(token.address.assetName)),
- await BigNum.fromStr(token.amount),
- );
- const value = await Value.new(await BigNum.fromStr('0'));
- await multiasset.insert(await toScripthash(token.address.policyId), assets);
- await value.setMultiasset(multiasset);
- return value;
-};
diff --git a/packages/swap/dex.spec.ts b/packages/swap/dex.spec.ts
deleted file mode 100644
index 7f65a0ec93..0000000000
--- a/packages/swap/dex.spec.ts
+++ /dev/null
@@ -1,606 +0,0 @@
-import { describe, expect, it, vi } from "vitest";
-import {
- calculateAmountsGivenInput,
- calculateAmountsGivenOutput,
- constructLimitOrder,
- constructMarketOrder,
- getOpenOrders,
- getOrderDatum,
- getSupportedTokens,
- getTokenPairPools,
-} from "./dex";
-
-describe("Yoroi DEX aggregator", () => {
- it("should be able to get the tokens supported by the DEX aggregator", async () => {
- const tokens = await getSupportedTokens();
-
- expect(tokens).containSubset([mockTokens[0]]);
- });
-
- it("should be able to get the pools for the selected token pair", async () => {
- const pools = await getTokenPairPools(ADA_TOKEN, GENS_TOKEN);
-
- expect(pools.length).toBeGreaterThan(0);
- expect(pools).containSubset([SUNDAE_POOL]);
- });
-
- it("should be able to calculate the amounts given input for a trade", () => {
- const tokenPair = calculateAmountsGivenInput(SUNDAE_POOL, {
- address: ADA_TOKEN,
- amount: "20000000",
- });
-
- expect(Number(tokenPair.receive.amount)).approximately(21_000_000, 500_000);
- });
-
- it("should be able to calculate the amounts given output for a trade", () => {
- const tokenPair = calculateAmountsGivenOutput(SUNDAE_POOL, {
- address: GENS_TOKEN,
- amount: "21000000",
- });
-
- expect(Number(tokenPair.send.amount)).approximately(20_000_000, 500_000);
- });
-
- it("should get the same result for a trade regardless of the direction", () => {
- const tokenPairInput = calculateAmountsGivenInput(SUNDAE_POOL, {
- address: ADA_TOKEN,
- amount: "20000000",
- });
-
- const tokenPairOutput = calculateAmountsGivenOutput(SUNDAE_POOL, {
- address: GENS_TOKEN,
- amount: "21366237",
- });
-
- expect(Number(tokenPairInput.send.amount)).approximately(
- Number(tokenPairOutput.send.amount),
- 10
- );
- expect(Number(tokenPairInput.receive.amount)).approximately(
- Number(tokenPairOutput.receive.amount),
- 10
- );
- });
-
- it("should be able to construct a LIMIT order for a trade given a wallet address and slippage tolerance", () => {
- const slippage = 15; // 5%
- const address = "some wallet address here";
-
- const order = constructLimitOrder(
- {
- address: ADA_TOKEN,
- amount: "25000000",
- },
- {
- address: GENS_TOKEN,
- amount: "50000000",
- },
- SUNDAE_POOL,
- slippage,
- address
- );
-
- expect(order?.protocol).toEqual("sundaeswap");
- expect(order?.walletAddress).toEqual(address);
- expect(order?.send.amount).toEqual("25000000");
- expect(Number(order?.receive.amount)).approximately(
- Number("42500000"),
- 100_000
- );
- });
-
- it("should be able to construct a MARKET order for a trade given a wallet address and slippage tolerance", async () => {
- const pools = await getTokenPairPools(ADA_TOKEN, GENS_TOKEN);
- const address = "some wallet address here";
- const slippage = 5; // 5%
-
- const order = constructMarketOrder(
- {
- address: ADA_TOKEN,
- amount: "25000000",
- },
- GENS_TOKEN,
- pools,
- slippage,
- address
- );
-
- expect(order?.protocol).toEqual("wingriders");
- expect(order?.walletAddress).toEqual(address);
- expect(order?.send.amount).toEqual("25000000");
- expect(Number(order?.receive.amount)).approximately(
- Number("26700000"),
- 100_000
- );
- });
-
- it("should be able to get open orders for a wallet stake key hash", async () => {
- const orders = await getOpenOrders(
- "24fd15671a17a39268b7a31e2a6703f5893f254d4568411322baeeb7"
- );
- console.log(JSON.stringify(orders, null, 2));
-
- expect(orders.length).toBeGreaterThan(0);
- expect(orders).containSubset([
- {
- protocol: "sundaeswap",
- depposit: "2000000",
- utxo: "6c4b4e55301d79128071f05a018cf05b7de86bc3f92d05b6668423e220152a86#0",
- send: {
- address: {
- policyId: "",
- assetName: "",
- },
- amount: "1000000",
- },
- receive: {
- address: {
- policyId:
- "8a1cfae21368b8bebbbed9800fec304e95cce39a2a57dc35e2e3ebaa",
- assetName: "4d494c4b",
- },
- amount: "20",
- },
- },
- ]);
- });
-
- it("should be able to get the datum for a constructed order", async () => {
- const pools = await getTokenPairPools(ADA_TOKEN, GENS_TOKEN);
- const address = "some wallet address here";
- const slippage = 5; // 5%
-
- const order = constructMarketOrder(
- {
- address: ADA_TOKEN,
- amount: "25000000",
- },
- GENS_TOKEN,
- pools,
- slippage,
- address
- );
-
- const orderDatum = await getOrderDatum(order!);
-
- expect(orderDatum.contractAddress).toEqual(mockOrder.address);
- expect(orderDatum.datumHash).toEqual(mockOrder.hash);
- expect(orderDatum.datum).toEqual(mockOrder.datum);
- });
-});
-
-vi.mock("./openswap", async () => {
- const actual = await vi.importActual("./openswap");
-
- return {
- ...(actual as {}),
- createOrder: async () => mockOrder,
- getOrders: async () => [mockOpenOrder],
- getPools: async () => mockPools,
- getTokens: async () => mockTokens,
- };
-});
-
-const mockOpenOrder = {
- from: {
- amount: "1000000",
- token: ".",
- },
- to: {
- amount: "20",
- token: "8a1cfae21368b8bebbbed9800fec304e95cce39a2a57dc35e2e3ebaa.4d494c4b",
- },
- sender:
- "addr1qy0556dz9jssrrnhv0g3ga98uczdd465cut9jjs5a4k5qy3yl52kwxsh5wfx3darrc4xwql43ylj2n29dpq3xg46a6mska8vfz",
- owner:
- "addr1qy0556dz9jssrrnhv0g3ga98uczdd465cut9jjs5a4k5qy3yl52kwxsh5wfx3darrc4xwql43ylj2n29dpq3xg46a6mska8vfz",
- ownerPubKeyHash: "1f4a69a22ca1018e7763d11474a7e604d6d754c716594a14ed6d4012",
- batcherFee: {
- amount: "2500000",
- token: ".",
- },
- deposit: "2000000",
- valueAttached: [
- {
- amount: "5500000",
- token: ".",
- },
- ],
- utxo: "6c4b4e55301d79128071f05a018cf05b7de86bc3f92d05b6668423e220152a86#0",
- provider: "sundaeswap",
- batcherAddress: "addr1wxaptpmxcxawvr3pzlhgnpmzz3ql43n2tc8mn3av5kx0yzs09tqh8",
- poolId: "14",
- swapDirection: 0,
-};
-
-const mockOrder = {
- status: "success",
- datum:
- "d8799fd8799fd8799fd8799f581c353b8bc29a15603f0b73eac44653d1bd944d92e0e0dcd5eb185164a2ffd8799fd8799fd8799f581cda22c532206a75a628778eebaf63826f9d93fbe9b4ac69a7f8e4cd78ffffffff581c353b8bc29a15603f0b73eac44653d1bd944d92e0e0dcd5eb185164a21b00000188f2408726d8799fd8799f4040ffd8799f581cdda5fdb1002f7389b33e036b6afee82a8189becb6cba852e8b79b4fb480014df1047454e53ffffffd8799fd879801a0006517affff",
- hash: "4ae3fc5498e9d0f04daaf2ee739e41dc3f6f4119391e7274f0b3fa15aa2163ff",
- address: "addr1wxr2a8htmzuhj39y2gq7ftkpxv98y2g67tg8zezthgq4jkg0a4ul4",
-};
-
-const mockPools = [
- {
- provider: "muesliswap_v2",
- fee: "0.3",
- tokenA: { amount: "2778918813", token: "." },
- tokenB: {
- amount: "3046518484",
- token:
- "dda5fdb1002f7389b33e036b6afee82a8189becb6cba852e8b79b4fb.0014df1047454e53",
- },
- price: 0.9121621377301974,
- batcherFee: { amount: "950000", token: "." },
- depositFee: { amount: "2000000", token: "." },
- deposit: 2000000,
- utxo: "b9d6cef3002de24896e5949619bf5e76ddcbd44147d1a127ee04e2b7486747df#7",
- poolId:
- "909133088303c49f3a30f1cc8ed553a73857a29779f6c6561cd8093f.34e551a0dabee7dfddcb5dcea93d22040a8b9e36348057da2987a6f1bc731935",
- timestamp: "2023-05-26 15:12:11",
- lpToken: {
- amount: "2864077440",
- token:
- "af3d70acf4bd5b3abb319a7d75c89fb3e56eafcdd46b2e9b57a2557f.34e551a0dabee7dfddcb5dcea93d22040a8b9e36348057da2987a6f1bc731935",
- },
- },
- {
- provider: "minswap",
- fee: "0.3",
- tokenA: { amount: "159431695049", token: "." },
- tokenB: {
- amount: "179285403975",
- token:
- "dda5fdb1002f7389b33e036b6afee82a8189becb6cba852e8b79b4fb.0014df1047454e53",
- },
- price: 0.8892619896220417,
- batcherFee: { amount: "2000000", token: "." },
- depositFee: { amount: "2000000", token: "." },
- deposit: 2000000,
- utxo: "a9c6386fcca2cc9ad86e86940566b3f1b22ac3e47f59fa040d42c62811184e82#3",
- poolId:
- "0be55d262b29f564998ff81efe21bdc0022621c12f15af08d0f2ddb1.4705d99a4cf6bce9181e60fdbbf961edf6acad7141ed69186c8a8883600e59c5",
- timestamp: "2023-05-26 15:12:11",
- lpToken: {
- amount: "169067370752",
- token:
- "e4214b7cce62ac6fbba385d164df48e157eae5863521b4b67ca71d86.4705d99a4cf6bce9181e60fdbbf961edf6acad7141ed69186c8a8883600e59c5",
- },
- },
- {
- provider: "sundaeswap",
- fee: "0.05",
- tokenA: { amount: "1762028491", token: "." },
- tokenB: {
- amount: "1904703890",
- token:
- "dda5fdb1002f7389b33e036b6afee82a8189becb6cba852e8b79b4fb.0014df1047454e53",
- },
- price: 0.925093134030403,
- batcherFee: { amount: "2500000", token: "." },
- depositFee: { amount: "2000000", token: "." },
- deposit: 2000000,
- utxo: "b28ccb32adf80618a4e78d97012754f8c402b29907f1d2e6ed5df973003844f4#0",
- poolId: "0029cb7c88c7567b63d1a512c0ed626aa169688ec980730c0473b913.7020e403",
- timestamp: "2023-05-26 15:12:11",
- lpToken: {
- amount: "1813689472",
- token:
- "0029cb7c88c7567b63d1a512c0ed626aa169688ec980730c0473b913.6c7020e403",
- },
- },
- {
- provider: "wingriders",
- fee: "0.35",
- tokenA: { amount: "25476044027", token: "." },
- tokenB: {
- amount: "28844597339",
- token:
- "dda5fdb1002f7389b33e036b6afee82a8189becb6cba852e8b79b4fb.0014df1047454e53",
- },
- price: 0.8832171837099813,
- batcherFee: { amount: "2000000", token: "." },
- depositFee: { amount: "2000000", token: "." },
- deposit: 2000000,
- utxo: "2120d4a9ece0add45461d5d3262f3708bd6cbdd8a473c7ceb32eb5e83f4c633e#0",
- poolId:
- "026a18d04a0c642759bb3d83b12e3344894e5c1c7b2aeb1a2113a570.84eaa43491009c3a9f4e257370352e84ea7246ee8a27d9d3d9e458141d9bcf97",
- timestamp: "2023-05-26 15:10:51",
- lpToken: null,
- },
-];
-
-const mockTokens = [
- {
- info: {
- supply: { total: "10000", circulating: null },
- status: "verified",
- address: {
- policyId: "53fb41609e208f1cd3cae467c0b9abfc69f1a552bf9a90d51665a4d6",
- name: "f9c09e1913e29072938cad1fda7b70939090c0be1f7fa48150a2e63a2b9ad715",
- },
- symbol: "OBOND [VICIS]",
- website: "https://app.optim.finance/dashboard",
- description:
- "A lender bond issued by Optim Finance corresponding to 100 ADA_TOKEN lent. Lending APY 5.33%. Max Duration 72 Epochs. Interest Buffer 6 epochs. Verify on token homepage to assess more details.",
- decimalPlaces: 0,
- categories: ["2"],
- },
- price: {
- volume: { base: 0, quote: 0 },
- volumeChange: { base: 0, quote: 0 },
- price: 1000.0,
- askPrice: 0,
- bidPrice: 1000.0,
- priceChange: { "24h": 0, "7d": 0 },
- fromToken: ".",
- toToken:
- "53fb41609e208f1cd3cae467c0b9abfc69f1a552bf9a90d51665a4d6.f9c09e1913e29072938cad1fda7b70939090c0be1f7fa48150a2e63a2b9ad715",
- quoteDecimalPlaces: 0,
- baseDecimalPlaces: 6,
- quoteAddress: {
- policyId: "53fb41609e208f1cd3cae467c0b9abfc69f1a552bf9a90d51665a4d6",
- name: "f9c09e1913e29072938cad1fda7b70939090c0be1f7fa48150a2e63a2b9ad715",
- },
- baseAddress: { policyId: "", name: "" },
- price10d: [],
- },
- },
- {
- info: {
- supply: { total: "100000000000", circulating: null },
- status: "unverified",
- website: "https://cardanofight.club",
- symbol: "CFC",
- decimalPlaces: 0,
- image:
- "https://tokens.muesliswap.com/static/img/tokens/71ccb467ef856b242753ca53ade36cd1f8d9abb33fdfa7d1ff89cda3.434643.png",
- description:
- "The official token for Cardano Fight Club. The ultimate utility token for all CFC Products and Development.",
- address: {
- policyId: "71ccb467ef856b242753ca53ade36cd1f8d9abb33fdfa7d1ff89cda3",
- name: "434643",
- },
- categories: [],
- },
- price: {
- volume: { base: 0, quote: 0 },
- volumeChange: { base: 0, quote: 0 },
- price: 14.5211676983,
- askPrice: 0,
- bidPrice: 1.112,
- priceChange: { "24h": 0, "7d": 0 },
- fromToken: ".",
- toToken:
- "71ccb467ef856b242753ca53ade36cd1f8d9abb33fdfa7d1ff89cda3.434643",
- quoteDecimalPlaces: 0,
- baseDecimalPlaces: 6,
- quoteAddress: {
- policyId: "71ccb467ef856b242753ca53ade36cd1f8d9abb33fdfa7d1ff89cda3",
- name: "434643",
- },
- baseAddress: { policyId: "", name: "" },
- price10d: [],
- },
- },
- {
- info: {
- supply: { total: "10000000", circulating: null },
- status: "unverified",
- website: "https://www.cardanofly.io/",
- symbol: "cdfly",
- decimalPlaces: 0,
- image: "ipfs://QmQaKcTjrBSVbmK5xZgaWjEa5yKzUSLRAbVyhyzbceXSMH",
- description: "",
- address: {
- policyId: "5d5aadeebb07a5e48827ef6efe577c7b4db4a69b2db1b29279e0b514",
- name: "6364666c79",
- },
- imageIpfsHash: "QmQaKcTjrBSVbmK5xZgaWjEa5yKzUSLRAbVyhyzbceXSMH",
- minting: {
- type: "time-lock-policy",
- blockchain: "cardano",
- mintedBeforeSlotNumber: 74916255,
- },
- mediatype: "image/png",
- tokentype: "token",
- totalsupply: 10000000,
- categories: [],
- },
- price: {
- volume: { base: 0, quote: 0 },
- volumeChange: { base: 0, quote: 0 },
- price: 0,
- askPrice: 0,
- bidPrice: 0,
- priceChange: { "24h": 0, "7d": 0 },
- quoteDecimalPlaces: 0,
- baseDecimalPlaces: 6,
- quoteAddress: {
- policyId: "5d5aadeebb07a5e48827ef6efe577c7b4db4a69b2db1b29279e0b514",
- name: "6364666c79",
- },
- baseAddress: { policyId: "", name: "" },
- price10d: [],
- },
- },
- {
- info: {
- supply: { total: "100000000000000", circulating: null },
- status: "unverified",
- website: "https://linktr.ee/nutriemp.CRYPTO",
- symbol: "BUDZ",
- decimalPlaces: 2,
- image: "ipfs://QmSESYYcMk9i3EDbQSWBmkfEWK4X7TokohJyvQThxpANgq",
- description: "NUTRIEMP.CRYPTO - GROWERS UNITE",
- address: {
- policyId: "d2cb1f7a8ae3bb94117e30d241566c2dd5adbd0708c40a8a5ac9ae60",
- name: "4255445a",
- },
- imageIpfsHash: "QmSESYYcMk9i3EDbQSWBmkfEWK4X7TokohJyvQThxpANgq",
- minting: {
- type: "time-lock-policy",
- blockchain: "cardano",
- mintedBeforeSlotNumber: 78641478,
- },
- mediatype: "image/png",
- tokentype: "token",
- totalsupply: 100000000000000,
- categories: [],
- },
- price: {
- volume: { base: 0, quote: 0 },
- volumeChange: { base: 0, quote: 0 },
- price: 0,
- askPrice: 0,
- bidPrice: 0,
- priceChange: { "24h": 0, "7d": 0 },
- quoteDecimalPlaces: 2,
- baseDecimalPlaces: 6,
- quoteAddress: {
- policyId: "d2cb1f7a8ae3bb94117e30d241566c2dd5adbd0708c40a8a5ac9ae60",
- name: "4255445a",
- },
- baseAddress: { policyId: "", name: "" },
- price10d: [],
- },
- },
- {
- info: {
- supply: { total: "10000000", circulating: null },
- status: "unverified",
- website: "https://ratsonchain.com",
- description: "The official token of Rats On Chain",
- image: "ipfs://QmeJPgcLWDDTBKxqTzWdDj1ruE5huBf3cbJNPVetnqW7DH",
- symbol: "ROC",
- decimalPlaces: 0,
- address: {
- policyId: "91ec8c9ae38d203c0fa1e0a31274f05bee7be4eb0133ed0beb837297",
- name: "524f43",
- },
- imageIpfsHash: "QmeJPgcLWDDTBKxqTzWdDj1ruE5huBf3cbJNPVetnqW7DH",
- ticker: "ROC",
- project: "Rats On Chain",
- mediatype: "image/png",
- categories: [],
- },
- price: {
- volume: { base: 0, quote: 0 },
- volumeChange: { base: 0, quote: 0 },
- price: 0,
- askPrice: 0,
- bidPrice: 0,
- priceChange: { "24h": 0, "7d": 0 },
- quoteDecimalPlaces: 0,
- baseDecimalPlaces: 6,
- quoteAddress: {
- policyId: "91ec8c9ae38d203c0fa1e0a31274f05bee7be4eb0133ed0beb837297",
- name: "524f43",
- },
- baseAddress: { policyId: "", name: "" },
- price10d: [],
- },
- },
- {
- info: {
- supply: { total: "39997832", circulating: null },
- status: "unverified",
- symbol: "BURPZ",
- decimalPlaces: 0,
- image: "ipfs://QmZcnb7DSFXdd9crvTMyTXnbQvUU5srktxK8aZxUNFvZDJ",
- description: "Official Fluffy Utility Token",
- address: {
- policyId: "ef71f8d84c69bb45f60e00b1f595545238b339f00b137dd5643794bb",
- name: "425552505a",
- },
- imageIpfsHash: "QmZcnb7DSFXdd9crvTMyTXnbQvUU5srktxK8aZxUNFvZDJ",
- ticker: "BURPZ",
- twitter: "@FluffysNFT",
- mediatype: "image/png",
- categories: [],
- },
- price: {
- volume: { base: "0", quote: "0" },
- volumeChange: { base: 0, quote: 0 },
- price: 0,
- askPrice: 0,
- bidPrice: 0,
- priceChange: { "24h": "0", "7d": "0" },
- fromToken: "lovelace",
- toToken:
- "ef71f8d84c69bb45f60e00b1f595545238b339f00b137dd5643794bb.425552505a",
- price10d: [],
- quoteDecimalPlaces: 0,
- baseDecimalPlaces: 6,
- quoteAddress: {
- policyId: "ef71f8d84c69bb45f60e00b1f595545238b339f00b137dd5643794bb",
- name: "425552505a",
- },
- baseAddress: { policyId: "", name: "" },
- },
- },
- {
- info: {
- supply: { total: "76222812878", circulating: null },
- status: "unverified",
- address: {
- policyId: "e4214b7cce62ac6fbba385d164df48e157eae5863521b4b67ca71d86",
- name: "f86a805f257a14b127b9b54444e556ab1a066f690501d4474bbad4454324845b",
- },
- decimalPlaces: 0,
- symbol:
- "f86a805f257a14b127b9b54444e556ab1a066f690501d4474bbad4454324845b",
- categories: [],
- },
- price: {
- volume: { base: 0, quote: 0 },
- volumeChange: { base: 0, quote: 0 },
- price: 0,
- askPrice: 0,
- bidPrice: 0,
- priceChange: { "24h": 0, "7d": 0 },
- quoteDecimalPlaces: 0,
- baseDecimalPlaces: 6,
- quoteAddress: {
- policyId: "e4214b7cce62ac6fbba385d164df48e157eae5863521b4b67ca71d86",
- name: "f86a805f257a14b127b9b54444e556ab1a066f690501d4474bbad4454324845b",
- },
- baseAddress: { policyId: "", name: "" },
- price10d: [],
- },
- },
-];
-
-const ADA_TOKEN = {
- policyId: "",
- assetName: "",
-};
-
-const GENS_TOKEN = {
- policyId: "dda5fdb1002f7389b33e036b6afee82a8189becb6cba852e8b79b4fb",
- assetName: "0014df1047454e53",
-};
-
-const SUNDAE_POOL = {
- protocol: "sundaeswap" as const,
- batcherFee: "2500000",
- lvlDeposit: "2000000",
- tokenA: {
- address: {
- policyId: "",
- assetName: "",
- },
- amount: "1762028491",
- },
- tokenB: {
- address: {
- policyId: "dda5fdb1002f7389b33e036b6afee82a8189becb6cba852e8b79b4fb",
- assetName: "0014df1047454e53",
- },
- amount: "1904703890",
- },
- poolFee: "0.05",
- poolId: "0029cb7c88c7567b63d1a512c0ed626aa169688ec980730c0473b913.7020e403",
-};
diff --git a/packages/swap/dex.ts b/packages/swap/dex.ts
deleted file mode 100644
index 6b0a80f35d..0000000000
--- a/packages/swap/dex.ts
+++ /dev/null
@@ -1,292 +0,0 @@
-import {
- cancelOrder,
- createOrder,
- getOrders,
- getPools,
- getTokens,
-} from "./openswap";
-import type { Protocol, Token } from "./openswap";
-
-export type OrderDetails = {
- protocol: Protocol;
- poolId?: string; // only required for SundaeSwap trades.
- send: TokenAmount;
- receive: TokenAmount;
- walletAddress: string;
-};
-
-export type OrderDatum = {
- contractAddress: string;
- datumHash: string;
- datum: string;
-};
-
-export type TokenAddress = {
- policyId: string;
- assetName: string;
-};
-
-export type TokenAmount = {
- address: TokenAddress;
- amount: string;
-};
-
-export type TokenPair = {
- send: TokenAmount; // token to swap from aka selling
- receive: TokenAmount; // token to swap to aka buying
-};
-
-export type TokenPairPool = {
- protocol: Protocol;
- lpToken?: TokenAmount;
- tokenA: TokenAmount;
- tokenB: TokenAmount;
- batcherFee: string;
- lvlDeposit: string;
- poolFee: string;
- poolId: string;
-};
-
-export const getOpenOrders = async (stakeKeyHash: string) =>
- (await getOrders(stakeKeyHash)).map((order) => ({
- protocol: order.provider,
- depposit: order.deposit,
- utxo: order.utxo,
- send: {
- address: {
- policyId: order.from.token.split(".")[0],
- assetName: order.from.token.split(".")[1],
- },
- amount: order.from.amount,
- },
- receive: {
- address: {
- policyId: order.to.token.split(".")[0],
- assetName: order.to.token.split(".")[1],
- },
- amount: order.to.amount,
- },
- }));
-
-export const getCancelOrderTx = async (
- orderUTxO: string,
- collateralUTxOs: string,
- walletAddress: string
-) => await cancelOrder(orderUTxO, collateralUTxOs, walletAddress);
-
-export const getOrderDatum = async (order: OrderDetails): Promise => {
- const { address, hash, datum } = await createOrder({
- address: order.walletAddress,
- protocol: order.protocol,
- poolId: order.poolId,
- sell: {
- policyId: order.send.address.policyId,
- assetName: order.send.address.assetName,
- amount: order.send.amount,
- },
- buy: {
- policyId: order.receive.address.policyId,
- assetName: order.receive.address.assetName,
- amount: order.receive.amount,
- },
- });
-
- return {
- contractAddress: address,
- datumHash: hash,
- datum,
- };
-};
-
-export const getSupportedTokens = async (
- baseToken?: TokenAddress
-): Promise => getTokens(baseToken?.policyId, baseToken?.assetName);
-
-export const getTokenPairPools = async (
- sendToken: TokenAddress,
- receiveToken: TokenAddress
-): Promise =>
- (await getPools(sendToken, receiveToken)).map((pool) => ({
- protocol: pool.provider as Protocol,
- lpToken: pool.lpToken && {
- address: {
- policyId: pool.lpToken.token.split(".")[0],
- assetName: pool.lpToken.token.split(".")[1],
- },
- amount: pool.lpToken.amount,
- },
- batcherFee: pool.batcherFee.amount,
- lvlDeposit: pool.deposit.toString(),
- tokenA: {
- address: {
- policyId: pool.tokenA.token.split(".")[0],
- assetName: pool.tokenA.token.split(".")[1],
- },
- amount: pool.tokenA.amount,
- },
- tokenB: {
- address: {
- policyId: pool.tokenB.token.split(".")[0],
- assetName: pool.tokenB.token.split(".")[1],
- },
- amount: pool.tokenB.amount,
- },
- poolFee: pool.fee,
- poolId: pool.poolId,
- }));
-
-export const calculateAmountsGivenInput = (
- pool: TokenPairPool,
- from: TokenAmount
-): TokenPair => {
- const poolA = BigInt(pool.tokenA.amount);
- const poolB = BigInt(pool.tokenB.amount);
- const poolsProduct = poolA * poolB; // fee is part of tokens sent -> this means the constant product increases after the swap!
-
- const fromAmount = BigInt(from.amount);
-
- const poolFee = ceilDivision(
- BigInt(Number(pool.poolFee) * 1000) * fromAmount,
- BigInt(100 * 1000)
- );
-
- const getReceiveAmount = (poolA_: bigint, poolB_: bigint) => {
- const newPoolA = poolA_ + fromAmount - poolFee;
- const newPoolB = ceilDivision(poolsProduct, newPoolA);
-
- return (poolB_ - newPoolB).toString();
- };
-
- const receive = sameToken(from.address, pool.tokenA.address)
- ? { amount: getReceiveAmount(poolA, poolB), address: pool.tokenB.address }
- : { amount: getReceiveAmount(poolB, poolA), address: pool.tokenA.address };
-
- return { send: from, receive };
-};
-
-export const calculateAmountsGivenOutput = (
- pool: TokenPairPool,
- to: TokenAmount
-): TokenPair => {
- const poolA = BigInt(pool.tokenA.amount);
- const poolB = BigInt(pool.tokenB.amount);
- const poolsProduct = poolA * poolB; // fee is part of tokens sent -> this means the constant product increases after the swap!
-
- const toAmount = BigInt(to.amount);
-
- const poolFee = BigInt(100 * 1000) - BigInt(Number(pool.poolFee) * 1000);
-
- const getSendAmount = (poolA_: bigint, poolB_: bigint) => {
- const newPoolA =
- poolA_ - (poolA_ > toAmount ? toAmount : poolA_ - BigInt(1));
- const newPoolB = ceilDivision(poolsProduct + newPoolA, newPoolA);
- return ceilDivision(
- (newPoolB - poolB_) * BigInt(100 * 1000),
- poolFee
- ).toString();
- };
-
- const send = sameToken(to.address, pool.tokenA.address)
- ? { amount: getSendAmount(poolA, poolB), address: pool.tokenB.address }
- : { amount: getSendAmount(poolB, poolA), address: pool.tokenA.address };
-
- return { send, receive: to };
-};
-
-export const constructLimitOrder = (
- send: TokenAmount,
- receive: TokenAmount,
- pool: TokenPairPool,
- slippage: number,
- address: string
-): OrderDetails => {
- const receiveAmountWithSlippage = calculateAmountWithSlippage(
- BigInt(receive.amount),
- BigInt(slippage)
- );
-
- return {
- protocol: pool.protocol,
- poolId: pool.poolId,
- send: send,
- receive: {
- address: receive.address,
- amount: receiveAmountWithSlippage.toString(),
- },
- walletAddress: address,
- };
-};
-
-export const constructMarketOrder = (
- send: TokenAmount,
- receive: TokenAddress,
- pools: TokenPairPool[],
- slippage: number,
- address: string
-): OrderDetails | undefined => {
- if (pools?.length === 0) {
- return undefined;
- }
-
- const findBestOrder = (
- order: OrderDetails | undefined,
- pool: TokenPairPool
- ): OrderDetails => {
- const tokenPair = calculateAmountsGivenInput(pool, send);
-
- const receiveAmount = BigInt(tokenPair.receive.amount);
- const receiveAmountWithSlippage = calculateAmountWithSlippage(
- receiveAmount,
- BigInt(slippage)
- );
-
- const newOrder: OrderDetails = {
- protocol: pool.protocol,
- poolId: pool.poolId,
- send: tokenPair.send,
- receive: {
- address: receive,
- amount: receiveAmountWithSlippage.toString(),
- },
- walletAddress: address,
- };
-
- if (
- order === undefined ||
- BigInt(order.receive.amount) < BigInt(newOrder.receive.amount)
- ) {
- return newOrder;
- }
-
- return order;
- };
-
- return pools.reduce(findBestOrder, undefined);
-};
-
-const calculateAmountWithSlippage = (
- amount: bigint,
- slippage: bigint
-): bigint => {
- const slippageAmount = ceilDivision(
- BigInt(1000) * slippage * amount,
- BigInt(100 * 1000)
- );
-
- return amount - slippageAmount;
-};
-
-function ceilDivision(a: bigint, b: bigint): bigint {
- return (a + b - BigInt(1)) / b;
-}
-
-function sameToken(a: TokenAddress, b: TokenAddress): boolean {
- return (
- a != null &&
- b != null &&
- a != undefined &&
- b != undefined &&
- a.policyId === b.policyId &&
- a.assetName === b.assetName
- );
-}
diff --git a/packages/swap/index.ts b/packages/swap/index.ts
index ef81038d16..1c7b7a0582 100644
--- a/packages/swap/index.ts
+++ b/packages/swap/index.ts
@@ -1 +1 @@
-export * from './dex';
+export * from './api/index';
diff --git a/packages/swap/openswap/index.ts b/packages/swap/openswap/index.ts
deleted file mode 100644
index 02d200648a..0000000000
--- a/packages/swap/openswap/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export * from './orders';
-export * from './pools';
-export * from './tokens';
diff --git a/packages/swap/openswap/orders.ts b/packages/swap/openswap/orders.ts
deleted file mode 100644
index eb2b2c321b..0000000000
--- a/packages/swap/openswap/orders.ts
+++ /dev/null
@@ -1,105 +0,0 @@
-import axios from "axios";
-
-export type Protocol = "minswap" | "sundaeswap" | "wingriders" | "muesliswap";
-
-export type Order = {
- address: string;
- protocol: Protocol;
- poolId?: string; // only required for SundaeSwap trades.
- sell: {
- policyId: string;
- assetName: string; // hexadecimal representation of token, i.e. "" for lovelace, "4d494c4b" for MILK.
- amount: string;
- };
- buy: {
- policyId: string;
- assetName: string; // hexadecimal representation of token, i.e. "" for lovelace, "4d494c4b" for MILK.
- amount: string;
- };
-};
-
-export type OpenOrder = {
- provider: Protocol;
- from: {
- amount: string;
- token: string;
- };
- to: {
- amount: string;
- token: string;
- };
- deposit: string;
- utxo: string;
-};
-
-const client = axios.create({
- baseURL: "https://aggregator.muesliswap.com",
- headers: {
- Accept: "application/json",
- "Content-Type": "application/json",
- },
-});
-
-/**
- * @param orderUTxO order UTxO from the smart contract to cancel. e.g. "txhash#0"
- * @param collateralUTxOs collateral UTxOs to use for canceling the order in cbor format.
- * @param walletAddress address of the wallet that owns the order in cbor format.
- * @returns an unsigned transaction to cancel the order.
- */
-export const cancelOrder = async (
- orderUTxO: string,
- collateralUTxOs: string,
- walletAddress: string
-) => {
- const response = await client.get(
- `/cancelSwapTransaction?wallet=${walletAddress}&utxo=${orderUTxO}&collateralUtxo=${collateralUTxOs}`
- );
-
- if (response.status !== 200) {
- throw new Error("Failed to construct cancel swap transaction", {
- cause: response.data,
- });
- }
-
- return {
- tx: response.data.cbor,
- };
-};
-
-/**
- * @param order the order to construct the datum for and the address to send the order to.
- * @returns the order datum, order datum hash, and address to send the order to.
- */
-export const createOrder = async (order: Order) => {
- const response = await client.get(
- `/constructSwapDatum?walletAddr=${order.address}&protocol=${order.protocol}&poolId=${order.poolId}&sellTokenPolicyID=${order.sell.policyId}&sellTokenNameHex=${order.sell.assetName}&sellAmount=${order.sell.amount}&buyTokenPolicyID=${order.buy.policyId}&buyTokenNameHex=${order.buy.assetName}&buyAmount=${order.buy.amount}`
- );
-
- if (response.status !== 200) {
- throw new Error("Failed to construct swap datum", { cause: response.data });
- }
-
- return {
- hash: response.data.hash,
- datum: response.data.datum,
- address: response.data.address,
- };
-};
-
-/**
- * @param stakeKeyHash the stake key hash of the wallet to get orders for.
- * @returns all unfufilled orders for the given stake key hash.
- */
-export const getOrders = async (stakeKeyHash: string): Promise => {
- const response = await client.get(`/all?stake-key-hash=${stakeKeyHash}`, {
- baseURL: "https://onchain2.muesliswap.com/orders",
- });
-
- if (response.status !== 200) {
- throw new Error(`Failed to get orders for ${stakeKeyHash}`, {
- cause: response.data,
- });
- }
-
- return response.data;
-};
diff --git a/packages/swap/openswap/pools.ts b/packages/swap/openswap/pools.ts
deleted file mode 100644
index c245908fb3..0000000000
--- a/packages/swap/openswap/pools.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import axios from 'axios';
-
-export type Pool = {
- provider: 'minswap' | 'sundaeswap' | 'wingriders' | 'muesliswap_v1' | 'muesliswap_v2' | 'muesliswap_v3',
- fee: string, // % pool liquidity provider fee, usually 0.3.
- tokenA: {
- amount: string, // amount of tokenA in the pool, without decimals.
- token: string, // hexadecimal representation of tokenA, i.e. "." for lovelace, "8a1cfae21368b8bebbbed9800fec304e95cce39a2a57dc35e2e3ebaa.4d494c4b" for MILK.
- },
- tokenB: {
- amount: string, // amount of tokenB in the pool, without decimals.
- token: string, // hexadecimal representation of tokenB, i.e. "." for lovelace, "8a1cfae21368b8bebbbed9800fec304e95cce39a2a57dc35e2e3ebaa.4d494c4b" for MILK.
- },
- price: number, // float, current price in tokenA / tokenB according to the pool, NOT SUITABLE for price calculations, just for display purposes, i.e. 0.9097362621640215.
- batcherFee: {
- amount: string, // amount of fee taken by protocol batchers, in lovelace.
- token: '.',
- },
- deposit: number, // amount of deposit / minUTxO required by protocol, returned to user, in lovelace.
- utxo: string, // txhash#txindex of latest transaction involving this pool.
- poolId: string, // identifier of the pool across platforms.
- timestamp: string, // latest update of this pool in UTC, i.e. 2023-05-23 06:13:26.
- lpToken: {
- amount: string, // amount of lpToken minted by the pool, without decimals.
- token: string, // hexadecimal representation of lpToken,
- },
-};
-
-const client = axios.create({
- baseURL: 'https://onchain2.muesliswap.com/pools',
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json'
- }
-});
-
-/**
- * @param tokenA the token to swap from.
- * @param tokenB the token to swap to.
- * @returns a list of pools for the given token pair.
- */
-export const getPools = async (
- tokenA: { policyId: string, assetName: string },
- tokenB: { policyId: string, assetName: string },
-): Promise => {
- const response = await client
- .get(`/pair?policy-id1=${tokenA.policyId}&tokenname1=${tokenA.assetName}&policy-id2=${tokenB.policyId}&tokenname-hex2=${tokenB.assetName}`);
-
- if (response.status !== 200) {
- throw new Error('Failed to fetch pools for token pair', { cause: response.data });
- }
-
- return response.data;
-}
diff --git a/packages/swap/openswap/tokens.ts b/packages/swap/openswap/tokens.ts
deleted file mode 100644
index 606af368cf..0000000000
--- a/packages/swap/openswap/tokens.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-import axios from 'axios';
-
-export type Token = {
- info: {
- supply: {
- total: string, // total circulating supply of the token, without decimals.
- circulating: string | null, // if set the circulating supply of the token, if null the amount in circulation is unknown.
- },
- status: 'verified' | 'unverified' | 'scam' | 'outdated',
- address: {
- policyId: string, // policy id of the token.
- name: string, // hexadecimal representation of token name.
- },
- symbol: string, // shorthand token symbol.
- image?: string, // http link to the token image.
- website: string,
- description: string,
- decimalPlaces: number, // number of decimal places of the token, i.e. 6 for ADA and 0 for MILK.
- categories: string[], // encoding categories as ids.
- },
- price: {
- volume: {
- base: number, // float, trading volume 24h in base currency (e.g. ADA).
- quote: number, // float, trading volume 24h in quote currency.
- },
- volumeChange: {
- base: number, // float, percent change of trading volume in comparison to previous 24h.
- quote: number, // float, percent change of trading volume in comparison to previous 24h.
- },
- price: number, // live trading price in base currency (e.g. ADA).
- askPrice: number, // lowest ask price in base currency (e.g. ADA).
- bidPrice: number, // highest bid price in base currency (e.g. ADA).
- priceChange: {
- '24h': number, // float, price change last 24 hours.
- '7d': number,// float, price change last 7 days.
- },
- quoteDecimalPlaces: number, // decimal places of quote token.
- baseDecimalPlaces: number, // decimal places of base token.
- price10d: number[], //float, prices of this tokens averaged for the last 10 days, in chronological order i.e.oldest first.
- }
-};
-
-const client = axios.create({
- baseURL: 'https://api.muesliswap.com',
- headers: {
- 'Accept': 'application/json',
- 'Content-Type': 'application/json'
- }
-});
-
-/**
- * @param policyId the policy id of the base token to calculate the price for in hexadecimal representation.
- * @param assetName the asset name of the base token to calculate the price for in hexadecimal representation.
- * @returns a list of tokens supported by MuesliSwap.
- */
-export const getTokens = async (policyId = '', assetName = ''): Promise => {
- const response = await client
- .get(`/list?base-policy-id=${policyId}&base-tokenname=${assetName}`);
-
- if (response.status !== 200) {
- throw new Error('Failed to fetch tokens', { cause: response.data });
- }
-
- return response.data;
-}
diff --git a/packages/swap/package.json b/packages/swap/package.json
index c1c2dedb3f..17b09a7ff6 100644
--- a/packages/swap/package.json
+++ b/packages/swap/package.json
@@ -1,18 +1,18 @@
{
"name": "@yoroi/swap",
"version": "0.0.1",
- "main": "dist/index.js",
- "source": "index",
"repository": {
"type": "github",
"url": "https://github.com/Emurgo/yoroi.git",
"directory": "packages/swap"
},
- "author": "EMURGO",
"license": "Apache-2.0",
+ "author": "EMURGO",
+ "main": "dist/index.js",
+ "source": "index",
"scripts": {
- "clean": "rm -rf dist",
"build": "tsc",
+ "clean": "rm -rf dist",
"test": "vitest"
},
"dependencies": {
diff --git a/packages/swap/tsconfig.json b/packages/swap/tsconfig.json
index 3e19a099b4..abb0ecde2a 100644
--- a/packages/swap/tsconfig.json
+++ b/packages/swap/tsconfig.json
@@ -39,7 +39,7 @@
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
- // "resolveJsonModule": true, /* Enable importing .json files. */
+ "resolveJsonModule": true /* Enable importing .json files. */,
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
diff --git a/packages/types/src/balance/token.ts b/packages/types/src/balance/token.ts
new file mode 100644
index 0000000000..cc7b7be2a2
--- /dev/null
+++ b/packages/types/src/balance/token.ts
@@ -0,0 +1,73 @@
+type BalanceTokenInfo = {
+ kind: 'ft' | 'nft'
+
+ id: string
+ fingerprint: string // fingerprint is temporary since we can't use it as id for now
+ group: string // for cardano policy id
+ name: string // for cardano asset name
+
+ description: string | undefined
+ image: string | undefined // link to image
+ icon: string | undefined // base64 encoded
+
+ decimals: number | undefined
+ symbol: string | undefined // shorthand as monetary i.e Ω
+ ticker: string | undefined // shorthand as token e.g. ADA
+
+ // metatada should be used only for NFT gallery
+ metadatas: Meta
+}
+
+export type BalanceCardanoMetadatas = {
+ mintNft?: NftMetadata
+ mintFt?: FtMetadata
+ tokenRegistry?: FtMetadata
+}
+
+type FtMetadata = {
+ description: string | Array | undefined
+ icon: string | Array | undefined
+ decimals: number | undefined
+ ticker: string | undefined
+ url: string | undefined
+ version: string | undefined
+}
+
+type NftMetadata = unknown
+
+type BalanceTokenPrice = {
+ volume: {
+ base: number // float, trading volume 24h in base currency (e.g. ADA).
+ quote: number // float, trading volume 24h in quote currency.
+ }
+ volumeChange: {
+ base: number // float, percent change of trading volume in comparison to previous 24h.
+ quote: number // float, percent change of trading volume in comparison to previous 24h.
+ }
+ price: number // live trading price in base currency (e.g. ADA).
+ askPrice: number // lowest ask price in base currency (e.g. ADA).
+ bidPrice: number // highest bid price in base currency (e.g. ADA).
+ priceChange: {
+ '24h': number // float, price change last 24 hours.
+ '7d': number // float, price change last 7 days.
+ }
+ quoteDecimalPlaces: number // decimal places of quote token.
+ baseDecimalPlaces: number // decimal places of base token.
+ price10d: number[] //float, prices of this tokens averaged for the last 10 days, in chronological order i.e.oldest first.
+}
+
+export type BalanceToken = {
+ info: BalanceTokenInfo
+ price: BalanceTokenPrice
+}
+
+export type BalanceQuantity = `${number}`
+
+export type BalanceAmounts = {
+ [tokenId: string]: BalanceQuantity
+}
+
+export type BalanceAmount = {
+ tokenId: string
+ quantity: BalanceQuantity
+}
diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts
index 63848ac1d9..d585b3c21a 100644
--- a/packages/types/src/index.ts
+++ b/packages/types/src/index.ts
@@ -1,6 +1,27 @@
+import {
+ BalanceAmount,
+ BalanceAmounts,
+ BalanceCardanoMetadatas,
+ BalanceQuantity,
+ BalanceToken,
+} from './balance/token'
import {MetricsModule, MetricsFactoryOptions} from './metrics/module'
import {MetricsStorage} from './metrics/storage'
import {MetricsTrack} from './metrics/track'
+import {
+ SwapFactoryOptions,
+ SwapOrderType,
+ SwapProtocol,
+ SwapOpenOrder,
+ SwapNetwork,
+ SwapPool,
+ SwapTokenInfo,
+ SwapBaseTokenInfo,
+ SwapCreateOrderResponse,
+ SwapCreateOrderData,
+ SwapApi,
+} from './swap/module'
+import {SwapStorage} from './swap/storage'
export namespace Metrics {
export type Module = MetricsModule
@@ -10,3 +31,32 @@ export namespace Metrics {
export type Storage = MetricsStorage
}
+
+export namespace Swap {
+ export type FactoryOptions = SwapFactoryOptions
+
+ export type CreateOrderData = SwapCreateOrderData
+ export type CreateOrderResponse = SwapCreateOrderResponse
+ export type OrderType = SwapOrderType
+ export type Protocol = SwapProtocol
+ export type Network = SwapNetwork
+ export type OpenOrder = SwapOpenOrder
+ export type Pool = SwapPool
+ export type TokenInfo = SwapTokenInfo
+ export type BaseTokenInfo = SwapBaseTokenInfo
+ export interface Api extends SwapApi {}
+
+ export type Storage = SwapStorage
+}
+
+export namespace Balance {
+ export type Token = BalanceToken
+ export type TokenInfo = BalanceToken['info']
+ export type TokenPrice = BalanceToken['price']
+
+ export type CardanoMetadatas = BalanceCardanoMetadatas
+
+ export type Quantity = BalanceQuantity
+ export type Amount = BalanceAmount
+ export type Amounts = BalanceAmounts
+}
diff --git a/packages/types/src/metrics/swap.ts b/packages/types/src/metrics/swap.ts
new file mode 100644
index 0000000000..24f79a8ad3
--- /dev/null
+++ b/packages/types/src/metrics/swap.ts
@@ -0,0 +1,86 @@
+import {SwapOrderType} from 'src/swap/module'
+import {MetricsTrackProperties} from './properties'
+
+export type MetricsSwapEvent =
+ | 'swap_asset_from_changed'
+ | 'swap_asset_to_changed'
+ | 'swap_cancelation_settled'
+ | 'swap_cancelation_submitted'
+ | 'swap_initiated'
+ | 'swap_order_requested'
+ | 'swap_order_selected'
+ | 'swap_order_settled'
+ | 'swap_order_submitted'
+ | 'swap_pool_changed'
+ | 'swap_slippage_changed'
+
+type MetricsSwapAssetFromChanged = MetricsTrackProperties<
+ MetricsSwapEvent,
+ 'swap_asset_from_changed'
+>
+
+type MetricsSwapAssetToChanged = MetricsTrackProperties<
+ MetricsSwapEvent,
+ 'swap_asset_to_changed'
+>
+
+type MetricsSwapCancelationSettled = MetricsTrackProperties<
+ MetricsSwapEvent,
+ 'swap_cancelation_settled'
+>
+
+type MetricsSwapCancelationSubmitted = MetricsTrackProperties<
+ MetricsSwapEvent,
+ 'swap_cancelation_submitted'
+>
+
+type MetricsSwapInitiated = MetricsTrackProperties<
+ MetricsSwapEvent,
+ 'swap_initiated',
+ {
+ orderType: SwapOrderType
+ }
+>
+
+type MetricsSwapOrderRequested = MetricsTrackProperties<
+ MetricsSwapEvent,
+ 'swap_order_requested'
+>
+
+type MetricsSwapOrderSelected = MetricsTrackProperties<
+ MetricsSwapEvent,
+ 'swap_order_selected'
+>
+
+type MetricsSwapOrderSettled = MetricsTrackProperties<
+ MetricsSwapEvent,
+ 'swap_order_settled'
+>
+
+type MetricsSwapOrderSubmitted = MetricsTrackProperties<
+ MetricsSwapEvent,
+ 'swap_order_submitted'
+>
+
+type MetricsSwapPoolChanged = MetricsTrackProperties<
+ MetricsSwapEvent,
+ 'swap_pool_changed'
+>
+
+type MetricsSwapSlippageChanged = MetricsTrackProperties<
+ MetricsSwapEvent,
+ 'swap_slippage_changed'
+>
+
+export type MetricsSwapTrack =
+ | MetricsSwapAssetFromChanged
+ | MetricsSwapAssetToChanged
+ | MetricsSwapCancelationSettled
+ | MetricsSwapCancelationSubmitted
+ | MetricsSwapInitiated
+ | MetricsSwapOrderRequested
+ | MetricsSwapOrderSelected
+ | MetricsSwapOrderSettled
+ | MetricsSwapOrderSubmitted
+ | MetricsSwapPoolChanged
+ | MetricsSwapSlippageChanged
diff --git a/packages/types/src/swap/module.ts b/packages/types/src/swap/module.ts
new file mode 100644
index 0000000000..8fb3de4a9d
--- /dev/null
+++ b/packages/types/src/swap/module.ts
@@ -0,0 +1,205 @@
+import {BalanceAmount, BalanceToken} from '../balance/token'
+
+export type SwapOrderType = 'market' | 'limit'
+
+export type SwapProtocol =
+ | 'minswap'
+ | 'sundaeswap'
+ | 'wingriders'
+ | 'muesliswap'
+
+export type SwapCreateOrderData = {
+ address: string
+ protocol: SwapProtocol
+ poolId?: string // Only required for SundaeSwap trades.
+ sell: {
+ policyId: string
+ assetName: string // Hexadecimal representation of token, i.e. "" for lovelace, "4d494c4b" for MILK.
+ amount: string
+ }
+ buy: {
+ policyId: string
+ assetName: string
+ amount: string
+ }
+}
+
+export type SwapCreateOrderResponse = Record<
+ 'datumHash' | 'datum' | 'contractAddress',
+ string
+>
+
+// todo: add full type def.
+export type SwapOpenOrder = {
+ provider: SwapProtocol
+ from: {
+ amount: string
+ token: string
+ }
+ to: {
+ amount: string
+ token: string
+ }
+ deposit: string
+ utxo: string
+}
+
+export type SwapNetwork = 'mainnet' | 'preprod'
+
+export type SwapFactoryOptions = {
+ network: SwapNetwork
+ stakingKey: string
+}
+
+export type SwapOrderCreateData = {
+ amounts: {
+ sell: BalanceAmount
+ buy: BalanceAmount
+ }
+ address: string
+ slippage: number
+} & (
+ | {
+ protocol: Omit
+ poolId: string | undefined // only required for SundaeSwap trades.
+ }
+ | {
+ protocol: 'sundaeswap'
+ poolId: string
+ }
+)
+
+export type SwapOrderCancelData = {
+ utxos: {
+ order: string
+ collaterals: Array
+ }
+ address: string
+}
+
+export type SwapOrderDatum = {
+ contractAddress: string
+ datumHash: string
+ datum: string
+}
+
+export type SwapPool = {
+ provider:
+ | 'minswap'
+ | 'sundaeswap'
+ | 'wingriders'
+ | 'muesliswap_v1'
+ | 'muesliswap_v2'
+ | 'muesliswap_v3'
+ fee: string // % pool liquidity provider fee, usually 0.3.
+ tokenA: {
+ amount: string // amount of tokenA in the pool, without decimals.
+ token: string // hexadecimal representation of tokenA, i.e. "." for lovelace, "8a1cfae21368b8bebbbed9800fec304e95cce39a2a57dc35e2e3ebaa.4d494c4b" for MILK.
+ }
+ tokenB: {
+ amount: string // amount of tokenB in the pool, without decimals.
+ token: string // hexadecimal representation of tokenB, i.e. "." for lovelace, "8a1cfae21368b8bebbbed9800fec304e95cce39a2a57dc35e2e3ebaa.4d494c4b" for MILK.
+ }
+ price: number // float, current price in tokenA / tokenB according to the pool, NOT SUITABLE for price calculations, just for display purposes, i.e. 0.9097362621640215.
+ batcherFee: {
+ amount: string // amount of fee taken by protocol batchers, in lovelace.
+ token: '.'
+ }
+ deposit: number // amount of deposit / minUTxO required by protocol, returned to user, in lovelace.
+ utxo: string // txhash#txindex of latest transaction involving this pool.
+ poolId: string // identifier of the pool across platforms.
+ timestamp: string // latest update of this pool in UTC, i.e. 2023-05-23 06:13:26.
+ lpToken: {
+ amount: string // amount of lpToken minted by the pool, without decimals.
+ token: string // hexadecimal representation of lpToken,
+ }
+}
+
+export type SwapTokenInfo = {
+ info: {
+ supply: {
+ total: string // total circulating supply of the token, without decimals.
+ circulating: string | null // if set the circulating supply of the token, if null the amount in circulation is unknown.
+ }
+ status: 'verified' | 'unverified' | 'scam' | 'outdated'
+ address: {
+ policyId: string // policy id of the token.
+ name: string // hexadecimal representation of token name.
+ }
+ symbol: string // shorthand token symbol.
+ image?: string // http link to the token image.
+ website: string
+ description: string
+ decimalPlaces: number // number of decimal places of the token, i.e. 6 for ADA and 0 for MILK.
+ categories: string[] // encoding categories as ids.
+ }
+ price: {
+ volume: {
+ base: number // float, trading volume 24h in base currency (e.g. ADA).
+ quote: number // float, trading volume 24h in quote currency.
+ }
+ volumeChange: {
+ base: number // float, percent change of trading volume in comparison to previous 24h.
+ quote: number // float, percent change of trading volume in comparison to previous 24h.
+ }
+ price: number // live trading price in base currency (e.g. ADA).
+ askPrice: number // lowest ask price in base currency (e.g. ADA).
+ bidPrice: number // highest bid price in base currency (e.g. ADA).
+ priceChange: {
+ '24h': number // float, price change last 24 hours.
+ '7d': number // float, price change last 7 days.
+ }
+ quoteDecimalPlaces: number // decimal places of quote token.
+ baseDecimalPlaces: number // decimal places of base token.
+ price10d: number[] //float, prices of this tokens averaged for the last 10 days, in chronological order i.e.oldest first.
+ }
+}
+
+// todo: choose better name
+export type SwapBaseTokenInfo =
+ | {policyId: string; assetName: string}
+ | {policyId: string; assetNameHex: string}
+
+export interface SwapApi {
+ createOrder(order: SwapCreateOrderData): Promise
+ cancelOrder(
+ orderUTxO: string,
+ collateralUTxO: string,
+ walletAddress: string,
+ ): Promise
+ getOrders(stakeKeyHash: string): Promise
+ getPools(
+ tokenA: SwapBaseTokenInfo,
+ tokenB: SwapBaseTokenInfo,
+ ): Promise
+ getTokens(policyId?: string, assetName?: string): Promise
+}
+
+export type SwapModule = {
+ orders: {
+ prepare: (order: SwapOrderCreateData) => Promise
+ create: (order: SwapOrderCreateData) => Promise
+ cancel: (order: SwapOrderCancelData) => Promise
+ list: {
+ byStatusOpen: () => Promise>
+ }
+ }
+ pairs: {
+ list: {
+ byToken: (
+ baseTokenId: BalanceToken['info']['id'],
+ ) => Promise>
+ }
+ }
+ pools: {
+ list: {
+ byPair: ({
+ from,
+ to,
+ }: {
+ from: BalanceToken['info']['id']
+ to: BalanceToken['info']['id']
+ }) => Promise>
+ }
+ }
+}
diff --git a/packages/types/src/swap/storage.ts b/packages/types/src/swap/storage.ts
new file mode 100644
index 0000000000..7f8007e0fc
--- /dev/null
+++ b/packages/types/src/swap/storage.ts
@@ -0,0 +1,9 @@
+export type SwapStorage = {
+ slippage: {
+ read(): Promise
+ remove(): Promise
+ save(slippage: number): Promise
+ }
+
+ reset(): Promise<[() => Promise]>
+}
diff --git a/yarn.lock b/yarn.lock
index 8cdccc693c..664a173840 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2020,11 +2020,31 @@
minimatch "^3.1.2"
strip-json-comments "^3.1.1"
+"@eslint/eslintrc@^2.1.0":
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.0.tgz#82256f164cc9e0b59669efc19d57f8092706841d"
+ integrity sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==
+ dependencies:
+ ajv "^6.12.4"
+ debug "^4.3.2"
+ espree "^9.6.0"
+ globals "^13.19.0"
+ ignore "^5.2.0"
+ import-fresh "^3.2.1"
+ js-yaml "^4.1.0"
+ minimatch "^3.1.2"
+ strip-json-comments "^3.1.1"
+
"@eslint/js@8.42.0":
version "8.42.0"
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.42.0.tgz#484a1d638de2911e6f5a30c12f49c7e4a3270fb6"
integrity sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==
+"@eslint/js@8.44.0":
+ version "8.44.0"
+ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.44.0.tgz#961a5903c74139390478bdc808bcde3fc45ab7af"
+ integrity sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==
+
"@ethersproject/abi@^5.0.1":
version "5.7.0"
resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449"
@@ -4628,6 +4648,72 @@
regenerator-runtime "^0.13.7"
resolve-from "^5.0.0"
+"@swc/core-darwin-arm64@1.3.69":
+ version "1.3.69"
+ resolved "https://registry.yarnpkg.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.3.69.tgz#e22032471244ec80c22bee8efc2100e456bb9488"
+ integrity sha512-IjZTf12zIPWkV3D7toaLDoJPSkLhQ4fDH8G6/yCJUI27cBFOI3L8LXqptYmISoN5yYdrcnNpdqdapD09JPuNJg==
+
+"@swc/core-darwin-x64@1.3.69":
+ version "1.3.69"
+ resolved "https://registry.yarnpkg.com/@swc/core-darwin-x64/-/core-darwin-x64-1.3.69.tgz#4c2034ba409b9e061b9e8ad56a762b8bb7815f18"
+ integrity sha512-/wBO0Rn5oS5dJI/L9kJRkPAdksVwl5H9nleW/NM3A40N98VV8T7h/i1nO051mxIjq0R6qXVGOWFbBoLrPYucJg==
+
+"@swc/core-linux-arm-gnueabihf@1.3.69":
+ version "1.3.69"
+ resolved "https://registry.yarnpkg.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.3.69.tgz#3d89053b6de2fd8553ce5c5808251246f2870e1e"
+ integrity sha512-NShCjMv6Xn8ckMKBRqmprXvUF14+jXY0TcNKXwjYErzoIUFOnG72M36HxT4QEeAtKZ4Eg4CZFE4zlJ27fDp1gg==
+
+"@swc/core-linux-arm64-gnu@1.3.69":
+ version "1.3.69"
+ resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.3.69.tgz#df12d3befc5e86555ee561202dc9c279d8865bf3"
+ integrity sha512-VRPOJj4idopSHIj1bOVXX0SgaB18R8yZNunb7eXS5ZcjVxAcdvqyIz3RdQX1zaJFCGzcdPLzBRP32DZWWGE8Ng==
+
+"@swc/core-linux-arm64-musl@1.3.69":
+ version "1.3.69"
+ resolved "https://registry.yarnpkg.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.3.69.tgz#8372222a3298fdd0bd18da564a3009614a6c0920"
+ integrity sha512-QxeSiZqo5x1X8vq8oUWLibq+IZJcxl9vy0sLUmzdjF2b/Z+qxKP3gutxnb2tzJaHqPVBbEZaILERIGy1qWdumQ==
+
+"@swc/core-linux-x64-gnu@1.3.69":
+ version "1.3.69"
+ resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.3.69.tgz#6879057d28f261b051fac52daca6c256f3a7fb7d"
+ integrity sha512-b+DUlVxYox3BwD3PyTwhLvqtu6TYZtW+S6O0FnttH11o4skHN0XyJ/cUZSI0X2biSmfDsizRDUt1PWPFM+F7SA==
+
+"@swc/core-linux-x64-musl@1.3.69":
+ version "1.3.69"
+ resolved "https://registry.yarnpkg.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.3.69.tgz#bf4f9a74156524211472bb713d34f0bb7265669f"
+ integrity sha512-QXjsI+f8n9XPZHUvmGgkABpzN4M9kdSbhqBOZmv3o0AsDGNCA4uVowQqgZoPFAqlJTpwHeDmrv5sQ13HN+LOGw==
+
+"@swc/core-win32-arm64-msvc@1.3.69":
+ version "1.3.69"
+ resolved "https://registry.yarnpkg.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.3.69.tgz#8242e4a406d11f0e078da39cf9962695e6b73d2d"
+ integrity sha512-wn7A8Ws1fyviuCUB2Vg6IotiZeuqiO1Mz3d+YDae2EYyNpj1kNHvjBip8GHkfGzZG+jVrvG6NHsDo0KO/pGb8A==
+
+"@swc/core-win32-ia32-msvc@1.3.69":
+ version "1.3.69"
+ resolved "https://registry.yarnpkg.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.3.69.tgz#68dbd41e200e5db71aa6644d793acff3607862f0"
+ integrity sha512-LsFBXtXqxEcVaaOGEZ9X3qdMzobVoJqKv8DnksuDsWcBk+9WCeTz2u/iB+7yZ2HGuPXkCqTRqhFo6FX9aC00kQ==
+
+"@swc/core-win32-x64-msvc@1.3.69":
+ version "1.3.69"
+ resolved "https://registry.yarnpkg.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.3.69.tgz#304e1050d59bae21215a15839b05668d48a92837"
+ integrity sha512-ieBscU0gUgKjaseFI07tAaGqHvKyweNknPeSYEZOasVZUczhD6fK2GRnVREhv2RB2qdKC/VGFBsgRDMgzq1VLw==
+
+"@swc/core@^1.3.61":
+ version "1.3.69"
+ resolved "https://registry.yarnpkg.com/@swc/core/-/core-1.3.69.tgz#b4a41e84de11832c233fbe714c6e412d8404bab0"
+ integrity sha512-Khc/DE9D5+2tYTHgAIp5DZARbs8kldWg3b0Jp6l8FQLjelcLFmlQWSwKhVZrgv4oIbgZydIp8jInsvTalMHqnQ==
+ optionalDependencies:
+ "@swc/core-darwin-arm64" "1.3.69"
+ "@swc/core-darwin-x64" "1.3.69"
+ "@swc/core-linux-arm-gnueabihf" "1.3.69"
+ "@swc/core-linux-arm64-gnu" "1.3.69"
+ "@swc/core-linux-arm64-musl" "1.3.69"
+ "@swc/core-linux-x64-gnu" "1.3.69"
+ "@swc/core-linux-x64-musl" "1.3.69"
+ "@swc/core-win32-arm64-msvc" "1.3.69"
+ "@swc/core-win32-ia32-msvc" "1.3.69"
+ "@swc/core-win32-x64-msvc" "1.3.69"
+
"@szmarczak/http-timer@^4.0.5":
version "4.0.6"
resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807"
@@ -5025,6 +5111,13 @@
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
+"@types/react-dom@^18.2.6":
+ version "18.2.7"
+ resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.7.tgz#67222a08c0a6ae0a0da33c3532348277c70abb63"
+ integrity sha512-GRaAEriuT4zp9N4p1i8BDBYmEyfo+xQ3yHjJU4eiK5NDa1RmUZG+unZABUTK4/Ox/M+GaHwb6Ow8rUITrtjszA==
+ dependencies:
+ "@types/react" "*"
+
"@types/react-native-background-timer@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@types/react-native-background-timer/-/react-native-background-timer-2.0.0.tgz#c44c57f8fbca9d9d5521fdd72a8f55232b79381e"
@@ -5062,6 +5155,15 @@
"@types/scheduler" "*"
csstype "^3.0.2"
+"@types/react@^18.2.14":
+ version "18.2.15"
+ resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.15.tgz#14792b35df676c20ec3cf595b262f8c615a73066"
+ integrity sha512-oEjE7TQt1fFTFSbf8kkNuc798ahTUzn3Le67/PWjE8MAfYAD/qB7O8hSTcromLFqHCt9bcdOg5GXMokzTjJ5SA==
+ dependencies:
+ "@types/prop-types" "*"
+ "@types/scheduler" "*"
+ csstype "^3.0.2"
+
"@types/recursive-readdir@^2.2.0":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@types/recursive-readdir/-/recursive-readdir-2.2.1.tgz#330f5ec0b73e8aeaf267a6e056884e393f3543a3"
@@ -5233,6 +5335,16 @@
"@typescript-eslint/typescript-estree" "5.59.9"
debug "^4.3.4"
+"@typescript-eslint/parser@^5.61.0":
+ version "5.62.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.62.0.tgz#1b63d082d849a2fcae8a569248fbe2ee1b8a56c7"
+ integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==
+ dependencies:
+ "@typescript-eslint/scope-manager" "5.62.0"
+ "@typescript-eslint/types" "5.62.0"
+ "@typescript-eslint/typescript-estree" "5.62.0"
+ debug "^4.3.4"
+
"@typescript-eslint/scope-manager@5.59.9":
version "5.59.9"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.59.9.tgz#eadce1f2733389cdb58c49770192c0f95470d2f4"
@@ -5241,6 +5353,14 @@
"@typescript-eslint/types" "5.59.9"
"@typescript-eslint/visitor-keys" "5.59.9"
+"@typescript-eslint/scope-manager@5.62.0":
+ version "5.62.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz#d9457ccc6a0b8d6b37d0eb252a23022478c5460c"
+ integrity sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==
+ dependencies:
+ "@typescript-eslint/types" "5.62.0"
+ "@typescript-eslint/visitor-keys" "5.62.0"
+
"@typescript-eslint/type-utils@5.59.9":
version "5.59.9"
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.59.9.tgz#53bfaae2e901e6ac637ab0536d1754dfef4dafc2"
@@ -5251,6 +5371,16 @@
debug "^4.3.4"
tsutils "^3.21.0"
+"@typescript-eslint/type-utils@5.62.0":
+ version "5.62.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz#286f0389c41681376cdad96b309cedd17d70346a"
+ integrity sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==
+ dependencies:
+ "@typescript-eslint/typescript-estree" "5.62.0"
+ "@typescript-eslint/utils" "5.62.0"
+ debug "^4.3.4"
+ tsutils "^3.21.0"
+
"@typescript-eslint/types@5.59.0":
version "5.59.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.0.tgz#3fcdac7dbf923ec5251545acdd9f1d42d7c4fe32"
@@ -5261,6 +5391,11 @@
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.59.9.tgz#3b4e7ae63718ce1b966e0ae620adc4099a6dcc52"
integrity sha512-uW8H5NRgTVneSVTfiCVffBb8AbwWSKg7qcA4Ot3JI3MPCJGsB4Db4BhvAODIIYE5mNj7Q+VJkK7JxmRhk2Lyjw==
+"@typescript-eslint/types@5.62.0":
+ version "5.62.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.62.0.tgz#258607e60effa309f067608931c3df6fed41fd2f"
+ integrity sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==
+
"@typescript-eslint/typescript-estree@5.59.0":
version "5.59.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.0.tgz#8869156ee1dcfc5a95be3ed0e2809969ea28e965"
@@ -5301,6 +5436,20 @@
eslint-scope "^5.1.1"
semver "^7.3.7"
+"@typescript-eslint/utils@5.62.0":
+ version "5.62.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.62.0.tgz#141e809c71636e4a75daa39faed2fb5f4b10df86"
+ integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==
+ dependencies:
+ "@eslint-community/eslint-utils" "^4.2.0"
+ "@types/json-schema" "^7.0.9"
+ "@types/semver" "^7.3.12"
+ "@typescript-eslint/scope-manager" "5.62.0"
+ "@typescript-eslint/types" "5.62.0"
+ "@typescript-eslint/typescript-estree" "5.62.0"
+ eslint-scope "^5.1.1"
+ semver "^7.3.7"
+
"@typescript-eslint/visitor-keys@5.59.0":
version "5.59.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.0.tgz#a59913f2bf0baeb61b5cfcb6135d3926c3854365"
@@ -5317,6 +5466,14 @@
"@typescript-eslint/types" "5.59.9"
eslint-visitor-keys "^3.3.0"
+"@typescript-eslint/visitor-keys@5.62.0":
+ version "5.62.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz#2174011917ce582875954ffe2f6912d5931e353e"
+ integrity sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==
+ dependencies:
+ "@typescript-eslint/types" "5.62.0"
+ eslint-visitor-keys "^3.3.0"
+
"@unstoppabledomains/resolution@6.0.3":
version "6.0.3"
resolved "https://registry.yarnpkg.com/@unstoppabledomains/resolution/-/resolution-6.0.3.tgz#a70888840c86a5918cb3134e7ac745e054c92aa5"
@@ -5354,6 +5511,13 @@
"@urql/core" ">=2.3.1"
wonka "^4.0.14"
+"@vitejs/plugin-react-swc@^3.3.2":
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/@vitejs/plugin-react-swc/-/plugin-react-swc-3.3.2.tgz#34a82c1728066f48a86dfecb2f15df60f89207fb"
+ integrity sha512-VJFWY5sfoZerQRvJrh518h3AcQt6f/yTuWn4/TRB+dqmYU0NX1qz7qM5Wfd+gOQqUzQW4gxKqKN3KpE/P3+zrA==
+ dependencies:
+ "@swc/core" "^1.3.61"
+
"@vitest/expect@0.31.1":
version "0.31.1"
resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-0.31.1.tgz#db8cb5a14a91167b948f377b9d29442229c73747"
@@ -10007,6 +10171,49 @@ eslint@^8.19.0, eslint@^8.4.1:
strip-json-comments "^3.1.0"
text-table "^0.2.0"
+eslint@^8.44.0:
+ version "8.45.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.45.0.tgz#bab660f90d18e1364352c0a6b7c6db8edb458b78"
+ integrity sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw==
+ dependencies:
+ "@eslint-community/eslint-utils" "^4.2.0"
+ "@eslint-community/regexpp" "^4.4.0"
+ "@eslint/eslintrc" "^2.1.0"
+ "@eslint/js" "8.44.0"
+ "@humanwhocodes/config-array" "^0.11.10"
+ "@humanwhocodes/module-importer" "^1.0.1"
+ "@nodelib/fs.walk" "^1.2.8"
+ ajv "^6.10.0"
+ chalk "^4.0.0"
+ cross-spawn "^7.0.2"
+ debug "^4.3.2"
+ doctrine "^3.0.0"
+ escape-string-regexp "^4.0.0"
+ eslint-scope "^7.2.0"
+ eslint-visitor-keys "^3.4.1"
+ espree "^9.6.0"
+ esquery "^1.4.2"
+ esutils "^2.0.2"
+ fast-deep-equal "^3.1.3"
+ file-entry-cache "^6.0.1"
+ find-up "^5.0.0"
+ glob-parent "^6.0.2"
+ globals "^13.19.0"
+ graphemer "^1.4.0"
+ ignore "^5.2.0"
+ imurmurhash "^0.1.4"
+ is-glob "^4.0.0"
+ is-path-inside "^3.0.3"
+ js-yaml "^4.1.0"
+ json-stable-stringify-without-jsonify "^1.0.1"
+ levn "^0.4.1"
+ lodash.merge "^4.6.2"
+ minimatch "^3.1.2"
+ natural-compare "^1.4.0"
+ optionator "^0.9.3"
+ strip-ansi "^6.0.1"
+ text-table "^0.2.0"
+
esm@^3.2.25:
version "3.2.25"
resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10"
@@ -10021,6 +10228,15 @@ espree@^9.5.2:
acorn-jsx "^5.3.2"
eslint-visitor-keys "^3.4.1"
+espree@^9.6.0:
+ version "9.6.1"
+ resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f"
+ integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==
+ dependencies:
+ acorn "^8.9.0"
+ acorn-jsx "^5.3.2"
+ eslint-visitor-keys "^3.4.1"
+
esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
@@ -11838,6 +12054,11 @@ image-size@^0.6.0:
resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.6.3.tgz#e7e5c65bb534bd7cdcedd6cb5166272a85f75fb2"
integrity sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA==
+immer@^10.0.2:
+ version "10.0.2"
+ resolved "https://registry.yarnpkg.com/immer/-/immer-10.0.2.tgz#11636c5b77acf529e059582d76faf338beb56141"
+ integrity sha512-Rx3CqeqQ19sxUtYV9CU911Vhy8/721wRFnJv3REVGWUmoAcIwzifTsdmJte/MV+0/XpM35LZdQMBGkRIoLPwQA==
+
import-fresh@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546"
@@ -15809,6 +16030,18 @@ optionator@^0.9.1:
type-check "^0.4.0"
word-wrap "^1.2.3"
+optionator@^0.9.3:
+ version "0.9.3"
+ resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64"
+ integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==
+ dependencies:
+ "@aashutoshrathi/word-wrap" "^1.2.3"
+ deep-is "^0.1.3"
+ fast-levenshtein "^2.0.6"
+ levn "^0.4.1"
+ prelude-ls "^1.2.1"
+ type-check "^0.4.0"
+
options-defaults@^2.0.39:
version "2.0.40"
resolved "https://registry.yarnpkg.com/options-defaults/-/options-defaults-2.0.40.tgz#18a1521519e68e561e2267a3dcb409f2fba81510"
@@ -17024,6 +17257,14 @@ react-dom@^16.13.1:
prop-types "^15.6.2"
scheduler "^0.19.1"
+react-dom@^18.2.0:
+ version "18.2.0"
+ resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
+ integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
+ dependencies:
+ loose-envify "^1.1.0"
+ scheduler "^0.23.0"
+
react-error-boundary@^3.1.0, react-error-boundary@^3.1.4:
version "3.1.4"
resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-3.1.4.tgz#255db92b23197108757a888b01e5b729919abde0"
@@ -17465,7 +17706,7 @@ react-transition-group@^4.3.0:
loose-envify "^1.4.0"
prop-types "^15.6.2"
-react@18.2.0:
+react@18.2.0, react@^18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
@@ -20086,6 +20327,11 @@ typescript@^4.5.2:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.3.tgz#8d84219244a6b40b6fb2b33cc1c062f715b9e826"
integrity sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==
+typescript@^5.0.2:
+ version "5.1.6"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.6.tgz#02f8ac202b6dad2c0dd5e0913745b47a37998274"
+ integrity sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==
+
typescript@~4.4.4:
version "4.4.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.4.tgz#2cd01a1a1f160704d3101fd5a58ff0f9fcb8030c"
@@ -20505,6 +20751,17 @@ vite-node@0.31.1:
optionalDependencies:
fsevents "~2.3.2"
+vite@^4.4.0:
+ version "4.4.4"
+ resolved "https://registry.yarnpkg.com/vite/-/vite-4.4.4.tgz#b76e6049c0e080cb54e735ad2d18287753752118"
+ integrity sha512-4mvsTxjkveWrKDJI70QmelfVqTm+ihFAb6+xf4sjEU2TmUCTlVX87tmg/QooPEMQb/lM9qGHT99ebqPziEd3wg==
+ dependencies:
+ esbuild "^0.18.10"
+ postcss "^8.4.25"
+ rollup "^3.25.2"
+ optionalDependencies:
+ fsevents "~2.3.2"
+
vitest@0.31.1:
version "0.31.1"
resolved "https://registry.yarnpkg.com/vitest/-/vitest-0.31.1.tgz#e3d1b68a44e76e24f142c1156fe9772ef603e52c"