Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Pool Escrow for ETH1 withdrawals #75

Merged
merged 2 commits into from
Apr 5, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
144 changes: 144 additions & 0 deletions abi/PoolEscrow.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
[
{
"inputs": [
{
"internalType": "address",
"name": "_owner",
"type": "address"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "previousOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "OwnershipTransferApplied",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "currentOwner",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "futureOwner",
"type": "address"
}
],
"name": "OwnershipTransferCommitted",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"indexed": true,
"internalType": "address",
"name": "payee",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "Withdrawn",
"type": "event"
},
{
"inputs": [],
"name": "applyOwnershipTransfer",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "commitOwnershipTransfer",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "futureOwner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "owner",
"outputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address payable",
"name": "payee",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"name": "withdraw",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
]
73 changes: 73 additions & 0 deletions contracts/collectors/PoolEscrow.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.7.5;

import "@openzeppelin/contracts/utils/Address.sol";
import "../interfaces/IPoolEscrow.sol";

/**
* @title PoolEscrow
*
* @dev PoolEscrow contract is used to receive transfers from ETH2 system contract for the pool validators.
* The withdrawal credentials of the Pool must be set to
* https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.2/specs/phase0/validator.md#eth1_address_withdrawal_prefix
* using the address of this contract as a destination.
*/
contract PoolEscrow is IPoolEscrow {
using Address for address payable;

// @dev The address of the current contract owner.
address public override owner;

// @dev The address the ownership is planned to be transferred to.
address public override futureOwner;

/**
* @dev Constructor for initializing the PoolEscrow contract.
* @param _owner - address of the contract owner.
*/
constructor(address _owner) {
owner = _owner;
emit OwnershipTransferApplied(address(0), _owner);
}

/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(owner == msg.sender, "PoolEscrow: caller is not the owner");
_;
}

/**
* @dev See {IPoolEscrow-commitOwnershipTransfer}.
*/
function commitOwnershipTransfer(address newOwner) external override onlyOwner {
require(newOwner != address(0), "PoolEscrow: new owner is the zero address");
futureOwner = newOwner;
emit OwnershipTransferCommitted(msg.sender, newOwner);
}

/**
* @dev See {IPoolEscrow-applyOwnershipTransfer}.
*/
function applyOwnershipTransfer() external override onlyOwner {
address newOwner = futureOwner;
require(newOwner != address(0), "PoolEscrow: new owner is the zero address");
(owner, futureOwner) = (newOwner, address(0));
emit OwnershipTransferApplied(msg.sender, newOwner);
}

/**
* @dev See {IPoolEscrow-withdraw}.
*/
function withdraw(address payable payee, uint256 amount) external override onlyOwner {
payee.sendValue(amount);
emit Withdrawn(msg.sender, payee, amount);
}

/**
* @dev Function for receiving withdrawals from ETH2 system contract.
*/
receive() external payable { }
}
65 changes: 65 additions & 0 deletions contracts/interfaces/IPoolEscrow.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.7.5;

/**
* @dev Interface of the PoolEscrow contract.
*/
interface IPoolEscrow {
/**
* @dev Event for tracking withdrawn ether.
* @param sender - the address of the transaction sender.
* @param payee - the address where the funds were transferred to.
* @param amount - the amount of ether transferred to payee.
*/
event Withdrawn(address indexed sender, address indexed payee, uint256 amount);

/**
* @dev Event for tracking ownership transfer commits.
* @param currentOwner - the address of the current owner.
* @param futureOwner - the address the ownership is planned to be transferred to.
*/
event OwnershipTransferCommitted(address indexed currentOwner, address indexed futureOwner);

/**
* @dev Event for tracking ownership transfers.
* @param previousOwner - the address the ownership was transferred from.
* @param newOwner - the address the ownership was transferred to.
*/
event OwnershipTransferApplied(address indexed previousOwner, address indexed newOwner);

/**
* @dev Function for retrieving the address of the current owner.
*/
function owner() external view returns (address);

/**
* @dev Function for retrieving the address of the future owner.
*/
function futureOwner() external view returns (address);

/**
* @dev Commit contract ownership transfer to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function commitOwnershipTransfer(address newOwner) external;

/**
* @dev Apply contract ownership transfer to a new account (`futureOwner`).
* Can only be called by the current owner.
*/
function applyOwnershipTransfer() external;

/**
* @dev Withdraw balance for a payee, forwarding all gas to the
* recipient. Can only be called by the current owner.
*
* WARNING: Forwarding all gas opens the door to reentrancy vulnerabilities.
* Make sure you trust the recipient, or are either following the
* checks-effects-interactions pattern or using {ReentrancyGuard}.
*
* @param payee - the address where the funds will be transferred to.
* @param amount - the amount of ether to transfer to payee.
*/
function withdraw(address payable payee, uint256 amount) external;
}
12 changes: 12 additions & 0 deletions deployments/collectors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const { ethers } = require('hardhat');

async function deployPoolEscrow(adminAddress) {
const PoolEscrow = await ethers.getContractFactory('PoolEscrow');
const poolEscrow = await PoolEscrow.deploy(adminAddress);
await poolEscrow.deployed();
return poolEscrow.address;
}

module.exports = {
deployPoolEscrow,
};
9 changes: 9 additions & 0 deletions deployments/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const hre = require('hardhat');
const { deployPoolEscrow } = require('./collectors');
const { white, green } = require('chalk');
const { contractSettings, contracts } = require('./settings');
const { deployAndInitializeStakeWiseToken } = require('./tokens');
Expand Down Expand Up @@ -48,10 +49,18 @@ async function upgradeContracts() {
)
);

let poolEscrowContractAddress = await deployPoolEscrow(
contractSettings.admin
);
log(
white(`Deployed PoolEscrow contract: ${green(poolEscrowContractAddress)}`)
);

return {
...contracts,
vestingEscrowFactory: vestingEscrowFactoryContractAddress,
vestingEscrow: vestingEscrowContractAddress,
poolEscrow: poolEscrowContractAddress,
stakeWiseToken: stakeWiseTokenContractAddress,
};
}
Expand Down
1 change: 1 addition & 0 deletions hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ module.exports = {
'Validators',
'VestingEscrow',
'VestingEscrowFactory',
'PoolEscrow',
],
clear: true,
flat: true,
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
"@nomiclabs/hardhat-etherscan": "^2.1.1",
"@nomiclabs/hardhat-truffle5": "^2.0.0",
"@nomiclabs/hardhat-web3": "^2.0.0",
"@openzeppelin/contracts": "3.3.0",
"@openzeppelin/contracts": "3.4.1",
"@openzeppelin/contracts-upgradeable": "3.4.1",
"@openzeppelin/hardhat-upgrades": "^1.6.0",
"@openzeppelin/test-helpers": "^0.5.10",
Expand Down