UNPKG

zic-evm-sdk

Version:
495 lines (494 loc) 21.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.ZebecCardService = void 0; const bignumber_js_1 = __importDefault(require("bignumber.js")); const ethers_1 = require("ethers"); const artifacts_1 = require("./artifacts"); const constants_1 = require("./constants"); const utils_1 = require("./utils"); /** * A class which holds methods and properties to interact with Zebec Instanct Card evm contracts. * @example * * const signer = <ethers.Signer Instance> // most wallet provider have way to create signer * const chainId = 11155111 // sepolia; * const service = new ZebecCardService(signer, chainId); * */ class ZebecCardService { /** * Create instance of ZebecCardService. * @param signer ethers signer * @param chainId chain ID supported by sdk */ constructor(signer, chainId) { this.signer = signer; this.chainId = chainId; const zebecCardAddress = (0, constants_1.getZebecCardAddress)(chainId); const usdcAddress = (0, constants_1.getUsdcAddress)(chainId); const wethAddress = (0, constants_1.getWethAddress)(chainId); this.zebecCard = [constants_1.ODYSSEY_MAINNET_CHAIN_ID, constants_1.ODYSSEY_TESTNET_CHAIN_ID].includes(chainId) ? artifacts_1.OdysseyZebecCard__factory.connect(zebecCardAddress, signer) : artifacts_1.ZebecCard__factory.connect(zebecCardAddress, signer); this.usdcToken = artifacts_1.Token__factory.connect(usdcAddress, signer); this.weth = artifacts_1.Weth__factory.connect(wethAddress, signer); } /** * Sets Native Fee in card config. * * Can only be invoked admin. * * @param params * @returns */ async setNativeFee(params) { const feeInBps = BigInt((0, utils_1.percentToBps)(params.feeInPercent)); return this.zebecCard.setNativeFee(feeInBps); } /** * Sets NonNative Fee in card config * * Can only be invoked admin. * * @param params * @returns */ async setNonNativeFee(params) { const feeInBps = BigInt((0, utils_1.percentToBps)(params.feeInPercent)); return this.zebecCard.setNonNativeFee(feeInBps); } /** * Sets Revenue Fee in card config * * Can only be invoked admin. * * @param params * @returns */ async setRevenueFee(params) { const feeInBps = BigInt((0, utils_1.percentToBps)(params.feeInPercent)); return this.zebecCard.setRevenueFee(feeInBps); } /** * Sets Revenue vault address in card config * * Can only be invoked admin. * * @param params * @returns */ async setRevenueVault(params) { return this.zebecCard.setRevenueVault(params.vaultAddress); } /** * Sets commission vault address in card config * * Can only be invoked admin. * * @param params * @returns */ async setCommissionVault(params) { return this.zebecCard.setComissionVault(params.vaultAddress); } /** * Sets card vault address in card config * * Can only be invoked admin. * */ async setCardVault(params) { return this.zebecCard.setCardVault(params.vaultAddress); } /** * Sets usdc address in card config * Can only be invoked by admin */ async setUsdcAddress(params) { return this.zebecCard.setUsdcAddress(params.tokenAddress); } /** * Sets minimum card amount for purchasing card in card config */ async setMinCardAmount(params) { const decimals = await this.usdcToken.decimals(); const minCardAmount = ethers_1.ethers.parseUnits(params.minCardAmount, decimals); return this.zebecCard.setMinCardAmount(minCardAmount); } /** * Sets maximum card amount for purchasing card in card config */ async setMaxCardAmount(params) { const decimals = await this.usdcToken.decimals(); const maxCardAmount = ethers_1.ethers.parseUnits(params.maxCardAmount, decimals); return this.zebecCard.setMaxCardAmount(maxCardAmount); } /** * Sets daily card purchase limit in card config */ async setDailyCardPurchaseLimit(params) { const decimals = await this.usdcToken.decimals(); const dailyCardPurchaseLimit = ethers_1.ethers.parseUnits(params.dailyCardPurchaseLimit, decimals); return this.zebecCard.setDailyCardBuyLimit(dailyCardPurchaseLimit); } /** * Updates fee for given min - max (range) amount and inserts if range in not found. */ async setFee(params) { const decimals = await this.usdcToken.decimals(); const minAmount = ethers_1.ethers.parseUnits(params.minAmount, decimals); const maxAmount = ethers_1.ethers.parseUnits(params.maxAmount, decimals); const fee = (0, utils_1.percentToBps)(params.feePercent); return this.zebecCard.setFee(minAmount, maxAmount, fee); } /** * Sets fee tiers only be invoked by admin */ async setFeeTiers(params) { const parsedFeeTiers = await this._parseFeeTiers(params.feeTiers); return this.zebecCard.setFeeArray(parsedFeeTiers); } async _parseFeeTiers(feeTiers) { const decimals = await this.usdcToken.decimals(); return feeTiers.map((feeTier) => { return { fee: (0, utils_1.percentToBps)(feeTier.feePercent), minAmount: ethers_1.ethers.parseUnits(feeTier.minAmount, decimals), maxAmount: ethers_1.ethers.parseUnits(feeTier.maxAmount, decimals), }; }); } async setCustomFee(params) { const fee = (0, utils_1.percentToBps)(params.fee.toString()); return this.zebecCard.setCustomTokenFee(params.tokenAddress, fee); } async getCustomFee(params) { const fee = await this.zebecCard.getCustomTokenFee(params.tokenAddress); return (0, utils_1.bpsToPercent)(fee.toString()); } /** * Deposits usdc to user vault * @param params * @returns */ async depositUsdc(params) { if ([constants_1.ODYSSEY_MAINNET_CHAIN_ID, constants_1.ODYSSEY_TESTNET_CHAIN_ID].includes(this.chainId)) { throw new Error("Method not supported for this chain"); } const decimals = await this.usdcToken.decimals(); const parsedAmount = ethers_1.ethers.parseUnits(params.amount, decimals); return this.zebecCard.depositUsdc(parsedAmount); } /** * Withdraw usdc from user vault * @param params * @returns */ async withdraw(params) { if ([constants_1.ODYSSEY_MAINNET_CHAIN_ID, constants_1.ODYSSEY_TESTNET_CHAIN_ID].includes(this.chainId)) { throw new Error("Method not supported for this chain"); } const decimals = await this.usdcToken.decimals(); const parsedAmount = ethers_1.ethers.parseUnits(params.amount, decimals); return this.zebecCard.withdraw(parsedAmount); } /** * Transfer specified amount from user's vault balance to card vault with some fee amount for card purchase. * @param params * @returns */ async buyCard(params) { if ([constants_1.ODYSSEY_MAINNET_CHAIN_ID, constants_1.ODYSSEY_TESTNET_CHAIN_ID].includes(this.chainId)) { throw new Error("Method not supported for this chain"); } const decimals = await this.usdcToken.decimals(); const parsedAmount = ethers_1.ethers.parseUnits(params.amount, decimals); if (!(0, utils_1.isEmailValid)(params.buyerEmail)) { throw new Error("Invalid email: " + params.buyerEmail); } const vaultBalance = await this.zebecCard.cardBalances(this.signer); if (parsedAmount > vaultBalance) { throw new Error("Not enough balance. Vault balance: " + ethers_1.ethers.formatUnits(vaultBalance, decimals) + " Requested amount: " + params.amount); } const cardConfig = await this.zebecCard.cardConfig(); const minRange = cardConfig.minCardAmount; const maxRange = cardConfig.maxCardAmount; if (parsedAmount < minRange || parsedAmount > maxRange) { throw new Error("Amount must be with range: " + ethers_1.ethers.formatUnits(minRange, decimals) + " - " + ethers_1.ethers.formatUnits(maxRange, decimals)); } const cardPurchaseInfo = await this.zebecCard.cardPurchases(this.signer); const lastCardPurchaseDate = new Date(Number(cardPurchaseInfo.unixInRecord * 1000n)); const today = new Date(); let cardPurchaseOfDay = 0n; if ((0, utils_1.areDatesOfSameDay)(today, lastCardPurchaseDate)) { cardPurchaseOfDay = cardPurchaseInfo.totalCardBoughtPerDay + parsedAmount; } else { cardPurchaseOfDay = parsedAmount; } if (cardPurchaseOfDay > cardConfig.dailyCardBuyLimit) { throw new Error("Requested card purchase amount exceeds daily purchase limit. Daily limit: " + ethers_1.ethers.formatUnits(cardConfig.dailyCardBuyLimit, decimals) + " Today's purchase amount: " + ethers_1.ethers.formatUnits(cardPurchaseInfo.totalCardBoughtPerDay, decimals)); } const emailHash = await (0, utils_1.hashSHA256)(params.buyerEmail); return this.zebecCard.buyCard(parsedAmount, params.cardTypeId, emailHash); } /** * Swaps given src token to usdc and transfers to user's vault * @param params * @param overrides * @returns */ async swapAndDeposit(params, overrides) { if ([constants_1.ODYSSEY_MAINNET_CHAIN_ID, constants_1.ODYSSEY_TESTNET_CHAIN_ID].includes(this.chainId)) { throw new Error("Method not supported for this chain"); } const { swapParams, ether } = params; const srcToken = artifacts_1.Token__factory.connect(swapParams.description.srcToken, this.signer); const dstToken = artifacts_1.Token__factory.connect(swapParams.description.dstToken, this.signer); const srcTokenDecimals = await srcToken.decimals(); const dstTokenDecimals = await dstToken.decimals(); const executor = swapParams.executor; const description = { srcToken: swapParams.description.srcToken, dstToken: swapParams.description.dstToken, srcReceiver: swapParams.description.srcReceiver, dstReceiver: swapParams.description.dstReceiver, amount: ethers_1.ethers.parseUnits(swapParams.description.srcAmount, srcTokenDecimals), minReturnAmount: ethers_1.ethers.parseUnits(swapParams.description.minReturnAmount, dstTokenDecimals), flags: BigInt(swapParams.description.flags), }; const routeData = swapParams.routeData; return this.zebecCard.swapAndDeposit(executor, description, routeData, { value: ethers_1.ethers.parseEther(ether), ...overrides, }); } async buyCardDirect(params) { const decimals = await this.usdcToken.decimals(); const parsedAmount = ethers_1.ethers.parseUnits(params.amount, decimals); if (!(0, utils_1.isEmailValid)(params.buyerEmail)) { throw new Error("Invalid email: " + params.buyerEmail); } const cardConfig = await this.zebecCard.cardConfig(); const minRange = cardConfig.minCardAmount; const maxRange = cardConfig.maxCardAmount; if (parsedAmount < minRange || parsedAmount > maxRange) { throw new Error("Amount must be with range: " + ethers_1.ethers.formatUnits(minRange, decimals) + " - " + ethers_1.ethers.formatUnits(maxRange, decimals)); } const cardPurchaseInfo = await this.zebecCard.cardPurchases(this.signer); const lastCardPurchaseDate = new Date(Number(cardPurchaseInfo.unixInRecord * 1000n)); const today = new Date(); let cardPurchaseOfDay = 0n; if ((0, utils_1.areDatesOfSameDay)(today, lastCardPurchaseDate)) { cardPurchaseOfDay = cardPurchaseInfo.totalCardBoughtPerDay + parsedAmount; } else { cardPurchaseOfDay = parsedAmount; } if (cardPurchaseOfDay > cardConfig.dailyCardBuyLimit) { throw new Error("Requested card purchase amount exceeds daily purchase limit. Daily limit: " + ethers_1.ethers.formatUnits(cardConfig.dailyCardBuyLimit, decimals) + " Today's purchase amount: " + ethers_1.ethers.formatUnits(cardPurchaseInfo.totalCardBoughtPerDay, decimals)); } const emailHash = await (0, utils_1.hashSHA256)(params.buyerEmail); return this.zebecCard.buyCardDirect(parsedAmount, params.cardTypeId, emailHash); } async swapAndBuyCardDirect(params, overrides) { if ([constants_1.ODYSSEY_MAINNET_CHAIN_ID, constants_1.ODYSSEY_TESTNET_CHAIN_ID].includes(this.chainId)) { throw new Error("Method not supported for this chain"); } const { buyerEmail, cardTypeId, swapParams, ether } = params; const srcToken = artifacts_1.Token__factory.connect(swapParams.description.srcToken, this.signer); const dstToken = artifacts_1.Token__factory.connect(swapParams.description.dstToken, this.signer); const srcTokenDecimals = await srcToken.decimals(); const dstTokenDecimals = await dstToken.decimals(); const executor = swapParams.executor; const amount = ethers_1.ethers.parseUnits(swapParams.description.srcAmount, srcTokenDecimals); const minReturnAmount = ethers_1.ethers.parseUnits(swapParams.description.minReturnAmount, dstTokenDecimals); const description = { srcToken: swapParams.description.srcToken, dstToken: swapParams.description.dstToken, srcReceiver: swapParams.description.srcReceiver, dstReceiver: swapParams.description.dstReceiver, amount, minReturnAmount, flags: BigInt(swapParams.description.flags), }; const routeData = swapParams.routeData; const cardConfig = await this.zebecCard.cardConfig(); const minRange = cardConfig.minCardAmount; const maxRange = cardConfig.maxCardAmount; const fee = await this.getCustomFee({ tokenAddress: swapParams.description.srcToken }); const feeAmount = (0, bignumber_js_1.default)(amount.toString()).times((0, bignumber_js_1.default)(fee).div(100)); const amountAfterFeeDeduction = BigInt((0, bignumber_js_1.default)(amount.toString()).minus(feeAmount).toFixed(0, bignumber_js_1.default.ROUND_DOWN)); if (amountAfterFeeDeduction < minRange || amountAfterFeeDeduction > maxRange) { throw new Error("Amount must be with range: " + ethers_1.ethers.formatUnits(minRange, dstTokenDecimals) + " - " + ethers_1.ethers.formatUnits(maxRange, dstTokenDecimals)); } const cardPurchaseInfo = await this.zebecCard.cardPurchases(this.signer); const lastCardPurchaseDate = new Date(Number(cardPurchaseInfo.unixInRecord * 1000n)); const today = new Date(); let cardPurchaseOfDay = 0n; if ((0, utils_1.areDatesOfSameDay)(today, lastCardPurchaseDate)) { cardPurchaseOfDay = cardPurchaseInfo.totalCardBoughtPerDay + amountAfterFeeDeduction; } else { cardPurchaseOfDay = amountAfterFeeDeduction; } if (cardPurchaseOfDay > cardConfig.dailyCardBuyLimit) { throw new Error("Requested card purchase amount exceeds daily purchase limit. Daily limit: " + ethers_1.ethers.formatUnits(cardConfig.dailyCardBuyLimit, dstTokenDecimals) + " Today's purchase amount will be: " + ethers_1.ethers.formatUnits(cardPurchaseOfDay, dstTokenDecimals)); } const emailHash = await (0, utils_1.hashSHA256)(buyerEmail); return this.zebecCard.swapAndBuy(executor, description, routeData, cardTypeId, emailHash, { value: ethers_1.ethers.parseEther(ether), ...overrides, }); } /** * Deposit usdc from user's vault to yield provider * @param params * @returns */ async generateYield(params) { if ([constants_1.ODYSSEY_MAINNET_CHAIN_ID, constants_1.ODYSSEY_TESTNET_CHAIN_ID].includes(this.chainId)) { throw new Error("Method not supported for this chain"); } const decimals = await this.usdcToken.decimals(); const parsedAmount = ethers_1.ethers.parseUnits(params.amount, decimals); return this.zebecCard.generateYield(parsedAmount); } /** * Withdaw deposits and yield from yield provider * @param params * @returns */ async withdrawYield(params) { if ([constants_1.ODYSSEY_MAINNET_CHAIN_ID, constants_1.ODYSSEY_TESTNET_CHAIN_ID].includes(this.chainId)) { throw new Error("Method not supported for this chain"); } const decimals = await this.usdcToken.decimals(); const parsedAmount = ethers_1.ethers.parseUnits(params.amount, decimals); return this.zebecCard.withdrawYield(parsedAmount); } /** * Gets user's vault balance * @param params * @returns */ async getUserBalance(params) { const decimals = await this.usdcToken.decimals(); const cardBalance = await this.zebecCard.cardBalances(params.userAddress); const formattedBalance = ethers_1.ethers.formatUnits(cardBalance, decimals); return formattedBalance; } /** * Gets user's card purchase * @param params * @returns */ async getCardPurhcaseOfDay(params) { const decimals = await this.usdcToken.decimals(); const cardPurchase = await this.zebecCard.cardPurchases(params.userAddress); const totalCardPurchased = ethers_1.ethers.formatUnits(cardPurchase.totalCardBoughtPerDay, decimals); const cardPurchasedTimestamp = Number(cardPurchase.unixInRecord.toString()); return { totalCardPurchased, cardPurchasedTimestamp, }; } /** * Gets Zebec Instant Card contract configs * @returns */ async getCardConfig() { const cardConfig = await this.zebecCard.cardConfig(); const nativeFeePercent = (0, utils_1.bpsToPercent)(cardConfig.nativeFee.toString()); const nonNativeFeePercent = (0, utils_1.bpsToPercent)(cardConfig.nonNativeFee.toString()); const revenueFeePercent = (0, utils_1.bpsToPercent)(cardConfig.revenueFee.toString()); const decimals = await this.usdcToken.decimals(); const minCardAmount = ethers_1.ethers.formatUnits(cardConfig.minCardAmount, decimals); const maxCardAmount = ethers_1.ethers.formatUnits(cardConfig.maxCardAmount, decimals); const dailyCardPurchaseLimit = ethers_1.ethers.formatUnits(cardConfig.dailyCardBuyLimit, decimals); return { cardVault: cardConfig.cardVault, commissionVault: cardConfig.commissionVault, revenueVault: cardConfig.revenueVault, nativeFeePercent, nonNativeFeePercent, revenueFeePercent, totalCardSold: cardConfig.counter, usdcAddress: cardConfig.usdcAddress, maxCardAmount, minCardAmount, dailyCardPurchaseLimit, }; } /** * Gets fee tiers * @returns Array of fee tier */ async getFeeTiers() { const decimals = await this.usdcToken.decimals(); const feeTiers = await this.zebecCard.getFeeTiers(); return feeTiers.map((feeTier) => { return { feePercent: (0, utils_1.bpsToPercent)(feeTier.fee.toString()), maxAmount: ethers_1.ethers.formatUnits(feeTier.maxAmount, decimals), minAmount: ethers_1.ethers.formatUnits(feeTier.minAmount, decimals), }; }); } /** * Gets admin adddress * @returns */ async getAdmin() { return this.zebecCard.owner(); } /** * add allowance of given amount of given token to given spender * @param params * @returns */ async approve(params) { const token = artifacts_1.Token__factory.connect(params.token, this.signer); const decimals = await token.decimals(); const parsedAmount = ethers_1.ethers.parseUnits(params.amount, decimals); const allowance = await token.allowance(this.signer, params.spender); if (allowance < parsedAmount) { return await token.approve(params.spender, parsedAmount); } return null; } /** * Wrap ETH to Wrapped ETH * @param param * @returns */ async wrapEth(param) { const parsedAmount = ethers_1.ethers.parseEther(param.amount); return this.weth.deposit({ value: parsedAmount }); } } exports.ZebecCardService = ZebecCardService;