mirror of
https://github.com/Instadapp/dsa-connectors.git
synced 2024-07-29 22:37:00 +00:00
commit
89e88c873d
41
contracts/avalanche/connectors/pangolin/exchange/events.sol
Normal file
41
contracts/avalanche/connectors/pangolin/exchange/events.sol
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
pragma solidity ^0.7.0;
|
||||||
|
|
||||||
|
contract Events {
|
||||||
|
event LogDepositLiquidity(
|
||||||
|
address indexed tokenA,
|
||||||
|
address indexed tokenB,
|
||||||
|
uint256 amtA,
|
||||||
|
uint256 amtB,
|
||||||
|
uint256 uniAmount,
|
||||||
|
uint256 getId,
|
||||||
|
uint256 setId
|
||||||
|
);
|
||||||
|
|
||||||
|
event LogWithdrawLiquidity(
|
||||||
|
address indexed tokenA,
|
||||||
|
address indexed tokenB,
|
||||||
|
uint256 amountA,
|
||||||
|
uint256 amountB,
|
||||||
|
uint256 uniAmount,
|
||||||
|
uint256 getId,
|
||||||
|
uint256[] setId
|
||||||
|
);
|
||||||
|
|
||||||
|
event LogBuy(
|
||||||
|
address indexed buyToken,
|
||||||
|
address indexed sellToken,
|
||||||
|
uint256 buyAmt,
|
||||||
|
uint256 sellAmt,
|
||||||
|
uint256 getId,
|
||||||
|
uint256 setId
|
||||||
|
);
|
||||||
|
|
||||||
|
event LogSell(
|
||||||
|
address indexed buyToken,
|
||||||
|
address indexed sellToken,
|
||||||
|
uint256 buyAmt,
|
||||||
|
uint256 sellAmt,
|
||||||
|
uint256 getId,
|
||||||
|
uint256 setId
|
||||||
|
);
|
||||||
|
}
|
146
contracts/avalanche/connectors/pangolin/exchange/helpers.sol
Normal file
146
contracts/avalanche/connectors/pangolin/exchange/helpers.sol
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
pragma solidity ^0.7.0;
|
||||||
|
|
||||||
|
import { TokenInterface } from "../../../common/interfaces.sol";
|
||||||
|
import { DSMath } from "../../../common/math.sol";
|
||||||
|
import { Basic } from "../../../common/basic.sol";
|
||||||
|
import { IPangolinRouter, IPangolinFactory } from "./interface.sol";
|
||||||
|
|
||||||
|
abstract contract Helpers is DSMath, Basic {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Pangolin Router
|
||||||
|
*/
|
||||||
|
IPangolinRouter internal constant router = IPangolinRouter(0xE54Ca86531e17Ef3616d22Ca28b0D458b6C89106);
|
||||||
|
|
||||||
|
function getExpectedBuyAmt(
|
||||||
|
address[] memory paths,
|
||||||
|
uint sellAmt
|
||||||
|
) internal view returns(uint buyAmt) {
|
||||||
|
uint[] memory amts = router.getAmountsOut(
|
||||||
|
sellAmt,
|
||||||
|
paths
|
||||||
|
);
|
||||||
|
buyAmt = amts[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getExpectedSellAmt(
|
||||||
|
address[] memory paths,
|
||||||
|
uint buyAmt
|
||||||
|
) internal view returns(uint sellAmt) {
|
||||||
|
uint[] memory amts = router.getAmountsIn(
|
||||||
|
buyAmt,
|
||||||
|
paths
|
||||||
|
);
|
||||||
|
sellAmt = amts[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkPair(
|
||||||
|
address[] memory paths
|
||||||
|
) internal view {
|
||||||
|
address pair = IPangolinFactory(router.factory()).getPair(paths[0], paths[1]);
|
||||||
|
require(pair != address(0), "No-exchange-address");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPaths(
|
||||||
|
address buyAddr,
|
||||||
|
address sellAddr
|
||||||
|
) internal pure returns(address[] memory paths) {
|
||||||
|
paths = new address[](2);
|
||||||
|
paths[0] = address(sellAddr);
|
||||||
|
paths[1] = address(buyAddr);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMinAmount(
|
||||||
|
TokenInterface token,
|
||||||
|
uint amt,
|
||||||
|
uint slippage
|
||||||
|
) internal view returns(uint minAmt) {
|
||||||
|
uint _amt18 = convertTo18(token.decimals(), amt);
|
||||||
|
minAmt = wmul(_amt18, sub(WAD, slippage));
|
||||||
|
minAmt = convert18ToDec(token.decimals(), minAmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _addLiquidity(
|
||||||
|
address tokenA,
|
||||||
|
address tokenB,
|
||||||
|
uint _amt,
|
||||||
|
uint unitAmt,
|
||||||
|
uint slippage
|
||||||
|
) internal returns (uint _amtA, uint _amtB, uint _liquidity) {
|
||||||
|
(TokenInterface _tokenA, TokenInterface _tokenB) = changeAvaxAddress(tokenA, tokenB);
|
||||||
|
|
||||||
|
_amtA = _amt == uint(-1) ? getTokenBal(TokenInterface(tokenA)) : _amt;
|
||||||
|
_amtB = convert18ToDec(_tokenB.decimals(), wmul(unitAmt, convertTo18(_tokenA.decimals(), _amtA)));
|
||||||
|
|
||||||
|
bool isAvax = address(_tokenA) == wavaxAddr;
|
||||||
|
convertAvaxToWavax(isAvax, _tokenA, _amtA);
|
||||||
|
|
||||||
|
isAvax = address(_tokenB) == wavaxAddr;
|
||||||
|
convertAvaxToWavax(isAvax, _tokenB, _amtB);
|
||||||
|
|
||||||
|
approve(_tokenA, address(router), _amtA);
|
||||||
|
approve(_tokenB, address(router), _amtB);
|
||||||
|
|
||||||
|
uint minAmtA = getMinAmount(_tokenA, _amtA, slippage);
|
||||||
|
uint minAmtB = getMinAmount(_tokenB, _amtB, slippage);
|
||||||
|
(_amtA, _amtB, _liquidity) = router.addLiquidity(
|
||||||
|
address(_tokenA),
|
||||||
|
address(_tokenB),
|
||||||
|
_amtA,
|
||||||
|
_amtB,
|
||||||
|
minAmtA,
|
||||||
|
minAmtB,
|
||||||
|
address(this),
|
||||||
|
block.timestamp + 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _removeLiquidity(
|
||||||
|
address tokenA,
|
||||||
|
address tokenB,
|
||||||
|
uint _amt,
|
||||||
|
uint unitAmtA,
|
||||||
|
uint unitAmtB
|
||||||
|
) internal returns (uint _amtA, uint _amtB, uint _uniAmt) {
|
||||||
|
TokenInterface _tokenA;
|
||||||
|
TokenInterface _tokenB;
|
||||||
|
(_tokenA, _tokenB, _uniAmt) = _getRemoveLiquidityData(
|
||||||
|
tokenA,
|
||||||
|
tokenB,
|
||||||
|
_amt
|
||||||
|
);
|
||||||
|
{
|
||||||
|
uint minAmtA = convert18ToDec(_tokenA.decimals(), wmul(unitAmtA, _uniAmt));
|
||||||
|
uint minAmtB = convert18ToDec(_tokenB.decimals(), wmul(unitAmtB, _uniAmt));
|
||||||
|
(_amtA, _amtB) = router.removeLiquidity(
|
||||||
|
address(_tokenA),
|
||||||
|
address(_tokenB),
|
||||||
|
_uniAmt,
|
||||||
|
minAmtA,
|
||||||
|
minAmtB,
|
||||||
|
address(this),
|
||||||
|
block.timestamp + 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isAvax = address(_tokenA) == wavaxAddr;
|
||||||
|
convertWavaxToAvax(isAvax, _tokenA, _amtA);
|
||||||
|
|
||||||
|
isAvax = address(_tokenB) == wavaxAddr;
|
||||||
|
convertWavaxToAvax(isAvax, _tokenB, _amtB);
|
||||||
|
}
|
||||||
|
|
||||||
|
function _getRemoveLiquidityData(
|
||||||
|
address tokenA,
|
||||||
|
address tokenB,
|
||||||
|
uint _amt
|
||||||
|
) internal returns (TokenInterface _tokenA, TokenInterface _tokenB, uint _uniAmt) {
|
||||||
|
(_tokenA, _tokenB) = changeAvaxAddress(tokenA, tokenB);
|
||||||
|
address exchangeAddr = IPangolinFactory(router.factory()).getPair(address(_tokenA), address(_tokenB));
|
||||||
|
require(exchangeAddr != address(0), "pair-not-found.");
|
||||||
|
|
||||||
|
TokenInterface pngToken = TokenInterface(exchangeAddr);
|
||||||
|
_uniAmt = _amt == uint(-1) ? pngToken.balanceOf(address(this)) : _amt;
|
||||||
|
approve(pngToken, address(router), _uniAmt);
|
||||||
|
}
|
||||||
|
}
|
164
contracts/avalanche/connectors/pangolin/exchange/interface.sol
Normal file
164
contracts/avalanche/connectors/pangolin/exchange/interface.sol
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
pragma solidity >=0.6.2;
|
||||||
|
|
||||||
|
interface IPangolinRouter {
|
||||||
|
function factory() external pure returns (address);
|
||||||
|
function WAVAX() external pure returns (address);
|
||||||
|
|
||||||
|
function addLiquidity(
|
||||||
|
address tokenA,
|
||||||
|
address tokenB,
|
||||||
|
uint amountADesired,
|
||||||
|
uint amountBDesired,
|
||||||
|
uint amountAMin,
|
||||||
|
uint amountBMin,
|
||||||
|
address to,
|
||||||
|
uint deadline
|
||||||
|
) external returns (uint amountA, uint amountB, uint liquidity);
|
||||||
|
|
||||||
|
function addLiquidityAVAX(
|
||||||
|
address token,
|
||||||
|
uint amountTokenDesired,
|
||||||
|
uint amountTokenMin,
|
||||||
|
uint amountAVAXMin,
|
||||||
|
address to,
|
||||||
|
uint deadline
|
||||||
|
) external payable returns (uint amountToken, uint amountAVAX, uint liquidity);
|
||||||
|
|
||||||
|
function removeLiquidity(
|
||||||
|
address tokenA,
|
||||||
|
address tokenB,
|
||||||
|
uint liquidity,
|
||||||
|
uint amountAMin,
|
||||||
|
uint amountBMin,
|
||||||
|
address to,
|
||||||
|
uint deadline
|
||||||
|
) external returns (uint amountA, uint amountB);
|
||||||
|
|
||||||
|
function removeLiquidityAVAX(
|
||||||
|
address token,
|
||||||
|
uint liquidity,
|
||||||
|
uint amountTokenMin,
|
||||||
|
uint amountAVAXMin,
|
||||||
|
address to,
|
||||||
|
uint deadline
|
||||||
|
) external returns (uint amountToken, uint amountAVAX);
|
||||||
|
|
||||||
|
function removeLiquidityWithPermit(
|
||||||
|
address tokenA,
|
||||||
|
address tokenB,
|
||||||
|
uint liquidity,
|
||||||
|
uint amountAMin,
|
||||||
|
uint amountBMin,
|
||||||
|
address to,
|
||||||
|
uint deadline,
|
||||||
|
bool approveMax, uint8 v, bytes32 r, bytes32 s
|
||||||
|
) external returns (uint amountA, uint amountB);
|
||||||
|
|
||||||
|
function removeLiquidityAVAXWithPermit(
|
||||||
|
address token,
|
||||||
|
uint liquidity,
|
||||||
|
uint amountTokenMin,
|
||||||
|
uint amountAVAXMin,
|
||||||
|
address to,
|
||||||
|
uint deadline,
|
||||||
|
bool approveMax, uint8 v, bytes32 r, bytes32 s
|
||||||
|
) external returns (uint amountToken, uint amountAVAX);
|
||||||
|
|
||||||
|
function swapExactTokensForTokens(
|
||||||
|
uint amountIn,
|
||||||
|
uint amountOutMin,
|
||||||
|
address[] calldata path,
|
||||||
|
address to,
|
||||||
|
uint deadline
|
||||||
|
) external returns (uint[] memory amounts);
|
||||||
|
|
||||||
|
function swapTokensForExactTokens(
|
||||||
|
uint amountOut,
|
||||||
|
uint amountInMax,
|
||||||
|
address[] calldata path,
|
||||||
|
address to,
|
||||||
|
uint deadline
|
||||||
|
) external returns (uint[] memory amounts);
|
||||||
|
|
||||||
|
function swapExactAVAXForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
returns (uint[] memory amounts);
|
||||||
|
|
||||||
|
function swapTokensForExactAVAX(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
|
||||||
|
external
|
||||||
|
returns (uint[] memory amounts);
|
||||||
|
|
||||||
|
function swapExactTokensForAVAX(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
|
||||||
|
external
|
||||||
|
returns (uint[] memory amounts);
|
||||||
|
|
||||||
|
function swapAVAXForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
|
||||||
|
external
|
||||||
|
payable
|
||||||
|
returns (uint[] memory amounts);
|
||||||
|
|
||||||
|
function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
|
||||||
|
function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
|
||||||
|
function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
|
||||||
|
function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
|
||||||
|
function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
|
||||||
|
|
||||||
|
function removeLiquidityAVAXSupportingFeeOnTransferTokens(
|
||||||
|
address token,
|
||||||
|
uint liquidity,
|
||||||
|
uint amountTokenMin,
|
||||||
|
uint amountAVAXMin,
|
||||||
|
address to,
|
||||||
|
uint deadline
|
||||||
|
) external returns (uint amountAVAX);
|
||||||
|
|
||||||
|
function removeLiquidityAVAXWithPermitSupportingFeeOnTransferTokens(
|
||||||
|
address token,
|
||||||
|
uint liquidity,
|
||||||
|
uint amountTokenMin,
|
||||||
|
uint amountAVAXMin,
|
||||||
|
address to,
|
||||||
|
uint deadline,
|
||||||
|
bool approveMax, uint8 v, bytes32 r, bytes32 s
|
||||||
|
) external returns (uint amountAVAX);
|
||||||
|
|
||||||
|
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
|
||||||
|
uint amountIn,
|
||||||
|
uint amountOutMin,
|
||||||
|
address[] calldata path,
|
||||||
|
address to,
|
||||||
|
uint deadline
|
||||||
|
) external;
|
||||||
|
|
||||||
|
function swapExactAVAXForTokensSupportingFeeOnTransferTokens(
|
||||||
|
uint amountOutMin,
|
||||||
|
address[] calldata path,
|
||||||
|
address to,
|
||||||
|
uint deadline
|
||||||
|
) external payable;
|
||||||
|
|
||||||
|
function swapExactTokensForAVAXSupportingFeeOnTransferTokens(
|
||||||
|
uint amountIn,
|
||||||
|
uint amountOutMin,
|
||||||
|
address[] calldata path,
|
||||||
|
address to,
|
||||||
|
uint deadline
|
||||||
|
) external;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IPangolinFactory {
|
||||||
|
event PairCreated(address indexed token0, address indexed token1, address pair, uint);
|
||||||
|
|
||||||
|
function getPair(address tokenA, address tokenB) external view returns (address pair);
|
||||||
|
function allPairs(uint) external view returns (address pair);
|
||||||
|
function allPairsLength() external view returns (uint);
|
||||||
|
|
||||||
|
function feeTo() external view returns (address);
|
||||||
|
function feeToSetter() external view returns (address);
|
||||||
|
|
||||||
|
function createPair(address tokenA, address tokenB) external returns (address pair);
|
||||||
|
|
||||||
|
function setFeeTo(address) external;
|
||||||
|
function setFeeToSetter(address) external;
|
||||||
|
}
|
196
contracts/avalanche/connectors/pangolin/exchange/main.sol
Normal file
196
contracts/avalanche/connectors/pangolin/exchange/main.sol
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
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 PangolinResolver is Helpers, Events {
|
||||||
|
/**
|
||||||
|
* @dev Deposit Liquidity.
|
||||||
|
* @notice Deposit Liquidity to a Pangolin pool.
|
||||||
|
* @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 amtB/amtA with slippage.
|
||||||
|
* @param slippage Slippage amount.
|
||||||
|
* @param getId ID to retrieve amtA.
|
||||||
|
* @param setId ID stores the amount of pools tokens received.
|
||||||
|
*/
|
||||||
|
function deposit(
|
||||||
|
address tokenA,
|
||||||
|
address tokenB,
|
||||||
|
uint256 amtA,
|
||||||
|
uint256 unitAmt,
|
||||||
|
uint256 slippage,
|
||||||
|
uint256 getId,
|
||||||
|
uint256 setId
|
||||||
|
) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||||
|
uint _amt = getUint(getId, amtA);
|
||||||
|
|
||||||
|
(uint _amtA, uint _amtB, uint _uniAmt) = _addLiquidity(
|
||||||
|
tokenA,
|
||||||
|
tokenB,
|
||||||
|
_amt,
|
||||||
|
unitAmt,
|
||||||
|
slippage
|
||||||
|
);
|
||||||
|
setUint(setId, _uniAmt);
|
||||||
|
|
||||||
|
_eventName = "LogDepositLiquidity(address,address,uint256,uint256,uint256,uint256,uint256)";
|
||||||
|
_eventParam = abi.encode(tokenA, tokenB, _amtA, _amtB, _uniAmt, getId, setId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Withdraw Liquidity.
|
||||||
|
* @notice Withdraw Liquidity from a Pangolin pool.
|
||||||
|
* @param tokenA The address of token A.(For AVAX: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
|
||||||
|
* @param tokenB The address of token B.(For AVAX: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
|
||||||
|
* @param uniAmt The amount of pool tokens to withdraw.
|
||||||
|
* @param unitAmtA The unit amount of amtA/uniAmt with slippage.
|
||||||
|
* @param unitAmtB The unit amount of amtB/uniAmt with slippage.
|
||||||
|
* @param getId ID to retrieve uniAmt.
|
||||||
|
* @param setIds Array of IDs to store the amount tokens received.
|
||||||
|
*/
|
||||||
|
function withdraw(
|
||||||
|
address tokenA,
|
||||||
|
address tokenB,
|
||||||
|
uint256 uniAmt,
|
||||||
|
uint256 unitAmtA,
|
||||||
|
uint256 unitAmtB,
|
||||||
|
uint256 getId,
|
||||||
|
uint256[] calldata setIds
|
||||||
|
) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||||
|
uint _amt = getUint(getId, uniAmt);
|
||||||
|
|
||||||
|
(uint _amtA, uint _amtB, uint _uniAmt) = _removeLiquidity(
|
||||||
|
tokenA,
|
||||||
|
tokenB,
|
||||||
|
_amt,
|
||||||
|
unitAmtA,
|
||||||
|
unitAmtB
|
||||||
|
);
|
||||||
|
|
||||||
|
setUint(setIds[0], _amtA);
|
||||||
|
setUint(setIds[1], _amtB);
|
||||||
|
|
||||||
|
_eventName = "LogWithdrawLiquidity(address,address,uint256,uint256,uint256,uint256,uint256[])";
|
||||||
|
_eventParam = abi.encode(tokenA, tokenB, _amtA, _amtB, _uniAmt, getId, setIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Buy AVAX/ERC20_Token.
|
||||||
|
* @notice Buy a token using a Pangolin
|
||||||
|
* @param buyAddr The address of the token to buy.(For AVAX: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
|
||||||
|
* @param sellAddr The address of the token to sell.(For AVAX: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
|
||||||
|
* @param buyAmt The amount of tokens to buy.
|
||||||
|
* @param unitAmt The unit amount of sellAmt/buyAmt with slippage.
|
||||||
|
* @param getId ID to retrieve buyAmt.
|
||||||
|
* @param setId ID to store the amount of tokens sold.
|
||||||
|
*/
|
||||||
|
function buy(
|
||||||
|
address buyAddr,
|
||||||
|
address sellAddr,
|
||||||
|
uint256 buyAmt,
|
||||||
|
uint256 unitAmt,
|
||||||
|
uint256 getId,
|
||||||
|
uint256 setId
|
||||||
|
) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||||
|
uint _buyAmt = getUint(getId, buyAmt);
|
||||||
|
(TokenInterface _buyAddr, TokenInterface _sellAddr) = changeAvaxAddress(buyAddr, sellAddr);
|
||||||
|
address[] memory paths = getPaths(address(_buyAddr), address(_sellAddr));
|
||||||
|
|
||||||
|
uint _slippageAmt = convert18ToDec(_sellAddr.decimals(),
|
||||||
|
wmul(unitAmt, convertTo18(_buyAddr.decimals(), _buyAmt))
|
||||||
|
);
|
||||||
|
|
||||||
|
checkPair(paths);
|
||||||
|
uint _expectedAmt = getExpectedSellAmt(paths, _buyAmt);
|
||||||
|
require(_slippageAmt >= _expectedAmt, "Too much slippage");
|
||||||
|
|
||||||
|
bool isAvax = address(_sellAddr) == wavaxAddr;
|
||||||
|
convertAvaxToWavax(isAvax, _sellAddr, _expectedAmt);
|
||||||
|
approve(_sellAddr, address(router), _expectedAmt);
|
||||||
|
|
||||||
|
uint _sellAmt = router.swapTokensForExactTokens(
|
||||||
|
_buyAmt,
|
||||||
|
_expectedAmt,
|
||||||
|
paths,
|
||||||
|
address(this),
|
||||||
|
block.timestamp + 1
|
||||||
|
)[0];
|
||||||
|
|
||||||
|
isAvax = address(_buyAddr) == wavaxAddr;
|
||||||
|
convertWavaxToAvax(isAvax, _buyAddr, _buyAmt);
|
||||||
|
|
||||||
|
setUint(setId, _sellAmt);
|
||||||
|
|
||||||
|
_eventName = "LogBuy(address,address,uint256,uint256,uint256,uint256)";
|
||||||
|
_eventParam = abi.encode(buyAddr, sellAddr, _buyAmt, _sellAmt, getId, setId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Sell AVAX/ERC20_Token.
|
||||||
|
* @notice Sell a token using a Pangolin
|
||||||
|
* @param buyAddr The address of the token to buy.(For AVAX: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
|
||||||
|
* @param sellAddr The address of the token to sell.(For AVAX: 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)
|
||||||
|
* @param sellAmt The amount of the token to sell.
|
||||||
|
* @param unitAmt The unit amount of buyAmt/sellAmt with slippage.
|
||||||
|
* @param getId ID to retrieve sellAmt.
|
||||||
|
* @param setId ID stores the amount of token brought.
|
||||||
|
*/
|
||||||
|
function sell(
|
||||||
|
address buyAddr,
|
||||||
|
address sellAddr,
|
||||||
|
uint256 sellAmt,
|
||||||
|
uint256 unitAmt,
|
||||||
|
uint256 getId,
|
||||||
|
uint256 setId
|
||||||
|
) external payable returns (string memory _eventName, bytes memory _eventParam) {
|
||||||
|
uint _sellAmt = getUint(getId, sellAmt);
|
||||||
|
(TokenInterface _buyAddr, TokenInterface _sellAddr) = changeAvaxAddress(buyAddr, sellAddr);
|
||||||
|
address[] memory paths = getPaths(address(_buyAddr), address(_sellAddr));
|
||||||
|
|
||||||
|
if (_sellAmt == uint(-1)) {
|
||||||
|
_sellAmt = sellAddr == avaxAddr ?
|
||||||
|
address(this).balance :
|
||||||
|
_sellAddr.balanceOf(address(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint _slippageAmt = convert18ToDec(_buyAddr.decimals(),
|
||||||
|
wmul(unitAmt, convertTo18(_sellAddr.decimals(), _sellAmt))
|
||||||
|
);
|
||||||
|
|
||||||
|
checkPair(paths);
|
||||||
|
uint _expectedAmt = getExpectedBuyAmt(paths, _sellAmt);
|
||||||
|
require(_slippageAmt <= _expectedAmt, "Too much slippage");
|
||||||
|
|
||||||
|
bool isAvax = address(_sellAddr) == wavaxAddr;
|
||||||
|
convertAvaxToWavax(isAvax, _sellAddr, _sellAmt);
|
||||||
|
approve(_sellAddr, address(router), _sellAmt);
|
||||||
|
|
||||||
|
uint _buyAmt = router.swapExactTokensForTokens(
|
||||||
|
_sellAmt,
|
||||||
|
_expectedAmt,
|
||||||
|
paths,
|
||||||
|
address(this),
|
||||||
|
block.timestamp + 1
|
||||||
|
)[1];
|
||||||
|
|
||||||
|
isAvax = address(_buyAddr) == wavaxAddr;
|
||||||
|
convertWavaxToAvax(isAvax, _buyAddr, _buyAmt);
|
||||||
|
|
||||||
|
setUint(setId, _buyAmt);
|
||||||
|
|
||||||
|
_eventName = "LogSell(address,address,uint256,uint256,uint256,uint256)";
|
||||||
|
_eventParam = abi.encode(buyAddr, sellAddr, _buyAmt, _sellAmt, getId, setId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
contract ConnectV2PngAvalanche is PangolinResolver {
|
||||||
|
string public constant name = "Pangolin-v1";
|
||||||
|
}
|
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";
|
||||||
|
}
|
282
test/avalanche/pangolin/pangolin_exchange.test.ts
Normal file
282
test/avalanche/pangolin/pangolin_exchange.test.ts
Normal file
|
@ -0,0 +1,282 @@
|
||||||
|
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 } from "ethers";
|
||||||
|
|
||||||
|
import { ConnectV2PngAvalanche__factory } from "../../../typechain";
|
||||||
|
|
||||||
|
const PNG_ADDRESS = "0x60781C2586D68229fde47564546784ab3fACA982";
|
||||||
|
const WAVAX_ADDRESS = "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7";
|
||||||
|
const PNG_AVAX_LP_ADDRESS = "0xd7538cABBf8605BdE1f4901B47B8D42c61DE0367";
|
||||||
|
|
||||||
|
describe("Pangolin DEX - Avalanche", function () {
|
||||||
|
const pangolinConnectorName = "PANGOLIN-TEST-A"
|
||||||
|
|
||||||
|
let dsaWallet0: Contract;
|
||||||
|
let masterSigner: Signer;
|
||||||
|
let instaConnectorsV2: Contract;
|
||||||
|
let pangolinConnector: 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
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Should have contracts deployed.", async function () {
|
||||||
|
expect(!!instaConnectorsV2.address).to.be.true;
|
||||||
|
expect(!!pangolinConnector.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("Main - PANGOLIN PNG/AVAX Liquidity Test", function () {
|
||||||
|
|
||||||
|
it("Should use pangolin to swap AVAX for PNG, and deposit to PNG/AVAX LP", async function () {
|
||||||
|
const amount = ethers.utils.parseEther("100"); // 100 PNG
|
||||||
|
const int_slippage = 0.03
|
||||||
|
const slippage = ethers.utils.parseEther(int_slippage.toString());
|
||||||
|
const setId = "83528353";
|
||||||
|
|
||||||
|
const PangolinRouterABI = [
|
||||||
|
"function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)"
|
||||||
|
];
|
||||||
|
|
||||||
|
// Get amount of AVAX for 100 POOL 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,
|
||||||
|
setId
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
connector: pangolinConnectorName,
|
||||||
|
method: "deposit",
|
||||||
|
args: [
|
||||||
|
PNG_ADDRESS,
|
||||||
|
"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
|
||||||
|
amount,
|
||||||
|
unitAmount,
|
||||||
|
slippage,
|
||||||
|
0,
|
||||||
|
setId
|
||||||
|
]
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Before Spell
|
||||||
|
let avaxBalance = await ethers.provider.getBalance(dsaWallet0.address);
|
||||||
|
expect(avaxBalance, `AVAX Balance equals 10`).to.be.eq(ethers.utils.parseEther("10"));
|
||||||
|
|
||||||
|
let pngToken = await ethers.getContractAt(abis.basic.erc20, PNG_ADDRESS);
|
||||||
|
const pngBalance = await pngToken.balanceOf(dsaWallet0.address);
|
||||||
|
expect(pngBalance, `PNG Token greater than 0`).to.be.eq(0);
|
||||||
|
|
||||||
|
let pangolinLPToken = await ethers.getContractAt(
|
||||||
|
abis.basic.erc20,
|
||||||
|
PNG_AVAX_LP_ADDRESS
|
||||||
|
);
|
||||||
|
const pangolinPoolAVAXBalance = await pangolinLPToken.balanceOf(dsaWallet0.address);
|
||||||
|
expect(pangolinPoolAVAXBalance, `Pangolin PNG/AVAX LP equals 0`).to.be.eq(0);
|
||||||
|
|
||||||
|
// Run spell transaction
|
||||||
|
const tx = await dsaWallet0.connect(wallet0).cast(
|
||||||
|
...encodeSpells(spells), wallet1.address
|
||||||
|
);
|
||||||
|
const receipt = await tx.wait();
|
||||||
|
|
||||||
|
// After spell
|
||||||
|
avaxBalance = await ethers.provider.getBalance(dsaWallet0.address);
|
||||||
|
expect(avaxBalance, `AVAX Balance less than 10`).to.be.lt(ethers.utils.parseEther("10"));
|
||||||
|
|
||||||
|
const pngBalanceAfter = await pngToken.balanceOf(dsaWallet0.address)
|
||||||
|
expect(pngBalanceAfter, `PNG Token to be same after spell`).to.be.eq(pngBalance);
|
||||||
|
|
||||||
|
const pangolinPoolAVAXBalanceAfter = await pangolinLPToken.balanceOf(dsaWallet0.address);
|
||||||
|
expect(
|
||||||
|
pangolinPoolAVAXBalanceAfter,
|
||||||
|
`Pangolin PNG/AVAX LP greater than 0`
|
||||||
|
).to.be.gt(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should use pangolin to withdraw to PNG/AVAX LP, and swap PNG for AVAX", async function () {
|
||||||
|
const amount = ethers.utils.parseEther("100"); // 100 PNG
|
||||||
|
const int_slippage = 0.03
|
||||||
|
|
||||||
|
// Before Spell
|
||||||
|
let avaxBalance = await ethers.provider.getBalance(dsaWallet0.address);
|
||||||
|
let pngToken = await ethers.getContractAt(abis.basic.erc20, PNG_ADDRESS);
|
||||||
|
let pangolinLPToken = await ethers.getContractAt(
|
||||||
|
abis.basic.erc20,
|
||||||
|
PNG_AVAX_LP_ADDRESS
|
||||||
|
);
|
||||||
|
|
||||||
|
const pngBalance = await pngToken.balanceOf(dsaWallet0.address)
|
||||||
|
expect(pngBalance, `PNG Token balance equal to 0`).to.be.eq(0);
|
||||||
|
|
||||||
|
const pangolinPoolAVAXBalance = await pangolinLPToken.balanceOf(dsaWallet0.address);
|
||||||
|
expect(pangolinPoolAVAXBalance, `Pangolin PNG/AVAX LP greater than 0`).to.be.gt(0);
|
||||||
|
|
||||||
|
const PangolinRouterABI = [
|
||||||
|
"function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)"
|
||||||
|
];
|
||||||
|
|
||||||
|
// Get amount of avax for 100 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 unitAmtA = ethers.utils.parseEther(
|
||||||
|
(amtA * (1 - int_slippage) / pangolinPoolAVAXBalance).toString()
|
||||||
|
);
|
||||||
|
const unitAmtB = ethers.utils.parseEther(
|
||||||
|
(amtB * (1 - int_slippage) / pangolinPoolAVAXBalance).toString()
|
||||||
|
);
|
||||||
|
|
||||||
|
let spells = [
|
||||||
|
{
|
||||||
|
connector: pangolinConnectorName,
|
||||||
|
method: "withdraw",
|
||||||
|
args: [
|
||||||
|
PNG_ADDRESS,
|
||||||
|
"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
|
||||||
|
pangolinPoolAVAXBalance,
|
||||||
|
unitAmtA,
|
||||||
|
unitAmtB,
|
||||||
|
0,
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Run spell transaction (withdraw token of pool)
|
||||||
|
const tx = await dsaWallet0.connect(wallet0).cast(
|
||||||
|
...encodeSpells(spells),
|
||||||
|
wallet1.address
|
||||||
|
);
|
||||||
|
const receipt = await tx.wait();
|
||||||
|
|
||||||
|
// After spell
|
||||||
|
const pangolinPoolAVAXBalanceAfter = await pangolinLPToken.balanceOf(
|
||||||
|
dsaWallet0.address
|
||||||
|
);
|
||||||
|
expect(pangolinPoolAVAXBalanceAfter, `Pangolin PNG/AVAX LP equal 0`).to.be.eq(0);
|
||||||
|
|
||||||
|
let pngBalanceAfter = await pngToken.balanceOf(dsaWallet0.address);
|
||||||
|
expect(pngBalanceAfter, `PNG Token balance greater than`).to.be.gt(0);
|
||||||
|
const unitAmt = amount.div(pngBalanceAfter);
|
||||||
|
|
||||||
|
spells = [
|
||||||
|
{
|
||||||
|
connector: pangolinConnectorName,
|
||||||
|
method: "sell",
|
||||||
|
args: [
|
||||||
|
"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
|
||||||
|
PNG_ADDRESS,
|
||||||
|
pngBalanceAfter,
|
||||||
|
unitAmt,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Run spell transaction (withdraw token of pool)
|
||||||
|
const tx2 = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address);
|
||||||
|
const receipt2 = await tx2.wait();
|
||||||
|
|
||||||
|
let avaxBalanceAfter = await ethers.provider.getBalance(dsaWallet0.address);
|
||||||
|
expect(
|
||||||
|
avaxBalanceAfter,
|
||||||
|
`AVAX Balance After greater than AVAX Balance Before`
|
||||||
|
).to.be.gt(avaxBalance);
|
||||||
|
|
||||||
|
pngBalanceAfter = await pngToken.balanceOf(dsaWallet0.address);
|
||||||
|
expect(pngBalanceAfter, `PNG Token balance equal 0`).to.be.eq(0);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
});
|
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