UNPKG

butterjs-sdk

Version:
361 lines (360 loc) 17.4 kB
"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(); }