diff --git a/src/adapters/polkadot.ts b/src/adapters/polkadot.ts index 853d791..f7aea1e 100644 --- a/src/adapters/polkadot.ts +++ b/src/adapters/polkadot.ts @@ -1,6 +1,6 @@ import { Storage } from "@acala-network/sdk/utils/storage"; import { AnyApi, FixedPointNumber as FN } from "@acala-network/sdk-core"; -import { combineLatest, map, Observable } from "rxjs"; +import { combineLatest, map, Observable, from } from "rxjs"; import { SubmittableExtrinsic } from "@polkadot/api/types"; import { DeriveBalancesAll } from "@polkadot/api-derive/balances/types"; @@ -14,6 +14,7 @@ import { BalanceData, BasicToken, TransferParams } from "../types"; import { createRouteConfigs, getDestAccountInfo, + getPolkadotXcmDeleveryFee, validateAddress, } from "../utils"; @@ -172,8 +173,9 @@ class BasePolkadotAdapter extends BaseCrossChainAdapter { balance: this.balanceAdapter .subscribeBalance(token, address) .pipe(map((i) => i.available)), + deliveryFee: from(getPolkadotXcmDeleveryFee(this.chain.id, to, this.api)), }).pipe( - map(({ balance, txFee }) => { + map(({ balance, txFee, deliveryFee }) => { const tokenMeta = this.balanceAdapter?.getToken(token); const feeFactor = 1.2; const fee = FN.fromInner(txFee, tokenMeta?.decimals).mul( @@ -183,6 +185,7 @@ class BasePolkadotAdapter extends BaseCrossChainAdapter { // always minus ed return balance .minus(fee) + .minus(deliveryFee) .minus(FN.fromInner(tokenMeta?.ed || "0", tokenMeta?.decimals)); }) ); diff --git a/src/utils/get-xcm-delivery-fee.ts b/src/utils/get-xcm-delivery-fee.ts new file mode 100644 index 0000000..d553ae2 --- /dev/null +++ b/src/utils/get-xcm-delivery-fee.ts @@ -0,0 +1,119 @@ +import { AnyApi, FixedPointNumber } from "@acala-network/sdk-core"; +import { XcmVersionedXcm } from "@polkadot/types/lookup"; +import { Codec, Observable } from "@polkadot/types/types"; +import { firstValueFrom } from "rxjs"; + +import { ChainId, chains } from "../configs"; + +// refer to https://github.com/albertov19/xcmTools/blob/main/calculateKusamaDeliveryFees.ts +// delivery_fee_factor * (base_fee + encoded_msg_len * per_byte_fee) +// TODO: when most chains added xcm delivery fee, may move these to bridge package + +/** + * Only for polkadot and kusama dmp + */ +export async function getPolkadotXcmDeleveryFee( + from: ChainId, + to: ChainId, + fromApi?: AnyApi +) { + if (from !== "polkadot" && from !== "kusama") { + throw new Error("from chain must be polkadot or kusama"); + } + if (!fromApi) return FixedPointNumber.ZERO; + const decimal = fromApi?.registry.chainDecimals[0]; + const paraID = chains[to].paraChainId; + + // https://github.com/polkadot-fellows/runtimes/blob/16635f6abda9c5fe4b62231d632ab6f6695b48f7/system-parachains/constants/src/polkadot.rs#L60C29-L60C43 + // https://github.com/polkadot-fellows/runtimes/blob/16635f6abda9c5fe4b62231d632ab6f6695b48f7/system-parachains/constants/src/kusama.rs#L38 + const UNITS = + from === "polkadot" ? BigInt(10000000000) : BigInt(1000000000000); + const QUID = UNITS / BigInt(30); + const CENTS = QUID / BigInt(100); + // const GRAND = QUID / BigInt(1000); + const MILLICENTS = CENTS / BigInt(1000); + + const byteFee = BigInt(10) * MILLICENTS; + const baseDeliveryFee = BigInt(3) * CENTS; + + const exampleXcm: XcmVersionedXcm = fromApi?.createType( + "XcmVersionedXcm", + exampleXcmMessage + ) as any; + const xcmBytes = exampleXcm.toU8a(); + + const deliveryFeeFactor: Codec = await firstValueFrom( + fromApi?.query.dmp.deliveryFeeFactor(BigInt(paraID)) as Observable + ); + + const convDeliveryFeeFactor = + BigInt(deliveryFeeFactor.toString()) / BigInt(10 ** 18); + + const fee = + convDeliveryFeeFactor * + (baseDeliveryFee + BigInt(xcmBytes.length) * byteFee); + + console.log( + fee, + FixedPointNumber.fromInner(fee.toString(), decimal) + .mul(new FixedPointNumber(1.2)) + .toString() + ); + + return FixedPointNumber.fromInner(fee.toString(), decimal).mul( + new FixedPointNumber(1.2) // add some buffer + ); +} + +const exampleXcmMessage = { + V3: [ + { + WithdrawAsset: [ + { + assets: [ + { + ConcreteFungible: { + id: "Here", + amount: "20000000000000", + }, + }, + ], + effects: [ + { + BuyExecution: { + fees: "0", + weight_limit: { + Unlimited: null, + }, + }, + }, + { + DepositAsset: { + assets: "All", + dest: { + X1: { + Parachain: "2000", + }, + }, + }, + }, + { + DepositReserveAsset: { + assets: "All", + dest: { + X1: { + AccountId32: { + network: "Any", + id: "0x76c7e05b8ee00557f8f7e11f283aacd3bbee59c1d9fd588ebbe1b6c435fe504c", + }, + }, + }, + effects: [], + }, + }, + ], + }, + ], + }, + ], +}; diff --git a/src/utils/index.ts b/src/utils/index.ts index 55221c0..d91a3e2 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -5,3 +5,4 @@ export * from "./xtokens-params"; export * from "./polkadot-xcm-params"; export * from "./create-route-configs"; export * from "./destination-utils"; +export * from "./get-xcm-delivery-fee";