From b32f2051b5c9b78070b607bd140bb807aa10f727 Mon Sep 17 00:00:00 2001 From: Shriya Tyagi Date: Thu, 9 Mar 2023 22:16:20 +0400 Subject: [PATCH 01/16] Set up compound v3 contracts on polygon --- contracts/polygon/common/interfaces.sol | 1 + .../connectors/compound/v3-rewards/events.sol | 20 + .../compound/v3-rewards/helpers.sol | 13 + .../compound/v3-rewards/interface.sol | 37 + .../connectors/compound/v3-rewards/main.sol | 68 ++ .../polygon/connectors/compound/v3/events.sol | 165 ++++ .../connectors/compound/v3/helpers.sol | 273 +++++ .../connectors/compound/v3/interface.sol | 121 +++ .../polygon/connectors/compound/v3/main.sol | 934 ++++++++++++++++++ 9 files changed, 1632 insertions(+) create mode 100644 contracts/polygon/connectors/compound/v3-rewards/events.sol create mode 100644 contracts/polygon/connectors/compound/v3-rewards/helpers.sol create mode 100644 contracts/polygon/connectors/compound/v3-rewards/interface.sol create mode 100644 contracts/polygon/connectors/compound/v3-rewards/main.sol create mode 100644 contracts/polygon/connectors/compound/v3/events.sol create mode 100644 contracts/polygon/connectors/compound/v3/helpers.sol create mode 100644 contracts/polygon/connectors/compound/v3/interface.sol create mode 100644 contracts/polygon/connectors/compound/v3/main.sol 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"; +} From ae774a8240a4f6b221ec02543399aa54a1bb0c24 Mon Sep 17 00:00:00 2001 From: Shriya Tyagi Date: Fri, 14 Apr 2023 15:31:53 +0400 Subject: [PATCH 02/16] Added approve contracts - production --- .../connectors/approve-tokens/events.sol | 6 +++ .../connectors/approve-tokens/interface.sol | 6 +++ .../connectors/approve-tokens/main.sol | 37 +++++++++++++++++++ .../connectors/approve-tokens/events.sol | 6 +++ .../connectors/approve-tokens/interface.sol | 6 +++ .../connectors/approve-tokens/main.sol | 37 +++++++++++++++++++ .../connectors/approve-tokens/events.sol | 6 +++ .../connectors/approve-tokens/interface.sol | 6 +++ .../connectors/approve-tokens/main.sol | 37 +++++++++++++++++++ .../connectors/approve-tokens/events.sol | 6 +++ .../connectors/approve-tokens/interface.sol | 6 +++ .../connectors/approve-tokens/main.sol | 37 +++++++++++++++++++ .../connectors/approve-tokens/events.sol | 6 +++ .../connectors/approve-tokens/interface.sol | 6 +++ .../connectors/approve-tokens/main.sol | 37 +++++++++++++++++++ 15 files changed, 245 insertions(+) create mode 100644 contracts/arbitrum/connectors/approve-tokens/events.sol create mode 100644 contracts/arbitrum/connectors/approve-tokens/interface.sol create mode 100644 contracts/arbitrum/connectors/approve-tokens/main.sol create mode 100644 contracts/avalanche/connectors/approve-tokens/events.sol create mode 100644 contracts/avalanche/connectors/approve-tokens/interface.sol create mode 100644 contracts/avalanche/connectors/approve-tokens/main.sol create mode 100644 contracts/mainnet/connectors/approve-tokens/events.sol create mode 100644 contracts/mainnet/connectors/approve-tokens/interface.sol create mode 100644 contracts/mainnet/connectors/approve-tokens/main.sol create mode 100644 contracts/optimism/connectors/approve-tokens/events.sol create mode 100644 contracts/optimism/connectors/approve-tokens/interface.sol create mode 100644 contracts/optimism/connectors/approve-tokens/main.sol create mode 100644 contracts/polygon/connectors/approve-tokens/events.sol create mode 100644 contracts/polygon/connectors/approve-tokens/interface.sol create mode 100644 contracts/polygon/connectors/approve-tokens/main.sol diff --git a/contracts/arbitrum/connectors/approve-tokens/events.sol b/contracts/arbitrum/connectors/approve-tokens/events.sol new file mode 100644 index 00000000..704273be --- /dev/null +++ b/contracts/arbitrum/connectors/approve-tokens/events.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +contract Events { + event LogApproveTokens(address[] tokenAddresses, uint256[] amounts); +} \ No newline at end of file diff --git a/contracts/arbitrum/connectors/approve-tokens/interface.sol b/contracts/arbitrum/connectors/approve-tokens/interface.sol new file mode 100644 index 00000000..38abe986 --- /dev/null +++ b/contracts/arbitrum/connectors/approve-tokens/interface.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +interface IAvoFactory { + function computeAddress(address owner_) external view returns (address); +} \ No newline at end of file diff --git a/contracts/arbitrum/connectors/approve-tokens/main.sol b/contracts/arbitrum/connectors/approve-tokens/main.sol new file mode 100644 index 00000000..9545d04f --- /dev/null +++ b/contracts/arbitrum/connectors/approve-tokens/main.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; +import "./events.sol"; +import "./interface.sol"; + +contract ApproveTokensResolver is Events { + using SafeERC20 for IERC20; + + IAvoFactory public constant AVO_FACTORY = IAvoFactory(0x3AdAE9699029AB2953F607AE1f62372681D35978); + + function approveTokens( + address[] calldata tokens, + uint256[] calldata amounts + ) public returns (string memory _eventName, bytes memory _eventParam) { + require(tokens.length == amounts.length, "array-length-mismatch"); + + address avocadoAddress = AVO_FACTORY.computeAddress(msg.sender); + + for (uint256 i = 0; i < tokens.length; i++) { + uint256 allowanceAmount = + amounts[i] == type(uint256).max + ? IERC20(tokens[i]).balanceOf(address(this)) + : amounts[i]; + IERC20(tokens[i]).safeApprove(avocadoAddress, allowanceAmount); + } + + _eventName = "LogApproveTokens(address[],uint256[])"; + _eventParam = abi.encode(tokens, amounts); + } +} + +contract ConnectV2ApproveTokensArbitrum is ApproveTokensResolver { + string constant public name = "ApproveTokens-v1"; +} \ No newline at end of file diff --git a/contracts/avalanche/connectors/approve-tokens/events.sol b/contracts/avalanche/connectors/approve-tokens/events.sol new file mode 100644 index 00000000..704273be --- /dev/null +++ b/contracts/avalanche/connectors/approve-tokens/events.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +contract Events { + event LogApproveTokens(address[] tokenAddresses, uint256[] amounts); +} \ No newline at end of file diff --git a/contracts/avalanche/connectors/approve-tokens/interface.sol b/contracts/avalanche/connectors/approve-tokens/interface.sol new file mode 100644 index 00000000..38abe986 --- /dev/null +++ b/contracts/avalanche/connectors/approve-tokens/interface.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +interface IAvoFactory { + function computeAddress(address owner_) external view returns (address); +} \ No newline at end of file diff --git a/contracts/avalanche/connectors/approve-tokens/main.sol b/contracts/avalanche/connectors/approve-tokens/main.sol new file mode 100644 index 00000000..b7d2cd82 --- /dev/null +++ b/contracts/avalanche/connectors/approve-tokens/main.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; +import "./events.sol"; +import "./interface.sol"; + +contract ApproveTokensResolver is Events { + using SafeERC20 for IERC20; + + IAvoFactory public constant AVO_FACTORY = IAvoFactory(0x3AdAE9699029AB2953F607AE1f62372681D35978); + + function approveTokens( + address[] calldata tokens, + uint256[] calldata amounts + ) public returns (string memory _eventName, bytes memory _eventParam) { + require(tokens.length == amounts.length, "array-length-mismatch"); + + address avocadoAddress = AVO_FACTORY.computeAddress(msg.sender); + + for (uint256 i = 0; i < tokens.length; i++) { + uint256 allowanceAmount = + amounts[i] == type(uint256).max + ? IERC20(tokens[i]).balanceOf(address(this)) + : amounts[i]; + IERC20(tokens[i]).safeApprove(avocadoAddress, allowanceAmount); + } + + _eventName = "LogApproveTokens(address[],uint256[])"; + _eventParam = abi.encode(tokens, amounts); + } +} + +contract ConnectV2ApproveTokensAvalanche is ApproveTokensResolver { + string constant public name = "ApproveTokens-v1"; +} \ No newline at end of file diff --git a/contracts/mainnet/connectors/approve-tokens/events.sol b/contracts/mainnet/connectors/approve-tokens/events.sol new file mode 100644 index 00000000..704273be --- /dev/null +++ b/contracts/mainnet/connectors/approve-tokens/events.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +contract Events { + event LogApproveTokens(address[] tokenAddresses, uint256[] amounts); +} \ No newline at end of file diff --git a/contracts/mainnet/connectors/approve-tokens/interface.sol b/contracts/mainnet/connectors/approve-tokens/interface.sol new file mode 100644 index 00000000..38abe986 --- /dev/null +++ b/contracts/mainnet/connectors/approve-tokens/interface.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +interface IAvoFactory { + function computeAddress(address owner_) external view returns (address); +} \ No newline at end of file diff --git a/contracts/mainnet/connectors/approve-tokens/main.sol b/contracts/mainnet/connectors/approve-tokens/main.sol new file mode 100644 index 00000000..caea31af --- /dev/null +++ b/contracts/mainnet/connectors/approve-tokens/main.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; +import "./events.sol"; +import "./interface.sol"; + +contract ApproveTokensResolver is Events { + using SafeERC20 for IERC20; + + IAvoFactory public constant AVO_FACTORY = IAvoFactory(0x3AdAE9699029AB2953F607AE1f62372681D35978); + + function approveTokens( + address[] calldata tokens, + uint256[] calldata amounts + ) public returns (string memory _eventName, bytes memory _eventParam) { + require(tokens.length == amounts.length, "array-length-mismatch"); + + address avocadoAddress = AVO_FACTORY.computeAddress(msg.sender); + + for (uint256 i = 0; i < tokens.length; i++) { + uint256 allowanceAmount = + amounts[i] == type(uint256).max + ? IERC20(tokens[i]).balanceOf(address(this)) + : amounts[i]; + IERC20(tokens[i]).safeApprove(avocadoAddress, allowanceAmount); + } + + _eventName = "LogApproveTokens(address[],uint256[])"; + _eventParam = abi.encode(tokens, amounts); + } +} + +contract ConnectV2ApproveTokens is ApproveTokensResolver { + string constant public name = "ApproveTokens-v1"; +} \ No newline at end of file diff --git a/contracts/optimism/connectors/approve-tokens/events.sol b/contracts/optimism/connectors/approve-tokens/events.sol new file mode 100644 index 00000000..704273be --- /dev/null +++ b/contracts/optimism/connectors/approve-tokens/events.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +contract Events { + event LogApproveTokens(address[] tokenAddresses, uint256[] amounts); +} \ No newline at end of file diff --git a/contracts/optimism/connectors/approve-tokens/interface.sol b/contracts/optimism/connectors/approve-tokens/interface.sol new file mode 100644 index 00000000..38abe986 --- /dev/null +++ b/contracts/optimism/connectors/approve-tokens/interface.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +interface IAvoFactory { + function computeAddress(address owner_) external view returns (address); +} \ No newline at end of file diff --git a/contracts/optimism/connectors/approve-tokens/main.sol b/contracts/optimism/connectors/approve-tokens/main.sol new file mode 100644 index 00000000..b1a22daa --- /dev/null +++ b/contracts/optimism/connectors/approve-tokens/main.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; +import "./events.sol"; +import "./interface.sol"; + +contract ApproveTokensResolver is Events { + using SafeERC20 for IERC20; + + IAvoFactory public constant AVO_FACTORY = IAvoFactory(0x3AdAE9699029AB2953F607AE1f62372681D35978); + + function approveTokens( + address[] calldata tokens, + uint256[] calldata amounts + ) public returns (string memory _eventName, bytes memory _eventParam) { + require(tokens.length == amounts.length, "array-length-mismatch"); + + address avocadoAddress = AVO_FACTORY.computeAddress(msg.sender); + + for (uint256 i = 0; i < tokens.length; i++) { + uint256 allowanceAmount = + amounts[i] == type(uint256).max + ? IERC20(tokens[i]).balanceOf(address(this)) + : amounts[i]; + IERC20(tokens[i]).safeApprove(avocadoAddress, allowanceAmount); + } + + _eventName = "LogApproveTokens(address[],uint256[])"; + _eventParam = abi.encode(tokens, amounts); + } +} + +contract ConnectV2ApproveTokensOptimism is ApproveTokensResolver { + string constant public name = "ApproveTokens-v1"; +} \ No newline at end of file diff --git a/contracts/polygon/connectors/approve-tokens/events.sol b/contracts/polygon/connectors/approve-tokens/events.sol new file mode 100644 index 00000000..704273be --- /dev/null +++ b/contracts/polygon/connectors/approve-tokens/events.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +contract Events { + event LogApproveTokens(address[] tokenAddresses, uint256[] amounts); +} \ No newline at end of file diff --git a/contracts/polygon/connectors/approve-tokens/interface.sol b/contracts/polygon/connectors/approve-tokens/interface.sol new file mode 100644 index 00000000..38abe986 --- /dev/null +++ b/contracts/polygon/connectors/approve-tokens/interface.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +interface IAvoFactory { + function computeAddress(address owner_) external view returns (address); +} \ No newline at end of file diff --git a/contracts/polygon/connectors/approve-tokens/main.sol b/contracts/polygon/connectors/approve-tokens/main.sol new file mode 100644 index 00000000..33f1e421 --- /dev/null +++ b/contracts/polygon/connectors/approve-tokens/main.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; +import "./events.sol"; +import "./interface.sol"; + +contract ApproveTokensResolver is Events { + using SafeERC20 for IERC20; + + IAvoFactory public constant AVO_FACTORY = IAvoFactory(0x3AdAE9699029AB2953F607AE1f62372681D35978); + + function approveTokens( + address[] calldata tokens, + uint256[] calldata amounts + ) public returns (string memory _eventName, bytes memory _eventParam) { + require(tokens.length == amounts.length, "array-length-mismatch"); + + address avocadoAddress = AVO_FACTORY.computeAddress(msg.sender); + + for (uint256 i = 0; i < tokens.length; i++) { + uint256 allowanceAmount = + amounts[i] == type(uint256).max + ? IERC20(tokens[i]).balanceOf(address(this)) + : amounts[i]; + IERC20(tokens[i]).safeApprove(avocadoAddress, allowanceAmount); + } + + _eventName = "LogApproveTokens(address[],uint256[])"; + _eventParam = abi.encode(tokens, amounts); + } +} + +contract ConnectV2ApproveTokensPolygon is ApproveTokensResolver { + string constant public name = "ApproveTokens-v1"; +} \ No newline at end of file From b3956b5ddc5afa9b0c8e6a659844b518c20c9d9d Mon Sep 17 00:00:00 2001 From: Shriya Tyagi Date: Mon, 17 Apr 2023 13:17:49 +0400 Subject: [PATCH 03/16] Added makerdao import connector - production --- .../connectors/makerdao-import/events.sol | 6 +++ .../connectors/makerdao-import/helpers.sol | 34 ++++++++++++++ .../connectors/makerdao-import/interface.sol | 45 +++++++++++++++++++ .../connectors/makerdao-import/main.sol | 27 +++++++++++ package-lock.json | 20 +++++++++ 5 files changed, 132 insertions(+) create mode 100644 contracts/mainnet/connectors/makerdao-import/events.sol create mode 100644 contracts/mainnet/connectors/makerdao-import/helpers.sol create mode 100644 contracts/mainnet/connectors/makerdao-import/interface.sol create mode 100644 contracts/mainnet/connectors/makerdao-import/main.sol diff --git a/contracts/mainnet/connectors/makerdao-import/events.sol b/contracts/mainnet/connectors/makerdao-import/events.sol new file mode 100644 index 00000000..6f59d9ab --- /dev/null +++ b/contracts/mainnet/connectors/makerdao-import/events.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +contract Events { + event LogTransferToAvo(uint256 indexed vault, bytes32 indexed ilk, address indexed avoAddress); +} \ No newline at end of file diff --git a/contracts/mainnet/connectors/makerdao-import/helpers.sol b/contracts/mainnet/connectors/makerdao-import/helpers.sol new file mode 100644 index 00000000..1c261d28 --- /dev/null +++ b/contracts/mainnet/connectors/makerdao-import/helpers.sol @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import { DSMath } from "../../common/math.sol"; +import { Basic } from "../../common/basic.sol"; +import { TokenInterface } from "../../common/interfaces.sol"; +import { ManagerLike, VatLike, IAvoFactory, IAvoCreditManagerAddress } from "./interface.sol"; + +abstract contract Helpers is DSMath, Basic { + ManagerLike internal constant managerContract = ManagerLike(0x5ef30b9986345249bc32d8928B7ee64DE9435E39); + IAvoFactory internal constant avoFactory = IAvoFactory(0x3AdAE9699029AB2953F607AE1f62372681D35978); + IAvoCreditManagerAddress internal constant avoCreditManager = IAvoCreditManagerAddress(0xE4C9751D5CBCde942165871Ca2089172307F9971); + + function getVaultData(uint vault) internal view returns (bytes32 ilk, address urn) { + ilk = managerContract.ilks(vault); + urn = managerContract.urns(vault); + } + + function stringToBytes32(string memory str) internal pure returns (bytes32 result) { + require(bytes(str).length != 0, "string-empty"); + assembly { + result := mload(add(str, 32)) + } + } + + function getVault(uint vault) internal view returns (uint _vault) { + if (vault == 0) { + require(managerContract.count(address(this)) > 0, "no-vault-opened"); + _vault = managerContract.last(address(this)); + } else { + _vault = vault; + } + } +} \ No newline at end of file diff --git a/contracts/mainnet/connectors/makerdao-import/interface.sol b/contracts/mainnet/connectors/makerdao-import/interface.sol new file mode 100644 index 00000000..7d2d7534 --- /dev/null +++ b/contracts/mainnet/connectors/makerdao-import/interface.sol @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import { TokenInterface } from "../../common/interfaces.sol"; + +interface ManagerLike { + function cdpCan(address, uint, address) external view returns (uint); + function ilks(uint) external view returns (bytes32); + function last(address) external view returns (uint); + function count(address) external view returns (uint); + function owns(uint) external view returns (address); + function urns(uint) external view returns (address); + function vat() external view returns (address); + function open(bytes32, address) external returns (uint); + function give(uint, address) external; + function frob(uint, int, int) external; + function flux(uint, address, uint) external; + function move(uint, address, uint) external; +} + +interface VatLike { + function can(address, address) external view returns (uint); + function ilks(bytes32) external view returns (uint, uint, uint, uint, uint); + function dai(address) external view returns (uint); + function urns(bytes32, address) external view returns (uint, uint); + function frob( + bytes32, + address, + address, + address, + int, + int + ) external; + function hope(address) external; + function move(address, address, uint) external; + function gem(bytes32, address) external view returns (uint); +} + +interface IAvoFactory { + function computeAddress(address owner_) external view returns (address); +} + +interface IAvoCreditManagerAddress { + function dsaMakerImport(uint256 vaultId_, address avoSafe_, address auth_) external; +} \ No newline at end of file diff --git a/contracts/mainnet/connectors/makerdao-import/main.sol b/contracts/mainnet/connectors/makerdao-import/main.sol new file mode 100644 index 00000000..0ca17b80 --- /dev/null +++ b/contracts/mainnet/connectors/makerdao-import/main.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import { TokenInterface, AccountInterface } from "../../common/interfaces.sol"; +import { Helpers } from "./helpers.sol"; +import { Events } from "./events.sol"; + +contract MakerTransferResolver is Helpers, Events { + function transferToAvo(uint256 vaultId) public payable returns (string memory _eventName, bytes memory _eventParam) { + uint256 _vault = getVault(vaultId); + (bytes32 ilk,) = getVaultData(_vault); + + require(managerContract.owns(_vault) == address(this), "not-owner"); + + address avoAddress = avoFactory.computeAddress(msg.sender); + managerContract.give(_vault, avoAddress); + + avoCreditManager.dsaMakerImport(_vault, avoAddress, msg.sender); + + _eventName = "LogTransferToAvo(uint256,bytes32,address)"; + _eventParam = abi.encode(_vault, ilk, avoAddress); + } +} + +contract ConnectV2AvoMakerImport is MakerTransferResolver { + string public constant name = "Avocado-Maker-Import-v1.0"; +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index bc7ad39e..66958ac1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24015,6 +24015,20 @@ "node": ">=6" } }, + "node_modules/mocha/node_modules/fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "deprecated": "\"Please update to latest v2.3 or v2.2\"", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/mocha/node_modules/glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", @@ -49813,6 +49827,12 @@ "locate-path": "^3.0.0" } }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "optional": true + }, "glob": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", From d700a3d40761f57c42807f191cc9492846e6e6a5 Mon Sep 17 00:00:00 2001 From: q1q0 Date: Thu, 20 Apr 2023 16:48:45 -0400 Subject: [PATCH 04/16] add compoundV3 connector on Polygon --- .../polygon/connectors/compound/v3/main.sol | 4 +- scripts/tests/polygon/tokens.ts | 7 + test/polygon/compound/compound.iii.test.ts | 676 ++++++++++++++++++ 3 files changed, 684 insertions(+), 3 deletions(-) create mode 100644 test/polygon/compound/compound.iii.test.ts diff --git a/contracts/polygon/connectors/compound/v3/main.sol b/contracts/polygon/connectors/compound/v3/main.sol index 3a2dd6fb..3c9af8d7 100644 --- a/contracts/polygon/connectors/compound/v3/main.sol +++ b/contracts/polygon/connectors/compound/v3/main.sol @@ -62,7 +62,6 @@ abstract contract CompoundV3Contract is Events, Helpers { approve(tokenContract, market, amt_); CometInterface(market).supply(token_, amt_); - setUint(setId, amt_); eventName_ = "LogDeposit(address,address,uint256,uint256,uint256)"; @@ -412,7 +411,6 @@ abstract contract CompoundV3Contract is Events, Helpers { uint256 initialBal = CometInterface(market).borrowBalanceOf( address(this) ); - CometInterface(market).withdraw(token_, amt_); uint256 finalBal = CometInterface(market).borrowBalanceOf( @@ -930,5 +928,5 @@ abstract contract CompoundV3Contract is Events, Helpers { } contract ConnectV2CompoundV3Polygon is CompoundV3Contract { - string public name = "CompoundV3-v1.0"; + string public name = "CompoundV3-Polygon-v1.0"; } diff --git a/scripts/tests/polygon/tokens.ts b/scripts/tests/polygon/tokens.ts index 83b66b68..2b30e422 100644 --- a/scripts/tests/polygon/tokens.ts +++ b/scripts/tests/polygon/tokens.ts @@ -14,6 +14,13 @@ export const tokens = { address: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", decimals: 18, }, + wmatic: { + type: "token", + symbol: "WMATIC", + name: "Wrapped Matic", + address: "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", + decimals: 18, + }, eth: { type: "token", symbol: "ETH", diff --git a/test/polygon/compound/compound.iii.test.ts b/test/polygon/compound/compound.iii.test.ts new file mode 100644 index 00000000..3d16ee64 --- /dev/null +++ b/test/polygon/compound/compound.iii.test.ts @@ -0,0 +1,676 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { waffle, ethers } = hre; +const { provider, deployContract } = waffle; + +import { Signer, Contract } from "ethers"; +import { BigNumber } from "bignumber.js"; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/polygon/addresses"; +import { tokens, tokenMapping } from "../../../scripts/tests/polygon/tokens"; +// import { addresses } from "../../../scripts/tests/mainnet/addresses"; +// import { tokens, tokenMapping } from "../../../scripts/tests/mainnet/tokens"; +import { abis } from "../../../scripts/constant/abis"; +import { constants } from "../../../scripts/constant/constant"; +import { ConnectV2CompoundV3Polygon__factory } from "../../../typechain"; +import { ConnectV2CompoundV3__factory } from "../../../typechain"; +import { MaxUint256 } from "@uniswap/sdk-core"; +import Test from './Test.json' + +describe("Compound III", async function () { + const connectorName = "COMPOUND-POLYGON-V3-TEST-A"; + const market = "0xF25212E676D1F7F89Cd72fFEe66158f541246445"; + const base = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"; + // const market = "0xc3d688B66703497DAA19211EEdff47f25384cdc3"; + // const base = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; + const account = "0xf827c3E5fD68e78aa092245D442398E12988901C"; + const wethWhale = "0x06959153B974D0D5fDfd87D561db6d8d4FA0bb0B"; + + const ABI = [ + "function balanceOf(address account) public view returns (uint256)", + "function approve(address spender, uint256 amount) external returns(bool)", + "function transfer(address recipient, uint256 amount) external returns (bool)" + ]; + let wethContract: any; + let baseContract: any; + // const linkContract = new ethers.Contract(tokens.link.address, ABI); + + const cometABI = [ + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "balanceOf", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "borrowBalanceOf", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "baseBorrowMin", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "baseMinForRewards", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "baseToken", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "decimals", + outputs: [{ internalType: "uint8", name: "", type: "uint8" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "priceFeed", type: "address" }], + name: "getPrice", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "owner", type: "address" }, + { internalType: "address", name: "manager", type: "address" } + ], + name: "hasPermission", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "numAssets", + outputs: [{ internalType: "uint8", name: "", type: "uint8" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "asset", type: "address" }, + { internalType: "uint256", name: "baseAmount", type: "uint256" } + ], + name: "quoteCollateral", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "", type: "address" }], + name: "userBasic", + outputs: [ + { internalType: "int104", name: "principal", type: "int104" }, + { internalType: "uint64", name: "baseTrackingIndex", type: "uint64" }, + { internalType: "uint64", name: "baseTrackingAccrued", type: "uint64" }, + { internalType: "uint16", name: "assetsIn", type: "uint16" }, + { internalType: "uint8", name: "_reserved", type: "uint8" } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "", type: "address" }, + { internalType: "address", name: "", type: "address" } + ], + name: "userCollateral", + outputs: [ + { internalType: "uint128", name: "balance", type: "uint128" }, + { internalType: "uint128", name: "_reserved", type: "uint128" } + ], + stateMutability: "view", + type: "function" + } + ]; + + let dsaWallet0: any; + let dsaWallet1: any; + let dsaWallet2: any; + let dsaWallet3: any; + let wallet: any; + let dsa0Signer: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + let signer: any; + // let wallet0: any; + let wethSigner: any; + + let comet: any; + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + // blockNumber: 27054896 + } + } + ] + }); + masterSigner = await getMasterSigner(); + await hre.network.provider.send("hardhat_setBalance", [ + await masterSigner.getAddress(), + "0x3635c9adc5de9fffff", + ]); + // [wallet0] = await ethers.getSigners(); + + signer = await ethers.getSigner(account); + + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + // contractArtifact: ConnectV2CompoundV3__factory, + contractArtifact: ConnectV2CompoundV3Polygon__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + signer = await ethers.getSigner(account); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [wethWhale] + }); + wethSigner = await ethers.getSigner(wethWhale); + baseContract = new ethers.Contract(base, ABI, wethSigner) + wethContract = new ethers.Contract(tokens.wmatic.address, ABI, wethSigner) + comet = new ethers.Contract(market, cometABI, wethSigner) + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + dsaWallet1 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet1.address).to.be.true; + dsaWallet2 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet2.address).to.be.true; + dsaWallet3 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet3.address).to.be.true; + wallet = await ethers.getSigner(dsaWallet0.address); + expect(!!dsaWallet1.address).to.be.true; + + const bal = await baseContract.balanceOf(wethWhale) + console.log("+++++++++++++++++++++++++",bal.toString()) + await baseContract.connect(wethSigner).transfer(dsaWallet0.address, ethers.utils.parseUnits("1000", 6)); + await baseContract.connect(wethSigner).transfer(dsaWallet1.address, ethers.utils.parseUnits("1000", 6)); + }); + + describe("DSA wallet setup", function () { + it("Deposit ETH into DSA wallet", async function () { + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [wallet.address] + }); + + dsa0Signer = await ethers.getSigner(wallet.address); + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + await wallet0.sendTransaction({ + to: dsaWallet1.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + await wallet0.sendTransaction({ + to: dsaWallet3.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + //deposit asset + it("Should supply ETH collateral in Compound V3", async function () { + const amount = ethers.utils.parseEther("5"); // 5 ETH + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [market, tokens.matic.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("5")); + expect((await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.wmatic.address)).balance).to.be.gte( + ethers.utils.parseEther("5") + ); + }); + + //deposit asset on behalf of + it("Should supply ETH collateral on behalf of dsaWallet0 in Compound V3", async function () { + const amount = ethers.utils.parseEther("1"); // 1 ETH + const spells = [ + { + connector: connectorName, + method: "depositOnBehalf", + args: [market, tokens.matic.address, dsaWallet0.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet1.address)).to.be.lte(ethers.utils.parseEther("9")); + expect((await comet.connect(wallet0).userCollateral(dsaWallet0.address, tokens.wmatic.address)).balance).to.be.gte( + ethers.utils.parseEther("6") + ); + }); + + it("Should borrow and payback base token from Compound", async function () { + const amount = ethers.utils.parseUnits("150", 6); + const spells = [ + { + connector: connectorName, + method: "borrow", + args: [market, base, amount, 0, 0] + }, + { + connector: connectorName, + method: "payback", + args: [market, base, ethers.utils.parseUnits("50", 6), 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await comet.connect(wallet0).borrowBalanceOf(dsaWallet0.address)).to.be.equal( + ethers.utils.parseUnits("100", 6) + ); + expect(await baseContract.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.equal( + ethers.utils.parseUnits("100", 6) + ); + }); + + it("should allow manager for dsaWallet0's collateral and base", async function () { + const spells = [ + { + connector: connectorName, + method: "toggleAccountManager", + args: [market, dsaWallet2.address, true] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + }); + + it("should payback base token on Compound using manager", async function () { + await baseContract.connect(signer).transfer(dsaWallet0.address, ethers.utils.parseUnits("5", 6)); + + const amount = ethers.utils.parseUnits("102", 6); + await baseContract.connect(dsa0Signer).approve(market, amount); + + const spells = [ + { + connector: connectorName, + method: "paybackFromUsingManager", + args: [market, base, dsaWallet0.address, dsaWallet0.address, ethers.constants.MaxUint256, 0, 0] + } + ]; + + const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await comet.connect(signer).borrowBalanceOf(dsaWallet0.address)).to.be.equal( + ethers.utils.parseUnits("0", 6) + ); + }); + + it("Should borrow to another dsa from Compound", async function () { + const amount = ethers.utils.parseUnits("100", 6); + const spells = [ + { + connector: connectorName, + method: "borrowTo", + args: [market, base, dsaWallet1.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await comet.connect(signer).borrowBalanceOf(dsaWallet0.address)).toFixed()).to.be.equal( + ethers.utils.parseUnits("100", 6) + ); + }); + + it("Should payback on behalf of from Compound", async function () { + const spells = [ + { + connector: connectorName, + method: "paybackOnBehalf", + args: [market, base, dsaWallet0.address, ethers.constants.MaxUint256, 0, 0] + } + ]; + + const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await comet.connect(signer).borrowBalanceOf(dsaWallet0.address)).to.be.equal( + ethers.utils.parseUnits("0", 6) + ); + }); + + it("should withdraw some ETH collateral", async function () { + let initialBal = await ethers.provider.getBalance(dsaWallet0.address); + const amount_ = ethers.utils.parseEther("2"); + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: [market, tokens.matic.address, amount_, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect((await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.wmatic.address)).balance).to.be.gte( + ethers.utils.parseEther("4") + ); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(initialBal.add(amount_).toString()); + }); + + it("manager should be able to withdraw collateral from the position and transfer", async function () { + await wallet1.sendTransaction({ + to: tokens.wmatic.address, + value: ethers.utils.parseEther("10") + }); + const amount = ethers.constants.MaxUint256; + const spells = [ + { + connector: connectorName, + method: "withdrawOnBehalfAndTransfer", + args: [market, tokens.matic.address, dsaWallet0.address, dsaWallet1.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect((await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.wmatic.address)).balance).to.be.gte( + ethers.utils.parseEther("0") + ); + expect(await wethContract.connect(wallet0).balanceOf(dsaWallet1.address)).to.be.gte(ethers.utils.parseEther("4")); + }); + + it("Should withdraw collateral to another DSA", async function () { + const spells1 = [ + { + connector: connectorName, + method: "deposit", + args: [market, tokens.matic.address, ethers.utils.parseEther("5"), 0, 0] + } + ]; + + const tx1 = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells1), wallet1.address); + let initialBal = await ethers.provider.getBalance(dsaWallet0.address); + + const amount = ethers.utils.parseEther("2"); + const spells = [ + { + connector: connectorName, + method: "withdrawTo", + args: [market, tokens.matic.address, dsaWallet0.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await wethContract.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte(amount); + + expect((await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.wmatic.address)).balance).to.be.gte( + ethers.utils.parseEther("3") + ); + }); + + it("Should withdraw collateral to another DSA", async function () { + const spells1 = [ + { + connector: connectorName, + method: "deposit", + args: [market, tokens.matic.address, ethers.utils.parseEther("3"), 0, 0] + } + ]; + + const tx1 = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells1), wallet1.address); + let initialBal = await ethers.provider.getBalance(dsaWallet0.address); + + const amount = ethers.utils.parseEther("2"); + const spells = [ + { + connector: connectorName, + method: "withdrawTo", + args: [market, tokens.matic.address, dsaWallet0.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(initialBal.add(amount)); + + expect((await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.wmatic.address)).balance).to.be.gte( + ethers.utils.parseEther("1") + ); + }); + + it("should transfer eth from dsaWallet1 to dsaWallet0 position", async function () { + const spells = [ + { + connector: connectorName, + method: "transferAsset", + args: [market, tokens.matic.address, dsaWallet0.address, ethers.utils.parseEther("3"), 0, 0] + } + ]; + + const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect((await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.wmatic.address)).balance).to.be.gte( + ethers.utils.parseEther("0") + ); + expect((await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.wmatic.address)).balance).to.be.gte( + ethers.utils.parseEther("3") + ); + }); + + it("should transfer base token from dsaWallet1 to dsaWallet0 position", async function () { + await baseContract.connect(wethSigner).transfer(dsaWallet1.address, ethers.utils.parseUnits("10", 6)); + + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [market, base, ethers.constants.MaxUint256, 0, 0] + } + ]; + const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + let initialBal = await baseContract.connect(signer).balanceOf(dsaWallet1.address); + let spells1 = [ + { + connector: connectorName, + method: "transferAsset", + args: [market, base, dsaWallet0.address, ethers.constants.MaxUint256, 0, 0] + } + ]; + + const tx1 = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells1), wallet1.address); + const receipt1 = await tx.wait(); + expect(await comet.connect(signer).balanceOf(dsaWallet1.address)).to.be.lte(ethers.utils.parseUnits("0", 6)); + expect(await comet.connect(signer).balanceOf(dsaWallet0.address)).to.be.gte(initialBal); + }); + + it("should transfer base token using manager from dsaWallet0 to dsaWallet1 position", async function () { + const spells = [ + { + connector: connectorName, + method: "transferAssetOnBehalf", + args: [market, base, dsaWallet0.address, dsaWallet1.address, ethers.constants.MaxUint256, 0, 0] + } + ]; + let initialBal = await baseContract.connect(signer).balanceOf(dsaWallet0.address); + + const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await comet.connect(signer).balanceOf(dsaWallet0.address)).to.be.lte(ethers.utils.parseUnits("0", 6)); + expect(await comet.connect(signer).balanceOf(dsaWallet1.address)).to.be.gte(initialBal); + }); + + it("should deposit weth using manager", async function () { + await wethContract.connect(wethSigner).transfer(dsaWallet0.address, ethers.utils.parseEther("10")); + let initialBal = await wethContract.connect(wallet0).balanceOf(dsaWallet0.address); + + const amount = ethers.utils.parseEther("1"); + await wethContract.connect(dsa0Signer).approve(market, amount); + + const spells = [ + { + connector: connectorName, + method: "depositFromUsingManager", + args: [market, tokens.matic.address, dsaWallet0.address, dsaWallet1.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect((await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.wmatic.address)).balance).to.be.gte( + ethers.utils.parseEther("1") + ); + expect(await wethContract.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.lte(initialBal.sub(amount)); + }); + + it("should allow manager for dsaWallet0's collateral", async function () { + const spells = [ + { + connector: connectorName, + method: "toggleAccountManager", + args: [market, dsaWallet2.address, true] + } + ]; + + const tx = await dsaWallet3.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + }); + it("should borrow on behalf using manager", async function () { + let initialBal = await baseContract.connect(wallet0).balanceOf(dsaWallet0.address); + await wallet0.sendTransaction({ + to: dsaWallet3.address, + value: ethers.utils.parseEther("15") + }); + const spells1 = [ + { + connector: connectorName, + method: "deposit", + args: [market, tokens.matic.address, ethers.utils.parseEther("15"), 0, 0] + } + ]; + const tx1 = await dsaWallet3.connect(wallet0).cast(...encodeSpells(spells1), wallet1.address); + const amount = ethers.utils.parseUnits("500", 6); + const spells = [ + { + connector: connectorName, + method: "borrowOnBehalfAndTransfer", + args: [market, base, dsaWallet3.address, dsaWallet0.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await comet.connect(signer).borrowBalanceOf(dsaWallet3.address)).toFixed()).to.be.equal( + ethers.utils.parseUnits("500", 6) + ); + expect(await baseContract.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.equal(initialBal.add(amount)); + }); + + it("should transferAsset collateral using manager", async function () { + let bal1 = (await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.wmatic.address)).balance; + let bal0 = (await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.wmatic.address)).balance; + const spells = [ + { + connector: connectorName, + method: "transferAssetOnBehalf", + args: [market, tokens.matic.address, dsaWallet0.address, dsaWallet1.address, ethers.utils.parseEther("1"), 0, 0] + } + ]; + + const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect((await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.wmatic.address)).balance).to.be.gte( + bal1.add(ethers.utils.parseEther("1")).toString() + ); + expect((await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.wmatic.address)).balance).to.be.gte( + bal0.sub(ethers.utils.parseEther("1")).toString() + ); + }); + + //can buy only when target reserves not reached. + + // it("should buy collateral", async function () { + // //deposit 10 usdc(base token) to dsa + // await baseContract.connect(signer).transfer(dsaWallet0.address, ethers.utils.parseUnits("10", 6)); + // console.log(await baseContract.connect(signer).balanceOf(dsaWallet0.address)); + + // //dsawallet0 --> collateral 0eth, balance 9eth 10usdc + // //dsaWallet1 --> balance 2eth coll: 3eth + // const amount = ethers.utils.parseUnits("1",6); + // const bal = await baseContract.connect(signer).balanceOf(dsaWallet0.address); + // const spells = [ + // { + // connector: connectorName, + // method: "buyCollateral", + // args: [market, tokens.link.address, dsaWallet0.address, amount, bal, 0, 0] + // } + // ]; + + // const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + // const receipt = await tx.wait(); + // expect(new BigNumber(await linkContract.connect(signer).balanceOf(dsaWallet0.address)).toFixed()).to.be.gte( + // ethers.utils.parseEther("1") + // ); + + // //dsawallet0 --> collateral 0eth, balance 9eth >1link + // //dsaWallet1 --> balance 2eth coll: 3eth + // }); + }); +}); From f7633e3a8f988b59c7440e1dc2aee0684e9293ce Mon Sep 17 00:00:00 2001 From: q1q0 Date: Thu, 20 Apr 2023 16:49:51 -0400 Subject: [PATCH 05/16] update minor --- test/polygon/compound/compound.iii.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/polygon/compound/compound.iii.test.ts b/test/polygon/compound/compound.iii.test.ts index 3d16ee64..a15ab599 100644 --- a/test/polygon/compound/compound.iii.test.ts +++ b/test/polygon/compound/compound.iii.test.ts @@ -19,7 +19,6 @@ import { constants } from "../../../scripts/constant/constant"; import { ConnectV2CompoundV3Polygon__factory } from "../../../typechain"; import { ConnectV2CompoundV3__factory } from "../../../typechain"; import { MaxUint256 } from "@uniswap/sdk-core"; -import Test from './Test.json' describe("Compound III", async function () { const connectorName = "COMPOUND-POLYGON-V3-TEST-A"; @@ -232,7 +231,6 @@ describe("Compound III", async function () { expect(!!dsaWallet1.address).to.be.true; const bal = await baseContract.balanceOf(wethWhale) - console.log("+++++++++++++++++++++++++",bal.toString()) await baseContract.connect(wethSigner).transfer(dsaWallet0.address, ethers.utils.parseUnits("1000", 6)); await baseContract.connect(wethSigner).transfer(dsaWallet1.address, ethers.utils.parseUnits("1000", 6)); }); From bade2b66426e74d1bfb58a789817f279548ed928 Mon Sep 17 00:00:00 2001 From: Shriya Tyagi Date: Wed, 19 Apr 2023 20:06:43 +0400 Subject: [PATCH 06/16] Add ARB token claim connector --- .../arbitrum/connectors/arb-claim/events.sol | 23 +++++++ .../connectors/arb-claim/interface.sol | 39 ++++++++++++ .../arbitrum/connectors/arb-claim/main.sol | 60 +++++++++++++++++++ .../connectors/arb-claim/variables.sol | 20 +++++++ 4 files changed, 142 insertions(+) create mode 100644 contracts/arbitrum/connectors/arb-claim/events.sol create mode 100644 contracts/arbitrum/connectors/arb-claim/interface.sol create mode 100644 contracts/arbitrum/connectors/arb-claim/main.sol create mode 100644 contracts/arbitrum/connectors/arb-claim/variables.sol diff --git a/contracts/arbitrum/connectors/arb-claim/events.sol b/contracts/arbitrum/connectors/arb-claim/events.sol new file mode 100644 index 00000000..788fad86 --- /dev/null +++ b/contracts/arbitrum/connectors/arb-claim/events.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +contract Events { + event LogArbAirdropClaimed( + address indexed account, + uint256 indexed claimable, + uint256 setId + ); + + event LogArbTokensDelegated( + address indexed account, + address indexed delegatee, + uint256 indexed delegatedAmount + ); + + event LogArbTokensDelegatedBySig( + address indexed account, + address indexed delegatee, + uint256 indexed delegatedAmount, + uint256 nonce + ); +} diff --git a/contracts/arbitrum/connectors/arb-claim/interface.sol b/contracts/arbitrum/connectors/arb-claim/interface.sol new file mode 100644 index 00000000..a856d242 --- /dev/null +++ b/contracts/arbitrum/connectors/arb-claim/interface.sol @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +interface IArbitrumTokenDistributor { + function claim() external; + + function claimAndDelegate( + address delegatee, + uint256 expiry, + uint8 v, + bytes32 r, + bytes32 s + ) external; + + function claimableTokens(address) external view returns (uint256); +} + +interface IArbTokenContract { + function delegate(address delegatee) external; + + function delegateBySig( + address delegatee, + uint256 nonce, + uint256 expiry, + uint8 v, + bytes32 r, + bytes32 s + ) external; +} + +interface TokenInterface { + function approve(address, uint256) external; + function transfer(address, uint) external; + function transferFrom(address, address, uint) external; + function deposit() external payable; + function withdraw(uint) external; + function balanceOf(address) external view returns (uint); + function decimals() external view returns (uint); +} diff --git a/contracts/arbitrum/connectors/arb-claim/main.sol b/contracts/arbitrum/connectors/arb-claim/main.sol new file mode 100644 index 00000000..cda225ff --- /dev/null +++ b/contracts/arbitrum/connectors/arb-claim/main.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; +pragma experimental ABIEncoderV2; + +import "./variables.sol"; +import { Events } from "./events.sol"; +import { IArbitrumTokenDistributor } from "./interface.sol"; + +abstract contract ArbitrumAirdrop is Events, Variables { + function claimArbAirdrop(uint256 setId) + public + returns (string memory eventName_, bytes memory eventParam_) + { + uint256 claimable = claimableArbTokens(address(this)); + require(claimable > 0, "0-tokens-claimable"); + ARBITRUM_TOKEN_DISTRIBUTOR.claim(); + setUint(setId, claimable); + + eventName_ = "LogArbAirdropClaimed(address,uint256,uint256)"; + eventParam_ = abi.encode(address(this), claimable, setId); + } + + function delegateArbTokens(address delegatee) + public + returns (string memory eventName_, bytes memory eventParam_) + { + uint256 balance = TokenInterface(address(ARB_TOKEN_CONTRACT)).balanceOf(address(this)); + require(balance > 0, "no-balance-to-delegate"); + + ARB_TOKEN_CONTRACT.delegate(delegatee); + + eventName_ = "LogArbTokensDelegated(address,address,uint256)"; + eventParam_ = abi.encode(address(this), delegatee, balance); + } + + function delegateArbTokensBySig( + address delegatee, + uint256 nonce, + SignedPermits calldata permits + ) + public + returns (string memory eventName_, bytes memory eventParam_) + { + uint256 balance = TokenInterface(address(ARB_TOKEN_CONTRACT)).balanceOf(address(this)); + require(balance > 0, "no-balance-to-delegate"); + + ARB_TOKEN_CONTRACT.delegateBySig(delegatee, nonce, permits.expiry, permits.v, permits.r, permits.s); + + eventName_ = "LogArbTokensDelegatedBySig(address,address,uint256,uint256)"; + eventParam_ = abi.encode(address(this), delegatee, balance, nonce); + } + + function claimableArbTokens(address user) public view returns (uint256) { + return ARBITRUM_TOKEN_DISTRIBUTOR.claimableTokens(user); + } +} + +contract ConnectV2ArbitrumAirdrop is ArbitrumAirdrop { + string public name = "ArbitrumAirdrop-v1"; +} diff --git a/contracts/arbitrum/connectors/arb-claim/variables.sol b/contracts/arbitrum/connectors/arb-claim/variables.sol new file mode 100644 index 00000000..b03a6f75 --- /dev/null +++ b/contracts/arbitrum/connectors/arb-claim/variables.sol @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "./interface.sol"; +import { Basic } from "../../common/basic.sol"; + +contract Variables is Basic { + IArbitrumTokenDistributor public constant ARBITRUM_TOKEN_DISTRIBUTOR = + IArbitrumTokenDistributor(0x67a24CE4321aB3aF51c2D0a4801c3E111D88C9d9); + + IArbTokenContract public constant ARB_TOKEN_CONTRACT = + IArbTokenContract(0x912CE59144191C1204E64559FE8253a0e49E6548); + + struct SignedPermits { + uint8 v; + bytes32 r; + bytes32 s; + uint256 expiry; + } +} From a51382d1dad4003c72de2f4e1ca379716e4e82b3 Mon Sep 17 00:00:00 2001 From: Shriya Tyagi Date: Wed, 19 Apr 2023 20:10:27 +0400 Subject: [PATCH 07/16] Added claimAndDelegateArbAirdrop + prettier --- .../arbitrum/connectors/arb-claim/events.sol | 7 ++ .../connectors/arb-claim/interface.sol | 38 +++++++---- .../arbitrum/connectors/arb-claim/main.sol | 66 +++++++++++++------ 3 files changed, 78 insertions(+), 33 deletions(-) diff --git a/contracts/arbitrum/connectors/arb-claim/events.sol b/contracts/arbitrum/connectors/arb-claim/events.sol index 788fad86..8ecdd7b0 100644 --- a/contracts/arbitrum/connectors/arb-claim/events.sol +++ b/contracts/arbitrum/connectors/arb-claim/events.sol @@ -20,4 +20,11 @@ contract Events { uint256 indexed delegatedAmount, uint256 nonce ); + + event LogArbAirdropClaimedAndDelegated( + address indexed account, + address indexed delegatee, + uint256 indexed claimable, + uint256 setId + ); } diff --git a/contracts/arbitrum/connectors/arb-claim/interface.sol b/contracts/arbitrum/connectors/arb-claim/interface.sol index a856d242..3831a6cf 100644 --- a/contracts/arbitrum/connectors/arb-claim/interface.sol +++ b/contracts/arbitrum/connectors/arb-claim/interface.sol @@ -19,21 +19,31 @@ interface IArbTokenContract { function delegate(address delegatee) external; function delegateBySig( - address delegatee, - uint256 nonce, - uint256 expiry, - uint8 v, - bytes32 r, - bytes32 s - ) external; + address delegatee, + uint256 nonce, + uint256 expiry, + uint8 v, + bytes32 r, + bytes32 s + ) external; } interface TokenInterface { - function approve(address, uint256) external; - function transfer(address, uint) external; - function transferFrom(address, address, uint) external; - function deposit() external payable; - function withdraw(uint) external; - function balanceOf(address) external view returns (uint); - function decimals() external view returns (uint); + function approve(address, uint256) external; + + function transfer(address, uint256) external; + + function transferFrom( + address, + address, + uint256 + ) external; + + function deposit() external payable; + + function withdraw(uint256) external; + + function balanceOf(address) external view returns (uint256); + + function decimals() external view returns (uint256); } diff --git a/contracts/arbitrum/connectors/arb-claim/main.sol b/contracts/arbitrum/connectors/arb-claim/main.sol index cda225ff..9ec1b5e4 100644 --- a/contracts/arbitrum/connectors/arb-claim/main.sol +++ b/contracts/arbitrum/connectors/arb-claim/main.sol @@ -7,6 +7,10 @@ import { Events } from "./events.sol"; import { IArbitrumTokenDistributor } from "./interface.sol"; abstract contract ArbitrumAirdrop is Events, Variables { + function claimableArbTokens(address user) public view returns (uint256) { + return ARBITRUM_TOKEN_DISTRIBUTOR.claimableTokens(user); + } + function claimArbAirdrop(uint256 setId) public returns (string memory eventName_, bytes memory eventParam_) @@ -20,12 +24,14 @@ abstract contract ArbitrumAirdrop is Events, Variables { eventParam_ = abi.encode(address(this), claimable, setId); } - function delegateArbTokens(address delegatee) + function delegateArbTokens(address delegatee) public returns (string memory eventName_, bytes memory eventParam_) { - uint256 balance = TokenInterface(address(ARB_TOKEN_CONTRACT)).balanceOf(address(this)); - require(balance > 0, "no-balance-to-delegate"); + uint256 balance = TokenInterface(address(ARB_TOKEN_CONTRACT)).balanceOf( + address(this) + ); + require(balance > 0, "no-balance-to-delegate"); ARB_TOKEN_CONTRACT.delegate(delegatee); @@ -33,25 +39,47 @@ abstract contract ArbitrumAirdrop is Events, Variables { eventParam_ = abi.encode(address(this), delegatee, balance); } - function delegateArbTokensBySig( - address delegatee, - uint256 nonce, - SignedPermits calldata permits - ) - public - returns (string memory eventName_, bytes memory eventParam_) - { - uint256 balance = TokenInterface(address(ARB_TOKEN_CONTRACT)).balanceOf(address(this)); - require(balance > 0, "no-balance-to-delegate"); + function delegateArbTokensBySig( + address delegatee, + uint256 nonce, + SignedPermits calldata permits + ) public returns (string memory eventName_, bytes memory eventParam_) { + uint256 balance = TokenInterface(address(ARB_TOKEN_CONTRACT)).balanceOf( + address(this) + ); + require(balance > 0, "no-balance-to-delegate"); - ARB_TOKEN_CONTRACT.delegateBySig(delegatee, nonce, permits.expiry, permits.v, permits.r, permits.s); + ARB_TOKEN_CONTRACT.delegateBySig( + delegatee, + nonce, + permits.expiry, + permits.v, + permits.r, + permits.s + ); - eventName_ = "LogArbTokensDelegatedBySig(address,address,uint256,uint256)"; - eventParam_ = abi.encode(address(this), delegatee, balance, nonce); - } + eventName_ = "LogArbTokensDelegatedBySig(address,address,uint256,uint256)"; + eventParam_ = abi.encode(address(this), delegatee, balance, nonce); + } - function claimableArbTokens(address user) public view returns (uint256) { - return ARBITRUM_TOKEN_DISTRIBUTOR.claimableTokens(user); + function claimAndDelegateArbAirdrop( + address delegatee, + SignedPermits memory permits, + uint256 setId + ) public returns (string memory eventName_, bytes memory eventParam_) { + uint256 claimable = claimableArbTokens(address(this)); + require(claimable > 0, "0-tokens-claimable"); + ARBITRUM_TOKEN_DISTRIBUTOR.claimAndDelegate( + delegatee, + permits.expiry, + permits.v, + permits.r, + permits.s + ); + setUint(setId, claimable); + + eventName_ = "LogArbAirdropClaimedAndDelegated(address,address,uint256,uint256)"; + eventParam_ = abi.encode(address(this), delegatee, claimable, setId); } } From a85da7c8c4d51fc5df32b53a8bce730650132adf Mon Sep 17 00:00:00 2001 From: Shriya Tyagi Date: Wed, 19 Apr 2023 21:13:05 +0400 Subject: [PATCH 08/16] Add helpers contract + optimize --- .../arbitrum/connectors/arb-claim/events.sol | 11 +--- .../arbitrum/connectors/arb-claim/helpers.sol | 11 ++++ .../arbitrum/connectors/arb-claim/main.sol | 53 ++++--------------- .../connectors/arb-claim/variables.sol | 3 +- 4 files changed, 23 insertions(+), 55 deletions(-) create mode 100644 contracts/arbitrum/connectors/arb-claim/helpers.sol diff --git a/contracts/arbitrum/connectors/arb-claim/events.sol b/contracts/arbitrum/connectors/arb-claim/events.sol index 8ecdd7b0..ba1a1835 100644 --- a/contracts/arbitrum/connectors/arb-claim/events.sol +++ b/contracts/arbitrum/connectors/arb-claim/events.sol @@ -10,21 +10,12 @@ contract Events { event LogArbTokensDelegated( address indexed account, - address indexed delegatee, - uint256 indexed delegatedAmount + address indexed delegatee ); event LogArbTokensDelegatedBySig( address indexed account, address indexed delegatee, - uint256 indexed delegatedAmount, uint256 nonce ); - - event LogArbAirdropClaimedAndDelegated( - address indexed account, - address indexed delegatee, - uint256 indexed claimable, - uint256 setId - ); } diff --git a/contracts/arbitrum/connectors/arb-claim/helpers.sol b/contracts/arbitrum/connectors/arb-claim/helpers.sol new file mode 100644 index 00000000..0e91e30e --- /dev/null +++ b/contracts/arbitrum/connectors/arb-claim/helpers.sol @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.7.0; + +import "./variables.sol"; +import { Basic } from "../../common/basic.sol"; + +contract Helpers is Variables, Basic { + function claimableArbTokens(address user) internal view returns (uint256) { + return ARBITRUM_TOKEN_DISTRIBUTOR.claimableTokens(user); + } +} \ No newline at end of file diff --git a/contracts/arbitrum/connectors/arb-claim/main.sol b/contracts/arbitrum/connectors/arb-claim/main.sol index 9ec1b5e4..e1068821 100644 --- a/contracts/arbitrum/connectors/arb-claim/main.sol +++ b/contracts/arbitrum/connectors/arb-claim/main.sol @@ -2,16 +2,13 @@ pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; -import "./variables.sol"; +import "./helpers.sol"; import { Events } from "./events.sol"; import { IArbitrumTokenDistributor } from "./interface.sol"; -abstract contract ArbitrumAirdrop is Events, Variables { - function claimableArbTokens(address user) public view returns (uint256) { - return ARBITRUM_TOKEN_DISTRIBUTOR.claimableTokens(user); - } +abstract contract ArbitrumAirdrop is Events, Helpers { - function claimArbAirdrop(uint256 setId) + function claimAirdrop(uint256 setId) public returns (string memory eventName_, bytes memory eventParam_) { @@ -24,31 +21,21 @@ abstract contract ArbitrumAirdrop is Events, Variables { eventParam_ = abi.encode(address(this), claimable, setId); } - function delegateArbTokens(address delegatee) + function delegate(address delegatee) public returns (string memory eventName_, bytes memory eventParam_) { - uint256 balance = TokenInterface(address(ARB_TOKEN_CONTRACT)).balanceOf( - address(this) - ); - require(balance > 0, "no-balance-to-delegate"); - ARB_TOKEN_CONTRACT.delegate(delegatee); - eventName_ = "LogArbTokensDelegated(address,address,uint256)"; - eventParam_ = abi.encode(address(this), delegatee, balance); + eventName_ = "LogArbTokensDelegated(address,address)"; + eventParam_ = abi.encode(address(this), delegatee); } - function delegateArbTokensBySig( + function delegateBySig( address delegatee, uint256 nonce, SignedPermits calldata permits ) public returns (string memory eventName_, bytes memory eventParam_) { - uint256 balance = TokenInterface(address(ARB_TOKEN_CONTRACT)).balanceOf( - address(this) - ); - require(balance > 0, "no-balance-to-delegate"); - ARB_TOKEN_CONTRACT.delegateBySig( delegatee, nonce, @@ -58,31 +45,11 @@ abstract contract ArbitrumAirdrop is Events, Variables { permits.s ); - eventName_ = "LogArbTokensDelegatedBySig(address,address,uint256,uint256)"; - eventParam_ = abi.encode(address(this), delegatee, balance, nonce); - } - - function claimAndDelegateArbAirdrop( - address delegatee, - SignedPermits memory permits, - uint256 setId - ) public returns (string memory eventName_, bytes memory eventParam_) { - uint256 claimable = claimableArbTokens(address(this)); - require(claimable > 0, "0-tokens-claimable"); - ARBITRUM_TOKEN_DISTRIBUTOR.claimAndDelegate( - delegatee, - permits.expiry, - permits.v, - permits.r, - permits.s - ); - setUint(setId, claimable); - - eventName_ = "LogArbAirdropClaimedAndDelegated(address,address,uint256,uint256)"; - eventParam_ = abi.encode(address(this), delegatee, claimable, setId); + eventName_ = "LogArbTokensDelegatedBySig(address,address,uint256)"; + eventParam_ = abi.encode(address(this), delegatee, nonce); } } contract ConnectV2ArbitrumAirdrop is ArbitrumAirdrop { - string public name = "ArbitrumAirdrop-v1"; + string public constant name = "ArbitrumAirdrop-v1"; } diff --git a/contracts/arbitrum/connectors/arb-claim/variables.sol b/contracts/arbitrum/connectors/arb-claim/variables.sol index b03a6f75..4f08a35c 100644 --- a/contracts/arbitrum/connectors/arb-claim/variables.sol +++ b/contracts/arbitrum/connectors/arb-claim/variables.sol @@ -2,9 +2,8 @@ pragma solidity ^0.7.0; import "./interface.sol"; -import { Basic } from "../../common/basic.sol"; -contract Variables is Basic { +contract Variables { IArbitrumTokenDistributor public constant ARBITRUM_TOKEN_DISTRIBUTOR = IArbitrumTokenDistributor(0x67a24CE4321aB3aF51c2D0a4801c3E111D88C9d9); From dcb3a925cfeb4c539f62b0f22605954b341a8105 Mon Sep 17 00:00:00 2001 From: Shriya Tyagi Date: Thu, 20 Apr 2023 01:18:08 +0400 Subject: [PATCH 09/16] Added testcases --- test/arbitrum/arb-claim/test.ts | 56 +++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 test/arbitrum/arb-claim/test.ts diff --git a/test/arbitrum/arb-claim/test.ts b/test/arbitrum/arb-claim/test.ts new file mode 100644 index 00000000..eaab4143 --- /dev/null +++ b/test/arbitrum/arb-claim/test.ts @@ -0,0 +1,56 @@ +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import { ConnectV2ArbitrumAirdrop, ConnectV2ArbitrumAirdrop__factory } from "../../../typechain"; +import hre, { ethers } from "hardhat"; + +describe("Arbitrum Airdrop Claim Test", () => { + let signer: SignerWithAddress; + let userSigner: SignerWithAddress; + let signer_user: any; + const user = "0x30c3D961a21c2352A6FfAfFd4e8cB8730Bf82757"; + + describe("Arbitrum Airdrop Functions", () => { + let contract: ConnectV2ArbitrumAirdrop; + + //@ts-ignore + console.log('hre.config.networks.hardhat.forking.url: ', hre.config.networks.hardhat.forking.url) + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 70606643 + } + } + ] + }); + + [signer] = await ethers.getSigners(); + console.log("Signer: ", signer.address); + + userSigner = await ethers.getSigner(user); + + const deployer = new ConnectV2ArbitrumAirdrop__factory(signer); + contract = await deployer.deploy(); + await contract.deployed(); + console.log("Contract deployed at: ", contract.address); + + await hre.network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [user], + }); + + signer_user = await ethers.getSigner(user); + }); + + describe("Arbitrum Arbitrum", async () => { + it("Claims Arbitrum Airdrop", async () => { + const response = await contract.connect(signer_user).claimAirdrop("4256"); + console.log('response: ', response); + }); + }) + }); +}); \ No newline at end of file From 6bc432be52cc9d9b191ed7a00811235795d60aea Mon Sep 17 00:00:00 2001 From: Shriya Tyagi Date: Thu, 20 Apr 2023 01:27:07 +0400 Subject: [PATCH 10/16] Added NatSpec comments --- contracts/arbitrum/connectors/arb-claim/main.sol | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/contracts/arbitrum/connectors/arb-claim/main.sol b/contracts/arbitrum/connectors/arb-claim/main.sol index e1068821..27f0600f 100644 --- a/contracts/arbitrum/connectors/arb-claim/main.sol +++ b/contracts/arbitrum/connectors/arb-claim/main.sol @@ -8,6 +8,10 @@ import { IArbitrumTokenDistributor } from "./interface.sol"; abstract contract ArbitrumAirdrop is Events, Helpers { + /** + * @dev DSA Arbitrum airdrop claim function. + * @param setId ID to set the claimable amount in the DSA + */ function claimAirdrop(uint256 setId) public returns (string memory eventName_, bytes memory eventParam_) @@ -21,6 +25,10 @@ abstract contract ArbitrumAirdrop is Events, Helpers { eventParam_ = abi.encode(address(this), claimable, setId); } + /** + * @dev Delegates votes from signer to `delegatee`. + * @param delegatee The address to delegate the ARB tokens to. + */ function delegate(address delegatee) public returns (string memory eventName_, bytes memory eventParam_) @@ -31,6 +39,12 @@ abstract contract ArbitrumAirdrop is Events, Helpers { eventParam_ = abi.encode(address(this), delegatee); } + /** + * @dev Delegates votes from signer to `delegatee`. + * @param delegatee The address to delegate the ARB tokens to. + * @param nonce The nonce number. + * @param permits The struct containing signed permit data like v,r,s,expiry. + */ function delegateBySig( address delegatee, uint256 nonce, From a800c3b197f8561579c388dd042b0a81d7054957 Mon Sep 17 00:00:00 2001 From: Shriya Tyagi Date: Thu, 20 Apr 2023 15:56:45 +0400 Subject: [PATCH 11/16] Update test --- test/arbitrum/arb-claim/test.ts | 64 ++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/test/arbitrum/arb-claim/test.ts b/test/arbitrum/arb-claim/test.ts index eaab4143..734fc065 100644 --- a/test/arbitrum/arb-claim/test.ts +++ b/test/arbitrum/arb-claim/test.ts @@ -1,19 +1,28 @@ import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import { ethers } from "hardhat"; import { ConnectV2ArbitrumAirdrop, ConnectV2ArbitrumAirdrop__factory } from "../../../typechain"; -import hre, { ethers } from "hardhat"; +import hre from "hardhat"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/arbitrum/addresses"; +import { abis } from "../../../scripts/constant/abis"; describe("Arbitrum Airdrop Claim Test", () => { let signer: SignerWithAddress; - let userSigner: SignerWithAddress; let signer_user: any; const user = "0x30c3D961a21c2352A6FfAfFd4e8cB8730Bf82757"; + const connectorName = "arbitrum-airdrop"; + let dsaWallet0: any; + + before(async () => { + [signer] = await ethers.getSigners(); + }); describe("Arbitrum Airdrop Functions", () => { let contract: ConnectV2ArbitrumAirdrop; - //@ts-ignore - console.log('hre.config.networks.hardhat.forking.url: ', hre.config.networks.hardhat.forking.url) - before(async () => { await hre.network.provider.request({ method: "hardhat_reset", @@ -22,35 +31,50 @@ describe("Arbitrum Airdrop Claim Test", () => { forking: { //@ts-ignore jsonRpcUrl: hre.config.networks.hardhat.forking.url, - blockNumber: 70606643 - } - } - ] + blockNumber: 70606643, + }, + }, + ], }); - [signer] = await ethers.getSigners(); - console.log("Signer: ", signer.address); - - userSigner = await ethers.getSigner(user); - const deployer = new ConnectV2ArbitrumAirdrop__factory(signer); contract = await deployer.deploy(); await contract.deployed(); console.log("Contract deployed at: ", contract.address); + await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2ArbitrumAirdrop__factory, + signer: signer, + connectors: await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2), + }); + await hre.network.provider.request({ method: 'hardhat_impersonateAccount', params: [user], }); signer_user = await ethers.getSigner(user); + dsaWallet0 = await buildDSAv2(user); }); - describe("Arbitrum Arbitrum", async () => { - it("Claims Arbitrum Airdrop", async () => { - const response = await contract.connect(signer_user).claimAirdrop("4256"); - console.log('response: ', response); - }); - }) + it("Claims Arbitrum Airdrop and checks claimable tokens", async () => { + const claimableBefore = await contract.claimableArbTokens(user); + console.log("Claimable tokens before: ", claimableBefore.toString()); + + const spells = [ + { + connector: connectorName, + method: "claimAirdrop", + args: ["0"], + }, + ]; + + const tx = await dsaWallet0.connect(signer_user).cast(...encodeSpells(spells), user); + await tx.wait(); + + const claimableAfter = await contract.claimableArbTokens(user); + console.log("Claimable tokens after: ", claimableAfter.toString()); + }); }); }); \ No newline at end of file From 2d2f9d01b0d23db8fa5ffb4398224d3f7bcbf7db Mon Sep 17 00:00:00 2001 From: Shriya Tyagi Date: Thu, 20 Apr 2023 16:55:57 +0400 Subject: [PATCH 12/16] Remove delegateBySig --- .../arbitrum/connectors/arb-claim/events.sol | 6 ----- .../arbitrum/connectors/arb-claim/helpers.sol | 2 +- .../arbitrum/connectors/arb-claim/main.sol | 27 ++----------------- .../connectors/arb-claim/variables.sol | 7 ----- 4 files changed, 3 insertions(+), 39 deletions(-) diff --git a/contracts/arbitrum/connectors/arb-claim/events.sol b/contracts/arbitrum/connectors/arb-claim/events.sol index ba1a1835..6d220d43 100644 --- a/contracts/arbitrum/connectors/arb-claim/events.sol +++ b/contracts/arbitrum/connectors/arb-claim/events.sol @@ -12,10 +12,4 @@ contract Events { address indexed account, address indexed delegatee ); - - event LogArbTokensDelegatedBySig( - address indexed account, - address indexed delegatee, - uint256 nonce - ); } diff --git a/contracts/arbitrum/connectors/arb-claim/helpers.sol b/contracts/arbitrum/connectors/arb-claim/helpers.sol index 0e91e30e..4eb872de 100644 --- a/contracts/arbitrum/connectors/arb-claim/helpers.sol +++ b/contracts/arbitrum/connectors/arb-claim/helpers.sol @@ -5,7 +5,7 @@ import "./variables.sol"; import { Basic } from "../../common/basic.sol"; contract Helpers is Variables, Basic { - function claimableArbTokens(address user) internal view returns (uint256) { + function claimableArbTokens(address user) public view returns (uint256) { return ARBITRUM_TOKEN_DISTRIBUTOR.claimableTokens(user); } } \ No newline at end of file diff --git a/contracts/arbitrum/connectors/arb-claim/main.sol b/contracts/arbitrum/connectors/arb-claim/main.sol index 27f0600f..f908b8b1 100644 --- a/contracts/arbitrum/connectors/arb-claim/main.sol +++ b/contracts/arbitrum/connectors/arb-claim/main.sol @@ -17,7 +17,8 @@ abstract contract ArbitrumAirdrop is Events, Helpers { returns (string memory eventName_, bytes memory eventParam_) { uint256 claimable = claimableArbTokens(address(this)); - require(claimable > 0, "0-tokens-claimable"); + + // If claimable <= 0, ARB TokenDistributor will revert on claim. ARBITRUM_TOKEN_DISTRIBUTOR.claim(); setUint(setId, claimable); @@ -38,30 +39,6 @@ abstract contract ArbitrumAirdrop is Events, Helpers { eventName_ = "LogArbTokensDelegated(address,address)"; eventParam_ = abi.encode(address(this), delegatee); } - - /** - * @dev Delegates votes from signer to `delegatee`. - * @param delegatee The address to delegate the ARB tokens to. - * @param nonce The nonce number. - * @param permits The struct containing signed permit data like v,r,s,expiry. - */ - function delegateBySig( - address delegatee, - uint256 nonce, - SignedPermits calldata permits - ) public returns (string memory eventName_, bytes memory eventParam_) { - ARB_TOKEN_CONTRACT.delegateBySig( - delegatee, - nonce, - permits.expiry, - permits.v, - permits.r, - permits.s - ); - - eventName_ = "LogArbTokensDelegatedBySig(address,address,uint256)"; - eventParam_ = abi.encode(address(this), delegatee, nonce); - } } contract ConnectV2ArbitrumAirdrop is ArbitrumAirdrop { diff --git a/contracts/arbitrum/connectors/arb-claim/variables.sol b/contracts/arbitrum/connectors/arb-claim/variables.sol index 4f08a35c..fb3e29d5 100644 --- a/contracts/arbitrum/connectors/arb-claim/variables.sol +++ b/contracts/arbitrum/connectors/arb-claim/variables.sol @@ -9,11 +9,4 @@ contract Variables { IArbTokenContract public constant ARB_TOKEN_CONTRACT = IArbTokenContract(0x912CE59144191C1204E64559FE8253a0e49E6548); - - struct SignedPermits { - uint8 v; - bytes32 r; - bytes32 s; - uint256 expiry; - } } From 991770f8f21a53bb737c0dc08200abe46dceedcf Mon Sep 17 00:00:00 2001 From: q1q0 Date: Thu, 20 Apr 2023 17:05:47 -0400 Subject: [PATCH 13/16] remove comments --- test/polygon/compound/compound.iii.test.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/test/polygon/compound/compound.iii.test.ts b/test/polygon/compound/compound.iii.test.ts index a15ab599..dc8a2c46 100644 --- a/test/polygon/compound/compound.iii.test.ts +++ b/test/polygon/compound/compound.iii.test.ts @@ -12,8 +12,6 @@ import { encodeSpells } from "../../../scripts/tests/encodeSpells"; import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; import { addresses } from "../../../scripts/tests/polygon/addresses"; import { tokens, tokenMapping } from "../../../scripts/tests/polygon/tokens"; -// import { addresses } from "../../../scripts/tests/mainnet/addresses"; -// import { tokens, tokenMapping } from "../../../scripts/tests/mainnet/tokens"; import { abis } from "../../../scripts/constant/abis"; import { constants } from "../../../scripts/constant/constant"; import { ConnectV2CompoundV3Polygon__factory } from "../../../typechain"; @@ -24,8 +22,6 @@ describe("Compound III", async function () { const connectorName = "COMPOUND-POLYGON-V3-TEST-A"; const market = "0xF25212E676D1F7F89Cd72fFEe66158f541246445"; const base = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"; - // const market = "0xc3d688B66703497DAA19211EEdff47f25384cdc3"; - // const base = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; const account = "0xf827c3E5fD68e78aa092245D442398E12988901C"; const wethWhale = "0x06959153B974D0D5fDfd87D561db6d8d4FA0bb0B"; @@ -36,7 +32,6 @@ describe("Compound III", async function () { ]; let wethContract: any; let baseContract: any; - // const linkContract = new ethers.Contract(tokens.link.address, ABI); const cometABI = [ { @@ -153,7 +148,6 @@ describe("Compound III", async function () { let instaConnectorsV2: Contract; let connector: any; let signer: any; - // let wallet0: any; let wethSigner: any; let comet: any; @@ -179,14 +173,12 @@ describe("Compound III", async function () { await masterSigner.getAddress(), "0x3635c9adc5de9fffff", ]); - // [wallet0] = await ethers.getSigners(); signer = await ethers.getSigner(account); instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); connector = await deployAndEnableConnector({ connectorName, - // contractArtifact: ConnectV2CompoundV3__factory, contractArtifact: ConnectV2CompoundV3Polygon__factory, signer: masterSigner, connectors: instaConnectorsV2 From c9b2208ce0e064051703c46fc0a723b776b81e25 Mon Sep 17 00:00:00 2001 From: q1q0 Date: Thu, 20 Apr 2023 17:52:24 -0400 Subject: [PATCH 14/16] add compound polygon reward test script --- .../compound/v3-rewards/helpers.sol | 2 +- .../connectors/compound/v3-rewards/main.sol | 4 +- .../compound/compound.iii.rewards.test.ts | 448 ++++++++++++++++++ test/polygon/compound/compound.iii.test.ts | 1 - 4 files changed, 451 insertions(+), 4 deletions(-) create mode 100644 test/polygon/compound/compound.iii.rewards.test.ts diff --git a/contracts/polygon/connectors/compound/v3-rewards/helpers.sol b/contracts/polygon/connectors/compound/v3-rewards/helpers.sol index 769eb8aa..6b3cd72a 100644 --- a/contracts/polygon/connectors/compound/v3-rewards/helpers.sol +++ b/contracts/polygon/connectors/compound/v3-rewards/helpers.sol @@ -9,5 +9,5 @@ import { CometRewards } from "./interface.sol"; abstract contract Helpers is DSMath, Basic { CometRewards internal constant cometRewards = - CometRewards(0x1B0e765F6224C21223AeA2af16c1C46E38885a40); + CometRewards(0x45939657d1CA34A8FA39A924B71D28Fe8431e581); } diff --git a/contracts/polygon/connectors/compound/v3-rewards/main.sol b/contracts/polygon/connectors/compound/v3-rewards/main.sol index 94404856..af702b29 100644 --- a/contracts/polygon/connectors/compound/v3-rewards/main.sol +++ b/contracts/polygon/connectors/compound/v3-rewards/main.sol @@ -63,6 +63,6 @@ abstract contract CompoundV3RewardsResolver is Events, Helpers { } } -contract ConnectV2CompoundV3Rewards is CompoundV3RewardsResolver { - string public name = "CompoundV3Rewards-v1.0"; +contract ConnectV2CompoundV3PolygonRewards is CompoundV3RewardsResolver { + string public name = "CompoundV3-Polygon-Rewards-v1.0"; } diff --git a/test/polygon/compound/compound.iii.rewards.test.ts b/test/polygon/compound/compound.iii.rewards.test.ts new file mode 100644 index 00000000..08e688b0 --- /dev/null +++ b/test/polygon/compound/compound.iii.rewards.test.ts @@ -0,0 +1,448 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { waffle, ethers } = hre; +const { provider, deployContract } = waffle; + +import { Signer, Contract } from "ethers"; +import { BigNumber } from "bignumber.js"; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/polygon/addresses"; +import { tokens, tokenMapping } from "../../../scripts/tests/polygon/tokens"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2CompoundV3PolygonRewards__factory, ConnectV2CompoundV3Polygon__factory } from "../../../typechain"; + +describe("Compound III Rewards", function () { + let connectorName = "COMPOUND-V3-REWARDS-TEST-A"; + const market = "0xF25212E676D1F7F89Cd72fFEe66158f541246445"; + const rewards = "0x45939657d1CA34A8FA39A924B71D28Fe8431e581"; + const base = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"; + const account = "0xf827c3E5fD68e78aa092245D442398E12988901C"; + const wethWhale = "0xadbF1854e5883eB8aa7BAf50705338739e558E5b"; + const baseWhale = "0x72a53cdbbcc1b9efa39c834a540550e23463aacb"; + + const ABI = [ + "function balanceOf(address account) public view returns (uint256)", + "function approve(address spender, uint256 amount) external returns(bool)", + "function transfer(address recipient, uint256 amount) external returns (bool)" + ]; + const wethContract = new ethers.Contract(tokens.wmatic.address, ABI); + const baseContract = new ethers.Contract(base, ABI); + + const cometABI = [ + { + inputs: [ + { internalType: "address", name: "comet", type: "address" }, + { internalType: "address", name: "src", type: "address" }, + { internalType: "bool", name: "shouldAccrue", type: "bool" } + ], + name: "claim", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "comet", type: "address" }, + { internalType: "address", name: "src", type: "address" }, + { internalType: "address", name: "to", type: "address" }, + { internalType: "bool", name: "shouldAccrue", type: "bool" } + ], + name: "claimTo", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "comet", type: "address" }, + { internalType: "address", name: "account", type: "address" } + ], + name: "getRewardOwed", + outputs: [ + { + components: [ + { internalType: "address", name: "token", type: "address" }, + { internalType: "uint256", name: "owed", type: "uint256" } + ], + internalType: "struct CometRewards.RewardOwed", + name: "", + type: "tuple" + } + ], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "", type: "address" }], + name: "rewardConfig", + outputs: [ + { internalType: "address", name: "token", type: "address" }, + { internalType: "uint64", name: "rescaleFactor", type: "uint64" }, + { internalType: "bool", name: "shouldUpscale", type: "bool" } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "", type: "address" }, + { internalType: "address", name: "", type: "address" } + ], + name: "rewardsClaimed", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + } + ]; + + const marketABI = [ + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "balanceOf", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "borrowBalanceOf", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "baseBorrowMin", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "baseMinForRewards", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "baseToken", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "decimals", + outputs: [{ internalType: "uint8", name: "", type: "uint8" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "priceFeed", type: "address" }], + name: "getPrice", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "owner", type: "address" }, + { internalType: "address", name: "manager", type: "address" } + ], + name: "hasPermission", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "numAssets", + outputs: [{ internalType: "uint8", name: "", type: "uint8" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "asset", type: "address" }, + { internalType: "uint256", name: "baseAmount", type: "uint256" } + ], + name: "quoteCollateral", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "", type: "address" }], + name: "userBasic", + outputs: [ + { internalType: "int104", name: "principal", type: "int104" }, + { internalType: "uint64", name: "baseTrackingIndex", type: "uint64" }, + { internalType: "uint64", name: "baseTrackingAccrued", type: "uint64" }, + { internalType: "uint16", name: "assetsIn", type: "uint16" }, + { internalType: "uint8", name: "_reserved", type: "uint8" } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "", type: "address" }, + { internalType: "address", name: "", type: "address" } + ], + name: "userCollateral", + outputs: [ + { internalType: "uint128", name: "balance", type: "uint128" }, + { internalType: "uint128", name: "_reserved", type: "uint128" } + ], + stateMutability: "view", + type: "function" + } + ]; + + let dsaWallet0: any; + let dsaWallet1: any; + let wallet: any; + let dsa0Signer: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + let connectorMain: any; + let signer: any; + let wethSigner: any; + let usdcSigner: any; + + const cometReward = new ethers.Contract(rewards, cometABI); + const comet = new ethers.Contract(market, marketABI); + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + // blockNumber: 15444500 + } + } + ] + }); + masterSigner = await getMasterSigner(); + await hre.network.provider.send("hardhat_setBalance", [ + await masterSigner.getAddress(), + "0x3635c9adc5de9fffff", + ]); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2CompoundV3PolygonRewards__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + signer = await ethers.getSigner(account); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [wethWhale] + }); + wethSigner = await ethers.getSigner(wethWhale); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [baseWhale] + }); + usdcSigner = await ethers.getSigner(baseWhale); + await hre.network.provider.send("hardhat_setBalance", [ + usdcSigner.address, + ethers.utils.parseEther("10").toHexString() + ]); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + dsaWallet1 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet1.address).to.be.true; + wallet = await ethers.getSigner(dsaWallet0.address); + expect(!!dsaWallet1.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [wallet.address] + }); + + dsa0Signer = await ethers.getSigner(wallet.address); + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + await wallet0.sendTransaction({ + to: dsaWallet1.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet1.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + + it("should deposit USDC in dsa wallet", async function () { + await baseContract.connect(usdcSigner).transfer(dsaWallet0.address, ethers.utils.parseUnits("500", 6)); + + expect(await baseContract.connect(usdcSigner).balanceOf(dsaWallet0.address)).to.be.gte( + ethers.utils.parseUnits("500", 6) + ); + }); + }); + + describe("Main", function () { + //deposit asset + it("Should supply USDC in Compound V3", async function () { + connectorName = "COMPOUND-V3-TEST-A"; + connectorMain = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2CompoundV3Polygon__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + const amount = ethers.utils.parseUnits("400", 6); + const spells = [ + { + connector: "COMPOUND-V3-TEST-A", + method: "deposit", + args: [market, base, amount, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await baseContract.connect(signer).balanceOf(dsaWallet0.address)).toFixed(0)).to.be.lte( + ethers.utils.parseUnits("100", 6) + ); + expect(new BigNumber(await comet.connect(signer).balanceOf(dsaWallet0.address)).toFixed(0)).to.be.gte( + ethers.utils.parseUnits("399", 6) + ); + }); + + let connector_ = "COMPOUND-V3-REWARDS-TEST-A"; + it("Should claim rewards", async function () { + let reward = (await cometReward.connect(signer).rewardConfig(market)).token; + let rewardInterface = new ethers.Contract(reward, ABI); + let owed_ = await cometReward.connect(signer).callStatic.getRewardOwed(market, dsaWallet0.address); + let amt: number = owed_.owed; + console.log(new BigNumber(amt).toFixed(0)); + const spells = [ + { + connector: connector_, + method: "claimRewards", + args: [market, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await rewardInterface.connect(signer).balanceOf(dsaWallet0.address)).toFixed(0)).to.be.gte( + amt + ); + }); + + it("Should supply USDC in Compound V3 through dsaWallet0", async function () { + const amount = ethers.utils.parseUnits("100", 6); // 1 ETH + const spells = [ + { + connector: "COMPOUND-V3-TEST-A", + method: "deposit", + args: [market, base, amount, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await baseContract.connect(signer).balanceOf(dsaWallet0.address)).toFixed(0)).to.be.lte( + ethers.utils.parseUnits("0", 6) + ); + expect(new BigNumber(await comet.connect(signer).balanceOf(dsaWallet0.address)).toFixed(0)).to.be.gte( + ethers.utils.parseUnits("499", 6) + ); + }); + + it("Should claim rewards to dsa1", async function () { + let reward = (await cometReward.connect(signer).rewardConfig(market)).token; + let rewardInterface = new ethers.Contract(reward, ABI); + let owed_ = await cometReward.connect(signer).callStatic.getRewardOwed(market, dsaWallet0.address); + let amt: number = owed_.owed; + + const spells = [ + { + connector: connector_, + method: "claimRewardsOnBehalfOf", + args: [market, dsaWallet0.address, dsaWallet1.address, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await rewardInterface.connect(signer).balanceOf(dsaWallet1.address)).toFixed(0)).to.be.gte( + amt + ); + }); + + it("should allow manager for dsaWallet0's collateral and base", async function () { + const spells = [ + { + connector: connectorName, + method: "toggleAccountManager", + args: [market, dsaWallet1.address, true] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + }); + + it("Should claim rewards to dsa1 using manager", async function () { + let reward = (await cometReward.connect(signer).rewardConfig(market)).token; + let rewardInterface = new ethers.Contract(reward, ABI); + let owed_ = await cometReward.connect(signer).callStatic.getRewardOwed(market, dsaWallet0.address); + let amt: number = owed_.owed; + + const spells = [ + { + connector: connector_, + method: "claimRewardsOnBehalfOf", + args: [market, dsaWallet0.address, dsaWallet1.address, 0] + } + ]; + + const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await rewardInterface.connect(signer).balanceOf(dsaWallet1.address)).toFixed(0)).to.be.gte( + amt + ); + }); + }); +}); diff --git a/test/polygon/compound/compound.iii.test.ts b/test/polygon/compound/compound.iii.test.ts index dc8a2c46..f1daf2bc 100644 --- a/test/polygon/compound/compound.iii.test.ts +++ b/test/polygon/compound/compound.iii.test.ts @@ -15,7 +15,6 @@ import { tokens, tokenMapping } from "../../../scripts/tests/polygon/tokens"; import { abis } from "../../../scripts/constant/abis"; import { constants } from "../../../scripts/constant/constant"; import { ConnectV2CompoundV3Polygon__factory } from "../../../typechain"; -import { ConnectV2CompoundV3__factory } from "../../../typechain"; import { MaxUint256 } from "@uniswap/sdk-core"; describe("Compound III", async function () { From 266d62622405e1b60ee7f6105f927a9bd512ccd2 Mon Sep 17 00:00:00 2001 From: q1q0 Date: Sun, 23 Apr 2023 09:51:09 -0400 Subject: [PATCH 15/16] update script file --- test/polygon/compound/compound.iii.test.ts | 98 +++++++++++----------- 1 file changed, 47 insertions(+), 51 deletions(-) diff --git a/test/polygon/compound/compound.iii.test.ts b/test/polygon/compound/compound.iii.test.ts index f1daf2bc..ca490f66 100644 --- a/test/polygon/compound/compound.iii.test.ts +++ b/test/polygon/compound/compound.iii.test.ts @@ -21,7 +21,7 @@ describe("Compound III", async function () { const connectorName = "COMPOUND-POLYGON-V3-TEST-A"; const market = "0xF25212E676D1F7F89Cd72fFEe66158f541246445"; const base = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"; - const account = "0xf827c3E5fD68e78aa092245D442398E12988901C"; + const account = "0xF25212E676D1F7F89Cd72fFEe66158f541246445"; const wethWhale = "0x06959153B974D0D5fDfd87D561db6d8d4FA0bb0B"; const ABI = [ @@ -29,7 +29,7 @@ describe("Compound III", async function () { "function approve(address spender, uint256 amount) external returns(bool)", "function transfer(address recipient, uint256 amount) external returns (bool)" ]; - let wethContract: any; + let wmaticContract: any; let baseContract: any; const cometABI = [ @@ -162,7 +162,7 @@ describe("Compound III", async function () { forking: { //@ts-ignore jsonRpcUrl: hre.config.networks.hardhat.forking.url, - // blockNumber: 27054896 + // blockNumber: 41860206 } } ] @@ -199,7 +199,7 @@ describe("Compound III", async function () { }); wethSigner = await ethers.getSigner(wethWhale); baseContract = new ethers.Contract(base, ABI, wethSigner) - wethContract = new ethers.Contract(tokens.wmatic.address, ABI, wethSigner) + wmaticContract = new ethers.Contract(tokens.wmatic.address, ABI, wethSigner) comet = new ethers.Contract(market, cometABI, wethSigner) }); @@ -220,14 +220,10 @@ describe("Compound III", async function () { expect(!!dsaWallet3.address).to.be.true; wallet = await ethers.getSigner(dsaWallet0.address); expect(!!dsaWallet1.address).to.be.true; - - const bal = await baseContract.balanceOf(wethWhale) - await baseContract.connect(wethSigner).transfer(dsaWallet0.address, ethers.utils.parseUnits("1000", 6)); - await baseContract.connect(wethSigner).transfer(dsaWallet1.address, ethers.utils.parseUnits("1000", 6)); }); describe("DSA wallet setup", function () { - it("Deposit ETH into DSA wallet", async function () { + it("Deposit Matic into DSA wallet", async function () { await hre.network.provider.request({ method: "hardhat_impersonateAccount", params: [wallet.address] @@ -236,26 +232,26 @@ describe("Compound III", async function () { dsa0Signer = await ethers.getSigner(wallet.address); await wallet0.sendTransaction({ to: dsaWallet0.address, - value: ethers.utils.parseEther("10") + value: ethers.utils.parseEther("1000") }); - expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("1000")); await wallet0.sendTransaction({ to: dsaWallet1.address, - value: ethers.utils.parseEther("10") + value: ethers.utils.parseEther("1000") }); - expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("1000")); await wallet0.sendTransaction({ to: dsaWallet3.address, - value: ethers.utils.parseEther("10") + value: ethers.utils.parseEther("1000") }); - expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("1000")); }); }); describe("Main", function () { //deposit asset - it("Should supply ETH collateral in Compound V3", async function () { - const amount = ethers.utils.parseEther("5"); // 5 ETH + it("Should supply Matic collateral in Compound V3", async function () { + const amount = ethers.utils.parseEther("500"); // 5 Matic const spells = [ { connector: connectorName, @@ -266,15 +262,15 @@ describe("Compound III", async function () { const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); const receipt = await tx.wait(); - expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("5")); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("500")); expect((await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.wmatic.address)).balance).to.be.gte( - ethers.utils.parseEther("5") + ethers.utils.parseEther("500") ); }); //deposit asset on behalf of - it("Should supply ETH collateral on behalf of dsaWallet0 in Compound V3", async function () { - const amount = ethers.utils.parseEther("1"); // 1 ETH + it("Should supply Matic collateral on behalf of dsaWallet0 in Compound V3", async function () { + const amount = ethers.utils.parseEther("100"); // 100 Matic const spells = [ { connector: connectorName, @@ -285,9 +281,9 @@ describe("Compound III", async function () { const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); const receipt = await tx.wait(); - expect(await ethers.provider.getBalance(dsaWallet1.address)).to.be.lte(ethers.utils.parseEther("9")); + expect(await ethers.provider.getBalance(dsaWallet1.address)).to.be.lte(ethers.utils.parseEther("900")); expect((await comet.connect(wallet0).userCollateral(dsaWallet0.address, tokens.wmatic.address)).balance).to.be.gte( - ethers.utils.parseEther("6") + ethers.utils.parseEther("600") ); }); @@ -383,9 +379,9 @@ describe("Compound III", async function () { ); }); - it("should withdraw some ETH collateral", async function () { + it("should withdraw some Matic collateral", async function () { let initialBal = await ethers.provider.getBalance(dsaWallet0.address); - const amount_ = ethers.utils.parseEther("2"); + const amount_ = ethers.utils.parseEther("200"); const spells = [ { connector: connectorName, @@ -397,7 +393,7 @@ describe("Compound III", async function () { const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); const receipt = await tx.wait(); expect((await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.wmatic.address)).balance).to.be.gte( - ethers.utils.parseEther("4") + ethers.utils.parseEther("400") ); expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(initialBal.add(amount_).toString()); }); @@ -405,7 +401,7 @@ describe("Compound III", async function () { it("manager should be able to withdraw collateral from the position and transfer", async function () { await wallet1.sendTransaction({ to: tokens.wmatic.address, - value: ethers.utils.parseEther("10") + value: ethers.utils.parseEther("1000") }); const amount = ethers.constants.MaxUint256; const spells = [ @@ -421,7 +417,7 @@ describe("Compound III", async function () { expect((await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.wmatic.address)).balance).to.be.gte( ethers.utils.parseEther("0") ); - expect(await wethContract.connect(wallet0).balanceOf(dsaWallet1.address)).to.be.gte(ethers.utils.parseEther("4")); + expect(await wmaticContract.connect(wallet0).balanceOf(dsaWallet1.address)).to.be.gte(ethers.utils.parseEther("400")); }); it("Should withdraw collateral to another DSA", async function () { @@ -429,14 +425,14 @@ describe("Compound III", async function () { { connector: connectorName, method: "deposit", - args: [market, tokens.matic.address, ethers.utils.parseEther("5"), 0, 0] + args: [market, tokens.matic.address, ethers.utils.parseEther("500"), 0, 0] } ]; const tx1 = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells1), wallet1.address); let initialBal = await ethers.provider.getBalance(dsaWallet0.address); - const amount = ethers.utils.parseEther("2"); + const amount = ethers.utils.parseEther("200"); const spells = [ { connector: connectorName, @@ -447,10 +443,10 @@ describe("Compound III", async function () { const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); const receipt = await tx.wait(); - expect(await wethContract.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte(amount); + expect(await wmaticContract.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte(amount); expect((await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.wmatic.address)).balance).to.be.gte( - ethers.utils.parseEther("3") + ethers.utils.parseEther("300") ); }); @@ -459,14 +455,14 @@ describe("Compound III", async function () { { connector: connectorName, method: "deposit", - args: [market, tokens.matic.address, ethers.utils.parseEther("3"), 0, 0] + args: [market, tokens.matic.address, ethers.utils.parseEther("300"), 0, 0] } ]; const tx1 = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells1), wallet1.address); let initialBal = await ethers.provider.getBalance(dsaWallet0.address); - const amount = ethers.utils.parseEther("2"); + const amount = ethers.utils.parseEther("200"); const spells = [ { connector: connectorName, @@ -480,16 +476,16 @@ describe("Compound III", async function () { expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(initialBal.add(amount)); expect((await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.wmatic.address)).balance).to.be.gte( - ethers.utils.parseEther("1") + ethers.utils.parseEther("100") ); }); - it("should transfer eth from dsaWallet1 to dsaWallet0 position", async function () { + it("should transfer matic from dsaWallet1 to dsaWallet0 position", async function () { const spells = [ { connector: connectorName, method: "transferAsset", - args: [market, tokens.matic.address, dsaWallet0.address, ethers.utils.parseEther("3"), 0, 0] + args: [market, tokens.matic.address, dsaWallet0.address, ethers.utils.parseEther("300"), 0, 0] } ]; @@ -499,12 +495,12 @@ describe("Compound III", async function () { ethers.utils.parseEther("0") ); expect((await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.wmatic.address)).balance).to.be.gte( - ethers.utils.parseEther("3") + ethers.utils.parseEther("300") ); }); it("should transfer base token from dsaWallet1 to dsaWallet0 position", async function () { - await baseContract.connect(wethSigner).transfer(dsaWallet1.address, ethers.utils.parseUnits("10", 6)); + await baseContract.connect(wethSigner).transfer(dsaWallet1.address, ethers.utils.parseUnits("1000", 6)); const spells = [ { @@ -547,11 +543,11 @@ describe("Compound III", async function () { }); it("should deposit weth using manager", async function () { - await wethContract.connect(wethSigner).transfer(dsaWallet0.address, ethers.utils.parseEther("10")); - let initialBal = await wethContract.connect(wallet0).balanceOf(dsaWallet0.address); + await wmaticContract.connect(signer).transfer(dsaWallet0.address, ethers.utils.parseEther("1000")); + let initialBal = await wmaticContract.connect(wallet0).balanceOf(dsaWallet0.address); - const amount = ethers.utils.parseEther("1"); - await wethContract.connect(dsa0Signer).approve(market, amount); + const amount = ethers.utils.parseEther("100"); + await wmaticContract.connect(dsa0Signer).approve(market, amount); const spells = [ { @@ -564,9 +560,9 @@ describe("Compound III", async function () { const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); const receipt = await tx.wait(); expect((await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.wmatic.address)).balance).to.be.gte( - ethers.utils.parseEther("1") + ethers.utils.parseEther("100") ); - expect(await wethContract.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.lte(initialBal.sub(amount)); + expect(await wmaticContract.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.lte(initialBal.sub(amount)); }); it("should allow manager for dsaWallet0's collateral", async function () { @@ -585,13 +581,13 @@ describe("Compound III", async function () { let initialBal = await baseContract.connect(wallet0).balanceOf(dsaWallet0.address); await wallet0.sendTransaction({ to: dsaWallet3.address, - value: ethers.utils.parseEther("15") + value: ethers.utils.parseEther("1500") }); const spells1 = [ { connector: connectorName, method: "deposit", - args: [market, tokens.matic.address, ethers.utils.parseEther("15"), 0, 0] + args: [market, tokens.matic.address, ethers.utils.parseEther("1500"), 0, 0] } ]; const tx1 = await dsaWallet3.connect(wallet0).cast(...encodeSpells(spells1), wallet1.address); @@ -619,17 +615,17 @@ describe("Compound III", async function () { { connector: connectorName, method: "transferAssetOnBehalf", - args: [market, tokens.matic.address, dsaWallet0.address, dsaWallet1.address, ethers.utils.parseEther("1"), 0, 0] + args: [market, tokens.matic.address, dsaWallet0.address, dsaWallet1.address, ethers.utils.parseEther("100"), 0, 0] } ]; const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); const receipt = await tx.wait(); expect((await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.wmatic.address)).balance).to.be.gte( - bal1.add(ethers.utils.parseEther("1")).toString() + bal1.add(ethers.utils.parseEther("100")).toString() ); expect((await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.wmatic.address)).balance).to.be.gte( - bal0.sub(ethers.utils.parseEther("1")).toString() + bal0.sub(ethers.utils.parseEther("100")).toString() ); }); @@ -655,7 +651,7 @@ describe("Compound III", async function () { // const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); // const receipt = await tx.wait(); // expect(new BigNumber(await linkContract.connect(signer).balanceOf(dsaWallet0.address)).toFixed()).to.be.gte( - // ethers.utils.parseEther("1") + // ethers.utils.parseEther("100") // ); // //dsawallet0 --> collateral 0eth, balance 9eth >1link From f9d01d53f9d48e03d24ddd7aa7b790685ab80312 Mon Sep 17 00:00:00 2001 From: Shriya Tyagi Date: Mon, 24 Apr 2023 01:08:00 +0400 Subject: [PATCH 16/16] Minor updates --- contracts/polygon/connectors/compound/v3-rewards/events.sol | 3 +-- contracts/polygon/connectors/compound/v3-rewards/helpers.sol | 1 - .../polygon/connectors/compound/v3-rewards/interface.sol | 2 +- contracts/polygon/connectors/compound/v3-rewards/main.sol | 4 ++-- contracts/polygon/connectors/compound/v3/main.sol | 4 ++-- 5 files changed, 6 insertions(+), 8 deletions(-) diff --git a/contracts/polygon/connectors/compound/v3-rewards/events.sol b/contracts/polygon/connectors/compound/v3-rewards/events.sol index 81754e7c..a279d988 100644 --- a/contracts/polygon/connectors/compound/v3-rewards/events.sol +++ b/contracts/polygon/connectors/compound/v3-rewards/events.sol @@ -1,6 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.7.6; -pragma experimental ABIEncoderV2; +pragma solidity ^0.7.0; contract Events { event LogRewardsClaimed( diff --git a/contracts/polygon/connectors/compound/v3-rewards/helpers.sol b/contracts/polygon/connectors/compound/v3-rewards/helpers.sol index 6b3cd72a..b4f7c125 100644 --- a/contracts/polygon/connectors/compound/v3-rewards/helpers.sol +++ b/contracts/polygon/connectors/compound/v3-rewards/helpers.sol @@ -1,6 +1,5 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.7.0; -pragma abicoder v2; import { TokenInterface } from "../../../common/interfaces.sol"; import { DSMath } from "../../../common/math.sol"; diff --git a/contracts/polygon/connectors/compound/v3-rewards/interface.sol b/contracts/polygon/connectors/compound/v3-rewards/interface.sol index b2157255..26e633b3 100644 --- a/contracts/polygon/connectors/compound/v3-rewards/interface.sol +++ b/contracts/polygon/connectors/compound/v3-rewards/interface.sol @@ -1,6 +1,6 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.7.0; -pragma abicoder v2; +pragma experimental ABIEncoderV2; struct UserCollateral { uint128 balance; diff --git a/contracts/polygon/connectors/compound/v3-rewards/main.sol b/contracts/polygon/connectors/compound/v3-rewards/main.sol index af702b29..ce86a7af 100644 --- a/contracts/polygon/connectors/compound/v3-rewards/main.sol +++ b/contracts/polygon/connectors/compound/v3-rewards/main.sol @@ -3,7 +3,7 @@ pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; /** - * @title Compound. + * @title Compound Polygon. * @dev Rewards. */ @@ -64,5 +64,5 @@ abstract contract CompoundV3RewardsResolver is Events, Helpers { } contract ConnectV2CompoundV3PolygonRewards is CompoundV3RewardsResolver { - string public name = "CompoundV3-Polygon-Rewards-v1.0"; + string public constant name = "CompoundV3-Polygon-Rewards-v1.0"; } diff --git a/contracts/polygon/connectors/compound/v3/main.sol b/contracts/polygon/connectors/compound/v3/main.sol index 3c9af8d7..b0b98dbe 100644 --- a/contracts/polygon/connectors/compound/v3/main.sol +++ b/contracts/polygon/connectors/compound/v3/main.sol @@ -3,7 +3,7 @@ pragma solidity ^0.7.0; pragma experimental ABIEncoderV2; /** - * @title Compound III + * @title Compound III Polygon. * @dev Lending & Borrowing. */ @@ -928,5 +928,5 @@ abstract contract CompoundV3Contract is Events, Helpers { } contract ConnectV2CompoundV3Polygon is CompoundV3Contract { - string public name = "CompoundV3-Polygon-v1.0"; + string public constant name = "CompoundV3-Polygon-v1.0"; }