Skip to content

Commit

Permalink
Merge branch 'main' into feat/guardian-dashboard--hekla
Browse files Browse the repository at this point in the history
  • Loading branch information
davidtaikocha authored Apr 12, 2024
2 parents 5e8cd14 + 3cd7cce commit c8caf25
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { promises as fs } from 'fs';
import path from 'path';
import { Project, SourceFile, VariableDeclarationKind } from 'ts-morph';

import configuredChainsSchema from '../../config/schemas/configuredChains.schema.json';
import configuredCustomTokens from '../../config/schemas/configuredCustomTokens.schema.json';
import type { Token } from '../../src/libs/token/types';
import { decodeBase64ToJson } from './../utils/decodeBase64ToJson';
import { formatSourceFile } from './../utils/formatSourceFile';
Expand Down Expand Up @@ -40,7 +40,7 @@ export function generateCustomTokenConfig() {
configuredTokenConfigFile = decodeBase64ToJson(process.env.CONFIGURED_CUSTOM_TOKENS || '');

// Valid JSON against schema
const isValid = validateJsonAgainstSchema(configuredTokenConfigFile, configuredChainsSchema);
const isValid = validateJsonAgainstSchema(configuredTokenConfigFile, configuredCustomTokens);

if (!isValid) {
throw new Error('encoded generateCustomTokenConfig.json is not valid.');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@
{#if calculatingRecommendedAmount}
<LoadingText mask="0.0017730224073" /> ETH
{:else if errorCalculatingRecommendedAmount}
{$t('processing_fee.recommended.error')}
<FlatAlert type="warning" message={$t('processing_fee.recommended.error')} />
{:else}
{formatEther($processingFee ?? BigInt(0))} ETH {#if $processingFee !== recommendedAmount}
<span class="text-primary-link">| {$t('common.customized')}</span>
Expand All @@ -153,7 +153,7 @@
{#if calculatingRecommendedAmount}
<LoadingText mask="0.0017730224073" />
{:else if errorCalculatingRecommendedAmount}
{$t('processing_fee.recommended.error')}
<span class="text-warning-sentiment">{$t('processing_fee.recommended.error')}</span>
{:else}
{formatEther($processingFee ?? BigInt(0))} ETH {#if $processingFee !== recommendedAmount}
<span class="text-primary-link">| {$t('common.customized')}</span>
Expand All @@ -179,7 +179,7 @@
{#if calculatingRecommendedAmount}
<LoadingText mask="0.0001" /> ETH
{:else if errorCalculatingRecommendedAmount}
{$t('processing_fee.recommended.error')}
<FlatAlert type="warning" message={$t('processing_fee.recommended.error')} />
{:else}
{formatEther($processingFee ?? BigInt(0))} ETH {#if $processingFee !== recommendedAmount}
<span class="text-primary-link">| {$t('common.customized')}</span>
Expand Down Expand Up @@ -212,7 +212,7 @@
{#if calculatingRecommendedAmount}
<LoadingText mask="0.0001" /> ETH
{:else if errorCalculatingRecommendedAmount}
{$t('processing_fee.recommended.error')}
<FlatAlert type="warning" message={$t('processing_fee.recommended.error')} />
{:else}
{formatEther(recommendedAmount)} ETH
{/if}
Expand Down Expand Up @@ -299,7 +299,10 @@
</div>
{/if}

<RecommendedFee bind:amount={recommendedAmount} bind:calculating={calculatingRecommendedAmount} />
<RecommendedFee
bind:amount={recommendedAmount}
bind:calculating={calculatingRecommendedAmount}
bind:error={errorCalculatingRecommendedAmount} />

<NoneOption
bind:enoughEth={hasEnoughEth}
Expand Down
14 changes: 7 additions & 7 deletions packages/bridge-ui/src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -239,15 +239,15 @@
"ok": "Okay",
"recipient": "Recipient",
"review": "Review",
"see_results": "See results",
"sender": "Sender",
"status": "Status",
"success": "Success",
"switch_to": "Switch to",
"symbol": "Symbol",
"to": "To",
"token_id": "Token ID",
"token_standard": "Token standard",
"see_results": "See results"
"token_standard": "Token standard"
},
"custom_recipient": {
"placeholder": "Add custom recipient"
Expand Down Expand Up @@ -358,6 +358,10 @@
"token": "Token",
"transactions": "Transactions"
},
"paginator": {
"of": "of",
"page": "Page"
},
"paused_modal": {
"description": "The bridge is currently not available. Follow our official communication channels for more information. ",
"title": "Bridge under maintenance!"
Expand All @@ -381,7 +385,7 @@
},
"recommended": {
"calculating": "Calculating",
"error": "Error calculating",
"error": "Error calculating processing fee, you will need to claim manually!",
"label": "Recommended"
},
"title": "Processing fee",
Expand Down Expand Up @@ -565,9 +569,5 @@
"connecting": "Connecting",
"disconnected": "Disconnected"
}
},
"paginator": {
"page": "Page",
"of": "of"
}
}
6 changes: 6 additions & 0 deletions packages/bridge-ui/src/libs/chain/chains.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import type { Chain } from 'viem';
import { chainConfig } from '$chainConfig';
import type { ChainConfig } from '$libs/chain';

import { LayerType } from './types';

function mapChainConfigToChain(chainId: string, chainConfig: ChainConfig): Chain {
return {
id: Number(chainId),
Expand All @@ -21,6 +23,10 @@ export const chainIdToChain = (chainId: number): Chain => {
return chain;
};

export const isL2Chain = (chainId: number) => {
return chainConfig[chainId].type === LayerType.L2;
};

export const chains: [Chain, ...Chain[]] = Object.entries(chainConfig).map(([chainId, chainConfig]) =>
mapChainConfigToChain(chainId, chainConfig),
) as [Chain, ...Chain[]];
Expand Down
69 changes: 39 additions & 30 deletions packages/bridge-ui/src/libs/fee/recommendProcessingFee.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { getPublicClient } from '@wagmi/core';

import { recommendProcessingFeeConfig } from '$config';
import { NoCanonicalInfoFoundError } from '$libs/error';
import { relayerApiServices } from '$libs/relayer';
import { FeeTypes } from '$libs/relayer/types';
import { type Token, TokenType } from '$libs/token';
import { getTokenAddresses } from '$libs/token/getTokenAddresses';
import { getLogger } from '$libs/util/logger';
import { config } from '$libs/wagmi';

const log = getLogger('libs:recommendedProcessingFee');

Expand All @@ -15,16 +13,6 @@ type RecommendProcessingFeeArgs = {
srcChainId?: number;
};

const {
ethGasLimit,
erc20NotDeployedGasLimit,
erc20DeployedGasLimit,
erc1155DeployedGasLimit,
erc1155NotDeployedGasLimit,
erc721DeployedGasLimit,
erc721NotDeployedGasLimit,
} = recommendProcessingFeeConfig;

export async function recommendProcessingFee({
token,
destChainId,
Expand All @@ -33,16 +21,8 @@ export async function recommendProcessingFee({
if (!srcChainId) {
return 0n;
}
const destPublicClient = getPublicClient(config, { chainId: destChainId });

if (!destPublicClient) throw new Error('Could not get public client');

// getGasPrice will return gasPrice as 3000000001, rather than 3000000000
const gasPrice = await destPublicClient.getGasPrice();

// The gas limit for processMessage call for ETH is about ~800k.
// To make it enticing, we say 900k
let gasLimit = ethGasLimit;
let fee;

if (token.type !== TokenType.ETH) {
const tokenInfo = await getTokenAddresses({ token, srcChainId, destChainId });
Expand All @@ -58,29 +38,58 @@ export async function recommendProcessingFee({
}
if (token.type === TokenType.ERC20) {
if (isTokenAlreadyDeployed) {
gasLimit = erc20DeployedGasLimit;
log(`token ${token.symbol} is already deployed on chain ${destChainId}`);

fee = await relayerApiServices[0].recommendedProcessingFees({
typeFilter: FeeTypes.Erc20Deployed,
destChainIDFilter: destChainId,
});
} else {
gasLimit = erc20NotDeployedGasLimit;
fee = await relayerApiServices[0].recommendedProcessingFees({
typeFilter: FeeTypes.Erc20NotDeployed,
destChainIDFilter: destChainId,
});
log(`token ${token.symbol} is not deployed on chain ${destChainId}`);
}
} else if (token.type === TokenType.ERC721) {
if (isTokenAlreadyDeployed) {
gasLimit = erc721DeployedGasLimit;
log(`token ${token.symbol} is already deployed on chain ${destChainId}`);
fee = await relayerApiServices[0].recommendedProcessingFees({
typeFilter: FeeTypes.Erc721Deployed,
destChainIDFilter: destChainId,
});
} else {
gasLimit = erc721NotDeployedGasLimit;
log(`token ${token.symbol} is not deployed on chain ${destChainId}`);
fee = await relayerApiServices[0].recommendedProcessingFees({
typeFilter: FeeTypes.Erc721NotDeployed,
destChainIDFilter: destChainId,
});
}
} else if (token.type === TokenType.ERC1155) {
if (isTokenAlreadyDeployed) {
gasLimit = erc1155DeployedGasLimit;
log(`token ${token.symbol} is already deployed on chain ${destChainId}`);
fee = await relayerApiServices[0].recommendedProcessingFees({
typeFilter: FeeTypes.Erc1155Deployed,
destChainIDFilter: destChainId,
});
} else {
gasLimit = erc1155NotDeployedGasLimit;
log(`token ${token.symbol} is not deployed on chain ${destChainId}`);
fee = await relayerApiServices[0].recommendedProcessingFees({
typeFilter: FeeTypes.Erc1155NotDeployed,
destChainIDFilter: destChainId,
});
}
}
} else {
log(`Fee for ETH bridging`);
fee = await relayerApiServices[0].recommendedProcessingFees({
typeFilter: FeeTypes.Eth,
destChainIDFilter: destChainId,
});
}
return gasPrice * gasLimit;
if (!fee) throw new Error('Unable to get fee from relayer API');

const feeInWei = BigInt(fee[0].amount);
log(`Recommended fee: ${feeInWei.toString()}`);
return feeInWei;
}
36 changes: 36 additions & 0 deletions packages/bridge-ui/src/libs/relayer/RelayerAPIService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,12 @@ import {
type APIRequestParams,
type APIResponse,
type APIResponseTransaction,
type Fee,
type FeeType,
type GetAllByAddressResponse,
type PaginationInfo,
type PaginationParams,
type ProcessingFeeApiResponse,
type RelayerBlockInfo,
RelayerEventType,
} from './types';
Expand Down Expand Up @@ -278,6 +281,39 @@ export class RelayerAPIService {
}): Promise<Record<number, RelayerBlockInfo>> {
throw new Error('Not implemented');
}

async recommendedProcessingFees({
typeFilter,
destChainIDFilter,
}: {
typeFilter?: FeeType;
destChainIDFilter?: number;
}): Promise<Fee[]> {
const requestURL = `${this.baseUrl}/recommendedProcessingFees`;

try {
const response = await axios.get<ProcessingFeeApiResponse>(requestURL);

if (response.status >= 400) throw new Error('HTTP error', { cause: response });

let { fees } = response.data;

if (typeFilter) {
fees = fees.filter((fee) => fee.type === typeFilter);
}

if (destChainIDFilter !== undefined) {
fees = fees.filter((fee) => fee.destChainID === destChainIDFilter);
}

return fees;
} catch (error) {
console.error(error);
throw new Error('Failed to fetch recommended processing fees', {
cause: error instanceof Error ? error : undefined,
});
}
}
}

const _eventToTokenType = (eventType: RelayerEventType): TokenType => {
Expand Down
20 changes: 17 additions & 3 deletions packages/bridge-ui/src/libs/relayer/initRelayers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@ import { configuredRelayer } from '$relayerConfig';

import { RelayerAPIService } from './RelayerAPIService';

export const relayerApiServices: RelayerAPIService[] = configuredRelayer.map(
(relayerConfig: { url: string }) => new RelayerAPIService(relayerConfig.url),
);
class RelayerServiceFactory {
private static instanceCache: Map<string, RelayerAPIService> = new Map();

public static getServices(configuredRelayers: { url: string }[]): RelayerAPIService[] {
return configuredRelayers.map((relayerConfig) => this.getService(relayerConfig.url));
}

private static getService(url: string): RelayerAPIService {
if (!this.instanceCache.has(url)) {
const newInstance = new RelayerAPIService(url);
this.instanceCache.set(url, newInstance);
}
return this.instanceCache.get(url)!;
}
}

export const relayerApiServices: RelayerAPIService[] = RelayerServiceFactory.getServices(configuredRelayer);
20 changes: 20 additions & 0 deletions packages/bridge-ui/src/libs/relayer/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,23 @@ export type RelayerConfig = {
export type ConfiguredRelayer = {
configuredRelayer: RelayerConfig[];
};

export const FeeTypes = {
Eth: 'eth',
Erc20Deployed: 'erc20Deployed',
Erc20NotDeployed: 'erc20NotDeployed',
Erc721Deployed: 'erc721Deployed',
Erc721NotDeployed: 'erc721NotDeployed',
Erc1155NotDeployed: 'erc1155NotDeployed',
Erc1155Deployed: 'erc1155Deployed',
} as const;

export type FeeType = (typeof FeeTypes)[keyof typeof FeeTypes];

export type Fee = {
type: FeeType;
amount: string;
destChainID: number;
};

export type ProcessingFeeApiResponse = { fees: Fee[] };

0 comments on commit c8caf25

Please sign in to comment.