@volare.finance/volare.js
Version:
The SDK for Volare Protocol
453 lines • 21.2 kB
JavaScript
"use strict";
/**
* @file Controller.ts
* @author astra <astra@volare.finance>
* @date 2022
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.Controller = void 0;
const tslib_1 = require("tslib");
const bytes_1 = require("@ethersproject/bytes");
const utils_js_1 = require("@volare.finance/utils.js");
const ethers_1 = require("ethers");
const Controller_json_1 = require("../artifacts/Controller.json");
const cache_1 = require("./cache");
const errors_1 = require("./errors");
const protocols_1 = require("./protocols");
const VTokenImpl_1 = require("./VTokenImpl");
const VIRTUAL_VAULT_ID_MIN = 202300000;
class Controller extends utils_js_1.Provider {
static ABI() {
return Controller_json_1.abi;
}
constructor(address, endpoint) {
super(endpoint);
this.contract = new ethers_1.Contract(address, Controller.ABI(), this.provider);
}
/**
* @notice returns the current controller configuration
* @return whitelist, the address of the whitelist module
* @return oracle, the address of the oracle module
* @return calculator, the address of the calculator module
* @return pool, the address of the pool module
*/
getConfiguration() {
var _a;
return tslib_1.__awaiter(this, void 0, void 0, function* () {
if (this.configuration === undefined) {
const addresses = yield ((_a = this.contract) === null || _a === void 0 ? void 0 : _a.getConfiguration());
this.configuration = {
whitelist: addresses[0],
oracle: addresses[1],
calculator: addresses[2],
pool: addresses[3],
};
}
return this.configuration;
});
}
/**
* @notice get cap amount for collateral asset
* @param assetAddress collateral asset address
* @return cap amount
*/
getNakedCap(assetAddress) {
var _a;
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const assetAmount = yield ((_a = this.contract) === null || _a === void 0 ? void 0 : _a.getNakedCap(assetAddress));
const decimals = yield (0, cache_1.cDecimals)(assetAddress);
return (0, utils_js_1.$float)(assetAmount, decimals);
});
}
/**
* @notice get amount of collateral deposited in all naked margin vaults
* @param assetAddress collateral asset address
* @return naked pool balance
*/
getNakedPoolBalance(assetAddress) {
var _a;
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const assetAmount = yield ((_a = this.contract) === null || _a === void 0 ? void 0 : _a.getNakedPoolBalance(assetAddress));
const decimals = yield (0, cache_1.cDecimals)(assetAddress);
return (0, utils_js_1.$float)(assetAmount, decimals);
});
}
/**
* @notice get an vToken's payout/cash value after expiry, in the collateral asset
* @param vTokenAddress vToken address
* @param vTokenAmount amount of the vToken to calculate the payout for, always represented in 1e8
* @return amount of collateral to pay out
*/
getPayout(vTokenAddress, vTokenAmount) {
var _a;
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const scaledVTokenAmount = (0, utils_js_1.$)(vTokenAmount, protocols_1.VTOKEN_DECIMALS);
const payout = yield ((_a = this.contract) === null || _a === void 0 ? void 0 : _a.getPayout(vTokenAddress, scaledVTokenAmount.toString(10)));
const vTokenDetails = yield (0, cache_1.cVTokenDetails)(vTokenAddress);
const decimals = yield (0, cache_1.cDecimals)(vTokenDetails.collateralAsset);
return (0, utils_js_1.$float)(payout.toString(), decimals);
});
}
/**
* @notice get the number of vaults for a specified account owner
* @param ownerAddress account owner address
* @return number of vaults
*/
getAccountVaultCounter(ownerAddress) {
var _a;
return tslib_1.__awaiter(this, void 0, void 0, function* () {
return (yield ((_a = this.contract) === null || _a === void 0 ? void 0 : _a.getAccountVaultCounter(ownerAddress))).toNumber();
});
}
/**
* @notice return a specific vault
* @param ownerAddress account owner
* @param vaultId vault id of vault to return
* @return INativeVault struct that corresponds to the _vaultId of _owner
*/
getVault(ownerAddress, vaultId) {
var _a;
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const vault = yield ((_a = this.contract) === null || _a === void 0 ? void 0 : _a.getVault(ownerAddress, vaultId));
return this.v(vault);
});
}
/**
* @notice return a specific vault
* @param ownerAddress account owner
* @param vaultId vault id of vault to return
* @return INativeVault struct that corresponds to the vaultId of ownerAddress, vault type and the latest timestamp when the vault was updated
*/
getVaultWithDetails(ownerAddress, vaultId) {
var _a;
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const [vault, vaultType, vaultLatestUpdate] = yield ((_a = this.contract) === null || _a === void 0 ? void 0 : _a.getVaultWithDetails(ownerAddress, vaultId));
return [yield this.v(vault), (0, bytes_1.hexlify)(vaultType), vaultLatestUpdate.toNumber()];
});
}
/**
* @notice mint short vTokens from a vault which creates an obligation that is recorded in the vault
* @dev only the account owner or operator can mint an vToken, cannot be called when system is partiallyPaused or fullyPaused
* @param wallet
* @param vaultId
* @param index
* @param vToken
* @param vTokenAmount
* @param vaultType
*/
short(wallet, vaultId, index, vToken, vTokenAmount, vaultType = protocols_1.VaultType.FullyCollateralized) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const collateralContract = new utils_js_1.ERC20(vToken.collateralAsset, this.endpoint);
const collateralAmount = vToken.isPut ? vToken.strikePrice.multipliedBy(vTokenAmount) : vTokenAmount;
const scaledCollateralAmount = (0, utils_js_1.$)(collateralAmount, yield collateralContract.decimals());
const scaledVTokenAmount = (0, utils_js_1.$)(vTokenAmount, protocols_1.VTOKEN_DECIMALS);
const configuration = yield this.getConfiguration();
const allowance = yield collateralContract.allowance(yield wallet.getAddress(), configuration.pool);
if (allowance.lt(scaledCollateralAmount)) {
const tx = yield collateralContract.approve(wallet, configuration.pool, new utils_js_1.BigNumber(utils_js_1.ONE_BYTES32));
yield tx.wait();
}
return this.shortOptionOp(wallet, vaultId, index, vToken.address, collateralContract.address, scaledVTokenAmount, scaledCollateralAmount, vaultType);
});
}
/**
* @notice redeem an vToken after expiry, receiving the payout of the vToken in the collateral asset
* @dev cannot be called when system is fullyPaused
* @param wallet
* @param vToken
* @param vTokenAmount
*/
redeem(wallet, vToken, vTokenAmount) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const scaledVTokenAmount = (0, utils_js_1.$)(vTokenAmount, protocols_1.VTOKEN_DECIMALS);
return this.redeemOp(wallet, [vToken.address], [scaledVTokenAmount]);
});
}
/**
* @notice settle a vault after expiry, removing the net proceeds/collateral after both long and short vToken payouts have settled
* @dev deletes a vault of vaultId after net proceeds/collateral is removed, cannot be called when system is fullyPaused
* @param wallet
* @param vaultId
* @param vault
*/
settle(wallet, vaultId, vault) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
if (vaultId > VIRTUAL_VAULT_ID_MIN) {
const scaledVTokenAmounts = vault.longAmounts.map(longAmount => {
return (0, utils_js_1.$)(longAmount, protocols_1.VTOKEN_DECIMALS);
});
return this.redeemOp(wallet, vault.longVTokens, scaledVTokenAmounts);
}
return this.settleVaultOp(wallet, vaultId);
});
}
/**
* @notice liquidate naked margin vault
* @dev can liquidate different vaults id in the same operate() call
* @param wallet
* @param ownerAddress
* @param vaultId
* @param vTokenAmount
*/
liquidate(wallet, ownerAddress, vaultId, vTokenAmount) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const scaledVTokenAmount = (0, utils_js_1.$)(vTokenAmount, protocols_1.VTOKEN_DECIMALS);
return this.liquidateOp(wallet, ownerAddress, vaultId, scaledVTokenAmount);
});
}
/**
* @notice deposit a collateral asset into a vault
* @dev only the account owner or operator can deposit collateral, cannot be called when system is partiallyPaused or fullyPaused
* @param owner
* @param vaultId
* @param index
* @param collateralAddress
* @param collateralAmount
*/
depositCollateral(owner, vaultId, index, collateralAddress, collateralAmount) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const collateralContract = new utils_js_1.ERC20(collateralAddress, this.endpoint);
const collateralDecimals = yield collateralContract.decimals();
const scaledCollateralAmount = (0, utils_js_1.$)(collateralAmount, collateralDecimals);
const configuration = yield this.getConfiguration();
const allowance = new utils_js_1.BigNumber((yield collateralContract.allowance(yield owner.getAddress(), configuration.pool)).toString());
if (allowance.lt(scaledCollateralAmount)) {
const tx = yield collateralContract.approve(owner, configuration.pool, new utils_js_1.BigNumber(utils_js_1.ONE_BYTES32));
yield tx.wait();
}
return this.depositOp(protocols_1.ActionType.DepositCollateral, owner, vaultId, index, collateralAddress, scaledCollateralAmount);
});
}
/**
* @notice withdraw a collateral asset from a vault
* @dev only the account owner or operator can withdraw collateral, cannot be called when system is partiallyPaused or fullyPaused
* @param owner
* @param vaultId
* @param index
* @param collateralAddress
* @param collateralAmount
*/
withdrawCollateral(owner, vaultId, index, collateralAddress, collateralAmount) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const collateralDecimals = yield utils_js_1.ERC20.Decimals(collateralAddress);
const scaledCollateralAmount = (0, utils_js_1.$)(collateralAmount, collateralDecimals);
return this.withdrawOp(protocols_1.ActionType.WithdrawCollateral, owner, vaultId, index, collateralAddress, scaledCollateralAmount);
});
}
/**
* @notice deposit a long vToken into a vault
* @dev only the account owner or operator can deposit a long vToken, cannot be called when system is partiallyPaused or fullyPaused
* @param owner
* @param vaultId
* @param index
* @param vToken
* @param vTokenAmount
*/
depositLong(owner, vaultId, index, vToken, vTokenAmount) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const vTokenContract = new VTokenImpl_1.VTokenImpl(vToken.address, this.endpoint);
const scaledVTokenAmount = (0, utils_js_1.$)(vTokenAmount, protocols_1.VTOKEN_DECIMALS);
const configuration = yield this.getConfiguration();
const allowance = yield vTokenContract.allowance(yield owner.getAddress(), configuration.pool);
if (allowance.lt(scaledVTokenAmount)) {
const tx = yield vTokenContract.approve(owner, configuration.pool, new utils_js_1.BigNumber(utils_js_1.ONE_BYTES32));
yield tx.wait();
}
return this.depositOp(protocols_1.ActionType.DepositLongOption, owner, vaultId, index, vToken.address, scaledVTokenAmount);
});
}
/**
* @notice withdraw a long vToken from a vault
* @dev only the account owner or operator can withdraw a long vToken, cannot be called when system is partiallyPaused or fullyPaused
* @param owner
* @param vaultId
* @param index
* @param vToken
* @param vTokenAmount
*/
withdrawLong(owner, vaultId, index, vToken, vTokenAmount) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const scaledVTokenAmount = (0, utils_js_1.$)(vTokenAmount, protocols_1.VTOKEN_DECIMALS);
return this.withdrawOp(protocols_1.ActionType.WithdrawLongOption, owner, vaultId, index, vToken.address, scaledVTokenAmount);
});
}
v(vault) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const [shortVTokens, longVTokens, collateralAssets, shortAmounts, longAmounts, collateralAmounts] = vault;
const scaledCollateralAmounts = [];
for (let i = 0; i < collateralAssets.length; i++) {
if (collateralAssets[i] === utils_js_1.ZERO_ADDR) {
scaledCollateralAmounts[i] = utils_js_1.ZERO;
}
else {
const decimals = yield (0, cache_1.cDecimals)(collateralAssets[i]);
scaledCollateralAmounts[i] = (0, utils_js_1.$float)(collateralAmounts[i].toString(), decimals);
}
}
return {
shortVTokens,
longVTokens,
collateralAssets,
shortAmounts: shortAmounts.map((amount) => (0, utils_js_1.$float)(amount.toString(), protocols_1.VTOKEN_DECIMALS)),
longAmounts: longAmounts.map((amount) => (0, utils_js_1.$float)(amount.toString(), protocols_1.VTOKEN_DECIMALS)),
collateralAmounts: scaledCollateralAmounts,
};
});
}
shortOptionOp(owner, vaultId, index, vTokenAddress, collateralAddress, scaledVTokenAmount, scaledCollateralAmount, vaultType = protocols_1.VaultType.FullyCollateralized) {
var _a;
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const ownerAddress = yield owner.getAddress();
const vaultCounter = yield this.getAccountVaultCounter(ownerAddress);
const actionArgs = [];
if (vaultId > vaultCounter) {
throw errors_1.CONTROLLER.C35;
}
if (vaultId === 0) {
vaultId = vaultCounter + 1;
actionArgs.push({
actionType: protocols_1.ActionType.OpenVault,
owner: ownerAddress,
secondAddress: ownerAddress,
asset: utils_js_1.ZERO_ADDR,
vaultId: vaultId,
amount: 0,
index: 0,
data: (0, bytes_1.hexZeroPad)(vaultType, 32),
});
}
actionArgs.push({
actionType: protocols_1.ActionType.MintShortOption,
owner: ownerAddress,
secondAddress: ownerAddress,
asset: vTokenAddress,
vaultId,
amount: scaledVTokenAmount.toString(10),
index,
data: utils_js_1.ZERO_ADDR,
}, {
actionType: protocols_1.ActionType.DepositCollateral,
owner: ownerAddress,
secondAddress: ownerAddress,
asset: collateralAddress,
vaultId,
amount: scaledCollateralAmount.toString(10),
index,
data: utils_js_1.ZERO_ADDR,
});
try {
return yield ((_a = this.contract) === null || _a === void 0 ? void 0 : _a.connect(owner).operate(actionArgs));
}
catch (e) {
const s = e.error.reason.split(': ');
console.log(`${s[1]}: ${(0, errors_1.reason)(s[1])}`);
throw e;
}
});
}
settleVaultOp(owner, vaultId) {
var _a;
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const ownerAddress = yield owner.getAddress();
const actionArgs = [
{
actionType: protocols_1.ActionType.SettleVault,
owner: ownerAddress,
secondAddress: ownerAddress,
asset: utils_js_1.ZERO_ADDR,
vaultId,
amount: 0,
index: 0,
data: utils_js_1.ZERO_ADDR,
},
];
return (_a = this.contract) === null || _a === void 0 ? void 0 : _a.connect(owner).operate(actionArgs);
});
}
redeemOp(owner, vTokenAddresses, scaledVTokenAmounts) {
var _a;
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const ownerAddress = yield owner.getAddress();
const actionArgs = [];
for (let i = 0; i < vTokenAddresses.length; i++) {
actionArgs[i] = {
actionType: protocols_1.ActionType.Redeem,
owner: ownerAddress,
secondAddress: ownerAddress,
asset: vTokenAddresses[i],
vaultId: 0,
amount: scaledVTokenAmounts[i].toString(10),
index: 0,
data: utils_js_1.ZERO_ADDR,
};
}
return (_a = this.contract) === null || _a === void 0 ? void 0 : _a.connect(owner).operate(actionArgs);
});
}
liquidateOp(wallet, ownerAddress, vaultId, scaledVTokenAmount) {
var _a;
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const receiverAddress = yield wallet.getAddress();
const actionArgs = [
{
actionType: protocols_1.ActionType.Liquidate,
owner: ownerAddress,
secondAddress: receiverAddress,
asset: utils_js_1.ZERO_ADDR,
vaultId,
amount: scaledVTokenAmount.toString(10),
index: 0,
data: utils_js_1.ZERO_ADDR,
},
];
return (_a = this.contract) === null || _a === void 0 ? void 0 : _a.connect(wallet).operate(actionArgs);
});
}
depositOp(actionType, owner, vaultId, index, assetAddress, scaledAssetAmount) {
var _a;
return tslib_1.__awaiter(this, void 0, void 0, function* () {
if (actionType !== protocols_1.ActionType.DepositCollateral && actionType !== protocols_1.ActionType.DepositLongOption) {
throw errors_1.LIB_ACTIONS.A8;
}
const ownerAddress = yield owner.getAddress();
const actionArgs = [
{
actionType,
owner: ownerAddress,
secondAddress: ownerAddress,
asset: assetAddress,
vaultId,
amount: scaledAssetAmount.toString(10),
index,
data: utils_js_1.ZERO_ADDR,
},
];
return (_a = this.contract) === null || _a === void 0 ? void 0 : _a.connect(owner).operate(actionArgs);
});
}
withdrawOp(actionType, owner, vaultId, index, assetAddress, scaledAssetAmount) {
var _a;
return tslib_1.__awaiter(this, void 0, void 0, function* () {
if (actionType !== protocols_1.ActionType.WithdrawCollateral && actionType !== protocols_1.ActionType.WithdrawLongOption) {
throw errors_1.LIB_ACTIONS.A10;
}
const ownerAddress = yield owner.getAddress();
const actionArgs = [
{
actionType,
owner: ownerAddress,
secondAddress: ownerAddress,
asset: assetAddress,
vaultId,
amount: scaledAssetAmount.toString(10),
index,
data: utils_js_1.ZERO_ADDR,
},
];
return (_a = this.contract) === null || _a === void 0 ? void 0 : _a.connect(owner).operate(actionArgs);
});
}
}
exports.Controller = Controller;
Controller.OwnershipTransferred_t0 = ethers_1.utils.id('OwnershipTransferred(address,address)');
//# sourceMappingURL=Controller.js.map