Skip to content
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

feat: Support multiple to tokens #253

Closed
wants to merge 12 commits into from
  •  
  •  
  •  
11 changes: 6 additions & 5 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
"**/.hg": true,
"**/CVS": true,
"**/.DS_Store": true,
"**/node_modules": true,
"**/.next": true,
// "**/node_modules": true,
// "**/.next": true,
"**/*.log": true,
"**/dist": true,
"**/.rush": true,
"**/temp": true,
// "**/dist": true,
// "**/.rush": true,
// "**/temp": true,
"**/tsconfig.tsbuildinfo": true
},
"[typescript]": {
Expand Down Expand Up @@ -48,6 +48,7 @@
"Blocto",
"bnbchain",
"cbridge",
"Chakra",
"debridge",
"LAMPORTS",
"multichain",
Expand Down
2 changes: 1 addition & 1 deletion apps/canonical-bridge-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"rxjs": "^7.8.1"
},
"devDependencies": {
"@bnb-chain/prettier-config": "workspace:*",
"@bnb-chain/prettier-config": "^1",
"@nestjs/cli": "^10.0.0",
"@nestjs/schematics": "^10.0.0",
"@types/express": "^4.17.17",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,44 @@
import { Controller, Get, Inject, Logger } from '@nestjs/common';
import { CACHE_MANAGER, Cache } from '@nestjs/cache-manager';
import { CACHE_KEY } from '@/common/constants';
import { BridgeService } from '@/module/bridge/bridge.service';
import {
IDebridgeConfig,
IMesonChain,
IStargateBridgeTokenInfo,
ITransferConfigsForAll,
} from '@/shared/web3/web3.interface';

@Controller('bridge')
export class BridgeController {
private logger = new Logger(BridgeController.name);

constructor(@Inject(CACHE_MANAGER) private cache: Cache) {}
constructor(
@Inject(CACHE_MANAGER) private cache: Cache,
private bridgeService: BridgeService,
) {}

@Get('/cbridge')
getCbridgeConfig() {
return this.cache.get(CACHE_KEY.CBRIDGE_CONFIG);
async getCbridgeConfig() {
const config = await this.cache.get<ITransferConfigsForAll>(CACHE_KEY.CBRIDGE_CONFIG);
return this.bridgeService.removeCBridgeNoPriceTokens(config);
}

@Get('/debridge')
getDeBridgeConfig() {
return this.cache.get(CACHE_KEY.DEBRIDGE_CONFIG);
async getDeBridgeConfig() {
const config = await this.cache.get<IDebridgeConfig>(CACHE_KEY.DEBRIDGE_CONFIG);
return this.bridgeService.removeDeBridgeNoPriceTokens(config);
}

@Get('/stargate')
getStargateConfig() {
return this.cache.get(CACHE_KEY.STARGATE_CONFIG);
async getStargateConfig() {
const config = await this.cache.get<IStargateBridgeTokenInfo[]>(CACHE_KEY.STARGATE_CONFIG);
return this.bridgeService.removeStargateNoPriceTokens(config);
}

@Get('/meson')
getMesonConfig() {
return this.cache.get(CACHE_KEY.MESON_CONFIG);
async getMesonConfig() {
const config = await this.cache.get<IMesonChain[]>(CACHE_KEY.MESON_CONFIG);
return this.bridgeService.removeMesonNoPriceTokens(config);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export class BridgeSchedule implements OnModuleInit {

constructor(@InjectQueue(Queues.SyncBridge) private syncBridge: Queue) {}

@Cron(CronExpression.EVERY_5_MINUTES)
@Cron(CronExpression.EVERY_3_HOURS)
async syncBridgeInfo() {
this.logger.log('syncBridgeInfo');
await this.syncBridge.add(Tasks.fetchCbridge, null, {
Expand Down
222 changes: 220 additions & 2 deletions apps/canonical-bridge-server/src/module/bridge/bridge.service.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,227 @@
import { Injectable, Logger } from '@nestjs/common';
import { Inject, Injectable, Logger } from '@nestjs/common';
import { DatabaseService } from '@/shared/database/database.service';
import {
IDebridgeConfig,
IDebridgeToken,
IMesonChain,
IStargateBridgeTokenInfo,
ITransferConfigsForAll,
ITransferToken,
} from '@/shared/web3/web3.interface';
import { CACHE_MANAGER, Cache } from '@nestjs/cache-manager';
import { CACHE_KEY } from '@/common/constants';
import { ITokenPriceRecord } from '@/module/token/token.interface';

@Injectable()
export class BridgeService {
private logger = new Logger(BridgeService.name);

constructor(private databaseService: DatabaseService) {}
constructor(
@Inject(CACHE_MANAGER) private cache: Cache,
private databaseService: DatabaseService,
) {}

private updateDeBridgeConfigManually(config?: IDebridgeConfig) {
if (!config) return;

const finalConfig = {
tokens: [],
...config,
};

const extraConfigs: Record<number, any[]> = {
1: [
{
action: 'replace',
target: '0xebd9d99a3982d547c5bb4db7e3b1f9f14b67eb83',
data: {
address: '0x2dfF88A56767223A5529eA5960Da7A3F5f766406',
symbol: 'ID',
decimals: 18,
name: 'SPACE ID',
logoURI: '',
eip2612: false,
tags: ['tokens'],
},
},
{
action: 'append',
data: {
address: '0x152649eA73beAb28c5b49B26eb48f7EAD6d4c898',
symbol: 'Cake',
decimals: 18,
name: 'PancakeSwap Token',
logoURI: '',
eip2612: false,
tags: ['tokens'],
},
},
],
};

Object.entries(finalConfig.tokens).forEach(([key, value]) => {
const chainId = Number(key);
const extraConfig = extraConfigs[chainId];

if (extraConfig) {
extraConfig.forEach((item) => {
const { action, target, data } = item;
if (!value[data.address]) {
if (action === 'replace') {
const index = value.findIndex((item) => item.address === target);
if (index > -1) {
value[index] = data;
}
} else if (action === 'append') {
(value as any).push(data);
}
}
});
}
});

return finalConfig;
}

public async getPriceConfig() {
const [cmcRes, llamaRes] = await Promise.allSettled([
this.cache.get<ITokenPriceRecord>(CACHE_KEY.CMC_CONFIG),
this.cache.get<ITokenPriceRecord>(CACHE_KEY.LLAMA_CONFIG),
]);
return {
cmc: cmcRes.status === 'fulfilled' ? cmcRes.value : {},
llama: llamaRes.status === 'fulfilled' ? llamaRes.value : {},
};
}

public hasTokenPrice(params: {
prices: { cmc?: ITokenPriceRecord; llama?: ITokenPriceRecord };
tokenSymbol: string;
tokenAddress: string;
}) {
const key1 = params.tokenSymbol?.toLowerCase();
const key2 = `${params.tokenSymbol?.toLowerCase()}:${params.tokenAddress?.toLowerCase()}`;
const key3 = `ethereum:${key1}`;

const price =
params.prices.cmc?.[key2]?.price ??
params.prices.llama?.[key2]?.price ??
params.prices.cmc?.[key1]?.price ??
params.prices.llama?.[key1]?.price ??
params.prices.cmc?.[key3]?.price ??
params.prices.llama?.[key3]?.price;

return !!price;
}

public async removeCBridgeNoPriceTokens(config?: ITransferConfigsForAll) {
if (!config) return;

const prices = await this.getPriceConfig();

const chainToken: Record<number, { token: ITransferToken[] }> = {};
Object.entries(config.chain_token).forEach(([key, { token }]) => {
const chainId = Number(key);
chainToken[chainId] = { token: [] };
chainToken[chainId].token = token.filter((e) => {
return this.hasTokenPrice({
prices,
tokenAddress: e.token.address,
tokenSymbol: e.token.symbol,
});
});
});

const peggedPairConfigs: ITransferConfigsForAll['pegged_pair_configs'] =
config.pegged_pair_configs.filter((e) => {
const orgHasPrice = this.hasTokenPrice({
prices,
tokenSymbol: e.org_token.token.symbol,
tokenAddress: e.org_token.token.address,
});

const peggedHasPrice = this.hasTokenPrice({
prices,
tokenSymbol: e.pegged_token.token.symbol,
tokenAddress: e.pegged_token.token.address,
});

return orgHasPrice && peggedHasPrice;
});

const finalConfig: ITransferConfigsForAll = {
...config,
chain_token: chainToken,
pegged_pair_configs: peggedPairConfigs,
};

return finalConfig;
}

public async removeDeBridgeNoPriceTokens(_config?: IDebridgeConfig) {
const config = this.updateDeBridgeConfigManually(_config);
if (!config) return;

const prices = await this.getPriceConfig();
const chainTokens: Record<number, IDebridgeToken[]> = {};

Object.entries(config.tokens).forEach(([key, tokens]) => {
const chainId = Number(key);
chainTokens[chainId] = tokens.filter((e) => {
return this.hasTokenPrice({
prices,
tokenAddress: e.address,
tokenSymbol: e.symbol,
});
});
});

const finalConfig: IDebridgeConfig = {
...config,
tokens: chainTokens,
};

return finalConfig;
}

public async removeStargateNoPriceTokens(config?: IStargateBridgeTokenInfo[]) {
if (!config) return;

const prices = await this.getPriceConfig();

const finalConfig = config.filter((e) => {
return this.hasTokenPrice({
prices,
tokenAddress: e.token.address,
tokenSymbol: e.token.symbol,
});
});

return finalConfig;
}

public async removeMesonNoPriceTokens(config?: IMesonChain[]) {
if (!config) return;

const prices = await this.getPriceConfig();

const finalConfig: IMesonChain[] = [];
config.forEach((chain) => {
const tokens = chain.tokens.filter((e) => {
return this.hasTokenPrice({
prices,
tokenAddress: e.addr,
tokenSymbol: e.symbol,
});
});
if (tokens?.length) {
finalConfig.push({
...chain,
tokens,
});
}
});

return finalConfig;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,7 @@ export interface ITokenJob {
ids?: string;
keyMap?: Record<string, string>;
}

export interface ITokenPriceRecord {
[k: string]: { price: string; id: number };
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export class TokenProcessor extends WorkerHost {
return r;
}, {});

await this.cache.set(`${CACHE_KEY.LLAMA_CONFIG}`, config, TIME.MONTH);
await this.cache.set(`${CACHE_KEY.LLAMA_CONFIG}`, config, TIME.DAY);
return config;
}

Expand All @@ -79,7 +79,7 @@ export class TokenProcessor extends WorkerHost {
return r;
}, {});

await this.cache.set(`${CACHE_KEY.CMC_CONFIG}`, config, TIME.MONTH);
await this.cache.set(`${CACHE_KEY.CMC_CONFIG}`, config, TIME.DAY);
return config;
}

Expand All @@ -101,7 +101,7 @@ export class TokenProcessor extends WorkerHost {
{} as Record<string, string>,
);

await this.cache.set(`${CACHE_KEY.PLATFORM_MAPPING}`, mapping, TIME.MONTH);
await this.cache.set(`${CACHE_KEY.PLATFORM_MAPPING}`, mapping, TIME.DAY);
this.tokenService.syncCoingeckoTokens(coins, platforms);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ export interface IDebridgeChain {

export interface IDebridgeConfig {
chains: IDebridgeChain[];
tokens: Record<number, Record<string, IDebridgeToken>>;
tokens: Record<number, IDebridgeToken[]>;
}

export interface IAssetPlatform {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ChakraProvider, ColorMode, createLocalStorageManager, theme } from '@bnb-chain/space';

import { env } from '@/core/env';
import { walletStyles } from '@/core/theme/walletStyles';

interface ThemeProviderProps {
children: React.ReactNode;
Expand All @@ -25,6 +26,7 @@ export const ThemeProvider = ({ children }: ThemeProviderProps) => {
body: {
bg: theme.colors[colorMode].background[3],
},
...walletStyles(colorMode),
}),
},
};
Expand Down
Loading
Loading