@aurigami/sdk 1.6.0-no-src → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,317 @@
1
+ import BigNumber from 'bignumber.js';
2
+ import { Contract, providers, BigNumber as BN, ethers } from 'ethers';
3
+ import {
4
+ DECIMAL_PRECISION,
5
+ networkAddresses,
6
+ PRICE_WHITELISTED,
7
+ } from '../consts/constants';
8
+ import OracleABI from '../abis/Oracle.json';
9
+ import {
10
+ isSameAddress,
11
+ decimalFactor,
12
+ getDecimal,
13
+ assert,
14
+ devLog,
15
+ } from './helpers';
16
+ import { IUniswapV2Pair, PriceOracle } from '@aurigami/contracts/typechain';
17
+ import { TokenAmount } from '../entities/tokenAmount';
18
+ import { Address, TokenValuation } from '../types';
19
+ import IUniswapV2PairABI from '../abis/IUniswapV2Pair.json';
20
+ import AuriLensABI from '@aurigami/contracts/artifacts/contracts/AuriLens.sol/AuriLens.json';
21
+ import axios from 'axios';
22
+
23
+ const hardcodePLYPrice = true;
24
+ export async function fetchPriceFromCoingeckoAPI(
25
+ id: string
26
+ ): Promise<BigNumber> {
27
+ const price = await axios
28
+ .get(
29
+ `https://api.coingecko.com/api/v3/simple/price?ids=${id}&vs_currencies=usd`
30
+ )
31
+ .then((res: any) => {
32
+ return res.data;
33
+ });
34
+
35
+ return new BigNumber(price[id].usd);
36
+ }
37
+
38
+ export async function fetchPriceFromCoingecko(id: Address): Promise<BigNumber> {
39
+ switch (id.toLowerCase()) {
40
+ case networkAddresses.tokens.AURORA:
41
+ return fetchPriceFromCoingeckoAPI('aurora-near');
42
+
43
+ case networkAddresses.tokens.TRI:
44
+ return fetchPriceFromCoingeckoAPI('trisolaris');
45
+
46
+ case networkAddresses.tokens.META:
47
+ return fetchPriceFromCoingeckoAPI('meta-pool');
48
+
49
+ default:
50
+ throw Error(`Unknown token ${id} in fetchPriceFromCoingecko`);
51
+ }
52
+ }
53
+
54
+ export async function fetchLPPositions(
55
+ LPAddress: Address,
56
+ provider: providers.Provider
57
+ ): Promise<{
58
+ token0: Address;
59
+ token1: Address;
60
+ reserve0: BN;
61
+ reserve1: BN;
62
+ totalSupply: BN;
63
+ }> {
64
+ const LPContract = new Contract(
65
+ LPAddress,
66
+ IUniswapV2PairABI.abi,
67
+ provider
68
+ ) as IUniswapV2Pair;
69
+ var promises: any[] = [];
70
+ promises.push(LPContract.token0());
71
+ promises.push(LPContract.token1());
72
+ promises.push(LPContract.getReserves());
73
+ promises.push(LPContract.totalSupply());
74
+ const values: any[] = await Promise.all(promises);
75
+
76
+ return {
77
+ token0: values[0] as string,
78
+ token1: values[1] as string,
79
+ reserve0: (values[2] as { reserve0: BN; reserve1: BN }).reserve0,
80
+ reserve1: (values[2] as { reserve0: BN; reserve1: BN }).reserve1,
81
+ totalSupply: values[3] as BN,
82
+ };
83
+ }
84
+
85
+ export async function fetchLPPrice(
86
+ address: Address,
87
+ provider: providers.Provider
88
+ ): Promise<BigNumber> {
89
+ var LPInfo = await fetchLPPositions(address, provider);
90
+ const LPDecimal = getDecimal(address);
91
+
92
+ async function fetchLPPriceBy(tokenReverse: {
93
+ address: string;
94
+ reserve: BN;
95
+ }): Promise<BigNumber> {
96
+ if (tokenReverse.reserve.isZero()) return new BigNumber(0);
97
+
98
+ // Should NOT call `fetchPrice`, it will cause an infinite loop
99
+ // because the token can be PLY. To calculate PLY price, we need to
100
+ // call `fetchPrice` again...
101
+ const tokenPrice: BigNumber = await _fetchPrice(
102
+ tokenReverse.address,
103
+ provider
104
+ );
105
+ const tokenDecimal = getDecimal(tokenReverse.address);
106
+
107
+ // reserveUSD will be divided by 10^tokenDecimal later for more precision
108
+ const reserveUSD: BigNumber = tokenPrice
109
+ .multipliedBy(tokenReverse.reserve.toString())
110
+ .multipliedBy(2);
111
+ return reserveUSD
112
+ .multipliedBy(decimalFactor(LPDecimal))
113
+ .dividedBy(new BigNumber(LPInfo.totalSupply.toString()))
114
+ .dividedBy(decimalFactor(tokenDecimal));
115
+ }
116
+
117
+ try {
118
+ // MUST await here
119
+ return await fetchLPPriceBy({
120
+ address: LPInfo.token0,
121
+ reserve: LPInfo.reserve0,
122
+ });
123
+ } catch (e) {
124
+ devLog(`fetchLPPriceBy ${LPInfo.token0} failed`, e);
125
+ try {
126
+ // MUST await here
127
+ return await fetchLPPriceBy({
128
+ address: LPInfo.token1,
129
+ reserve: LPInfo.reserve1,
130
+ });
131
+ } catch (e) {
132
+ devLog(`fetchLPPriceBy ${LPInfo.token1} failed`, e);
133
+ throw Error(`Unable to fetch price for both tokens of LP ${address}`);
134
+ }
135
+ }
136
+ }
137
+
138
+ function processPriceFromOracle(rawPrice: BN, underlyingDecimal: number) {
139
+ return new BigNumber(rawPrice.toString()).div(
140
+ decimalFactor(36 - underlyingDecimal)
141
+ );
142
+ }
143
+ export async function fetchPriceFromOracle(
144
+ auTokenAddress: Address,
145
+ underlyingDecimal: number,
146
+ provider: providers.Provider
147
+ ): Promise<BigNumber> {
148
+ const oracleContract: PriceOracle = new Contract(
149
+ networkAddresses.misc.oracle,
150
+ OracleABI.abi,
151
+ provider
152
+ ) as PriceOracle;
153
+ const rawPrice = await oracleContract.getUnderlyingPrice(auTokenAddress);
154
+ return processPriceFromOracle(rawPrice, underlyingDecimal);
155
+ }
156
+
157
+ export async function fetchUnderlyingTokensPrices(
158
+ provider: providers.Provider
159
+ ): Promise<
160
+ {
161
+ auToken: string;
162
+ underlyingPrice: BigNumber;
163
+ }[]
164
+ > {
165
+ const auriLens: Contract = new Contract(
166
+ networkAddresses.misc.AURILENS,
167
+ AuriLensABI.abi,
168
+ provider
169
+ );
170
+ const auTokenAddresses = networkAddresses.auTokens.map(
171
+ (auToken) => auToken.address
172
+ );
173
+ const underlyingDecimals = networkAddresses.auTokens.map((auToken) =>
174
+ getDecimal(auToken.underlying)
175
+ );
176
+ const rawPrices = await auriLens.auTokenUnderlyingPriceAll(auTokenAddresses);
177
+ return rawPrices.map((res: any, ind: number) => {
178
+ return {
179
+ auToken: res.auToken,
180
+ underlyingPrice: processPriceFromOracle(
181
+ res.underlyingPrice,
182
+ underlyingDecimals[ind]
183
+ ),
184
+ };
185
+ });
186
+ }
187
+
188
+ export async function fetchStNEARPrice(): Promise<BigNumber> {
189
+ const ratioPromise = axios
190
+ .get('https://validators.narwallets.com/metrics_json')
191
+ .then((res) => res.data);
192
+ const nearPricePromise = fetchPriceFromCoingeckoAPI('near');
193
+ const [ratioRes, nearPrice]: [any, BigNumber] = await Promise.all([
194
+ ratioPromise,
195
+ nearPricePromise,
196
+ ]);
197
+ const ratio = new BigNumber(ratioRes.st_near_price);
198
+ return ratio.multipliedBy(nearPrice);
199
+ }
200
+
201
+ function shouldFetchFromCoingecko(address: string) {
202
+ address = address.toLowerCase();
203
+ return (
204
+ address == networkAddresses.tokens.AURORA ||
205
+ address == networkAddresses.tokens.TRI ||
206
+ address == networkAddresses.tokens.META
207
+ );
208
+ }
209
+
210
+ /**
211
+ * Fetch price from Oracle or Coingecko
212
+ */
213
+ async function _fetchPrice(
214
+ address: Address,
215
+ provider: providers.Provider
216
+ ): Promise<BigNumber> {
217
+ assert(
218
+ PRICE_WHITELISTED.has(address.toLocaleLowerCase()),
219
+ `Address ${address} is not whitelisted for price fetching`
220
+ );
221
+
222
+ if (isSameAddress(address, networkAddresses.tokens.stNEAR))
223
+ return fetchStNEARPrice();
224
+ if (shouldFetchFromCoingecko(address))
225
+ return fetchPriceFromCoingecko(address);
226
+
227
+ const matchingAuToken = networkAddresses.auTokens.find((auToken) => {
228
+ return isSameAddress(auToken.underlying, address);
229
+ });
230
+
231
+ if (matchingAuToken !== undefined) {
232
+ return fetchPriceFromOracle(
233
+ matchingAuToken.address,
234
+ getDecimal(address),
235
+ provider
236
+ );
237
+ } else {
238
+ throw Error(`Unable to fetch price for ${address}`);
239
+ }
240
+ }
241
+
242
+ export async function fetchPrice(
243
+ address: Address,
244
+ provider: providers.Provider
245
+ ): Promise<BigNumber> {
246
+ if (isSameAddress(address, networkAddresses.tokens.PLYWNEAR)) {
247
+ return fetchLPPrice(address, provider);
248
+ } else if (isSameAddress(address, networkAddresses.tokens.PLY)) {
249
+ return fetchPLYPrice(provider);
250
+ } else if (isSameAddress(address, networkAddresses.tokens.PULP)) {
251
+ return fetchPULPPrice(provider);
252
+ }
253
+ return _fetchPrice(address, provider);
254
+ }
255
+
256
+ export async function fetchPLYPrice(
257
+ provider: providers.Provider
258
+ ): Promise<BigNumber> {
259
+ //TODO: Fetch PLY price from CEX?
260
+ return fetchToken0PriceByLP(networkAddresses.tokens.PLYWNEAR, provider);
261
+ }
262
+
263
+ export async function fetchPULPPrice(
264
+ provider: providers.Provider
265
+ ): Promise<BigNumber> {
266
+ //TODO: Calculate PULP price
267
+ return new BigNumber(0);
268
+ }
269
+
270
+ export async function fetchToken0PriceByLP(
271
+ LP: Address,
272
+ provider: providers.Provider
273
+ ): Promise<BigNumber> {
274
+ const LPInfo = await fetchLPPositions(LP, provider);
275
+
276
+ if (LPInfo.reserve1.isZero()) {
277
+ return new BigNumber(0);
278
+ }
279
+
280
+ const token0Decimal = getDecimal(LPInfo.token0);
281
+ const token1Decimal = getDecimal(LPInfo.token1);
282
+
283
+ // (token0Price * reserve0) / 10^decimal0 = (token1Price * reserve1) / 10^decimal1
284
+ // token0Price = (token1Price * reserve1) * 10^decimal0 / 10^decimal1 / reserve0
285
+ return (await fetchPrice(LPInfo.token1, provider))
286
+ .multipliedBy(LPInfo.reserve1.toString())
287
+ .multipliedBy(decimalFactor(token0Decimal))
288
+ .dividedBy(decimalFactor(token1Decimal))
289
+ .dividedBy(LPInfo.reserve0.toString());
290
+ }
291
+
292
+ export async function fetchValuation(
293
+ tokenAmount: TokenAmount,
294
+ provider: providers.Provider
295
+ ): Promise<BigNumber> {
296
+ if (tokenAmount.rawAmount() == '0') return new BigNumber(0);
297
+ var price = await fetchPrice(tokenAmount.token.address, provider);
298
+ return calcValuation(tokenAmount, price);
299
+ }
300
+
301
+ export function calcValuation(tokenAmount: TokenAmount, price: BigNumber) {
302
+ return new BigNumber(tokenAmount.formattedAmount()).multipliedBy(price);
303
+ }
304
+
305
+ export async function valuateToken(
306
+ tokenAmount: TokenAmount,
307
+ provider: providers.Provider
308
+ ): Promise<TokenValuation> {
309
+ let tokenValuation = await fetchValuation(tokenAmount, provider);
310
+ return {
311
+ tokenAmount: tokenAmount,
312
+ currencyAmount: {
313
+ currency: 'USD',
314
+ amount: tokenValuation.toFixed(DECIMAL_PRECISION),
315
+ },
316
+ };
317
+ }
@@ -0,0 +1,70 @@
1
+ import { BlockchainEntityRead } from '../entities/BlockchainEntity';
2
+ import { NetworkConnection } from '../types';
3
+ import { AuToken } from '@aurigami/contracts/typechain';
4
+ import AuTokenABI from '@aurigami/contracts/artifacts/contracts/AuToken.sol/AuToken.json';
5
+ import { BigNumber as BN, Contract } from 'ethers';
6
+
7
+ type SimpleTransfer = {
8
+ from: string;
9
+ to: string;
10
+ amount: BN;
11
+ blockNumber: number;
12
+ };
13
+
14
+ export class TransferEventQuery extends BlockchainEntityRead {
15
+ auToken: AuToken;
16
+
17
+ constructor(tokenAddr: string, networkConnection: NetworkConnection) {
18
+ super(networkConnection);
19
+ this.auToken = new Contract(
20
+ tokenAddr,
21
+ AuTokenABI.abi,
22
+ this._networkConnection.provider
23
+ ) as AuToken;
24
+ }
25
+
26
+ /**
27
+ * query ERC20 transfers from/to an address & auto convert them into SimpleTransfer.
28
+ * The result will be sorted by blocknumber.
29
+ */
30
+ async queryERC20TransfersOf(
31
+ address: string,
32
+ fromBlock: number,
33
+ toBlock: number
34
+ ): Promise<SimpleTransfer[]> {
35
+ const filters = [
36
+ this.auToken.filters.Transfer(address, null),
37
+ this.auToken.filters.Transfer(null, address),
38
+ ];
39
+
40
+ const events = (
41
+ await Promise.all(
42
+ filters.map((filter) =>
43
+ this.auToken.queryFilter(filter, fromBlock, toBlock)
44
+ )
45
+ )
46
+ ).flat();
47
+
48
+ let results: SimpleTransfer[] = events.map((event) => {
49
+ return {
50
+ from: event.args![0],
51
+ to: event.args![1],
52
+ amount: event.args![2],
53
+ blockNumber: event.blockNumber,
54
+ };
55
+ });
56
+
57
+ results.sort((a, b) => a.blockNumber - b.blockNumber);
58
+
59
+ return results;
60
+ }
61
+
62
+ /**
63
+ * Get the balance of an address at a given block number.
64
+ *
65
+ * Note that this will return the balance after the block get mined.
66
+ */
67
+ async queryERC20BalanceAt(address: string, endBlock: number): Promise<BN> {
68
+ return this.auToken.balanceOf(address, { blockTag: endBlock + 1 });
69
+ }
70
+ }
package/src/index.ts ADDED
@@ -0,0 +1,13 @@
1
+ import BigNumber from 'bignumber.js';
2
+ import { ethers } from 'ethers';
3
+ import { Logger } from 'ethers/lib/utils';
4
+
5
+ ethers.utils.Logger.setLogLevel(Logger.levels.ERROR);
6
+ BigNumber.config({ DECIMAL_PLACES: 50 });
7
+
8
+ export * from './consts';
9
+ export * from './contracts';
10
+ export * from './entities';
11
+ export * from './helpers';
12
+ export * from './types';
13
+ export * from './SDK';
@@ -0,0 +1,34 @@
1
+ {
2
+ "auUSDC": {
3
+ "address": "0x4f0d864b1ABf4B701799a0b30b57A22dFEB5917b",
4
+ "exchangeRate": "200810680770707",
5
+ "deploymentBlock": "60501576",
6
+ "underlying": {
7
+ "minDeposit": "1000000000"
8
+ }
9
+ },
10
+ "auETH": {
11
+ "address": "0xca9511B610bA5fc7E311FDeF9cE16050eE4449E9",
12
+ "exchangeRate": "200390813218617347858934298",
13
+ "deploymentBlock": "60501625",
14
+ "underlying": {
15
+ "minDeposit": "300000000000000000"
16
+ }
17
+ },
18
+ "auWBTC": {
19
+ "address": "0xCFb6b0498cb7555e7e21502E0F449bf28760Adbb",
20
+ "exchangeRate": "20050472926613996",
21
+ "deploymentBlock": "60501670",
22
+ "underlying": {
23
+ "minDeposit": "2000000"
24
+ }
25
+ },
26
+ "auUSDT": {
27
+ "address": "0xaD5A2437Ff55ed7A8Cad3b797b3eC7c5a19B1c54",
28
+ "exchangeRate": "200868369820223",
29
+ "deploymentBlock": "60501723",
30
+ "underlying": {
31
+ "minDeposit": "1000000000"
32
+ }
33
+ }
34
+ }