@renproject/ren
Version:
Official Ren JavaScript SDK for bridging crypto assets cross-chain.
122 lines • 6.04 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { ErrorWithCode, isContractChain, isDepositChain, RenJSError, } from "@renproject/utils";
import BigNumber from "bignumber.js";
export const BIP_DENOMINATOR = 10000;
// Some assets may have their gas price defined in a different unit.
const assetGasDivisors = {
LUNA: 5,
};
export const estimateTransactionFee = (renVM, asset, fromChain, toChain) => __awaiter(void 0, void 0, void 0, function* () {
// Determine if the transaction is a lock-and-mint, burn-and-release or
// burn-and-mint.
const [blockState, isLockAssetOnFromChain, isLockAssetOnToChain, isMintAssetOnFromChain, isMintAssetOnToChain, decimalsOnFromChain, decimalsOnToChain, isDepositAssetOnFromChain, isDepositAssetOnToChain,] = yield Promise.all([
renVM.queryBlockState(asset, 5),
fromChain.isLockAsset(asset),
toChain.isLockAsset(asset),
isContractChain(fromChain) && fromChain.isMintAsset(asset),
isContractChain(toChain) && toChain.isMintAsset(asset),
fromChain.assetDecimals(asset),
toChain.assetDecimals(asset),
isDepositChain(fromChain) && fromChain.isDepositAsset(asset),
isDepositChain(toChain) && toChain.isDepositAsset(asset),
]);
if (!isLockAssetOnFromChain && !isMintAssetOnFromChain) {
throw ErrorWithCode.updateError(new Error(`Asset not supported by chain ${fromChain.chain}.`), RenJSError.PARAMETER_ERROR);
}
if (!isLockAssetOnToChain && !isMintAssetOnToChain) {
throw ErrorWithCode.updateError(new Error(`Asset not supported by chain ${toChain.chain}.`), RenJSError.PARAMETER_ERROR);
}
const isLockAndMint = isLockAssetOnFromChain;
const isBurnAndRelease = isLockAssetOnToChain;
// const isBurnAndMint = isMintAssetOnFromChain && isMintAssetOnToChain;
if (!blockState[asset]) {
throw ErrorWithCode.updateError(new Error(`No fee details found for ${asset}`), RenJSError.UNKNOWN_ERROR);
}
const { gasLimit, gasCap, minimumAmount: minimumBeforeFees, dustAmount, } = blockState[asset];
// For burning, use the fees for the origin chain. For other txs, use
// the fees for the target chain.
const feesChain = isBurnAndRelease ? fromChain.chain : toChain.chain;
const mintAndBurnFees = blockState[asset].fees.chains.filter((chainFees) => chainFees.chain === feesChain)[0];
// No other way of getting proper decimals for burn-and-mints.
const nativeDecimals = Math.max(decimalsOnFromChain, decimalsOnToChain);
const requiresTransfer = (isLockAndMint && isDepositAssetOnFromChain) ||
(isBurnAndRelease && isDepositAssetOnToChain);
const fixedFee = requiresTransfer
? gasLimit
.times(gasCap)
.shiftedBy(-assetGasDivisors[asset] || 0)
.plus(isBurnAndRelease ? dustAmount.plus(1) : 0)
: new BigNumber(0);
const mintFee = mintAndBurnFees && mintAndBurnFees.mintFee
? mintAndBurnFees.mintFee.toNumber()
: 15;
const burnFee = mintAndBurnFees && mintAndBurnFees.burnFee
? mintAndBurnFees.burnFee.toNumber()
: 15;
const burnAndMintFee = mintAndBurnFees && mintAndBurnFees.burnAndMintFee
? mintAndBurnFees.burnAndMintFee.toNumber()
: 15;
const variableFee = isLockAndMint
? mintFee
: isBurnAndRelease
? burnFee
: burnAndMintFee;
const minimumAmount = minimumBeforeFees
.plus(fixedFee)
.plus(minimumBeforeFees
.plus(fixedFee)
.times(variableFee)
.dividedBy(BIP_DENOMINATOR)
.decimalPlaces(0, BigNumber.ROUND_DOWN));
const estimateOutput = (input) => {
const amount = BigNumber.isBigNumber(input) ||
typeof input === "string" ||
typeof input === "number"
? input
: input.amount;
const convertUnit = typeof input === "object" && !BigNumber.isBigNumber(input)
? input.convertUnit || false
: false;
const amountBN = new BigNumber(amount).shiftedBy(convertUnit ? nativeDecimals : 0);
if (amountBN.isLessThan(minimumAmount)) {
return new BigNumber(0);
}
if (isLockAndMint) {
return BigNumber.max(amountBN
.minus(fixedFee)
.times(BIP_DENOMINATOR - variableFee)
.dividedBy(BIP_DENOMINATOR)
.decimalPlaces(0, BigNumber.ROUND_DOWN), 0).shiftedBy(convertUnit ? -nativeDecimals : 0);
}
else if (isBurnAndRelease) {
return BigNumber.max(amountBN
.times(BIP_DENOMINATOR - variableFee)
.dividedBy(BIP_DENOMINATOR)
.minus(fixedFee)
.decimalPlaces(0, BigNumber.ROUND_DOWN), 0).shiftedBy(convertUnit ? -nativeDecimals : 0);
}
else {
// Burn-and-mint transaction. If a fixed-fee is ever added for all
// transactions, it will need to be added here.
return BigNumber.max(amountBN
.times(BIP_DENOMINATOR - variableFee)
.dividedBy(BIP_DENOMINATOR)
.decimalPlaces(0, BigNumber.ROUND_DOWN), 0).shiftedBy(convertUnit ? -nativeDecimals : 0);
}
};
return {
fixedFee,
variableFee,
minimumAmount,
estimateOutput,
};
});
//# sourceMappingURL=fees.js.map