Merge pull request #7 from InstaDApp/testcases

Testcases
This commit is contained in:
Sowmay Jain 2020-09-01 21:11:27 +10:00 committed by GitHub
commit 80d318bcbb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 12212 additions and 265 deletions

1
.env.example Normal file
View File

@ -0,0 +1 @@
INFURA_KEY=[INFURA_PROJECT_ID]

2
.gitignore vendored
View File

@ -7,3 +7,5 @@ build/*
#Vim
*.swp
.env

View File

@ -33,19 +33,20 @@ interface RateInterface {
contract PoolToken is ReentrancyGuard, DSMath, ERC20Pausable {
using SafeERC20 for IERC20;
event LogDeploy(address token, uint amount);
event LogDeploy(address indexed dsa, address token, uint amount);
event LogExchangeRate(uint exchangeRate, uint tokenBalance, uint insuranceAmt);
event LogSettle(uint settleTime);
event LogDeposit(uint depositAmt, uint poolMintAmt);
event LogWithdraw(uint withdrawAmt, uint poolBurnAmt, uint feeAmt);
event LogSettle(uint settleBlock);
event LogDeposit(address indexed user, uint depositAmt, uint poolMintAmt);
event LogWithdraw(address indexed user, uint withdrawAmt, uint poolBurnAmt, uint feeAmt);
event LogAddInsurance(uint amount);
event LogWithdrawInsurance(uint amount);
event LogPausePool(bool);
IERC20 public immutable baseToken; // Base token. Eg:- DAI, USDC, etc.
RegistryInterface public immutable registry; // Pool Registry
IndexInterface public constant instaIndex = IndexInterface(0x2971AdFa57b20E5a416aE5a708A8655A9c74f723); // Main Index
uint private tokenBalance; // total token balance since last rebalancing
uint private tokenBalance; // total token balance
uint public exchangeRate; // initial 1 token = 1
uint public insuranceAmt; // insurance amount to keep pool safe
@ -65,44 +66,69 @@ contract PoolToken is ReentrancyGuard, DSMath, ERC20Pausable {
_;
}
/**
* @dev Deploy assets to DSA.
* @param _dsa DSA address
* @param token token address
* @param amount token amount
*/
function deploy(address _dsa, address token, uint amount) public isChief {
require(registry.isDsa(address(this), _dsa), "not-autheticated-dsa");
require(AccountInterface(_dsa).isAuth(address(this)), "token-pool-not-auth");
if (token == address(0)) {
if (token == address(0)) { // pool base token
baseToken.safeTransfer(_dsa, amount);
} else if (token == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE){
} else if (token == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE){ // non-pool ethereum
payable(_dsa).transfer(amount);
} else {
} else { // non-pool other tokens
IERC20(token).safeTransfer(_dsa, amount);
}
emit LogDeploy(token, amount);
emit LogDeploy(_dsa, token, amount);
}
/**
* @dev get pool token rate
* @param tokenAmt total token amount
*/
function getCurrentRate(uint tokenAmt) public view returns (uint) {
return wdiv(totalSupply(), tokenAmt);
}
/**
* @dev sets exchange rates
*/
function setExchangeRate() public isChief {
uint _previousRate = exchangeRate;
uint _totalToken = RateInterface(registry.poolLogic(address(this))).getTotalToken();
_totalToken = sub(_totalToken, insuranceAmt);
uint _currentRate = wdiv(totalSupply(), _totalToken);
require(_currentRate != 0, "currentRate-is-0");
if (_currentRate > _previousRate) {
uint difTkn = sub(tokenBalance, _totalToken);
if (difTkn < insuranceAmt) {
insuranceAmt = sub(insuranceAmt, difTkn);
uint _currentRate = getCurrentRate(_totalToken);
require(_currentRate != 0, "current-rate-is-zero");
if (_currentRate > _previousRate) { // loss => deduct partially/fully from insurance amount
uint _loss = sub(tokenBalance, _totalToken);
if (_loss <= insuranceAmt) {
insuranceAmt = sub(insuranceAmt, _loss);
_currentRate = _previousRate;
} else {
tokenBalance = add(_totalToken, insuranceAmt);
_currentRate = wdiv(totalSupply(), tokenBalance);
insuranceAmt = 0;
_currentRate = getCurrentRate(tokenBalance);
}
} else {
} else { // profit => add to insurance amount
uint insureFeeAmt = wmul(sub(_totalToken, tokenBalance), registry.insureFee(address(this)));
insuranceAmt = add(insuranceAmt, insureFeeAmt);
tokenBalance = sub(_totalToken, insureFeeAmt);
_currentRate = wdiv(totalSupply(), tokenBalance);
_currentRate = getCurrentRate(tokenBalance);
}
exchangeRate = _currentRate;
emit LogExchangeRate(exchangeRate, tokenBalance, insuranceAmt);
}
/**
* @dev Settle the assets on dsa and update exchange rate
* @param _dsa DSA address
* @param _targets array of connector's address
* @param _datas array of connector's function calldata
* @param _origin origin address
*/
function settle(address _dsa, address[] calldata _targets, bytes[] calldata _datas, address _origin) external isChief {
require(registry.isDsa(address(this), _dsa), "not-autheticated-dsa");
AccountInterface dsaWallet = AccountInterface(_dsa);
@ -110,35 +136,48 @@ contract PoolToken is ReentrancyGuard, DSMath, ERC20Pausable {
dsaWallet.cast(_targets, _datas, _origin);
}
require(dsaWallet.isAuth(address(this)), "token-pool-not-auth");
setExchangeRate();
emit LogSettle(block.timestamp);
emit LogSettle(block.number);
}
function deposit(uint tknAmt) external whenNotPaused payable returns(uint) {
uint _newTokenBal = add(tokenBalance, tknAmt);
/**
* @dev Deposit token.
* @param tknAmt token amount
* @return _mintAmt amount of wrap token minted
*/
function deposit(uint tknAmt) external whenNotPaused payable returns (uint _mintAmt) {
require(msg.value == 0, "non-eth-pool");
tokenBalance = add(tokenBalance, tknAmt);
baseToken.safeTransferFrom(msg.sender, address(this), tknAmt);
uint _mintAmt = wmul(tknAmt, exchangeRate);
_mintAmt = wmul(tknAmt, exchangeRate);
_mint(msg.sender, _mintAmt);
emit LogDeposit(tknAmt, _mintAmt);
emit LogDeposit(msg.sender, tknAmt, _mintAmt);
}
/**
* @dev Withdraw tokens.
* @param tknAmt token amount
* @param to withdraw tokens to address
* @return _tknAmt amount of token withdrawn
*/
function withdraw(uint tknAmt, address to) external nonReentrant whenNotPaused returns (uint _tknAmt) {
uint poolBal = baseToken.balanceOf(address(this));
require(tknAmt <= poolBal, "not-enough-liquidity-available");
require(to != address(0), "to-address-not-vaild");
uint _bal = balanceOf(msg.sender);
uint _tknBal = wdiv(_bal, exchangeRate);
uint _burnAmt;
if (tknAmt == uint(-1)) {
if (tknAmt >= _tknBal) {
_burnAmt = _bal;
_tknAmt = _tknBal;
} else {
require(tknAmt <= _tknBal, "balance-exceeded");
_burnAmt = wmul(tknAmt, exchangeRate);
_tknAmt = tknAmt;
}
require(_tknAmt <= poolBal, "not-enough-liquidity-available");
tokenBalance = sub(tokenBalance, _tknAmt);
_burn(msg.sender, _burnAmt);
@ -152,28 +191,36 @@ contract PoolToken is ReentrancyGuard, DSMath, ERC20Pausable {
baseToken.safeTransfer(to, _tknAmt);
emit LogWithdraw(tknAmt, _burnAmt, _feeAmt);
emit LogWithdraw(msg.sender, _tknAmt, _burnAmt, _feeAmt);
}
/**
* @dev Add Insurance to the pool.
* @param tknAmt insurance token amount to add
*/
function addInsurance(uint tknAmt) external {
baseToken.safeTransferFrom(msg.sender, address(this), tknAmt);
insuranceAmt = add(insuranceAmt, tknAmt);
emit LogAddInsurance(tknAmt);
}
/**
* @dev Withdraw Insurance from the pool.
* @notice only master can call this function.
* @param tknAmt insurance token amount to remove
*/
function withdrawInsurance(uint tknAmt) external {
require(msg.sender == instaIndex.master(), "not-master");
require(tknAmt <= insuranceAmt || tknAmt == uint(-1), "not-enough-insurance");
if (tknAmt == uint(-1)) {
baseToken.safeTransfer(msg.sender, insuranceAmt);
insuranceAmt = 0;
} else {
baseToken.safeTransfer(msg.sender, tknAmt);
insuranceAmt = sub(insuranceAmt, tknAmt);
}
emit LogAddInsurance(tknAmt);
require(tknAmt <= insuranceAmt, "not-enough-insurance");
baseToken.safeTransfer(msg.sender, tknAmt);
insuranceAmt = sub(insuranceAmt, tknAmt);
emit LogWithdrawInsurance(tknAmt);
}
/**
* @dev Shut the pool.
* @notice only master can call this function.
*/
function shutdown() external {
require(msg.sender == instaIndex.master(), "not-master");
paused() ? _unpause() : _pause();

View File

@ -30,22 +30,23 @@ interface RateInterface {
function getTotalToken() external returns (uint totalUnderlyingTkn);
}
contract PoolToken is ReentrancyGuard, ERC20Pausable, DSMath {
contract PoolETH is ReentrancyGuard, ERC20Pausable, DSMath {
using SafeERC20 for IERC20;
event LogDeploy(address indexed token, uint amount);
event LogDeploy(address indexed dsa, address indexed token, uint amount);
event LogExchangeRate(uint exchangeRate, uint tokenBalance, uint insuranceAmt);
event LogSettle(uint settleTime);
event LogDeposit(uint depositAmt, uint poolMintAmt);
event LogWithdraw(uint withdrawAmt, uint poolBurnAmt, uint feeAmt);
event LogSettle(uint settleBlock);
event LogDeposit(address indexed user, uint depositAmt, uint poolMintAmt);
event LogWithdraw(address indexed user, uint withdrawAmt, uint poolBurnAmt, uint feeAmt);
event LogAddInsurance(uint amount);
event LogWithdrawInsurance(uint amount);
event LogPausePool(bool);
RegistryInterface public immutable registry; // Pool Registry
IndexInterface public constant instaIndex = IndexInterface(0x2971AdFa57b20E5a416aE5a708A8655A9c74f723);
IERC20 public immutable baseToken; // Base token.
uint private tokenBalance; // total token balance since last rebalancing
uint private tokenBalance; // total token balance
uint public exchangeRate = 10 ** 18; // initial 1 token = 1
uint public insuranceAmt; // insurance amount to keep pool safe
@ -64,78 +65,115 @@ contract PoolToken is ReentrancyGuard, ERC20Pausable, DSMath {
_;
}
/**
* @dev Deploy assets to DSA.
* @param _dsa DSA address
* @param token token address
* @param amount token amount
*/
function deploy(address _dsa, address token, uint amount) external isChief {
require(registry.isDsa(address(this), _dsa), "not-autheticated-dsa");
require(AccountInterface(_dsa).isAuth(address(this)), "token-pool-not-auth");
if (token == address(0)) {
require(AccountInterface(_dsa).isAuth(address(this)), "token-pool-not-auth");
if (token == address(0)) { // pool base ETH
payable(_dsa).transfer(amount);
} else {
} else { // non-pool other tokens
IERC20(token).safeTransfer(_dsa, amount);
}
emit LogDeploy(token, amount);
emit LogDeploy(_dsa, token, amount);
}
/**
* @dev get pool token rate
* @param tokenAmt total token amount
*/
function getCurrentRate(uint tokenAmt) public view returns (uint) {
return wdiv(totalSupply(), tokenAmt);
}
/**
* @dev sets exchange rates
*/
function setExchangeRate() public isChief {
uint _previousRate = exchangeRate;
uint _totalToken = RateInterface(registry.poolLogic(address(this))).getTotalToken();
_totalToken = sub(_totalToken, insuranceAmt);
uint _currentRate = wdiv(totalSupply(), _totalToken);
require(_currentRate != 0, "currentRate-is-0");
if (_currentRate > _previousRate) {
uint difTkn = sub(tokenBalance, _totalToken);
if (difTkn < insuranceAmt) {
insuranceAmt = sub(insuranceAmt, difTkn);
uint _currentRate = getCurrentRate(_totalToken);
require(_currentRate != 0, "current-rate-is-zero");
if (_currentRate > _previousRate) { // loss => deduct partially/fully from insurance amount
uint _loss = sub(tokenBalance, _totalToken);
if (_loss <= insuranceAmt) {
insuranceAmt = sub(insuranceAmt, _loss);
_currentRate = _previousRate;
} else {
tokenBalance = add(_totalToken, insuranceAmt);
_currentRate = wdiv(totalSupply(), tokenBalance);
insuranceAmt = 0;
_currentRate = getCurrentRate(tokenBalance);
}
} else {
} else { // profit => add to insurance amount
uint insureFeeAmt = wmul(sub(_totalToken, tokenBalance), registry.insureFee(address(this)));
insuranceAmt = add(insuranceAmt, insureFeeAmt);
tokenBalance = sub(_totalToken, insureFeeAmt);
_currentRate = wdiv(totalSupply(), tokenBalance);
_currentRate = getCurrentRate(tokenBalance);
}
exchangeRate = _currentRate;
emit LogExchangeRate(exchangeRate, tokenBalance, insuranceAmt);
}
/**
* @dev Settle the assets on dsa and update exchange rate
* @param _dsa DSA address
* @param _targets array of connector's address
* @param _datas array of connector's function calldata
* @param _origin origin address
*/
function settle(address _dsa, address[] calldata _targets, bytes[] calldata _datas, address _origin) external isChief {
require(registry.isDsa(address(this), _dsa), "not-autheticated-dsa");
AccountInterface dsaWallet = AccountInterface(_dsa);
if (_targets.length > 0 && _datas.length > 0) {
dsaWallet.cast(_targets, _datas, _origin);
}
require(dsaWallet.isAuth(address(this)), "token-pool-not-auth");
require(dsaWallet.isAuth(address(this)), "token-pool-not-auth");
setExchangeRate();
emit LogSettle(block.timestamp);
emit LogSettle(block.number);
}
function deposit(uint tknAmt) public whenNotPaused payable returns(uint) {
/**
* @dev Deposit token.
* @param tknAmt token amount
* @return _mintAmt amount of wrap token minted
*/
function deposit(uint tknAmt) public whenNotPaused payable returns (uint _mintAmt) {
require(tknAmt == msg.value, "unmatched-amount");
uint _newTokenBal = add(tokenBalance, msg.value);
tokenBalance = add(tokenBalance, tknAmt);
uint _mintAmt = wmul(msg.value, exchangeRate);
_mintAmt = wmul(msg.value, exchangeRate);
_mint(msg.sender, _mintAmt);
emit LogDeposit(tknAmt, _mintAmt);
emit LogDeposit(msg.sender, tknAmt, _mintAmt);
}
/**
* @dev Withdraw tokens.
* @param tknAmt token amount
* @param to withdraw tokens to address
* @return _tknAmt amount of token withdrawn
*/
function withdraw(uint tknAmt, address to) external nonReentrant whenNotPaused returns (uint _tknAmt) {
uint poolBal = address(this).balance;
require(tknAmt <= poolBal, "not-enough-liquidity-available");
require(to != address(0), "to-address-not-vaild");
uint _bal = balanceOf(msg.sender);
uint _tknBal = wdiv(_bal, exchangeRate);
uint _burnAmt;
if (tknAmt == uint(-1)) {
if (tknAmt >= _tknBal) {
_burnAmt = _bal;
_tknAmt = _tknBal;
} else {
require(tknAmt <= _tknBal, "balance-exceeded");
_burnAmt = wmul(tknAmt, exchangeRate);
_tknAmt = tknAmt;
}
require(_tknAmt <= poolBal, "not-enough-liquidity-available");
tokenBalance = sub(tokenBalance, _tknAmt);
_burn(msg.sender, _burnAmt);
@ -149,33 +187,40 @@ contract PoolToken is ReentrancyGuard, ERC20Pausable, DSMath {
payable(to).transfer(_tknAmt);
emit LogWithdraw(tknAmt, _burnAmt, _feeAmt);
emit LogWithdraw(msg.sender, _tknAmt, _burnAmt, _feeAmt);
}
/**
* @dev Add Insurance to the pool.
* @param tknAmt insurance token amount to add
*/
function addInsurance(uint tknAmt) external payable {
require(tknAmt == msg.value, "unmatched-amount");
insuranceAmt += tknAmt;
insuranceAmt = add(insuranceAmt, tknAmt);
emit LogAddInsurance(tknAmt);
}
/**
* @dev Withdraw Insurance from the pool.
* @notice only master can call this function.
* @param tknAmt insurance token amount to remove
*/
function withdrawInsurance(uint tknAmt) external {
require(msg.sender == instaIndex.master(), "not-master");
require(tknAmt <= insuranceAmt || tknAmt == uint(-1), "not-enough-insurance");
if (tknAmt == uint(-1)) {
msg.sender.transfer(insuranceAmt);
insuranceAmt = 0;
} else {
msg.sender.transfer(tknAmt);
insuranceAmt = sub(insuranceAmt, tknAmt);
}
emit LogAddInsurance(tknAmt);
require(tknAmt <= insuranceAmt, "not-enough-insurance");
msg.sender.transfer(tknAmt);
insuranceAmt = sub(insuranceAmt, tknAmt);
emit LogWithdrawInsurance(tknAmt);
}
/**
* @dev Shut the pool.
* @notice only master can call this function.
*/
function shutdown() external {
require(msg.sender == instaIndex.master(), "not-master");
paused() ? _unpause() : _pause();
}
receive() external payable {}
}

View File

@ -127,6 +127,11 @@ contract Registry {
emit LogUpdatePool(_pool, isPool[_pool]);
}
/**
* @dev update pool rate logic
* @param _pool pool address
* @param _newLogic new rate logic address
*/
function updatePoolLogic(address _pool, address _newLogic) external isMaster {
require(isPool[_pool], "not-pool");
require(_newLogic != address(0), "invalid-address");
@ -135,6 +140,11 @@ contract Registry {
emit LogUpdatePoolLogic(_pool, _newLogic);
}
/**
* @dev update pool insure fee
* @param _pool pool address
* @param _newFee new fee amount
*/
function updateInsureFee(address _pool, uint _newFee) external isMaster {
require(isPool[_pool], "not-pool");
require(_newFee < 10 ** 18, "insure-fee-limit-reached");
@ -143,6 +153,11 @@ contract Registry {
emit LogUpdateInsureFee(_pool, _newFee);
}
/**
* @dev update pool withdrawal fee
* @param _pool pool address
* @param _newFee new withdrawal fee amount
*/
function updateWithdrawalFee(address _pool, uint _newFee) external isMaster {
require(isPool[_pool], "not-pool");
require(_newFee < 1 * 10 ** 16, "withdrawal-fee-limit-reached");
@ -151,6 +166,11 @@ contract Registry {
emit LogUpdateWithdrawalFee(_pool, _newFee);
}
/**
* @dev add dsa for a pool
* @param _pool pool address
* @param _dsa DSA address
*/
function addDsa(address _pool, address _dsa) external isMaster {
require(isPool[_pool], "not-pool");
if (_dsa == address(0)) _dsa = instaIndex.build(_pool, 1, address(this));
@ -159,6 +179,11 @@ contract Registry {
emit LogNewDSA(_pool, _dsa);
}
/**
* @dev remove dsa from a pool
* @param _pool pool address
* @param _dsa DSA address
*/
function removeDsa(address _pool, address _dsa) external isMaster {
require(isPool[_pool], "not-pool");
require(isDsa[_pool][_dsa], "not-dsa-for-pool");

View File

@ -0,0 +1,28 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.8;
interface TokenInterface {
function balanceOf(address) external view returns (uint);
function transfer(address, uint) external returns (bool);
}
contract DaiRateLogic {
address poolToken;
TokenInterface baseToken;
function getTotalToken() public returns (uint) {
uint bal = baseToken.balanceOf(address(this));
bal += baseToken.balanceOf(address(poolToken));
return bal;
}
function reduceDai(uint amt) public {
baseToken.transfer(address(this), amt);
}
constructor (address daiPool, address dai) public {
poolToken = address(daiPool);
baseToken = TokenInterface(address(dai));
}
}

View File

@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.8;
contract EthRateLogic {
address poolToken;
function getTotalToken() public returns (uint) {
uint bal = (address(this).balance);
bal += (address(poolToken).balance);
return bal;
}
function reduceETH(uint amt) public {
payable(address(0)).transfer(amt);
}
constructor (address ethPool) public {
poolToken = address(ethPool);
}
receive() external payable {}
}

4
migrations/2_registry.js Normal file
View File

@ -0,0 +1,4 @@
const Registry = artifacts.require("Registry");
module.exports = async function(deployer, networks, accounts) {
await deployer.deploy(Registry, accounts[0]); //deploy registry.sol contract
};

10
migrations/3_erc20Pool.js Normal file
View File

@ -0,0 +1,10 @@
const PoolToken = artifacts.require("PoolToken");
const DaiRateLogic = artifacts.require("DaiRateLogic");
const Registry = artifacts.require("Registry");
module.exports = async function(deployer, networks, accounts) {
var DAI_Addr = "0x6B175474E89094C44Da98b954EedeAC495271d0F";
var registryInstance = await Registry.deployed();
var daiPoolInstance = await deployer.deploy(PoolToken, registryInstance.address, "Insta DAI", "IDAI", DAI_Addr);
var daiRateInstance = await deployer.deploy(DaiRateLogic, daiPoolInstance.address, DAI_Addr);
};

11
migrations/4_ethPool.js Normal file
View File

@ -0,0 +1,11 @@
const PoolETH = artifacts.require("PoolETH");
const Registry = artifacts.require("Registry");
const EthRateLogic = artifacts.require("EthRateLogic");
module.exports = async function(deployer, networks, accounts) {
var ETH_Addr = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
var registryInstance = await Registry.deployed();
var ethPoolInstance = await deployer.deploy(PoolETH, registryInstance.address, "Insta ETH", "IETH", ETH_Addr);
var ethRateInstance = await deployer.deploy(EthRateLogic, ethPoolInstance.address);
};

11372
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,9 @@
"description": "DSA Yield ERC20 Pool Contract",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"deploy:fork": "truffle deploy",
"test:fork": "truffle test",
"ganache:fork": "dotenv -- cross-var ganache-cli --fork https://mainnet.infura.io/v3/%INFURA_KEY% --unlock 0xfCD22438AD6eD564a1C26151Df73F6B33B817B56 --unlock 0x6b175474e89094c44da98b954eedeac495271d0f --u 0x9eb7f2591ed42dee9315b6e2aaf21ba85ea69f8c"
},
"repository": {
"type": "git",
@ -18,7 +20,11 @@
"homepage": "https://github.com/InstaDApp/dsa-yield-contract#readme",
"dependencies": {
"@openzeppelin/contracts": "^3.1.0",
"cross-var": "^1.1.0",
"dotenv": "^7.0.0",
"dotenv-cli": "^3.2.0",
"solc": "^0.6.8",
"chai": "^4.2.0",
"truffle-assertions": "^0.9.2",
"truffle-hdwallet-provider": "^1.0.17",
"truffle-typings": "^1.0.8",
@ -26,6 +32,7 @@
},
"devDependencies": {
"@nomiclabs/buidler": "^1.4.3",
"@nomiclabs/buidler-ganache": "^1.3.3",
"@nomiclabs/buidler-truffle5": "^1.3.4",
"@nomiclabs/buidler-web3": "^1.3.4",
"@openzeppelin/test-helpers": "^0.5.6",

5
scripts/utils.js Normal file
View File

@ -0,0 +1,5 @@
module.exports.asyncForEach = async (array, callback) => {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
};

122
test/1_registry.js Normal file
View File

@ -0,0 +1,122 @@
const { BN, ether, balance } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const RegistryContract = artifacts.require("Registry");
const PoolTokenContract = artifacts.require("PoolToken");
const PoolETHContract = artifacts.require("PoolETH");
const DaiRateLogic = artifacts.require("DaiRateLogic");
const EthRateLogic = artifacts.require("EthRateLogic");
const masterAddr = "0xfCD22438AD6eD564a1C26151Df73F6B33B817B56"
contract('Registry.sol', async accounts => {
let ethAddr = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
let daiAddr = "0x6b175474e89094c44da98b954eedeac495271d0f";
let defaultAddr = "0x0000000000000000000000000000000000000000";
let ethPoolInstance;
let daiPoolInstance;
let registryInstance;
let ethRateLogicInstance;
let daiRateLogicInstance;
before(async() => {
registryInstance = await RegistryContract.deployed();
ethPoolInstance = await PoolETHContract.deployed();
daiPoolInstance = await PoolTokenContract.deployed();
ethRateLogicInstance = await EthRateLogic.deployed();
daiRateLogicInstance = await DaiRateLogic.deployed();
})
it('should send ether to the Master address', async () => {
await web3.eth.sendTransaction({
from: accounts[0],
to: masterAddr,
value: ether('10')
});
const ethBalance = await balance.current(masterAddr);
expect(new BN(ethBalance)).to.be.bignumber.least(new BN(ether('10')));
});
it('should add ETH pool in registry', async () => {
await addPool(registryInstance, ethPoolInstance.address, ethAddr);
});
it('should enable ETH pool in registry', async () => {
await enablePool(registryInstance, ethPoolInstance.address);
});
it('should remove ETH pool in registry', async () => {
await removePool(registryInstance, ethAddr);
});
it('should disable ETH pool in registry', async () => {
await disablePool(registryInstance, ethPoolInstance.address);
});
it('should add ETH pool in registry', async () => {
await addPool(registryInstance, ethPoolInstance.address, ethAddr);
});
it('should enable ETH pool in registry', async () => {
await enablePool(registryInstance, ethPoolInstance.address);
});
it('should add DAI pool in registry', async () => {
await addPool(registryInstance, daiPoolInstance.address, daiAddr);
});
it('should enable DAI pool in registry', async () => {
await enablePool(registryInstance, daiPoolInstance.address);
});
it('should update ETH Logic contract in registry', async () => {
await updateRateLogic(registryInstance, ethPoolInstance.address, ethRateLogicInstance.address);
});
it('should update DAI Logic contract in registry', async () => {
await updateRateLogic(registryInstance, daiPoolInstance.address, daiRateLogicInstance.address);
});
});
async function addPool(registryInstance, poolAddr, tokenAddr) {
await registryInstance.addPool(tokenAddr, poolAddr, {from: masterAddr});
var _poolAddr = await registryInstance.poolToken(tokenAddr);
expect(_poolAddr).to.equal(poolAddr);
}
async function removePool(registryInstance, tokenAddr) {
await registryInstance.removePool(tokenAddr, {from: masterAddr});
var _poolAddr = await registryInstance.poolToken(tokenAddr);
expect(_poolAddr).to.equal("0x0000000000000000000000000000000000000000");
}
async function enablePool(registryInstance, poolAddr) {
await registryInstance.updatePool(poolAddr, {from: masterAddr});
var _isPool = await registryInstance.isPool(poolAddr);
expect(_isPool).to.equal(true);
}
async function disablePool(registryInstance, poolAddr) {
await registryInstance.updatePool(poolAddr, {from: masterAddr});
var _isPool = await registryInstance.isPool(poolAddr);
expect(_isPool).to.equal(false);
}
async function updateRateLogic(registryInstance, poolAddr, logicAddr) {
await registryInstance.updatePoolLogic(poolAddr, logicAddr, {from: masterAddr});
var _logicAddr = await registryInstance.poolLogic(poolAddr);
expect(_logicAddr).to.equal(logicAddr);
}

164
test/2_daiPool.js Normal file
View File

@ -0,0 +1,164 @@
const { BN, ether, balance } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const RegistryContract = artifacts.require("Registry");
const PoolTokenContract = artifacts.require("PoolToken");
const PoolETHContract = artifacts.require("PoolETH");
const DaiRateLogic = artifacts.require("DaiRateLogic");
const EthRateLogic = artifacts.require("EthRateLogic");
const masterAddr = "0xfCD22438AD6eD564a1C26151Df73F6B33B817B56"
// ABI
const daiABI = require('./abi/erc20');
const userAddress = '0x9eb7f2591ed42dee9315b6e2aaf21ba85ea69f8c';
const daiAddress = '0x6b175474e89094c44da98b954eedeac495271d0f';
const daiContract = new web3.eth.Contract(daiABI, daiAddress);
contract('DAI Pool', async accounts => {
let ethAddr = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
let daiAddr = "0x6b175474e89094c44da98b954eedeac495271d0f";
let defaultAddr = "0x0000000000000000000000000000000000000000";
let ethPoolInstance;
let daiPoolInstance;
let registryInstance;
let ethRateLogicInstance;
let daiRateLogicInstance;
before(async() => {
registryInstance = await RegistryContract.deployed();
ethPoolInstance = await PoolETHContract.deployed();
daiPoolInstance = await PoolTokenContract.deployed();
ethRateLogicInstance = await EthRateLogic.deployed();
daiRateLogicInstance = await DaiRateLogic.deployed();
})
it('should send ether to the user address', async () => {
// Send 1 eth to userAddress to have gas to send an ERC20 tx.
await web3.eth.sendTransaction({
from: accounts[0],
to: userAddress,
value: ether('1')
});
const ethBalance = await balance.current(userAddress);
expect(new BN(ethBalance)).to.be.bignumber.least(new BN(ether('1')));
});
it('should send ether to the master address', async () => {
// Send 1 eth to userAddress to have gas to send an ERC20 tx.
await web3.eth.sendTransaction({
from: accounts[0],
to: masterAddr,
value: ether('1')
});
const ethBalance = await balance.current(masterAddr);
expect(new BN(ethBalance)).to.be.bignumber.least(new BN(ether('1')));
});
it('should send DAI to the account[0] address', async () => {
await daiContract.methods
.transfer(accounts[0], ether('1000').toString())
.send({ from: userAddress});
const daiBalance = await daiContract.methods.balanceOf(accounts[0]).call();
expect(new BN(daiBalance)).to.be.bignumber.least(ether('1000'));
});
it('should add DAI pool in registry', async () => {
await addPool(registryInstance, daiPoolInstance.address, daiAddr);
});
it('should enable DAI pool in registry', async () => {
await enablePool(registryInstance, daiPoolInstance.address);
});
it('should update DAI Logic contract in registry', async () => {
await updateRateLogic(registryInstance, daiPoolInstance.address, daiRateLogicInstance.address);
});
it('should give DAI allowance for DAI pool', async () => {
await daiContract.methods
.approve(daiPoolInstance.address, ether('1000').toString())
.send({ from: userAddress});
const daiBalance = await daiContract.methods.allowance(userAddress, daiPoolInstance.address).call();
expect(new BN(daiBalance)).to.be.bignumber.least(ether('1000'));
});
it('should deposit 100 DAI in DAI pool', async () => {
var amountInWei = (ether("100")).toString()
await daiPoolInstance.deposit(amountInWei, {from: userAddress});
const daiBalance = await daiContract.methods.balanceOf(daiPoolInstance.address).call();
expect(new BN(daiBalance)).to.be.bignumber.least(amountInWei);
var totalSupply = await daiPoolInstance.totalSupply();
expect(new BN(totalSupply)).to.be.bignumber.least(amountInWei);
});
it('should add profit 10 DAI and calculate exchange rate', async () => {
var amountInWei = new BN(ether("10")).toString()
await daiContract.methods
.transfer(daiRateLogicInstance.address, amountInWei)
.send({ from: userAddress});
var exchangeRateInit = await daiPoolInstance.exchangeRate()
await daiPoolInstance.setExchangeRate({from: masterAddr});
var exchangeRateFinal = await daiPoolInstance.exchangeRate()
expect(exchangeRateInit).to.not.equal(exchangeRateFinal);
});
it('should give DAI allowance for DAI pool(accounts[0])', async () => {
await daiContract.methods
.approve(daiPoolInstance.address, ether('1000').toString())
.send({ from: accounts[0]});
const daiBalance = await daiContract.methods.allowance(accounts[0], daiPoolInstance.address).call();
expect(new BN(daiBalance)).to.be.bignumber.least(ether('1000'));
});
it('should deposit 100 DAI in DAI pool(accounts[0])', async () => {
var amountInWei = (ether("100")).toString()
await daiPoolInstance.deposit(amountInWei, {from: accounts[0]});
const wrapDaiBalance = await daiPoolInstance.balanceOf(accounts[0])
expect(new BN(wrapDaiBalance)).to.be.bignumber.least((ether("90")).toString());
});
it('should withdraw 10 DAI in DAI pool', async () => {
var amountInWei = (ether("10")).toString()
await daiPoolInstance.withdraw(amountInWei, accounts[1], {from: userAddress});
const daiBalance = await daiContract.methods.balanceOf(accounts[1]).call();
expect(new BN(daiBalance)).to.be.bignumber.least(amountInWei);
});
it('should withdraw total DAI in DAI pool', async () => {
var amountInWei = (ether("1000")).toString()
var checkAmt = (ether("90")).toString()
await daiPoolInstance.withdraw(amountInWei, accounts[2], {from: userAddress});
const daiBalance = await daiContract.methods.balanceOf(accounts[2]).call();
expect(new BN(daiBalance)).to.be.bignumber.least(checkAmt);
});
});
async function addPool(registryInstance, poolAddr, tokenAddr) {
await registryInstance.addPool(tokenAddr, poolAddr, {from: masterAddr});
var _poolAddr = await registryInstance.poolToken(tokenAddr);
expect(_poolAddr).to.equal(poolAddr);
}
async function enablePool(registryInstance, poolAddr) {
await registryInstance.updatePool(poolAddr, {from: masterAddr});
var _isPool = await registryInstance.isPool(poolAddr);
expect(_isPool).to.equal(true);
}
async function updateRateLogic(registryInstance, poolAddr, logicAddr) {
await registryInstance.updatePoolLogic(poolAddr, logicAddr, {from: masterAddr});
var _logicAddr = await registryInstance.poolLogic(poolAddr);
expect(_logicAddr).to.equal(logicAddr);
}

119
test/3_ethPool.js Normal file
View File

@ -0,0 +1,119 @@
const { BN, ether, balance } = require('@openzeppelin/test-helpers');
const { expect } = require('chai');
const RegistryContract = artifacts.require("Registry");
const PoolETHContract = artifacts.require("PoolETH");
const EthRateLogic = artifacts.require("EthRateLogic");
const masterAddr = "0xfCD22438AD6eD564a1C26151Df73F6B33B817B56"
// ABI
contract('ETH Pool', async accounts => {
let ethAddr = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE";
let accountA = accounts[0];
let accountB = accounts[1];
let ethPoolInstance;
let registryInstance;
let ethRateLogicInstance;
before(async() => {
registryInstance = await RegistryContract.deployed();
ethPoolInstance = await PoolETHContract.deployed();
ethRateLogicInstance = await EthRateLogic.deployed();
})
it('should send ether to the master address', async () => {
// Send 1 eth to userAddress to have gas to send an ERC20 tx.
await web3.eth.sendTransaction({
from: accounts[0],
to: masterAddr,
value: ether('1')
});
const ethBalance = await balance.current(masterAddr);
expect(new BN(ethBalance)).to.be.bignumber.least(new BN(ether('1')));
});
it('should add ETH pool in registry', async () => {
await addPool(registryInstance, ethPoolInstance.address, ethAddr);
});
it('should enable ETH pool in registry', async () => {
await enablePool(registryInstance, ethPoolInstance.address);
});
it('should update ETH Logic contract in registry', async () => {
await updateRateLogic(registryInstance, ethPoolInstance.address, ethRateLogicInstance.address);
});
it('should deposit 5 ETH in ETH pool', async () => {
var amountInWei = (ether("5")).toString()
await ethPoolInstance.deposit(amountInWei, {from: accountA, value: amountInWei});
const ethBalance = await web3.eth.getBalance(ethPoolInstance.address);
expect(new BN(ethBalance)).to.be.bignumber.least(amountInWei);
var totalSupply = await ethPoolInstance.totalSupply();
expect(new BN(totalSupply)).to.be.bignumber.least(amountInWei);
});
it('should add profit 0.5 ETH and calculate exchange rate', async () => {
var amountInWei = new BN(ether("0.5")).toString()
await web3.eth.sendTransaction({
from: accountA,
to: ethRateLogicInstance.address,
value: amountInWei
});
var exchangeRateInit = await ethPoolInstance.exchangeRate()
await ethPoolInstance.setExchangeRate({from: masterAddr});
var exchangeRateFinal = await ethPoolInstance.exchangeRate()
expect(exchangeRateInit).to.not.equal(exchangeRateFinal);
});
it('should deposit 5 ETH in ETH pool(accountB)', async () => {
var amountInWei = (ether("5")).toString()
await ethPoolInstance.deposit(amountInWei, {from: accountB, value: amountInWei});
const wrapETHBalance = await ethPoolInstance.balanceOf(accountB)
expect(new BN(wrapETHBalance)).to.be.bignumber.least((ether("4")).toString());
});
it('should withdraw 0.5 ETH in ETH pool', async () => {
var amountInWei = (ether("0.5")).toString()
await ethPoolInstance.withdraw(amountInWei, accounts[2], {from: accountA});
const ethBalance = await web3.eth.getBalance(accounts[2]);
expect(new BN(ethBalance)).to.be.bignumber.least(amountInWei);
});
it('should withdraw total ETH in ETH pool', async () => {
var amountInWei = (ether("1000")).toString()
var checkAmt = (ether("4.5")).toString()
await ethPoolInstance.withdraw(amountInWei, accounts[3], {from: accountA});
const ethBalance = await web3.eth.getBalance(accounts[3]);
expect(new BN(ethBalance)).to.be.bignumber.least(checkAmt);
});
});
async function addPool(registryInstance, poolAddr, tokenAddr) {
await registryInstance.addPool(tokenAddr, poolAddr, {from: masterAddr});
var _poolAddr = await registryInstance.poolToken(tokenAddr);
expect(_poolAddr).to.equal(poolAddr);
}
async function enablePool(registryInstance, poolAddr) {
await registryInstance.updatePool(poolAddr, {from: masterAddr});
var _isPool = await registryInstance.isPool(poolAddr);
expect(_isPool).to.equal(true);
}
async function updateRateLogic(registryInstance, poolAddr, logicAddr) {
await registryInstance.updatePoolLogic(poolAddr, logicAddr, {from: masterAddr});
var _logicAddr = await registryInstance.poolLogic(poolAddr);
expect(_logicAddr).to.equal(logicAddr);
}

325
test/abi/erc20.js Normal file
View File

@ -0,0 +1,325 @@
module.exports = [
{
constant: true,
inputs: [],
name: 'name',
outputs: [{ name: '', type: 'bytes32' }],
payable: false,
stateMutability: 'view',
type: 'function'
},
{
constant: false,
inputs: [],
name: 'stop',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function'
},
{
constant: false,
inputs: [
{ name: 'guy', type: 'address' },
{ name: 'wad', type: 'uint256' }
],
name: 'approve',
outputs: [{ name: '', type: 'bool' }],
payable: false,
stateMutability: 'nonpayable',
type: 'function'
},
{
constant: false,
inputs: [{ name: 'owner_', type: 'address' }],
name: 'setOwner',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function'
},
{
constant: true,
inputs: [],
name: 'totalSupply',
outputs: [{ name: '', type: 'uint256' }],
payable: false,
stateMutability: 'view',
type: 'function'
},
{
constant: false,
inputs: [
{ name: 'src', type: 'address' },
{ name: 'dst', type: 'address' },
{ name: 'wad', type: 'uint256' }
],
name: 'transferFrom',
outputs: [{ name: '', type: 'bool' }],
payable: false,
stateMutability: 'nonpayable',
type: 'function'
},
{
constant: true,
inputs: [],
name: 'decimals',
outputs: [{ name: '', type: 'uint256' }],
payable: false,
stateMutability: 'view',
type: 'function'
},
{
constant: false,
inputs: [
{ name: 'guy', type: 'address' },
{ name: 'wad', type: 'uint256' }
],
name: 'mint',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function'
},
{
constant: false,
inputs: [{ name: 'wad', type: 'uint256' }],
name: 'burn',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function'
},
{
constant: false,
inputs: [{ name: 'name_', type: 'bytes32' }],
name: 'setName',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function'
},
{
constant: true,
inputs: [{ name: 'src', type: 'address' }],
name: 'balanceOf',
outputs: [{ name: '', type: 'uint256' }],
payable: false,
stateMutability: 'view',
type: 'function'
},
{
constant: true,
inputs: [],
name: 'stopped',
outputs: [{ name: '', type: 'bool' }],
payable: false,
stateMutability: 'view',
type: 'function'
},
{
constant: false,
inputs: [{ name: 'authority_', type: 'address' }],
name: 'setAuthority',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function'
},
{
constant: true,
inputs: [],
name: 'owner',
outputs: [{ name: '', type: 'address' }],
payable: false,
stateMutability: 'view',
type: 'function'
},
{
constant: true,
inputs: [],
name: 'symbol',
outputs: [{ name: '', type: 'bytes32' }],
payable: false,
stateMutability: 'view',
type: 'function'
},
{
constant: false,
inputs: [
{ name: 'guy', type: 'address' },
{ name: 'wad', type: 'uint256' }
],
name: 'burn',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function'
},
{
constant: false,
inputs: [{ name: 'wad', type: 'uint256' }],
name: 'mint',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function'
},
{
constant: false,
inputs: [
{ name: 'dst', type: 'address' },
{ name: 'wad', type: 'uint256' }
],
name: 'transfer',
outputs: [{ name: '', type: 'bool' }],
payable: false,
stateMutability: 'nonpayable',
type: 'function'
},
{
constant: false,
inputs: [
{ name: 'dst', type: 'address' },
{ name: 'wad', type: 'uint256' }
],
name: 'push',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function'
},
{
constant: false,
inputs: [
{ name: 'src', type: 'address' },
{ name: 'dst', type: 'address' },
{ name: 'wad', type: 'uint256' }
],
name: 'move',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function'
},
{
constant: false,
inputs: [],
name: 'start',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function'
},
{
constant: true,
inputs: [],
name: 'authority',
outputs: [{ name: '', type: 'address' }],
payable: false,
stateMutability: 'view',
type: 'function'
},
{
constant: false,
inputs: [{ name: 'guy', type: 'address' }],
name: 'approve',
outputs: [{ name: '', type: 'bool' }],
payable: false,
stateMutability: 'nonpayable',
type: 'function'
},
{
constant: true,
inputs: [
{ name: 'src', type: 'address' },
{ name: 'guy', type: 'address' }
],
name: 'allowance',
outputs: [{ name: '', type: 'uint256' }],
payable: false,
stateMutability: 'view',
type: 'function'
},
{
constant: false,
inputs: [
{ name: 'src', type: 'address' },
{ name: 'wad', type: 'uint256' }
],
name: 'pull',
outputs: [],
payable: false,
stateMutability: 'nonpayable',
type: 'function'
},
{
inputs: [{ name: 'symbol_', type: 'bytes32' }],
payable: false,
stateMutability: 'nonpayable',
type: 'constructor'
},
{
anonymous: false,
inputs: [
{ indexed: true, name: 'guy', type: 'address' },
{ indexed: false, name: 'wad', type: 'uint256' }
],
name: 'Mint',
type: 'event'
},
{
anonymous: false,
inputs: [
{ indexed: true, name: 'guy', type: 'address' },
{ indexed: false, name: 'wad', type: 'uint256' }
],
name: 'Burn',
type: 'event'
},
{
anonymous: false,
inputs: [{ indexed: true, name: 'authority', type: 'address' }],
name: 'LogSetAuthority',
type: 'event'
},
{
anonymous: false,
inputs: [{ indexed: true, name: 'owner', type: 'address' }],
name: 'LogSetOwner',
type: 'event'
},
{
anonymous: true,
inputs: [
{ indexed: true, name: 'sig', type: 'bytes4' },
{ indexed: true, name: 'guy', type: 'address' },
{ indexed: true, name: 'foo', type: 'bytes32' },
{ indexed: true, name: 'bar', type: 'bytes32' },
{ indexed: false, name: 'wad', type: 'uint256' },
{ indexed: false, name: 'fax', type: 'bytes' }
],
name: 'LogNote',
type: 'event'
},
{
anonymous: false,
inputs: [
{ indexed: true, name: 'src', type: 'address' },
{ indexed: true, name: 'guy', type: 'address' },
{ indexed: false, name: 'wad', type: 'uint256' }
],
name: 'Approval',
type: 'event'
},
{
anonymous: false,
inputs: [
{ indexed: true, name: 'src', type: 'address' },
{ indexed: true, name: 'dst', type: 'address' },
{ indexed: false, name: 'wad', type: 'uint256' }
],
name: 'Transfer',
type: 'event'
}
];

View File

@ -42,11 +42,11 @@ module.exports = {
// tab if you use this network and you must also set the `host`, `port` and `network_id`
// options below to some value.
//
// development: {
// host: "127.0.0.1", // Localhost (default: none)
// port: 8545, // Standard Ethereum port (default: none)
// network_id: "*", // Any network (default: none)
// },
development: {
host: "127.0.0.1", // Localhost (default: none)
port: 8545, // Standard Ethereum port (default: none)
network_id: "*", // Any network (default: none)
},
// Another network with more advanced options...
// advanced: {