Skip to content
This repository has been archived by the owner on Nov 28, 2021. It is now read-only.

Ref/update claim rewards interface #77

Draft
wants to merge 21 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
/.netlify/
/node_modules/
/cache/
/artifacts/
/coverage/
/.env
/coverage.json
File renamed without changes.
1 change: 1 addition & 0 deletions client/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ yarn-debug.log*
yarn-error.log*

/.netlify
/src/artifacts/
7 changes: 7 additions & 0 deletions client/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,17 @@ export const CONTRACT_ADDRESSES = {
1: {
REN_TOKEN: '0x408e41876cCCDC0F92210600ef50372656052a38',
DARKNODE_REGISTRY: '0x2D7b6C95aFeFFa50C068D50f89C5C0014e054f0A',
REN_POOL: '0x0000000000000000000000000000000000000000',
},
42: {
REN_TOKEN: '0x2CD647668494c1B15743AB283A0f980d90a87394',
DARKNODE_REGISTRY: '0x9954C9F839b31E82bc9CA98F234313112D269712',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I see we repeating ourselves with addresses configuration. We already have that configuration in Hardhat, we need to undertand the best way to reuse from both projects.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree. I'll give it some thought. The only address that changes (but still we should be able to store it somewhere at deploy time) is the RenPool address.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In PR #88 I tried to reuse contract address from client.

REN_POOL: '0xf1e98770ab8ed1364371B8c7DBdA56633F7cB6a9',
},
1337: {
REN_TOKEN: '0x0000000000000000000000000000000000000000',
DARKNODE_REGISTRY: '0x0000000000000000000000000000000000000000',
REN_POOL: '0x0000000000000000000000000000000000000000',
},
}

Expand Down
2 changes: 1 addition & 1 deletion client/src/context/DarknodeRegistryProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import React, { FC, useState, useEffect, createContext } from 'react'
import { Contract } from '@ethersproject/contracts'
import { BigNumber } from '@ethersproject/bignumber'
import { CONTRACT_ADDRESSES } from '../constants'
import artifact from'../artifacts/interfaces/IDarknodeRegistry.json'
import artifact from'../artifacts/interfaces/IDarknodeRegistry.sol/IDarknodeRegistry.json'
import { useActiveWeb3React } from '../hooks/useActiveWeb3React'
import { useContract } from '../hooks/useContract'

Expand Down
24 changes: 12 additions & 12 deletions client/src/context/RenPoolProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React, { FC, useState, useEffect, createContext } from 'react'
import { Contract, ContractInterface } from '@ethersproject/contracts'
import { BigNumber } from '@ethersproject/bignumber'
import { ContractNames } from '../constants'
import map from '../artifacts/deployments/map.json'
import { ContractNames, CONTRACT_ADDRESSES } from '../constants'
import artifact from '../artifacts/contracts/RenPool.sol/RenPool.json'
import { useActiveWeb3React } from '../hooks/useActiveWeb3React'
import { useContract } from '../hooks/useContract'

Expand Down Expand Up @@ -45,18 +45,18 @@ export const RenPoolProvider: FC = ({
}) => {
const { account } = useActiveWeb3React()

let address
let artifact: { abi: ContractInterface }
// let address
// let artifact: { abi: ContractInterface }

try {
address = map[CHAIN_ID][ContractNames.RenPool][0]
artifact = require(`../artifacts/deployments/${CHAIN_ID}/${address}.json`)
} catch (e) {
alert(`Could not load contract ${ContractNames.RenPool}, ${JSON.stringify(e, null, 2)}`)
return null
}
// try {
// address = CONTRACT_ADDRESSES[CHAIN_ID].REN_POOL
// artifact = require(`../artifacts/deployments/${CHAIN_ID}/${address}.json`)
// } catch (e) {
// alert(`Could not load contract ${ContractNames.RenPool}, ${JSON.stringify(e, null, 2)}`)
// return null
// }

const renPool = useContract(address, artifact.abi)
const renPool = useContract(CONTRACT_ADDRESSES[CHAIN_ID].REN_POOL, artifact.abi)

const [owner, setOwner] = useState<string | null>(null)
const [nodeOperator, setNodeOperator] = useState<string | null>(null)
Expand Down
12 changes: 6 additions & 6 deletions client/src/context/RenTokenProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import React, { FC, useState, useEffect, createContext } from 'react'
import { Contract } from '@ethersproject/contracts'
import { BigNumber } from '@ethersproject/bignumber'
import { ContractNames, CONTRACT_ADDRESSES } from '../constants'
import artifact from'../artifacts/contracts/dependencies/OpenZeppelin/[email protected]/IERC20.json'
import map from '../artifacts/deployments/map.json'
import artifact from'../artifacts/@renproject/gateway-sol/contracts/Gateway/interfaces/IERC20Standard.sol/IERC20Standard.json'
// import map from '../artifacts/deployments/map.json'
import { useActiveWeb3React } from '../hooks/useActiveWeb3React'
import { useContract } from '../hooks/useContract'

Expand Down Expand Up @@ -32,11 +32,11 @@ export const RenTokenProvider: FC = ({
}) => {
const { library, account } = useActiveWeb3React()

const address: string = CHAIN_ID === '1337'
? map[CHAIN_ID][ContractNames.RenToken][0]
: CONTRACT_ADDRESSES[CHAIN_ID].REN_TOKEN
// const address: string = CHAIN_ID === '1337'
// ? map[CHAIN_ID][ContractNames.RenToken][0]
// : CONTRACT_ADDRESSES[CHAIN_ID].REN_TOKEN

const renToken = useContract(address, artifact.abi)
const renToken = useContract(CONTRACT_ADDRESSES[CHAIN_ID].REN_TOKEN, artifact.abi)

const [balance, setBalance] = useState<BigNumber>(BigNumber.from(0))

Expand Down
126 changes: 73 additions & 53 deletions contracts/RenPool.sol
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

import "hardhat/console.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@renproject/gateway-sol/contracts/Gateway/interfaces/IGatewayRegistry.sol";
import "../interfaces/IDarknodeRegistry.sol";
import "../interfaces/IDarknodePayment.sol";
import "../interfaces/IClaimRewards.sol";
import "../interfaces/IGateway.sol";
// TODO: use safeMath
import "../interfaces/IClaimRewardsV1.sol";
fede-rodes marked this conversation as resolved.
Show resolved Hide resolved
// TODO: Ownable + Ownable.initialize(_owner);

contract RenPool {
Expand All @@ -24,35 +24,36 @@ contract RenPool {
uint256 public ownerFee; // Percentage
uint256 public nodeOperatorFee; // Percentage

uint64 public nonce;

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

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

IERC20 public renToken;
IDarknodeRegistry public darknodeRegistry;
IDarknodePayment public darknodePayment;
IClaimRewards public claimRewards;
IGateway public gateway; // OR IMintGateway????
IClaimRewardsV1 public claimRewards;
IGatewayRegistry public gatewayRegistry;

event RenDeposited(address indexed _from, uint256 _amount);
event RenWithdrawn(address indexed _from, uint256 _amount);
event EthDeposited(address indexed _from, uint256 _amount);
event EthWithdrawn(address indexed _from, uint256 _amount);
event PoolLocked();
event PoolUnlocked();
event RewardsClaimed(address indexed _from, uint256 _amount, uint256 _nonce);
event RewardsMinted(address indexed _from, uint256 _mintedAmount);

/**
* @notice Deploy a new RenPool instance.
*
* @param _renTokenAddr The REN token contract address.
* @param _darknodeRegistryAddr The DarknodeRegistry contract address.
* @param _darknodePaymentAddr The DarknodePayment contract address.
* @param _claimRewardsAddr The ClaimRewards contract address.
* @param _gatewayAddr The Gateway contract address.
* @param _claimRewardsAddr The ClaimRewardsV1 contract address.
* @param _gatewayRegistryAddr The GatewayRegistry contract address.
* @param _owner The protocol owner's address. Possibly a multising wallet.
* @param _bond The amount of REN tokens required to register a darknode.
*/
Expand All @@ -61,7 +62,7 @@ contract RenPool {
address _darknodeRegistryAddr,
address _darknodePaymentAddr,
address _claimRewardsAddr,
address _gatewayAddr,
address _gatewayRegistryAddr,
address _owner,
uint256 _bond
)
Expand All @@ -71,14 +72,13 @@ contract RenPool {
renToken = IERC20(_renTokenAddr);
darknodeRegistry = IDarknodeRegistry(_darknodeRegistryAddr);
darknodePayment = IDarknodePayment(_darknodePaymentAddr);
claimRewards = IClaimRewards(_claimRewardsAddr);
gateway = IGateway(_gatewayAddr);
claimRewards = IClaimRewardsV1(_claimRewardsAddr);
gatewayRegistry = IGatewayRegistry(_gatewayRegistryAddr);
bond = _bond;
isLocked = false;
totalPooled = 0;
ownerFee = 5;
nodeOperatorFee = 5;
nonce = 0;

// TODO: register pool into RenPoolStore
}
Expand Down Expand Up @@ -302,59 +302,79 @@ contract RenPool {
emit EthWithdrawn(nodeOperator, balance);
}

/**
* @notice Transfer rewards from darknode to darknode owner prior to calling claimDarknodeRewards.
*
* @param _tokens List of tokens to transfer. (here we could have a list with all available tokens)
*/
function transferRewardsToDarknodeOwner(address[] calldata _tokens) external {
darknodePayment.withdrawMultiple(address(this), _tokens);
}
function getDarknodeBalance(string memory _assetSymbol) external view returns(uint256) {
return gatewayRegistry.getTokenBySymbol(_assetSymbol).balanceOf(address(this));
}

/**
* @notice Claim darknode rewards.
*
* @param _assetSymbol The asset being claimed. e.g. "BTC" or "DOGE".
* @param _amount The amount of the token being minted, in its smallest
* denomination (e.g. satoshis for BTC).
* @param _recipientAddress The Ethereum address to which the assets are
* being withdrawn to. This same address must then call `mint` on
* the asset's Ren Gateway contract.
* @param _amount The amount of the token being minted, in its smallest
* denomination (e.g. satoshis for BTC).
*
* @dev When RenVM sees the claim, it will produce a signature which needs
* to be submitted to the asset's Ren Gateway contract on Ethereum. The
* signature has to be fetched via a JSON-RPC request made to the associated
* lightnode (https://lightnode-devnet.herokuapp) with the transaction
* details from the claimRewardsToEthereum call.
*/
function claimDarknodeRewards(
function claimRewardsToChain(
string memory _assetSymbol,
uint256 _amount, // avoid this param, read from user balance instead. What about airdrops?
address _recipientAddress
address _recipientAddress,
uint256 _amount
)
external
returns(uint256, uint256)
returns(uint256)
{
// TODO: check that sender has the amount to be claimed
address sender = msg.sender;

// TODO: check that sender has the amount to be claimed
// uint256 balance = gatewayRegistry.getTokenBySymbol(_assetSymbol).balanceOf(address(this));
uint256 fractionInBps = 10_000; // TODO: this should be the share of the user for the given token
uint256 sig = claimRewards.claimRewardsToEthereum(_assetSymbol, _recipientAddress, fractionInBps);
nonce += 1;

return (sig, nonce);
// bytes32 pHash = keccak256(abi.encode(_assetSymbol, _recipientAddress));
// bytes32 nHash = keccak256(abi.encode(nonce, _amount, pHash));

// gateway.mint(pHash, _amount, nHash, sig);

/*
const nHash = randomBytes(32);
const pHash = randomBytes(32);

const hash = await gateway.hashForSignature.call(
pHash,
value,
user,
nHash
);
const sig = ecsign(
Buffer.from(hash.slice(2), "hex"),
privKey
);
See: https://github.com/renproject/gateway-sol/blob/7bd51d8a897952a31134875d7b2b621e4542deaa/test/Gateway.ts
*/
uint256 nonce = claimRewards.claimRewardsToEthereum(_assetSymbol, _recipientAddress, fractionInBps);
// TODO: Use claimReardsToChain instead
nonces[sender] = nonce;
emit RewardsClaimed(sender, _amount, nonce);
return nonce;
}

/**
* @notice mint verifies a mint approval signature from RenVM and creates
* tokens after taking a fee for the `_feeRecipient`.
*
* @param _amount The amount of the token being minted, in its smallest
* denomination (e.g. satoshis for BTC).
* @param _sig The signature of the hash of the following values:
* (pHash, amount, msg.sender, nHash), signed by the mintAuthority. Where
* mintAuthority refers to the address of the key that can sign mint requests.
*
* @dev You'll need to make an RPC request to the RenVM after calling claimRewardsToChain
* in order to get the signature from the mint authority.
* Source: https://renproject.github.io/ren-client-docs/contracts/integrating-contracts#writing-a-mint-function
*/
function mintRewards(
string memory _assetSymbol,
address _recipientAddress,
uint256 _amount,
uint256 _nonce,
bytes memory _sig
)
external
{
// _pHash (payload hash) The hash of the payload associated with the
// mint, ie, asset symbol and recipient address.
bytes32 pHash = keccak256(abi.encode(_assetSymbol, _recipientAddress));

// _nHash (nonce hash) The hash of the nonce, amount and pHash.
bytes32 nHash = keccak256(abi.encode(_nonce, _amount, pHash));

uint256 mintAmount = gatewayRegistry.getGatewayBySymbol(_assetSymbol).mint(pHash, _amount, nHash, _sig);
console.log("mintAmount", mintAmount);

emit RewardsMinted(msg.sender, mintAmount);
}
}
9 changes: 6 additions & 3 deletions hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const networks = {
darknodeRegistryStoreAddr: '0x60Ab11FE605D2A2C3cf351824816772a131f8782',
darknodePaymentAddr: '0x098e1708b920EFBdD7afe33Adb6a4CBa30c370B9',
claimRewardsAddr: '0x0000000000000000000000000000000000000000',
gatewayAddr: '0x0000000000000000000000000000000000000000',
gatewayRegistryAddr: '0xe80d347DF1209a76DD9d2319d62912ba98C54DDD',
},
},
kovan: {
Expand All @@ -40,8 +40,8 @@ const networks = {
darknodeRegistryAddr: '0x9954C9F839b31E82bc9CA98F234313112D269712',
darknodeRegistryStoreAddr: '0x9daa16aA19e37f3de06197a8B5E638EC5e487392',
darknodePaymentAddr: '0x023f2e94C3eb128D3bFa6317a3fF860BF93C1616',
claimRewardsAddr: '0x7F8f7Aff44a63f61b7a120Ef2c34Ea2c4D9bD216',
gatewayAddr: '0x0000000000000000000000000000000000000000',
claimRewardsAddr: '0x64C8bdfE42cFFC41D9ca6617350c5F5371DdD0F7',
gatewayRegistryAddr: '0x557e211EC5fc9a6737d2C6b7a1aDe3e0C11A8D5D',
},
}
};
Expand Down Expand Up @@ -81,4 +81,7 @@ module.exports = {
etherscan: {
apiKey: process.env.ETHERSCAN_API_KEY,
},
paths: {
artifacts: "./client/src/artifacts"
fede-rodes marked this conversation as resolved.
Show resolved Hide resolved
},
};
25 changes: 0 additions & 25 deletions interfaces/IClaimRewards.sol

This file was deleted.

Loading