diff --git a/contracts/polygon/common/interfaces.sol b/contracts/polygon/common/interfaces.sol index 5041400a..a38be52a 100644 --- a/contracts/polygon/common/interfaces.sol +++ b/contracts/polygon/common/interfaces.sol @@ -10,6 +10,7 @@ interface TokenInterface { function withdraw(uint) external; function balanceOf(address) external view returns (uint); function decimals() external view returns (uint); + function allowance(address owner, address spender) external view returns (uint256); } interface MemoryInterface { diff --git a/contracts/polygon/connectors/compound/v3-rewards/events.sol b/contracts/polygon/connectors/compound/v3-rewards/events.sol new file mode 100644 index 00000000..81754e7c --- /dev/null +++ b/contracts/polygon/connectors/compound/v3-rewards/events.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.6; +pragma experimental ABIEncoderV2; + +contract Events { + event LogRewardsClaimed( + address indexed market, + address indexed account, + uint256 indexed rewardsClaimed, + uint256 setId + ); + + event LogRewardsClaimedOnBehalf( + address indexed market, + address indexed owner, + address to, + uint256 indexed rewardsClaimed, + uint256 setId + ); +} diff --git a/contracts/polygon/connectors/compound/v3-rewards/helpers.sol b/contracts/polygon/connectors/compound/v3-rewards/helpers.sol new file mode 100644 index 00000000..769eb8aa --- /dev/null +++ b/contracts/polygon/connectors/compound/v3-rewards/helpers.sol @@ -0,0 +1,13 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma abicoder v2; + +import { TokenInterface } from "../../../common/interfaces.sol"; +import { DSMath } from "../../../common/math.sol"; +import { Basic } from "../../../common/basic.sol"; +import { CometRewards } from "./interface.sol"; + +abstract contract Helpers is DSMath, Basic { + CometRewards internal constant cometRewards = + CometRewards(0x1B0e765F6224C21223AeA2af16c1C46E38885a40); +} diff --git a/contracts/polygon/connectors/compound/v3-rewards/interface.sol b/contracts/polygon/connectors/compound/v3-rewards/interface.sol new file mode 100644 index 00000000..b2157255 --- /dev/null +++ b/contracts/polygon/connectors/compound/v3-rewards/interface.sol @@ -0,0 +1,37 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma abicoder v2; + +struct UserCollateral { + uint128 balance; + uint128 _reserved; +} + +struct RewardOwed { + address token; + uint256 owed; +} + +interface CometRewards { + function claim( + address comet, + address src, + bool shouldAccrue + ) external; + + function claimTo( + address comet, + address src, + address to, + bool shouldAccrue + ) external; + + function getRewardOwed(address comet, address account) + external + returns (RewardOwed memory); + + function rewardsClaimed(address cometProxy, address account) + external + view + returns (uint256); +} diff --git a/contracts/polygon/connectors/compound/v3-rewards/main.sol b/contracts/polygon/connectors/compound/v3-rewards/main.sol new file mode 100644 index 00000000..94404856 --- /dev/null +++ b/contracts/polygon/connectors/compound/v3-rewards/main.sol @@ -0,0 +1,68 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +/** + * @title Compound. + * @dev Rewards. + */ + +import { TokenInterface } from "../../../common/interfaces.sol"; +import { Stores } from "../../../common/stores.sol"; +import { Helpers } from "./helpers.sol"; +import { Events } from "./events.sol"; + +abstract contract CompoundV3RewardsResolver is Events, Helpers { + /** + * @dev Claim rewards and interests accrued in supplied/borrowed base asset. + * @notice Claim rewards and interests accrued. + * @param market The address of the market. + * @param setId ID stores the amount of rewards claimed. + */ + function claimRewards( + address market, + uint256 setId + ) public returns (string memory eventName_, bytes memory eventParam_) { + uint256 rewardsOwed = cometRewards.getRewardOwed(market, address(this)).owed; + cometRewards.claim(market, address(this), true); + + setUint(setId, rewardsOwed); + + eventName_ = "LogRewardsClaimed(address,address,uint256,uint256)"; + eventParam_ = abi.encode(market, address(this), rewardsOwed, setId); + } + + /** + * @dev Claim rewards and interests accrued in supplied/borrowed base asset. + * @notice Claim rewards and interests accrued and transfer to dest address. + * @param market The address of the market. + * @param owner The account of which the rewards are to be claimed. + * @param to The account where to transfer the claimed rewards. + * @param setId ID stores the amount of rewards claimed. + */ + function claimRewardsOnBehalfOf( + address market, + address owner, + address to, + uint256 setId + ) public returns (string memory eventName_, bytes memory eventParam_) { + //in reward token decimals + uint256 rewardsOwed = cometRewards.getRewardOwed(market, owner).owed; + cometRewards.claimTo(market, owner, to, true); + + setUint(setId, rewardsOwed); + + eventName_ = "LogRewardsClaimedOnBehalf(address,address,address,uint256,uint256)"; + eventParam_ = abi.encode( + market, + owner, + to, + rewardsOwed, + setId + ); + } +} + +contract ConnectV2CompoundV3Rewards is CompoundV3RewardsResolver { + string public name = "CompoundV3Rewards-v1.0"; +} diff --git a/contracts/polygon/connectors/compound/v3/events.sol b/contracts/polygon/connectors/compound/v3/events.sol new file mode 100644 index 00000000..6de419f6 --- /dev/null +++ b/contracts/polygon/connectors/compound/v3/events.sol @@ -0,0 +1,165 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +contract Events { + event LogDeposit( + address indexed market, + address indexed token, + uint256 tokenAmt, + uint256 getId, + uint256 setId + ); + + event LogDepositOnBehalf( + address indexed market, + address indexed token, + address to, + uint256 tokenAmt, + uint256 getId, + uint256 setId + ); + + event LogDepositFromUsingManager( + address indexed market, + address indexed token, + address from, + address to, + uint256 tokenAmt, + uint256 getId, + uint256 setId + ); + + event LogWithdraw( + address indexed market, + address indexed token, + uint256 tokenAmt, + uint256 getId, + uint256 setId + ); + + event LogWithdrawTo( + address indexed market, + address indexed token, + address to, + uint256 tokenAmt, + uint256 getId, + uint256 setId + ); + + event LogWithdrawOnBehalf( + address indexed market, + address indexed token, + address from, + uint256 tokenAmt, + uint256 getId, + uint256 setId + ); + + event LogWithdrawOnBehalfAndTransfer( + address indexed market, + address indexed token, + address from, + address to, + uint256 tokenAmt, + uint256 getId, + uint256 setId + ); + + event LogBorrow( + address indexed market, + uint256 tokenAmt, + uint256 getId, + uint256 setId + ); + + event LogBorrowTo( + address indexed market, + address to, + uint256 tokenAmt, + uint256 getId, + uint256 setId + ); + + event LogBorrowOnBehalf( + address indexed market, + address from, + uint256 tokenAmt, + uint256 getId, + uint256 setId + ); + + event LogBorrowOnBehalfAndTransfer( + address indexed market, + address from, + address to, + uint256 tokenAmt, + uint256 getId, + uint256 setId + ); + + event LogPayback(address indexed market, uint256 tokenAmt, uint256 getId, uint256 setId); + + event LogPaybackOnBehalf( + address indexed market, + address to, + uint256 tokenAmt, + uint256 getId, + uint256 setId + ); + + event LogPaybackFromUsingManager( + address indexed market, + address from, + address to, + uint256 tokenAmt, + uint256 getId, + uint256 setId + ); + + event LogBuyCollateral( + address indexed market, + address indexed buyToken, + uint256 indexed baseSellAmt, + uint256 unitAmt, + uint256 buyAmount, + uint256 getId, + uint256 setId + ); + + event LogTransferAsset( + address indexed market, + address token, + address indexed dest, + uint256 amount, + uint256 getId, + uint256 setId + ); + + event LogTransferAssetOnBehalf( + address indexed market, + address token, + address indexed from, + address indexed dest, + uint256 amount, + uint256 getId, + uint256 setId + ); + + event LogToggleAccountManager( + address indexed market, + address indexed manager, + bool allow + ); + + event LogToggleAccountManagerWithPermit( + address indexed market, + address indexed owner, + address indexed manager, + bool allow, + uint256 expiry, + uint256 nonce, + uint8 v, + bytes32 r, + bytes32 s + ); +} diff --git a/contracts/polygon/connectors/compound/v3/helpers.sol b/contracts/polygon/connectors/compound/v3/helpers.sol new file mode 100644 index 00000000..1fd3c0c1 --- /dev/null +++ b/contracts/polygon/connectors/compound/v3/helpers.sol @@ -0,0 +1,273 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +import { TokenInterface } from "../../../common/interfaces.sol"; +import { DSMath } from "../../../common/math.sol"; +import { Basic } from "../../../common/basic.sol"; +import { CometInterface } from "./interface.sol"; + +abstract contract Helpers is DSMath, Basic { + struct BorrowWithdrawParams { + address market; + address token; + address from; + address to; + uint256 amt; + uint256 getId; + uint256 setId; + } + + struct BuyCollateralData { + address market; + address sellToken; + address buyAsset; + uint256 unitAmt; + uint256 baseSellAmt; + } + + enum Action { + REPAY, + DEPOSIT + } + + function getBaseToken(address market) + internal + view + returns (address baseToken) + { + baseToken = CometInterface(market).baseToken(); + } + + function _borrow(BorrowWithdrawParams memory params) + internal + returns (uint256 amt, uint256 setId) + { + uint256 amt_ = getUint(params.getId, params.amt); + + require( + params.market != address(0) && + params.token != address(0) && + params.to != address(0), + "invalid market/token/to address" + ); + bool isMatic = params.token == maticAddr; + address token_ = isMatic ? wmaticAddr : params.token; + + TokenInterface tokenContract = TokenInterface(token_); + + params.from = params.from == address(0) ? address(this) : params.from; + + require( + CometInterface(params.market).balanceOf(params.from) == 0, + "borrow-disabled-when-supplied-base" + ); + + uint256 initialBal = CometInterface(params.market).borrowBalanceOf( + params.from + ); + + CometInterface(params.market).withdrawFrom( + params.from, + params.to, + token_, + amt_ + ); + + uint256 finalBal = CometInterface(params.market).borrowBalanceOf( + params.from + ); + amt_ = sub(finalBal, initialBal); + + if (params.to == address(this)) + convertWmaticToMatic(isMatic, tokenContract, amt_); + + setUint(params.setId, amt_); + + amt = amt_; + setId = params.setId; + } + + function _withdraw(BorrowWithdrawParams memory params) + internal + returns (uint256 amt, uint256 setId) + { + uint256 amt_ = getUint(params.getId, params.amt); + + require( + params.market != address(0) && + params.token != address(0) && + params.to != address(0), + "invalid market/token/to address" + ); + + bool isMatic = params.token == maticAddr; + address token_ = isMatic ? wmaticAddr : params.token; + + TokenInterface tokenContract = TokenInterface(token_); + params.from = params.from == address(0) ? address(this) : params.from; + + uint256 initialBal = _getAccountSupplyBalanceOfAsset( + params.from, + params.market, + token_ + ); + + if (token_ == getBaseToken(params.market)) { + //if there are supplies, ensure withdrawn amount is not greater than supplied i.e can't borrow using withdraw. + if (amt_ == uint256(-1)) { + amt_ = initialBal; + } else { + require( + amt_ <= initialBal, + "withdraw-amt-greater-than-supplies" + ); + } + + //if borrow balance > 0, there are no supplies so no withdraw, borrow instead. + require( + CometInterface(params.market).borrowBalanceOf(params.from) == 0, + "withdraw-disabled-for-zero-supplies" + ); + } else { + amt_ = amt_ == uint256(-1) ? initialBal : amt_; + } + + CometInterface(params.market).withdrawFrom( + params.from, + params.to, + token_, + amt_ + ); + + uint256 finalBal = _getAccountSupplyBalanceOfAsset( + params.from, + params.market, + token_ + ); + amt_ = sub(initialBal, finalBal); + + if (params.to == address(this)) + convertWmaticToMatic(isMatic, tokenContract, amt_); + + setUint(params.setId, amt_); + + amt = amt_; + setId = params.setId; + } + + function _getAccountSupplyBalanceOfAsset( + address account, + address market, + address asset + ) internal returns (uint256 balance) { + if (asset == getBaseToken(market)) { + //balance in base + balance = CometInterface(market).balanceOf(account); + } else { + //balance in asset denomination + balance = uint256( + CometInterface(market).userCollateral(account, asset).balance + ); + } + } + + function _calculateFromAmount( + address market, + address token, + address src, + uint256 amt, + bool isMatic, + Action action + ) internal view returns (uint256) { + if (amt == uint256(-1)) { + uint256 allowance_ = TokenInterface(token).allowance(src, market); + uint256 bal_; + + if (action == Action.REPAY) { + bal_ = CometInterface(market).borrowBalanceOf(src); + } else if (action == Action.DEPOSIT) { + if (isMatic) bal_ = src.balance; + else bal_ = TokenInterface(token).balanceOf(src); + } + + amt = bal_ < allowance_ ? bal_ : allowance_; + } + + return amt; + } + + function _buyCollateral( + BuyCollateralData memory params, + uint256 getId, + uint256 setId + ) internal returns (string memory eventName_, bytes memory eventParam_) { + uint256 sellAmt_ = getUint(getId, params.baseSellAmt); + require( + params.market != address(0) && params.buyAsset != address(0), + "invalid market/token address" + ); + + bool isMatic = params.sellToken == maticAddr; + params.sellToken = isMatic ? wmaticAddr : params.sellToken; + + require( + params.sellToken == getBaseToken(params.market), + "invalid-sell-token" + ); + + if (sellAmt_ == uint256(-1)) { + sellAmt_ = isMatic + ? address(this).balance + : TokenInterface(params.sellToken).balanceOf(address(this)); + } + convertMaticToWmatic(isMatic, TokenInterface(params.sellToken), sellAmt_); + + isMatic = params.buyAsset == maticAddr; + params.buyAsset = isMatic ? wmaticAddr : params.buyAsset; + + uint256 slippageAmt_ = convert18ToDec( + TokenInterface(params.buyAsset).decimals(), + wmul( + params.unitAmt, + convertTo18( + TokenInterface(params.sellToken).decimals(), + sellAmt_ + ) + ) + ); + + uint256 initialCollBal_ = TokenInterface(params.buyAsset).balanceOf( + address(this) + ); + + approve(TokenInterface(params.sellToken), params.market, sellAmt_); + CometInterface(params.market).buyCollateral( + params.buyAsset, + slippageAmt_, + sellAmt_, + address(this) + ); + + uint256 finalCollBal_ = TokenInterface(params.buyAsset).balanceOf( + address(this) + ); + + uint256 buyAmt_ = sub(finalCollBal_, initialCollBal_); + require(slippageAmt_ <= buyAmt_, "too-much-slippage"); + + convertWmaticToMatic(isMatic, TokenInterface(params.buyAsset), buyAmt_); + setUint(setId, sellAmt_); + + eventName_ = "LogBuyCollateral(address,address,uint256,uint256,uint256,uint256,uint256)"; + eventParam_ = abi.encode( + params.market, + params.buyAsset, + sellAmt_, + params.unitAmt, + buyAmt_, + getId, + setId + ); + } +} diff --git a/contracts/polygon/connectors/compound/v3/interface.sol b/contracts/polygon/connectors/compound/v3/interface.sol new file mode 100644 index 00000000..3b1920d1 --- /dev/null +++ b/contracts/polygon/connectors/compound/v3/interface.sol @@ -0,0 +1,121 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +struct UserCollateral { + uint128 balance; + uint128 _reserved; +} + +struct RewardOwed { + address token; + uint256 owed; +} + +interface CometInterface { + function supply(address asset, uint256 amount) external virtual; + + function supplyTo( + address dst, + address asset, + uint256 amount + ) external virtual; + + function supplyFrom( + address from, + address dst, + address asset, + uint256 amount + ) external virtual; + + function transfer(address dst, uint256 amount) + external + virtual + returns (bool); + + function transferFrom( + address src, + address dst, + uint256 amount + ) external virtual returns (bool); + + function transferAsset( + address dst, + address asset, + uint256 amount + ) external virtual; + + function transferAssetFrom( + address src, + address dst, + address asset, + uint256 amount + ) external virtual; + + function withdraw(address asset, uint256 amount) external virtual; + + function withdrawTo( + address to, + address asset, + uint256 amount + ) external virtual; + + function withdrawFrom( + address src, + address to, + address asset, + uint256 amount + ) external virtual; + + function approveThis( + address manager, + address asset, + uint256 amount + ) external virtual; + + function withdrawReserves(address to, uint256 amount) external virtual; + + function absorb(address absorber, address[] calldata accounts) + external + virtual; + + function buyCollateral( + address asset, + uint256 minAmount, + uint256 baseAmount, + address recipient + ) external virtual; + + function quoteCollateral(address asset, uint256 baseAmount) + external + view + returns (uint256); + + function userCollateral(address, address) + external + returns (UserCollateral memory); + + function baseToken() external view returns (address); + + function balanceOf(address account) external view returns (uint256); + + function borrowBalanceOf(address account) external view returns (uint256); + + function allow(address manager, bool isAllowed_) external; + + function allowance(address owner, address spender) + external + view + returns (uint256); + + function allowBySig( + address owner, + address manager, + bool isAllowed_, + uint256 nonce, + uint256 expiry, + uint8 v, + bytes32 r, + bytes32 s + ) external; +} diff --git a/contracts/polygon/connectors/compound/v3/main.sol b/contracts/polygon/connectors/compound/v3/main.sol new file mode 100644 index 00000000..3a2dd6fb --- /dev/null +++ b/contracts/polygon/connectors/compound/v3/main.sol @@ -0,0 +1,934 @@ +//SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +/** + * @title Compound III + * @dev Lending & Borrowing. + */ + +import { TokenInterface } from "../../../common/interfaces.sol"; +import { Helpers } from "./helpers.sol"; +import { Events } from "./events.sol"; +import { CometInterface } from "./interface.sol"; + +abstract contract CompoundV3Contract is Events, Helpers { + /** + * @dev Deposit base asset or collateral asset supported by the market. + * @notice Deposit a token to Compound for lending / collaterization. + * @param market The address of the market. + * @param token The address of the token to be supplied. (For MATIC: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amt The amount of the token to deposit. (For max: `uint256(-1)`) + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens deposited. + */ + function deposit( + address market, + address token, + uint256 amt, + uint256 getId, + uint256 setId + ) + public + payable + returns (string memory eventName_, bytes memory eventParam_) + { + uint256 amt_ = getUint(getId, amt); + + require( + market != address(0) && token != address(0), + "invalid market/token address" + ); + + bool isMatic = token == maticAddr; + address token_ = isMatic ? wmaticAddr : token; + TokenInterface tokenContract = TokenInterface(token_); + + if (token_ == getBaseToken(market)) { + require( + CometInterface(market).borrowBalanceOf(address(this)) == 0, + "debt-not-repaid" + ); + } + + if (isMatic) { + amt_ = amt_ == uint256(-1) ? address(this).balance : amt_; + convertMaticToWmatic(isMatic, tokenContract, amt_); + } else { + amt_ = amt_ == uint256(-1) + ? tokenContract.balanceOf(address(this)) + : amt_; + } + approve(tokenContract, market, amt_); + + CometInterface(market).supply(token_, amt_); + + setUint(setId, amt_); + + eventName_ = "LogDeposit(address,address,uint256,uint256,uint256)"; + eventParam_ = abi.encode(market, token, amt_, getId, setId); + } + + /** + * @dev Deposit base asset or collateral asset supported by the market on behalf of 'to'. + * @notice Deposit a token to Compound for lending / collaterization on behalf of 'to'. + * @param market The address of the market. + * @param token The address of the token to be supplied. (For MATIC: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE). + * @param to The address on behalf of which the supply is made. + * @param amt The amount of the token to deposit. (For max: `uint256(-1)`) + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens deposited. + */ + function depositOnBehalf( + address market, + address token, + address to, + uint256 amt, + uint256 getId, + uint256 setId + ) + public + payable + returns (string memory eventName_, bytes memory eventParam_) + { + uint256 amt_ = getUint(getId, amt); + + require( + market != address(0) && token != address(0) && to != address(0), + "invalid market/token/to address" + ); + + bool isMatic = token == maticAddr; + address token_ = isMatic ? wmaticAddr : token; + TokenInterface tokenContract = TokenInterface(token_); + + if (token_ == getBaseToken(market)) { + require( + CometInterface(market).borrowBalanceOf(to) == 0, + "to-address-position-debt-not-repaid" + ); + } + + if (isMatic) { + amt_ = amt_ == uint256(-1) ? address(this).balance : amt_; + convertMaticToWmatic(isMatic, tokenContract, amt_); + } else { + amt_ = amt_ == uint256(-1) + ? tokenContract.balanceOf(address(this)) + : amt_; + } + approve(tokenContract, market, amt_); + + CometInterface(market).supplyTo(to, token_, amt_); + + setUint(setId, amt_); + + eventName_ = "LogDepositOnBehalf(address,address,address,uint256,uint256,uint256)"; + eventParam_ = abi.encode(market, token, to, amt_, getId, setId); + } + + /** + * @dev Deposit base asset or collateral asset supported by the market from 'from' address and update the position of 'to'. + * @notice Deposit a token to Compound for lending / collaterization from a address and update the position of 'to'. + * @param market The address of the market. + * @param token The address of the token to be supplied. (For MATIC: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param from The address from where amount is to be supplied. + * @param to The address on account of which the supply is made or whose positions are updated. + * @param amt The amount of the token to deposit. (For max: `uint256(-1)`) + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens deposited. + */ + function depositFromUsingManager( + address market, + address token, + address from, + address to, + uint256 amt, + uint256 getId, + uint256 setId + ) + public + payable + returns (string memory eventName_, bytes memory eventParam_) + { + uint256 amt_ = getUint(getId, amt); + + require( + market != address(0) && token != address(0) && to != address(0), + "invalid market/token/to address" + ); + require(from != address(this), "from-cannot-be-address(this)-use-depositOnBehalf"); + + bool isMatic = token == maticAddr; + address token_ = isMatic? wmaticAddr : token; + + if (token_ == getBaseToken(market)) { + require( + CometInterface(market).borrowBalanceOf(to) == 0, + "to-address-position-debt-not-repaid" + ); + } + + amt_ = _calculateFromAmount( + market, + token_, + from, + amt_, + isMatic, + Action.DEPOSIT + ); + + CometInterface(market).supplyFrom(from, to, token_, amt_); + setUint(setId, amt_); + + eventName_ = "LogDepositFromUsingManager(address,address,address,address,uint256,uint256,uint256)"; + eventParam_ = abi.encode(market, token, from, to, amt_, getId, setId); + } + + /** + * @dev Withdraw base/collateral asset. + * @notice Withdraw base token or deposited token from Compound. + * @param market The address of the market. + * @param token The address of the token to be withdrawn. (For MATIC: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amt The amount of the token to withdraw. (For max: `uint256(-1)`) + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens withdrawn. + */ + function withdraw( + address market, + address token, + uint256 amt, + uint256 getId, + uint256 setId + ) + public + payable + returns (string memory eventName_, bytes memory eventParam_) + { + uint256 amt_ = getUint(getId, amt); + + require( + market != address(0) && token != address(0), + "invalid market/token address" + ); + + bool isMatic = token == maticAddr; + address token_ = isMatic ? wmaticAddr : token; + + TokenInterface tokenContract = TokenInterface(token_); + + uint256 initialBal = _getAccountSupplyBalanceOfAsset( + address(this), + market, + token_ + ); + + if (token_ == getBaseToken(market)) { + if (amt_ == uint256(-1)) { + amt_ = initialBal; + } else { + //if there are supplies, ensure withdrawn amount is not greater than supplied i.e can't borrow using withdraw. + require(amt_ <= initialBal, "withdraw-amt-greater-than-supplies"); + } + + //if borrow balance > 0, there are no supplies so no withdraw, borrow instead. + require( + CometInterface(market).borrowBalanceOf(address(this)) == 0, + "withdraw-disabled-for-zero-supplies" + ); + } else { + amt_ = amt_ == uint256(-1) ? initialBal : amt_; + } + + CometInterface(market).withdraw(token_, amt_); + + uint256 finalBal = _getAccountSupplyBalanceOfAsset( + address(this), + market, + token_ + ); + + amt_ = sub(initialBal, finalBal); + + convertWmaticToMatic(isMatic, tokenContract, amt_); + + setUint(setId, amt_); + + eventName_ = "LogWithdraw(address,address,uint256,uint256,uint256)"; + eventParam_ = abi.encode(market, token, amt_, getId, setId); + } + + /** + * @dev Withdraw base/collateral asset and transfer to 'to'. + * @notice Withdraw base token or deposited token from Compound on behalf of an address and transfer to 'to'. + * @param market The address of the market. + * @param token The address of the token to be withdrawn. (For MATIC: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param to The address to which the borrowed assets are to be transferred. + * @param amt The amount of the token to withdraw. (For max: `uint256(-1)`) + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens withdrawn. + */ + function withdrawTo( + address market, + address token, + address to, + uint256 amt, + uint256 getId, + uint256 setId + ) + public + payable + returns (string memory eventName_, bytes memory eventParam_) + { + (uint256 amt_, uint256 setId_) = _withdraw( + BorrowWithdrawParams({ + market: market, + token: token, + from: address(this), + to: to, + amt: amt, + getId: getId, + setId: setId + }) + ); + + eventName_ = "LogWithdrawTo(address,address,address,uint256,uint256,uint256)"; + eventParam_ = abi.encode(market, token, to, amt_, getId, setId_); + } + + /** + * @dev Withdraw base/collateral asset from an account and transfer to DSA. + * @notice Withdraw base token or deposited token from Compound from an address and transfer to DSA. + * @param market The address of the market. + * @param token The address of the token to be withdrawn. (For MATIC: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param from The address from where asset is to be withdrawed. + * @param amt The amount of the token to withdraw. (For max: `uint256(-1)`) + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens withdrawn. + */ + function withdrawOnBehalf( + address market, + address token, + address from, + uint256 amt, + uint256 getId, + uint256 setId + ) + public + payable + returns (string memory eventName_, bytes memory eventParam_) + { + (uint256 amt_, uint256 setId_) = _withdraw( + BorrowWithdrawParams({ + market: market, + token: token, + from: from, + to: address(this), + amt: amt, + getId: getId, + setId: setId + }) + ); + + eventName_ = "LogWithdrawOnBehalf(address,address,address,uint256,uint256,uint256)"; + eventParam_ = abi.encode(market, token, from, amt_, getId, setId_); + } + + /** + * @dev Withdraw base/collateral asset from an account and transfer to 'to'. + * @notice Withdraw base token or deposited token from Compound from an address and transfer to 'to'. + * @param market The address of the market. + * @param token The address of the token to be withdrawn. (For MATIC: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param from The address from where asset is to be withdrawed. + * @param to The address to which the borrowed assets are to be transferred. + * @param amt The amount of the token to withdraw. (For max: `uint256(-1)`) + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens withdrawn. + */ + function withdrawOnBehalfAndTransfer( + address market, + address token, + address from, + address to, + uint256 amt, + uint256 getId, + uint256 setId + ) + public + payable + returns (string memory eventName_, bytes memory eventParam_) + { + (uint256 amt_, uint256 setId_) = _withdraw( + BorrowWithdrawParams({ + market: market, + token: token, + from: from, + to: to, + amt: amt, + getId: getId, + setId: setId + }) + ); + + eventName_ = "LogWithdrawOnBehalfAndTransfer(address,address,address,address,uint256,uint256,uint256)"; + eventParam_ = abi.encode(market, token, from, to, amt_, getId, setId_); + } + + /** + * @dev Borrow base asset. + * @notice Borrow base token from Compound. + * @param market The address of the market. + * @param token The address of the token to be borrowed. (For MATIC: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amt The amount of base token to borrow. + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens borrowed. + */ + function borrow( + address market, + address token, + uint256 amt, + uint256 getId, + uint256 setId + ) + external + payable + returns (string memory eventName_, bytes memory eventParam_) + { + uint256 amt_ = getUint(getId, amt); + + require(market != address(0), "invalid market address"); + + bool isMatic = token == maticAddr; + address token_ = getBaseToken(market); + require(token == token_ || isMatic, "invalid-token"); + + TokenInterface tokenContract = TokenInterface(token_); + + require( + CometInterface(market).balanceOf(address(this)) == 0, + "borrow-disabled-when-supplied-base" + ); + + uint256 initialBal = CometInterface(market).borrowBalanceOf( + address(this) + ); + + CometInterface(market).withdraw(token_, amt_); + + uint256 finalBal = CometInterface(market).borrowBalanceOf( + address(this) + ); + + amt_ = sub(finalBal, initialBal); + + convertWmaticToMatic(isMatic, tokenContract, amt_); + + setUint(setId, amt_); + + eventName_ = "LogBorrow(address,uint256,uint256,uint256)"; + eventParam_ = abi.encode(market, amt_, getId, setId); + } + + /** + * @dev Borrow base asset and transfer to 'to' account. + * @notice Borrow base token from Compound on behalf of an address. + * @param market The address of the market. + * @param token The address of the token to be borrowed. (For MATIC: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param to The address to which the borrowed asset is transferred. + * @param amt The amount of the token to withdraw. (For max: `uint256(-1)`) + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens borrowed. + */ + function borrowTo( + address market, + address token, + address to, + uint256 amt, + uint256 getId, + uint256 setId + ) + external + payable + returns (string memory eventName_, bytes memory eventParam_) + { + require( + token == maticAddr || token == getBaseToken(market), + "invalid-token" + ); + (uint256 amt_, uint256 setId_) = _borrow( + BorrowWithdrawParams({ + market: market, + token: token, + from: address(this), + to: to, + amt: amt, + getId: getId, + setId: setId + }) + ); + eventName_ = "LogBorrowTo(address,address,uint256,uint256,uint256)"; + eventParam_ = abi.encode(market, to, amt_, getId, setId_); + } + + /** + * @dev Borrow base asset from 'from' and transfer to 'to'. + * @notice Borrow base token or deposited token from Compound. + * @param market The address of the market. + * @param token The address of the token to be borrowed. (For MATIC: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amt The amount of the token to withdraw. (For max: `uint256(-1)`) + * @param from The address from where asset is to be withdrawed. + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens borrowed. + */ + function borrowOnBehalf( + address market, + address token, + address from, + uint256 amt, + uint256 getId, + uint256 setId + ) + external + payable + returns (string memory eventName_, bytes memory eventParam_) + { + require( + token == maticAddr || token == getBaseToken(market), + "invalid-token" + ); + (uint256 amt_, uint256 setId_) = _borrow( + BorrowWithdrawParams({ + market: market, + token: token, + from: from, + to: address(this), + amt: amt, + getId: getId, + setId: setId + }) + ); + eventName_ = "LogBorrowOnBehalf(address,address,uint256,uint256,uint256)"; + eventParam_ = abi.encode(market, from, amt_, getId, setId_); + } + + /** + * @dev Borrow base asset from 'from' and transfer to 'to'. + * @notice Borrow base token or deposited token from Compound. + * @param market The address of the market. + * @param token The address of the token to be borrowed. (For MATIC: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amt The amount of the token to withdraw. (For max: `uint256(-1)`) + * @param from The address from where asset is to be withdrawed. + * @param to The address to which the borrowed assets are to be transferred. + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens borrowed. + */ + function borrowOnBehalfAndTransfer( + address market, + address token, + address from, + address to, + uint256 amt, + uint256 getId, + uint256 setId + ) + external + payable + returns (string memory eventName_, bytes memory eventParam_) + { + require( + token == maticAddr || token == getBaseToken(market), + "invalid-token" + ); + (uint256 amt_, uint256 setId_) = _borrow( + BorrowWithdrawParams({ + market: market, + token: token, + from: from, + to: to, + amt: amt, + getId: getId, + setId: setId + }) + ); + eventName_ = "LogBorrowOnBehalfAndTransfer(address,address,address,uint256,uint256,uint256)"; + eventParam_ = abi.encode(market, from, to, amt_, getId, setId_); + } + + /** + * @dev Repays the borrowed base asset. + * @notice Repays the borrow of the base asset. + * @param market The address of the market. + * @param token The address of the token to be repaid. (For MATIC: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param amt The amount to be repaid. + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens repaid. + */ + function payback( + address market, + address token, + uint256 amt, + uint256 getId, + uint256 setId + ) + external + payable + returns (string memory eventName_, bytes memory eventParam_) + { + uint256 amt_ = getUint(getId, amt); + require( + market != address(0) && token != address(0), + "invalid market/token address" + ); + + bool isMatic = token == maticAddr; + address token_ = getBaseToken(market); + require(token == token_ || isMatic, "invalid-token"); + + TokenInterface tokenContract = TokenInterface(token_); + + uint256 borrowedBalance_ = CometInterface(market).borrowBalanceOf( + address(this) + ); + + if (amt_ == uint256(-1)) { + amt_ = borrowedBalance_; + } else { + require( + amt_ <= borrowedBalance_, + "payback-amt-greater-than-borrows" + ); + } + + //if supply balance > 0, there are no borrowing so no repay, supply instead. + require( + CometInterface(market).balanceOf(address(this)) == 0, + "cannot-repay-when-supplied" + ); + + convertMaticToWmatic(isMatic, tokenContract, amt_); + approve(tokenContract, market, amt_); + + CometInterface(market).supply(token_, amt_); + + setUint(setId, amt_); + + eventName_ = "LogPayback(address,uint256,uint256,uint256)"; + eventParam_ = abi.encode(market, amt_, getId, setId); + } + + /** + * @dev Repays borrow of the base asset on behalf of 'to'. + * @notice Repays borrow of the base asset on behalf of 'to'. + * @param market The address of the market. + * @param token The address of the token to be repaid. (For MATIC: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param to The address on behalf of which the borrow is to be repaid. + * @param amt The amount to be repaid. + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens repaid. + */ + function paybackOnBehalf( + address market, + address token, + address to, + uint256 amt, + uint256 getId, + uint256 setId + ) + external + payable + returns (string memory eventName_, bytes memory eventParam_) + { + uint256 amt_ = getUint(getId, amt); + require( + market != address(0) && token != address(0) && to != address(0), + "invalid market/token/to address" + ); + + address token_ = getBaseToken(market); + bool isMatic = token == maticAddr; + require(token == token_ || isMatic, "invalid-token"); + + TokenInterface tokenContract = TokenInterface(token_); + + uint256 borrowedBalance_ = CometInterface(market).borrowBalanceOf(to); + + if (amt_ == uint256(-1)) { + amt_ = borrowedBalance_; + } else { + require( + amt_ <= borrowedBalance_, + "payback-amt-greater-than-borrows" + ); + } + + //if supply balance > 0, there are no borrowing so no repay, supply instead. + require( + CometInterface(market).balanceOf(to) == 0, + "cannot-repay-when-supplied" + ); + + convertMaticToWmatic(isMatic, tokenContract, amt_); + approve(tokenContract, market, amt_); + + CometInterface(market).supplyTo(to, token_, amt_); + + setUint(setId, amt_); + + eventName_ = "LogPaybackOnBehalf(address,address,uint256,uint256,uint256)"; + eventParam_ = abi.encode(market, to, amt_, getId, setId); + } + + /** + * @dev Repays borrow of the base asset form 'from' on behalf of 'to'. + * @notice Repays borrow of the base asset on behalf of 'to'. 'From' address must approve the comet market. + * @param market The address of the market. + * @param token The address of the token to be repaid. (For MATIC: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param from The address from which the borrow has to be repaid on behalf of 'to'. + * @param to The address on behalf of which the borrow is to be repaid. + * @param amt The amount to be repaid. + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens repaid. + */ + function paybackFromUsingManager( + address market, + address token, + address from, + address to, + uint256 amt, + uint256 getId, + uint256 setId + ) + external + payable + returns (string memory eventName_, bytes memory eventParam_) + { + uint256 amt_ = getUint(getId, amt); + require( + market != address(0) && token != address(0) && to != address(0), + "invalid market/token/to address" + ); + require(from != address(this), "from-cannot-be-address(this)-use-paybackOnBehalf"); + + address token_ = getBaseToken(market); + bool isMatic = token == maticAddr; + require(token == token_ || isMatic, "invalid-token"); + + if (amt_ == uint256(-1)) { + amt_ = _calculateFromAmount( + market, + token_, + from, + amt_, + isMatic, + Action.REPAY + ); + } else { + uint256 borrowedBalance_ = CometInterface(market).borrowBalanceOf(to); + require( + amt_ <= borrowedBalance_, + "payback-amt-greater-than-borrows" + ); + } + + //if supply balance > 0, there are no borrowing so no repay, withdraw instead. + require( + CometInterface(market).balanceOf(to) == 0, + "cannot-repay-when-supplied" + ); + + CometInterface(market).supplyFrom(from, to, token_, amt_); + + setUint(setId, amt_); + + eventName_ = "LogPaybackFromUsingManager(address,address,address,uint256,uint256,uint256)"; + eventParam_ = abi.encode(market, from, to, amt_, getId, setId); + } + + /** + * @dev Buy collateral asset absorbed, from the market. + * @notice Buy collateral asset to increase protocol base reserves until targetReserves is reached. + * @param market The address of the market from where to withdraw. + * @param sellToken base token. (For MATIC: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param buyAsset The collateral asset to purachase. (For MATIC: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) + * @param unitAmt Minimum amount of collateral expected to be received. + * @param baseSellAmt Amount of base asset to be sold for collateral. + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of base tokens sold. + */ + function buyCollateral( + address market, + address sellToken, + address buyAsset, + uint256 unitAmt, + uint256 baseSellAmt, + uint256 getId, + uint256 setId + ) + external + payable + returns (string memory eventName_, bytes memory eventParam_) + { + (eventName_, eventParam_) = _buyCollateral( + BuyCollateralData({ + market: market, + sellToken: sellToken, + buyAsset: buyAsset, + unitAmt: unitAmt, + baseSellAmt: baseSellAmt + }), + getId, + setId + ); + } + + /** + * @dev Transfer base/collateral or base asset to dest address from this account. + * @notice Transfer base/collateral asset to dest address from caller's account. + * @param market The address of the market. + * @param token The collateral asset to transfer to dest address. + * @param dest The account where to transfer the base assets. + * @param amount The amount of the collateral token to transfer. (For max: `uint256(-1)`) + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens transferred. + */ + function transferAsset( + address market, + address token, + address dest, + uint256 amount, + uint256 getId, + uint256 setId + ) + external + payable + returns (string memory eventName_, bytes memory eventParam_) + { + uint256 amt_ = getUint(getId, amount); + require( + market != address(0) && token != address(0) && dest != address(0), + "invalid market/token/to address" + ); + + address token_ = token == maticAddr ? wmaticAddr : token; + + amt_ = amt_ == uint256(-1) ? _getAccountSupplyBalanceOfAsset(address(this), market, token) : amt_; + + CometInterface(market).transferAssetFrom(address(this), dest, token_, amt_); + + setUint(setId, amt_); + + eventName_ = "LogTransferAsset(address,address,address,uint256,uint256,uint256)"; + eventParam_ = abi.encode(market, token_, dest, amt_, getId, setId); + } + + /** + * @dev Transfer collateral or base asset to dest address from src account. + * @notice Transfer collateral asset to dest address from src's account. + * @param market The address of the market. + * @param token The collateral asset to transfer to dest address. + * @param src The account from where to transfer the collaterals. + * @param dest The account where to transfer the collateral assets. + * @param amount The amount of the collateral token to transfer. (For max: `uint256(-1)`) + * @param getId ID to retrieve amt. + * @param setId ID stores the amount of tokens transferred. + */ + function transferAssetOnBehalf( + address market, + address token, + address src, + address dest, + uint256 amount, + uint256 getId, + uint256 setId + ) + external + payable + returns (string memory eventName_, bytes memory eventParam_) + { + uint256 amt_ = getUint(getId, amount); + require( + market != address(0) && token != address(0) && dest != address(0), + "invalid market/token/to address" + ); + + address token_ = token == maticAddr ? wmaticAddr : token; + + amt_ = amt_ == uint256(-1) ? _getAccountSupplyBalanceOfAsset(src, market, token) : amt_; + + CometInterface(market).transferAssetFrom(src, dest, token_, amt_); + + setUint(setId, amt_); + + eventName_ = "LogTransferAssetOnBehalf(address,address,address,address,uint256,uint256,uint256)"; + eventParam_ = abi.encode(market, token_, src, dest, amt_, getId, setId); + } + + /** + * @dev Allow/Disallow managers to handle position. + * @notice Authorize/Remove managers to perform write operations for the position. + * @param market The address of the market where to supply. + * @param manager The address to be authorized. + * @param isAllowed Whether to allow or disallow the manager. + */ + function toggleAccountManager( + address market, + address manager, + bool isAllowed + ) external returns (string memory eventName_, bytes memory eventParam_) { + CometInterface(market).allow(manager, isAllowed); + eventName_ = "LogToggleAccountManager(address,address,bool)"; + eventParam_ = abi.encode(market, manager, isAllowed); + } + + /** + * @dev Allow/Disallow managers to handle owner's position. + * @notice Authorize/Remove managers to perform write operations for owner's position. + * @param market The address of the market where to supply. + * @param owner The authorizind owner account. + * @param manager The address to be authorized. + * @param isAllowed Whether to allow or disallow the manager. + * @param nonce Signer's nonce. + * @param expiry The duration for which to permit the manager. + * @param v Recovery byte of the signature. + * @param r Half of the ECDSA signature pair. + * @param s Half of the ECDSA signature pair. + */ + function toggleAccountManagerWithPermit( + address market, + address owner, + address manager, + bool isAllowed, + uint256 nonce, + uint256 expiry, + uint8 v, + bytes32 r, + bytes32 s + ) external returns (string memory eventName_, bytes memory eventParam_) { + CometInterface(market).allowBySig( + owner, + manager, + isAllowed, + nonce, + expiry, + v, + r, + s + ); + eventName_ = "LogToggleAccountManagerWithPermit(address,address,address,bool,uint256,uint256,uint8,bytes32,bytes32)"; + eventParam_ = abi.encode( + market, + owner, + manager, + isAllowed, + expiry, + nonce, + v, + r, + s + ); + } +} + +contract ConnectV2CompoundV3Polygon is CompoundV3Contract { + string public name = "CompoundV3-v1.0"; +}