@ajna-finance/sdk
Version:
A typescript SDK that can be used to create Dapps in Ajna ecosystem.
195 lines • 9.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.FungiblePool = void 0;
const tslib_1 = require("tslib");
const ethers_1 = require("ethers");
const constants_1 = require("../constants");
const erc20_1 = require("../contracts/erc20");
const erc20_pool_1 = require("../contracts/erc20-pool");
const pool_1 = require("../contracts/pool");
const types_1 = require("../types");
const numeric_1 = require("../utils/numeric");
const pricing_1 = require("../utils/pricing");
const time_1 = require("../utils/time");
const FungibleBucket_1 = require("./FungibleBucket");
const Pool_1 = require("./Pool");
/**
* Models a pool with ERC-20 collateral.
*/
class FungiblePool extends Pool_1.Pool {
constructor(provider, poolAddress, ajnaAddress) {
super(provider, poolAddress, ajnaAddress, (0, erc20_pool_1.getErc20PoolContract)(poolAddress, provider), (0, erc20_pool_1.getErc20PoolContractMulti)(poolAddress));
}
initialize() {
const _super = Object.create(null, {
initialize: { get: () => super.initialize }
});
return tslib_1.__awaiter(this, void 0, void 0, function* () {
yield _super.initialize.call(this);
try {
const collateralToken = (0, erc20_1.getErc20Contract)(this.collateralAddress, this.provider);
this.collateralSymbol = (yield collateralToken.symbol()).replace(/"+/g, '');
}
catch (e) {
const collateralToken = (0, erc20_1.getDSTokenContract)(this.collateralAddress, this.provider);
this.collateralSymbol = ethers_1.utils
.parseBytes32String(yield collateralToken.symbol())
.replace(/"+/g, '');
}
this.name = this.collateralSymbol + '-' + this.quoteSymbol;
});
}
toString() {
return this.name + ' pool';
}
/**
* Approve this pool to manage collateral token.
* @param signer pool user
* @param allowance normalized approval amount (or MaxUint256)
* @returns promise to transaction
*/
collateralApprove(signer, allowance) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const denormalizedAllowance = allowance.eq(ethers_1.constants.MaxUint256)
? allowance
: allowance.div(yield (0, erc20_pool_1.collateralScale)(this.contract));
return (0, erc20_pool_1.approve)(signer, this.poolAddress, this.collateralAddress, denormalizedAllowance);
});
}
/**
* Pledges collateral and draws debt.
* @param signer borrower
* @param amountToBorrow new debt to draw
* @param collateralToPledge new collateral to deposit
* @param limitIndex revert if loan would drop LUP below this bucket (or pass MAX_FENWICK_INDEX)
* @returns promise to transaction
*/
drawDebt(signer, amountToBorrow, collateralToPledge, limitIndex) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const contractPoolWithSigner = this.contract.connect(signer);
const borrowerAddress = yield signer.getAddress();
return (0, erc20_pool_1.drawDebt)(contractPoolWithSigner, borrowerAddress, amountToBorrow, limitIndex !== null && limitIndex !== void 0 ? limitIndex : constants_1.MAX_FENWICK_INDEX, collateralToPledge);
});
}
/**
* Repays debt and pulls collateral.
* @param signer borrower
* @param maxQuoteTokenAmountToRepay amount for partial repayment, MaxUint256 for full repayment, 0 for no repayment
* @param collateralAmountToPull amount of collateral to withdraw after repayment
* @param limitIndex revert if LUP has moved below this bucket by the time the transaction is processed
* @returns promise to transaction
*/
repayDebt(signer, maxQuoteTokenAmountToRepay, collateralAmountToPull, limitIndex) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const contractPoolWithSigner = this.contract.connect(signer);
const sender = yield signer.getAddress();
return (0, erc20_pool_1.repayDebt)(contractPoolWithSigner, sender, maxQuoteTokenAmountToRepay, collateralAmountToPull, sender, limitIndex !== null && limitIndex !== void 0 ? limitIndex : constants_1.MAX_FENWICK_INDEX);
});
}
/**
* Deposit collateral token into a bucket (not for borrowers).
* @param signer address to be awarded LP
* @param collateralAmountToAdd deposit amount
* @param bucketIndex identifies the price bucket
* @param ttlSeconds revert if not processed in this amount of time
* @returns promise to transaction
*/
addCollateral(signer, bucketIndex, collateralAmountToAdd, ttlSeconds) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const contractPoolWithSigner = this.contract.connect(signer);
return (0, erc20_pool_1.addCollateral)(contractPoolWithSigner, collateralAmountToAdd, bucketIndex, yield (0, time_1.getExpiry)(this.provider, ttlSeconds));
});
}
/**
* Withdraw collateral from a bucket (not for borrowers).
* @param signer address to redeem LP
* @param bucketIndex identifies the price bucket
* @param maxAmount optionally limits amount to remove
* @returns promise to transaction
*/
removeCollateral(signer_1, bucketIndex_1) {
return tslib_1.__awaiter(this, arguments, void 0, function* (signer, bucketIndex, maxAmount = ethers_1.constants.MaxUint256) {
const contractPoolWithSigner = this.contract.connect(signer);
return (0, erc20_pool_1.removeCollateral)(contractPoolWithSigner, bucketIndex, maxAmount);
});
}
/**
* @param bucketIndex fenwick index of the desired bucket
* @returns {@link FungibleBucket} modeling bucket at specified index
*/
getBucketByIndex(bucketIndex) {
const bucket = new FungibleBucket_1.FungibleBucket(this.provider, this, bucketIndex);
return bucket;
}
/**
* @param price price within range supported by Ajna
* @returns {@link FungibleBucket} modeling bucket at nearest to specified price
*/
getBucketByPrice(price) {
const bucketIndex = (0, pricing_1.priceToIndex)(price);
// priceToIndex should throw upon invalid price
const bucket = new FungibleBucket_1.FungibleBucket(this.provider, this, bucketIndex);
return bucket;
}
/**
* @param minPrice lowest desired price
* @param maxPrice highest desired price
* @returns array of {@link FungibleBucket}s between specified prices
*/
getBucketsByPriceRange(minPrice, maxPrice) {
if (minPrice.gt(maxPrice))
throw new types_1.SdkError('maxPrice must exceed minPrice');
const buckets = new Array();
for (let index = (0, pricing_1.priceToIndex)(maxPrice); index <= (0, pricing_1.priceToIndex)(minPrice); index++) {
buckets.push(new FungibleBucket_1.FungibleBucket(this.provider, this, index));
}
return buckets;
}
/**
* Withdraw all available liquidity from the given buckets using multicall transaction (first quote token, then - collateral if LP is left).
* @param signer address to redeem LP
* @param bucketIndices array of bucket indices to withdraw liquidity from
* @returns promise to transaction
*/
withdrawLiquidity(signer, bucketIndices) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const signerAddress = yield signer.getAddress();
const callData = [];
// get buckets
const bucketPromises = bucketIndices.map(bucketIndex => this.getBucketByIndex(bucketIndex));
const buckets = yield Promise.all(bucketPromises);
// get bucket details
const bucketStatusPromises = buckets.map(bucket => bucket.getStatus());
const bucketStatuses = yield Promise.all(bucketStatusPromises);
// determine lender's LP balance
const lpBalancePromises = bucketIndices.map(bucketIndex => (0, pool_1.lenderInfo)(this.contract, signerAddress, bucketIndex));
const lpBalances = yield Promise.all(lpBalancePromises);
for (let i = 0; i < bucketIndices.length; ++i) {
const [lpBalance] = lpBalances[i];
const bucketStatus = bucketStatuses[i];
const bucketIndex = bucketIndices[i];
// if there is any quote token in the bucket, redeem LP for deposit first
if (lpBalance && bucketStatus.deposit.gt(0)) {
callData.push({
methodName: 'removeQuoteToken',
args: [ethers_1.constants.MaxUint256, bucketIndex],
});
}
const depositWithdrawnEstimate = (0, numeric_1.wmul)(lpBalance, bucketStatus.exchangeRate);
// CAUTION: This estimate may cause revert because we cannot predict exchange rate for an
// arbitrary future block where the TX will be processed.
const withdrawCollateral = (bucketStatus.deposit.eq(0) || depositWithdrawnEstimate.gt(bucketStatus.deposit)) &&
bucketStatus.collateral.gt(0);
if (withdrawCollateral) {
callData.push({
methodName: 'removeCollateral',
args: [ethers_1.constants.MaxUint256, bucketIndex],
});
}
}
return this.multicall(signer, callData);
});
}
}
exports.FungiblePool = FungiblePool;
//# sourceMappingURL=FungiblePool.js.map