Created
June 1, 2023 04:00
-
-
Save fielding/bff3887044ba02740bebbfffe08f600b to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.8.18+commit.87f61d96.js&optimize=false&runs=200&gist=
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.0; | |
import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; | |
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | |
import "@identity.com/gateway-protocol-eth/contracts/Gated.sol"; | |
import "./MasterNodeFundCertificate.sol"; | |
contract MasterNodeFund is Gated, MasterNodeFundCertificate { | |
using SafeERC20 for ERC20; | |
uint256 private constant MAX_FUNDS = 10000100 * (10**18); | |
uint256 private constant MIN_CONTRIBUTION = 150000 * (10**18); | |
uint256 private constant MAX_CONTRIBUTION = 10000000 * (10**18); | |
uint256 public currentFunds = 0; | |
uint256 public nftCounter = 1; | |
uint256 public rewardsStartTime = 0; // if this needs to be private then change it before deploying, but needs to be public for testing via test contract | |
uint256 private constant REWARDS_PERIOD = 2628000 seconds; // roughly a month | |
uint256 private constant LOCK_PERIOD = 94668408 seconds; // 3 years and 8 seconds | |
address private payoutAddress; | |
ERC20 private xdcToken; | |
mapping(address => Investment) public investments; | |
mapping(uint256 => uint256) public investmentAmounts; | |
struct Investment { | |
uint256 amount; | |
uint256 time; | |
bool claimed; | |
} | |
constructor(address gatewayTokenContract, uint256 gatekeeperNetworkSlotId, address xdcTokenAddress) | |
Gated(gatewayTokenContract, gatekeeperNetworkSlotId) { | |
payoutAddress = msg.sender; | |
xdcToken = ERC20(xdcTokenAddress); | |
} | |
function contribute(uint256 amount) virtual external gated { | |
require(currentFunds + amount <= MAX_FUNDS, "Total funds limit reached"); | |
require(amount >= MIN_CONTRIBUTION && amount <= MAX_CONTRIBUTION, "Invalid contribution amount"); | |
xdcToken.safeTransferFrom(msg.sender, address(this), amount); | |
if (investments[msg.sender].time == 0) { | |
_createInvestment(msg.sender, amount); | |
} else { | |
investments[msg.sender].amount += amount; | |
} | |
currentFunds += amount; | |
// Update rewardsStartTime if the goal is reached and rewardsStartTime is not set already | |
if (currentFunds >= MAX_FUNDS && rewardsStartTime == 0) { | |
rewardsStartTime = block.timestamp + LOCK_PERIOD; | |
} | |
} | |
function _createInvestment(address investor, uint256 amount) private { | |
investments[investor] = Investment(amount, block.timestamp, false); | |
_mintNFT(investor); | |
} | |
function _mintNFT(address investor) private { | |
uint256 tokenId = nftCounter; | |
mint(investor, tokenId); | |
nftCounter += 1; | |
investmentAmounts[tokenId] = investments[investor].amount; | |
} | |
function distributeMonthlyRewards(uint256 totalRewardAmount) external { | |
require(msg.sender == payoutAddress, "Unauthorized"); | |
require(block.timestamp >= rewardsStartTime, "Rewards distribution not started"); | |
xdcToken.safeTransferFrom(msg.sender, address(this), totalRewardAmount); | |
uint256 tokenId = 1; | |
while (tokenId < nftCounter) { | |
if (!_exists(tokenId)) { | |
tokenId++; | |
continue; | |
} | |
address holder = ownerOf(tokenId); | |
uint256 holderInvestment = investmentAmounts[tokenId]; | |
uint256 holderReward = (holderInvestment * totalRewardAmount) / MAX_FUNDS; | |
xdcToken.safeTransfer(holder, holderReward); | |
tokenId++; | |
} | |
rewardsStartTime += REWARDS_PERIOD; | |
} | |
function claimInitialFunds() external { | |
require(block.timestamp >= investments[msg.sender].time + LOCK_PERIOD, "Lock period not ended"); | |
require(!investments[msg.sender].claimed, "Initial funds already claimed"); | |
uint256 claimAmount = investments[msg.sender].amount; | |
require(claimAmount > 0, "No investment found"); | |
investments[msg.sender].claimed = true; | |
xdcToken.safeTransfer(msg.sender, claimAmount); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.8.0; | |
import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; | |
contract MasterNodeFundCertificate is ERC721 { | |
constructor() ERC721("MasterNodeFundCertificate", "MNFC") { | |
} | |
function mint(address to, uint256 tokenId) internal { | |
_mint(to, tokenId); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
// this should NOT ever be deployed to anything other than the test net for testing purposes only | |
pragma solidity ^0.8.0; | |
import "./MasterNodeFund.sol"; | |
contract MasterNodeFundTest is MasterNodeFund { | |
uint256 public testMaxFunds; | |
uint256 public testMinContribution; | |
uint256 public testMaxContribution; | |
constructor(address gatewayTokenContract, uint256 gatekeeperNetworkSlotId, address xdcTokenAddress) | |
MasterNodeFund(gatewayTokenContract, gatekeeperNetworkSlotId, xdcTokenAddress) { | |
_setUpTestValues(); | |
} | |
function _setUpTestValues() private { | |
testMaxFunds = 10 * (10**18); | |
testMinContribution = 1 * (10**18); | |
testMaxContribution = 10 * (10**18); | |
} | |
function contribute(uint256 amount) override external gated { | |
require(currentFunds + amount <= testMaxFunds, "Total funds limit reached"); | |
require(amount >= testMinContribution && amount <= testMaxContribution, "Invalid contribution amount"); | |
super.contribute(amount); | |
} | |
function progressByOneMonth() external { | |
rewardsStartTime /= 30 days; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment