@merkl/api 0.18.5 → 0.18.7
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.
- package/dist/src/eden/index.d.ts +39 -54
- package/dist/src/factories/opportunityMetadata/implementations/Ajna.js +1 -1
- package/dist/src/factories/opportunityMetadata/implementations/Badger.js +1 -1
- package/dist/src/index.d.ts +7 -10
- package/dist/src/modules/v4/campaign/campaign.repository.js +1 -1
- package/dist/src/modules/v4/enso/enso.service.js +5 -5
- package/dist/src/modules/v4/kyberzap/kyberzap.service.js +5 -5
- package/dist/src/modules/v4/opportunity/opportunity.controller.d.ts +0 -7
- package/dist/src/modules/v4/opportunity/opportunity.repository.d.ts +0 -7
- package/dist/src/modules/v4/opportunity/opportunity.service.d.ts +0 -28
- package/dist/src/modules/v4/opportunity/opportunity.service.js +1 -1
- package/dist/src/modules/v4/price/price.controller.js +1 -1
- package/dist/src/modules/v4/price/price.repository.d.ts +4 -1
- package/dist/src/modules/v4/price/price.repository.js +6 -2
- package/dist/src/modules/v4/price/price.service.d.ts +4 -1
- package/dist/src/modules/v4/price/price.service.js +2 -2
- package/dist/src/modules/v4/reward/reward.service.js +1 -1
- package/dist/src/modules/v4/router.d.ts +7 -10
- package/dist/src/modules/v4/token/token.controller.d.ts +7 -3
- package/dist/src/modules/v4/token/token.controller.js +5 -1
- package/dist/src/modules/v4/token/token.model.d.ts +1 -0
- package/dist/src/modules/v4/token/token.model.js +1 -0
- package/dist/src/modules/v4/token/token.repository.d.ts +2 -0
- package/dist/src/modules/v4/token/token.repository.js +42 -28
- package/dist/src/modules/v4/token/token.service.d.ts +29 -46
- package/dist/src/modules/v4/token/token.service.js +120 -106
- package/dist/src/routes/v3/app.js +1 -1
- package/dist/src/utils/prices/priceService.js +1 -1
- package/dist/tsconfig.package.tsbuildinfo +1 -1
- package/package.json +1 -1
@@ -93,7 +93,11 @@ export const TokenController = new Elysia({ prefix: "/tokens", detail: { tags: [
|
|
93
93
|
beforeHandle: BackOfficeGuard,
|
94
94
|
detail: { hide: true },
|
95
95
|
})
|
96
|
-
.
|
96
|
+
.post("/icons", async () => await TokenService.tryToFillWithCoingeckoIcons(), {
|
97
|
+
headers: AuthorizationHeadersDto,
|
98
|
+
beforeHandle: BackOfficeGuard,
|
99
|
+
detail: { hide: true },
|
100
|
+
})
|
97
101
|
.group("/webhooks", app => {
|
98
102
|
return app.post("/notion", async ({ body }) => TokenService.notionWebhook(body), {
|
99
103
|
body: NotionWebhookDto,
|
@@ -41,6 +41,7 @@ export declare const GetTokenQueryDto: import("@sinclair/typebox").TObject<{
|
|
41
41
|
name: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
|
42
42
|
verified: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
43
43
|
test: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
44
|
+
missingIcons: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
|
44
45
|
}>;
|
45
46
|
export declare const TokenDto: import("@sinclair/typebox").TObject<{
|
46
47
|
address: import("@sinclair/typebox").TString;
|
@@ -77,6 +77,8 @@ export declare abstract class TokenRepository {
|
|
77
77
|
* @param chainId
|
78
78
|
*/
|
79
79
|
static updateAddressPrices(address: string, price: number, chainId?: number): Promise<import("database/api/.generated/runtime/library").GetBatchResult>;
|
80
|
+
static updateMissingIconsPerSymbol(symbol: string, icon: string): Promise<import("database/api/.generated/runtime/library").GetBatchResult>;
|
81
|
+
static fetchIconFromCoingeckoTicker(coingeckoTicker: string): Promise<any>;
|
80
82
|
static findChains(): Promise<Record<string, ChainId>>;
|
81
83
|
/**
|
82
84
|
* create a token on database
|
@@ -2,35 +2,8 @@ import { getTokenInfo } from "@/libs/tokens/tokenInfo";
|
|
2
2
|
import { executeSimple } from "@/utils/execute";
|
3
3
|
import { apiDbClient } from "@/utils/prisma";
|
4
4
|
import { NETWORK_LABELS } from "@sdk";
|
5
|
+
import axios from "axios";
|
5
6
|
export class TokenRepository {
|
6
|
-
/**
|
7
|
-
* Fetch token metadata from onchain
|
8
|
-
* @param chainId
|
9
|
-
* @param address
|
10
|
-
* @returns
|
11
|
-
*/
|
12
|
-
static async getTokenInfo(token) {
|
13
|
-
//TODO: replace with onchain module
|
14
|
-
const calls = getTokenInfo(token.address);
|
15
|
-
//TODO: replace with onchain module
|
16
|
-
return executeSimple(token.chainId, calls);
|
17
|
-
}
|
18
|
-
/**
|
19
|
-
* Read token from database
|
20
|
-
* @param chainId
|
21
|
-
* @param address
|
22
|
-
*/
|
23
|
-
static async findUnique(id) {
|
24
|
-
return (await apiDbClient.token.findUnique({ where: { id } })) ?? undefined;
|
25
|
-
}
|
26
|
-
/**
|
27
|
-
* Read token from database
|
28
|
-
* @param chainId
|
29
|
-
* @param address
|
30
|
-
*/
|
31
|
-
static async findUniqueOrThrow(id) {
|
32
|
-
return await apiDbClient.token.findUniqueOrThrow({ where: { id } });
|
33
|
-
}
|
34
7
|
static #transformQueryToPrismaFilters(query) {
|
35
8
|
const ids = query.id ? query.id.map(id => ({ id })) : [];
|
36
9
|
return {
|
@@ -58,9 +31,38 @@ export class TokenRepository {
|
|
58
31
|
name: query.name ? { contains: query.name, mode: "insensitive" } : undefined,
|
59
32
|
verified: query.verified ? { equals: query.verified } : undefined,
|
60
33
|
isTest: query.test ? { equals: query.test } : undefined,
|
34
|
+
icon: query.missingIcons ? { equals: "" } : undefined,
|
61
35
|
},
|
62
36
|
};
|
63
37
|
}
|
38
|
+
/**
|
39
|
+
* Fetch token metadata from onchain
|
40
|
+
* @param chainId
|
41
|
+
* @param address
|
42
|
+
* @returns
|
43
|
+
*/
|
44
|
+
static async getTokenInfo(token) {
|
45
|
+
//TODO: replace with onchain module
|
46
|
+
const calls = getTokenInfo(token.address);
|
47
|
+
//TODO: replace with onchain module
|
48
|
+
return executeSimple(token.chainId, calls);
|
49
|
+
}
|
50
|
+
/**
|
51
|
+
* Read token from database
|
52
|
+
* @param chainId
|
53
|
+
* @param address
|
54
|
+
*/
|
55
|
+
static async findUnique(id) {
|
56
|
+
return (await apiDbClient.token.findUnique({ where: { id } })) ?? undefined;
|
57
|
+
}
|
58
|
+
/**
|
59
|
+
* Read token from database
|
60
|
+
* @param chainId
|
61
|
+
* @param address
|
62
|
+
*/
|
63
|
+
static async findUniqueOrThrow(id) {
|
64
|
+
return await apiDbClient.token.findUniqueOrThrow({ where: { id } });
|
65
|
+
}
|
64
66
|
/**
|
65
67
|
* Read token from database by its symbol
|
66
68
|
* @param symbol
|
@@ -128,6 +130,18 @@ export class TokenRepository {
|
|
128
130
|
},
|
129
131
|
});
|
130
132
|
}
|
133
|
+
static async updateMissingIconsPerSymbol(symbol, icon) {
|
134
|
+
return await apiDbClient.token.updateMany({
|
135
|
+
data: { icon },
|
136
|
+
where: {
|
137
|
+
symbol: symbol,
|
138
|
+
icon: "",
|
139
|
+
},
|
140
|
+
});
|
141
|
+
}
|
142
|
+
static async fetchIconFromCoingeckoTicker(coingeckoTicker) {
|
143
|
+
return (await axios.get(`https://api.coingecko.com/api/v3/coins/${coingeckoTicker}`)).data.image.small;
|
144
|
+
}
|
131
145
|
static async findChains() {
|
132
146
|
const tokens = await apiDbClient.token.findMany({
|
133
147
|
select: { chainId: true, id: true },
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import type { Pricer } from "@/utils/pricer";
|
2
2
|
import { type ChainId } from "@sdk";
|
3
|
-
import type { NotionWebhookModel } from "./token.model";
|
4
|
-
import type {
|
3
|
+
import type { CreateTokenModel, NotionWebhookModel } from "./token.model";
|
4
|
+
import type { GetTokenQueryModel, Token, TokenModel, TokenUnique, UpdateTokenModel } from "./token.model";
|
5
5
|
import { TokenRepository } from "./token.repository";
|
6
6
|
export declare abstract class TokenService {
|
7
7
|
static hashId(token: TokenUnique): string;
|
@@ -54,6 +54,7 @@ export declare abstract class TokenService {
|
|
54
54
|
balance: bigint;
|
55
55
|
})[]>;
|
56
56
|
static fetchOnChain(token: TokenModel): Promise<Omit<Token["model"], "id">>;
|
57
|
+
static tryToFillWithCoingeckoIcons(): Promise<void>;
|
57
58
|
static fetchManyOnChain(chainId: ChainId, addresses: string[]): Promise<{
|
58
59
|
[address: string]: Omit<{
|
59
60
|
symbol: string;
|
@@ -91,49 +92,6 @@ export declare abstract class TokenService {
|
|
91
92
|
* @param excludeAddresses to not update symbols that defined using addresses
|
92
93
|
*/
|
93
94
|
static updatePrices(pricer: Pricer): Promise<void>;
|
94
|
-
/**
|
95
|
-
* Get all tokens from https://github.com/AngleProtocol/angle-token-list and override icons from it
|
96
|
-
* TODO: use the bucket
|
97
|
-
*/
|
98
|
-
static fillTokenAndIconsFromTokenList(): Promise<void>;
|
99
|
-
/**
|
100
|
-
* Create token on database
|
101
|
-
* @param chainId
|
102
|
-
* @param address
|
103
|
-
*/
|
104
|
-
static create(token: Omit<Token["model"], "id">): Promise<({
|
105
|
-
symbol: string;
|
106
|
-
id: string;
|
107
|
-
name: string | null;
|
108
|
-
icon: string;
|
109
|
-
chainId: number;
|
110
|
-
address: string;
|
111
|
-
decimals: number;
|
112
|
-
verified: boolean;
|
113
|
-
isTest: boolean;
|
114
|
-
isNative: boolean;
|
115
|
-
} & {
|
116
|
-
price?: number | null | undefined;
|
117
|
-
}) | undefined>;
|
118
|
-
/**
|
119
|
-
* Fetches symbol, address, decimals and creates token on database
|
120
|
-
* @param chainId
|
121
|
-
* @param address
|
122
|
-
*/
|
123
|
-
static fillAndCreate(token: CreateTokenModel): Promise<({
|
124
|
-
symbol: string;
|
125
|
-
id: string;
|
126
|
-
name: string | null;
|
127
|
-
icon: string;
|
128
|
-
chainId: number;
|
129
|
-
address: string;
|
130
|
-
decimals: number;
|
131
|
-
verified: boolean;
|
132
|
-
isTest: boolean;
|
133
|
-
isNative: boolean;
|
134
|
-
} & {
|
135
|
-
price?: number | null | undefined;
|
136
|
-
}) | undefined>;
|
137
95
|
/**
|
138
96
|
* Read token from database
|
139
97
|
* @param chainId
|
@@ -210,7 +168,7 @@ export declare abstract class TokenService {
|
|
210
168
|
* @param chainId
|
211
169
|
* @param address
|
212
170
|
*/
|
213
|
-
static
|
171
|
+
static findManyOrCreate(tokens: TokenModel[]): Promise<(Token["model"] | undefined)[]>;
|
214
172
|
static getValidRewardTokens(chainId: number): Promise<{
|
215
173
|
minimumAmountPerHour: any;
|
216
174
|
symbol: string;
|
@@ -254,4 +212,29 @@ export declare abstract class TokenService {
|
|
254
212
|
isNative: boolean;
|
255
213
|
price: number | null;
|
256
214
|
}>;
|
215
|
+
/**
|
216
|
+
* Fetches symbol, address, decimals and creates token on database
|
217
|
+
* @param chainId
|
218
|
+
* @param address
|
219
|
+
*/
|
220
|
+
static fillAndCreate(token: CreateTokenModel): Promise<({
|
221
|
+
symbol: string;
|
222
|
+
id: string;
|
223
|
+
name: string | null;
|
224
|
+
icon: string;
|
225
|
+
chainId: number;
|
226
|
+
address: string;
|
227
|
+
decimals: number;
|
228
|
+
verified: boolean;
|
229
|
+
isTest: boolean;
|
230
|
+
isNative: boolean;
|
231
|
+
} & {
|
232
|
+
price?: number | null | undefined;
|
233
|
+
}) | undefined>;
|
234
|
+
/**
|
235
|
+
* @deprecated Should be useless now that the token list is not used anymore
|
236
|
+
* Get all tokens from https://github.com/AngleProtocol/angle-token-list and override icons from it
|
237
|
+
* TODO: use the bucket
|
238
|
+
*/
|
239
|
+
static fillTokenAndIconsFromTokenList(): Promise<void>;
|
257
240
|
}
|
@@ -51,7 +51,7 @@ export class TokenService {
|
|
51
51
|
* Fetches tokens and include balances
|
52
52
|
*/
|
53
53
|
static async fetchTokensAndBalances(chainId, userAddress, addresses) {
|
54
|
-
const tokens = await TokenService.
|
54
|
+
const tokens = await TokenService.findManyOrCreate(addresses?.map(address => ({ chainId, address })));
|
55
55
|
return TokenService.fetchBalances(chainId, userAddress, tokens.filter(t => t !== undefined));
|
56
56
|
}
|
57
57
|
/**
|
@@ -63,7 +63,7 @@ export class TokenService {
|
|
63
63
|
const verifiedTokens = await TokenService.findMany({ chainId: chainId, verified: true });
|
64
64
|
const nativeTokens = await TokenService.findMany({ chainId: chainId, isNative: true });
|
65
65
|
const additionalTokens = !!additionalTokenAddresses?.length
|
66
|
-
? await TokenService.
|
66
|
+
? await TokenService.findManyOrCreate(additionalTokenAddresses?.map(address => ({ chainId, address })))
|
67
67
|
: [];
|
68
68
|
const allTokens = verifiedTokens
|
69
69
|
.concat(additionalTokens.filter(t => t !== undefined))
|
@@ -72,7 +72,6 @@ export class TokenService {
|
|
72
72
|
}
|
73
73
|
static async fetchOnChain(token) {
|
74
74
|
const onchainData = await TokenRepository.getTokenInfo(token);
|
75
|
-
//TODO: find a way to get the icon
|
76
75
|
return {
|
77
76
|
chainId: token.chainId,
|
78
77
|
address: token.address,
|
@@ -80,9 +79,36 @@ export class TokenService {
|
|
80
79
|
...Object.assign({ name: "unknown", decimals: 18, symbol: "UNKNOWN", verified: false, isTest: false, isNative: false }, onchainData),
|
81
80
|
};
|
82
81
|
}
|
82
|
+
static async tryToFillWithCoingeckoIcons() {
|
83
|
+
// 1 - Find coingecko price sources
|
84
|
+
let priceSources = await PriceService.findManyPriceSources({ method: "COINGECKO" });
|
85
|
+
// 2 - Find tokens with missing logos
|
86
|
+
const missingIcons = await TokenService.findMany({ missingIcons: true });
|
87
|
+
// 3 - Do the intersection of both
|
88
|
+
priceSources = priceSources.filter(p => !!missingIcons.find(t => t.symbol === p.symbol));
|
89
|
+
log.info(`found ${priceSources.length} tokens with missing icons on coingecko.`);
|
90
|
+
// 4 - Loop through each and try to get the icon
|
91
|
+
for (const priceSource of priceSources) {
|
92
|
+
const coingeckoTicker = priceSource.args?.ticker;
|
93
|
+
if (!!coingeckoTicker) {
|
94
|
+
try {
|
95
|
+
const icon = await TokenRepository.fetchIconFromCoingeckoTicker(coingeckoTicker);
|
96
|
+
if (icon.length > 0) {
|
97
|
+
const res = await TokenRepository.updateMissingIconsPerSymbol(priceSource.symbol, icon);
|
98
|
+
log.info(`updated ${res.count} tokens with icon ${icon} ${priceSource.symbol}`);
|
99
|
+
}
|
100
|
+
}
|
101
|
+
catch (e) {
|
102
|
+
console.error(e);
|
103
|
+
}
|
104
|
+
// Avoid rate limits, wait 1 min
|
105
|
+
await new Promise(resolve => setTimeout(resolve, 60_000));
|
106
|
+
}
|
107
|
+
}
|
108
|
+
}
|
83
109
|
static async fetchManyOnChain(chainId, addresses) {
|
84
110
|
const tokens = {};
|
85
|
-
//Batch onchain calls together when multiples
|
111
|
+
// Batch onchain calls together when multiples
|
86
112
|
for (const address of addresses) {
|
87
113
|
tokens[address] = await TokenService.fetchOnChain({ chainId, address });
|
88
114
|
}
|
@@ -128,101 +154,6 @@ export class TokenService {
|
|
128
154
|
await TokenService.updateAddressPrices(address, price);
|
129
155
|
}
|
130
156
|
}
|
131
|
-
/**
|
132
|
-
* Get all tokens from https://github.com/AngleProtocol/angle-token-list and override icons from it
|
133
|
-
* TODO: use the bucket
|
134
|
-
*/
|
135
|
-
static async fillTokenAndIconsFromTokenList() {
|
136
|
-
const tokenList = await getTokensListWithCache();
|
137
|
-
for (const chain of Object.keys(tokenList)) {
|
138
|
-
for (const [symbol, token] of Object.entries(tokenList[chain])) {
|
139
|
-
if (!(await TokenRepository.findUnique(TokenService.hashId({ chainId: Number.parseInt(chain), address: token.address })))) {
|
140
|
-
try {
|
141
|
-
const res = await TokenRepository.create({
|
142
|
-
id: TokenService.hashId({ chainId: Number.parseInt(chain), address: token.address }),
|
143
|
-
chainId: Number.parseInt(chain),
|
144
|
-
address: token.address,
|
145
|
-
name: token.name,
|
146
|
-
symbol: token.symbol,
|
147
|
-
verified: true,
|
148
|
-
decimals: token.decimals,
|
149
|
-
icon: token.logoURI,
|
150
|
-
isTest: false,
|
151
|
-
isNative: false,
|
152
|
-
});
|
153
|
-
log.local(`Token created: ${res?.symbol} on ${NETWORK_LABELS[Number.parseInt(chain)]}`);
|
154
|
-
}
|
155
|
-
catch (e) {
|
156
|
-
console.error(e);
|
157
|
-
}
|
158
|
-
}
|
159
|
-
try {
|
160
|
-
await apiDbClient.token.update({
|
161
|
-
data: {
|
162
|
-
chainId: Number.parseInt(chain),
|
163
|
-
address: token.address,
|
164
|
-
name: token.name,
|
165
|
-
symbol: token.symbol,
|
166
|
-
verified: true,
|
167
|
-
decimals: token.decimals,
|
168
|
-
icon: token.logoURI,
|
169
|
-
},
|
170
|
-
where: {
|
171
|
-
chainId_address: {
|
172
|
-
chainId: Number.parseInt(chain),
|
173
|
-
address: token.address,
|
174
|
-
},
|
175
|
-
},
|
176
|
-
});
|
177
|
-
}
|
178
|
-
catch (e) {
|
179
|
-
console.error(e);
|
180
|
-
}
|
181
|
-
try {
|
182
|
-
const tokensWithSameSymbol = await apiDbClient.token.findMany({
|
183
|
-
select: { chainId: true, address: true },
|
184
|
-
where: { symbol: { equals: symbol, mode: "insensitive" } },
|
185
|
-
});
|
186
|
-
for (const dbToken of tokensWithSameSymbol) {
|
187
|
-
await apiDbClient.token.update({
|
188
|
-
data: { icon: token.logoURI },
|
189
|
-
where: {
|
190
|
-
chainId_address: {
|
191
|
-
chainId: dbToken.chainId,
|
192
|
-
address: dbToken.address,
|
193
|
-
},
|
194
|
-
},
|
195
|
-
});
|
196
|
-
}
|
197
|
-
}
|
198
|
-
catch (_err) { }
|
199
|
-
}
|
200
|
-
}
|
201
|
-
}
|
202
|
-
/**
|
203
|
-
* Create token on database
|
204
|
-
* @param chainId
|
205
|
-
* @param address
|
206
|
-
*/
|
207
|
-
static async create(token) {
|
208
|
-
const id = TokenService.hashId(token);
|
209
|
-
return TokenRepository.upsert({ ...token, id });
|
210
|
-
}
|
211
|
-
/**
|
212
|
-
* Fetches symbol, address, decimals and creates token on database
|
213
|
-
* @param chainId
|
214
|
-
* @param address
|
215
|
-
*/
|
216
|
-
static async fillAndCreate(token) {
|
217
|
-
try {
|
218
|
-
const id = TokenService.hashId(token);
|
219
|
-
const filledData = await TokenService.fetchOnChain({ address: token.address, chainId: token.chainId });
|
220
|
-
return await TokenRepository.upsert({ ...filledData, ...token, id });
|
221
|
-
}
|
222
|
-
catch (e) {
|
223
|
-
console.error(e);
|
224
|
-
}
|
225
|
-
}
|
226
157
|
/**
|
227
158
|
* Read token from database
|
228
159
|
* @param chainId
|
@@ -252,7 +183,7 @@ export class TokenService {
|
|
252
183
|
* @returns the cumulated dollar value of all tokens
|
253
184
|
*/
|
254
185
|
static async getValue(tokenAmounts) {
|
255
|
-
const tokens = await TokenService.
|
186
|
+
const tokens = await TokenService.findManyOrCreate(tokenAmounts.map(({ address, chainId }) => ({ address, chainId })));
|
256
187
|
return tokenAmounts.reduce((sum, { amount, address, chainId }) => {
|
257
188
|
const token = tokens
|
258
189
|
.filter(t => t !== undefined)
|
@@ -290,7 +221,7 @@ export class TokenService {
|
|
290
221
|
* @param chainId
|
291
222
|
* @param address
|
292
223
|
*/
|
293
|
-
static async
|
224
|
+
static async findManyOrCreate(tokens) {
|
294
225
|
return await Promise.all(tokens.map(async (token) => {
|
295
226
|
const id = TokenService.hashId(token);
|
296
227
|
try {
|
@@ -321,10 +252,6 @@ export class TokenService {
|
|
321
252
|
}, chainId);
|
322
253
|
}
|
323
254
|
static async update(id, data) {
|
324
|
-
// let iconUri = data.icon;
|
325
|
-
// if (data.iconFile) {
|
326
|
-
// iconUri = await BucketService.upload("merkl-assets", `/tokens/${id}`, data.iconFile.stream(), true);
|
327
|
-
// }
|
328
255
|
return await TokenRepository.update(id, data);
|
329
256
|
}
|
330
257
|
static async notionWebhook(body) {
|
@@ -342,7 +269,7 @@ export class TokenService {
|
|
342
269
|
const isVerified = properties.Verified.checkbox;
|
343
270
|
const coingeckoApiId = properties["CoinGecko API ID"].rich_text[0].plain_text;
|
344
271
|
const byteArray = await iconFile.bytes();
|
345
|
-
const [token] = await TokenService.
|
272
|
+
const [token] = await TokenService.findManyOrCreate([
|
346
273
|
{
|
347
274
|
chainId,
|
348
275
|
address,
|
@@ -381,4 +308,91 @@ export class TokenService {
|
|
381
308
|
verified: isVerified,
|
382
309
|
});
|
383
310
|
}
|
311
|
+
/**
|
312
|
+
* Fetches symbol, address, decimals and creates token on database
|
313
|
+
* @param chainId
|
314
|
+
* @param address
|
315
|
+
*/
|
316
|
+
static async fillAndCreate(token) {
|
317
|
+
try {
|
318
|
+
const id = TokenService.hashId(token);
|
319
|
+
const filledData = await TokenService.fetchOnChain({ address: token.address, chainId: token.chainId });
|
320
|
+
return await TokenRepository.upsert({ ...filledData, ...token, id });
|
321
|
+
}
|
322
|
+
catch (e) {
|
323
|
+
console.error(e);
|
324
|
+
}
|
325
|
+
}
|
326
|
+
/**
|
327
|
+
* @deprecated Should be useless now that the token list is not used anymore
|
328
|
+
* Get all tokens from https://github.com/AngleProtocol/angle-token-list and override icons from it
|
329
|
+
* TODO: use the bucket
|
330
|
+
*/
|
331
|
+
static async fillTokenAndIconsFromTokenList() {
|
332
|
+
const tokenList = await getTokensListWithCache();
|
333
|
+
for (const chain of Object.keys(tokenList)) {
|
334
|
+
for (const [symbol, token] of Object.entries(tokenList[chain])) {
|
335
|
+
if (!(await TokenRepository.findUnique(TokenService.hashId({ chainId: Number.parseInt(chain), address: token.address })))) {
|
336
|
+
try {
|
337
|
+
const res = await TokenRepository.create({
|
338
|
+
id: TokenService.hashId({ chainId: Number.parseInt(chain), address: token.address }),
|
339
|
+
chainId: Number.parseInt(chain),
|
340
|
+
address: token.address,
|
341
|
+
name: token.name,
|
342
|
+
symbol: token.symbol,
|
343
|
+
verified: true,
|
344
|
+
decimals: token.decimals,
|
345
|
+
icon: token.logoURI,
|
346
|
+
isTest: false,
|
347
|
+
isNative: false,
|
348
|
+
});
|
349
|
+
log.local(`Token created: ${res?.symbol} on ${NETWORK_LABELS[Number.parseInt(chain)]}`);
|
350
|
+
}
|
351
|
+
catch (e) {
|
352
|
+
console.error(e);
|
353
|
+
}
|
354
|
+
}
|
355
|
+
try {
|
356
|
+
await apiDbClient.token.update({
|
357
|
+
data: {
|
358
|
+
chainId: Number.parseInt(chain),
|
359
|
+
address: token.address,
|
360
|
+
name: token.name,
|
361
|
+
symbol: token.symbol,
|
362
|
+
verified: true,
|
363
|
+
decimals: token.decimals,
|
364
|
+
icon: token.logoURI,
|
365
|
+
},
|
366
|
+
where: {
|
367
|
+
chainId_address: {
|
368
|
+
chainId: Number.parseInt(chain),
|
369
|
+
address: token.address,
|
370
|
+
},
|
371
|
+
},
|
372
|
+
});
|
373
|
+
}
|
374
|
+
catch (e) {
|
375
|
+
console.error(e);
|
376
|
+
}
|
377
|
+
try {
|
378
|
+
const tokensWithSameSymbol = await apiDbClient.token.findMany({
|
379
|
+
select: { chainId: true, address: true },
|
380
|
+
where: { symbol: { equals: symbol, mode: "insensitive" } },
|
381
|
+
});
|
382
|
+
for (const dbToken of tokensWithSameSymbol) {
|
383
|
+
await apiDbClient.token.update({
|
384
|
+
data: { icon: token.logoURI },
|
385
|
+
where: {
|
386
|
+
chainId_address: {
|
387
|
+
chainId: dbToken.chainId,
|
388
|
+
address: dbToken.address,
|
389
|
+
},
|
390
|
+
},
|
391
|
+
});
|
392
|
+
}
|
393
|
+
}
|
394
|
+
catch (_err) { }
|
395
|
+
}
|
396
|
+
}
|
397
|
+
}
|
384
398
|
}
|
@@ -19,7 +19,7 @@ export default (app) => app.get("/app", async () => {
|
|
19
19
|
if (!json.tokens[token.chainId]) {
|
20
20
|
json.tokens[token.chainId] = {};
|
21
21
|
}
|
22
|
-
if (!json.tokens[token.chainId][token.address]) {
|
22
|
+
if (!json.tokens[token.chainId][token.address] && token.icon?.length > 0) {
|
23
23
|
json.tokens[token.chainId][token.address] = {
|
24
24
|
address: token.address,
|
25
25
|
chainId: token.chainId,
|
@@ -26,7 +26,7 @@ export default class PriceService {
|
|
26
26
|
});
|
27
27
|
};
|
28
28
|
async fetchPrices() {
|
29
|
-
const tokenPriceSources = await PriceSourceService.
|
29
|
+
const tokenPriceSources = await PriceSourceService.findManyPriceSources();
|
30
30
|
/**
|
31
31
|
* @description Factory pricer's call to get prices from different sources
|
32
32
|
*/
|