butterjs-sdk
Version:
Butter Network SDK
361 lines (360 loc) • 17.4 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDistributeRate = exports.isTokenMintable = exports.getTokenCandidates = exports.getTokenCandidatesOneByOne = exports.getTargetTokenAddress = exports.getTargetToken = exports.getVaultBalance = exports.getSwapFee = exports.getBridgeFee = void 0;
const constants_1 = require("../../constants");
const TokenRegister_1 = require("../../libs/TokenRegister");
const ethers_1 = require("ethers");
const tokenUtil_1 = require("../../utils/tokenUtil");
const tokenUtil_2 = require("../../utils/tokenUtil");
const utils_1 = require("../../utils");
const VaultToken_1 = require("../../libs/VaultToken");
const EVMOmnichainService_1 = require("../../libs/mos/EVMOmnichainService");
const MAPOmnichainServiceRelay_json_1 = __importDefault(require("../../abis/MAPOmnichainServiceRelay.json"));
const MAPOmnichainService_json_1 = __importDefault(require("../../abis/MAPOmnichainService.json"));
const near_api_js_1 = require("near-api-js");
const near_method_names_1 = require("../../constants/near_method_names");
const batchRequestUtils_1 = require("../../utils/batchRequestUtils");
const web3_1 = __importDefault(require("web3"));
const TokenRegister_json_1 = __importDefault(require("../../abis/TokenRegister.json"));
const requestUtils_1 = require("../../utils/requestUtils");
const constants_2 = require("../../constants/constants");
/**
* get fee for bridging srcToken to targetChain
* @param srcToken
* @param targetChain
* @param amount
* @param mapRpcProvider
*/
async function getBridgeFee(srcToken, targetChain, amount, mapRpcProvider) {
const chainId = mapRpcProvider.chainId.toString();
const mapChainId = mapRpcProvider.chainId.toString();
const mapProvider = new ethers_1.ethers.providers.JsonRpcProvider(mapRpcProvider.url ? mapRpcProvider.url : (0, constants_1.ID_TO_DEFAULT_RPC_URL)(mapChainId));
const tokenRegister = new TokenRegister_1.TokenRegister(constants_1.TOKEN_REGISTER_ADDRESS_SET[chainId], mapProvider);
let feeAmount = '';
let feeRate = { lowest: '0', rate: '0', highest: '0' };
if ((0, constants_1.IS_MAP)(srcToken.chainId)) {
const tokenAddress = srcToken.isNative
? srcToken.wrapped.address
: srcToken.address;
const tokenFeeRate = await tokenRegister.getFeeRate(tokenAddress, targetChain);
feeRate.lowest = tokenFeeRate.lowest.toString();
feeRate.highest = tokenFeeRate.highest.toString();
feeRate.rate = ethers_1.BigNumber.from(tokenFeeRate.rate).div(100).toString();
feeAmount = _getFeeAmount(amount, feeRate);
}
else {
const mapTokenAddress = await tokenRegister.getRelayChainToken(srcToken.chainId.toString(), srcToken);
const relayChainAmount = await tokenRegister.getRelayChainAmount(mapTokenAddress, srcToken.chainId.toString(), amount);
const tokenFeeRate = await tokenRegister.getFeeRate(mapTokenAddress, targetChain);
feeRate.lowest = tokenFeeRate.lowest;
feeRate.highest = tokenFeeRate.highest;
feeRate.rate = ethers_1.BigNumber.from(tokenFeeRate.rate).div(100).toString();
const feeAmountInMappingToken = _getFeeAmount(relayChainAmount, feeRate);
const feeAmountBN = ethers_1.BigNumber.from(feeAmountInMappingToken);
feeRate.lowest = ethers_1.BigNumber.from(feeRate.lowest)
.mul(amount)
.div(relayChainAmount)
.toString();
feeRate.highest = ethers_1.BigNumber.from(feeRate.highest)
.mul(amount)
.div(relayChainAmount)
.toString();
feeAmount = feeAmountBN.mul(amount).div(relayChainAmount).toString();
}
const distribution = await getDistributeRate(mapChainId);
return Promise.resolve({
feeToken: srcToken,
feeRate: feeRate,
amount: feeAmount.toString(),
feeDistribution: distribution,
});
}
exports.getBridgeFee = getBridgeFee;
/**
* get fee for cross-chain exchange
* @param srcToken source token
* @param targetChain target chain id
* @param amount amount in minimal uint
* @param routeStr cross-chain route in string format
* @param mapRpcProvider map relay chain rpc provider
*/
async function getSwapFee(srcToken, targetChain, amount, routeStr, mapRpcProvider) {
const routes = (0, requestUtils_1.assembleCrossChainRouteFromJson)(routeStr, constants_2.DEFAULT_SLIPPAGE);
const srcRoute = routes.srcChain;
if (srcRoute === undefined ||
srcRoute.length === 0 ||
srcRoute[0].path.length === 0) {
return await getBridgeFee(srcToken, targetChain, amount, mapRpcProvider);
}
let totalAmountOut = '0';
for (let route of routes.mapChain) {
totalAmountOut = ethers_1.BigNumber.from(totalAmountOut)
.add(route.amountOut)
.toString();
}
const tokenOut = srcRoute[0].tokenOut;
const chainId = mapRpcProvider.chainId.toString();
const mapChainId = mapRpcProvider.chainId.toString();
const mapProvider = new ethers_1.ethers.providers.JsonRpcProvider(mapRpcProvider.url ? mapRpcProvider.url : (0, constants_1.ID_TO_DEFAULT_RPC_URL)(mapChainId));
const tokenRegister = new TokenRegister_1.TokenRegister(constants_1.TOKEN_REGISTER_ADDRESS_SET[chainId], mapProvider);
let feeAmount = '';
let feeRate = { lowest: '0', rate: '0', highest: '0' };
if ((0, constants_1.IS_MAP)(srcToken.chainId)) {
const tokenAddress = srcToken.isNative
? srcToken.wrapped.address
: srcToken.address;
const tokenFeeRate = await tokenRegister.getFeeRate(tokenAddress, targetChain);
feeRate.lowest = tokenFeeRate.lowest.toString();
feeRate.highest = tokenFeeRate.highest.toString();
feeRate.rate = ethers_1.BigNumber.from(tokenFeeRate.rate).div(100).toString();
feeAmount = _getFeeAmount(amount, feeRate);
}
else {
const mapTokenAddress = await tokenRegister.getRelayChainToken(srcToken.chainId.toString(), tokenOut);
const relayChainAmount = await tokenRegister.getRelayChainAmount(mapTokenAddress, srcToken.chainId.toString(), totalAmountOut);
const tokenFeeRate = await tokenRegister.getFeeRate(mapTokenAddress, targetChain);
feeRate.lowest = tokenFeeRate.lowest;
feeRate.highest = tokenFeeRate.highest;
feeRate.rate = ethers_1.BigNumber.from(tokenFeeRate.rate).div(100).toString();
const feeAmountInMappingToken = _getFeeAmount(relayChainAmount, feeRate);
const feeAmountBN = ethers_1.BigNumber.from(feeAmountInMappingToken);
feeRate.lowest = ethers_1.BigNumber.from(feeRate.lowest)
.mul(totalAmountOut)
.div(relayChainAmount)
.toString();
feeRate.highest = ethers_1.BigNumber.from(feeRate.highest)
.mul(totalAmountOut)
.div(relayChainAmount)
.toString();
feeAmount = feeAmountBN
.mul(totalAmountOut)
.div(relayChainAmount)
.toString();
}
const distribution = await getDistributeRate(mapChainId);
console.log('11123123123123213123');
console.log('distribution', distribution);
return Promise.resolve({
feeToken: (0, tokenUtil_1.getTokenByAddressAndChainId)((0, utils_1.getHexAddress)(tokenOut.address, srcToken.chainId, !(0, constants_1.IS_NEAR)(srcToken.chainId)), srcToken.chainId),
feeRate: feeRate,
amount: feeAmount.toString(),
feeDistribution: distribution,
});
}
exports.getSwapFee = getSwapFee;
/**
* get vault balance
* @param fromChainId
* @param fromToken
* @param toChainId
* @param rpcProvider
*/
async function getVaultBalance(fromChainId, fromToken, toChainId, rpcProvider) {
if (fromChainId != fromToken.chainId) {
throw new Error("Request Error: chainId and token.chainId doesn't match");
}
const mapChainId = rpcProvider.chainId.toString();
const provider = new ethers_1.ethers.providers.JsonRpcProvider(rpcProvider.url ? rpcProvider.url : (0, constants_1.ID_TO_DEFAULT_RPC_URL)(mapChainId));
const tokenRegister = new TokenRegister_1.TokenRegister(constants_1.TOKEN_REGISTER_ADDRESS_SET[mapChainId], provider);
if (fromToken.isNative) {
fromToken = fromToken.wrapped;
}
const mapTokenAddress = (0, constants_1.IS_MAP)(fromChainId)
? fromToken.address
: await tokenRegister.getRelayChainToken(fromChainId.toString(), fromToken);
const vaultAddress = await tokenRegister.getVaultToken(mapTokenAddress);
if (vaultAddress === constants_1.ZERO_ADDRESS) {
throw new Error(`getVaultBalance: vault address not found for token: ${mapTokenAddress}`);
}
const vaultToken = new VaultToken_1.VaultToken(vaultAddress, provider);
let tokenBalance = await vaultToken.getVaultBalance(toChainId.toString());
let toChainTokenAddress = mapTokenAddress;
if (!(0, constants_1.IS_MAP)(toChainId)) {
toChainTokenAddress = await tokenRegister.getToChainToken(mapTokenAddress, toChainId);
if (toChainTokenAddress === '0x') {
throw new Error('Internal Error: Cannot find corresponding target token on target chain');
}
const mapToken = (0, tokenUtil_1.getTokenByAddressAndChainId)(mapTokenAddress, mapChainId);
const toChainToken = (0, tokenUtil_1.getTokenByAddressAndChainId)(toChainTokenAddress, toChainId);
tokenBalance = ethers_1.BigNumber.from(tokenBalance)
.mul(ethers_1.ethers.utils.parseUnits('1', toChainToken.decimals))
.div(ethers_1.ethers.utils.parseUnits('1', mapToken.decimals))
.toString();
}
return Promise.resolve({
token: (0, tokenUtil_1.getTokenByAddressAndChainId)(toChainTokenAddress, toChainId),
balance: tokenBalance.toString(),
isMintable: await isTokenMintable(toChainTokenAddress, toChainId),
});
}
exports.getVaultBalance = getVaultBalance;
/**
* get srcToken mapping token on target chain
* @param srcToken
* @param targetChainId
* @param rpcProvider
*/
async function getTargetToken(srcToken, targetChainId, rpcProvider) {
const tokenAddress = await getTargetTokenAddress(srcToken, targetChainId, rpcProvider);
if (tokenAddress === '0x') {
throw new Error('token does not exist');
}
return (0, tokenUtil_1.getTokenByAddressAndChainId)(tokenAddress, targetChainId);
}
exports.getTargetToken = getTargetToken;
/**
* get srcToken mapping token on target chain
* @param srcToken
* @param targetChainId
* @param rpcProvider
*/
async function getTargetTokenAddress(srcToken, targetChainId, rpcProvider) {
const mapChainId = rpcProvider.chainId.toString();
const provider = new ethers_1.ethers.providers.JsonRpcProvider(rpcProvider.url ? rpcProvider.url : (0, constants_1.ID_TO_DEFAULT_RPC_URL)(mapChainId));
const tokenRegister = new TokenRegister_1.TokenRegister(constants_1.TOKEN_REGISTER_ADDRESS_SET[mapChainId], provider);
let mapTokenAddress = srcToken.address;
if (!(0, constants_1.IS_MAP)(srcToken.chainId)) {
mapTokenAddress = await tokenRegister.getRelayChainToken(srcToken.chainId.toString(), srcToken);
}
let targetTokenAddress = mapTokenAddress;
if (!(0, constants_1.IS_MAP)(targetChainId)) {
targetTokenAddress = await tokenRegister.getToChainToken(mapTokenAddress, targetChainId);
}
return targetTokenAddress;
}
exports.getTargetTokenAddress = getTargetTokenAddress;
/**
* get what token can be bridge from src chain to target chain
* @param fromChainId
* @param toChainId
* @param provider
*/
async function getTokenCandidatesOneByOne(fromChainId, toChainId, provider) {
let ret = [];
const fromChainTokenList = (0, tokenUtil_2.ID_TO_SUPPORTED_TOKEN)(fromChainId);
for (let i = 0; i < fromChainTokenList.length; i++) {
const token = fromChainTokenList[i];
const tokenToCheck = token.isNative ? token.wrapped : token;
if ((await getTargetTokenAddress(tokenToCheck, toChainId, provider)) != '0x') {
ret.push(token);
}
}
return ret;
}
exports.getTokenCandidatesOneByOne = getTokenCandidatesOneByOne;
/**
* get token candidates with one transaction call
* @param fromChainId
* @param toChainId
* @param provider
*/
async function getTokenCandidates(fromChainId, toChainId, provider) {
const mapUrl = provider.url
? provider.url
: (0, constants_1.ID_TO_DEFAULT_RPC_URL)(provider.chainId.toString());
const web3 = new web3_1.default(mapUrl);
const tokenRegisterContract = new web3.eth.Contract(TokenRegister_json_1.default.abi, constants_1.TOKEN_REGISTER_ADDRESS_SET[provider.chainId.toString()]);
let tokenArr = (0, tokenUtil_2.ID_TO_SUPPORTED_TOKEN)(fromChainId).map((token) => {
if ((0, constants_1.IS_NEAR)(token.chainId)) {
if (token.isNative) {
return (0, utils_1.getHexAddress)(token.wrapped.address, token.chainId, false);
}
else
return (0, utils_1.getHexAddress)(token.address, token.chainId, false);
}
else {
if (token.isNative) {
return token.wrapped.address;
}
else
return token.address;
}
});
if (!(0, constants_1.IS_MAP)(fromChainId)) {
tokenArr = await (0, batchRequestUtils_1.batchGetRelayChainToken)(tokenRegisterContract, fromChainId, tokenArr, mapUrl);
}
if ((0, constants_1.IS_MAP)(toChainId)) {
return (0, tokenUtil_2.ID_TO_SUPPORTED_TOKEN)(fromChainId);
}
const toChainTokenList = await (0, batchRequestUtils_1.batchGetToChainToken)(tokenRegisterContract, tokenArr, toChainId, mapUrl);
let supportedFromChainTokenArr = [];
for (let i = 0; i < toChainTokenList.length; i++) {
if (toChainTokenList[i] != null && toChainTokenList[i] != '0x') {
supportedFromChainTokenArr.push((0, tokenUtil_2.ID_TO_SUPPORTED_TOKEN)(fromChainId)[i]);
}
}
return supportedFromChainTokenArr;
}
exports.getTokenCandidates = getTokenCandidates;
/**
* check if a token is mintable on mos
* @param tokenAddress
* @param chainId
*/
async function isTokenMintable(tokenAddress, chainId) {
const rpcUrl = (0, constants_1.ID_TO_DEFAULT_RPC_URL)(chainId);
const rpcProvider = new ethers_1.ethers.providers.JsonRpcProvider(rpcUrl);
if ((0, constants_1.IS_MAP)(chainId)) {
const tokenRegister = new TokenRegister_1.TokenRegister(constants_1.TOKEN_REGISTER_ADDRESS_SET[chainId], rpcProvider);
return tokenRegister.checkMintable(tokenAddress);
}
else if ((0, constants_1.IS_NEAR)(chainId)) {
const accountId = constants_1.MOS_CONTRACT_ADDRESS_SET[(0, constants_1.ID_TO_CHAIN_ID)(chainId)];
const connectionConfig = {
networkId: (0, constants_1.ID_TO_NEAR_NETWORK)(chainId),
nodeUrl: (0, constants_1.ID_TO_DEFAULT_RPC_URL)(chainId),
};
const near = await (0, near_api_js_1.connect)(connectionConfig);
const response = await near.connection.provider.query({
request_type: 'call_function',
finality: 'final',
account_id: accountId,
method_name: near_method_names_1.GET_MCS_TOKENS,
args_base64: 'e30=',
});
const mosTokenSet = JSON.parse((0, utils_1.asciiToString)(response.result));
for (let i = 0; i < mosTokenSet.length; i++) {
if ((0, utils_1.getHexAddress)(mosTokenSet[i][0].toLowerCase(), chainId, false) ===
tokenAddress.toLowerCase()) {
return true;
}
}
return false;
}
else {
const mos = new EVMOmnichainService_1.EVMOmnichainService(constants_1.MOS_CONTRACT_ADDRESS_SET[(0, constants_1.ID_TO_CHAIN_ID)(chainId)], MAPOmnichainService_json_1.default.abi, rpcProvider);
return mos.isMintable(tokenAddress);
}
}
exports.isTokenMintable = isTokenMintable;
async function getDistributeRate(mapChainId) {
const rpcUrl = (0, constants_1.ID_TO_DEFAULT_RPC_URL)(mapChainId);
const rpcProvider = new ethers_1.ethers.providers.JsonRpcProvider(rpcUrl);
if (!(0, constants_1.IS_MAP)(mapChainId)) {
throw new Error('chain id is not MAP');
}
const mos = new ethers_1.ethers.Contract(constants_1.MOS_CONTRACT_ADDRESS_SET[(0, constants_1.ID_TO_CHAIN_ID)(mapChainId)], MAPOmnichainServiceRelay_json_1.default.abi, rpcProvider);
const lpRate = await mos.distributeRate(0);
const relayerRate = await mos.distributeRate(1);
const protocolRate = await mos.distributeRate(2);
console.log('relay', relayerRate);
return Promise.resolve({
relayer: relayerRate.rate.div(100).toString(),
lp: lpRate.rate.div(100).toString(),
protocol: protocolRate.rate.div(100).toString(),
});
}
exports.getDistributeRate = getDistributeRate;
function _getFeeAmount(amount, feeRate) {
const feeAmount = ethers_1.BigNumber.from(amount).mul(feeRate.rate).div(10000);
if (feeAmount.gt(feeRate.highest)) {
return feeRate.highest.toString();
}
else if (feeAmount.lt(feeRate.lowest)) {
return feeRate.lowest.toString();
}
return feeAmount.toString();
}