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 #277

Merged
merged 23 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
3fd4ca7
refactor: Import local packages from npm
wenty22 Dec 23, 2024
bc1fdc7
Merge branch 'feat/tokenApi1216' into wenty/aggregator
wenty22 Dec 23, 2024
326309a
refactor: Adjust config structure
wenty22 Dec 24, 2024
7c02d98
Merge branch 'main' into wenty/aggregator
wenty22 Jan 2, 2025
ea1d1c4
feat: Support multiple to tokens
wenty22 Jan 2, 2025
d36a484
feat: Support multiple to tokens
wenty22 Jan 3, 2025
9cd2d03
Merge branch 'main' into wenty/aggregator
wenty22 Jan 3, 2025
ed8e2e6
feat: Support multiple to tokens
wenty22 Jan 6, 2025
e895128
feat: Support multiple to tokens
wenty22 Jan 6, 2025
54699e4
feat: Filter no price tokens
wenty22 Jan 6, 2025
82eac74
feat: Move config to api server
wenty22 Jan 6, 2025
92b12a9
feat: Update syncBridgeInfo task cronTime
wenty22 Jan 6, 2025
d6755da
Merge branch 'main' into wenty/aggregator
wenty22 Jan 8, 2025
46d80be
feat: Change deBridgeReferralCode to an environment variable
wenty22 Jan 8, 2025
e57b843
chore: Release version
Halibao-Lala Jan 8, 2025
4ab9537
Merge pull request #272 from bnb-chain/hotfix/toTokenAddrIssue
Halibao-Lala Jan 8, 2025
724fe5d
chore: Update versions
github-actions[bot] Jan 8, 2025
c87f8b0
Merge pull request #273 from bnb-chain/changeset-release/main
wenty22 Jan 8, 2025
f36573e
feat: Add cache to filtered data
wenty22 Jan 8, 2025
b9b05ce
Merge branch 'main' into wenty/aggregator
wenty22 Jan 8, 2025
6c5e1ab
style: Format code styles
wenty22 Jan 8, 2025
db7bf0b
feat: Add app version
wenty22 Jan 8, 2025
93b0b2f
feat: Update eth & weth mapping rule
wenty22 Jan 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .github/workflows/example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ jobs:
NEXT_PUBLIC_ASSET_PREFIX=$ASSET_PREFIX
NEXT_PUBLIC_SERVER_ENDPOINT=$SERVER_ENDPOINT
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=$WALLET_CONNECT_PROJECT_ID
NEXT_PUBLIC_DEBRIDGE_REFERRAL_CODE=$DEBRIDGE_REFERRAL_CODE
EOF
env:
APP_NAME: canonical-bridge
Expand Down
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
3 changes: 1 addition & 2 deletions apps/canonical-bridge-server/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { AllExceptionFilter } from './common/filters/all-exception.filter';
import { HttpExceptionFilter } from './common/filters/http-exception.filter';
import { TransformInterceptor } from './common/interceptors/transform.interceptor';
import { TimeoutInterceptor } from './common/interceptors/timeout.interceptor';
import { REDIS_HOST, REDIS_PORT, TIME } from './common/constants';
import { REDIS_HOST, REDIS_PORT } from './common/constants';
import { TokenModule } from './module/token/token.module';
import { BullModule } from '@nestjs/bullmq';
import { Web3Module } from '@/shared/web3/web3.module';
Expand All @@ -32,7 +32,6 @@ import { BridgeModule } from '@/module/bridge/bridge.module';
HealthModule,
ScheduleModule.forRoot(),
CacheModule.register<RedisOptions>({
ttl: TIME.DAY,
isGlobal: true,
store: () => redisStore({ host: REDIS_HOST, port: REDIS_PORT }),
}),
Expand Down
10 changes: 10 additions & 0 deletions apps/canonical-bridge-server/src/common/constants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ export enum Tasks {
fetchMeson = 'fetchMeson',
cacheCmcConfig = 'cacheCmcConfig',
cacheLlamaConfig = 'cacheLlamaConfig',

filterCBridge = 'filterCBridge',
filterDeBridge = 'filterDeBridge',
filterStargate = 'filterStargate',
filterMeson = 'filterMeson',
}

export const TOKEN_REQUEST_LIMIT = 1000;
Expand All @@ -53,6 +58,11 @@ export const CACHE_KEY = {
CMC_CONFIG: 'cmc:config',
LLAMA_CONFIG: 'llama:config',
PLATFORM_MAPPING: 'llama:platform',

FIELDED_CBRIDGE_CONFIG: 'bridge:filtered:cbridge',
FIELDED_DEBRIDGE_CONFIG: 'bridge:filtered:debridge',
FIELDED_STARGATE_CONFIG: 'bridge:filtered:stargate',
FIELDED_MESON_CONFIG: 'bridge:filtered:meson',
};

export const JOB_KEY = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,44 @@ export class BridgeController {
getMesonConfig() {
return this.cache.get(CACHE_KEY.MESON_CONFIG);
}

@Get('/v2/cbridge')
async getCbridgeConfigV2() {
const config = await this.cache.get(CACHE_KEY.FIELDED_CBRIDGE_CONFIG);
if (config) {
return config;
}

return this.cache.get(CACHE_KEY.CBRIDGE_CONFIG);
}

@Get('/v2/debridge')
async getDeBridgeConfigV2() {
const config = await this.cache.get(CACHE_KEY.FIELDED_DEBRIDGE_CONFIG);
if (config) {
return config;
}

return this.cache.get(CACHE_KEY.DEBRIDGE_CONFIG);
}

@Get('/v2/stargate')
async getStargateConfigV2() {
const config = await this.cache.get(CACHE_KEY.FIELDED_STARGATE_CONFIG);
if (config) {
return config;
}

return this.cache.get(CACHE_KEY.STARGATE_CONFIG);
}

@Get('/v2/meson')
async getMesonConfigV2() {
const config = await this.cache.get(CACHE_KEY.FIELDED_MESON_CONFIG);
if (config) {
return config;
}

return this.cache.get(CACHE_KEY.MESON_CONFIG);
}
}
231 changes: 230 additions & 1 deletion apps/canonical-bridge-server/src/module/bridge/bridge.processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,16 @@ import { Inject, Logger } from '@nestjs/common';
import { Job } from 'bullmq';
import { Web3Service } from '@/shared/web3/web3.service';
import { Cache, CACHE_MANAGER } from '@nestjs/cache-manager';
import { IDebridgeToken } from '@/shared/web3/web3.interface';
import {
IDebridgeConfig,
IDebridgeToken,
IMesonChain,
IStargateBridgeTokenInfo,
ITransferConfigsForAll,
ITransferToken,
} from '@/shared/web3/web3.interface';
import { ITokenPriceRecord } from '@/module/token/token.interface';
import { isEmpty } from 'lodash';

@Processor(Queues.SyncBridge)
export class BridgeProcessor extends WorkerHost {
Expand All @@ -27,6 +36,15 @@ export class BridgeProcessor extends WorkerHost {
return this.fetchMeson();
case Tasks.fetchStargate:
return this.fetchStargate();

case Tasks.filterCBridge:
return this.filterCBridge();
case Tasks.filterDeBridge:
return this.filterDeBridge();
case Tasks.filterStargate:
return this.filterStargate();
case Tasks.filterMeson:
return this.filterMeson();
default:
}
}
Expand Down Expand Up @@ -65,4 +83,215 @@ export class BridgeProcessor extends WorkerHost {
if (!config) return;
await this.cache.set(`${CACHE_KEY.MESON_CONFIG}`, config, TIME.DAY);
}

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

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;
}) {
if (isEmpty(params.prices.cmc) && isEmpty(params.prices.llama)) {
return true;
}
const key1 = `${params.tokenSymbol?.toLowerCase()}:${params.tokenAddress?.toLowerCase()}`;
const key3 = params.tokenSymbol?.toLowerCase();
const key2 = `ethereum:${key3}`;

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

return !!price;
}

async filterCBridge() {
const config = await this.cache.get<ITransferConfigsForAll>(CACHE_KEY.CBRIDGE_CONFIG);
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,
};

await this.cache.set(`${CACHE_KEY.FIELDED_CBRIDGE_CONFIG}`, finalConfig, TIME.DAY);
}

async filterDeBridge() {
const _config = await this.cache.get<IDebridgeConfig>(CACHE_KEY.DEBRIDGE_CONFIG);
const config = this.updateDeBridgeConfigManually(_config);
if (!config) return config;

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,
};

await this.cache.set(`${CACHE_KEY.FIELDED_DEBRIDGE_CONFIG}`, finalConfig, TIME.DAY);
}

async filterStargate() {
const config = await this.cache.get<IStargateBridgeTokenInfo[]>(CACHE_KEY.STARGATE_CONFIG);
if (!config) return config;

const prices = await this.getPriceConfig();

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

await this.cache.set(`${CACHE_KEY.FIELDED_STARGATE_CONFIG}`, finalConfig, TIME.DAY);
}

async filterMeson() {
const config = await this.cache.get<IMesonChain[]>(CACHE_KEY.MESON_CONFIG);
if (!config) return config;

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,
});
}
});

await this.cache.set(`${CACHE_KEY.FIELDED_MESON_CONFIG}`, finalConfig, TIME.DAY);
}
}
Loading
Loading