文件 1 的 21:Address.sol
pragma solidity ^0.7.0;
library Address {
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly { size := extcodesize(account) }
return size > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{ value: value }(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
文件 2 的 21:Aludel.sol
pragma solidity 0.7.6;
pragma abicoder v2;
import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/EnumerableSet.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {TransferHelper} from "@uniswap/lib/contracts/libraries/TransferHelper.sol";
import {IFactory} from "../factory/IFactory.sol";
import {IInstanceRegistry} from "../factory/InstanceRegistry.sol";
import {IUniversalVault} from "../crucible/Crucible.sol";
import {IRewardPool} from "./RewardPool.sol";
import {Powered} from "./Powered.sol";
interface IRageQuit {
function rageQuit() external;
}
interface IAludel is IRageQuit {
event AludelCreated(address rewardPool, address powerSwitch);
event AludelFunded(uint256 amount, uint256 duration);
event BonusTokenRegistered(address token);
event VaultFactoryRegistered(address factory);
event VaultFactoryRemoved(address factory);
event Staked(address vault, uint256 amount);
event Unstaked(address vault, uint256 amount);
event RewardClaimed(address vault, address recipient, address token, uint256 amount);
struct AludelData {
address stakingToken;
address rewardToken;
address rewardPool;
RewardScaling rewardScaling;
uint256 rewardSharesOutstanding;
uint256 totalStake;
uint256 totalStakeUnits;
uint256 lastUpdate;
RewardSchedule[] rewardSchedules;
}
struct RewardSchedule {
uint256 duration;
uint256 start;
uint256 shares;
}
struct VaultData {
uint256 totalStake;
StakeData[] stakes;
}
struct StakeData {
uint256 amount;
uint256 timestamp;
}
struct RewardScaling {
uint256 floor;
uint256 ceiling;
uint256 time;
}
struct RewardOutput {
uint256 lastStakeAmount;
uint256 newStakesCount;
uint256 reward;
uint256 newTotalStakeUnits;
}
function stake(
address vault,
uint256 amount,
bytes calldata permission
) external;
function unstakeAndClaim(
address vault,
address recipient,
uint256 amount,
bytes calldata permission
) external;
function getAludelData() external view returns (AludelData memory aludel);
function getBonusTokenSetLength() external view returns (uint256 length);
function getBonusTokenAtIndex(uint256 index) external view returns (address bonusToken);
function getVaultFactorySetLength() external view returns (uint256 length);
function getVaultFactoryAtIndex(uint256 index) external view returns (address factory);
function getVaultData(address vault) external view returns (VaultData memory vaultData);
function isValidAddress(address target) external view returns (bool validity);
function isValidVault(address target) external view returns (bool validity);
function getCurrentUnlockedRewards() external view returns (uint256 unlockedRewards);
function getFutureUnlockedRewards(uint256 timestamp)
external
view
returns (uint256 unlockedRewards);
function getCurrentVaultReward(address vault) external view returns (uint256 reward);
function getCurrentStakeReward(address vault, uint256 stakeAmount)
external
view
returns (uint256 reward);
function getFutureVaultReward(address vault, uint256 timestamp)
external
view
returns (uint256 reward);
function getFutureStakeReward(
address vault,
uint256 stakeAmount,
uint256 timestamp
) external view returns (uint256 reward);
function getCurrentVaultStakeUnits(address vault) external view returns (uint256 stakeUnits);
function getFutureVaultStakeUnits(address vault, uint256 timestamp)
external
view
returns (uint256 stakeUnits);
function getCurrentTotalStakeUnits() external view returns (uint256 totalStakeUnits);
function getFutureTotalStakeUnits(uint256 timestamp)
external
view
returns (uint256 totalStakeUnits);
function calculateTotalStakeUnits(StakeData[] memory stakes, uint256 timestamp)
external
pure
returns (uint256 totalStakeUnits);
function calculateStakeUnits(
uint256 amount,
uint256 start,
uint256 end
) external pure returns (uint256 stakeUnits);
function calculateUnlockedRewards(
RewardSchedule[] memory rewardSchedules,
uint256 rewardBalance,
uint256 sharesOutstanding,
uint256 timestamp
) external pure returns (uint256 unlockedRewards);
function calculateRewardFromStakes(
StakeData[] memory stakes,
uint256 unstakeAmount,
uint256 unlockedRewards,
uint256 totalStakeUnits,
uint256 timestamp,
RewardScaling memory rewardScaling
) external pure returns (RewardOutput memory out);
function calculateReward(
uint256 unlockedRewards,
uint256 stakeAmount,
uint256 stakeDuration,
uint256 totalStakeUnits,
RewardScaling memory rewardScaling
) external pure returns (uint256 reward);
}
contract Aludel is IAludel, Powered, Ownable {
using SafeMath for uint256;
using EnumerableSet for EnumerableSet.AddressSet;
uint256 public constant MAX_STAKES_PER_VAULT = 30;
uint256 public constant MAX_REWARD_TOKENS = 50;
uint256 public constant BASE_SHARES_PER_WEI = 1000000;
AludelData private _aludel;
mapping(address => VaultData) private _vaults;
EnumerableSet.AddressSet private _bonusTokenSet;
EnumerableSet.AddressSet private _vaultFactorySet;
constructor(
address ownerAddress,
address rewardPoolFactory,
address powerSwitchFactory,
address stakingToken,
address rewardToken,
RewardScaling memory rewardScaling
) {
require(rewardScaling.floor <= rewardScaling.ceiling, "Aludel: floor above ceiling");
require(rewardScaling.time != 0, "Aludel: scaling time cannot be zero");
address powerSwitch = IFactory(powerSwitchFactory).create(abi.encode(ownerAddress));
address rewardPool = IFactory(rewardPoolFactory).create(abi.encode(powerSwitch));
Ownable.transferOwnership(ownerAddress);
Powered._setPowerSwitch(powerSwitch);
_aludel.stakingToken = stakingToken;
_aludel.rewardToken = rewardToken;
_aludel.rewardPool = rewardPool;
_aludel.rewardScaling = rewardScaling;
emit AludelCreated(rewardPool, powerSwitch);
}
function getBonusTokenSetLength() external view override returns (uint256 length) {
return _bonusTokenSet.length();
}
function getBonusTokenAtIndex(uint256 index)
external
view
override
returns (address bonusToken)
{
return _bonusTokenSet.at(index);
}
function getVaultFactorySetLength() external view override returns (uint256 length) {
return _vaultFactorySet.length();
}
function getVaultFactoryAtIndex(uint256 index)
external
view
override
returns (address factory)
{
return _vaultFactorySet.at(index);
}
function isValidVault(address target) public view override returns (bool validity) {
for (uint256 index = 0; index < _vaultFactorySet.length(); index++) {
if (IInstanceRegistry(_vaultFactorySet.at(index)).isInstance(target)) {
return true;
}
}
return false;
}
function isValidAddress(address target) public view override returns (bool validity) {
return
target != address(this) &&
target != address(0) &&
target != _aludel.stakingToken &&
target != _aludel.rewardToken &&
target != _aludel.rewardPool &&
!_bonusTokenSet.contains(target);
}
function getAludelData() external view override returns (AludelData memory aludel) {
return _aludel;
}
function getCurrentUnlockedRewards() public view override returns (uint256 unlockedRewards) {
return getFutureUnlockedRewards(block.timestamp);
}
function getFutureUnlockedRewards(uint256 timestamp)
public
view
override
returns (uint256 unlockedRewards)
{
uint256 remainingRewards = IERC20(_aludel.rewardToken).balanceOf(_aludel.rewardPool);
unlockedRewards = calculateUnlockedRewards(
_aludel.rewardSchedules,
remainingRewards,
_aludel.rewardSharesOutstanding,
timestamp
);
return unlockedRewards;
}
function getCurrentTotalStakeUnits() public view override returns (uint256 totalStakeUnits) {
return getFutureTotalStakeUnits(block.timestamp);
}
function getFutureTotalStakeUnits(uint256 timestamp)
public
view
override
returns (uint256 totalStakeUnits)
{
if (timestamp == _aludel.lastUpdate) return _aludel.totalStakeUnits;
uint256 newStakeUnits =
calculateStakeUnits(_aludel.totalStake, _aludel.lastUpdate, timestamp);
totalStakeUnits = _aludel.totalStakeUnits.add(newStakeUnits);
return totalStakeUnits;
}
function getVaultData(address vault)
external
view
override
returns (VaultData memory vaultData)
{
return _vaults[vault];
}
function getCurrentVaultReward(address vault) external view override returns (uint256 reward) {
return
calculateRewardFromStakes(
_vaults[vault]
.stakes,
_vaults[vault]
.totalStake,
getCurrentUnlockedRewards(),
getCurrentTotalStakeUnits(),
block
.timestamp,
_aludel
.rewardScaling
)
.reward;
}
function getFutureVaultReward(address vault, uint256 timestamp)
external
view
override
returns (uint256 reward)
{
return
calculateRewardFromStakes(
_vaults[vault]
.stakes,
_vaults[vault]
.totalStake,
getFutureUnlockedRewards(timestamp),
getFutureTotalStakeUnits(timestamp),
timestamp,
_aludel
.rewardScaling
)
.reward;
}
function getCurrentStakeReward(address vault, uint256 stakeAmount)
external
view
override
returns (uint256 reward)
{
return
calculateRewardFromStakes(
_vaults[vault]
.stakes,
stakeAmount,
getCurrentUnlockedRewards(),
getCurrentTotalStakeUnits(),
block
.timestamp,
_aludel
.rewardScaling
)
.reward;
}
function getFutureStakeReward(
address vault,
uint256 stakeAmount,
uint256 timestamp
) external view override returns (uint256 reward) {
return
calculateRewardFromStakes(
_vaults[vault]
.stakes,
stakeAmount,
getFutureUnlockedRewards(timestamp),
getFutureTotalStakeUnits(timestamp),
timestamp,
_aludel
.rewardScaling
)
.reward;
}
function getCurrentVaultStakeUnits(address vault)
public
view
override
returns (uint256 stakeUnits)
{
return getFutureVaultStakeUnits(vault, block.timestamp);
}
function getFutureVaultStakeUnits(address vault, uint256 timestamp)
public
view
override
returns (uint256 stakeUnits)
{
return calculateTotalStakeUnits(_vaults[vault].stakes, timestamp);
}
function calculateTotalStakeUnits(StakeData[] memory stakes, uint256 timestamp)
public
pure
override
returns (uint256 totalStakeUnits)
{
for (uint256 index; index < stakes.length; index++) {
StakeData memory stakeData = stakes[index];
uint256 stakeUnits =
calculateStakeUnits(stakeData.amount, stakeData.timestamp, timestamp);
totalStakeUnits = totalStakeUnits.add(stakeUnits);
}
}
function calculateStakeUnits(
uint256 amount,
uint256 start,
uint256 end
) public pure override returns (uint256 stakeUnits) {
uint256 duration = end.sub(start);
stakeUnits = duration.mul(amount);
return stakeUnits;
}
function calculateUnlockedRewards(
RewardSchedule[] memory rewardSchedules,
uint256 rewardBalance,
uint256 sharesOutstanding,
uint256 timestamp
) public pure override returns (uint256 unlockedRewards) {
if (rewardSchedules.length == 0) {
return 0;
}
uint256 sharesLocked;
for (uint256 index = 0; index < rewardSchedules.length; index++) {
RewardSchedule memory schedule = rewardSchedules[index];
uint256 currentSharesLocked = 0;
if (timestamp.sub(schedule.start) < schedule.duration) {
currentSharesLocked = schedule.shares.sub(
schedule.shares.mul(timestamp.sub(schedule.start)).div(schedule.duration)
);
}
sharesLocked = sharesLocked.add(currentSharesLocked);
}
uint256 rewardLocked = sharesLocked.mul(rewardBalance).div(sharesOutstanding);
unlockedRewards = rewardBalance.sub(rewardLocked);
return unlockedRewards;
}
function calculateRewardFromStakes(
StakeData[] memory stakes,
uint256 unstakeAmount,
uint256 unlockedRewards,
uint256 totalStakeUnits,
uint256 timestamp,
RewardScaling memory rewardScaling
) public pure override returns (RewardOutput memory out) {
uint256 stakesToDrop = 0;
while (unstakeAmount > 0) {
StakeData memory lastStake = stakes[stakes.length.sub(stakesToDrop).sub(1)];
uint256 stakeDuration = timestamp.sub(lastStake.timestamp);
uint256 currentAmount;
if (lastStake.amount > unstakeAmount) {
currentAmount = unstakeAmount;
out.lastStakeAmount = lastStake.amount.sub(unstakeAmount);
} else {
currentAmount = lastStake.amount;
stakesToDrop += 1;
}
unstakeAmount = unstakeAmount.sub(currentAmount);
uint256 currentReward =
calculateReward(
unlockedRewards,
currentAmount,
stakeDuration,
totalStakeUnits,
rewardScaling
);
out.reward = out.reward.add(currentReward);
unlockedRewards = unlockedRewards.sub(currentReward);
uint256 stakeUnits = currentAmount.mul(stakeDuration);
totalStakeUnits = totalStakeUnits.sub(stakeUnits);
}
return
RewardOutput(
out.lastStakeAmount,
stakes.length.sub(stakesToDrop),
out.reward,
totalStakeUnits
);
}
function calculateReward(
uint256 unlockedRewards,
uint256 stakeAmount,
uint256 stakeDuration,
uint256 totalStakeUnits,
RewardScaling memory rewardScaling
) public pure override returns (uint256 reward) {
uint256 stakeUnits = stakeAmount.mul(stakeDuration);
uint256 baseReward = 0;
if (totalStakeUnits != 0) {
baseReward = unlockedRewards.mul(stakeUnits).div(totalStakeUnits);
}
if (stakeDuration >= rewardScaling.time || rewardScaling.floor == rewardScaling.ceiling) {
reward = baseReward;
} else {
uint256 minReward = baseReward.mul(rewardScaling.floor).div(rewardScaling.ceiling);
uint256 bonusReward =
baseReward
.mul(stakeDuration)
.mul(rewardScaling.ceiling.sub(rewardScaling.floor))
.div(rewardScaling.ceiling)
.div(rewardScaling.time);
reward = minReward.add(bonusReward);
}
return reward;
}
function fund(uint256 amount, uint256 duration) external onlyOwner onlyOnline {
require(duration != 0, "Aludel: invalid duration");
uint256 newRewardShares;
if (_aludel.rewardSharesOutstanding > 0) {
uint256 remainingRewards = IERC20(_aludel.rewardToken).balanceOf(_aludel.rewardPool);
newRewardShares = _aludel.rewardSharesOutstanding.mul(amount).div(remainingRewards);
} else {
newRewardShares = amount.mul(BASE_SHARES_PER_WEI);
}
_aludel.rewardSharesOutstanding = _aludel.rewardSharesOutstanding.add(newRewardShares);
_aludel.rewardSchedules.push(RewardSchedule(duration, block.timestamp, newRewardShares));
TransferHelper.safeTransferFrom(
_aludel.rewardToken,
msg.sender,
_aludel.rewardPool,
amount
);
emit AludelFunded(amount, duration);
}
function registerVaultFactory(address factory) external onlyOwner notShutdown {
require(_vaultFactorySet.add(factory), "Aludel: vault factory already registered");
emit VaultFactoryRegistered(factory);
}
function removeVaultFactory(address factory) external onlyOwner notShutdown {
require(_vaultFactorySet.remove(factory), "Aludel: vault factory not registered");
emit VaultFactoryRemoved(factory);
}
function registerBonusToken(address bonusToken) external onlyOwner onlyOnline {
_validateAddress(bonusToken);
require(_bonusTokenSet.length() < MAX_REWARD_TOKENS, "Aludel: max bonus tokens reached ");
assert(_bonusTokenSet.add(bonusToken));
emit BonusTokenRegistered(bonusToken);
}
function rescueTokensFromRewardPool(
address token,
address recipient,
uint256 amount
) external onlyOwner onlyOnline {
_validateAddress(recipient);
require(token != _aludel.rewardToken, "Aludel: invalid address");
require(!_bonusTokenSet.contains(token), "Aludel: invalid address");
IRewardPool(_aludel.rewardPool).sendERC20(token, recipient, amount);
}
function stake(
address vault,
uint256 amount,
bytes calldata permission
) external override onlyOnline {
require(isValidVault(vault), "Aludel: vault is not registered");
require(amount != 0, "Aludel: no amount staked");
VaultData storage vaultData = _vaults[vault];
require(
vaultData.stakes.length < MAX_STAKES_PER_VAULT,
"Aludel: MAX_STAKES_PER_VAULT reached"
);
_updateTotalStakeUnits();
vaultData.stakes.push(StakeData(amount, block.timestamp));
vaultData.totalStake = vaultData.totalStake.add(amount);
_aludel.totalStake = _aludel.totalStake.add(amount);
IUniversalVault(vault).lock(_aludel.stakingToken, amount, permission);
emit Staked(vault, amount);
}
function unstakeAndClaim(
address vault,
address recipient,
uint256 amount,
bytes calldata permission
) external override onlyOnline {
VaultData storage vaultData = _vaults[vault];
require(amount != 0, "Aludel: no amount unstaked");
_validateAddress(recipient);
require(vaultData.totalStake >= amount, "Aludel: insufficient vault stake");
assert(_aludel.totalStake >= amount);
_updateTotalStakeUnits();
uint256 remainingRewards = IERC20(_aludel.rewardToken).balanceOf(_aludel.rewardPool);
uint256 unlockedRewards =
calculateUnlockedRewards(
_aludel.rewardSchedules,
remainingRewards,
_aludel.rewardSharesOutstanding,
block.timestamp
);
RewardOutput memory out =
calculateRewardFromStakes(
vaultData.stakes,
amount,
unlockedRewards,
_aludel.totalStakeUnits,
block.timestamp,
_aludel.rewardScaling
);
if (out.newStakesCount == 0) {
delete vaultData.stakes;
} else {
while (vaultData.stakes.length > out.newStakesCount) vaultData.stakes.pop();
vaultData.stakes[out.newStakesCount.sub(1)].amount = out.lastStakeAmount;
}
vaultData.totalStake = vaultData.totalStake.sub(amount);
_aludel.totalStake = _aludel.totalStake.sub(amount);
_aludel.totalStakeUnits = out.newTotalStakeUnits;
IUniversalVault(vault).unlock(_aludel.stakingToken, amount, permission);
emit Unstaked(vault, amount);
if (out.reward > 0) {
uint256 sharesToBurn =
_aludel.rewardSharesOutstanding.mul(out.reward).div(remainingRewards);
_aludel.rewardSharesOutstanding = _aludel.rewardSharesOutstanding.sub(sharesToBurn);
if (_bonusTokenSet.length() > 0) {
for (uint256 index = 0; index < _bonusTokenSet.length(); index++) {
address bonusToken = _bonusTokenSet.at(index);
uint256 bonusAmount =
IERC20(bonusToken).balanceOf(_aludel.rewardPool).mul(out.reward).div(
remainingRewards
);
IRewardPool(_aludel.rewardPool).sendERC20(bonusToken, recipient, bonusAmount);
emit RewardClaimed(vault, recipient, bonusToken, bonusAmount);
}
}
IRewardPool(_aludel.rewardPool).sendERC20(_aludel.rewardToken, recipient, out.reward);
emit RewardClaimed(vault, recipient, _aludel.rewardToken, out.reward);
}
}
function rageQuit() external override {
VaultData storage _vaultData = _vaults[msg.sender];
require(_vaultData.stakes.length != 0, "Aludel: no stake");
_updateTotalStakeUnits();
emit Unstaked(msg.sender, _vaultData.totalStake);
_aludel.totalStake = _aludel.totalStake.sub(_vaultData.totalStake);
_aludel.totalStakeUnits = _aludel.totalStakeUnits.sub(
calculateTotalStakeUnits(_vaultData.stakes, block.timestamp)
);
delete _vaults[msg.sender];
}
function _updateTotalStakeUnits() private {
_aludel.totalStakeUnits = getCurrentTotalStakeUnits();
_aludel.lastUpdate = block.timestamp;
}
function _validateAddress(address target) private view {
require(isValidAddress(target), "Aludel: invalid address");
}
function _truncateStakesArray(StakeData[] memory array, uint256 newLength)
private
pure
returns (StakeData[] memory newArray)
{
newArray = new StakeData[](newLength);
for (uint256 index = 0; index < newLength; index++) {
newArray[index] = array[index];
}
return newArray;
}
}
文件 3 的 21:Context.sol
pragma solidity >=0.6.0 <0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this;
return msg.data;
}
}
文件 4 的 21:Crucible.sol
pragma solidity 0.7.6;
pragma abicoder v2;
import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Initializable} from "@openzeppelin/contracts/proxy/Initializable.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/EnumerableSet.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {TransferHelper} from "@uniswap/lib/contracts/libraries/TransferHelper.sol";
import {EIP712} from "./EIP712.sol";
import {ERC1271} from "./ERC1271.sol";
import {OwnableERC721} from "./OwnableERC721.sol";
import {IRageQuit} from "../aludel/Aludel.sol";
interface IUniversalVault {
event Locked(address delegate, address token, uint256 amount);
event Unlocked(address delegate, address token, uint256 amount);
event RageQuit(address delegate, address token, bool notified, string reason);
struct LockData {
address delegate;
address token;
uint256 balance;
}
function initialize() external;
function lock(
address token,
uint256 amount,
bytes calldata permission
) external;
function unlock(
address token,
uint256 amount,
bytes calldata permission
) external;
function rageQuit(address delegate, address token)
external
returns (bool notified, string memory error);
function transferERC20(
address token,
address to,
uint256 amount
) external;
function transferETH(address to, uint256 amount) external payable;
function calculateLockID(address delegate, address token)
external
pure
returns (bytes32 lockID);
function getPermissionHash(
bytes32 eip712TypeHash,
address delegate,
address token,
uint256 amount,
uint256 nonce
) external view returns (bytes32 permissionHash);
function getNonce() external view returns (uint256 nonce);
function owner() external view returns (address ownerAddress);
function getLockSetCount() external view returns (uint256 count);
function getLockAt(uint256 index) external view returns (LockData memory lockData);
function getBalanceDelegated(address token, address delegate)
external
view
returns (uint256 balance);
function getBalanceLocked(address token) external view returns (uint256 balance);
function checkBalances() external view returns (bool validity);
}
contract Crucible is
IUniversalVault,
EIP712("UniversalVault", "1.0.0"),
ERC1271,
OwnableERC721,
Initializable
{
using SafeMath for uint256;
using Address for address;
using Address for address payable;
using EnumerableSet for EnumerableSet.Bytes32Set;
uint256 public constant RAGEQUIT_GAS = 500000;
bytes32 public constant LOCK_TYPEHASH =
keccak256("Lock(address delegate,address token,uint256 amount,uint256 nonce)");
bytes32 public constant UNLOCK_TYPEHASH =
keccak256("Unlock(address delegate,address token,uint256 amount,uint256 nonce)");
uint256 private _nonce;
mapping(bytes32 => LockData) private _locks;
EnumerableSet.Bytes32Set private _lockSet;
function initializeLock() external initializer {}
function initialize() external override initializer {
OwnableERC721._setNFT(msg.sender);
}
receive() external payable {}
function _getOwner() internal view override(ERC1271) returns (address ownerAddress) {
return OwnableERC721.owner();
}
function calculateLockID(address delegate, address token)
public
pure
override
returns (bytes32 lockID)
{
return keccak256(abi.encodePacked(delegate, token));
}
function getPermissionHash(
bytes32 eip712TypeHash,
address delegate,
address token,
uint256 amount,
uint256 nonce
) public view override returns (bytes32 permissionHash) {
return
EIP712._hashTypedDataV4(
keccak256(abi.encode(eip712TypeHash, delegate, token, amount, nonce))
);
}
function getNonce() external view override returns (uint256 nonce) {
return _nonce;
}
function owner()
public
view
override(IUniversalVault, OwnableERC721)
returns (address ownerAddress)
{
return OwnableERC721.owner();
}
function getLockSetCount() external view override returns (uint256 count) {
return _lockSet.length();
}
function getLockAt(uint256 index) external view override returns (LockData memory lockData) {
return _locks[_lockSet.at(index)];
}
function getBalanceDelegated(address token, address delegate)
external
view
override
returns (uint256 balance)
{
return _locks[calculateLockID(delegate, token)].balance;
}
function getBalanceLocked(address token) public view override returns (uint256 balance) {
uint256 count = _lockSet.length();
for (uint256 index; index < count; index++) {
LockData storage _lockData = _locks[_lockSet.at(index)];
if (_lockData.token == token && _lockData.balance > balance)
balance = _lockData.balance;
}
return balance;
}
function checkBalances() external view override returns (bool validity) {
uint256 count = _lockSet.length();
for (uint256 index; index < count; index++) {
LockData storage _lockData = _locks[_lockSet.at(index)];
if (IERC20(_lockData.token).balanceOf(address(this)) < _lockData.balance) return false;
}
return true;
}
function lock(
address token,
uint256 amount,
bytes calldata permission
)
external
override
onlyValidSignature(
getPermissionHash(LOCK_TYPEHASH, msg.sender, token, amount, _nonce),
permission
)
{
bytes32 lockID = calculateLockID(msg.sender, token);
if (_lockSet.contains(lockID)) {
_locks[lockID].balance = _locks[lockID].balance.add(amount);
} else {
assert(_lockSet.add(lockID));
_locks[lockID] = LockData(msg.sender, token, amount);
}
require(
IERC20(token).balanceOf(address(this)) >= _locks[lockID].balance,
"UniversalVault: insufficient balance"
);
_nonce += 1;
emit Locked(msg.sender, token, amount);
}
function unlock(
address token,
uint256 amount,
bytes calldata permission
)
external
override
onlyValidSignature(
getPermissionHash(UNLOCK_TYPEHASH, msg.sender, token, amount, _nonce),
permission
)
{
bytes32 lockID = calculateLockID(msg.sender, token);
require(_lockSet.contains(lockID), "UniversalVault: missing lock");
if (_locks[lockID].balance > amount) {
_locks[lockID].balance = _locks[lockID].balance.sub(amount);
} else {
delete _locks[lockID];
assert(_lockSet.remove(lockID));
}
_nonce += 1;
emit Unlocked(msg.sender, token, amount);
}
function rageQuit(address delegate, address token)
external
override
onlyOwner
returns (bool notified, string memory error)
{
bytes32 lockID = calculateLockID(delegate, token);
require(_lockSet.contains(lockID), "UniversalVault: missing lock");
if (delegate.isContract()) {
require(gasleft() >= RAGEQUIT_GAS, "UniversalVault: insufficient gas");
try IRageQuit(delegate).rageQuit{gas: RAGEQUIT_GAS}() {
notified = true;
} catch Error(string memory res) {
notified = false;
error = res;
} catch (bytes memory) {
notified = false;
}
}
assert(_lockSet.remove(lockID));
delete _locks[lockID];
emit RageQuit(delegate, token, notified, error);
}
function transferERC20(
address token,
address to,
uint256 amount
) external override onlyOwner {
require(
IERC20(token).balanceOf(address(this)) >= getBalanceLocked(token).add(amount),
"UniversalVault: insufficient balance"
);
TransferHelper.safeTransfer(token, to, amount);
}
function transferETH(address to, uint256 amount) external payable override onlyOwner {
TransferHelper.safeTransferETH(to, amount);
}
}
文件 5 的 21:ECDSA.sol
pragma solidity ^0.7.0;
library ECDSA {
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
if (signature.length != 65) {
revert("ECDSA: invalid signature length");
}
bytes32 r;
bytes32 s;
uint8 v;
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return recover(hash, v, r, s);
}
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value");
require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value");
address signer = ecrecover(hash, v, r, s);
require(signer != address(0), "ECDSA: invalid signature");
return signer;
}
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
}
文件 6 的 21:EIP712.sol
pragma solidity >=0.6.0 <0.8.0;
abstract contract EIP712 {
bytes32 private immutable _HASHED_NAME;
bytes32 private immutable _HASHED_VERSION;
bytes32 private constant _TYPE_HASH =
keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
);
constructor(string memory name, string memory version) {
_HASHED_NAME = keccak256(bytes(name));
_HASHED_VERSION = keccak256(bytes(version));
}
function _domainSeparatorV4() internal view returns (bytes32) {
return _buildDomainSeparator(_TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash());
}
function _buildDomainSeparator(
bytes32 typeHash,
bytes32 name,
bytes32 version
) private view returns (bytes32) {
return keccak256(abi.encode(typeHash, name, version, _getChainId(), address(this)));
}
function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x01", _domainSeparatorV4(), structHash));
}
function _getChainId() private view returns (uint256 chainId) {
this;
assembly {
chainId := chainid()
}
}
function _EIP712NameHash() internal view virtual returns (bytes32) {
return _HASHED_NAME;
}
function _EIP712VersionHash() internal view virtual returns (bytes32) {
return _HASHED_VERSION;
}
}
文件 7 的 21:ERC1271.sol
pragma solidity 0.7.6;
import {ECDSA} from "@openzeppelin/contracts/cryptography/ECDSA.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
interface IERC1271 {
function isValidSignature(bytes32 _messageHash, bytes memory _signature)
external
view
returns (bytes4 magicValue);
}
library SignatureChecker {
function isValidSignature(
address signer,
bytes32 hash,
bytes memory signature
) internal view returns (bool) {
if (Address.isContract(signer)) {
bytes4 selector = IERC1271.isValidSignature.selector;
(bool success, bytes memory returndata) =
signer.staticcall(abi.encodeWithSelector(selector, hash, signature));
return success && abi.decode(returndata, (bytes4)) == selector;
} else {
return ECDSA.recover(hash, signature) == signer;
}
}
}
abstract contract ERC1271 is IERC1271 {
bytes4 internal constant VALID_SIG = IERC1271.isValidSignature.selector;
bytes4 internal constant INVALID_SIG = bytes4(0);
modifier onlyValidSignature(bytes32 permissionHash, bytes memory signature) {
require(
isValidSignature(permissionHash, signature) == VALID_SIG,
"ERC1271: Invalid signature"
);
_;
}
function _getOwner() internal view virtual returns (address owner);
function isValidSignature(bytes32 permissionHash, bytes memory signature)
public
view
override
returns (bytes4)
{
return
SignatureChecker.isValidSignature(_getOwner(), permissionHash, signature)
? VALID_SIG
: INVALID_SIG;
}
}
文件 8 的 21:EnumerableSet.sol
pragma solidity ^0.7.0;
library EnumerableSet {
struct Set {
bytes32[] _values;
mapping (bytes32 => uint256) _indexes;
}
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
function _remove(Set storage set, bytes32 value) private returns (bool) {
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
bytes32 lastvalue = set._values[lastIndex];
set._values[toDeleteIndex] = lastvalue;
set._indexes[lastvalue] = toDeleteIndex + 1;
set._values.pop();
delete set._indexes[value];
return true;
} else {
return false;
}
}
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
function _at(Set storage set, uint256 index) private view returns (bytes32) {
require(set._values.length > index, "EnumerableSet: index out of bounds");
return set._values[index];
}
struct Bytes32Set {
Set _inner;
}
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
struct AddressSet {
Set _inner;
}
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
struct UintSet {
Set _inner;
}
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
}
文件 9 的 21:IERC165.sol
pragma solidity ^0.7.0;
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
文件 10 的 21:IERC20.sol
pragma solidity ^0.7.0;
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
}
文件 11 的 21:IERC721.sol
pragma solidity ^0.7.0;
import "../../introspection/IERC165.sol";
interface IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
function balanceOf(address owner) external view returns (uint256 balance);
function ownerOf(uint256 tokenId) external view returns (address owner);
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function transferFrom(address from, address to, uint256 tokenId) external;
function approve(address to, uint256 tokenId) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function setApprovalForAll(address operator, bool _approved) external;
function isApprovedForAll(address owner, address operator) external view returns (bool);
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
}
文件 12 的 21:IFactory.sol
pragma solidity 0.7.6;
interface IFactory {
function create(bytes calldata args) external returns (address instance);
function create2(bytes calldata args, bytes32 salt) external returns (address instance);
}
文件 13 的 21:Initializable.sol
pragma solidity >=0.4.24 <0.8.0;
import "../utils/Address.sol";
abstract contract Initializable {
bool private _initialized;
bool private _initializing;
modifier initializer() {
require(_initializing || _isConstructor() || !_initialized, "Initializable: contract is already initialized");
bool isTopLevelCall = !_initializing;
if (isTopLevelCall) {
_initializing = true;
_initialized = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
}
}
function _isConstructor() private view returns (bool) {
return !Address.isContract(address(this));
}
}
文件 14 的 21:InstanceRegistry.sol
pragma solidity 0.7.6;
import {EnumerableSet} from "@openzeppelin/contracts/utils/EnumerableSet.sol";
interface IInstanceRegistry {
event InstanceAdded(address instance);
event InstanceRemoved(address instance);
function isInstance(address instance) external view returns (bool validity);
function instanceCount() external view returns (uint256 count);
function instanceAt(uint256 index) external view returns (address instance);
}
contract InstanceRegistry is IInstanceRegistry {
using EnumerableSet for EnumerableSet.AddressSet;
EnumerableSet.AddressSet private _instanceSet;
function isInstance(address instance) external view override returns (bool validity) {
return _instanceSet.contains(instance);
}
function instanceCount() external view override returns (uint256 count) {
return _instanceSet.length();
}
function instanceAt(uint256 index) external view override returns (address instance) {
return _instanceSet.at(index);
}
function _register(address instance) internal {
require(_instanceSet.add(instance), "InstanceRegistry: already registered");
emit InstanceAdded(instance);
}
}
文件 15 的 21:Ownable.sol
pragma solidity ^0.7.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor () {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
function owner() public view virtual returns (address) {
return _owner;
}
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
文件 16 的 21:OwnableERC721.sol
pragma solidity 0.7.6;
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
contract OwnableERC721 {
address private _nftAddress;
modifier onlyOwner() {
require(owner() == msg.sender, "OwnableERC721: caller is not the owner");
_;
}
function _setNFT(address nftAddress) internal {
_nftAddress = nftAddress;
}
function nft() public view virtual returns (address nftAddress) {
return _nftAddress;
}
function owner() public view virtual returns (address ownerAddress) {
return IERC721(_nftAddress).ownerOf(uint256(address(this)));
}
}
文件 17 的 21:PowerSwitch.sol
pragma solidity 0.7.6;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
interface IPowerSwitch {
event PowerOn();
event PowerOff();
event EmergencyShutdown();
enum State {Online, Offline, Shutdown}
function powerOn() external;
function powerOff() external;
function emergencyShutdown() external;
function isOnline() external view returns (bool status);
function isOffline() external view returns (bool status);
function isShutdown() external view returns (bool status);
function getStatus() external view returns (State status);
function getPowerController() external view returns (address controller);
}
contract PowerSwitch is IPowerSwitch, Ownable {
IPowerSwitch.State private _status;
constructor(address owner) {
require(owner != address(0), "PowerSwitch: invalid owner");
Ownable.transferOwnership(owner);
}
function powerOn() external override onlyOwner {
require(_status == IPowerSwitch.State.Offline, "PowerSwitch: cannot power on");
_status = IPowerSwitch.State.Online;
emit PowerOn();
}
function powerOff() external override onlyOwner {
require(_status == IPowerSwitch.State.Online, "PowerSwitch: cannot power off");
_status = IPowerSwitch.State.Offline;
emit PowerOff();
}
function emergencyShutdown() external override onlyOwner {
require(_status != IPowerSwitch.State.Shutdown, "PowerSwitch: cannot shutdown");
_status = IPowerSwitch.State.Shutdown;
emit EmergencyShutdown();
}
function isOnline() external view override returns (bool status) {
return _status == State.Online;
}
function isOffline() external view override returns (bool status) {
return _status == State.Offline;
}
function isShutdown() external view override returns (bool status) {
return _status == State.Shutdown;
}
function getStatus() external view override returns (IPowerSwitch.State status) {
return _status;
}
function getPowerController() external view override returns (address controller) {
return Ownable.owner();
}
}
文件 18 的 21:Powered.sol
pragma solidity 0.7.6;
import {IPowerSwitch} from "./PowerSwitch.sol";
interface IPowered {
function isOnline() external view returns (bool status);
function isOffline() external view returns (bool status);
function isShutdown() external view returns (bool status);
function getPowerSwitch() external view returns (address powerSwitch);
function getPowerController() external view returns (address controller);
}
contract Powered is IPowered {
address private _powerSwitch;
modifier onlyOnline() {
_onlyOnline();
_;
}
modifier onlyOffline() {
_onlyOffline();
_;
}
modifier notShutdown() {
_notShutdown();
_;
}
modifier onlyShutdown() {
_onlyShutdown();
_;
}
function _setPowerSwitch(address powerSwitch) internal {
_powerSwitch = powerSwitch;
}
function isOnline() public view override returns (bool status) {
return IPowerSwitch(_powerSwitch).isOnline();
}
function isOffline() public view override returns (bool status) {
return IPowerSwitch(_powerSwitch).isOffline();
}
function isShutdown() public view override returns (bool status) {
return IPowerSwitch(_powerSwitch).isShutdown();
}
function getPowerSwitch() public view override returns (address powerSwitch) {
return _powerSwitch;
}
function getPowerController() public view override returns (address controller) {
return IPowerSwitch(_powerSwitch).getPowerController();
}
function _onlyOnline() private view {
require(isOnline(), "Powered: is not online");
}
function _onlyOffline() private view {
require(isOffline(), "Powered: is not offline");
}
function _notShutdown() private view {
require(!isShutdown(), "Powered: is shutdown");
}
function _onlyShutdown() private view {
require(isShutdown(), "Powered: is not shutdown");
}
}
文件 19 的 21:RewardPool.sol
pragma solidity 0.7.6;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {TransferHelper} from "@uniswap/lib/contracts/libraries/TransferHelper.sol";
import {Powered} from "./Powered.sol";
interface IRewardPool {
function sendERC20(
address token,
address to,
uint256 value
) external;
function rescueERC20(address[] calldata tokens, address recipient) external;
}
contract RewardPool is IRewardPool, Powered, Ownable {
constructor(address powerSwitch) {
Powered._setPowerSwitch(powerSwitch);
}
function sendERC20(
address token,
address to,
uint256 value
) external override onlyOwner onlyOnline {
TransferHelper.safeTransfer(token, to, value);
}
function rescueERC20(address[] calldata tokens, address recipient)
external
override
onlyShutdown
{
require(
msg.sender == Powered.getPowerController(),
"RewardPool: only controller can withdraw after shutdown"
);
require(recipient != address(0), "RewardPool: recipient not defined");
for (uint256 index = 0; index < tokens.length; index++) {
address token = tokens[index];
uint256 balance = IERC20(token).balanceOf(address(this));
TransferHelper.safeTransfer(token, recipient, balance);
}
}
}
文件 20 的 21:SafeMath.sol
pragma solidity ^0.7.0;
library SafeMath {
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b > a) return (false, 0);
return (true, a - b);
}
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a / b);
}
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a % b);
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) return 0;
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
return a / b;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: modulo by zero");
return a % b;
}
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
return a - b;
}
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a / b;
}
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a % b;
}
}
文件 21 的 21:TransferHelper.sol
pragma solidity >=0.6.0;
library TransferHelper {
function safeApprove(
address token,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
'TransferHelper::safeApprove: approve failed'
);
}
function safeTransfer(
address token,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
'TransferHelper::safeTransfer: transfer failed'
);
}
function safeTransferFrom(
address token,
address from,
address to,
uint256 value
) internal {
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
'TransferHelper::transferFrom: transferFrom failed'
);
}
function safeTransferETH(address to, uint256 value) internal {
(bool success, ) = to.call{value: value}(new bytes(0));
require(success, 'TransferHelper::safeTransferETH: ETH transfer failed');
}
}
{
"compilationTarget": {
"contracts/aludel/Aludel.sol": "Aludel"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 1000
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"ownerAddress","type":"address"},{"internalType":"address","name":"rewardPoolFactory","type":"address"},{"internalType":"address","name":"powerSwitchFactory","type":"address"},{"internalType":"address","name":"stakingToken","type":"address"},{"internalType":"address","name":"rewardToken","type":"address"},{"components":[{"internalType":"uint256","name":"floor","type":"uint256"},{"internalType":"uint256","name":"ceiling","type":"uint256"},{"internalType":"uint256","name":"time","type":"uint256"}],"internalType":"struct IAludel.RewardScaling","name":"rewardScaling","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"rewardPool","type":"address"},{"indexed":false,"internalType":"address","name":"powerSwitch","type":"address"}],"name":"AludelCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"duration","type":"uint256"}],"name":"AludelFunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"BonusTokenRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"vault","type":"address"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"vault","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Staked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"vault","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Unstaked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"factory","type":"address"}],"name":"VaultFactoryRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"factory","type":"address"}],"name":"VaultFactoryRemoved","type":"event"},{"inputs":[],"name":"BASE_SHARES_PER_WEI","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_REWARD_TOKENS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_STAKES_PER_VAULT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"unlockedRewards","type":"uint256"},{"internalType":"uint256","name":"stakeAmount","type":"uint256"},{"internalType":"uint256","name":"stakeDuration","type":"uint256"},{"internalType":"uint256","name":"totalStakeUnits","type":"uint256"},{"components":[{"internalType":"uint256","name":"floor","type":"uint256"},{"internalType":"uint256","name":"ceiling","type":"uint256"},{"internalType":"uint256","name":"time","type":"uint256"}],"internalType":"struct IAludel.RewardScaling","name":"rewardScaling","type":"tuple"}],"name":"calculateReward","outputs":[{"internalType":"uint256","name":"reward","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"internalType":"struct IAludel.StakeData[]","name":"stakes","type":"tuple[]"},{"internalType":"uint256","name":"unstakeAmount","type":"uint256"},{"internalType":"uint256","name":"unlockedRewards","type":"uint256"},{"internalType":"uint256","name":"totalStakeUnits","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"components":[{"internalType":"uint256","name":"floor","type":"uint256"},{"internalType":"uint256","name":"ceiling","type":"uint256"},{"internalType":"uint256","name":"time","type":"uint256"}],"internalType":"struct IAludel.RewardScaling","name":"rewardScaling","type":"tuple"}],"name":"calculateRewardFromStakes","outputs":[{"components":[{"internalType":"uint256","name":"lastStakeAmount","type":"uint256"},{"internalType":"uint256","name":"newStakesCount","type":"uint256"},{"internalType":"uint256","name":"reward","type":"uint256"},{"internalType":"uint256","name":"newTotalStakeUnits","type":"uint256"}],"internalType":"struct IAludel.RewardOutput","name":"out","type":"tuple"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"end","type":"uint256"}],"name":"calculateStakeUnits","outputs":[{"internalType":"uint256","name":"stakeUnits","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"internalType":"struct IAludel.StakeData[]","name":"stakes","type":"tuple[]"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"calculateTotalStakeUnits","outputs":[{"internalType":"uint256","name":"totalStakeUnits","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"shares","type":"uint256"}],"internalType":"struct IAludel.RewardSchedule[]","name":"rewardSchedules","type":"tuple[]"},{"internalType":"uint256","name":"rewardBalance","type":"uint256"},{"internalType":"uint256","name":"sharesOutstanding","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"calculateUnlockedRewards","outputs":[{"internalType":"uint256","name":"unlockedRewards","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"duration","type":"uint256"}],"name":"fund","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAludelData","outputs":[{"components":[{"internalType":"address","name":"stakingToken","type":"address"},{"internalType":"address","name":"rewardToken","type":"address"},{"internalType":"address","name":"rewardPool","type":"address"},{"components":[{"internalType":"uint256","name":"floor","type":"uint256"},{"internalType":"uint256","name":"ceiling","type":"uint256"},{"internalType":"uint256","name":"time","type":"uint256"}],"internalType":"struct IAludel.RewardScaling","name":"rewardScaling","type":"tuple"},{"internalType":"uint256","name":"rewardSharesOutstanding","type":"uint256"},{"internalType":"uint256","name":"totalStake","type":"uint256"},{"internalType":"uint256","name":"totalStakeUnits","type":"uint256"},{"internalType":"uint256","name":"lastUpdate","type":"uint256"},{"components":[{"internalType":"uint256","name":"duration","type":"uint256"},{"internalType":"uint256","name":"start","type":"uint256"},{"internalType":"uint256","name":"shares","type":"uint256"}],"internalType":"struct IAludel.RewardSchedule[]","name":"rewardSchedules","type":"tuple[]"}],"internalType":"struct IAludel.AludelData","name":"aludel","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getBonusTokenAtIndex","outputs":[{"internalType":"address","name":"bonusToken","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBonusTokenSetLength","outputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"uint256","name":"stakeAmount","type":"uint256"}],"name":"getCurrentStakeReward","outputs":[{"internalType":"uint256","name":"reward","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentTotalStakeUnits","outputs":[{"internalType":"uint256","name":"totalStakeUnits","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentUnlockedRewards","outputs":[{"internalType":"uint256","name":"unlockedRewards","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"}],"name":"getCurrentVaultReward","outputs":[{"internalType":"uint256","name":"reward","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"}],"name":"getCurrentVaultStakeUnits","outputs":[{"internalType":"uint256","name":"stakeUnits","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"uint256","name":"stakeAmount","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getFutureStakeReward","outputs":[{"internalType":"uint256","name":"reward","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getFutureTotalStakeUnits","outputs":[{"internalType":"uint256","name":"totalStakeUnits","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getFutureUnlockedRewards","outputs":[{"internalType":"uint256","name":"unlockedRewards","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getFutureVaultReward","outputs":[{"internalType":"uint256","name":"reward","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"getFutureVaultStakeUnits","outputs":[{"internalType":"uint256","name":"stakeUnits","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPowerController","outputs":[{"internalType":"address","name":"controller","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPowerSwitch","outputs":[{"internalType":"address","name":"powerSwitch","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"}],"name":"getVaultData","outputs":[{"components":[{"internalType":"uint256","name":"totalStake","type":"uint256"},{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"timestamp","type":"uint256"}],"internalType":"struct IAludel.StakeData[]","name":"stakes","type":"tuple[]"}],"internalType":"struct IAludel.VaultData","name":"vaultData","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getVaultFactoryAtIndex","outputs":[{"internalType":"address","name":"factory","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVaultFactorySetLength","outputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isOffline","outputs":[{"internalType":"bool","name":"status","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isOnline","outputs":[{"internalType":"bool","name":"status","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isShutdown","outputs":[{"internalType":"bool","name":"status","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"isValidAddress","outputs":[{"internalType":"bool","name":"validity","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"isValidVault","outputs":[{"internalType":"bool","name":"validity","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rageQuit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"bonusToken","type":"address"}],"name":"registerBonusToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"factory","type":"address"}],"name":"registerVaultFactory","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"factory","type":"address"}],"name":"removeVaultFactory","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"rescueTokensFromRewardPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"permission","type":"bytes"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"permission","type":"bytes"}],"name":"unstakeAndClaim","outputs":[],"stateMutability":"nonpayable","type":"function"}]