Skip to content

Commit

Permalink
migrate staking
Browse files Browse the repository at this point in the history
  • Loading branch information
wolverineks committed Jun 24, 2022
1 parent 96a6364 commit 87dc473
Show file tree
Hide file tree
Showing 15 changed files with 151 additions and 137 deletions.
1 change: 1 addition & 0 deletions android/app/src/dev/res/xml/network_security_config.xml
Expand Up @@ -4,5 +4,6 @@
<domain includeSubdomains="false">localhost</domain>
<domain includeSubdomains="false">10.0.2.2</domain>
<domain includeSubdomains="false">10.0.3.2</domain>
<domain includeSubdomains="false">192.168.86.21</domain>
</domain-config>
</network-security-config>
4 changes: 2 additions & 2 deletions package.json
Expand Up @@ -82,7 +82,7 @@
"@emurgo/cip14-js": "2.0.0",
"@emurgo/cip4-js": "1.0.7",
"@emurgo/react-native-haskell-shelley": "3.1.3",
"@emurgo/yoroi-lib-mobile": "^0.5.0-alpha.31",
"@emurgo/yoroi-lib-mobile": "^0.7.0-alpha.35",
"@formatjs/intl": "^1.14.1",
"@formatjs/intl-datetimeformat": "^4.2.3",
"@formatjs/intl-getcanonicallocales": "^1.7.3",
Expand Down Expand Up @@ -182,7 +182,7 @@
"@babel/preset-react": "^7.16.7",
"@babel/runtime": "^7.15.4",
"@emurgo/cardano-serialization-lib-nodejs": "^9.1.0",
"@emurgo/yoroi-lib-core": "^0.5.0-alpha.31",
"@emurgo/yoroi-lib-core": "^0.7.0-alpha.35",
"@storybook/addon-actions": "5.3.14",
"@storybook/addon-knobs": "5.3.14",
"@storybook/addon-links": "5.3.14",
Expand Down
Expand Up @@ -8,7 +8,7 @@ import {Boundary, DangerousAction, PleaseWaitView, Spacer} from '../../component
import {useWithdrawalTx} from '../../hooks'
import globalMessages, {ledgerMessages} from '../../i18n/global-messages'
import KeyStore from '../../legacy/KeyStore'
import {serverStatusSelector, utxosSelector} from '../../legacy/selectors'
import {defaultNetworkAssetSelector, serverStatusSelector, utxosSelector} from '../../legacy/selectors'
import {theme} from '../../theme'
import {YoroiWallet} from '../../yoroi-wallets'
import {YoroiUnsignedTx} from '../../yoroi-wallets/types'
Expand Down Expand Up @@ -56,8 +56,9 @@ export const WithdrawalTxForm: React.FC<{
const [deregister, setDeregister] = React.useState<boolean>()
const utxos = useSelector(utxosSelector) || []
const serverStatus = useSelector(serverStatusSelector)
const defaultAsset = useSelector(defaultNetworkAssetSelector)
const {isLoading} = useWithdrawalTx(
{wallet, deregister, utxos, serverTime: serverStatus.serverTime},
{wallet, deregister, defaultAsset, utxos, serverTime: serverStatus.serverTime},
{
onSuccess: (withdrawalTx) => onDone(withdrawalTx),
enabled: deregister != null,
Expand Down
59 changes: 24 additions & 35 deletions src/Staking/DelegationConfirmation/DelegationConfirmation.tsx
@@ -1,8 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {BigNumber} from 'bignumber.js'
import React, {useEffect, useState} from 'react'
import {useIntl} from 'react-intl'
import {defineMessages} from 'react-intl'
import {defineMessages, useIntl} from 'react-intl'
import {ScrollView, StyleSheet, View, ViewProps} from 'react-native'
import {useSelector} from 'react-redux'

Expand All @@ -17,20 +16,20 @@ import {useParams, useWalletNavigation} from '../../navigation'
import {useSelectedWallet} from '../../SelectedWallet'
import {COLORS} from '../../theme'
import {DefaultAsset} from '../../types'
import type {CreateDelegationTxResponse, CreateUnsignedTxResponse} from '../../yoroi-wallets'
import {MultiToken} from '../../yoroi-wallets'
import {Quantity, YoroiUnsignedTx} from '../../yoroi-wallets/types'
import {Amounts, Entries, Quantities} from '../../yoroi-wallets/utils'

export type Params = {
poolHash: string
poolName: string
transactionData: CreateDelegationTxResponse
unsignedTx: YoroiUnsignedTx
}

const isParams = (params?: Params | object | undefined): params is Params => {
return (
!!params &&
'transactionData' in params &&
typeof params.transactionData === 'object' &&
'unsignedTx' in params &&
typeof params.unsignedTx === 'object' &&
'poolHash' in params &&
typeof params.poolHash === 'string' &&
'poolName' in params &&
Expand All @@ -41,31 +40,22 @@ const isParams = (params?: Params | object | undefined): params is Params => {
export const DelegationConfirmation = ({mockDefaultAsset}: {mockDefaultAsset?: DefaultAsset}) => {
const {resetToTxHistory} = useWalletNavigation()
const wallet = useSelectedWallet()
const {isHW, isEasyConfirmationEnabled} = wallet
const defaultNetworkAsset = useSelector(defaultNetworkAssetSelector)
const defaultAsset = mockDefaultAsset || defaultNetworkAsset
const {poolHash, poolName, transactionData: delegationTxData} = useParams<Params>(isParams)
const [transactionFee, setTransactionFee] = useState<MultiToken>()
const strings = useStrings()

const {poolHash, poolName, unsignedTx: yoroiTx} = useParams<Params>(isParams)
if (!yoroiTx.staking) throw new Error('invalid transaction')
const stakingAmount = Amounts.getAmount(Entries.toAmounts(yoroiTx.staking.delegations), '')
const reward = approximateReward(stakingAmount.quantity)

const [password, setPassword] = useState('')
const [useUSB, setUseUSB] = useState(false)

const signRequest: CreateUnsignedTxResponse = delegationTxData.signRequest
const amountToDelegate: MultiToken = delegationTxData.totalAmountToDelegate
const reward = approximateReward(amountToDelegate.getDefault())

useEffect(() => {
if (CONFIG.DEBUG.PREFILL_FORMS && __DEV__) {
setPassword(CONFIG.DEBUG.PASSWORD)
}
if (CONFIG.DEBUG.PREFILL_FORMS && __DEV__) setPassword(CONFIG.DEBUG.PASSWORD)
}, [])

useEffect(() => {
void (async () => {
setTransactionFee(await delegationTxData.signRequest.fee())
})()
}, [delegationTxData])

const onSuccess = () => {
resetToTxHistory()
}
Expand All @@ -87,32 +77,30 @@ export const DelegationConfirmation = ({mockDefaultAsset}: {mockDefaultAsset?: D

<View style={styles.input}>
<Text small style={styles.fees}>
{`+ ${transactionFee ? formatTokenAmount(transactionFee.getDefault(), defaultAsset) : ''} ${
strings.ofFees
}`}
{`+ ${formatTokenAmount(new BigNumber(yoroiTx.fee['']), defaultAsset)} ${strings.ofFees}`}
</Text>

{/* requires a handler so we pass on a dummy function */}
<ValidatedTextInput
onChangeText={() => undefined}
editable={false}
value={formatTokenAmount(amountToDelegate.getDefault(), defaultAsset)}
value={formatTokenAmount(new BigNumber(stakingAmount.quantity), defaultAsset)}
label={strings.amount}
/>
</View>

{!isEasyConfirmationEnabled && !isHW && (
{!wallet.isEasyConfirmationEnabled && !wallet.isHW && (
<View style={styles.input}>
<ValidatedTextInput secureTextEntry value={password} label={strings.password} onChangeText={setPassword} />
</View>
)}

<View style={styles.itemBlock}>
<Text style={styles.itemTitle}>{strings.rewardsExplanation}</Text>
<Text style={styles.rewards}>{formatTokenWithText(reward, defaultAsset)}</Text>
<Text style={styles.rewards}>{formatTokenWithText(new BigNumber(reward), defaultAsset)}</Text>
</View>

{isHW && <HWInstructions useUSB={useUSB} addMargin />}
{wallet.isHW && <HWInstructions useUSB={useUSB} addMargin />}
</ScrollView>

<Actions>
Expand All @@ -126,7 +114,7 @@ export const DelegationConfirmation = ({mockDefaultAsset}: {mockDefaultAsset?: D
onSuccess={onSuccess}
setUseUSB={setUseUSB}
useUSB={useUSB}
txDataSignRequest={signRequest}
txDataSignRequest={yoroiTx.unsignedTx}
/>
</Actions>
</View>
Expand Down Expand Up @@ -169,10 +157,11 @@ const messages = defineMessages({
* TODO: based on https://staking.cardano.org/en/calculator/
* needs to be update per-network
*/
const approximateReward = (amount: BigNumber): BigNumber => {
return amount
.times(CONFIG.NETWORKS.HASKELL_SHELLEY.PER_EPOCH_PERCENTAGE_REWARD)
.div(CONFIG.NUMBERS.EPOCH_REWARD_DENOMINATOR)
const approximateReward = (stakedQuantity: Quantity): Quantity => {
return Quantities.quotient(
Quantities.product([stakedQuantity, `${CONFIG.NETWORKS.HASKELL_SHELLEY.PER_EPOCH_PERCENTAGE_REWARD}`]),
CONFIG.NUMBERS.EPOCH_REWARD_DENOMINATOR.toString() as Quantity,
)
}

const styles = StyleSheet.create({
Expand Down
2 changes: 1 addition & 1 deletion src/Staking/PoolDetails/PoolDetailScreen.tsx
Expand Up @@ -6,7 +6,7 @@ import {Button, Text, TextInput} from '../../components'
import {COLORS, spacing} from '../../theme'

type Props = {
onPressDelegate: (poolHash?: string) => void
onPressDelegate: (poolHash: string) => void
disabled?: boolean
}

Expand Down
45 changes: 24 additions & 21 deletions src/Staking/StakingCenter/StakingCenter.tsx
Expand Up @@ -5,7 +5,7 @@ import React, {useEffect, useState} from 'react'
import type {IntlShape} from 'react-intl'
import {defineMessages, useIntl} from 'react-intl'
import {ActivityIndicator, View} from 'react-native'
import {WebView} from 'react-native-webview'
import {WebView, WebViewMessageEvent} from 'react-native-webview'
import {useSelector} from 'react-redux'

import {AccountAutoRefresher} from '../../AccountAutoRefresher'
Expand All @@ -16,7 +16,6 @@ import {showErrorDialog} from '../../legacy/actions'
import {CONFIG, getTestStakingPool, isNightly, SHOW_PROD_POOLS_IN_DEV} from '../../legacy/config'
import {InsufficientFunds} from '../../legacy/errors'
import {ApiError, NetworkError} from '../../legacy/errors'
import {ObjectValues} from '../../legacy/flow'
import {normalizeTokenAmount} from '../../legacy/format'
import {Logger} from '../../legacy/logging'
import {getNetworkConfigById} from '../../legacy/networks'
Expand All @@ -33,7 +32,7 @@ import {StakingCenterRouteNavigation} from '../../navigation'
import {useSelectedWallet} from '../../SelectedWallet'
import {DefaultAsset} from '../../types'
import {UtxoAutoRefresher} from '../../UtxoAutoRefresher'
import {ServerStatus, walletManager} from '../../yoroi-wallets'
import {ServerStatus, YoroiWallet} from '../../yoroi-wallets'
import {PoolDetailScreen} from '../PoolDetails'
import {PoolWarningModal} from '../PoolWarningModal'

Expand Down Expand Up @@ -62,29 +61,30 @@ export const StakingCenter = () => {
// pools user is currently delegating to
const poolList = poolOperator != null ? [poolOperator] : null

const handleOnPress = (poolHash?: string) => {
const handleOnPress = (poolHash: string) => {
const selectedPoolHashes = poolHash ? [poolHash] : nightlyAndDevPoolHashes
Logger.debug('manual inputted or config pool:', selectedPoolHashes)
_delegate(selectedPoolHashes)
delegate(selectedPoolHashes)
}

const handleOnMessage = async (event) => {
const handleOnMessage = async (event: WebViewMessageEvent) => {
if (isFetchingUtxos) {
return showErrorDialog(waitSyncDialog, intl)
}
if (event) {
const selectedPoolHashes: Array<string> = JSON.parse(decodeURI(event.nativeEvent.data))
Logger.debug('selected pools from explorer:', selectedPoolHashes)
_delegate(selectedPoolHashes)
const selectedPoolHashes = JSON.parse(decodeURI(event.nativeEvent.data))
if (!Array.isArray(selectedPoolHashes) || selectedPoolHashes.length < 1) {
await showErrorDialog(noPoolDataDialog, intl)
}
Logger.debug('selected pools from explorer:', selectedPoolHashes)
delegate(selectedPoolHashes)
}

const _delegate = async (selectedPoolHashes: Array<string> = []) => {
const delegate = async (selectedPoolHashes: Array<string>) => {
try {
setBusy(true)

if (selectedPoolHashes.length) {
await _handleSelectedPoolHashes(
await handleSelectedPoolHashes(
selectedPoolHashes,
setSelectedPools,
setReputationInfo,
Expand All @@ -95,9 +95,8 @@ export const StakingCenter = () => {
intl,
navigation,
serverStatus,
wallet,
)
} else {
await showErrorDialog(noPoolDataDialog, intl)
}
} finally {
setBusy(false)
Expand All @@ -108,7 +107,7 @@ export const StakingCenter = () => {
() => {
const getAmountToDelegate: () => Promise<void> = async () => {
if (utxos != null) {
const utxosForKey = await walletManager.getAllUtxosForKey(utxos)
const utxosForKey = await wallet.getAllUtxosForKey(utxos)
const _amountToDelegate = utxosForKey
.map((utxo) => utxo.amount)
.reduce((x: BigNumber, y) => x.plus(new BigNumber(y || 0)), new BigNumber(0))
Expand Down Expand Up @@ -158,6 +157,7 @@ export const StakingCenter = () => {
intl,
navigation,
serverStatus,
wallet,
)
}}
onRequestClose={() => setShowPoolWarning(false)}
Expand Down Expand Up @@ -225,11 +225,12 @@ const navigateToDelegationConfirm = async (
intl: IntlShape,
navigation: StakingCenterRouteNavigation,
serverStatus: ServerStatus,
wallet: YoroiWallet,
) => {
try {
const selectedPool = selectedPools[0]
if (accountBalance == null) return
const transactionData = await walletManager.createDelegationTx(
const yoroiTx = await wallet.createDelegationTx(
selectedPool.poolHash,
accountBalance,
utxos,
Expand All @@ -239,7 +240,7 @@ const navigateToDelegationConfirm = async (
navigation.navigate('delegation-confirmation', {
poolName: selectedPool?.poolName ?? '',
poolHash: selectedPool.poolHash,
transactionData,
yoroiTx,
})
} catch (e) {
if (e instanceof InsufficientFunds) {
Expand All @@ -253,7 +254,7 @@ const navigateToDelegationConfirm = async (
}
}

const _handleSelectedPoolHashes = async (
const handleSelectedPoolHashes = async (
selectedPoolHashes: Array<string>,
setSelectedPools: (selectedPools: Array<SelectedPool>) => void,
setReputationInfo: (reputationInfo: Record<string, unknown>) => void,
Expand All @@ -264,18 +265,19 @@ const _handleSelectedPoolHashes = async (
intl: IntlShape,
navigation,
serverStatus: ServerStatus,
wallet: YoroiWallet,
) => {
try {
const poolInfoResponse = await walletManager.fetchPoolInfo({
const poolInfoResponse = await wallet.fetchPoolInfo({
poolIds: selectedPoolHashes,
})
const poolInfo: any = ObjectValues(poolInfoResponse)[0]
const poolInfo = Object.values(poolInfoResponse)[0]
Logger.debug('StakingCenter::poolInfo', poolInfo)

// TODO: fetch reputation info once an endpoint is implemented
const poolsReputation: {[key: string]: SelectedPool} = {}

if (poolInfo && poolInfo.info != null) {
if ('info' in poolInfo) {
const selectedPools: Array<SelectedPool> = [
{
poolName: poolInfo.info.name,
Expand Down Expand Up @@ -303,6 +305,7 @@ const _handleSelectedPoolHashes = async (
intl,
navigation,
serverStatus,
wallet,
)
}
} else {
Expand Down
6 changes: 4 additions & 2 deletions src/hooks/index.ts
Expand Up @@ -18,7 +18,7 @@ import {WalletMeta} from '../legacy/state'
import storage from '../legacy/storage'
import {CurrencySymbol, RawUtxo, TipStatusResponse} from '../legacy/types'
import {Storage} from '../Storage'
import {Token} from '../types'
import {DefaultAsset, Token} from '../types'
import {
decryptWithPassword,
encryptWithPassword,
Expand Down Expand Up @@ -201,19 +201,21 @@ export const useWithdrawalTx = (
{
wallet,
utxos,
defaultAsset,
deregister = false,
serverTime,
}: {
wallet: YoroiWallet
utxos: Array<RawUtxo>
defaultAsset: DefaultAsset
deregister?: boolean
serverTime?: Date
},
options?: UseQueryOptions<YoroiUnsignedTx>,
) => {
const query = useQuery({
queryKey: [wallet.id, 'withdrawalTx', {deregister}],
queryFn: () => wallet.createWithdrawalTx(utxos, deregister, serverTime),
queryFn: () => wallet.createWithdrawalTx(utxos, defaultAsset, deregister, serverTime),
retry: false,
cacheTime: 0,
...options,
Expand Down
4 changes: 2 additions & 2 deletions src/navigation.ts
Expand Up @@ -8,7 +8,7 @@ import {HWDeviceInfo} from './legacy/ledgerUtils'
import type {RawUtxo} from './legacy/types'
import {COLORS} from './theme'
import {NetworkId, TokenEntry, WalletImplementationId, YoroiProvider} from './yoroi-wallets'
import {CreateDelegationTxResponse} from './yoroi-wallets/cardano/shelley/delegationUtils'
import {YoroiUnsignedTx} from './yoroi-wallets/types'

// prettier-ignore
export const useUnsafeParams = <Params, >() => {
Expand Down Expand Up @@ -184,7 +184,7 @@ export type StakingCenterRoutes = {
'delegation-confirmation': {
poolName: string
poolHash: string
transactionData: CreateDelegationTxResponse
yoroiTx: YoroiUnsignedTx
}
}
export type StakingCenterRouteNavigation = StackNavigationProp<StakingCenterRoutes>
Expand Down

0 comments on commit 87dc473

Please sign in to comment.