pragma solidity ^0.4.18;
/**
* @title ERC20Basic
* @dev Simpler version of ERC20 interface
*/
contract ERC20Basic {
uint256 public totalSupply;
function balanceOf(address who) public view returns (uint256);
function transfer(address to, uint256 value) public returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
}
/**
* @title Ownable
* @dev The Ownable contract has an owner address, and provides basic authorization control
* functions, this simplifies the implementation of "user permissions".
*/
contract Ownable {
address public owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
function Ownable() public {
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
require(newOwner != address(0));
OwnershipTransferred(owner, newOwner);
owner = newOwner;
}
}
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 c = a * b;
assert(c / a == b);
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
/**
* @title Basic token
* @dev Basic version of StandardToken, with no allowances.
*/
contract BasicToken is ERC20Basic {
using SafeMath for uint256;
mapping(address => uint256) balances;
/**
* @dev transfer token for a specified address
* @param _to The address to transfer to.
* @param _value The amount to be transferred.
*/
function transfer(address _to, uint256 _value) public returns (bool) {
require(_to != address(0));
require(_value <= balances[msg.sender]);
// SafeMath.sub will throw if there is not enough balance.
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
Transfer(msg.sender, _to, _value);
return true;
}
/**
* @dev Gets the balance of the specified address.
* @param _owner The address to query the the balance of.
* @return An uint256 representing the amount owned by the passed address.
*/
function balanceOf(address _owner) public view returns (uint256 balance) {
return balances[_owner];
}
}
/**
* @title ERC20 interface
*/
contract ERC20 is ERC20Basic {
function allowance(address owner, address spender) public view returns (uint256);
function transferFrom(address from, address to, uint256 value) public returns (bool);
function approve(address spender, uint256 value) public returns (bool);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
/**
* @title Standard ERC20 token
*
* @dev Implementation of the basic standard token.
*/
contract StandardToken is ERC20, BasicToken {
mapping (address => mapping (address => uint256)) internal allowed;
/**
* @dev Transfer tokens from one address to another
* @param _from address The address which you want to send tokens from
* @param _to address The address which you want to transfer to
* @param _value uint256 the amount of tokens to be transferred
*/
function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
require(_to != address(0));
require(_value <= balances[_from]);
require(_value <= allowed[_from][msg.sender]);
balances[_from] = balances[_from].sub(_value);
balances[_to] = balances[_to].add(_value);
allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
Transfer(_from, _to, _value);
return true;
}
/**
* @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
*
* 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:
* @param _spender The address which will spend the funds.
* @param _value The amount of tokens to be spent.
*/
function approve(address _spender, uint256 _value) public returns (bool) {
allowed[msg.sender][_spender] = _value;
Approval(msg.sender, _spender, _value);
return true;
}
/**
* @dev Function to check the amount of tokens that an owner allowed to a spender.
* @param _owner address The address which owns the funds.
* @param _spender address The address which will spend the funds.
* @return A uint256 specifying the amount of tokens still available for the spender.
*/
function allowance(address _owner, address _spender) public view returns (uint256) {
return allowed[_owner][_spender];
}
/**
* @dev Increase the amount of tokens that an owner allowed to a spender.
*
* approve should be called when allowed[_spender] == 0. To increment
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
* @param _spender The address which will spend the funds.
* @param _addedValue The amount of tokens to increase the allowance by.
*/
function increaseApproval(address _spender, uint _addedValue) public returns (bool) {
allowed[msg.sender][_spender] = allowed[msg.sender][_spender].add(_addedValue);
Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
/**
* @dev Decrease the amount of tokens that an owner allowed to a spender.
*
* approve should be called when allowed[_spender] == 0. To decrement
* allowed value is better to use this function to avoid 2 calls (and wait until
* the first transaction is mined)
* From MonolithDAO Token.sol
* @param _spender The address which will spend the funds.
* @param _subtractedValue The amount of tokens to decrease the allowance by.
*/
function decreaseApproval(address _spender, uint _subtractedValue) public returns (bool) {
uint oldValue = allowed[msg.sender][_spender];
if (_subtractedValue > oldValue) {
allowed[msg.sender][_spender] = 0;
} else {
allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
}
Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
return true;
}
}
/**
* @title Mintable token
* @dev Simple ERC20 Token example, with mintable token creation
*/
contract MintableToken is StandardToken, Ownable {
event Mint(address indexed to, uint256 amount);
//event MintFinished();
//bool public mintingFinished = false;
/*
modifier canMint() {
require(!mintingFinished);
_;
}
*/
/**
* @dev Function to mint tokens
* @param _to The address that will receive the minted tokens.
* @param _amount The amount of tokens to mint.
* @return A boolean that indicates if the operation was successful.
*/
function mint(address _to, uint256 _amount) internal returns (bool) {
totalSupply = totalSupply.add(_amount);
balances[_to] = balances[_to].add(_amount);
Mint(_to, _amount);
Transfer(address(0), _to, _amount);
return true;
}
/**
* @dev Function to stop minting new tokens.
* @return True if the operation was successful.
function finishMinting() onlyOwner canMint public returns (bool) {
mintingFinished = true;
MintFinished();
return true;
}
*/
}
/**
* @title Contracts that should not own Ether
* @dev This tries to block incoming ether to prevent accidental loss of Ether. Should Ether end up
* in the contract, it will allow the owner to reclaim this ether.
* @notice Ether can still be send to this contract by:
* calling functions labeled `payable`
* `selfdestruct(contract_address)`
* mining directly to the contract address
*/
contract HasNoEther is Ownable {
/**
* @dev Constructor that rejects incoming Ether
* @dev The `payable` flag is added so we can access `msg.value` without compiler warning. If we
* leave out payable, then Solidity will allow inheriting contracts to implement a payable
* constructor. By doing it this way we prevent a payable constructor from working. Alternatively
* we could use assembly to access msg.value.
*/
function HasNoEther() public payable {
require(msg.value == 0);
}
/**
* @dev Disallows direct send by settings a default function without the `payable` flag.
*/
function() external {
}
/**
* @dev Transfer all Ether held by the contract to the owner.
*/
function reclaimEther() external onlyOwner {
assert(owner.send(this.balance));
}
}
///* Remix format
//import "./MintableToken.sol";
//import "./HasNoEther.sol";
contract fmlink is MintableToken, HasNoEther
{
// Using libraries
using SafeMath for uint;
//////////////////////////////////////////////////
/// State Variables for the fmlink token contract
//////////////////////////////////////////////////
//////////////////////
// ERC20 token state
//////////////////////
/**
These state vars are handled in the OpenZeppelin libraries;
we display them here for the developer's information.
***
// ERC20Basic - Store account balances
mapping (address => uint256) public balances;
// StandardToken - Owner of account approves transfer of an amount to another account
mapping (address => mapping (address => uint256)) public allowed;
//
uint256 public totalSupply;
*/
//////////////////////
// Human token state
//////////////////////
string public constant name = "fmlink.finance";
string public constant symbol = "fmlink";
uint8 public constant decimals = 18;
///////////////////////////////////////////////////////////
// State vars for custom staking and budget functionality
///////////////////////////////////////////////////////////
// Owner last minted time
uint public ownerTimeLastMinted;
// Owner minted tokens per second
uint public ownerMintRate;
/// Stake minting
// Minted tokens per second for all stakers
uint private globalMintRate;
// Total tokens currently staked
uint public totalfmlinkStaked;
// struct that will hold user stake
struct TokenStakeData {
uint initialStakeBalance;
uint initialStakeTime;
uint initialStakePercentage;
address stakeSplitAddress;
}
// Track all tokens staked
mapping (address => TokenStakeData) public stakeBalances;
// Fire a loggable event when tokens are staked
event Stake(address indexed staker, address indexed stakeSplitAddress, uint256 value);
// Fire a loggable event when staked tokens are vested
event Vest(address indexed vester, address indexed stakeSplitAddress, uint256 stakedAmount, uint256 stakingGains);
//////////////////////////////////////////////////
/// Begin fmlink token functionality
//////////////////////////////////////////////////
/// @dev fmlink token constructor
function fmlink() public
{
// Define owner
owner = msg.sender;
// Define initial owner supply. (ether here is used only to get the decimals right)
uint _initOwnerSupply = 15000 ether;
// One-time bulk mint given to owner
bool _success = mint(msg.sender, _initOwnerSupply);
// Abort if initial minting failed for whatever reason
require(_success);
////////////////////////////////////
// Set up state minting variables
////////////////////////////////////
// Set last minted to current block.timestamp ('now')
ownerTimeLastMinted = now;
// 4100 minted tokens per day, 86400 seconds in a day
ownerMintRate = calculateFraction(4100, 86400, decimals);
// 5,000,000 targeted minted tokens per year via staking; 31,536,000 seconds per year
globalMintRate = calculateFraction(5000000, 31536000, decimals);
}
/// @dev staking function which allows users to stake an amount of tokens to gain interest for up to 10 days
function stakefmlink(uint _stakeAmount) external
{
// Require that tokens are staked successfully
require(stakeTokens(_stakeAmount));
}
/// @dev staking function which allows users to split the interest earned with another address
function stakefmlinkSplit(uint _stakeAmount, address _stakeSplitAddress) external
{
// Require that a fmlink split actually be split with an address
require(_stakeSplitAddress > 0);
// Store split address into stake mapping
stakeBalances[msg.sender].stakeSplitAddress = _stakeSplitAddress;
// Require that tokens are staked successfully
require(stakeTokens(_stakeAmount));
}
/// @dev allows users to reclaim any staked tokens
/// @return bool on success
function claimfmlink() external returns (bool success)
{
/// Sanity checks:
// require that there was some amount vested
require(stakeBalances[msg.sender].initialStakeBalance > 0);
// require that time has elapsed
require(now > stakeBalances[msg.sender].initialStakeTime);
// Calculate the time elapsed since the tokens were originally staked
uint _timePassedSinceStake = now.sub(stakeBalances[msg.sender].initialStakeTime);
// Calculate tokens to mint
uint _tokensToMint = calculateStakeGains(_timePassedSinceStake);
// Add the original stake back to the user's balance
balances[msg.sender] += stakeBalances[msg.sender].initialStakeBalance;
// Subtract stake balance from totalfmlinkStaked
totalfmlinkStaked -= stakeBalances[msg.sender].initialStakeBalance;
// Mint the new tokens; the new tokens are added to the user's balance
if (stakeBalances[msg.sender].stakeSplitAddress > 0)
{
// Splitting stake, so mint half to sender and half to stakeSplitAddress
mint(msg.sender, _tokensToMint.div(2));
mint(stakeBalances[msg.sender].stakeSplitAddress, _tokensToMint.div(2));
} else {
// Not spliting stake; mint all new tokens and give them to msg.sender
mint(msg.sender, _tokensToMint);
}
// Fire an event to tell the world of the newly vested tokens
Vest(msg.sender, stakeBalances[msg.sender].stakeSplitAddress, stakeBalances[msg.sender].initialStakeBalance, _tokensToMint);
// Clear out stored data from mapping
stakeBalances[msg.sender].initialStakeBalance = 0;
stakeBalances[msg.sender].initialStakeTime = 0;
stakeBalances[msg.sender].initialStakePercentage = 0;
stakeBalances[msg.sender].stakeSplitAddress = 0;
return true;
}
/// @dev Allows user to check their staked balance
function getStakedBalance() view external returns (uint stakedBalance)
{
return stakeBalances[msg.sender].initialStakeBalance;
}
/// @dev allows contract owner to claim their mint
function ownerClaim() external onlyOwner
{
// Sanity check: ensure that we didn't travel back in time
require(now > ownerTimeLastMinted);
uint _timePassedSinceLastMint;
uint _tokenMintCount;
bool _mintingSuccess;
// Calculate the number of seconds that have passed since the owner last took a claim
_timePassedSinceLastMint = now.sub(ownerTimeLastMinted);
// Sanity check: ensure that some time has passed since the owner last claimed
assert(_timePassedSinceLastMint > 0);
// Determine the token mint amount, determined from the number of seconds passed and the ownerMintRate
_tokenMintCount = calculateMintTotal(_timePassedSinceLastMint, ownerMintRate);
// Mint the owner's tokens; this also increases totalSupply
_mintingSuccess = mint(msg.sender, _tokenMintCount);
// Sanity check: ensure that the minting was successful
require(_mintingSuccess);
// New minting was a success! Set last time minted to current block.timestamp (now)
ownerTimeLastMinted = now;
}
/// @dev stake function reduces the user's total available balance. totalSupply is unaffected
/// @param _value determines how many tokens a user wants to stake
function stakeTokens(uint256 _value) private returns (bool success)
{
/// Sanity Checks:
// You can only stake as many tokens as you have
require(_value <= balances[msg.sender]);
// You can only stake tokens if you have not already staked tokens
require(stakeBalances[msg.sender].initialStakeBalance == 0);
// Subtract stake amount from regular token balance
balances[msg.sender] = balances[msg.sender].sub(_value);
// Add stake amount to staked balance
stakeBalances[msg.sender].initialStakeBalance = _value;
// Increment the global staked tokens value
totalfmlinkStaked += _value;
/// Determine percentage of global stake
stakeBalances[msg.sender].initialStakePercentage = calculateFraction(_value, totalfmlinkStaked, decimals);
// Save the time that the stake started
stakeBalances[msg.sender].initialStakeTime = now;
// Fire an event to tell the world of the newly staked tokens
Stake(msg.sender, stakeBalances[msg.sender].stakeSplitAddress, _value);
return true;
}
/// @dev Helper function to claimStake that modularizes the minting via staking calculation
function calculateStakeGains(uint _timePassedSinceStake) view private returns (uint mintTotal)
{
// Store seconds in a day (need it in variable to use SafeMath)
uint _secondsPerDay = 86400;
uint _finalStakePercentage; // store our stake percentage at the time of stake claim
uint _stakePercentageAverage; // store our calculated average minting rate ((initial+final) / 2)
uint _finalMintRate; // store our calculated final mint rate (in fmlink-per-second)
uint _tokensToMint = 0; // store number of new tokens to be minted
// Determine the amount to be newly minted upon vesting, if any
if (_timePassedSinceStake > _secondsPerDay) {
/// We've passed the minimum staking time; calculate minting rate average ((initialRate + finalRate) / 2)
// First, calculate our final stake percentage based upon the total amount of fmlink staked
_finalStakePercentage = calculateFraction(stakeBalances[msg.sender].initialStakeBalance, totalfmlinkStaked, decimals);
// Second, calculate average of initial and final stake percentage
_stakePercentageAverage = calculateFraction((stakeBalances[msg.sender].initialStakePercentage.add(_finalStakePercentage)), 2, 0);
// Finally, calculate our final mint rate (in fmlink-per-second)
_finalMintRate = globalMintRate.mul(_stakePercentageAverage);
_finalMintRate = _finalMintRate.div(1 ether);
// Tokens were staked for enough time to mint new tokens; determine how many
if (_timePassedSinceStake > _secondsPerDay.mul(10)) {
// Tokens were staked for the maximum amount of time (10 days)
_tokensToMint = calculateMintTotal(_secondsPerDay.mul(10), _finalMintRate);
} else {
// Tokens were staked for a mintable amount of time, but less than the 10-day max
_tokensToMint = calculateMintTotal(_timePassedSinceStake, _finalMintRate);
}
}
// Return the amount of new tokens to be minted
return _tokensToMint;
}
/// @dev calculateFraction allows us to better handle the Solidity ugliness of not having decimals as a native type
/// @param _numerator is the top part of the fraction we are calculating
/// @param _denominator is the bottom part of the fraction we are calculating
/// @param _precision tells the function how many significant digits to calculate out to
/// @return quotient returns the result of our fraction calculation
function calculateFraction(uint _numerator, uint _denominator, uint _precision) pure private returns(uint quotient)
{
// Take passed value and expand it to the required precision
_numerator = _numerator.mul(10 ** (_precision + 1));
// handle last-digit rounding
uint _quotient = ((_numerator.div(_denominator)) + 5) / 10;
return (_quotient);
}
/// @dev Determines mint total based upon how many seconds have passed
/// @param _timeInSeconds takes the time that has elapsed since the last minting
/// @return uint with the calculated number of new tokens to mint
function calculateMintTotal(uint _timeInSeconds, uint _mintRate) pure private returns(uint mintAmount)
{
// Calculates the amount of tokens to mint based upon the number of seconds passed
return(_timeInSeconds.mul(_mintRate));
}
}
{
"compilationTarget": {
"fmlink.sol": "fmlink"
},
"libraries": {},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalfmlinkStaked","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"stakeBalances","outputs":[{"name":"initialStakeBalance","type":"uint256"},{"name":"initialStakeTime","type":"uint256"},{"name":"initialStakePercentage","type":"uint256"},{"name":"stakeSplitAddress","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"claimfmlink","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"ownerClaim","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_subtractedValue","type":"uint256"}],"name":"decreaseApproval","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_stakeAmount","type":"uint256"}],"name":"stakefmlink","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getStakedBalance","outputs":[{"name":"stakedBalance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_stakeAmount","type":"uint256"},{"name":"_stakeSplitAddress","type":"address"}],"name":"stakefmlinkSplit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"reclaimEther","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"ownerMintRate","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"ownerTimeLastMinted","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_addedValue","type":"uint256"}],"name":"increaseApproval","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":false,"stateMutability":"nonpayable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"staker","type":"address"},{"indexed":true,"name":"stakeSplitAddress","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Stake","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"vester","type":"address"},{"indexed":true,"name":"stakeSplitAddress","type":"address"},{"indexed":false,"name":"stakedAmount","type":"uint256"},{"indexed":false,"name":"stakingGains","type":"uint256"}],"name":"Vest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}]