mirror of
				https://github.com/Instadapp/dsa-connectors.git
				synced 2024-07-29 22:37:00 +00:00 
			
		
		
		
	Add Pangolin Stake Connector and Tests
Resolve errors Resolve review issues Fix comment Co-authored-by: 0xPradyuman <63545809+pradyuman-verma@users.noreply.github.com> Added NatSpec to Staking Connector Fix NatSpecs
This commit is contained in:
		
							parent
							
								
									358b9bfebb
								
							
						
					
					
						commit
						7884bb31e0
					
				|  | @ -16,7 +16,7 @@ abstract contract PangolinResolver is Helpers, Events { | |||
|      * @param tokenA The address of token A.(For AVAX: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) | ||||
|      * @param tokenB The address of token B.(For AVAX: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE) | ||||
|      * @param amtA The amount of A tokens to deposit. | ||||
|      * @param unitAmt The unit amount of of amtB/amtA with slippage. | ||||
|      * @param unitAmt The unit amount of amtB/amtA with slippage. | ||||
|      * @param slippage Slippage amount. | ||||
|      * @param getId ID to retrieve amtA. | ||||
|      * @param setId ID stores the amount of pools tokens received. | ||||
|  |  | |||
							
								
								
									
										67
									
								
								contracts/avalanche/connectors/pangolin/staking/events.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								contracts/avalanche/connectors/pangolin/staking/events.sol
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,67 @@ | |||
| pragma solidity ^0.7.0; | ||||
| 
 | ||||
| contract Events { | ||||
|     event LogDepositLpStake( | ||||
|         address indexed lptoken, | ||||
|         uint256 indexed pid, | ||||
|         uint256 stakedAmount, | ||||
|         uint256 getId, | ||||
|         uint256 setId | ||||
|     ); | ||||
|   | ||||
|     event LogWithdrawLpStake( | ||||
|         address indexed lptoken, | ||||
|         uint256 indexed pid, | ||||
|         uint256 withdrawAmount, | ||||
|         uint256 getId, | ||||
|         uint256 setId | ||||
|     ); | ||||
| 
 | ||||
|     event LogWithdrawLpAndClaim( | ||||
|         address indexed lptoken, | ||||
|         uint256 indexed pid, | ||||
|         uint256 withdrawAmount, | ||||
|         uint256 rewardAmount, | ||||
|         uint256 getId, | ||||
|         uint256 setId | ||||
|     ); | ||||
| 
 | ||||
|     event LogClaimLpReward( | ||||
|         address indexed lptoken, | ||||
|         uint256 indexed pid, | ||||
|         uint256 rewardAmount | ||||
|     ); | ||||
| 
 | ||||
|     event LogEmergencyWithdrawLpStake( | ||||
|         address indexed lptoken, | ||||
|         uint256 indexed pid, | ||||
|         uint256 withdrawAmount | ||||
|     ); | ||||
| 
 | ||||
|     event LogDepositPNGStake( | ||||
|         address indexed stakingContract, | ||||
|         uint256 stakedAmount, | ||||
|         uint256 getId, | ||||
|         uint256 setId | ||||
|     ); | ||||
| 
 | ||||
|     event LogWithdrawPNGStake( | ||||
|         address indexed stakingContract, | ||||
|         uint256 withdrawAmount, | ||||
|         uint256 getId, | ||||
|         uint256 setId | ||||
|     ); | ||||
| 
 | ||||
|     event LogExitPNGStake( | ||||
|         address indexed stakingContract, | ||||
|         uint256 exitAmount, | ||||
|         uint256 rewardAmount, | ||||
|         address indexed rewardToken | ||||
|     ); | ||||
| 
 | ||||
|     event LogClaimPNGStakeReward( | ||||
|         address indexed stakingContract, | ||||
|         uint256 rewardAmount, | ||||
|         address indexed rewardToken | ||||
|     ); | ||||
| } | ||||
							
								
								
									
										160
									
								
								contracts/avalanche/connectors/pangolin/staking/helpers.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								contracts/avalanche/connectors/pangolin/staking/helpers.sol
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,160 @@ | |||
| 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 { IERC20, IMiniChefV2, IStakingRewards } from "./interface.sol"; | ||||
| 
 | ||||
| abstract contract Helpers is DSMath, Basic { | ||||
| 
 | ||||
|     /** | ||||
|      * @dev Pangolin MiniChefV2 | ||||
|      */ | ||||
|     IMiniChefV2 internal constant minichefv2 = IMiniChefV2(0x1f806f7C8dED893fd3caE279191ad7Aa3798E928); | ||||
| 
 | ||||
|     /** | ||||
|      * @dev Pangolin Token | ||||
|      */ | ||||
|     IERC20 internal constant PNG = IERC20(0x60781C2586D68229fde47564546784ab3fACA982); | ||||
| 
 | ||||
|     // LP Staking, use minichefv2 to staking lp tokens and earn png | ||||
|     function _depositLPStake( | ||||
|         uint pid, | ||||
|         uint amount | ||||
|     ) internal returns (address lpTokenAddr) { | ||||
|         require(pid < minichefv2.poolLength(), "Invalid pid!"); | ||||
|         IERC20 lptoken = minichefv2.lpToken(pid); | ||||
| 
 | ||||
|         require(amount > 0, "Invalid amount, amount cannot be 0"); | ||||
|         require(lptoken.balanceOf(address(this)) > 0, "Invalid LP token balance"); | ||||
|         require(lptoken.balanceOf(address(this)) >= amount, "Invalid amount, amount greater than balance of LP token"); | ||||
| 
 | ||||
|         approve( | ||||
|             lptoken,  | ||||
|             address(minichefv2),  | ||||
|             amount | ||||
|         ); | ||||
| 
 | ||||
|         minichefv2.deposit(pid, amount, address(this)); | ||||
|         lpTokenAddr = address(lptoken); | ||||
|     } | ||||
| 
 | ||||
|     function _withdraw_LP_Stake( | ||||
|         uint pid, | ||||
|         uint amount | ||||
|     ) internal returns (address lpTokenAddr) { | ||||
|         require(pid < minichefv2.poolLength(), "Invalid pid!"); | ||||
|          | ||||
|         IMiniChefV2.UserInfo memory userinfo = minichefv2.userInfo(pid, address(this)); | ||||
| 
 | ||||
|         require(userinfo.amount >= amount, "Invalid amount, amount greater than balance of staking"); | ||||
|         require(amount > 0, "Invalid amount, amount cannot be 0"); | ||||
| 
 | ||||
|         minichefv2.withdraw(pid, amount, address(this)); | ||||
| 
 | ||||
|         IERC20 lptoken = minichefv2.lpToken(pid); | ||||
|         lpTokenAddr = address(lptoken); | ||||
|     } | ||||
| 
 | ||||
|     function _withdraw_and_getRewards_LP_Stake( | ||||
|         uint pid, | ||||
|         uint amount | ||||
|     ) internal returns (uint256 rewardAmount, address lpTokenAddr) { | ||||
|         require(pid < minichefv2.poolLength(), "Invalid pid!"); | ||||
| 
 | ||||
|         IMiniChefV2.UserInfo memory userinfo = minichefv2.userInfo(pid, address(this)); | ||||
| 
 | ||||
|         require(userinfo.amount >=  amount, "Invalid amount, amount greater than balance of staking"); | ||||
|         require(amount > 0, "Invalid amount, amount cannot be 0"); | ||||
| 
 | ||||
|         rewardAmount = minichefv2.pendingReward(pid, address(this)); | ||||
| 
 | ||||
|         minichefv2.withdrawAndHarvest(pid, amount, address(this)); | ||||
| 
 | ||||
|         IERC20 lptoken = minichefv2.lpToken(pid); | ||||
|         lpTokenAddr = address(lptoken); | ||||
|     } | ||||
| 
 | ||||
|     function _getLPStakeReward( | ||||
|         uint pid | ||||
|     ) internal returns (uint256 rewardAmount, address lpTokenAddr) { | ||||
|         require(pid < minichefv2.poolLength(), "Invalid pid!"); | ||||
| 
 | ||||
|         rewardAmount = minichefv2.pendingReward(pid, address(this)); | ||||
| 
 | ||||
|         require(rewardAmount > 0, "No rewards to claim"); | ||||
| 
 | ||||
|         minichefv2.harvest(pid, address(this)); | ||||
| 
 | ||||
|         IERC20 lptoken = minichefv2.lpToken(pid); | ||||
|         lpTokenAddr = address(lptoken); | ||||
|     } | ||||
| 
 | ||||
|     function _emergencyWithdraw_LP_Stake( | ||||
|         uint pid | ||||
|     ) internal returns (uint256 lpAmount, address lpTokenAddr) { | ||||
|         require(pid < minichefv2.poolLength(), "Invalid pid!"); | ||||
| 
 | ||||
|         IMiniChefV2.UserInfo memory userinfo = minichefv2.userInfo(pid, address(this)); | ||||
|         lpAmount = userinfo.amount; | ||||
| 
 | ||||
|         minichefv2.emergencyWithdraw(pid, address(this)); | ||||
|         IERC20 lptoken = minichefv2.lpToken(pid); | ||||
|         lpTokenAddr = address(lptoken); | ||||
|     } | ||||
| 
 | ||||
|     // PNG Staking (Stake PNG, earn another token) | ||||
|     function _depositPNGStake( | ||||
|         address stakingContract_addr, | ||||
|         uint amount | ||||
|     ) internal { | ||||
|         IStakingRewards stakingContract = IStakingRewards(stakingContract_addr); | ||||
| 
 | ||||
|         require(amount > 0, "Invalid amount, amount cannot be 0"); | ||||
|         require(PNG.balanceOf(address(this)) > 0, "Invalid PNG balance"); | ||||
|         require(PNG.balanceOf(address(this)) >=  amount, "Invalid amount, amount greater than balance of PNG"); | ||||
| 
 | ||||
|         approve(PNG, stakingContract_addr, amount); | ||||
| 
 | ||||
|         stakingContract.stake(amount); | ||||
|     } | ||||
| 
 | ||||
|     function _withdrawPNGStake( | ||||
|         address stakingContract_addr, | ||||
|         uint amount | ||||
|     ) internal { | ||||
|         IStakingRewards stakingContract = IStakingRewards(stakingContract_addr); | ||||
| 
 | ||||
|         require(stakingContract.balanceOf(address(this)) >=  amount, "Invalid amount, amount greater than balance of staking"); | ||||
|         require(amount > 0, "Invalid amount, amount cannot be 0"); | ||||
| 
 | ||||
|         stakingContract.withdraw(amount); | ||||
|     } | ||||
| 
 | ||||
|     function _exitPNGStake( | ||||
|         address stakingContract_addr | ||||
|     ) internal returns (uint256 exitAmount, uint256 rewardAmount, address rewardToken){ | ||||
|         IStakingRewards stakingContract = IStakingRewards(stakingContract_addr); | ||||
| 
 | ||||
|         exitAmount = stakingContract.balanceOf(address(this)); | ||||
|         rewardAmount = stakingContract.rewards(address(this)); | ||||
| 
 | ||||
|         require(exitAmount > 0, "No balance to exit"); | ||||
| 
 | ||||
|         stakingContract.exit(); | ||||
|     } | ||||
| 
 | ||||
|     function _claimPNGStakeReward( | ||||
|         address stakingContract_addr | ||||
|     ) internal returns (uint256 rewardAmount, address rewardToken) { | ||||
|         IStakingRewards stakingContract = IStakingRewards(stakingContract_addr); | ||||
| 
 | ||||
|         rewardAmount = stakingContract.rewards(address(this)); | ||||
|         rewardToken = stakingContract.rewardsToken(); | ||||
| 
 | ||||
|         require(rewardAmount > 0, "No rewards to claim"); | ||||
| 
 | ||||
|         stakingContract.getReward(); | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,49 @@ | |||
| pragma solidity >=0.6.2; | ||||
| pragma abicoder v2; | ||||
| 
 | ||||
| import { TokenInterface } from "../../../common/interfaces.sol"; | ||||
| 
 | ||||
| interface IERC20 is TokenInterface{ | ||||
| 
 | ||||
|     // EIP 2612 | ||||
|     function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external; | ||||
| } | ||||
| 
 | ||||
| interface IStakingRewards { | ||||
|     // Storage | ||||
|     function rewards(address account) view external returns (uint256); | ||||
| 
 | ||||
|     // View | ||||
|     function balanceOf(address account) external view returns (uint256); | ||||
|     function rewardsToken() external view returns (address); | ||||
| 
 | ||||
|     // Mutative | ||||
|     function exit() external; | ||||
|     function getReward() external; | ||||
|     function stake(uint256 amount) external; | ||||
|     function withdraw(uint256 amount) external; | ||||
| } | ||||
| 
 | ||||
| interface IMiniChefV2 { | ||||
|     struct UserInfo { | ||||
|         uint256 amount; | ||||
|         int256 rewardDebt; | ||||
|     } | ||||
| 
 | ||||
|     // Storage | ||||
|     function addedTokens(address token) external returns (bool); | ||||
|     function lpToken(uint256 _pid) external view returns (IERC20); | ||||
|     function userInfo(uint256 _pid, address _user) external view returns (UserInfo memory); | ||||
| 
 | ||||
|     // View | ||||
|     function pendingReward(uint256 _pid, address _user) external view returns (uint256); | ||||
|     function poolLength() external view returns (uint256); | ||||
| 
 | ||||
|     // Mutative | ||||
|     function deposit(uint256 pid, uint256 amount, address to) external; | ||||
|     function depositWithPermit(uint256 pid, uint256 amount, address to, uint deadline, uint8 v, bytes32 r, bytes32 s) external; | ||||
|     function withdraw(uint256 pid, uint256 amount, address to) external; | ||||
|     function harvest(uint256 pid, address to) external; | ||||
|     function withdrawAndHarvest(uint256 pid, uint256 amount, address to) external; | ||||
|     function emergencyWithdraw(uint256 pid, address to) external; | ||||
| } | ||||
							
								
								
									
										190
									
								
								contracts/avalanche/connectors/pangolin/staking/main.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								contracts/avalanche/connectors/pangolin/staking/main.sol
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,190 @@ | |||
| pragma solidity ^0.7.0; | ||||
| 
 | ||||
| /** | ||||
|  * @title Pangolin. | ||||
|  * @dev Decentralized Exchange. | ||||
|  */ | ||||
| 
 | ||||
| import { TokenInterface } from "../../../common/interfaces.sol"; | ||||
| import { Helpers } from "./helpers.sol"; | ||||
| import { Events } from "./events.sol"; | ||||
| 
 | ||||
| abstract contract PangolinStakeResolver is Helpers, Events { | ||||
| 
 | ||||
|     // LP Staking | ||||
|     /** | ||||
|     * @notice Deposit LP token in MiniChefV2 | ||||
|     * @dev Use the Pangolin Stake resolver to get the pid | ||||
|     * @param pid The index of the LP token in MiniChefV2. | ||||
|     * @param amount The amount of the LP token to deposit. | ||||
|     * @param getId ID to retrieve sellAmt. | ||||
|     * @param setId ID stores the amount of token brought. | ||||
|     */ | ||||
|     function depositLpStake( | ||||
|         uint pid, | ||||
|         uint amount, | ||||
|         uint256 getId, | ||||
|         uint256 setId | ||||
|     ) external returns (string memory _eventName, bytes memory _eventParam) { | ||||
|         uint _amt = getUint(getId, amount); | ||||
| 
 | ||||
|         address lpTokenAddr = _depositLPStake(pid, _amt); | ||||
| 
 | ||||
|         setUint(setId, _amt); | ||||
|         _eventName = "LogDepositLpStake(address,uint256,uint256,uint256,uint256)"; | ||||
|         _eventParam = abi.encode(lpTokenAddr, pid, _amt, getId, setId); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|     * @notice Withdraw LP token from MiniChefV2 | ||||
|     * @dev Use the Pangolin Stake resolver to get the pid | ||||
|     * @param pid The index of the LP token in MiniChefV2. | ||||
|     * @param amount The amount of the LP token to withdraw. | ||||
|     * @param getId ID to retrieve sellAmt. | ||||
|     * @param setId ID stores the amount of token brought. | ||||
|     */ | ||||
|     function withdrawLpStake( | ||||
|         uint pid, | ||||
|         uint amount, | ||||
|         uint256 getId, | ||||
|         uint256 setId | ||||
|     ) external returns (string memory _eventName, bytes memory _eventParam) { | ||||
|         uint _amt = getUint(getId, amount); | ||||
| 
 | ||||
|         address lpTokenAddr = _withdraw_LP_Stake(pid, _amt); | ||||
| 
 | ||||
|         setUint(setId, _amt); | ||||
| 
 | ||||
|         _eventName = "LogWithdrawLpStake(address,uint256,uint256,uint256,uint256)"; | ||||
|         _eventParam = abi.encode(lpTokenAddr, pid, _amt, getId, setId); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|     * @notice Withdraw LP token staked and claim rewards from MiniChefV2 | ||||
|     * @dev Use the Pangolin Stake resolver to get the pid | ||||
|     * @param pid The index of the LP token in MiniChefV2. | ||||
|     * @param amount The amount of the LP token to withdraw. | ||||
|     * @param getId ID to retrieve sellAmt. | ||||
|     * @param setId ID stores the amount of token brought. | ||||
|     */ | ||||
|     function withdrawAndClaimLpRewards( | ||||
|         uint pid, | ||||
|         uint amount, | ||||
|         uint256 getId, | ||||
|         uint256 setId | ||||
|     ) external returns (string memory _eventName, bytes memory _eventParam) { | ||||
|         uint _amt = getUint(getId, amount); | ||||
| 
 | ||||
|         (uint256 rewardAmount, address lpTokenAddr) = _withdraw_and_getRewards_LP_Stake(pid, _amt); | ||||
| 
 | ||||
|         setUint(setId, _amt); | ||||
| 
 | ||||
|         _eventName = "LogWithdrawLpAndClaim(address,uint256,uint256,uint256,uint256,uint256)"; | ||||
|         _eventParam = abi.encode(lpTokenAddr, pid, _amt, rewardAmount, getId, setId); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|     * @notice Claim rewards from MiniChefV2 | ||||
|     * @dev Use the Pangolin Stake resolver to get the pid | ||||
|     * @param pid The index of the LP token in MiniChefV2. | ||||
|     */ | ||||
|     function claimLpRewards( | ||||
|         uint pid | ||||
|     ) external returns (string memory _eventName, bytes memory _eventParam) { | ||||
|         (uint256 rewardAmount, address lpTokenAddr) = _getLPStakeReward(pid); | ||||
| 
 | ||||
|         _eventName = "LogClaimLpReward(address,uint256,uint256)"; | ||||
|         _eventParam = abi.encode(lpTokenAddr, pid, rewardAmount); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|     * @notice Emergency withdraw all LP token staked from MiniChefV2 | ||||
|     * @dev Use the Pangolin Stake resolver to get the pid | ||||
|     * @param pid The index of the LP token in MiniChefV2. | ||||
|     */ | ||||
|     function emergencyWithdrawLpStake( | ||||
|         uint pid | ||||
|     ) external returns (string memory _eventName, bytes memory _eventParam) { | ||||
|         (uint amount, address lpTokenAddr) = _emergencyWithdraw_LP_Stake(pid); | ||||
| 
 | ||||
|         _eventName = "LogEmergencyWithdrawLpStake(address,uint256,uint256)"; | ||||
|         _eventParam = abi.encode(lpTokenAddr, pid, amount); | ||||
|     } | ||||
| 
 | ||||
|     // PNG Staking | ||||
|     /** | ||||
|     * @notice Deposit PNG in staking contract | ||||
|     * @param stakingContract The address of the single PNG staking contract | ||||
|     * @param amount The amount of the PNG to deposit. | ||||
|     * @param getId ID to retrieve sellAmt. | ||||
|     * @param setId ID stores the amount of token brought. | ||||
|     */ | ||||
|     function depositPNGStake( | ||||
|         address stakingContract, | ||||
|         uint256 amount, | ||||
|         uint256 getId, | ||||
|         uint256 setId | ||||
|     ) external returns (string memory _eventName, bytes memory _eventParam) { | ||||
|         uint _amt = getUint(getId, amount); | ||||
| 
 | ||||
|         _depositPNGStake(stakingContract, _amt); | ||||
| 
 | ||||
|         setUint(setId, _amt); | ||||
| 
 | ||||
|         _eventName = "LogDepositPNGStake(address,uint256,uint256,uint256)"; | ||||
|         _eventParam = abi.encode(stakingContract, _amt, getId, setId); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|     * @notice Withdraw PNG staked from staking contract | ||||
|     * @param stakingContract The address of the single PNG staking contract | ||||
|     * @param amount The amount of the PNG to withdraw. | ||||
|     * @param getId ID to retrieve sellAmt. | ||||
|     * @param setId ID stores the amount of token brought. | ||||
|     */ | ||||
|     function withdrawPNGStake( | ||||
|         address stakingContract, | ||||
|         uint256 amount, | ||||
|         uint256 getId, | ||||
|         uint256 setId | ||||
|     ) external returns (string memory _eventName, bytes memory _eventParam) { | ||||
|         uint _amt = getUint(getId, amount); | ||||
| 
 | ||||
|         _withdrawPNGStake(stakingContract, _amt); | ||||
| 
 | ||||
|         setUint(setId, _amt); | ||||
| 
 | ||||
|         _eventName = "LogWithdrawPNGStake(address,uint256,uint256,uint256)"; | ||||
|         _eventParam = abi.encode(stakingContract, _amt, getId, setId); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|     * @notice Withdraw all PNG staked from staking contract | ||||
|     * @param stakingContract The address of the single PNG staking contract | ||||
|     */ | ||||
|     function exitPNGStake( | ||||
|         address stakingContract | ||||
|     ) external returns (string memory _eventName, bytes memory _eventParam) { | ||||
|         (uint256 exitAmount, uint256 rewardAmount, address rewardToken) = _exitPNGStake(stakingContract); | ||||
| 
 | ||||
|         _eventName = "LogExitPNGStake(address,uint256,uint256,address)"; | ||||
|         _eventParam = abi.encode(stakingContract, exitAmount, rewardAmount, rewardToken); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|     * @notice Claim rewards from staking contract | ||||
|     * @param stakingContract The address of the single PNG staking contract | ||||
|     */ | ||||
|     function claimPNGStakeReward( | ||||
|         address stakingContract | ||||
|     ) external returns (string memory _eventName, bytes memory _eventParam) { | ||||
|         (uint256 rewardAmount, address rewardToken) = _claimPNGStakeReward(stakingContract); | ||||
| 
 | ||||
|         _eventName = "LogClaimPNGStakeReward(address,uint256,address)"; | ||||
|         _eventParam = abi.encode(stakingContract, rewardAmount, rewardToken); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| contract ConnectV2PngStakeAvalanche is PangolinStakeResolver { | ||||
|     string public constant name = "Pangolin-Stake-v1"; | ||||
| } | ||||
|  | @ -1,17 +1,16 @@ | |||
| import { expect } from "chai"; | ||||
| import hre from "hardhat"; | ||||
| 
 | ||||
| const { web3, deployments, waffle, ethers } = hre; | ||||
| const { provider, deployContract } = waffle; | ||||
| const { waffle, ethers } = hre; | ||||
| const { provider } = waffle; | ||||
| 
 | ||||
| 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 { addLiquidity } from "../../../scripts/tests/addLiquidity"; | ||||
| import { addresses } from "../../../scripts/tests/avalanche/addresses"; | ||||
| import { abis } from "../../../scripts/constant/abis"; | ||||
| import type { Signer, Contract } from "ethers"; | ||||
| import { Signer, Contract } from "ethers"; | ||||
| 
 | ||||
| import { ConnectV2PngAvalanche__factory } from "../../../typechain"; | ||||
| 
 | ||||
|  | @ -22,13 +21,13 @@ const PNG_AVAX_LP_ADDRESS = "0xd7538cABBf8605BdE1f4901B47B8D42c61DE0367"; | |||
| describe("Pangolin DEX - Avalanche", function () { | ||||
|     const pangolinConnectorName = "PANGOLIN-TEST-A" | ||||
|      | ||||
|     let dsaWallet0: any; | ||||
|     let masterSigner: any; | ||||
|     let instaConnectorsV2: any; | ||||
|     let pangolinConnector: any; | ||||
|     let dsaWallet0: Contract; | ||||
|     let masterSigner: Signer; | ||||
|     let instaConnectorsV2: Contract; | ||||
|     let pangolinConnector: Contract; | ||||
|      | ||||
|     const wallets = provider.getWallets() | ||||
|     const [wallet0, wallet1, wallet2, wallet3] = wallets | ||||
|     const [wallet0, wallet1] = wallets | ||||
|     before(async () => { | ||||
|         await hre.network.provider.request({ | ||||
|             method: "hardhat_reset", | ||||
|  | @ -61,12 +60,12 @@ describe("Pangolin DEX - Avalanche", function () { | |||
|     it("Should have contracts deployed.", async function () { | ||||
|         expect(!!instaConnectorsV2.address).to.be.true; | ||||
|         expect(!!pangolinConnector.address).to.be.true; | ||||
|         expect(!!masterSigner.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) | ||||
|             dsaWallet0 = await buildDSAv2(wallet0.getAddress()) | ||||
|             expect(!!dsaWallet0.address).to.be.true; | ||||
|         }); | ||||
| 
 | ||||
							
								
								
									
										821
									
								
								test/avalanche/pangolin/pangolin_stake.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										821
									
								
								test/avalanche/pangolin/pangolin_stake.test.ts
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,821 @@ | |||
| import { expect } from "chai"; | ||||
| import hre from "hardhat"; | ||||
| 
 | ||||
| const { waffle, ethers } = hre; | ||||
| const { provider } = waffle; | ||||
| 
 | ||||
| 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/avalanche/addresses"; | ||||
| import { abis } from "../../../scripts/constant/abis"; | ||||
| import { Signer, Contract, BigNumber } from "ethers"; | ||||
| 
 | ||||
| import { ConnectV2PngAvalanche__factory, ConnectV2PngStakeAvalanche__factory } from "../../../typechain"; | ||||
| 
 | ||||
| const PNG_ADDRESS  = "0x60781C2586D68229fde47564546784ab3fACA982"; | ||||
| const WAVAX_ADDRESS = "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7"; | ||||
| const PNG_AVAX_LP_ADDRESS = "0xd7538cABBf8605BdE1f4901B47B8D42c61DE0367"; | ||||
| const PNG_STAKING_ADDRESS = "0x88afdaE1a9F58Da3E68584421937E5F564A0135b"; | ||||
| 
 | ||||
| describe("Pangolin Stake - Avalanche", function () { | ||||
|     const pangolinConnectorName = "PANGOLIN-TEST-A" | ||||
|     const pangolinStakeConnectorName = "PANGOLIN-STAKE-TEST-A" | ||||
|      | ||||
|     let dsaWallet0: Contract; | ||||
|     let masterSigner: Signer; | ||||
|     let instaConnectorsV2: Contract; | ||||
|     let pangolinConnector: Contract; | ||||
|     let pangolinStakeConnector: Contract; | ||||
|      | ||||
|     let PNG: Contract; | ||||
| 
 | ||||
|     const wallets = provider.getWallets() | ||||
|     const [wallet0, wallet1] = wallets | ||||
|     before(async () => { | ||||
|         await hre.network.provider.request({ | ||||
|             method: "hardhat_reset", | ||||
|             params: [ | ||||
|                 { | ||||
|                     forking: { | ||||
|                         jsonRpcUrl: `https://api.avax.network/ext/bc/C/rpc`, | ||||
|                         blockNumber: 8197390 | ||||
|                     }, | ||||
|                 }, | ||||
|             ], | ||||
|         }); | ||||
| 
 | ||||
|         PNG = await ethers.getContractAt( | ||||
|             abis.basic.erc20,  | ||||
|             PNG_ADDRESS | ||||
|         ); | ||||
| 
 | ||||
|         masterSigner = await getMasterSigner(); | ||||
|         instaConnectorsV2 = await ethers.getContractAt( | ||||
|             abis.core.connectorsV2, | ||||
|             addresses.core.connectorsV2 | ||||
|         ); | ||||
| 
 | ||||
|         // Deploy and enable Pangolin Connector
 | ||||
|         pangolinConnector = await deployAndEnableConnector({ | ||||
|             connectorName: pangolinConnectorName, | ||||
|             contractArtifact: ConnectV2PngAvalanche__factory, | ||||
|             signer: masterSigner, | ||||
|             connectors: instaConnectorsV2 | ||||
|         }); | ||||
|         console.log("Pangolin Connector address: "+ pangolinConnector.address); | ||||
| 
 | ||||
|         // Deploy and enable Pangolin Stake Connector
 | ||||
|         pangolinStakeConnector = await deployAndEnableConnector({ | ||||
|             connectorName: pangolinStakeConnectorName, | ||||
|             contractArtifact: ConnectV2PngStakeAvalanche__factory, | ||||
|             signer: masterSigner, | ||||
|             connectors: instaConnectorsV2 | ||||
|         }); | ||||
|         console.log("Pangolin Stake Connector address: "+ pangolinStakeConnector.address); | ||||
|     }) | ||||
| 
 | ||||
|     it("Should have contracts deployed.", async function () { | ||||
|         expect(!!instaConnectorsV2.address).to.be.true; | ||||
|         expect(!!pangolinConnector.address).to.be.true; | ||||
|         expect(!!pangolinStakeConnector.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.getAddress()) | ||||
|             expect(!!dsaWallet0.address).to.be.true; | ||||
|         }); | ||||
| 
 | ||||
|         it("Deposit 10 AVAX into DSA wallet", async function () { | ||||
|             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")); | ||||
|         }); | ||||
|     }); | ||||
| 
 | ||||
|     describe("Pangolin Staking - LP Stake Test", function () { | ||||
|         let lpAmount: BigNumber; | ||||
|         let pangolinLPToken: Contract; | ||||
|         // Buy 100 PNG and deposity in PNG/AVAX LP
 | ||||
|         before(async () => { | ||||
|             const amount = ethers.utils.parseEther("100"); // 100 PNG
 | ||||
|             const int_slippage = 0.03 | ||||
|             const slippage = ethers.utils.parseEther(int_slippage.toString()); | ||||
|             const setId = "0"; | ||||
|      | ||||
|             const PangolinRouterABI = [ | ||||
|                 "function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)" | ||||
|             ]; | ||||
|      | ||||
|             // Get amount of AVAX for 200 PNG from Pangolin
 | ||||
|             const PangolinRouter = await ethers.getContractAt( | ||||
|                 PangolinRouterABI,  | ||||
|                 "0xE54Ca86531e17Ef3616d22Ca28b0D458b6C89106" | ||||
|             ); | ||||
|             const amounts = await PangolinRouter.getAmountsOut( | ||||
|                 amount,  | ||||
|                 [ | ||||
|                     PNG_ADDRESS,  | ||||
|                     WAVAX_ADDRESS | ||||
|                 ] | ||||
|             ); | ||||
| 
 | ||||
|             const amtA = amounts[0]; | ||||
|             const amtB = amounts[1]; | ||||
|             const unitAmt = (amtB * (1 + int_slippage)) / amtA; | ||||
|             const unitAmount = ethers.utils.parseEther(unitAmt.toString()); | ||||
| 
 | ||||
|             const spells = [ | ||||
|                 { | ||||
|                     connector: pangolinConnectorName, | ||||
|                     method: "buy", | ||||
|                     args: [ | ||||
|                         PNG_ADDRESS,  | ||||
|                         "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",  | ||||
|                         amount,  | ||||
|                         unitAmount,  | ||||
|                         0,  | ||||
|                         0 | ||||
|                     ] | ||||
|                 }, | ||||
|                 { | ||||
|                     connector: pangolinConnectorName, | ||||
|                     method: "deposit", | ||||
|                     args: [ | ||||
|                         PNG_ADDRESS,  | ||||
|                         "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",  | ||||
|                         amount,  | ||||
|                         unitAmount,  | ||||
|                         slippage,  | ||||
|                         0,  | ||||
|                         setId | ||||
|                     ] | ||||
|                 }, | ||||
|             ]; | ||||
|             // Run spell transaction
 | ||||
|             const tx = await dsaWallet0.connect(wallet0).cast( | ||||
|                 ...encodeSpells(spells), wallet1.address | ||||
|             ); | ||||
|             const receipt = await tx.wait(); | ||||
|             pangolinLPToken = await ethers.getContractAt( | ||||
|                 abis.basic.erc20,  | ||||
|                 PNG_AVAX_LP_ADDRESS | ||||
|             ); | ||||
|         }); | ||||
| 
 | ||||
|         it("Check if has PNG/AVAX LP", async function () { | ||||
|             const pangolinPoolAVAXBalance = await pangolinLPToken.balanceOf(dsaWallet0.address); | ||||
|             expect(pangolinPoolAVAXBalance, `Pangolin PNG/AVAX LP greater than 0`).to.be.gt(0); | ||||
|             console.log("PNG/AVAX LP: ", ethers.utils.formatUnits(pangolinPoolAVAXBalance, "ether").toString()) | ||||
|             lpAmount = pangolinPoolAVAXBalance; | ||||
|         }); | ||||
|          | ||||
|         it("Check if all functions reverts by: Invalid pid!", async function () { | ||||
|             const pid = BigNumber.from("999999999999"); | ||||
|             const amount = ethers.utils.parseEther("1"); | ||||
|             const getId = 0; | ||||
|             const setId = 0; | ||||
| 
 | ||||
|             let spells = [ | ||||
|                 { | ||||
|                     connector: pangolinStakeConnectorName, | ||||
|                     method: "depositLpStake", | ||||
|                     args: [ | ||||
|                         pid,  | ||||
|                         amount,  | ||||
|                         getId,  | ||||
|                         setId | ||||
|                     ] | ||||
|                 } | ||||
|             ]; | ||||
|             await expect( | ||||
|                 dsaWallet0.connect(wallet0).cast( | ||||
|                   ...encodeSpells(spells), | ||||
|                   wallet1.address | ||||
|                 ) | ||||
|             ).to.be.revertedWith("Invalid pid!"); | ||||
| 
 | ||||
|             spells[0].method = "withdrawLpStake" | ||||
|             await expect( | ||||
|                 dsaWallet0.connect(wallet0).cast( | ||||
|                   ...encodeSpells(spells), | ||||
|                   wallet1.address | ||||
|                 ) | ||||
|             ).to.be.revertedWith("Invalid pid!"); | ||||
| 
 | ||||
|             spells[0].method = "withdrawAndClaimLpRewards" | ||||
|             await expect( | ||||
|                 dsaWallet0.connect(wallet0).cast( | ||||
|                   ...encodeSpells(spells), | ||||
|                   wallet1.address | ||||
|                 ) | ||||
|             ).to.be.revertedWith("Invalid pid!"); | ||||
| 
 | ||||
|             spells = [ | ||||
|                 { | ||||
|                     connector: pangolinStakeConnectorName, | ||||
|                     method: "claimLpRewards", | ||||
|                     args: [ | ||||
|                         pid | ||||
|                     ] | ||||
|                 } | ||||
|             ]; | ||||
|             await expect( | ||||
|                 dsaWallet0.connect(wallet0).cast( | ||||
|                   ...encodeSpells(spells), | ||||
|                   wallet1.address | ||||
|                 ) | ||||
|             ).to.be.revertedWith("Invalid pid!"); | ||||
| 
 | ||||
|             spells[0].method = "emergencyWithdrawLpStake" | ||||
|             await expect( | ||||
|                 dsaWallet0.connect(wallet0).cast( | ||||
|                   ...encodeSpells(spells), | ||||
|                   wallet1.address | ||||
|                 ) | ||||
|             ).to.be.revertedWith("Invalid pid!"); | ||||
|         }); | ||||
| 
 | ||||
|         it("Check if all functions reverts by: 'Invalid amount, amount cannot be 0'", async function () { | ||||
|             let spells = [ | ||||
|                 { | ||||
|                     connector: pangolinStakeConnectorName, | ||||
|                     method: "depositLpStake", | ||||
|                     args: [ | ||||
|                         0, | ||||
|                         0, | ||||
|                         0, | ||||
|                         0 | ||||
|                     ] | ||||
|                 } | ||||
|             ]; | ||||
|             await expect( | ||||
|                 dsaWallet0.connect(wallet0).cast( | ||||
|                   ...encodeSpells(spells), | ||||
|                   wallet1.address | ||||
|                 ) | ||||
|             ).to.be.revertedWith("Invalid amount, amount cannot be 0"); | ||||
| 
 | ||||
|             spells[0].method = "withdrawLpStake" | ||||
|             await expect( | ||||
|                 dsaWallet0.connect(wallet0).cast( | ||||
|                   ...encodeSpells(spells), | ||||
|                   wallet1.address | ||||
|                 ) | ||||
|             ).to.be.revertedWith("Invalid amount, amount cannot be 0"); | ||||
| 
 | ||||
|             spells[0].method = "withdrawLpStake" | ||||
|             await expect( | ||||
|                 dsaWallet0.connect(wallet0).cast( | ||||
|                   ...encodeSpells(spells), | ||||
|                   wallet1.address | ||||
|                 ) | ||||
|             ).to.be.revertedWith("Invalid amount, amount cannot be 0"); | ||||
| 
 | ||||
|             spells[0].method = "withdrawAndClaimLpRewards" | ||||
|             await expect( | ||||
|                 dsaWallet0.connect(wallet0).cast( | ||||
|                   ...encodeSpells(spells), | ||||
|                   wallet1.address | ||||
|                 ) | ||||
|             ).to.be.revertedWith("Invalid amount, amount cannot be 0"); | ||||
|         }); | ||||
| 
 | ||||
|         describe("depositLpStake function", function () { | ||||
|             it("Check if depositLpStake function reverts by: Invalid amount, amount greater than balance of LP token", async function () { | ||||
|                 const amount = lpAmount.mul(2); | ||||
|                 const spells = [ | ||||
|                     { | ||||
|                         connector: pangolinStakeConnectorName, | ||||
|                         method: "depositLpStake", | ||||
|                         args: [ | ||||
|                             0, | ||||
|                             amount, | ||||
|                             0, | ||||
|                             0 | ||||
|                         ] | ||||
|                     } | ||||
|                 ]; | ||||
|                 await expect( | ||||
|                     dsaWallet0.connect(wallet0).cast( | ||||
|                       ...encodeSpells(spells), | ||||
|                       wallet1.address | ||||
|                     ) | ||||
|                 ).to.be.revertedWith("Invalid amount, amount greater than balance of LP token"); | ||||
|             }); | ||||
| 
 | ||||
|             it("Check if success in depositLpStake", async function () { | ||||
|                 const spells = [ | ||||
|                     { | ||||
|                         connector: pangolinStakeConnectorName, | ||||
|                         method: "depositLpStake", | ||||
|                         args: [ | ||||
|                             0, | ||||
|                             lpAmount, | ||||
|                             0, | ||||
|                             0 | ||||
|                         ] | ||||
|                     } | ||||
|                 ]; | ||||
|                 await expect( | ||||
|                     dsaWallet0.connect(wallet0).cast( | ||||
|                       ...encodeSpells(spells), | ||||
|                       wallet1.address | ||||
|                     ) | ||||
|                 ).to.be.not.reverted; | ||||
|                 // Check if PNG/AVAX LP is equal 0
 | ||||
|                 const balance = await pangolinLPToken.balanceOf(dsaWallet0.address); | ||||
|                 expect(balance).to.be.eq(0); | ||||
|             }); | ||||
| 
 | ||||
|             it("Check if depositLpStake function reverts by: Invalid LP token balance", async function () { | ||||
|                 const spells = [ | ||||
|                     { | ||||
|                         connector: pangolinStakeConnectorName, | ||||
|                         method: "depositLpStake", | ||||
|                         args: [ | ||||
|                             0, | ||||
|                             lpAmount, | ||||
|                             0, | ||||
|                             0 | ||||
|                         ] | ||||
|                     } | ||||
|                 ]; | ||||
|                 await expect( | ||||
|                     dsaWallet0.connect(wallet0).cast( | ||||
|                       ...encodeSpells(spells), | ||||
|                       wallet1.address | ||||
|                     ) | ||||
|                 ).to.be.revertedWith("Invalid LP token balance"); | ||||
|             }); | ||||
|         }); | ||||
| 
 | ||||
|         describe("claimLpRewards function", function () { | ||||
|             it("Check if success in claimLpRewards", async function () { | ||||
|                 // Increase Time in 20 seconds
 | ||||
|                 await hre.network.provider.send("evm_increaseTime", [20]); | ||||
|                 // Mine new block
 | ||||
|                 await hre.network.provider.send("evm_mine"); | ||||
|                 const spells = [ | ||||
|                     { | ||||
|                         connector: pangolinStakeConnectorName, | ||||
|                         method: "claimLpRewards", | ||||
|                         args: [0] | ||||
|                     } | ||||
|                 ]; | ||||
|                 await expect( | ||||
|                     dsaWallet0.connect(wallet0).cast( | ||||
|                       ...encodeSpells(spells), | ||||
|                       wallet1.address | ||||
|                     ) | ||||
|                 ).to.be.not.reverted; | ||||
|                 // Checks if the wallet has more than 100 PNG
 | ||||
|                 const balance = await PNG.balanceOf(dsaWallet0.address); | ||||
|                 expect(balance).to.be.gt(0); | ||||
|             }); | ||||
|         }); | ||||
| 
 | ||||
|         describe("withdrawLpStake function", function () { | ||||
|             it("Check if withdrawLpStake function reverts by: Invalid amount, amount greater than balance of staking", async function () { | ||||
|                 const amount = lpAmount.mul(2); | ||||
|                 const spells = [ | ||||
|                     { | ||||
|                         connector: pangolinStakeConnectorName, | ||||
|                         method: "withdrawLpStake", | ||||
|                         args: [ | ||||
|                             0, | ||||
|                             amount, | ||||
|                             0, | ||||
|                             0 | ||||
|                         ] | ||||
|                     } | ||||
|                 ]; | ||||
|                 await expect( | ||||
|                     dsaWallet0.connect(wallet0).cast( | ||||
|                       ...encodeSpells(spells), | ||||
|                       wallet1.address | ||||
|                     ) | ||||
|                 ).to.be.revertedWith("Invalid amount, amount greater than balance of staking"); | ||||
|             }); | ||||
| 
 | ||||
|             it("Check if success in withdrawLpStake", async function () { | ||||
|                 const spells = [ | ||||
|                     { | ||||
|                         connector: pangolinStakeConnectorName, | ||||
|                         method: "withdrawLpStake", | ||||
|                         args: [ | ||||
|                             0, | ||||
|                             lpAmount.div(2), | ||||
|                             0, | ||||
|                             0 | ||||
|                         ] | ||||
|                     } | ||||
|                 ]; | ||||
|                 await expect( | ||||
|                     dsaWallet0.connect(wallet0).cast( | ||||
|                       ...encodeSpells(spells), | ||||
|                       wallet1.address | ||||
|                     ) | ||||
|                 ).to.be.not.reverted; | ||||
|                 // Check if PNG/AVAX LP is equal 0
 | ||||
|                 const balance = await pangolinLPToken.balanceOf(dsaWallet0.address); | ||||
|                 expect(balance).to.be.eq(lpAmount.div(2)); | ||||
|             }); | ||||
|         }); | ||||
| 
 | ||||
|         describe("withdrawAndClaimLpRewards function", function () { | ||||
|             it("Check if withdrawAndClaimLpRewards function reverts by: Invalid amount, amount greater than balance of staking", async function () { | ||||
|                 const amount = lpAmount.mul(2); | ||||
|                 const spells = [ | ||||
|                     { | ||||
|                         connector: pangolinStakeConnectorName, | ||||
|                         method: "withdrawAndClaimLpRewards", | ||||
|                         args: [ | ||||
|                             0, | ||||
|                             amount, | ||||
|                             0, | ||||
|                             0 | ||||
|                         ] | ||||
|                     } | ||||
|                 ]; | ||||
|                 await expect( | ||||
|                     dsaWallet0.connect(wallet0).cast( | ||||
|                       ...encodeSpells(spells), | ||||
|                       wallet1.address | ||||
|                     ) | ||||
|                 ).to.be.revertedWith("Invalid amount, amount greater than balance of staking"); | ||||
|             }); | ||||
| 
 | ||||
|             it("Check if success in withdrawAndClaimLpRewards", async function () { | ||||
|                 let balance = await pangolinLPToken.balanceOf(dsaWallet0.address); | ||||
|                 const png_balance = await PNG.balanceOf(dsaWallet0.address); | ||||
|                 const amount = lpAmount.sub(balance) | ||||
|                 const spells = [ | ||||
|                     { | ||||
|                         connector: pangolinStakeConnectorName, | ||||
|                         method: "withdrawAndClaimLpRewards", | ||||
|                         args: [ | ||||
|                             0, | ||||
|                             amount, | ||||
|                             0, | ||||
|                             0 | ||||
|                         ] | ||||
|                     } | ||||
|                 ]; | ||||
|                 await expect( | ||||
|                     dsaWallet0.connect(wallet0).cast( | ||||
|                       ...encodeSpells(spells), | ||||
|                       wallet1.address | ||||
|                     ) | ||||
|                 ).to.be.not.reverted; | ||||
|                 // Check if PNG/AVAX LP is equal 0
 | ||||
|                 balance = await pangolinLPToken.balanceOf(dsaWallet0.address); | ||||
|                 expect(balance).to.be.eq(lpAmount); | ||||
|                 const new_png_balance = await PNG.balanceOf(dsaWallet0.address); | ||||
|                 expect(new_png_balance).to.be.gt(png_balance); | ||||
|             }); | ||||
|         }); | ||||
| 
 | ||||
|         describe("emergencyWithdrawLpStake function", function () { | ||||
|             // Deposit LP again
 | ||||
|             before(async () => { | ||||
|                 const spells = [ | ||||
|                     { | ||||
|                         connector: pangolinStakeConnectorName, | ||||
|                         method: "depositLpStake", | ||||
|                         args: [ | ||||
|                             0, | ||||
|                             lpAmount, | ||||
|                             0, | ||||
|                             0 | ||||
|                         ] | ||||
|                     } | ||||
|                 ]; | ||||
|                 await dsaWallet0.connect(wallet0).cast( | ||||
|                     ...encodeSpells(spells), | ||||
|                     wallet1.address | ||||
|                 ) | ||||
|             }); | ||||
| 
 | ||||
|             it("Check if success in emergencyWithdrawLpStake", async function () { | ||||
|                 let balance = await pangolinLPToken.balanceOf(dsaWallet0.address); | ||||
|                 const amount = lpAmount.sub(balance) | ||||
|                 const spells = [ | ||||
|                     { | ||||
|                         connector: pangolinStakeConnectorName, | ||||
|                         method: "emergencyWithdrawLpStake", | ||||
|                         args: [0] | ||||
|                     } | ||||
|                 ]; | ||||
|                 await expect( | ||||
|                     dsaWallet0.connect(wallet0).cast( | ||||
|                       ...encodeSpells(spells), | ||||
|                       wallet1.address | ||||
|                     ) | ||||
|                 ).to.be.not.reverted; | ||||
|                 // Check if PNG/AVAX LP is equal 0
 | ||||
|                 balance = await pangolinLPToken.balanceOf(dsaWallet0.address); | ||||
|                 expect(balance).to.be.eq(lpAmount); | ||||
|             }); | ||||
|         }); | ||||
|     }); | ||||
| 
 | ||||
|     describe("Pangolin Staking - Single Stake Test (PNG)", function () { | ||||
|         let pngToken: Contract; | ||||
|         let stakingContract: Contract; | ||||
|         let stakingBalance: BigNumber; | ||||
|         before(async () => { | ||||
|             const amount = ethers.utils.parseEther("100"); // 100 PNG
 | ||||
|             const int_slippage = 0.03 | ||||
|      | ||||
|             const PangolinRouterABI = [ | ||||
|                 "function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)" | ||||
|             ]; | ||||
|      | ||||
|             // Get amount of AVAX for 200 PNG from Pangolin
 | ||||
|             const PangolinRouter = await ethers.getContractAt( | ||||
|                 PangolinRouterABI,  | ||||
|                 "0xE54Ca86531e17Ef3616d22Ca28b0D458b6C89106" | ||||
|             ); | ||||
|             const amounts = await PangolinRouter.getAmountsOut( | ||||
|                 amount,  | ||||
|                 [ | ||||
|                     PNG_ADDRESS,  | ||||
|                     WAVAX_ADDRESS | ||||
|                 ] | ||||
|             ); | ||||
| 
 | ||||
|             const amtA = amounts[0]; | ||||
|             const amtB = amounts[1]; | ||||
|             const unitAmt = (amtB * (1 + int_slippage)) / amtA; | ||||
|             const unitAmount = ethers.utils.parseEther(unitAmt.toString()); | ||||
| 
 | ||||
|             const spells = [ | ||||
|                 { | ||||
|                     connector: pangolinConnectorName, | ||||
|                     method: "buy", | ||||
|                     args: [ | ||||
|                         PNG_ADDRESS,  | ||||
|                         "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",  | ||||
|                         amount,  | ||||
|                         unitAmount,  | ||||
|                         0,  | ||||
|                         0 | ||||
|                     ] | ||||
|                 } | ||||
|             ]; | ||||
|             // Run spell transaction
 | ||||
|             const tx = await dsaWallet0.connect(wallet0).cast( | ||||
|                 ...encodeSpells(spells), wallet1.address | ||||
|             ); | ||||
|             const receipt = await tx.wait(); | ||||
| 
 | ||||
|             pngToken = await ethers.getContractAt(abis.basic.erc20, PNG_ADDRESS); | ||||
|             stakingContract = await ethers.getContractAt(abis.basic.erc20, PNG_STAKING_ADDRESS); | ||||
|         }); | ||||
| 
 | ||||
|         it("Check if has 100 PNG", async function () { | ||||
|             const amount = ethers.utils.parseEther("100"); | ||||
|             const pngBalance = await pngToken.balanceOf(dsaWallet0.address); | ||||
|             expect(pngBalance, `PNG Token is equal 100`).to.be.gt(amount.toString()); | ||||
|         }); | ||||
|      | ||||
|         it("Check if some functions reverts by: Invalid amount, amount cannot be 0", async function () { | ||||
|             const amount = 0; | ||||
|             const getId = 0; | ||||
|             const setId = 0; | ||||
|             let spells = [ | ||||
|                 { | ||||
|                     connector: pangolinStakeConnectorName, | ||||
|                     method: "depositPNGStake", | ||||
|                     args: [ | ||||
|                         PNG_STAKING_ADDRESS,  | ||||
|                         amount,  | ||||
|                         getId,  | ||||
|                         setId | ||||
|                     ] | ||||
|                 } | ||||
|             ]; | ||||
|             await expect( | ||||
|                 dsaWallet0.connect(wallet0).cast( | ||||
|                   ...encodeSpells(spells), | ||||
|                   wallet1.address | ||||
|                 ) | ||||
|             ).to.be.revertedWith("Invalid amount, amount cannot be 0"); | ||||
| 
 | ||||
|             spells[0].method = "withdrawPNGStake" | ||||
|             await expect( | ||||
|                 dsaWallet0.connect(wallet0).cast( | ||||
|                   ...encodeSpells(spells), | ||||
|                   wallet1.address | ||||
|                 ) | ||||
|             ).to.be.revertedWith("Invalid amount, amount cannot be 0"); | ||||
|         }); | ||||
| 
 | ||||
|         describe("depositPNGStake function", function () { | ||||
|             it("Check if reverts by: Invalid amount, amount greater than balance of PNG", async function () { | ||||
|                 const amount = ethers.utils.parseEther("200") | ||||
|                 let spells = [ | ||||
|                     { | ||||
|                         connector: pangolinStakeConnectorName, | ||||
|                         method: "depositPNGStake", | ||||
|                         args: [ | ||||
|                             PNG_STAKING_ADDRESS,  | ||||
|                             amount,  | ||||
|                             0,  | ||||
|                             0 | ||||
|                         ] | ||||
|                     } | ||||
|                 ]; | ||||
|                 await expect( | ||||
|                     dsaWallet0.connect(wallet0).cast( | ||||
|                       ...encodeSpells(spells), | ||||
|                       wallet1.address | ||||
|                     ) | ||||
|                 ).to.be.revertedWith("Invalid amount, amount greater than balance of PNG"); | ||||
|             }); | ||||
| 
 | ||||
|             it("Check if success in depositPNGStake", async function () { | ||||
|                 const amount = await pngToken.balanceOf(dsaWallet0.address); | ||||
|                 let spells = [ | ||||
|                     { | ||||
|                         connector: pangolinStakeConnectorName, | ||||
|                         method: "depositPNGStake", | ||||
|                         args: [ | ||||
|                             PNG_STAKING_ADDRESS,  | ||||
|                             amount,  | ||||
|                             0,  | ||||
|                             0 | ||||
|                         ] | ||||
|                     } | ||||
|                 ]; | ||||
|                 await expect( | ||||
|                     dsaWallet0.connect(wallet0).cast( | ||||
|                       ...encodeSpells(spells), | ||||
|                       wallet1.address | ||||
|                     ) | ||||
|                 ).to.be.not.reverted; | ||||
|                 const new_png_balance = await pngToken.balanceOf(dsaWallet0.address); | ||||
|                 expect(new_png_balance).to.be.eq(0); | ||||
|                 const staking_balance = await stakingContract.balanceOf(dsaWallet0.address); | ||||
|                 expect(staking_balance).to.be.gt(0); | ||||
|                 stakingBalance = staking_balance | ||||
|             }); | ||||
| 
 | ||||
|             it("Check if reverts by: Invalid PNG balance", async function () { | ||||
|                 const amount = ethers.utils.parseEther("100") | ||||
|                 let spells = [ | ||||
|                     { | ||||
|                         connector: pangolinStakeConnectorName, | ||||
|                         method: "depositPNGStake", | ||||
|                         args: [ | ||||
|                             PNG_STAKING_ADDRESS,  | ||||
|                             amount,  | ||||
|                             0,  | ||||
|                             0 | ||||
|                         ] | ||||
|                     } | ||||
|                 ]; | ||||
|                 await expect( | ||||
|                     dsaWallet0.connect(wallet0).cast( | ||||
|                       ...encodeSpells(spells), | ||||
|                       wallet1.address | ||||
|                     ) | ||||
|                 ).to.be.revertedWith("Invalid PNG balance"); | ||||
|             }); | ||||
|         }); | ||||
| 
 | ||||
|         describe("withdrawPNGStake function", function () { | ||||
|             it("Check if reverts by: Invalid amount, amount greater than balance of staking", async function () { | ||||
|                 const amount = ethers.utils.parseEther("200") | ||||
|                 let spells = [ | ||||
|                     { | ||||
|                         connector: pangolinStakeConnectorName, | ||||
|                         method: "withdrawPNGStake", | ||||
|                         args: [ | ||||
|                             PNG_STAKING_ADDRESS,  | ||||
|                             amount,  | ||||
|                             0,  | ||||
|                             0 | ||||
|                         ] | ||||
|                     } | ||||
|                 ]; | ||||
|                 await expect( | ||||
|                     dsaWallet0.connect(wallet0).cast( | ||||
|                       ...encodeSpells(spells), | ||||
|                       wallet1.address | ||||
|                     ) | ||||
|                 ).to.be.revertedWith("Invalid amount, amount greater than balance of staking"); | ||||
|             }); | ||||
| 
 | ||||
|             it("Check if success in withdrawPNGStake", async function () { | ||||
|                 const amount = ethers.utils.parseEther("50"); | ||||
|                 let spells = [ | ||||
|                     { | ||||
|                         connector: pangolinStakeConnectorName, | ||||
|                         method: "withdrawPNGStake", | ||||
|                         args: [ | ||||
|                             PNG_STAKING_ADDRESS,  | ||||
|                             amount,  | ||||
|                             0,  | ||||
|                             0 | ||||
|                         ] | ||||
|                     } | ||||
|                 ]; | ||||
|                 await expect( | ||||
|                     dsaWallet0.connect(wallet0).cast( | ||||
|                       ...encodeSpells(spells), | ||||
|                       wallet1.address | ||||
|                     ) | ||||
|                 ).to.be.not.reverted; | ||||
| 
 | ||||
|                 const balance = await pngToken.balanceOf(dsaWallet0.address); | ||||
|                 expect(balance).to.be.eq(amount); | ||||
|             }); | ||||
|         }); | ||||
| 
 | ||||
|         describe("claimPNGStakeReward function", function () { | ||||
|             it("Check if success in claimPNGStakeReward", async function () { | ||||
|                 // Increase Time in 20 seconds
 | ||||
|                 await hre.network.provider.send("evm_increaseTime", [20]); | ||||
|                 // Mine new block
 | ||||
|                 await hre.network.provider.send("evm_mine"); | ||||
|                 const amount = ethers.utils.parseEther("50"); | ||||
|                 let spells = [ | ||||
|                     { | ||||
|                         connector: pangolinStakeConnectorName, | ||||
|                         method: "claimPNGStakeReward", | ||||
|                         args: [PNG_STAKING_ADDRESS] | ||||
|                     } | ||||
|                 ]; | ||||
|                 await expect( | ||||
|                     dsaWallet0.connect(wallet0).cast( | ||||
|                       ...encodeSpells(spells), | ||||
|                       wallet1.address | ||||
|                     ) | ||||
|                 ).to.be.not.reverted; | ||||
| 
 | ||||
|                 const balance = await pngToken.balanceOf(dsaWallet0.address); | ||||
|                 expect(balance).to.be.gt(amount); | ||||
|             }); | ||||
| 
 | ||||
|             it("Check if reverts by: No rewards to claim", async function () { | ||||
|                 let spells = [ | ||||
|                     { | ||||
|                         connector: pangolinStakeConnectorName, | ||||
|                         method: "claimPNGStakeReward", | ||||
|                         args: [PNG_STAKING_ADDRESS] | ||||
|                     } | ||||
|                 ]; | ||||
|                 await expect( | ||||
|                     dsaWallet0.connect(wallet0).cast( | ||||
|                       ...encodeSpells(spells), | ||||
|                       wallet1.address | ||||
|                     ) | ||||
|                 ).to.be.revertedWith("No rewards to claim"); | ||||
|             }); | ||||
|         }); | ||||
| 
 | ||||
|         describe("exitPNGStake function", function () { | ||||
|             it("Check if success in exitPNGStake", async function () { | ||||
|                 let spells = [ | ||||
|                     { | ||||
|                         connector: pangolinStakeConnectorName, | ||||
|                         method: "exitPNGStake", | ||||
|                         args: [PNG_STAKING_ADDRESS] | ||||
|                     } | ||||
|                 ]; | ||||
|                 await expect( | ||||
|                     dsaWallet0.connect(wallet0).cast( | ||||
|                       ...encodeSpells(spells), | ||||
|                       wallet1.address | ||||
|                     ) | ||||
|                 ).to.be.not.reverted; | ||||
| 
 | ||||
|                 const balance = await stakingContract.balanceOf(dsaWallet0.address); | ||||
|                 expect(balance).to.be.eq(0); | ||||
|             }); | ||||
| 
 | ||||
|             it("Check if reverts by: No balance to exit", async function () { | ||||
|                 let spells = [ | ||||
|                     { | ||||
|                         connector: pangolinStakeConnectorName, | ||||
|                         method: "exitPNGStake", | ||||
|                         args: [PNG_STAKING_ADDRESS] | ||||
|                     } | ||||
|                 ]; | ||||
|                 await expect( | ||||
|                     dsaWallet0.connect(wallet0).cast( | ||||
|                       ...encodeSpells(spells), | ||||
|                       wallet1.address | ||||
|                     ) | ||||
|                 ).to.be.revertedWith("No balance to exit"); | ||||
|             }); | ||||
|         }); | ||||
|     }); | ||||
| }); | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Pedro
						Pedro