UNPKG

@mstable/protocol

Version:
252 lines 13.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.FeederMachine = void 0; const generated_1 = require("types/generated"); const math_1 = require("@utils/math"); const constants_1 = require("@utils/constants"); const standardAccounts_1 = require("./standardAccounts"); class FeederMachine { constructor(massetMachine) { this.mAssetMachine = massetMachine; this.sa = massetMachine.sa; } async initAccounts(accounts) { this.sa = await new standardAccounts_1.StandardAccounts().initAccounts(accounts); return this; } async deployFeeder(feederWeights = [200, 200], mAssetWeights = [2500, 2500, 2500, 2500], useLendingMarkets = false, useInterestValidator = false, use2dp = false, useRedemptionPrice = false) { const mAssetDetails = await this.mAssetMachine.deployMasset(useLendingMarkets, false); // Mints 10k mAsset to begin with await this.mAssetMachine.seedWithWeightings(mAssetDetails, mAssetWeights); const fAsset = await this.mAssetMachine.loadBassetProxy("Binance BTC", "bBTC", use2dp ? 2 : 18); const bAssets = [mAssetDetails.mAsset, fAsset]; const feederLogic = await new generated_1.FeederLogic__factory(this.sa.default.signer).deploy(); const feederManager = await new generated_1.FeederManager__factory(this.sa.default.signer).deploy(); const linkedAddress = { "contracts/feeders/FeederLogic.sol:FeederLogic": feederLogic.address, "contracts/feeders/FeederManager.sol:FeederManager": feederManager.address, }; let redemptionPriceSnap; let feederPoolFactory; let impl; // - Deploy InterestValidator contract let interestValidator; if (useInterestValidator) { interestValidator = await new generated_1.InterestValidator__factory(this.sa.default.signer).deploy(mAssetDetails.nexus.address); await mAssetDetails.nexus.setInterestValidator(interestValidator.address); } // - Add fAsset to lending markets const platformIntegration = new generated_1.MockPlatformIntegration__factory(this.sa.governor.signer).attach(mAssetDetails.integrationAddress); const pTokens = []; if (useLendingMarkets) { // - Deploy mock aToken for the mAsset and fAsset const aTokenFactory = new generated_1.MockATokenV2__factory(this.sa.default.signer); const mockATokenMasset = await aTokenFactory.deploy(mAssetDetails.aavePlatformAddress, mAssetDetails.mAsset.address); const mockATokenFasset = await aTokenFactory.deploy(mAssetDetails.aavePlatformAddress, fAsset.address); pTokens.push(mockATokenMasset.address, mockATokenFasset.address); // - Transfer some of the mAsset and fAsset supply to the mocked Aave await mAssetDetails.mAsset.transfer(mAssetDetails.aavePlatformAddress, (await mAssetDetails.mAsset.totalSupply()).div(1000)); await fAsset.transfer(mAssetDetails.aavePlatformAddress, (await fAsset.totalSupply()).div(1000)); // - Add mAsset and fAsset to the mocked Aave platform const mockAave = new generated_1.MockAaveV2__factory(this.sa.default.signer).attach(mAssetDetails.aavePlatformAddress); await mockAave.addAToken(mockATokenMasset.address, mAssetDetails.mAsset.address); await mockAave.addAToken(mockATokenFasset.address, fAsset.address); // - Add mAsset and fAsset to the platform integration await platformIntegration.setPTokenAddress(mAssetDetails.mAsset.address, mockATokenMasset.address); await platformIntegration.setPTokenAddress(fAsset.address, mockATokenFasset.address); } // Deploy feeder pool if (useRedemptionPrice) { // - Deploy RedemptionPriceSnapMock contract redemptionPriceSnap = await new generated_1.RedemptionPriceSnapMock__factory(this.sa.default.signer).deploy(); let redemptionPriceSnapAddress = redemptionPriceSnap.address; feederPoolFactory = generated_1.NonPeggedFeederPool__factory; impl = await new feederPoolFactory(linkedAddress, this.sa.default.signer).deploy(mAssetDetails.nexus.address, mAssetDetails.mAsset.address, redemptionPriceSnapAddress); } else { feederPoolFactory = generated_1.FeederPool__factory; impl = await new feederPoolFactory(linkedAddress, this.sa.default.signer).deploy(mAssetDetails.nexus.address, mAssetDetails.mAsset.address); } const data = impl.interface.encodeFunctionData("initialize", [ "mStable mBTC/bBTC Feeder", "bBTC fPool", { addr: mAssetDetails.mAsset.address, integrator: useLendingMarkets ? mAssetDetails.integrationAddress : constants_1.ZERO_ADDRESS, hasTxFee: false, status: 0, }, { addr: fAsset.address, integrator: useLendingMarkets ? mAssetDetails.integrationAddress : constants_1.ZERO_ADDRESS, hasTxFee: false, status: 0, }, mAssetDetails.bAssets.map((b) => b.address), { a: math_1.BN.from(300), limits: { min: math_1.simpleToExactAmount(20, 16), max: math_1.simpleToExactAmount(80, 16), // 97% }, }, ]); // Deploy feeder pool proxy and call initialize on the feeder pool implementation const poolProxy = await new generated_1.AssetProxy__factory(this.sa.default.signer).deploy(impl.address, constants_1.DEAD_ADDRESS, data); // Link the feeder pool ABI to its proxy const pool = await new feederPoolFactory(linkedAddress, this.sa.default.signer).attach(poolProxy.address); // - Add feeder pool to the platform integration whitelist if (useLendingMarkets) { await platformIntegration.addWhitelist([pool.address]); } if ((feederWeights === null || feederWeights === void 0 ? void 0 : feederWeights.length) > 0) { const approvals = await Promise.all(bAssets.map((b, i) => this.mAssetMachine.approveMasset(b, pool, feederWeights[i], this.sa.default.signer))); await pool.mintMulti(bAssets.map((b) => b.address), approvals, 0, this.sa.default.address); } return { pool, logic: feederLogic, manager: feederManager, interestValidator, mAsset: mAssetDetails.mAsset, fAsset, bAssets, pTokens, mAssetDetails, redemptionPriceSnap, }; } async getBassets(feederDetails) { const [personal, data] = await feederDetails.pool.getBassets(); const bArrays = personal.map((b, i) => { const d = data[i]; return { addr: b.addr, status: b.status, isTransferFeeCharged: b.hasTxFee, ratio: math_1.BN.from(d.ratio), vaultBalance: math_1.BN.from(d.vaultBalance), integratorAddr: b.integrator, }; }); const bAssetContracts = await Promise.all(bArrays.map((b) => generated_1.MockERC20__factory.connect(b.addr, this.sa.default.signer))); const integrators = await Promise.all(bArrays.map((b) => b.integratorAddr === constants_1.ZERO_ADDRESS ? null : generated_1.MockPlatformIntegration__factory.connect(b.integratorAddr, this.sa.default.signer))); return bArrays.map((b, i) => ({ ...b, contract: bAssetContracts[i], integrator: integrators[i], })); } // Gets the fAsset, mAsset or mpAsset async getAsset(feederDetails, assetAddress) { let asset; let isMpAsset = false; // If a feeder asset or mStable asset if (assetAddress === feederDetails.fAsset.address || assetAddress === feederDetails.mAsset.address) { asset = await feederDetails.pool.getBasset(assetAddress); // If a main pool asset } else if (feederDetails.mAssetDetails.bAssets.map((b) => b.address).includes(assetAddress)) { asset = await feederDetails.mAsset.getBasset(assetAddress); isMpAsset = true; } else { throw new Error(`Asset with address ${assetAddress} is not a fAsset, mAsset or mpAsset`); } const assetContract = generated_1.MockERC20__factory.connect(asset.personal.addr, this.sa.default.signer); const integrator = asset.personal.integrator === constants_1.ZERO_ADDRESS ? null : (await new generated_1.MockPlatformIntegration__factory(this.sa.default.signer).attach(asset.personal.integrator)); return { addr: asset.personal.addr, status: asset.personal.status, isTransferFeeCharged: asset.personal.hasTxFee, ratio: isMpAsset ? math_1.BN.from(asset.bData.ratio) : math_1.BN.from(asset.vaultData.ratio), vaultBalance: isMpAsset ? math_1.BN.from(asset.bData.vaultBalance) : math_1.BN.from(asset.vaultData.vaultBalance), integratorAddr: asset.personal.integrator, contract: assetContract, pToken: integrator ? await integrator.callStatic["bAssetToPToken(address)"](asset.personal.addr) : null, integrator, isMpAsset, feederPoolOrMassetContract: isMpAsset ? feederDetails.mAsset : feederDetails.pool, }; } async getBasketComposition(feederDetails) { // raw bAsset data const bAssets = await this.getBassets(feederDetails); // total supply of mAsset const supply = await feederDetails.pool.totalSupply(); // get actual balance of each bAsset const rawBalances = await Promise.all(bAssets.map((b) => b.integrator ? b.contract.balanceOf(b.integrator.address) : b.contract.balanceOf(feederDetails.pool.address))); const platformBalances = await Promise.all(bAssets.map((b) => (b.integrator ? b.integrator.callStatic.checkBalance(b.addr) : math_1.BN.from(0)))); const balances = rawBalances.map((b, i) => b.add(platformBalances[i])); // get overweight const currentVaultUnits = bAssets.map((b) => math_1.BN.from(b.vaultBalance).mul(math_1.BN.from(b.ratio)).div(constants_1.ratioScale)); // get total amount const sumOfBassets = currentVaultUnits.reduce((p, c) => p.add(c), math_1.BN.from(0)); return { bAssets: bAssets.map((b, i) => ({ ...b, address: b.addr, mAssetUnits: currentVaultUnits[i], actualBalance: balances[i], rawBalance: rawBalances[i], platformBalance: platformBalances[i], })), totalSupply: supply, surplus: math_1.BN.from(0), sumOfBassets, failed: false, undergoingRecol: false, }; } async approveFeeder(asset, feeder, assetQuantity, sender = this.sa.default.signer, inputIsBaseUnits = false) { const assetDecimals = await asset.decimals(); const approvalAmount = inputIsBaseUnits ? math_1.BN.from(assetQuantity) : math_1.simpleToExactAmount(assetQuantity, assetDecimals); await asset.connect(sender).approve(feeder, approvalAmount); return approvalAmount; } static async getPlatformInteraction(pool, type, amount, bAsset) { const hasIntegrator = bAsset.integratorAddr === constants_1.ZERO_ADDRESS; const integratorBalBefore = await bAsset.contract.balanceOf(bAsset.integrator ? bAsset.integratorAddr : pool.address); if (hasIntegrator) { return { hasLendingMarket: false, expectInteraction: false, rawBalance: type === "deposit" ? integratorBalBefore.add(amount) : integratorBalBefore.sub(amount), }; } const hasTxFee = bAsset.isTransferFeeCharged; if (hasTxFee) { return { hasLendingMarket: true, expectInteraction: true, amount, rawBalance: math_1.BN.from(0), }; } const totalSupply = await pool.totalSupply(); const { cacheSize, pendingFees } = await pool.data(); const maxC = totalSupply.add(pendingFees).mul(constants_1.ratioScale).div(math_1.BN.from(bAsset.ratio)).mul(cacheSize).div(constants_1.fullScale); const newSum = math_1.BN.from(integratorBalBefore).add(amount); const expectInteraction = type === "deposit" ? newSum.gte(maxC) : amount.gt(math_1.BN.from(integratorBalBefore)); return { hasLendingMarket: true, expectInteraction, amount: type === "deposit" ? newSum.sub(maxC.div(2)) : math_1.minimum(maxC.div(2).add(amount).sub(math_1.BN.from(integratorBalBefore)), math_1.BN.from(bAsset.vaultBalance).sub(math_1.BN.from(integratorBalBefore))), rawBalance: type === "deposit" ? expectInteraction ? maxC.div(2) : newSum : expectInteraction ? math_1.minimum(maxC.div(2), math_1.BN.from(bAsset.vaultBalance).sub(amount)) : math_1.BN.from(integratorBalBefore).sub(amount), }; } } exports.FeederMachine = FeederMachine; //# sourceMappingURL=feederMachine.js.map