@sovryn-zero/lib-ethers
Version:
Sovryn Zero SDK Ethers-based implementation
557 lines • 36.2 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.PopulatableEthersLiquity = exports.PopulatedEthersRedemption = exports.PopulatedEthersLiquityTransaction = exports.SentEthersLiquityTransaction = exports._redeemMaxIterations = void 0;
const assert_1 = __importDefault(require("assert"));
const constants_1 = require("@ethersproject/constants");
const lib_base_1 = require("@sovryn-zero/lib-base");
const EthersLiquityConnection_1 = require("./EthersLiquityConnection");
const contracts_1 = require("./contracts");
const parseLogs_1 = require("./parseLogs");
const decimalify = (bigNumber) => lib_base_1.Decimal.fromBigNumberString(bigNumber.toHexString());
// With 70 iterations redemption costs about ~10M gas, and each iteration accounts for ~138k more
/** @internal */
exports._redeemMaxIterations = 70;
const defaultBorrowingRateSlippageTolerance = lib_base_1.Decimal.from(0.005); // 0.5%
const defaultRedemptionRateSlippageTolerance = lib_base_1.Decimal.from(0.001); // 0.1%
const noDetails = () => undefined;
const compose = (f, g) => (_) => f(g(_));
const id = (t) => t;
// Takes ~6-7K to update lastFeeOperationTime. Let's be on the safe side.
const addGasForPotentialLastFeeOperationTimeUpdate = (gas) => gas.add(10000);
// First traversal in ascending direction takes ~50K, then ~13.5K per extra step.
// 80K should be enough for 3 steps, plus some extra to be safe.
const addGasForPotentialListTraversal = (gas) => gas.add(80000);
const addGasForZEROIssuance = (gas) => gas.add(50000);
// To get the best entropy available, we'd do something like:
//
// const bigRandomNumber = () =>
// BigNumber.from(
// `0x${Array.from(crypto.getRandomValues(new Uint32Array(8)))
// .map(u32 => u32.toString(16).padStart(8, "0"))
// .join("")}`
// );
//
// However, Window.crypto is browser-specific. Since we only use this for randomly picking Troves
// during the search for hints, Math.random() will do fine, too.
//
// This returns a random integer between 0 and Number.MAX_SAFE_INTEGER
const randomInteger = () => Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
// Maximum number of trials to perform in a single getApproxHint() call. If the number of trials
// required to get a statistically "good" hint is larger than this, the search for the hint will
// be broken up into multiple getApproxHint() calls.
//
// This should be low enough to work with popular public Ethereum providers like Infura without
// triggering any fair use limits.
const maxNumberOfTrialsAtOnce = 2500;
function* generateTrials(totalNumberOfTrials) {
assert_1.default(Number.isInteger(totalNumberOfTrials) && totalNumberOfTrials > 0);
while (totalNumberOfTrials) {
const numberOfTrials = Math.min(totalNumberOfTrials, maxNumberOfTrialsAtOnce);
yield numberOfTrials;
totalNumberOfTrials -= numberOfTrials;
}
}
/**
* A transaction that has already been sent.
*
* @remarks
* Returned by {@link SendableEthersLiquity} functions.
*
* @public
*/
class SentEthersLiquityTransaction {
/** @internal */
constructor(rawSentTransaction, connection, parse) {
this.rawSentTransaction = rawSentTransaction;
this._connection = connection;
this._parse = parse;
}
_receiptFrom(rawReceipt) {
return rawReceipt
? rawReceipt.status
? lib_base_1._successfulReceipt(rawReceipt, this._parse(rawReceipt), () => parseLogs_1.logsToString(rawReceipt, EthersLiquityConnection_1._getContracts(this._connection)))
: lib_base_1._failedReceipt(rawReceipt)
: lib_base_1._pendingReceipt;
}
/** {@inheritDoc @sovryn-zero/lib-base#SentLiquityTransaction.getReceipt} */
async getReceipt() {
return this._receiptFrom(await EthersLiquityConnection_1._getProvider(this._connection).getTransactionReceipt(this.rawSentTransaction.hash));
}
/** {@inheritDoc @sovryn-zero/lib-base#SentLiquityTransaction.waitForReceipt} */
async waitForReceipt() {
const receipt = this._receiptFrom(await EthersLiquityConnection_1._getProvider(this._connection).waitForTransaction(this.rawSentTransaction.hash));
assert_1.default(receipt.status !== "pending");
return receipt;
}
}
exports.SentEthersLiquityTransaction = SentEthersLiquityTransaction;
/**
* A transaction that has been prepared for sending.
*
* @remarks
* Returned by {@link PopulatableEthersLiquity} functions.
*
* @public
*/
class PopulatedEthersLiquityTransaction {
/** @internal */
constructor(rawPopulatedTransaction, connection, parse) {
this.rawPopulatedTransaction = rawPopulatedTransaction;
this._connection = connection;
this._parse = parse;
}
/** {@inheritDoc @sovryn-zero/lib-base#PopulatedLiquityTransaction.send} */
async send() {
return new SentEthersLiquityTransaction(await EthersLiquityConnection_1._requireSigner(this._connection).sendTransaction(this.rawPopulatedTransaction), this._connection, this._parse);
}
}
exports.PopulatedEthersLiquityTransaction = PopulatedEthersLiquityTransaction;
/**
* {@inheritDoc @sovryn-zero/lib-base#PopulatedRedemption}
*
* @public
*/
class PopulatedEthersRedemption extends PopulatedEthersLiquityTransaction {
/** @internal */
constructor(rawPopulatedTransaction, connection, attemptedZUSDAmount, redeemableZUSDAmount, increaseAmountByMinimumNetDebt) {
const { troveManager } = EthersLiquityConnection_1._getContracts(connection);
super(rawPopulatedTransaction, connection, ({ logs }) => troveManager
.extractEvents(logs, "Redemption")
.map(({ args: { _ETHSent, _ETHFee, _actualZUSDAmount, _attemptedZUSDAmount } }) => ({
attemptedZUSDAmount: decimalify(_attemptedZUSDAmount),
actualZUSDAmount: decimalify(_actualZUSDAmount),
collateralTaken: decimalify(_ETHSent),
fee: decimalify(_ETHFee)
}))[0]);
this.attemptedZUSDAmount = attemptedZUSDAmount;
this.redeemableZUSDAmount = redeemableZUSDAmount;
this.isTruncated = redeemableZUSDAmount.lt(attemptedZUSDAmount);
this._increaseAmountByMinimumNetDebt = increaseAmountByMinimumNetDebt;
}
/** {@inheritDoc @sovryn-zero/lib-base#PopulatedRedemption.increaseAmountByMinimumNetDebt} */
increaseAmountByMinimumNetDebt(maxRedemptionRate) {
if (!this._increaseAmountByMinimumNetDebt) {
throw new Error("PopulatedEthersRedemption: increaseAmountByMinimumNetDebt() can " +
"only be called when amount is truncated");
}
return this._increaseAmountByMinimumNetDebt(maxRedemptionRate);
}
}
exports.PopulatedEthersRedemption = PopulatedEthersRedemption;
/**
* Ethers-based implementation of {@link @sovryn-zero/lib-base#PopulatableLiquity}.
*
* @public
*/
class PopulatableEthersLiquity {
constructor(readable) {
this._readable = readable;
}
_wrapSimpleTransaction(rawPopulatedTransaction) {
return new PopulatedEthersLiquityTransaction(rawPopulatedTransaction, this._readable.connection, noDetails);
}
_wrapTroveChangeWithFees(params, rawPopulatedTransaction) {
const { borrowerOperations } = EthersLiquityConnection_1._getContracts(this._readable.connection);
return new PopulatedEthersLiquityTransaction(rawPopulatedTransaction, this._readable.connection, ({ logs }) => {
const [newTrove] = borrowerOperations
.extractEvents(logs, "TroveUpdated")
.map(({ args: { _coll, _debt } }) => new lib_base_1.Trove(decimalify(_coll), decimalify(_debt)));
const [fee] = borrowerOperations
.extractEvents(logs, "ZUSDBorrowingFeePaid")
.map(({ args: { _ZUSDFee } }) => decimalify(_ZUSDFee));
return {
params,
newTrove,
fee
};
});
}
async _wrapTroveClosure(rawPopulatedTransaction) {
const { activePool, zusdToken } = EthersLiquityConnection_1._getContracts(this._readable.connection);
return new PopulatedEthersLiquityTransaction(rawPopulatedTransaction, this._readable.connection, ({ logs, from: userAddress }) => {
const [repayZUSD] = zusdToken
.extractEvents(logs, "Transfer")
.filter(({ args: { from, to } }) => from === userAddress && to === constants_1.AddressZero)
.map(({ args: { value } }) => decimalify(value));
const [withdrawCollateral] = activePool
.extractEvents(logs, "EtherSent")
.filter(({ args: { _to } }) => _to === userAddress)
.map(({ args: { _amount } }) => decimalify(_amount));
return {
params: repayZUSD.nonZero ? { withdrawCollateral, repayZUSD } : { withdrawCollateral }
};
});
}
_wrapLiquidation(rawPopulatedTransaction) {
const { troveManager } = EthersLiquityConnection_1._getContracts(this._readable.connection);
return new PopulatedEthersLiquityTransaction(rawPopulatedTransaction, this._readable.connection, ({ logs }) => {
const liquidatedAddresses = troveManager
.extractEvents(logs, "TroveLiquidated")
.map(({ args: { _borrower } }) => _borrower);
const [totals] = troveManager
.extractEvents(logs, "Liquidation")
.map(({ args: { _ZUSDGasCompensation, _collGasCompensation, _liquidatedColl, _liquidatedDebt } }) => ({
collateralGasCompensation: decimalify(_collGasCompensation),
zusdGasCompensation: decimalify(_ZUSDGasCompensation),
totalLiquidated: new lib_base_1.Trove(decimalify(_liquidatedColl), decimalify(_liquidatedDebt))
}));
return {
liquidatedAddresses,
...totals
};
});
}
_extractStabilityPoolGainsWithdrawalDetails(logs) {
const { stabilityPool } = EthersLiquityConnection_1._getContracts(this._readable.connection);
const [newZUSDDeposit] = stabilityPool
.extractEvents(logs, "UserDepositChanged")
.map(({ args: { _newDeposit } }) => decimalify(_newDeposit));
const [[collateralGain, zusdLoss]] = stabilityPool
.extractEvents(logs, "ETHGainWithdrawn")
.map(({ args: { _ETH, _ZUSDLoss } }) => [decimalify(_ETH), decimalify(_ZUSDLoss)]);
const [zeroReward] = stabilityPool
.extractEvents(logs, "SOVPaidToDepositor")
.map(({ args: { _SOV } }) => decimalify(_SOV));
return {
zusdLoss,
newZUSDDeposit,
collateralGain,
zeroReward
};
}
_wrapStabilityPoolGainsWithdrawal(rawPopulatedTransaction) {
return new PopulatedEthersLiquityTransaction(rawPopulatedTransaction, this._readable.connection, ({ logs }) => this._extractStabilityPoolGainsWithdrawalDetails(logs));
}
_wrapStabilityDepositTopup(change, rawPopulatedTransaction) {
return new PopulatedEthersLiquityTransaction(rawPopulatedTransaction, this._readable.connection, ({ logs }) => ({
...this._extractStabilityPoolGainsWithdrawalDetails(logs),
change
}));
}
async _wrapStabilityDepositWithdrawal(rawPopulatedTransaction) {
const { stabilityPool, zusdToken } = EthersLiquityConnection_1._getContracts(this._readable.connection);
return new PopulatedEthersLiquityTransaction(rawPopulatedTransaction, this._readable.connection, ({ logs, from: userAddress }) => {
const gainsWithdrawalDetails = this._extractStabilityPoolGainsWithdrawalDetails(logs);
const [withdrawZUSD] = zusdToken
.extractEvents(logs, "Transfer")
.filter(({ args: { from, to } }) => from === stabilityPool.address && to === userAddress)
.map(({ args: { value } }) => decimalify(value));
return {
...gainsWithdrawalDetails,
change: { withdrawZUSD, withdrawAllZUSD: gainsWithdrawalDetails.newZUSDDeposit.isZero }
};
});
}
_wrapCollateralGainTransfer(rawPopulatedTransaction) {
const { borrowerOperations } = EthersLiquityConnection_1._getContracts(this._readable.connection);
return new PopulatedEthersLiquityTransaction(rawPopulatedTransaction, this._readable.connection, ({ logs }) => {
const [newTrove] = borrowerOperations
.extractEvents(logs, "TroveUpdated")
.map(({ args: { _coll, _debt } }) => new lib_base_1.Trove(decimalify(_coll), decimalify(_debt)));
return {
...this._extractStabilityPoolGainsWithdrawalDetails(logs),
newTrove
};
});
}
async _findHintsForNominalCollateralRatio(nominalCollateralRatio) {
const { sortedTroves, hintHelpers } = EthersLiquityConnection_1._getContracts(this._readable.connection);
const numberOfTroves = await this._readable.getNumberOfTroves();
if (!numberOfTroves) {
return [constants_1.AddressZero, constants_1.AddressZero];
}
if (nominalCollateralRatio.infinite) {
return [constants_1.AddressZero, await sortedTroves.getFirst()];
}
const totalNumberOfTrials = Math.ceil(10 * Math.sqrt(numberOfTroves));
const [firstTrials, ...restOfTrials] = generateTrials(totalNumberOfTrials);
const collectApproxHint = ({ latestRandomSeed, results }, numberOfTrials) => hintHelpers
.getApproxHint(nominalCollateralRatio.hex, numberOfTrials, latestRandomSeed)
.then(({ latestRandomSeed, ...result }) => ({
latestRandomSeed,
results: [...results, result]
}));
const { results } = await restOfTrials.reduce((p, numberOfTrials) => p.then(state => collectApproxHint(state, numberOfTrials)), collectApproxHint({ latestRandomSeed: randomInteger(), results: [] }, firstTrials));
const { hintAddress } = results.reduce((a, b) => (a.diff.lt(b.diff) ? a : b));
return sortedTroves.findInsertPosition(nominalCollateralRatio.hex, hintAddress, hintAddress);
}
async _findHints(trove) {
if (trove instanceof lib_base_1.TroveWithPendingRedistribution) {
throw new Error("Rewards must be applied to this Trove");
}
return this._findHintsForNominalCollateralRatio(trove._nominalCollateralRatio);
}
async _findRedemptionHints(amount) {
const { hintHelpers } = EthersLiquityConnection_1._getContracts(this._readable.connection);
const price = await this._readable.getPrice();
const { firstRedemptionHint, partialRedemptionHintNICR, truncatedZUSDamount } = await hintHelpers.getRedemptionHints(amount.hex, price.hex, exports._redeemMaxIterations);
const [partialRedemptionUpperHint, partialRedemptionLowerHint] = partialRedemptionHintNICR.isZero()
? [constants_1.AddressZero, constants_1.AddressZero]
: await this._findHintsForNominalCollateralRatio(decimalify(partialRedemptionHintNICR));
return [
decimalify(truncatedZUSDamount),
firstRedemptionHint,
partialRedemptionUpperHint,
partialRedemptionLowerHint,
partialRedemptionHintNICR
];
}
async findHints(trove) {
return this._findHints(trove);
}
/** {@inheritDoc @sovryn-zero/lib-base#PopulatableLiquity.openTrove} */
async openTrove(params, maxBorrowingRate, overrides) {
const { borrowerOperations } = EthersLiquityConnection_1._getContracts(this._readable.connection);
const normalized = lib_base_1._normalizeTroveCreation(params);
const { depositCollateral, borrowZUSD } = normalized;
const fees = await this._readable.getFees();
const borrowingRate = fees.borrowingRate();
const newTrove = lib_base_1.Trove.create(normalized, borrowingRate);
maxBorrowingRate =
maxBorrowingRate !== undefined
? lib_base_1.Decimal.from(maxBorrowingRate)
: borrowingRate.add(defaultBorrowingRateSlippageTolerance);
return this._wrapTroveChangeWithFees(normalized, await borrowerOperations.estimateAndPopulate.openTrove({ value: depositCollateral.hex, ...overrides }, compose(addGasForPotentialLastFeeOperationTimeUpdate, addGasForPotentialListTraversal), maxBorrowingRate.hex, borrowZUSD.hex, ...(await this._findHints(newTrove))));
}
/** {@inheritDoc @sovryn-zero/lib-base#PopulatableLiquity.openNueTrove} */
async openNueTrove(params, maxBorrowingRate, overrides) {
const { borrowerOperations } = EthersLiquityConnection_1._getContracts(this._readable.connection);
const normalized = lib_base_1._normalizeTroveCreation(params);
const { depositCollateral, borrowZUSD } = normalized;
const fees = await this._readable.getFees();
const borrowingRate = fees.borrowingRate();
const newTrove = lib_base_1.Trove.create(normalized, borrowingRate);
maxBorrowingRate =
maxBorrowingRate !== undefined
? lib_base_1.Decimal.from(maxBorrowingRate)
: borrowingRate.add(defaultBorrowingRateSlippageTolerance);
return this._wrapTroveChangeWithFees(normalized, await borrowerOperations.estimateAndPopulate.openNueTrove({ value: depositCollateral.hex, ...overrides }, compose(addGasForPotentialLastFeeOperationTimeUpdate, addGasForPotentialListTraversal), maxBorrowingRate.hex, borrowZUSD.hex, ...(await this._findHints(newTrove))));
}
/** {@inheritDoc @sovryn-zero/lib-base#PopulatableLiquity.closeTrove} */
async closeTrove(overrides) {
const { borrowerOperations } = EthersLiquityConnection_1._getContracts(this._readable.connection);
return this._wrapTroveClosure(await borrowerOperations.estimateAndPopulate.closeTrove({ ...overrides }, id));
}
/** {@inheritDoc @sovryn-zero/lib-base#PopulatableLiquity.closeNueTrove} */
async closeNueTrove(_permitParams, overrides) {
const { borrowerOperations } = EthersLiquityConnection_1._getContracts(this._readable.connection);
return this._wrapTroveClosure(await borrowerOperations.estimateAndPopulate.closeNueTrove({ ...overrides }, gas => gas.mul(125).div(100), _permitParams));
}
/** {@inheritDoc @sovryn-zero/lib-base#PopulatableLiquity.depositCollateral} */
depositCollateral(amount, overrides) {
return this.adjustTrove({ depositCollateral: amount }, undefined, overrides);
}
/** {@inheritDoc @sovryn-zero/lib-base#PopulatableLiquity.withdrawCollateral} */
withdrawCollateral(amount, overrides) {
return this.adjustTrove({ withdrawCollateral: amount }, undefined, overrides);
}
/** {@inheritDoc @sovryn-zero/lib-base#PopulatableLiquity.borrowZUSD} */
borrowZUSD(amount, maxBorrowingRate, overrides) {
return this.adjustTrove({ borrowZUSD: amount }, maxBorrowingRate, overrides);
}
/** {@inheritDoc @sovryn-zero/lib-base#PopulatableLiquity.repayZUSD} */
repayZUSD(amount, overrides) {
return this.adjustTrove({ repayZUSD: amount }, undefined, overrides);
}
/** {@inheritDoc @sovryn-zero/lib-base#PopulatableLiquity.adjustTrove} */
async adjustTrove(params, maxBorrowingRate, overrides) {
var _a, _b;
const address = EthersLiquityConnection_1._requireAddress(this._readable.connection, overrides);
const { borrowerOperations } = EthersLiquityConnection_1._getContracts(this._readable.connection);
const normalized = lib_base_1._normalizeTroveAdjustment(params);
const { depositCollateral, withdrawCollateral, borrowZUSD, repayZUSD } = normalized;
const [trove, fees] = await Promise.all([
this._readable.getTrove(address),
borrowZUSD && this._readable.getFees()
]);
const borrowingRate = fees === null || fees === void 0 ? void 0 : fees.borrowingRate();
const finalTrove = trove.adjust(normalized, borrowingRate);
maxBorrowingRate =
maxBorrowingRate !== undefined
? lib_base_1.Decimal.from(maxBorrowingRate)
: (_a = borrowingRate === null || borrowingRate === void 0 ? void 0 : borrowingRate.add(defaultBorrowingRateSlippageTolerance)) !== null && _a !== void 0 ? _a : lib_base_1.Decimal.ZERO;
return this._wrapTroveChangeWithFees(normalized, await borrowerOperations.estimateAndPopulate.adjustTrove({ value: depositCollateral === null || depositCollateral === void 0 ? void 0 : depositCollateral.hex, ...overrides }, compose(borrowZUSD ? addGasForPotentialLastFeeOperationTimeUpdate : id, addGasForPotentialListTraversal), maxBorrowingRate.hex, (withdrawCollateral !== null && withdrawCollateral !== void 0 ? withdrawCollateral : lib_base_1.Decimal.ZERO).hex, ((_b = borrowZUSD !== null && borrowZUSD !== void 0 ? borrowZUSD : repayZUSD) !== null && _b !== void 0 ? _b : lib_base_1.Decimal.ZERO).hex, !!borrowZUSD, ...(await this._findHints(finalTrove))));
}
/** {@inheritDoc @sovryn-zero/lib-base#PopulatableLiquity.adjustNueTrove} */
async adjustNueTrove(params, permitParams, maxBorrowingRate, overrides) {
var _a, _b;
const address = EthersLiquityConnection_1._requireAddress(this._readable.connection, overrides);
const { borrowerOperations } = EthersLiquityConnection_1._getContracts(this._readable.connection);
const normalized = lib_base_1._normalizeTroveAdjustment(params);
const { depositCollateral, withdrawCollateral, borrowZUSD, repayZUSD } = normalized;
const [trove, fees] = await Promise.all([
this._readable.getTrove(address),
borrowZUSD && this._readable.getFees()
]);
const borrowingRate = fees === null || fees === void 0 ? void 0 : fees.borrowingRate();
const finalTrove = trove.adjust(normalized, borrowingRate);
maxBorrowingRate =
maxBorrowingRate !== undefined
? lib_base_1.Decimal.from(maxBorrowingRate)
: (_a = borrowingRate === null || borrowingRate === void 0 ? void 0 : borrowingRate.add(defaultBorrowingRateSlippageTolerance)) !== null && _a !== void 0 ? _a : lib_base_1.Decimal.ZERO;
return this._wrapTroveChangeWithFees(normalized, await borrowerOperations.estimateAndPopulate.adjustNueTrove({ value: depositCollateral === null || depositCollateral === void 0 ? void 0 : depositCollateral.hex, ...overrides }, compose(borrowZUSD ? addGasForPotentialLastFeeOperationTimeUpdate : id, addGasForPotentialListTraversal), maxBorrowingRate.hex, (withdrawCollateral !== null && withdrawCollateral !== void 0 ? withdrawCollateral : lib_base_1.Decimal.ZERO).hex, ((_b = borrowZUSD !== null && borrowZUSD !== void 0 ? borrowZUSD : repayZUSD) !== null && _b !== void 0 ? _b : lib_base_1.Decimal.ZERO).hex, !!borrowZUSD, ...(await this._findHints(finalTrove)), permitParams));
}
async withdrawZusdAndConvertToDLLR(zusdAmount, maxBorrowingRate, overrides) {
var _a;
const address = EthersLiquityConnection_1._requireAddress(this._readable.connection, overrides);
const { borrowerOperations } = EthersLiquityConnection_1._getContracts(this._readable.connection);
const normalized = lib_base_1._normalizeTroveAdjustment({
repayZUSD: zusdAmount,
});
const { borrowZUSD, repayZUSD } = normalized;
const [trove, fees] = await Promise.all([
this._readable.getTrove(address),
borrowZUSD && this._readable.getFees()
]);
const borrowingRate = fees === null || fees === void 0 ? void 0 : fees.borrowingRate();
const finalTrove = trove.adjust(normalized, borrowingRate);
maxBorrowingRate =
maxBorrowingRate !== undefined
? lib_base_1.Decimal.from(maxBorrowingRate)
: (_a = borrowingRate === null || borrowingRate === void 0 ? void 0 : borrowingRate.add(defaultBorrowingRateSlippageTolerance)) !== null && _a !== void 0 ? _a : lib_base_1.Decimal.ZERO;
return this._wrapSimpleTransaction(await borrowerOperations.estimateAndPopulate.withdrawZusdAndConvertToDLLR({ ...overrides }, id, maxBorrowingRate.hex, (repayZUSD !== null && repayZUSD !== void 0 ? repayZUSD : lib_base_1.Decimal.ZERO).hex, ...(await this._findHints(finalTrove))));
}
async provideToSpFromDLLR(dllrAmount, permitParams, overrides) {
const { stabilityPool } = EthersLiquityConnection_1._getContracts(this._readable.connection);
return this._wrapSimpleTransaction(await stabilityPool.estimateAndPopulate.provideToSpFromDLLR({ ...overrides }, id, lib_base_1.Decimal.from(dllrAmount).hex, permitParams));
}
async withdrawFromSpAndConvertToDLLR(zusdAmountRequested, overrides) {
const { stabilityPool } = EthersLiquityConnection_1._getContracts(this._readable.connection);
return this._wrapSimpleTransaction(await stabilityPool.estimateAndPopulate.withdrawFromSpAndConvertToDLLR({ ...overrides }, id, lib_base_1.Decimal.from(zusdAmountRequested).hex));
}
/** {@inheritDoc @sovryn-zero/lib-base#PopulatableLiquity.claimCollateralSurplus} */
async claimCollateralSurplus(overrides) {
const { borrowerOperations } = EthersLiquityConnection_1._getContracts(this._readable.connection);
return this._wrapSimpleTransaction(await borrowerOperations.estimateAndPopulate.claimCollateral({ ...overrides }, id));
}
/** @internal */
async setPrice(price, overrides) {
const { priceFeed } = EthersLiquityConnection_1._getContracts(this._readable.connection);
if (!contracts_1._priceFeedIsTestnet(priceFeed)) {
throw new Error("setPrice() unavailable on this deployment of Zero");
}
return this._wrapSimpleTransaction(await priceFeed.estimateAndPopulate.setPrice({ ...overrides }, id, lib_base_1.Decimal.from(price).hex));
}
/** {@inheritDoc @sovryn-zero/lib-base#PopulatableLiquity.liquidate} */
async liquidate(address, overrides) {
const { troveManager } = EthersLiquityConnection_1._getContracts(this._readable.connection);
if (Array.isArray(address)) {
return this._wrapLiquidation(await troveManager.estimateAndPopulate.batchLiquidateTroves({ ...overrides }, addGasForZEROIssuance, address));
}
else {
return this._wrapLiquidation(await troveManager.estimateAndPopulate.liquidate({ ...overrides }, addGasForZEROIssuance, address));
}
}
/** {@inheritDoc @sovryn-zero/lib-base#PopulatableLiquity.liquidateUpTo} */
async liquidateUpTo(maximumNumberOfTrovesToLiquidate, overrides) {
const { troveManager } = EthersLiquityConnection_1._getContracts(this._readable.connection);
return this._wrapLiquidation(await troveManager.estimateAndPopulate.liquidateTroves({ ...overrides }, addGasForZEROIssuance, maximumNumberOfTrovesToLiquidate));
}
/** {@inheritDoc @sovryn-zero/lib-base#PopulatableLiquity.depositZUSDInStabilityPool} */
async depositZUSDInStabilityPool(amount, frontendTag, overrides) {
var _a;
const { stabilityPool } = EthersLiquityConnection_1._getContracts(this._readable.connection);
const depositZUSD = lib_base_1.Decimal.from(amount);
return this._wrapStabilityDepositTopup({ depositZUSD }, await stabilityPool.estimateAndPopulate.provideToSP({ ...overrides }, addGasForZEROIssuance, depositZUSD.hex, (_a = frontendTag !== null && frontendTag !== void 0 ? frontendTag : this._readable.connection.frontendTag) !== null && _a !== void 0 ? _a : constants_1.AddressZero));
}
/** {@inheritDoc @sovryn-zero/lib-base#PopulatableLiquity.withdrawZUSDFromStabilityPool} */
async withdrawZUSDFromStabilityPool(amount, overrides) {
const { stabilityPool } = EthersLiquityConnection_1._getContracts(this._readable.connection);
return this._wrapStabilityDepositWithdrawal(await stabilityPool.estimateAndPopulate.withdrawFromSP({ ...overrides }, addGasForZEROIssuance, lib_base_1.Decimal.from(amount).hex));
}
/** {@inheritDoc @sovryn-zero/lib-base#PopulatableLiquity.withdrawGainsFromStabilityPool} */
async withdrawGainsFromStabilityPool(overrides) {
const { stabilityPool } = EthersLiquityConnection_1._getContracts(this._readable.connection);
return this._wrapStabilityPoolGainsWithdrawal(await stabilityPool.estimateAndPopulate.withdrawFromSP({ ...overrides }, addGasForZEROIssuance, lib_base_1.Decimal.ZERO.hex));
}
/** {@inheritDoc @sovryn-zero/lib-base#PopulatableLiquity.transferCollateralGainToTrove} */
async transferCollateralGainToTrove(overrides) {
const address = EthersLiquityConnection_1._requireAddress(this._readable.connection, overrides);
const { stabilityPool } = EthersLiquityConnection_1._getContracts(this._readable.connection);
const [initialTrove, stabilityDeposit] = await Promise.all([
this._readable.getTrove(address),
this._readable.getStabilityDeposit(address)
]);
const finalTrove = initialTrove.addCollateral(stabilityDeposit.collateralGain);
return this._wrapCollateralGainTransfer(await stabilityPool.estimateAndPopulate.withdrawETHGainToTrove({ ...overrides }, compose(addGasForPotentialListTraversal, addGasForZEROIssuance), ...(await this._findHints(finalTrove))));
}
/** {@inheritDoc @sovryn-zero/lib-base#PopulatableLiquity.sendZUSD} */
async sendZUSD(toAddress, amount, overrides) {
const { zusdToken } = EthersLiquityConnection_1._getContracts(this._readable.connection);
return this._wrapSimpleTransaction(await zusdToken.estimateAndPopulate.transfer({ ...overrides }, id, toAddress, lib_base_1.Decimal.from(amount).hex));
}
/** {@inheritDoc @sovryn-zero/lib-base#PopulatableLiquity.sendZERO} */
async sendZERO(toAddress, amount, overrides) {
const { zeroToken } = EthersLiquityConnection_1._getContracts(this._readable.connection);
return this._wrapSimpleTransaction(await zeroToken.estimateAndPopulate.transfer({ ...overrides }, id, toAddress, lib_base_1.Decimal.from(amount).hex));
}
/** {@inheritDoc @sovryn-zero/lib-base#PopulatableLiquity.redeemZUSD} */
async redeemZUSD(amount, maxRedemptionRate, overrides) {
const { troveManager } = EthersLiquityConnection_1._getContracts(this._readable.connection);
const attemptedZUSDAmount = lib_base_1.Decimal.from(amount);
const [fees, total, [truncatedAmount, firstRedemptionHint, ...partialHints]] = await Promise.all([
this._readable.getFees(),
this._readable.getTotal(),
this._findRedemptionHints(attemptedZUSDAmount)
]);
if (truncatedAmount.isZero) {
throw new Error(`redeemZUSD: amount too low to redeem (try at least ${lib_base_1.ZUSD_MINIMUM_NET_DEBT})`);
}
const defaultMaxRedemptionRate = (amount) => lib_base_1.Decimal.min(fees.redemptionRate(amount.div(total.debt)).add(defaultRedemptionRateSlippageTolerance), lib_base_1.Decimal.ONE);
const populateRedemption = async (attemptedZUSDAmount, maxRedemptionRate, truncatedAmount = attemptedZUSDAmount, partialHints = [constants_1.AddressZero, constants_1.AddressZero, 0]) => {
const maxRedemptionRateOrDefault = maxRedemptionRate !== undefined
? lib_base_1.Decimal.from(maxRedemptionRate)
: defaultMaxRedemptionRate(truncatedAmount);
return new PopulatedEthersRedemption(await troveManager.estimateAndPopulate.redeemCollateral({ ...overrides }, addGasForPotentialLastFeeOperationTimeUpdate, truncatedAmount.hex, firstRedemptionHint, ...partialHints, exports._redeemMaxIterations, maxRedemptionRateOrDefault.hex), this._readable.connection, attemptedZUSDAmount, truncatedAmount, truncatedAmount.lt(attemptedZUSDAmount)
? newMaxRedemptionRate => populateRedemption(truncatedAmount.add(lib_base_1.ZUSD_MINIMUM_NET_DEBT), newMaxRedemptionRate !== null && newMaxRedemptionRate !== void 0 ? newMaxRedemptionRate : maxRedemptionRate)
: undefined);
};
return populateRedemption(attemptedZUSDAmount, maxRedemptionRate, truncatedAmount, partialHints);
}
async redeemCollateralViaDLLR(amount, permitParams, maxRedemptionRate, overrides) {
const { troveManager } = EthersLiquityConnection_1._getContracts(this._readable.connection);
const attemptedZUSDAmount = lib_base_1.Decimal.from(amount);
const [fees, total, [truncatedAmount, firstRedemptionHint, ...partialHints]] = await Promise.all([
this._readable.getFees(),
this._readable.getTotal(),
this._findRedemptionHints(attemptedZUSDAmount)
]);
if (truncatedAmount.isZero) {
throw new Error(`redeemZUSD: amount too low to redeem (try at least ${lib_base_1.ZUSD_MINIMUM_NET_DEBT})`);
}
const defaultMaxRedemptionRate = (amount) => lib_base_1.Decimal.min(fees.redemptionRate(amount.div(total.debt)).add(defaultRedemptionRateSlippageTolerance), lib_base_1.Decimal.ONE);
const populateRedemption = async (attemptedZUSDAmount, maxRedemptionRate, truncatedAmount = attemptedZUSDAmount, partialHints = [constants_1.AddressZero, constants_1.AddressZero, 0]) => {
const maxRedemptionRateOrDefault = maxRedemptionRate !== undefined
? lib_base_1.Decimal.from(maxRedemptionRate)
: defaultMaxRedemptionRate(truncatedAmount);
return new PopulatedEthersRedemption(await troveManager.estimateAndPopulate.redeemCollateralViaDLLR({ ...overrides }, addGasForPotentialLastFeeOperationTimeUpdate, truncatedAmount.hex, firstRedemptionHint, ...partialHints, exports._redeemMaxIterations, maxRedemptionRateOrDefault.hex, permitParams), this._readable.connection, attemptedZUSDAmount, truncatedAmount, truncatedAmount.lt(attemptedZUSDAmount)
? newMaxRedemptionRate => populateRedemption(truncatedAmount.add(lib_base_1.ZUSD_MINIMUM_NET_DEBT), newMaxRedemptionRate !== null && newMaxRedemptionRate !== void 0 ? newMaxRedemptionRate : maxRedemptionRate)
: undefined);
};
return populateRedemption(attemptedZUSDAmount, maxRedemptionRate, truncatedAmount, partialHints);
}
/** {@inheritDoc @sovryn-zero/lib-base#PopulatableLiquity.stakeZERO} */
async stakeZERO(amount, overrides) {
const { zeroStaking } = EthersLiquityConnection_1._getContracts(this._readable.connection);
return this._wrapSimpleTransaction(await zeroStaking.estimateAndPopulate.stake({ ...overrides }, id, lib_base_1.Decimal.from(amount).hex));
}
/** {@inheritDoc @sovryn-zero/lib-base#PopulatableLiquity.unstakeZERO} */
async unstakeZERO(amount, overrides) {
const { zeroStaking } = EthersLiquityConnection_1._getContracts(this._readable.connection);
return this._wrapSimpleTransaction(await zeroStaking.estimateAndPopulate.unstake({ ...overrides }, id, lib_base_1.Decimal.from(amount).hex));
}
/** {@inheritDoc @sovryn-zero/lib-base#PopulatableLiquity.withdrawGainsFromStaking} */
withdrawGainsFromStaking(overrides) {
return this.unstakeZERO(lib_base_1.Decimal.ZERO, overrides);
}
/** {@inheritDoc @sovryn-zero/lib-base#PopulatableLiquity.registerFrontend} */
async registerFrontend(kickbackRate, overrides) {
const { stabilityPool } = EthersLiquityConnection_1._getContracts(this._readable.connection);
return this._wrapSimpleTransaction(await stabilityPool.estimateAndPopulate.registerFrontEnd({ ...overrides }, id, lib_base_1.Decimal.from(kickbackRate).hex));
}
}
exports.PopulatableEthersLiquity = PopulatableEthersLiquity;
//# sourceMappingURL=PopulatableEthersLiquity.js.map