编译器
0.8.11+commit.d7f03943
文件 1 的 10:Context.sol
pragma solidity ^0.8.0;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 2 的 10:Fanspay.sol
pragma solidity ^0.8.11;
import "./FanspaySubscription.sol";
import "./FanspayDeferredCharge.sol";
contract Fanspay is FanspaySubscription, FanspayDeferredCharge {
event TopUp(uint amount, address from, bytes payload);
fallback() external payable {
(bool sent,) = payable(owner()).call{value : msg.value}("");
require(sent, "Failed to send Ether");
emit TopUp(msg.value, msg.sender, msg.data);
}
receive() external payable {
(bool sent,) = payable(owner()).call{value : msg.value}("");
require(sent, "Failed to send Ether");
}
}
文件 3 的 10:FanspayCharge.sol
pragma solidity ^0.8.11;
import "./FanspayFee.sol";
contract FanspayCharge is FanspayFee {
event ChargeEvent(uint amount, uint fee, string payload);
function charge(string memory _payload, Share[] memory _shareList, address _tokenAddress) public {
(uint fanspayFee, uint amount) = _getShareListSums(_shareList);
_checkFanspayFee(fanspayFee, amount);
_checkAllowance(_msgSender(), amount, _tokenAddress);
_checkBalance(_msgSender(), amount, _tokenAddress);
_transferToken(_msgSender(), _shareList, _tokenAddress);
emit ChargeEvent(amount, fanspayFee, _payload);
}
function _fanspayFeeIsCorrect(uint _fanspayFee, uint _amount) internal view returns (bool) {
uint fanspayFee = _amount * fee() / 1000;
if (fanspayFee <= 0) {
fanspayFee = 1;
}
return fanspayFee <= _fanspayFee;
}
function _checkFanspayFee(uint _fanspayFee, uint _amount) internal view {
require(
_fanspayFeeIsCorrect(_fanspayFee, _amount),
"Fee is not correct"
);
}
}
文件 4 的 10:FanspayDeferredCharge.sol
pragma solidity ^0.8.11;
import "./FanspayCharge.sol";
contract FanspayDeferredCharge is FanspayCharge {
struct DeferredCharge {
uint chargeTime;
address payer;
Share[] shareList;
string status;
address tokenAddress;
}
mapping(string => DeferredCharge) public deferredCharges;
event DeferredChargeEvent(string id);
function deferCharge(uint _deferredFor, string memory _id, Share[] memory _shareList, address _tokenAddress) public {
_checkAvailableDeferredCharge(_id);
DeferredCharge storage charge = deferredCharges[_id];
(uint fanspayFee, uint amount) = _getShareListSums(_shareList);
_checkFanspayFee(fanspayFee, amount);
_checkAllowance(_msgSender(), amount, _tokenAddress);
charge.chargeTime = block.timestamp + _deferredFor;
charge.payer = _msgSender();
charge.status = "waiting";
charge.tokenAddress = _tokenAddress;
for (uint i = 0; i < _shareList.length; i++) {
charge.shareList.push(_shareList[i]);
}
emit DeferredChargeEvent(_id);
}
function cancelDeferredCharge(string memory _id) public {
_checkDeferredChargeStatus(_id, "waiting");
deferredCharges[_id].status = "canceled";
emit DeferredChargeEvent(_id);
}
function getDeferredCharge(string memory _id) public view returns (DeferredCharge memory) {
_checkDeferredCharge(_id);
return deferredCharges[_id];
}
function chargeDeferredCharge(string memory _id) public {
_checkDeferredChargeStatus(_id, "waiting");
_checkDeferredChargeTime(_id);
DeferredCharge storage charge = deferredCharges[_id];
(uint fanspayFee, uint amount) = _getShareListSums(charge.shareList);
if (_isEnoughAllowanceAndBalance(charge.payer, amount, charge.tokenAddress)) {
_transferToken(charge.payer, charge.shareList, charge.tokenAddress);
charge.status = "paid";
emit DeferredChargeEvent(_id);
emit ChargeEvent(amount, fanspayFee, _id);
return;
}
charge.status = "failed";
emit DeferredChargeEvent(_id);
}
function chargeDeferredCharges(string[] memory _ids) public {
for (uint i = 0; i < _ids.length; i++) {
chargeDeferredCharge(_ids[i]);
}
}
function _checkAvailableDeferredCharge(string memory _id) view internal {
require(
deferredCharges[_id].chargeTime == 0,
"Deferred charge already exist"
);
}
function _checkDeferredCharge(string memory _id) view internal {
require(
deferredCharges[_id].chargeTime != 0,
"Deferred charge does not exist"
);
}
function _checkDeferredChargeStatus(string memory _id, string memory _status) view internal {
_checkDeferredCharge(_id);
require(
keccak256(abi.encodePacked(deferredCharges[_id].status)) == keccak256(abi.encodePacked(_status)),
string(abi.encodePacked("Deferred charge status is not ", _status))
);
}
function _checkDeferredChargeTime(string memory _id) view internal {
_checkDeferredCharge(_id);
require(
deferredCharges[_id].chargeTime <= block.timestamp,
"Deferred charge time is not reached"
);
}
}
文件 5 的 10:FanspayFee.sol
pragma solidity ^0.8.11;
import "./FanspayTokens.sol";
contract FanspayFee is FanspayTokens {
uint private _fee;
event FeeChangedEvent(uint previousFee, uint newFee);
constructor() {
_changeFee(10);
}
function fee() public view virtual returns (uint) {
return _fee;
}
function changeFee(uint _amount) public onlyOwner {
_changeFee(_amount);
}
function _changeFee(uint newFee) internal virtual {
uint oldFee = _fee;
_fee = newFee;
emit FeeChangedEvent(oldFee, newFee);
}
}
文件 6 的 10:FanspayManager.sol
pragma solidity ^0.8.11;
import "@openzeppelin/contracts/access/Ownable.sol";
contract FanspayManager is Ownable {
address private _manager;
event ManagerChangedEvent(address previousManager, address newManager);
constructor() {
_changeManager(_msgSender());
}
function getManager() public view virtual returns (address) {
return _manager;
}
function changeManager(address manager) public onlyOwner {
_changeManager(manager);
}
function _changeManager(address newManager) internal virtual {
address oldManager = _manager;
_manager = newManager;
emit ManagerChangedEvent(oldManager, newManager);
}
function _isManager(address manager) internal view returns (bool) {
return _manager == manager;
}
}
文件 7 的 10:FanspayShare.sol
pragma solidity ^0.8.11;
import "@openzeppelin/contracts/access/Ownable.sol";
contract FanspayShare is Ownable {
struct Share {
uint amount;
address receiver;
}
constructor() {}
function _getShareListSums(Share[] memory _shareList) internal view returns (uint, uint) {
uint fanspayFee;
uint sum;
for (uint i = 0; i < _shareList.length; i++) {
sum += _shareList[i].amount;
if (_shareList[i].receiver == owner()) {
fanspayFee += _shareList[i].amount;
}
}
return (fanspayFee, sum);
}
}
文件 8 的 10:FanspaySubscription.sol
pragma solidity ^0.8.11;
import "./FanspayCharge.sol";
import "./FanspayManager.sol";
contract FanspaySubscription is FanspayCharge, FanspayManager {
struct Subscription {
uint chargeTime;
uint interval;
address payer;
Share[] shareList;
string status;
address tokenAddress;
}
mapping(string => Subscription) public subscriptions;
event SubscriptionEvent(string id);
function subscribe(uint _deferredFor, uint _interval, string memory _id, Share[] memory _shareList, address _tokenAddress) public {
_checkAvailableSubscription(_id);
Subscription storage subscription = subscriptions[_id];
(uint fanspayFee, uint amount) = _getShareListSums(_shareList);
_checkFanspayFee(fanspayFee, amount);
_checkAllowance(_msgSender(), amount, _tokenAddress);
if (_deferredFor == 0) {
_checkBalance(_msgSender(), amount, _tokenAddress);
_transferToken(_msgSender(), _shareList, _tokenAddress);
subscription.chargeTime = block.timestamp + _interval;
subscription.status = "active";
emit ChargeEvent(amount, fanspayFee, _id);
} else {
subscription.chargeTime = block.timestamp + _deferredFor;
subscription.status = "waiting";
}
subscription.interval = _interval;
subscription.payer = _msgSender();
subscription.tokenAddress = _tokenAddress;
for (uint i = 0; i < _shareList.length; i++) {
subscription.shareList.push(_shareList[i]);
}
emit SubscriptionEvent(_id);
}
function cancelSubscription(string memory _id) public {
_checkSubscriptionLive(_id);
_checkSubscriptionPayer(_id, _msgSender());
subscriptions[_id].status = "canceled";
emit SubscriptionEvent(_id);
}
function cancelSubscriptions(string[] memory _ids) public {
for (uint i = 0; i < _ids.length; i++) {
if (_isSubscriptionLive(_ids[i]) && (_isSubscriptionPayer(_ids[i], _msgSender()) || _isManager(_msgSender()))) {
subscriptions[_ids[i]].status = "canceled";
emit SubscriptionEvent(_ids[i]);
}
}
}
function getSubscription(string memory _id) public view returns (Subscription memory) {
_checkSubscription(_id);
return subscriptions[_id];
}
function chargeSubscription(string memory _id) public {
_checkSubscriptionLive(_id);
_checkSubscriptionTime(_id);
_chargeSubscription(_id);
}
function chargeSubscriptions(string[] memory _ids) public {
for (uint i = 0; i < _ids.length; i++) {
if (_isSubscriptionLive(_ids[i]) && _isSubscriptionChargeTime(_ids[i])) {
_chargeSubscription(_ids[i]);
}
}
}
function _chargeSubscription(string memory _id) internal {
Subscription storage subscription = subscriptions[_id];
(uint fanspayFee, uint amount) = _getShareListSums(subscription.shareList);
if (_isEnoughAllowanceAndBalance(subscription.payer, amount, subscription.tokenAddress)) {
_transferToken(subscription.payer, subscription.shareList, subscription.tokenAddress);
subscription.chargeTime += subscription.interval;
if (_isSubscriptionStatus(_id, "waiting")) {
subscription.status = "active";
}
emit ChargeEvent(amount, fanspayFee, _id);
} else {
subscription.status = "failed";
}
emit SubscriptionEvent(_id);
}
function _checkSubscription(string memory _id) internal view {
require(
subscriptions[_id].chargeTime != 0,
"Subscription not exist"
);
}
function _checkSubscriptionPayer(string memory _id, address payer) internal view {
_checkSubscription(_id);
require(
_isSubscriptionPayer(_id, payer),
"Subscription payer is not correct"
);
}
function _isSubscriptionPayer(string memory _id, address payer) internal view returns (bool) {
return subscriptions[_id].payer == payer;
}
function _checkAvailableSubscription(string memory _id) view internal {
require(
subscriptions[_id].chargeTime == 0,
"Subscription already exists"
);
}
function _checkSubscriptionLive(string memory _id) internal view {
_checkSubscription(_id);
require(
_isSubscriptionLive(_id),
"Subscription status is not live"
);
}
function _isSubscriptionLive(string memory _id) internal view returns (bool) {
return _isSubscriptionStatus(_id, "active") || _isSubscriptionStatus(_id, "waiting");
}
function _isSubscriptionStatus(string memory _id, string memory _status) internal view returns (bool) {
return keccak256(abi.encodePacked(subscriptions[_id].status)) == keccak256(abi.encodePacked(_status));
}
function _checkSubscriptionTime(string memory _id) internal view {
require(
_isSubscriptionChargeTime(_id),
"Subscription charge time is not yet"
);
}
function _isSubscriptionChargeTime(string memory _id) internal view returns (bool) {
return subscriptions[_id].chargeTime <= block.timestamp;
}
}
文件 9 的 10:FanspayTokens.sol
pragma solidity ^0.8.11;
import "./FanspayShare.sol";
interface TokenInterface {
function allowance(address _owner, address _spender) external view returns (uint);
function balanceOf(address _spender) external view returns (uint);
function transferFrom(address _from, address _to, uint256 _value) external;
}
contract FanspayTokens is FanspayShare {
constructor() {}
function _isEnoughAllowanceAndBalance(address _address, uint _amount, address _tokenAddress) internal view returns (bool) {
return _isEnoughAllowance(_address, _amount, _tokenAddress) && _isEnoughBalance(_address, _amount, _tokenAddress);
}
function _checkAllowance(address _address, uint _amount, address _tokenAddress) internal view {
require(
_isEnoughAllowance(_address, _amount, _tokenAddress),
"Allowance not enough"
);
}
function _isEnoughAllowance(address _address, uint _amount, address _tokenAddress) internal view returns (bool) {
TokenInterface token = TokenInterface(_tokenAddress);
uint allowance = token.allowance(_address, address(this));
return _amount <= allowance;
}
function _checkBalance(address _address, uint _amount, address _tokenAddress) internal view {
require(
_isEnoughBalance(_address, _amount, _tokenAddress),
"Balance not enough"
);
}
function _isEnoughBalance(address _address, uint _amount, address _tokenAddress) internal view returns (bool) {
TokenInterface token = TokenInterface(_tokenAddress);
uint balance = token.balanceOf(_address);
return _amount <= balance;
}
function _transferToken(address _from, Share[] memory _shareList, address _tokenAddress) internal {
TokenInterface token = TokenInterface(_tokenAddress);
for (uint i = 0; i < _shareList.length; i++) {
token.transferFrom(_from, _shareList[i].receiver, _shareList[i].amount);
}
}
}
文件 10 的 10:Ownable.sol
pragma solidity ^0.8.0;
import "../utils/Context.sol";
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
constructor() {
_transferOwnership(_msgSender());
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
return _owner;
}
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
{
"compilationTarget": {
"Fanspay.sol": "Fanspay"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"},{"indexed":false,"internalType":"string","name":"payload","type":"string"}],"name":"ChargeEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"id","type":"string"}],"name":"DeferredChargeEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"previousFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newFee","type":"uint256"}],"name":"FeeChangedEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousManager","type":"address"},{"indexed":false,"internalType":"address","name":"newManager","type":"address"}],"name":"ManagerChangedEvent","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":"string","name":"id","type":"string"}],"name":"SubscriptionEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"bytes","name":"payload","type":"bytes"}],"name":"TopUp","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[{"internalType":"string","name":"_id","type":"string"}],"name":"cancelDeferredCharge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_id","type":"string"}],"name":"cancelSubscription","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string[]","name":"_ids","type":"string[]"}],"name":"cancelSubscriptions","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"changeFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"manager","type":"address"}],"name":"changeManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_payload","type":"string"},{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"internalType":"struct FanspayShare.Share[]","name":"_shareList","type":"tuple[]"},{"internalType":"address","name":"_tokenAddress","type":"address"}],"name":"charge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_id","type":"string"}],"name":"chargeDeferredCharge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string[]","name":"_ids","type":"string[]"}],"name":"chargeDeferredCharges","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_id","type":"string"}],"name":"chargeSubscription","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string[]","name":"_ids","type":"string[]"}],"name":"chargeSubscriptions","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_deferredFor","type":"uint256"},{"internalType":"string","name":"_id","type":"string"},{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"internalType":"struct FanspayShare.Share[]","name":"_shareList","type":"tuple[]"},{"internalType":"address","name":"_tokenAddress","type":"address"}],"name":"deferCharge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"deferredCharges","outputs":[{"internalType":"uint256","name":"chargeTime","type":"uint256"},{"internalType":"address","name":"payer","type":"address"},{"internalType":"string","name":"status","type":"string"},{"internalType":"address","name":"tokenAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_id","type":"string"}],"name":"getDeferredCharge","outputs":[{"components":[{"internalType":"uint256","name":"chargeTime","type":"uint256"},{"internalType":"address","name":"payer","type":"address"},{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"internalType":"struct FanspayShare.Share[]","name":"shareList","type":"tuple[]"},{"internalType":"string","name":"status","type":"string"},{"internalType":"address","name":"tokenAddress","type":"address"}],"internalType":"struct FanspayDeferredCharge.DeferredCharge","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"_id","type":"string"}],"name":"getSubscription","outputs":[{"components":[{"internalType":"uint256","name":"chargeTime","type":"uint256"},{"internalType":"uint256","name":"interval","type":"uint256"},{"internalType":"address","name":"payer","type":"address"},{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"internalType":"struct FanspayShare.Share[]","name":"shareList","type":"tuple[]"},{"internalType":"string","name":"status","type":"string"},{"internalType":"address","name":"tokenAddress","type":"address"}],"internalType":"struct FanspaySubscription.Subscription","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_deferredFor","type":"uint256"},{"internalType":"uint256","name":"_interval","type":"uint256"},{"internalType":"string","name":"_id","type":"string"},{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"internalType":"struct FanspayShare.Share[]","name":"_shareList","type":"tuple[]"},{"internalType":"address","name":"_tokenAddress","type":"address"}],"name":"subscribe","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"","type":"string"}],"name":"subscriptions","outputs":[{"internalType":"uint256","name":"chargeTime","type":"uint256"},{"internalType":"uint256","name":"interval","type":"uint256"},{"internalType":"address","name":"payer","type":"address"},{"internalType":"string","name":"status","type":"string"},{"internalType":"address","name":"tokenAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]