Skip to content

fix: Integrate Morpho issues #6492

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions packages/kit-bg/src/services/ServiceStaking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,8 @@ class ServiceStaking extends ServiceBase {
async buildStakeTransaction(
params: IStakeBaseParams,
): Promise<IStakeTxResponse> {
const { networkId, accountId, provider, symbol, ...rest } = params;
const { networkId, accountId, provider, symbol, morphoVault, ...rest } =
params;
const client = await this.getClient(EServiceEndpointEnum.Earn);
const vault = await vaultFactory.getVault({ networkId, accountId });
const account = await vault.getAccount();
Expand All @@ -203,6 +204,7 @@ class ServiceStaking extends ServiceBase {
networkId,
symbol,
provider,
vault: morphoVault,
firmwareDeviceType: await this.getFirmwareDeviceTypeParam({
accountId,
}),
Expand All @@ -213,7 +215,7 @@ class ServiceStaking extends ServiceBase {

@backgroundMethod()
async buildUnstakeTransaction(params: IWithdrawBaseParams) {
const { networkId, accountId, ...rest } = params;
const { networkId, accountId, morphoVault, ...rest } = params;
const client = await this.getClient(EServiceEndpointEnum.Earn);
const vault = await vaultFactory.getVault({ networkId, accountId });
const account = await vault.getAccount();
Expand All @@ -234,6 +236,7 @@ class ServiceStaking extends ServiceBase {
firmwareDeviceType: await this.getFirmwareDeviceTypeParam({
accountId,
}),
vault: morphoVault,
...rest,
});
return resp.data.data;
Expand Down
128 changes: 100 additions & 28 deletions packages/kit/src/views/Staking/components/ApproveBaseStake/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { PropsWithChildren } from 'react';
import { useCallback, useMemo, useState } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';

import BigNumber from 'bignumber.js';
import { useIntl } from 'react-intl';
Expand Down Expand Up @@ -68,6 +68,12 @@ type IApproveBaseStakeProps = {
onConfirm?: (amount: string) => Promise<void>;
};

type ITokenAnnualReward = {
amount: string;
fiatValue?: string;
token: IToken;
};

export const ApproveBaseStake = ({
details,

Expand Down Expand Up @@ -221,29 +227,88 @@ export const ApproveBaseStake = ({
onChangeAmountValue(balance);
}, [onChangeAmountValue, balance]);

const estAnnualRewardsState = useMemo(() => {
if (Number(amountValue) > 0 && Number(apr) > 0) {
const amountBN = BigNumber(amountValue)
.multipliedBy(apr ?? 0)
.dividedBy(100);
return {
amount: amountBN.toFixed(),
fiatValue:
Number(price) > 0
? amountBN.multipliedBy(price).toFixed()
const estimatedAnnualRewards = useMemo<ITokenAnnualReward[]>(() => {
const amountBN = new BigNumber(amountValue);
if (amountBN.isNaN() || amountBN.lte(0)) return [];

const rewards: ITokenAnnualReward[] = [];

if (details.provider.apys) {
// handle base token reward
const baseRateBN = new BigNumber(details.provider.apys.rate);
if (baseRateBN.gt(0)) {
const baseAmount = amountBN.multipliedBy(baseRateBN).dividedBy(100);

rewards.push({
amount: baseAmount.toFixed(),
fiatValue: new BigNumber(price).gt(0)
? baseAmount.multipliedBy(price).toFixed()
: undefined,
token: details.token.info,
});
}

// handle extra token reward
const { rewards: extraRewards } = details.provider.apys;
if (extraRewards && details.rewardAssets) {
Object.entries(extraRewards).forEach(([tokenAddress, apy]) => {
const rewardToken = details.rewardAssets?.[tokenAddress];
const apyBN = new BigNumber(apy);

if (rewardToken && apyBN.gt(0)) {
const rewardAmount = amountBN.multipliedBy(apyBN).dividedBy(100);

rewards.push({
amount: rewardAmount.toFixed(),
token: rewardToken,
// TODO: Add fiat value
// fiatValue: new BigNumber(rewardToken.price).gt(0)
// ? rewardAmount.multipliedBy(rewardToken.price).toFixed()
// : undefined,
});
}
});
}
} else {
// handle single token reward
const aprBN = new BigNumber(apr ?? 0);
if (aprBN.gt(0)) {
const rewardAmount = amountBN.multipliedBy(aprBN).dividedBy(100);

rewards.push({
amount: rewardAmount.toFixed(),
fiatValue: new BigNumber(price).gt(0)
? rewardAmount.multipliedBy(price).toFixed()
: undefined,
};
token,
});
}
}
}, [amountValue, apr, price]);

return rewards;
}, [amountValue, apr, price, details, token]);

const totalAnnualRewardsFiatValue = useMemo(() => {
if (!estimatedAnnualRewards.length) return undefined;

return estimatedAnnualRewards
.reduce((total, reward) => {
if (reward.fiatValue) {
return total.plus(reward.fiatValue);
}
return total;
}, new BigNumber(0))
.toFixed();
}, [estimatedAnnualRewards]);

const daysSpent = useMemo(() => {
if (estAnnualRewardsState?.fiatValue && estimateFeeResp?.feeFiatValue) {
if (totalAnnualRewardsFiatValue && estimateFeeResp?.feeFiatValue) {
return calcDaysSpent(
estAnnualRewardsState?.fiatValue,
totalAnnualRewardsFiatValue,
estimateFeeResp.feeFiatValue,
);
}
}, [estimateFeeResp?.feeFiatValue, estAnnualRewardsState?.fiatValue]);
}, [estimateFeeResp?.feeFiatValue, totalAnnualRewardsFiatValue]);

const onSubmit = useCallback(async () => {
const showDialog = () => {
Expand Down Expand Up @@ -281,9 +346,9 @@ export const ApproveBaseStake = ({
showCancelButton: false,
});
};
if (estAnnualRewardsState?.fiatValue && estimateFeeResp) {
if (totalAnnualRewardsFiatValue && estimateFeeResp) {
const daySpent = calcDaysSpent(
estAnnualRewardsState.fiatValue,
totalAnnualRewardsFiatValue,
estimateFeeResp.feeFiatValue,
);
if (daySpent && daySpent > 5) {
Expand All @@ -299,7 +364,7 @@ export const ApproveBaseStake = ({
}, [
onConfirm,
amountValue,
estAnnualRewardsState,
totalAnnualRewardsFiatValue,
estimateFeeResp,
showEstimateGasAlert,
details,
Expand Down Expand Up @@ -349,20 +414,27 @@ export const ApproveBaseStake = ({
/>
) : null}
<CalculationList>
{estAnnualRewardsState ? (
<CalculationListItem>
{estimatedAnnualRewards.length > 0 ? (
<CalculationListItem
alignItems={
estimatedAnnualRewards.length > 1 ? 'flex-start' : 'center'
}
>
<CalculationListItem.Label>
{intl.formatMessage({
id: ETranslations.earn_est_annual_rewards,
})}
</CalculationListItem.Label>
<CalculationListItem.Value>
<ValuePriceListItem
tokenSymbol={token.symbol}
fiatSymbol={symbol}
amount={estAnnualRewardsState.amount}
fiatValue={estAnnualRewardsState.fiatValue}
/>
{estimatedAnnualRewards.map((reward) => (
<ValuePriceListItem
key={reward.token.address}
tokenSymbol={reward.token.symbol}
fiatSymbol={symbol}
amount={reward.amount}
fiatValue={reward.fiatValue}
/>
))}
</CalculationListItem.Value>
</CalculationListItem>
) : null}
Expand Down Expand Up @@ -418,7 +490,7 @@ export const ApproveBaseStake = ({
{estimateFeeResp ? (
<EstimateNetworkFee
estimateFeeResp={estimateFeeResp}
isVisible={!!estAnnualRewardsState?.fiatValue}
isVisible={!!totalAnnualRewardsFiatValue}
onPress={() => {
showEstimateGasAlert({
daysConsumed: daysSpent,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useIntl } from 'react-intl';

import { SizableText, XStack, YStack } from '@onekeyhq/components';
import { ETranslations } from '@onekeyhq/shared/src/locale';
import earnUtils from '@onekeyhq/shared/src/utils/earnUtils';
import type { IStakeProtocolDetails } from '@onekeyhq/shared/types/staking';

import { capitalizeString } from '../../utils/utils';
Expand Down Expand Up @@ -133,7 +134,8 @@ function ProviderInfo({
{babylonConfirmedCap.value} BTC
</GridItem>
) : null}
{poolFee?.value ? (
{poolFee?.value &&
earnUtils.isMorphoProvider({ providerName: validator?.name ?? '' }) ? (
<GridItem
title={intl.formatMessage({ id: ETranslations.earn_commission })}
tooltip={intl.formatMessage({
Expand Down
6 changes: 6 additions & 0 deletions packages/kit/src/views/Staking/hooks/useUniversalHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export function useUniversalStake({
symbol,
term,
feeRate,
morphoVault,
provider,
stakingInfo,
onSuccess,
Expand All @@ -80,6 +81,7 @@ export function useUniversalStake({
symbol: string;
term?: number;
feeRate?: number;
morphoVault?: string;
provider: string;
stakingInfo?: IStakingInfo;
onSuccess?: IModalSendParamList['SendConfirm']['onSuccess'];
Expand All @@ -94,6 +96,7 @@ export function useUniversalStake({
term,
provider,
feeRate,
morphoVault,
});

const encodedTx = await backgroundApiProxy.serviceStaking.buildEarnTx({
Expand Down Expand Up @@ -154,6 +157,7 @@ export function useUniversalWithdraw({
symbol,
provider,
identity,
morphoVault,
stakingInfo,
onSuccess,
onFail,
Expand All @@ -162,6 +166,7 @@ export function useUniversalWithdraw({
symbol: string;
provider: string;
identity?: string;
morphoVault?: string;
stakingInfo?: IStakingInfo;
onSuccess?: IModalSendParamList['SendConfirm']['onSuccess'];
onFail?: IModalSendParamList['SendConfirm']['onFail'];
Expand Down Expand Up @@ -223,6 +228,7 @@ export function useUniversalWithdraw({
accountId,
symbol,
provider,
morphoVault,
});
}
const encodedTx = await backgroundApiProxy.serviceStaking.buildEarnTx({
Expand Down
40 changes: 36 additions & 4 deletions packages/kit/src/views/Staking/pages/ApproveBaseStake/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import type {
EModalStakingRoutes,
IModalStakingParamList,
} from '@onekeyhq/shared/src/routes';
import earnUtils from '@onekeyhq/shared/src/utils/earnUtils';
import { EAccountSelectorSceneName } from '@onekeyhq/shared/types';
import { EEarnLabels } from '@onekeyhq/shared/types/staking';

Expand All @@ -40,14 +41,21 @@ const BasicApproveBaseStakePage = () => {
await handleStake({
amount,
stakingInfo: {
label: EEarnLabels.Unknown,
protocol: provider.name,
label: EEarnLabels.Stake,
protocol: earnUtils.getEarnProviderName({
providerName: provider.name,
}),
protocolLogoURI: provider.logoURI,
send: { token: token.info, amount },
tags: [actionTag],
},
symbol: token.info.symbol.toUpperCase(),
provider: provider.name,
morphoVault: earnUtils.isMorphoProvider({
providerName: provider.name,
})
? provider.vault
: undefined,
onSuccess: () => {
appNavigation.pop();
defaultLogger.staking.page.staking({
Expand All @@ -62,10 +70,34 @@ const BasicApproveBaseStakePage = () => {
const intl = useIntl();

const showEstReceive = useMemo<boolean>(
() => provider.name.toLowerCase() === 'lido',
() =>
earnUtils.isLidoProvider({
providerName: provider.name,
}) ||
earnUtils.isMorphoProvider({
providerName: provider.name,
}),
[provider],
);

const estReceiveTokenRate = useMemo(() => {
if (
earnUtils.isLidoProvider({
providerName: provider.name,
})
) {
return provider.lidoStTokenRate;
}
if (
earnUtils.isMorphoProvider({
providerName: provider.name,
})
) {
return provider.morphoTokenRate;
}
return '1';
}, [provider]);

const providerLabel = useProviderLabel(provider.name);

const { result: estimateFeeResp } = usePromiseResult(async () => {
Expand Down Expand Up @@ -103,7 +135,7 @@ const BasicApproveBaseStakePage = () => {
providerLabel={providerLabel}
showEstReceive={showEstReceive}
estReceiveToken={details.rewardToken}
estReceiveTokenRate={provider.lidoStTokenRate}
estReceiveTokenRate={estReceiveTokenRate}
approveTarget={{
accountId,
networkId,
Expand Down
5 changes: 4 additions & 1 deletion packages/kit/src/views/Staking/pages/Claim/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type {
EModalStakingRoutes,
IModalStakingParamList,
} from '@onekeyhq/shared/src/routes';
import earnUtils from '@onekeyhq/shared/src/utils/earnUtils';
import { EEarnLabels } from '@onekeyhq/shared/types/staking';

import { UniversalClaim } from '../../components/UniversalClaim';
Expand Down Expand Up @@ -48,7 +49,9 @@ const ClaimPage = () => {
provider: provider.name,
stakingInfo: {
label: EEarnLabels.Claim,
protocol: provider.name,
protocol: earnUtils.getEarnProviderName({
providerName: provider.name,
}),
protocolLogoURI: provider.logoURI,
receive: { token: tokenInfo, amount },
tags: [actionTag],
Expand Down
Loading
Loading