diff --git a/.github/workflows/publish-back-core.yaml b/.github/workflows/publish-back-core.yaml index e64c545c..0551fe69 100644 --- a/.github/workflows/publish-back-core.yaml +++ b/.github/workflows/publish-back-core.yaml @@ -2,7 +2,7 @@ name: Create and publish image bridge-core on: push: - branches: [ main ] + branches: [ main, fee-amount-usd ] workflow_dispatch: env: diff --git a/.github/workflows/publish-indexer-evm.yaml b/.github/workflows/publish-indexer-evm.yaml index 87773c81..fe4c255d 100644 --- a/.github/workflows/publish-indexer-evm.yaml +++ b/.github/workflows/publish-indexer-evm.yaml @@ -2,7 +2,7 @@ name: Create and publish image indexer-evm on: push: - branches: [ main ] + branches: [ main, fee-amount-usd ] workflow_dispatch: env: diff --git a/.github/workflows/publish-indexer-svm.yaml b/.github/workflows/publish-indexer-svm.yaml index 4fc0a4d0..ce3fb237 100644 --- a/.github/workflows/publish-indexer-svm.yaml +++ b/.github/workflows/publish-indexer-svm.yaml @@ -2,7 +2,7 @@ name: Create and publish image indexer-svm on: push: - branches: [ main ] + branches: [ main, fee-amount-usd ] workflow_dispatch: env: diff --git a/backend/back-core/src/controllers/receipt.controller.ts b/backend/back-core/src/controllers/receipt.controller.ts index d5210712..cd9a79b4 100644 --- a/backend/back-core/src/controllers/receipt.controller.ts +++ b/backend/back-core/src/controllers/receipt.controller.ts @@ -64,8 +64,8 @@ export class ReceiptController { receipt.bridgeAddress, [ ...Object.values(stageConfig.contracts).map(c => c.startsWith("0x") ? c.toLowerCase() : c), - ...Object.values(tokensConfig.bridges).flatMap(network => - Object.values(network as Record).map(addr => + ...Object.values(tokensConfig.bridges).flatMap(network => + Object.values(network as Record).map(addr => addr.startsWith("0x") ? addr.toLowerCase() : addr ) ) @@ -164,6 +164,7 @@ export class ReceiptController { } } + async getReceiptIdByTransactionHash( transactionHash: string ): Promise { diff --git a/backend/back-core/src/controllers/send-signature.controller.ts b/backend/back-core/src/controllers/send-signature.controller.ts index b75d2691..264e66f9 100644 --- a/backend/back-core/src/controllers/send-signature.controller.ts +++ b/backend/back-core/src/controllers/send-signature.controller.ts @@ -54,7 +54,7 @@ export class SendSignatureController { if (!Networks.isSupportedNetwork(networkTo)) { throw new Error(`Network To (${networkTo}) is not supported`); } - const { feeAmount, amountToSend } = await getFees( + const { feeAmount, feeAmountUsd, amountToSend } = await getFees( networkFrom, networkTo, addressToUserFriendly(tokenAddress), amount, isMaxAmount ); const timestamp = Math.floor(Date.now() / 1000); @@ -97,6 +97,7 @@ export class SendSignatureController { } return { + feeAmountUsd, sendPayload, ...signResult, }; diff --git a/backend/back-core/src/fee/bridgeFee.ts b/backend/back-core/src/fee/bridgeFee.ts index 335e2daf..eed7b42f 100644 --- a/backend/back-core/src/fee/bridgeFee.ts +++ b/backend/back-core/src/fee/bridgeFee.ts @@ -6,7 +6,7 @@ const percentFromAmount: { [key: number]: number } = { }; -export function getBridgeFeeInNative(nativeUsdPrice: Decimal, tokenUsdPrice: Decimal, amount: Decimal, minBridgeFeeUSD: number): Decimal { +export function getBridgeFeeUSD(tokenUsdPrice: Decimal, amount: Decimal, minBridgeFeeUSD: number): Decimal { // Get fee in USD const amountUsd = coin2Usd(amount, tokenUsdPrice); const feePercent = getFeePercent(amountUsd); @@ -16,9 +16,7 @@ export function getBridgeFeeInNative(nativeUsdPrice: Decimal, tokenUsdPrice: Dec if (feeUsd.lessThan(minBridgeFeeUSD)) feeUsd = new Decimal(minBridgeFeeUSD); - - // Calculate fee in native token - return usd2Coin(feeUsd, nativeUsdPrice); + return feeUsd; } function getFeePercent(amountInUsdt: Decimal): number { diff --git a/backend/back-core/src/fee/fee.ts b/backend/back-core/src/fee/fee.ts index 2d2a62d1..60ad3ad0 100644 --- a/backend/back-core/src/fee/fee.ts +++ b/backend/back-core/src/fee/fee.ts @@ -1,16 +1,17 @@ -import { getTokenUSDPriceByAddress } from "./token-prices"; +import { convertFromDecimals, getTokenUSDPriceByAddress } from "./token-prices"; import Decimal from "decimal.js"; -import { getBridgeFeeInNative } from "./bridgeFee"; +import { getBridgeFeeUSD } from "./bridgeFee"; import { stageConfig } from "../../config"; +import { usd2Coin } from "./utils"; export async function getFees( - networkFrom: bigint, - networkTo: bigint, - tokenAddr: string, - amount: bigint, - isMaxAmount: boolean -): Promise<{ feeAmount: bigint; amountToSend: bigint }> { + networkFrom: bigint, + networkTo: bigint, + tokenAddr: string, + amount: bigint, + isMaxAmount: boolean +): Promise<{ feeAmountUsd: Decimal; feeAmount: bigint; amountToSend: bigint }> { console.log("getFees called with:", { networkFrom, @@ -31,9 +32,9 @@ export async function getFees( const networkFeeConfig = stageConfig.fees.networks[networkFrom.toString()]; console.log("networkFeeConfig:", networkFeeConfig); - - let bridgeFeeNative = getBridgeFeeInNative(fromCoinPrice, tokenPrice, amountDecimal, networkFeeConfig.minBridgeFeeUSD); - console.log("bridgeFeeNative (initial):", bridgeFeeNative.toString()); + + let bridgeFeeUSD = getBridgeFeeUSD(tokenPrice, amountDecimal, networkFeeConfig.minBridgeFeeUSD); + let bridgeFeeNative = usd2Coin(bridgeFeeUSD, fromCoinPrice); // try to calculate max amount of native coins that can be transferred considering fees if (isMaxAmount) { @@ -44,14 +45,19 @@ export async function getFees( if (amountDecimal.lte(0)) { throw new Error("Amount to send is too small"); } - bridgeFeeNative = getBridgeFeeInNative(fromCoinPrice, tokenPrice, amountDecimal, networkFeeConfig.minBridgeFeeUSD); - console.log("bridgeFeeNative (recalculated):", bridgeFeeNative.toString()); + bridgeFeeUSD = getBridgeFeeUSD(tokenPrice, amountDecimal, networkFeeConfig.minBridgeFeeUSD); } + + bridgeFeeNative = usd2Coin(bridgeFeeUSD, fromCoinPrice); bridgeFeeNative = bridgeFeeNative.ceil(); console.log("bridgeFeeNative (ceiled):", bridgeFeeNative.toString()); + console.log( + `[USD] Bridge fee in USD: ${bridgeFeeUSD.toString()}` + ); const result = { + feeAmountUsd: await convertFromDecimals(bridgeFeeUSD, networkFrom.toString(), tokenAddr), feeAmount: BigInt(bridgeFeeNative.toHex()), amountToSend: BigInt(amountDecimal.toHex()) }; diff --git a/backend/back-core/src/fee/token-prices.ts b/backend/back-core/src/fee/token-prices.ts index f2353912..056d7aba 100644 --- a/backend/back-core/src/fee/token-prices.ts +++ b/backend/back-core/src/fee/token-prices.ts @@ -14,6 +14,33 @@ export async function getTokenUSDPrice(tokenSymbol: string) { return new Decimal(price); } +export async function convertFromDecimals( + amount: Decimal | bigint | number, + networkName: string, + tokenAddr?: string +) { + const token = tokens.getToken(networkName, tokenAddr); + if (!token) + throw new Error(`Token ${tokenAddr} not found in ${networkName} network`); + const decimals = token.denomination; + return new Decimal(typeof amount === "bigint" ? amount.toString() : amount).dividedBy( + new Decimal(10).pow(decimals) + ); +} + +export async function convertToDecimals( + amount: Decimal, + networkName: string, + tokenAddr?: string +) { + const token = tokens.getToken(networkName, tokenAddr); + if (!token) + throw new Error(`Token ${tokenAddr} not found in ${networkName} network`); + const decimals = token.denomination; + return new Decimal(amount).mul( + new Decimal(10).pow(decimals) + ); +} class CachedPrice { prices: { [symbol: string]: Decimal } = {}; diff --git a/backend/back-core/src/routes/utils.ts b/backend/back-core/src/routes/utils.ts index 36f81093..de480c28 100644 --- a/backend/back-core/src/routes/utils.ts +++ b/backend/back-core/src/routes/utils.ts @@ -532,6 +532,10 @@ export const SendPayload = z.object({ export type SendPayload = z.infer; export const sendPayloadResponseSchema = z.object({ + feeAmountUsd: z.coerce.number().positive().openapi({ + example: 100, + description: "Amount of fee in usd", + }), sendPayload: SendPayload, signedBy: z .string()