Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/webapp-yield' into webapp-dev
Browse files Browse the repository at this point in the history
  • Loading branch information
saltict committed Oct 19, 2023
2 parents 8e8448f + 67e612b commit 50e940b
Show file tree
Hide file tree
Showing 14 changed files with 224 additions and 69 deletions.
67 changes: 59 additions & 8 deletions packages/extension-base/src/koni/api/yield/acalaLiquidStaking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,69 @@ import { _ChainAsset, _ChainInfo } from '@subwallet/chain-list/types';
import { ExtrinsicType, OptimalYieldPath, OptimalYieldPathParams, RequestCrossChainTransfer, RequestYieldStepSubmit, SubmitYieldStepData, TokenBalanceRaw, YieldPoolInfo, YieldPositionInfo, YieldPositionStats, YieldStepType } from '@subwallet/extension-base/background/KoniTypes';
import { ALL_ACCOUNT_KEY } from '@subwallet/extension-base/constants';
import { createXcmExtrinsic } from '@subwallet/extension-base/koni/api/xcm';
import { YIELD_POOL_STAT_REFRESH_INTERVAL } from '@subwallet/extension-base/koni/api/yield/helper/utils';
import { convertDerivativeToOriginToken, YIELD_POOL_STAT_REFRESH_INTERVAL } from '@subwallet/extension-base/koni/api/yield/helper/utils';
import { HandleYieldStepData } from '@subwallet/extension-base/koni/api/yield/index';
import { _SubstrateApi } from '@subwallet/extension-base/services/chain-service/types';
import { _getChainNativeTokenSlug, _getTokenOnChainInfo } from '@subwallet/extension-base/services/chain-service/utils';
import { sumBN } from '@subwallet/extension-base/utils';
import fetch from 'cross-fetch';

import { SubmittableExtrinsic } from '@polkadot/api/types';
import { BN } from '@polkadot/util';

// const YEAR = 365 * 24 * 60 * 60 * 1000;

const GRAPHQL_API = 'https://api.polkawallet.io/acala-liquid-staking-subql';
const EXCHANGE_RATE_REQUEST = 'query { dailySummaries(first:30, orderBy:TIMESTAMP_DESC) {nodes { exchangeRate timestamp }}}';

interface BifrostLiquidStakingMeta {
data: {
dailySummaries: {
nodes: BifrostLiquidStakingMetaItem[]
}
}
}

interface BifrostLiquidStakingMetaItem {
exchangeRate: string,
timestamp: string
}

export function subscribeAcalaLiquidStakingStats (chainApi: _SubstrateApi, chainInfoMap: Record<string, _ChainInfo>, poolInfo: YieldPoolInfo, callback: (rs: YieldPoolInfo) => void) {
async function getPoolStat () {
const substrateApi = await chainApi.isReady;

const [_toBondPool, _totalStakingBonded] = await Promise.all([
const stakingMetaPromise = new Promise(function (resolve) {
fetch(GRAPHQL_API, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
query: EXCHANGE_RATE_REQUEST
})
}).then((res) => {
resolve(res.json());
}).catch(console.error);
});

const [_toBondPool, _totalStakingBonded, _stakingMeta] = await Promise.all([
substrateApi.api.query.homa.toBondPool(),
substrateApi.api.query.homa.totalStakingBonded()
substrateApi.api.query.homa.totalStakingBonded(),
stakingMetaPromise
]);

const stakingMeta = _stakingMeta as BifrostLiquidStakingMeta;
const stakingMetaList = stakingMeta.data.dailySummaries.nodes;
const latestExchangeRate = parseInt(stakingMetaList[0].exchangeRate);
const decimals = 10 ** 10;

const endingBalance = parseInt(stakingMetaList[0].exchangeRate);
const beginBalance = parseInt(stakingMetaList[29].exchangeRate);

const diff = endingBalance / beginBalance;
const apy = diff ** (365 / 30) - 1;

const toBondPool = new BN(_toBondPool.toString());
const totalStakingBonded = new BN(_totalStakingBonded.toString());

Expand All @@ -36,15 +79,15 @@ export function subscribeAcalaLiquidStakingStats (chainApi: _SubstrateApi, chain
assetEarning: [
{
slug: poolInfo.rewardAssets[0],
apr: 20.86,
exchangeRate: 1 / 7.46544
apy: apy * 100,
exchangeRate: latestExchangeRate / decimals
}
],
maxCandidatePerFarmer: 1,
maxWithdrawalRequestPerFarmer: 1,
minJoinPool: '50000000000',
minWithdrawal: '0',
totalApr: 20.86,
minWithdrawal: '50000000000',
totalApy: apy * 100,
tvl: totalStakingBonded.add(toBondPool).toString()
}
});
Expand Down Expand Up @@ -188,6 +231,14 @@ export async function getAcalaLiquidStakingExtrinsic (address: string, params: O
export async function getAcalaLiquidStakingRedeem (params: OptimalYieldPathParams, amount: string): Promise<[ExtrinsicType, SubmittableExtrinsic<'promise'>]> {
const substrateApi = await params.substrateApiMap[params.poolInfo.chain].isReady;

const derivativeTokenSlug = params.poolInfo.derivativeAssets?.[0] || '';
const originTokenSlug = params.poolInfo.inputAssets[0] || '';

const derivativeTokenInfo = params.assetInfoMap[derivativeTokenSlug];
const originTokenInfo = params.assetInfoMap[originTokenSlug];

const formattedMinAmount = convertDerivativeToOriginToken(amount, params.poolInfo, derivativeTokenInfo, originTokenInfo);

const extrinsic = substrateApi.api.tx.aggregatedDex.swapWithExactSupply(
// Swap path
[
Expand All @@ -202,7 +253,7 @@ export async function getAcalaLiquidStakingRedeem (params: OptimalYieldPathParam
// Supply amount
amount,
// Min target amount
0 // should always set a min target to prevent unexpected result
formattedMinAmount // should always set a min target to prevent unexpected result
);

return [ExtrinsicType.REDEEM_LDOT, extrinsic];
Expand Down
18 changes: 14 additions & 4 deletions packages/extension-base/src/koni/api/yield/bifrostLiquidStaking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { _ChainAsset, _ChainInfo } from '@subwallet/chain-list/types';
import { ExtrinsicType, OptimalYieldPath, OptimalYieldPathParams, RequestCrossChainTransfer, RequestYieldStepSubmit, SubmitYieldStepData, TokenBalanceRaw, YieldPoolInfo, YieldPositionInfo, YieldPositionStats, YieldStepType } from '@subwallet/extension-base/background/KoniTypes';
import { ALL_ACCOUNT_KEY } from '@subwallet/extension-base/constants';
import { createXcmExtrinsic } from '@subwallet/extension-base/koni/api/xcm';
import { YIELD_POOL_STAT_REFRESH_INTERVAL } from '@subwallet/extension-base/koni/api/yield/helper/utils';
import { convertDerivativeToOriginToken, YIELD_POOL_STAT_REFRESH_INTERVAL } from '@subwallet/extension-base/koni/api/yield/helper/utils';
import { HandleYieldStepData } from '@subwallet/extension-base/koni/api/yield/index';
import { _SubstrateApi } from '@subwallet/extension-base/services/chain-service/types';
import { _getAssetDecimals, _getChainNativeTokenSlug, _getTokenOnChainInfo } from '@subwallet/extension-base/services/chain-service/utils';
Expand Down Expand Up @@ -210,10 +210,20 @@ export async function getBifrostLiquidStakingExtrinsic (address: string, params:
export async function getBifrostLiquidStakingRedeem (params: OptimalYieldPathParams, amount: string): Promise<[ExtrinsicType, SubmittableExtrinsic<'promise'>]> {
const substrateApi = await params.substrateApiMap[params.poolInfo.chain].isReady;
// @ts-ignore
const rewardTokenSlug = params.poolInfo.derivativeAssets[0];
const rewardTokenInfo = params.assetInfoMap[rewardTokenSlug];
// const rewardTokenSlug = params.poolInfo.derivativeAssets[0];
// const rewardTokenInfo = params.assetInfoMap[rewardTokenSlug];

const extrinsic = substrateApi.api.tx.vtokenMinting.redeem(_getTokenOnChainInfo(rewardTokenInfo), amount);
// const extrinsic = substrateApi.api.tx.vtokenMinting.redeem(_getTokenOnChainInfo(rewardTokenInfo), amount);

const derivativeTokenSlug = params.poolInfo.derivativeAssets?.[0] || '';
const originTokenSlug = params.poolInfo.inputAssets[0] || '';

const derivativeTokenInfo = params.assetInfoMap[derivativeTokenSlug];
const originTokenInfo = params.assetInfoMap[originTokenSlug];

const formattedMinAmount = convertDerivativeToOriginToken(amount, params.poolInfo, derivativeTokenInfo, originTokenInfo);

const extrinsic = substrateApi.api.tx.stablePool.swap(0, 1, 0, amount, formattedMinAmount);

return [ExtrinsicType.REDEEM_VDOT, extrinsic];
}
3 changes: 1 addition & 2 deletions packages/extension-base/src/koni/api/yield/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,7 @@ export const YIELD_POOLS_INFO: Record<string, YieldPoolInfo> = {
'interlay-LOCAL-qDOT'
],
rewardAssets: [
'interlay-LOCAL-DOT',
'interlay-NATIVE-INTR'
'interlay-LOCAL-DOT'
],
feeAssets: [
'interlay-NATIVE-INTR',
Expand Down
15 changes: 14 additions & 1 deletion packages/extension-base/src/koni/api/yield/helper/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Copyright 2019-2022 @subwallet/extension-base
// SPDX-License-Identifier: Apache-2.0

import { ExtrinsicType, YieldStepDetail, YieldStepType } from '@subwallet/extension-base/background/KoniTypes';
import { _ChainAsset } from '@subwallet/chain-list/types';
import { ExtrinsicType, YieldPoolInfo, YieldStepDetail, YieldStepType } from '@subwallet/extension-base/background/KoniTypes';
import { _getAssetDecimals } from '@subwallet/extension-base/services/chain-service/utils';

export interface RuntimeDispatchInfo {
weight: {
Expand Down Expand Up @@ -46,3 +48,14 @@ export const DEFAULT_YIELD_FIRST_STEP: YieldStepDetail = {
export const YIELD_EXTRINSIC_TYPES = [ExtrinsicType.MINT_VDOT, ExtrinsicType.MINT_LDOT, ExtrinsicType.MINT_SDOT, ExtrinsicType.MINT_QDOT];

export const YIELD_POOL_STAT_REFRESH_INTERVAL = 300000;

export function convertDerivativeToOriginToken (amount: string, poolInfo: YieldPoolInfo, derivativeTokenInfo: _ChainAsset, originTokenInfo: _ChainAsset) {
const derivativeDecimals = _getAssetDecimals(derivativeTokenInfo);
const originDecimals = _getAssetDecimals(originTokenInfo);

const exchangeRate = poolInfo.stats?.assetEarning?.[0].exchangeRate || 1;
const formattedAmount = parseInt(amount) / (10 ** derivativeDecimals); // TODO: decimals
const minAmount = formattedAmount * exchangeRate * 0.95;

return Math.floor(minAmount * (10 ** originDecimals));
}
8 changes: 2 additions & 6 deletions packages/extension-base/src/koni/api/yield/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ export function subscribeYieldPoolStats (substrateApiMap: Record<string, _Substr

unsubList.push(unsub);
} else if (poolInfo.slug === 'DOT___interlay_lending') {
const unsub = subscribeInterlayLendingStats(poolInfo, callback);
const unsub = subscribeInterlayLendingStats(substrateApi, chainInfoMap, poolInfo, assetInfoMap, callback);

unsubList.push(unsub);
} else if (poolInfo.slug === 'DOT___parallel_liquid_staking') {
const unsub = subscribeParallelLiquidStakingStats(substrateApi, chainInfoMap, poolInfo, callback, substrateApiMap);
const unsub = subscribeParallelLiquidStakingStats(substrateApi, poolInfo, callback);

unsubList.push(unsub);
} else if (poolInfo.slug === 'LcDOT___acala_euphrates_liquid_staking') {
Expand Down Expand Up @@ -403,10 +403,6 @@ export async function validateYieldProcess (address: string, params: OptimalYiel
return await validateEarningProcess(address, params, path, balanceService);
}

export function validateYieldRedeem (address: string, poolInfo: YieldPoolInfo, amount: string): TransactionError[] {
return [];
}

export interface HandleYieldStepData {
txChain: string,
extrinsicType: ExtrinsicType,
Expand Down
29 changes: 19 additions & 10 deletions packages/extension-base/src/koni/api/yield/interlayLending.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,17 @@ import { sumBN } from '@subwallet/extension-base/utils';
import { SubmittableExtrinsic } from '@polkadot/api/types';
import { BN } from '@polkadot/util';

export function subscribeInterlayLendingStats (poolInfo: YieldPoolInfo, callback: (rs: YieldPoolInfo) => void) {
function getPoolStat () {
export function subscribeInterlayLendingStats (chainApi: _SubstrateApi, chainInfoMap: Record<string, _ChainInfo>, poolInfo: YieldPoolInfo, assetInfoMap: Record<string, _ChainAsset>, callback: (rs: YieldPoolInfo) => void) {
async function getPoolStat () {
const substrateApi = await chainApi.isReady;
const inputTokenSlug = poolInfo.inputAssets[0];
const inputTokenInfo = assetInfoMap[inputTokenSlug];

const _exchangeRate = await substrateApi.api.query.loans.exchangeRate(_getTokenOnChainInfo(inputTokenInfo));

const exchangeRate = _exchangeRate.toPrimitive() as number;
const decimals = 10 ** 18;

// eslint-disable-next-line node/no-callback-literal
callback({
...poolInfo,
Expand All @@ -25,26 +34,26 @@ export function subscribeInterlayLendingStats (poolInfo: YieldPoolInfo, callback
{
slug: poolInfo.rewardAssets[0],
apr: 1.29,
exchangeRate: 1 / 49.77
},
{
slug: poolInfo.rewardAssets[1],
apr: 12.32
exchangeRate: exchangeRate / decimals
}
],
maxCandidatePerFarmer: 1,
maxWithdrawalRequestPerFarmer: 1,
minJoinPool: '10000000000',
minWithdrawal: '0',
totalApr: 13.61,
totalApr: 1.29,
tvl: '291890000000000'
}
});
}

getPoolStat();
function getStatInterval () {
getPoolStat().catch(console.error);
}

getStatInterval();

const interval = setInterval(getPoolStat, YIELD_POOL_STAT_REFRESH_INTERVAL);
const interval = setInterval(getStatInterval, YIELD_POOL_STAT_REFRESH_INTERVAL);

return () => {
clearInterval(interval);
Expand Down
43 changes: 30 additions & 13 deletions packages/extension-base/src/koni/api/yield/parallelLiquidStaking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,40 @@ import { COMMON_CHAIN_SLUGS } from '@subwallet/chain-list';
import { _ChainAsset, _ChainInfo } from '@subwallet/chain-list/types';
import { ExtrinsicType, OptimalYieldPath, OptimalYieldPathParams, RequestCrossChainTransfer, RequestYieldStepSubmit, SubmitYieldStepData, YieldPoolInfo, YieldPositionInfo, YieldPositionStats, YieldStepType } from '@subwallet/extension-base/background/KoniTypes';
import { ALL_ACCOUNT_KEY } from '@subwallet/extension-base/constants';
import { PalletStakingStakingLedger } from '@subwallet/extension-base/koni/api/staking/bonding/relayChain';
import { createXcmExtrinsic } from '@subwallet/extension-base/koni/api/xcm';
import { YIELD_POOL_STAT_REFRESH_INTERVAL } from '@subwallet/extension-base/koni/api/yield/helper/utils';
import { convertDerivativeToOriginToken, YIELD_POOL_STAT_REFRESH_INTERVAL } from '@subwallet/extension-base/koni/api/yield/helper/utils';
import { _SubstrateApi } from '@subwallet/extension-base/services/chain-service/types';
import { _getChainNativeTokenSlug, _getTokenOnChainAssetId } from '@subwallet/extension-base/services/chain-service/utils';

import { SubmittableExtrinsic } from '@polkadot/api/types';
import { BN } from '@polkadot/util';
import { BN, BN_ZERO } from '@polkadot/util';

interface BlockHeader {
number: number
}

export function subscribeParallelLiquidStakingStats (chainApi: _SubstrateApi, chainInfoMap: Record<string, _ChainInfo>, poolInfo: YieldPoolInfo, callback: (rs: YieldPoolInfo) => void, substrateApiMap: Record<string, _SubstrateApi>) {
export function subscribeParallelLiquidStakingStats (chainApi: _SubstrateApi, poolInfo: YieldPoolInfo, callback: (rs: YieldPoolInfo) => void) {
async function getPoolStat () {
const substrateApi = await chainApi.isReady;

const [_exchangeRate, _currentBlockHeader, _currentTimestamp] = await Promise.all([
const [_exchangeRate, _currentBlockHeader, _currentTimestamp, _stakingLedgers] = await Promise.all([
substrateApi.api.query.liquidStaking.exchangeRate(),
substrateApi.api.rpc.chain.getHeader(),
substrateApi.api.query.timestamp.now()
substrateApi.api.query.timestamp.now(),
substrateApi.api.query.liquidStaking.stakingLedgers.entries()
]);

const exchangeRate = _exchangeRate.toPrimitive() as number; // TODO
let tvl = BN_ZERO;

for (const _stakingLedger of _stakingLedgers) {
const _ledger = _stakingLedger[1];
const ledger = _ledger.toPrimitive() as unknown as PalletStakingStakingLedger;

tvl = tvl.add(new BN(ledger.total.toString()));
}

const exchangeRate = _exchangeRate.toPrimitive() as number;
const currentBlockHeader = _currentBlockHeader.toPrimitive() as unknown as BlockHeader;
const currentTimestamp = _currentTimestamp.toPrimitive() as number;

Expand All @@ -42,7 +53,7 @@ export function subscribeParallelLiquidStakingStats (chainApi: _SubstrateApi, ch

const beginTimestamp = _beginTimestamp.toPrimitive() as number;
const beginExchangeRate = _beginExchangeRate.toPrimitive() as number;
// const decimals = 10 ** 10;
const decimals = 10 ** 18;

const apy = (exchangeRate / beginExchangeRate) ** (365 * 24 * 60 * 60000 / (currentTimestamp - beginTimestamp)) - 1;

Expand All @@ -54,15 +65,15 @@ export function subscribeParallelLiquidStakingStats (chainApi: _SubstrateApi, ch
{
slug: poolInfo.rewardAssets[0],
apy: apy * 100,
exchangeRate: 1.266
exchangeRate: exchangeRate / decimals
}
],
maxCandidatePerFarmer: 1,
maxWithdrawalRequestPerFarmer: 1,
minJoinPool: '10000000000',
minWithdrawal: '0',
minWithdrawal: '5000000000',
totalApy: apy * 100,
tvl: '12812000000000000'
tvl: tvl.toString()
}
});
}
Expand Down Expand Up @@ -181,10 +192,16 @@ export async function getParallelLiquidStakingExtrinsic (address: string, params

export async function getParallelLiquidStakingRedeem (params: OptimalYieldPathParams, amount: string, address: string): Promise<[ExtrinsicType, SubmittableExtrinsic<'promise'>]> {
const substrateApi = await params.substrateApiMap[params.poolInfo.chain].isReady;
// const rewardTokenSlug = params.poolInfo.derivativeAssets[0];
// const rewardTokenInfo = params.assetInfoMap[rewardTokenSlug];

const extrinsic = substrateApi.api.tx.liquidStaking.fastMatchUnstake([address]);
const derivativeTokenSlug = params.poolInfo.derivativeAssets?.[0] || '';
const originTokenSlug = params.poolInfo.inputAssets[0] || '';

const derivativeTokenInfo = params.assetInfoMap[derivativeTokenSlug];
const originTokenInfo = params.assetInfoMap[originTokenSlug];

const formattedMinAmount = convertDerivativeToOriginToken(amount, params.poolInfo, derivativeTokenInfo, originTokenInfo);

const extrinsic = substrateApi.api.tx.ammRoute.swapExactTokensForTokens(['1001', '101'], amount, formattedMinAmount);

return [ExtrinsicType.REDEEM_SDOT, extrinsic];
}
2 changes: 2 additions & 0 deletions packages/extension-base/src/koni/background/cron.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ export class KoniCron {

// MantaPay
reloadMantaPay && this.removeCron('syncMantaPay');
commonReload && this.removeCron('refreshPoolingStakingReward');

// NFT
(commonReload || needUpdateNft) && this.resetNft(address);
Expand All @@ -136,6 +137,7 @@ export class KoniCron {
if (this.checkNetworkAvailable(serviceInfo)) { // only add cron job if there's at least 1 active network
(commonReload || needUpdateNft) && this.addCron('refreshNft', this.refreshNft(address, serviceInfo.chainApiMap, this.state.getSmartContractNfts(), this.state.getActiveChainInfoMap()), CRON_REFRESH_NFT_INTERVAL);
reloadMantaPay && this.addCron('syncMantaPay', this.syncMantaPay, CRON_SYNC_MANTA_PAY);
commonReload && this.addCron('refreshPoolingStakingReward', this.refreshStakingRewardFastInterval(currentAccountInfo.address), CRON_REFRESH_STAKING_REWARD_FAST_INTERVAL);
} else {
this.setStakingRewardReady();
}
Expand Down
Loading

0 comments on commit 50e940b

Please sign in to comment.