Skip to content
Draft
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
/node_modules/
/artifacts/**/*.json
!/artifacts/contracts/RenPool.sol/RenPool.json
!/artifacts/contracts/RenPoolFactory.sol/RenPoolFactory.json
!/artifacts/@renproject/gateway-sol/contracts/Gateway/interfaces/IERC20Standard.sol/IERC20Standard.json
!/artifacts/interfaces/IDarknodeRegistry.sol/IDarknodeRegistry.json
/cache/
Expand Down
100 changes: 95 additions & 5 deletions artifacts/contracts/RenPool.sol/RenPool.json

Large diffs are not rendered by default.

124 changes: 124 additions & 0 deletions artifacts/contracts/RenPoolFactory.sol/RenPoolFactory.json

Large diffs are not rendered by default.

96 changes: 63 additions & 33 deletions contracts/RenPool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,16 @@ contract RenPool {

uint256 public bond;
uint256 public totalPooled;
uint256 public totalWithdrawalRequested;
uint256 public ownerFee; // Percentage
uint256 public nodeOperatorFee; // Percentage

bool public isLocked;
// ^ we could use enum instead POOL_STATUS = { OPEN /* 0 */, CLOSE /* 1 */ }
bool public isRegistered;

mapping(address => uint256) public balances;
mapping(address => uint256) public withdrawRequests;
mapping(address => uint256) public withdrawalRequests;
mapping(address => uint256) public nonces;

IERC20 public renToken;
Expand All @@ -39,6 +41,9 @@ contract RenPool {

event RenDeposited(address indexed _from, uint256 _amount);
event RenWithdrawn(address indexed _from, uint256 _amount);
event RenWithdrawalRequested(address indexed _from, uint256 _amount);
event RenWithdrawalRequestFulfilled(address indexed _from, uint256 _amount);
event RenWithdrawalRequestCancelled(address indexed _from, uint256 _amount);
event EthDeposited(address indexed _from, uint256 _amount);
event EthWithdrawn(address indexed _from, uint256 _amount);
event PoolLocked();
Expand Down Expand Up @@ -78,31 +83,33 @@ contract RenPool {
gatewayRegistry = IGatewayRegistry(_gatewayRegistryAddr);
bond = _bond;
isLocked = false;
isRegistered = false;
totalPooled = 0;
totalWithdrawalRequested = 0;
ownerFee = 5;
nodeOperatorFee = 5;
}

modifier onlyNodeOperator() {
require (
msg.sender == nodeOperator,
"RenPool: Caller is not node operator"
"RenPool: Unauthorized"
);
_;
}

modifier onlyOwnerNodeOperator() {
require (
msg.sender == owner || msg.sender == nodeOperator,
"RenPool: Caller is not owner nor node operator"
"RenPool: Unauthorized"
);
_;
}

modifier onlyOwner() {
require (
msg.sender == owner,
"RenPool: Caller is not owner"
"RenPool: Unauthorized"
);
_;
}
Expand All @@ -116,8 +123,13 @@ contract RenPool {
emit PoolLocked();
}

function _deregisterDarknode() private {
darknodeRegistry.deregister(darknodeID);
isRegistered = false;
}

function unlockPool() external onlyOwnerNodeOperator {
require(renToken.balanceOf(address(this)) > 0, "Pool balance is zero");
require(renToken.balanceOf(address(this)) > 0, "RenPool: Pool balance is zero");
isLocked = false;
emit PoolUnlocked();
}
Expand Down Expand Up @@ -159,76 +171,93 @@ contract RenPool {
address sender = msg.sender;
uint256 senderBalance = balances[sender];

require(senderBalance > 0 && senderBalance >= _amount, "Insufficient funds");
require(!isLocked, "Pool is locked");
require(_amount > 0, "RenPool: Invalid amount");
require(senderBalance >= _amount, "RenPool: Insufficient funds");
require(!isLocked, "RenPool: Pool is locked");

totalPooled -= _amount;
balances[sender] -= _amount;

require(
renToken.transfer(sender, _amount),
"Withdraw failed"
"RenPool: Withdraw failed"
);

emit RenWithdrawn(sender, _amount);
}

/**
* @notice Requesting a withdraw in case the pool is locked. The amount
* that needs to be withdrawn will be replaced by another user using the
* fulfillWithdrawRequest method.
* @notice Requesting a withdraw in case the pool is locked. The amount
* that needs to be withdrawn will be replaced by another user using the
* fulfillWithdrawalRequest method.
*
* @param _amount The amount of REN to be withdrawn.
*
* @dev Users can have up to a single request active. In case of several
* calls to this method, only the last request will be preserved.
*/
function requestWithdraw(uint256 _amount) external {
function requestWithdrawal(uint256 _amount) external {
address sender = msg.sender;
uint256 senderBalance = balances[sender];

require(senderBalance > 0 && senderBalance >= _amount, "Insufficient funds");
require(isLocked, "Pool is not locked");
require(_amount > 0, "RenPool: Invalid amount");
require(senderBalance >= _amount, "RenPool: Insufficient funds");
require(isLocked, "RenPool: Pool is not locked");

withdrawalRequests[sender] = _amount;
totalWithdrawalRequested += _amount;

withdrawRequests[sender] = _amount;
if(isRegistered && totalWithdrawalRequested > bond / 2) {
_deregisterDarknode();
}

// TODO emit event
emit RenWithdrawalRequested(sender, _amount);
}

/**
* @notice User wanting to fullill the withdraw request will pay the amount
* @notice User wanting to fulfill the withdraw request will pay the amount
* the user wanting to withdraw his money.
*
* @param _target The amount of REN to be withdrawn.
*/
function fulfillWithdrawRequest(address _target) external {
function fulfillWithdrawalRequest(address _target) external {
address sender = msg.sender;
uint256 amount = withdrawRequests[_target];
// ^ This could not be defined plus make sure amount > 0
// TODO: make sure user cannot fullfil his own request
// TODO: add test for when _target doesn't have an associated withdrawRequest
uint256 amount = withdrawalRequests[_target];

require(isLocked, "Pool is not locked");
require(amount > 0, "RenPool: invalid amount");
require(isLocked, "RenPool: Pool is not locked");

balances[sender] += amount;
balances[_target] -= amount;
delete withdrawRequests[_target];
totalWithdrawalRequested -= amount;

delete withdrawalRequests[_target];

// Transfer funds from sender to _target
require(
renToken.transferFrom(sender, address(this), amount),
"Deposit failed"
"RenPool: Deposit failed"
);
require(
renToken.transfer(_target, amount),
"Refund failed"
"RenPool: Refund failed"
);

// TODO emit event
emit RenWithdrawalRequestFulfilled(sender, amount);
}

// TODO: cancelWithdrawRequest
// TODO: getWithdrawRequests
function cancelWithdrawalRequest() external {
address sender = msg.sender;
uint256 amount = withdrawalRequests[sender];

require(amount > 0, "RenPool: invalid amount");

totalWithdrawalRequested -= amount;

delete withdrawalRequests[sender];

emit RenWithdrawalRequestCancelled(sender, amount);
}

/**
* @notice Return REN balance for the given address.
Expand All @@ -244,11 +273,11 @@ contract RenPool {
* registering the darknode.
*/
function approveBondTransfer() external onlyNodeOperator {
require(isLocked, "Pool is not locked");
require(isLocked, "RenPool: Pool is not locked");

require(
renToken.approve(address(darknodeRegistry), bond),
"Bond transfer failed"
"RenPool: Bond transfer failed"
);
}

Expand All @@ -266,10 +295,11 @@ contract RenPool {
* other darknodes and traders to encrypt messages to the trader.
*/
function registerDarknode(address _darknodeID, bytes calldata _publicKey) external onlyNodeOperator {
require(isLocked, "Pool is not locked");
require(isLocked, "RenPool: Pool is not locked");

darknodeRegistry.register(_darknodeID, _publicKey);

isRegistered = true;
darknodeID = _darknodeID;
publicKey = _publicKey;
}
Expand All @@ -283,7 +313,7 @@ contract RenPool {
* to being able to call refund.
*/
function deregisterDarknode() external onlyOwnerNodeOperator {
darknodeRegistry.deregister(darknodeID);
_deregisterDarknode();
}

/**
Expand Down
1 change: 1 addition & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export type Artifact = {
};

export const RenPool: Artifact;
export const RenPoolFactory: Artifact;
export const IERC20Standard: Artifact;
export const IDarknodeRegistry: Artifact;

Expand Down
4 changes: 3 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
const RenPool = require('./artifacts/contracts/RenPool.sol/RenPool.json');
const RenPool= require('./artifacts/contracts/RenPool.sol/RenPool.json');
const RenPoolFactory = require('./artifacts/contracts/RenPoolFactory.sol/RenPoolFactory.json');
const IERC20Standard = require('./artifacts/@renproject/gateway-sol/contracts/Gateway/interfaces/IERC20Standard.sol/IERC20Standard.json');
const IDarknodeRegistry = require('./artifacts/interfaces/IDarknodeRegistry.sol/IDarknodeRegistry.json');
const deployments = require('./ren-deployments.js');

module.exports = {
RenPool,
RenPoolFactory,
IERC20Standard,
IDarknodeRegistry,
deployments,
Expand Down
68 changes: 35 additions & 33 deletions scripts/deploy.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,32 +40,34 @@ async function faucet(renToken, account) {
}

async function main() {
print(`${chalk.italic('\u{1F680} RenPool contract deployment')}`);
print(`${chalk.italic('\u{1F680} RenPoolFactory contract deployment')}`);
print(`Using network ${chalk.bold(hre.network.name)} (${chalk.bold(hre.network.config.chainId)})`);

print(`Getting signers to deploy RenPool contract`);
print(`Getting signers to deploy RenPoolFactory contract`);
const [owner] = await ethers.getSigners();
const nodeOperator = owner;

print(`Deploying ${chalk.bold('RenPool')} contract`);
const RenPool = await ethers.getContractFactory('RenPool');
const renPool = await RenPool.connect(nodeOperator).deploy(
renTokenAddr,
darknodeRegistryAddr,
darknodePaymentAddr,
claimRewardsAddr,
gatewayRegistryAddr,
owner.address,
nodeOperator.address,
POOL_BOND);
await renPool.deployed();

print(`Deployed to ${chalk.bold(renPool.address)} TX ${chalk.bold(renPool.deployTransaction.hash)}`);
// const nodeOperator = owner;

print(`Deploying ${chalk.bold('RenPoolFactory')} contract`);
const RenPoolFactory = await ethers.getContractFactory('RenPoolFactory');
const renPoolFactory = await RenPoolFactory.connect(owner).deploy();
await renPoolFactory.deployed();
// const renPool = await RenPoolFactory.connect(nodeOperator).deploy(
// renTokenAddr,
// darknodeRegistryAddr,
// darknodePaymentAddr,
// claimRewardsAddr,
// gatewayRegistryAddr,
// owner.address,
// nodeOperator.address,
// POOL_BOND);
// await renPool.deployed();

print(`Deployed to ${chalk.bold(renPoolFactory.address)} TX ${chalk.bold(renPoolFactory.deployTransaction.hash)}`);

const renToken = new ethers.Contract(renTokenAddr, RenToken.abi, owner);

if (hre.network.name === 'hardhat') {
print('Skipping RenPool contract Etherscan verification')
print('Skipping RenPoolFactory contract Etherscan verification')

await provider.request({ method: 'hardhat_impersonateAccount', params: [topRenTokenHolderAddr] });

Expand All @@ -88,26 +90,26 @@ async function main() {
} else {
print('Waiting before verification');
await sleep(30000);
const balance = await renPool.balanceOf(owner.address);
print(` Owner's balance is ${chalk.yellow(balance)}`);
// const balance = await renPool.balanceOf(owner.address);
// print(` Owner's balance is ${chalk.yellow(balance)}`);

print('Verifying RenPool smart contract in Etherscan')
print('Verifying RenPoolFactory smart contract in Etherscan')

await hre.run("verify:verify", {
address: renPool.address,
constructorArguments: [
renTokenAddr,
darknodeRegistryAddr,
darknodePaymentAddr,
claimRewardsAddr,
gatewayRegistryAddr,
owner.address,
POOL_BOND
],
address: renPoolFactory.address,
constructorArguments: []
// renTokenAddr,
// darknodeRegistryAddr,
// darknodePaymentAddr,
// claimRewardsAddr,
// gatewayRegistryAddr,
// owner.address,
// POOL_BOND
// ],
});
}

return { renPool, renToken, faucet };
return { renPoolFactory, renToken, faucet };
}

if (require.main === module) {
Expand Down
Loading