Skip to content

Commit 345274d

Browse files
authored
remove oracle rpc query (#465)
1 parent ede090e commit 345274d

File tree

8 files changed

+127
-208
lines changed

8 files changed

+127
-208
lines changed

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,5 @@
4545
"dependencies": {
4646
"dotenv": "^16.0.0"
4747
},
48-
"packageManager": "[email protected]",
49-
"stableVersion": "4.1.8-8"
48+
"packageManager": "[email protected]"
5049
}

packages/sdk-wallet/src/wallet-rx.ts

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,17 @@ import type { Option } from '@polkadot/types';
2424
import { WalletBase } from './wallet-base';
2525
import { AccountData, AccountInfo, BalanceLock, BlockHash } from '@polkadot/types/interfaces';
2626
import { BelowExistentialDeposit } from './errors';
27-
import { ORACLE_FEEDS_TOKEN } from './config';
2827
import { SubmittableExtrinsic } from '@polkadot/api/types';
2928
import { getMaxAvailableBalance } from './utils/get-max-available-balance';
30-
import { Homa } from '@acala-network/sdk';
31-
import { OraclePriceProvider } from '@acala-network/sdk/wallet/price-provider/oracle-price-provider';
3229
import { AcalaPrimitivesCurrencyCurrencyId } from '@polkadot/types/lookup';
3330

3431
const queryFN = getSubscribeOrAtQuery;
3532
export class WalletRx extends WalletBase<ApiRx> {
36-
private readonly oraclePriceProvider: OraclePriceProvider;
3733
public readonly assetMetadatas$: BehaviorSubject<Map<string, Token>>;
3834

3935
constructor(api: ApiRx) {
4036
super(api);
4137

42-
this.oraclePriceProvider = new OraclePriceProvider(api);
4338
this.assetMetadatas$ = new BehaviorSubject<Map<string, Token>>(new Map());
4439

4540
// subscribe oracle feed
@@ -86,12 +81,6 @@ export class WalletRx extends WalletBase<ApiRx> {
8681
// query price info
8782
public queryPrice = memoize((currency: MaybeCurrency, at?: number): Observable<PriceData> => {
8883
const tokenName = forceToCurrencyName(currency);
89-
const liquidCurrencyId = this.api.consts?.homa?.liquidCurrencyId || this.api.consts?.homaLite?.liquidCurrencyId;
90-
91-
// get liquid currency price from staking pool exchange rate
92-
if (liquidCurrencyId && forceToCurrencyName(currency) === forceToCurrencyName(liquidCurrencyId)) {
93-
return this.queryLiquidPriceFromHoma(at);
94-
}
9584

9685
// get dex share price
9786
if (isDexShareName(tokenName)) {
@@ -108,16 +97,6 @@ export class WalletRx extends WalletBase<ApiRx> {
10897
});
10998
}
11099

111-
// get price of ORACLE_FEEDS_TOKEN
112-
if (ORACLE_FEEDS_TOKEN.includes(tokenName)) {
113-
return this.queryPriceFromOracle(currency, at).pipe(
114-
map((price) => ({
115-
token: this.getToken(tokenName),
116-
price
117-
}))
118-
);
119-
}
120-
121100
// get price from dex default
122101
return this.queryPriceFromDex(currency, at);
123102
});
@@ -154,40 +133,10 @@ export class WalletRx extends WalletBase<ApiRx> {
154133
);
155134
});
156135

157-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
158-
public queryPriceFromOracle = memoize((token: MaybeCurrency, _at?: number) => {
159-
return this.oraclePriceProvider.subscribe(token);
160-
});
161-
162136
public subscribeToken = memoize((token: MaybeCurrency) => {
163137
return of(this.getToken(token));
164138
});
165139

166-
public queryLiquidPriceFromHoma = memoize((at?: number) => {
167-
// FIXME: need remove homaLite
168-
const liquidCurrencyId = this.api.consts.homa?.liquidCurrencyId;
169-
const stakingCurrencyId = this.api.consts.homa?.stakingCurrencyId;
170-
171-
const liquidToken = this.getToken(liquidCurrencyId);
172-
const stakingToken = this.getToken(stakingCurrencyId);
173-
174-
return this.getBlockHash(at).pipe(
175-
switchMap(() => {
176-
return combineLatest({
177-
stakingTokenPrice: this.queryPriceFromOracle(stakingToken, at),
178-
env: new Homa(this.api, this as any).env$
179-
});
180-
}),
181-
map(({ stakingTokenPrice, env }) => {
182-
return {
183-
token: liquidToken,
184-
price: stakingTokenPrice.times(env.exchangeRate)
185-
};
186-
}),
187-
shareReplay(1)
188-
);
189-
});
190-
191140
public queryDexPool = memoize((token1: MaybeCurrency, token2: MaybeCurrency, at?: number) => {
192141
const token1CurrencyId = forceToCurrencyId(this.api, token1);
193142
const token2CurrencyId = forceToCurrencyId(this.api, token2);

packages/sdk/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"homepage": "https://github.com/AcalaNetwork/acala.js",
2020
"dependencies": {
2121
"@acala-network/api": "4.1.8-9",
22-
"@acala-network/eth-providers": "^2.5.4",
22+
"@acala-network/eth-providers": "^2.5.9",
2323
"@acala-network/type-definitions": "4.1.8-9",
2424
"@ethersproject/bignumber": "^5.7.0",
2525
"@polkadot/api": "^9.9.1",
Lines changed: 50 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,137 +1,75 @@
1-
import { Observable, ReplaySubject, Subscription, firstValueFrom, combineLatest } from 'rxjs';
2-
import { map, filter } from 'rxjs/operators';
3-
import { PriceProvider } from './types';
4-
import { TimestampedValue } from '@open-web3/orml-types/interfaces';
5-
import {
6-
AnyApi,
7-
FixedPointNumber,
8-
FixedPointNumber as FN,
9-
forceToCurrencyName,
10-
isLiquidCrowdloanName,
11-
MaybeCurrency
12-
} from '@acala-network/sdk-core';
13-
import { OracleKey } from '@acala-network/types/interfaces';
1+
import { Observable, firstValueFrom, of } from 'rxjs';
2+
import { Option } from '@polkadot/types-codec';
3+
import { map, switchMap } from 'rxjs/operators';
4+
import { OracleConfig, PriceProvider } from './types';
5+
import { AnyApi, FixedPointNumber, FixedPointNumber as FN, Token } from '@acala-network/sdk-core';
146
import { Storage } from '../../utils/storage';
15-
import { AcalaPrimitivesCurrencyCurrencyId } from '@polkadot/types/lookup';
16-
import { SignedBlock } from '@polkadot/types/interfaces';
17-
import { getAllLiquidCrowdloanTokenPrice } from '../utils/get-liquid-crowdloan-token-price';
7+
import { OrmlOracleModuleTimestampedValue } from '@polkadot/types/lookup';
8+
import { subscribeLiquidCrowdloanTokenPrice } from '../utils/get-liquid-crowdloan-token-price';
9+
import { TokenProvider } from '../token-provider/type';
10+
11+
const DEFAULT_ORACLE_STRATEGIES: OracleConfig['strategies'] = {
12+
taiKSM: ['AS', 'KSM'],
13+
tDOT: ['AS', 'DOT'],
14+
lcDOT: ['LIQUID_CROWDLOAN', 13]
15+
};
1816

1917
export class OraclePriceProvider implements PriceProvider {
2018
private api: AnyApi;
21-
private oracleProvider: string;
22-
private subject: ReplaySubject<Record<string, FN>>;
23-
private liquidCrowdloanSubject: ReplaySubject<Record<string, FN>>;
24-
private processSubscriber: Subscription;
25-
private crowdloanPriceProcessSubscriber: Subscription;
26-
private consts: {
27-
stakingCurrency: AcalaPrimitivesCurrencyCurrencyId;
28-
liquidCurrency: AcalaPrimitivesCurrencyCurrencyId;
29-
};
19+
private stakingToken: Token;
20+
private strategies: OracleConfig['strategies'];
21+
private tokenProvider: TokenProvider;
3022

31-
constructor(api: AnyApi, oracleProvider = 'Aggregated') {
23+
constructor(api: AnyApi, config: OracleConfig) {
3224
this.api = api;
33-
this.oracleProvider = oracleProvider;
34-
this.subject = new ReplaySubject<Record<string, FN>>(1);
35-
this.liquidCrowdloanSubject = new ReplaySubject(1);
36-
37-
this.consts = {
38-
stakingCurrency: api.consts.prices.getStakingCurrencyId,
39-
liquidCurrency: api.consts.prices.getLiquidCurrencyId
40-
};
41-
42-
this.processSubscriber = this.process();
43-
this.crowdloanPriceProcessSubscriber = this.liquidCrowdloanPriceProcess();
44-
}
4525

46-
public unsub(): void {
47-
this.processSubscriber.unsubscribe();
48-
this.crowdloanPriceProcessSubscriber.unsubscribe();
26+
this.stakingToken = config.stakingToken;
27+
this.tokenProvider = config.tokenPrivoder;
28+
this.strategies = config?.strategies || DEFAULT_ORACLE_STRATEGIES;
4929
}
5030

51-
private oraclePrice$(name: string) {
52-
return this.subject.pipe(
53-
filter((values) => !!values),
54-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
55-
map((values) => values![name])
56-
);
57-
}
58-
59-
private liquidCrowdloanPriceProcess = () => {
60-
const storage$ = Storage.create<SignedBlock>({
31+
private queryFormOracle(token: Token) {
32+
const storage$ = Storage.create<Option<OrmlOracleModuleTimestampedValue>>({
6133
api: this.api,
62-
path: 'rpc.chain.getBlock',
63-
params: []
34+
path: 'query.acalaOracle.values',
35+
params: [token.toChainData()]
6436
}).observable;
6537

66-
return combineLatest({
67-
signedBlock: storage$,
68-
stakingTokenPrice: this.oraclePrice$(forceToCurrencyName(this.consts.stakingCurrency))
69-
}).subscribe({
70-
next: ({ signedBlock, stakingTokenPrice }) => {
71-
if (!stakingTokenPrice) return;
72-
73-
const prices = getAllLiquidCrowdloanTokenPrice(this.api, signedBlock, stakingTokenPrice);
38+
return storage$.pipe(
39+
map((value) => {
40+
const price = value.isEmpty ? '0' : value.value.value.toString();
7441

75-
if (prices) {
76-
// update all crowdloan token price
77-
this.liquidCrowdloanSubject.next(prices);
78-
}
79-
}
80-
});
81-
};
42+
return FixedPointNumber.fromInner(price, 18);
43+
})
44+
);
45+
}
8246

83-
private process = () => {
84-
const storage$ = Storage.create<[[OracleKey, TimestampedValue]]>({
85-
api: this.api,
86-
path: 'rpc.oracle.getAllValues',
87-
params: [this.oracleProvider],
88-
events: [{ section: '*', method: 'NewFeedData' }]
89-
}).observable;
47+
public subscribe(token: Token): Observable<FN> {
48+
const strategies = this.strategies;
9049

91-
return storage$.subscribe({
92-
next: (result) => {
93-
const formated = Object.fromEntries(
94-
result.map((item) => {
95-
const currency = forceToCurrencyName(item[0]);
96-
const price = FN.fromInner(
97-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
98-
(item[1]?.value as any)?.value.toString() || '0'
99-
);
50+
if (!strategies) return of(FixedPointNumber.ZERO);
10051

101-
return [currency, price];
102-
})
103-
);
104-
this.subject.next(formated);
105-
}
106-
});
107-
};
52+
const symbol = token.symbol;
10853

109-
public subscribe(currency: MaybeCurrency): Observable<FN> {
110-
return combineLatest({
111-
oracle: this.subject,
112-
liquidCrowdloanPrices: this.liquidCrowdloanSubject
113-
}).pipe(
114-
filter(({ oracle }) => oracle !== undefined),
115-
map(({ oracle, liquidCrowdloanPrices }) => {
116-
const name = forceToCurrencyName(currency);
54+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
55+
const [strategy, addon] = strategies[symbol] || ['STORAGE', ''];
11756

118-
if (isLiquidCrowdloanName(name)) {
119-
return liquidCrowdloanPrices[name] || FixedPointNumber.ZERO;
120-
}
57+
if (strategy === 'AS') {
58+
return this.subscribe(this.tokenProvider.getToken(addon as string));
59+
}
12160

122-
// TODO: should check the token symbol
123-
if (name === 'sa://0') {
124-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
125-
return oracle!.KSM || FixedPointNumber.ZERO;
126-
}
61+
if (strategy === 'LIQUID_CROWDLOAN') {
62+
return this.subscribe(this.stakingToken).pipe(
63+
switchMap((price) => {
64+
return subscribeLiquidCrowdloanTokenPrice(this.api, price, addon as number);
65+
})
66+
);
67+
}
12768

128-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
129-
return oracle![forceToCurrencyName(currency)] || FixedPointNumber.ZERO;
130-
})
131-
);
69+
return this.queryFormOracle(token);
13270
}
13371

134-
public async query(currency: MaybeCurrency): Promise<FN> {
72+
public async query(currency: Token): Promise<FN> {
13573
return firstValueFrom(this.subscribe(currency));
13674
}
13775
}
Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Observable } from 'rxjs';
22
import { FixedPointNumber as FN, Token } from '@acala-network/sdk-core';
3+
import { TokenProvider } from '../token-provider/type';
34

45
export interface PriceProvider {
56
subscribe(currency: Token): Observable<FN>;
@@ -8,7 +9,15 @@ export interface PriceProvider {
89

910
export enum PriceProviderType {
1011
'AGGREGATE',
11-
'MARKET', // query price form market
12+
'MARKET', // query prices form market
1213
'ORACLE', // query oracle feed prices
1314
'DEX' // query price form dex
1415
}
16+
17+
export type OracleStrategy = 'AS' | 'STORAGE' | 'LIQUID_CROWDLOAN';
18+
19+
export interface OracleConfig {
20+
stakingToken: Token;
21+
tokenPrivoder: TokenProvider;
22+
strategies?: Record<string, [OracleStrategy, any]>;
23+
}

packages/sdk/src/wallet/utils/get-liquid-crowdloan-token-price.ts

Lines changed: 1 addition & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
1-
import { AnyApi, createLiquidCrowdloanName, FixedPointNumber } from '@acala-network/sdk-core';
1+
import { AnyApi, FixedPointNumber } from '@acala-network/sdk-core';
22
import { u128 } from '@polkadot/types-codec';
33
import { Extrinsic, SignedBlock } from '@polkadot/types/interfaces';
44
import { CumulusPrimitivesParachainInherentParachainInherentData } from '@polkadot/types/lookup';
55
import { Storage } from '../../utils/storage';
66
import { map } from 'rxjs/operators';
77

8-
// current crowdloan token lease
9-
const CROWDLOAN_TOKEN_LEASE: number[] = [13];
10-
118
const LEASE_BLOCK_NUMBERS: Record<number, number> = {
129
13: 17_856_000
1310
};
@@ -34,36 +31,6 @@ const getLiquidCrowdloanPrice = (
3431
return stakingCurrencyPrice.mul(discount);
3532
};
3633

37-
export const getAllLiquidCrowdloanTokenPrice = (
38-
api: AnyApi,
39-
block: SignedBlock,
40-
stakingTokenPrice: FixedPointNumber
41-
): Record<string, FixedPointNumber> | undefined => {
42-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
43-
const extrinsic = block.block.extrinsics.find((item) => {
44-
return item.method.section === 'parachainSystem' && item.method.method === 'setValidationData';
45-
}) as Extrinsic | undefined;
46-
47-
if (extrinsic) {
48-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
49-
const args = extrinsic.method.args[0] as any as CumulusPrimitivesParachainInherentParachainInherentData;
50-
const relayChainBlockNumber = args?.validationData?.relayParentNumber?.toNumber();
51-
52-
const prices = Object.fromEntries(
53-
CROWDLOAN_TOKEN_LEASE.map((lease) => {
54-
return [
55-
createLiquidCrowdloanName(lease),
56-
getLiquidCrowdloanPrice(api, lease, relayChainBlockNumber, stakingTokenPrice)
57-
];
58-
})
59-
);
60-
61-
return prices;
62-
}
63-
64-
return undefined;
65-
};
66-
6734
export const subscribeLiquidCrowdloanTokenPrice = (api: AnyApi, stakingTokenPrice: FixedPointNumber, lease: number) => {
6835
const block$ = Storage.create<SignedBlock>({
6936
api: api,

packages/sdk/src/wallet/wallet.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,10 @@ export class Wallet implements BaseSDK {
8585
const market = new MarketPriceProvider();
8686
const dex = new DexPriceProvider(this.liquidity);
8787
const aggregate = new AggregateProvider({ market, dex });
88-
const oracle = new OraclePriceProvider(this.api);
88+
const oracle = new OraclePriceProvider(this.api, {
89+
stakingToken: this.getPresetTokens(true).stableToken,
90+
tokenPrivoder: this.tokenProvider
91+
});
8992

9093
this.priceProviders = {
9194
// default price provider
@@ -254,8 +257,8 @@ export class Wallet implements BaseSDK {
254257
return firstValueFrom(this.subscribeSuggestInput(token, address, isAllowDeath, fee));
255258
}
256259

257-
public getPresetTokens(): PresetTokens {
258-
if (this.isReady$.value === false) throw new SDKNotReady('wallet');
260+
public getPresetTokens(ignoreCheck = false): PresetTokens {
261+
if (!ignoreCheck && this.isReady$.value === false) throw new SDKNotReady('wallet');
259262

260263
const tokens = this.tokenProvider.getAllTokens();
261264

0 commit comments

Comments
 (0)