Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
105 changes: 84 additions & 21 deletions contracts/incentives/RewardProgram.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@ pragma experimental ABIEncoderV2;
import "../interfaces/IRewardProgram.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/introspection/IERC165.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Enumerable.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import "@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol";
import "@openzeppelin/contracts/utils/EnumerableSet.sol";
import "../interfaces/IChargedManagers.sol";
import "../interfaces/ILepton.sol";
Expand All @@ -39,19 +41,23 @@ import "../interfaces/IRewardNft.sol";
import "../lib/TokenInfo.sol";
import "../lib/ReentrancyGuard.sol";
import "../lib/BlackholePrevention.sol";
import "../interfaces/IERC20Detailed.sol";

contract RewardProgram is
IRewardProgram,
Ownable,
BlackholePrevention,
ReentrancyGuard
Ownable,
IERC165,
ReentrancyGuard,
IERC721Receiver,
IERC1155Receiver
{
using SafeMath for uint256;
using SafeERC20 for ERC20;
using TokenInfo for address;
using SafeERC20 for IERC20;
using EnumerableSet for EnumerableSet.UintSet;

uint256 constant private PERCENTAGE_SCALE = 1e4; // 10000 (100%)
uint256 constant private PERCENTAGE_SCALE = 1e4; // 10000 (100%)
uint256 constant private LEPTON_MULTIPLIER_SCALE = 1e2;

address private _universe;
Expand All @@ -61,6 +67,7 @@ contract RewardProgram is
mapping(uint256 => EnumerableSet.UintSet) private _multiplierNftsSet;
mapping(uint256 => AssetStake) private _assetStake;
mapping(uint256 => NftStake) private _nftStake;
mapping(address => uint256) public baseMultipliers;

/***********************************|
| Initialization |
Expand All @@ -81,7 +88,14 @@ contract RewardProgram is
return _getFundBalance();
}

function getClaimableRewards(address contractAddress, uint256 tokenId) external view override returns (uint256) {
function getClaimableRewards(
address contractAddress,
uint256 tokenId
)
external
view
override
returns (uint256) {
uint256 parentNftUuid = contractAddress.getTokenUUID(tokenId);
return _assetStake[parentNftUuid].claimableRewards;
}
Expand Down Expand Up @@ -110,11 +124,14 @@ contract RewardProgram is
nonReentrant
{
uint256 parentNftUuid = contractAddress.getTokenUUID(tokenId);

require(_assetStake[parentNftUuid].start == 0 && _assetStake[parentNftUuid].claimableRewards == 0, "RP:E-002");

// Initiate Asset Stake
IWalletManager walletMgr = _chargedManagers.getWalletManager(walletManagerId);

uint256 principal = walletMgr.getPrincipal(contractAddress, tokenId, stakingToken);

if (principal > 0) {
_assetStake[parentNftUuid] = AssetStake(block.number, 0, walletManagerId, stakingToken);
emit AssetRegistered(contractAddress, tokenId, walletManagerId, principal);
Expand All @@ -137,8 +154,9 @@ contract RewardProgram is

if (assetStake.start == 0) {
assetStake.start = block.number;
assetStake.walletManagerId = walletManagerId;
assetStake.stakingToken = stakingToken;
assetStake.walletManagerId = walletManagerId;

emit AssetDeposit(contractAddress, tokenId, walletManagerId, principalAmount);
}
}
Expand Down Expand Up @@ -174,7 +192,13 @@ contract RewardProgram is
emit AssetRelease(contractAddress, tokenId, interestAmount);
}

function registerNftDeposit(address contractAddress, uint256 tokenId, address depositNftAddress, uint256 depositNftTokenId, uint256 /* nftTokenAmount */)
function registerNftDeposit(
address contractAddress,
uint256 tokenId,
address depositNftAddress,
uint256 depositNftTokenId,
uint256 /* nftTokenAmount */
)
external
override
onlyUniverse
Expand Down Expand Up @@ -233,12 +257,20 @@ contract RewardProgram is
| Reward Calculation |
|__________________________________*/

function calculateBaseReward(uint256 amount) public view returns(uint256 baseReward) {
baseReward = _calculateBaseReward(amount);
function calculateBaseReward(address stakingAsset, uint256 amount) public view returns(uint256 baseReward) {
baseReward = _calculateBaseReward(stakingAsset, amount);
}

function calculateRewardsEarned(uint256 parentNftUuid, address stakingAsset,uint256 interestAmount) public view returns (uint256 totalReward) {
uint256 baseReward = _calculateBaseReward(interestAmount);
function calculateRewardsEarned(
uint256 parentNftUuid,
address stakingAsset,
uint256 interestAmount
)
public
view
returns (uint256 totalReward)
{
uint256 baseReward = _calculateBaseReward(stakingAsset, interestAmount);
uint256 leptonMultipliedReward = calculateMultipliedReward(parentNftUuid, baseReward);
totalReward = _convertDecimals(leptonMultipliedReward, stakingAsset);
}
Expand Down Expand Up @@ -277,24 +309,52 @@ contract RewardProgram is
return amountGeneratedWithoutNftDeposit.add(multipliedReward);
}

function onERC721Received(address, address, uint256, bytes calldata) external override returns (bytes4) {
return IERC721Receiver.onERC721Received.selector;
}

function onERC1155Received(address, address, uint256, uint256, bytes calldata) external override returns (bytes4) {
return IERC1155Receiver.onERC1155BatchReceived.selector;
}

function onERC1155BatchReceived(address, address, uint256[] calldata, uint256[] calldata, bytes calldata) external override returns (bytes4) {
return "";
}

function supportsInterface(bytes4 interfaceId)
public
view
virtual
override(IERC165)
returns (bool)
{
// default interface support
if (
interfaceId == type(IERC1155Receiver).interfaceId ||
interfaceId == type(IERC165).interfaceId
) {
return true;
}
}

/***********************************|
| Only Admin/DAO |
|__________________________________*/

function fundProgram(uint256 amount) external onlyOwner {
require(_programData.rewardToken != address(0), "RP:E-405");
IERC20 token = ERC20(_programData.rewardToken);
token.safeTransferFrom(msg.sender, address(this), amount);

IERC20(_programData.rewardToken).safeTransferFrom(msg.sender, address(this), amount);

emit RewardProgramFunded(amount);
}

function setRewardToken(address newRewardToken) external onlyOwner {
_programData.rewardToken = newRewardToken;
}

function setBaseMultiplier(uint256 newMultiplier) external onlyOwner {
_programData.baseMultiplier = newMultiplier; // Basis Points
function setBaseMultiplier(address assetToken, uint256 newMultiplier) external onlyOwner {
baseMultipliers[assetToken] = newMultiplier; // Basis Points
}

function setChargedManagers(address manager) external onlyOwner {
Expand Down Expand Up @@ -364,14 +424,17 @@ contract RewardProgram is
// Update Asset Stake
assetStake.claimableRewards = 0;
// Transfer Available Rewards to Receiver
ERC20(_programData.rewardToken).safeTransfer(receiver, totalReward);
IERC20(_programData.rewardToken).safeTransfer(receiver, totalReward);
}

emit RewardsClaimed(contractAddress, tokenId, receiver, totalReward, unavailReward);
}

function _calculateBaseReward(uint256 amount) internal view returns (uint256 baseReward) {
baseReward = amount.mul(_programData.baseMultiplier).div(PERCENTAGE_SCALE);
function _calculateBaseReward(address assetToken, uint256 amount) internal view returns (uint256 baseReward) {
uint256 baseMultiplier = baseMultipliers[assetToken];
require(baseMultiplier >= 0, "Base multiplier not set");

baseReward = amount.mul(baseMultiplier).div(PERCENTAGE_SCALE);
}

function _calculateTotalMultiplier(uint256 parentNftUuid) internal view returns (uint256) {
Expand Down Expand Up @@ -424,12 +487,12 @@ contract RewardProgram is
}

function _convertDecimals(uint256 reward, address stakingAsset) internal view returns (uint256) {
uint8 stakingTokenDecimals = ERC20(stakingAsset).decimals();
uint8 stakingTokenDecimals = IERC20Detailed(stakingAsset).decimals();
return reward.mul(10**(18 - uint256(stakingTokenDecimals)));
}

function _getFundBalance() internal view returns (uint256) {
return ERC20(_programData.rewardToken).balanceOf(address(this));
return IERC20Detailed(_programData.rewardToken).balanceOf(address(this));
}

modifier onlyUniverse() {
Expand Down
81 changes: 81 additions & 0 deletions contracts/interfaces/IERC20Detailed.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.6.0;

/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20Detailed {
function decimals() external view returns (uint8);
function symbol() external view returns (string memory);
function name() external view returns (string memory);

/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);

/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);

/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);

/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);

/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);

/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);

/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);

/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
1 change: 0 additions & 1 deletion contracts/interfaces/IRewardProgram.sol
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ interface IRewardProgram {
/* data types */
struct ProgramRewardData {
address rewardToken;
uint256 baseMultiplier; // Basis Points
address multiplierNft;
}

Expand Down
4 changes: 0 additions & 4 deletions deploy/J_deploy_fake_reward_program.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,6 @@ module.exports = async (hre) => {
await rewardProgram.setRewardToken(ionx.address)
);

await executeTx('3-c', `RewardProgram: Set Base Multiplier: ${(_BASE_MULTIPLIER_BP / 100)}%`, async () =>
await rewardProgram.setBaseMultiplier(_BASE_MULTIPLIER_BP)
);

await executeTx('3-d', 'RewardProgram: Set ChargedManagers', async () =>
await rewardProgram.setChargedManagers(ddChargedManagers.address)
);
Expand Down
27 changes: 21 additions & 6 deletions deploy/L_deploy_reward_program.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,10 +158,6 @@ module.exports = async (hre) => {
await rewardProgram.setRewardToken(ddIonx.address)
);

await executeTx('1-c', `RewardProgram: Set Base Multiplier: ${(_BASE_MULTIPLIER_BP / 100)}%`, async () =>
await rewardProgram.setBaseMultiplier(_BASE_MULTIPLIER_BP)
);

await executeTx('1-d', 'RewardProgram: Set ChargedManagers', async () =>
await rewardProgram.setChargedManagers(ddChargedManagers.address)
);
Expand All @@ -173,7 +169,11 @@ module.exports = async (hre) => {
// Update Universe Connections

await executeTx('1-f', 'Universe: Registering Reward Program', async () =>
await universeRP.setRewardProgram(rewardProgram.address, rewardStakingToken, ddLepton2.address)
await universeRP.setRewardProgram(rewardProgram.address, daiAddress, ddLepton2.address)
);

await executeTx('1-f', 'Universe: Registering Reward Program', async () =>
await universeRP.setRewardProgram(rewardProgram.address, usdcAddress, ddLepton2.address)
);

await executeTx('1-g', 'Universe: Registering ChargedParticles', async () =>
Expand All @@ -189,7 +189,6 @@ module.exports = async (hre) => {
);

// Clear Old Protons

await executeTx('1-j', 'ProtonA: Unregistering Universe', async () =>
await protonA.setUniverse(ethers.constants.AddressZero)
);
Expand All @@ -204,6 +203,22 @@ module.exports = async (hre) => {
await chargedSettings.enableNftContracts([protonA.address, protonB.address, protonC.address])
);


// Add staking token into Universe
// await executeTx('2-l', 'Universe: set reward staking tokens', async() => {
// await universeRP.setRewardProgram(rewardProgram.address, rewardStakingToken, ddLepton2.address)
// });

// Include staking toking into base multiplier
await executeTx('3-l', 'RewardProgram: set multiplier ', async() =>
await rewardProgram.setBaseMultiplier(daiAddress, '10000')
);

await executeTx('3-l', 'RewardProgram: set multiplier ', async() =>
await rewardProgram.setBaseMultiplier(usdcAddress, '10000')
);


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Fund RewardProgram
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
Loading