文件 1 的 1:SmartToken.sol
pragma solidity ^0.4.24;
pragma experimental "v0.5.0";
pragma experimental ABIEncoderV2;
library AddressExtension {
function isValid(address _address) internal pure returns (bool) {
return 0 != _address;
}
function isAccount(address _address) internal view returns (bool result) {
assembly {
result := iszero(extcodesize(_address))
}
}
function toBytes(address _address) internal pure returns (bytes b) {
assembly {
let m := mload(0x40)
mstore(add(m, 20), xor(0x140000000000000000000000000000000000000000, _address))
mstore(0x40, add(m, 52))
b := m
}
}
}
library Math {
struct Fraction {
uint256 numerator;
uint256 denominator;
}
function isPositive(Fraction memory fraction) internal pure returns (bool) {
return fraction.numerator > 0 && fraction.denominator > 0;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256 r) {
r = a * b;
require((a == 0) || (r / a == b));
}
function div(uint256 a, uint256 b) internal pure returns (uint256 r) {
r = a / b;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256 r) {
require((r = a - b) <= a);
}
function add(uint256 a, uint256 b) internal pure returns (uint256 r) {
require((r = a + b) >= a);
}
function min(uint256 x, uint256 y) internal pure returns (uint256 r) {
return x <= y ? x : y;
}
function max(uint256 x, uint256 y) internal pure returns (uint256 r) {
return x >= y ? x : y;
}
function mulDiv(uint256 value, uint256 m, uint256 d) internal pure returns (uint256 r) {
r = value * m;
if (r / value == m) {
r /= d;
} else {
r = mul(value / d, m);
}
}
function mulDivCeil(uint256 value, uint256 m, uint256 d) internal pure returns (uint256 r) {
r = value * m;
if (r / value == m) {
if (r % d == 0) {
r /= d;
} else {
r = (r / d) + 1;
}
} else {
r = mul(value / d, m);
if (value % d != 0) {
r += 1;
}
}
}
function mul(uint256 x, Fraction memory f) internal pure returns (uint256) {
return mulDiv(x, f.numerator, f.denominator);
}
function mulCeil(uint256 x, Fraction memory f) internal pure returns (uint256) {
return mulDivCeil(x, f.numerator, f.denominator);
}
function div(uint256 x, Fraction memory f) internal pure returns (uint256) {
return mulDiv(x, f.denominator, f.numerator);
}
function divCeil(uint256 x, Fraction memory f) internal pure returns (uint256) {
return mulDivCeil(x, f.denominator, f.numerator);
}
function mul(Fraction memory x, Fraction memory y) internal pure returns (Math.Fraction) {
return Math.Fraction({
numerator: mul(x.numerator, y.numerator),
denominator: mul(x.denominator, y.denominator)
});
}
}
contract FsTKAuthority {
function isAuthorized(address sender, address _contract, bytes data) public view returns (bool);
function isApproved(bytes32 hash, uint256 approveTime, bytes approveToken) public view returns (bool);
function validate() public pure returns (bytes4);
}
contract Authorizable {
event SetFsTKAuthority(FsTKAuthority indexed _address);
modifier onlyFsTKAuthorized {
require(fstkAuthority.isAuthorized(msg.sender, this, msg.data));
_;
}
modifier onlyFsTKApproved(bytes32 hash, uint256 approveTime, bytes approveToken) {
require(fstkAuthority.isApproved(hash, approveTime, approveToken));
_;
}
FsTKAuthority internal fstkAuthority;
constructor(FsTKAuthority _fstkAuthority) internal {
fstkAuthority = _fstkAuthority;
}
function setFsTKAuthority(FsTKAuthority _fstkAuthority) public onlyFsTKAuthorized {
require(_fstkAuthority.validate() == _fstkAuthority.validate.selector);
emit SetFsTKAuthority(fstkAuthority = _fstkAuthority);
}
}
contract IssuerContract {
using AddressExtension for address;
event SetIssuer(address indexed _address);
modifier onlyIssuer {
require(issuer == msg.sender);
_;
}
address public issuer;
address public newIssuer;
constructor(address _issuer) internal {
issuer = _issuer;
}
function setIssuer(address _address) public onlyIssuer {
newIssuer = _address;
}
function confirmSetIssuer() public {
require(newIssuer == msg.sender);
emit SetIssuer(issuer = newIssuer);
delete newIssuer;
}
}
contract ERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function balanceOf(address owner) public view returns (uint256);
function allowance(address owner, address spender) public view returns (uint256);
function transfer(address to, uint256 value) public returns (bool);
function transferFrom(address from, address to, uint256 value) public returns (bool);
function approve(address spender, uint256 value) public returns (bool);
}
contract SecureERC20 is ERC20 {
event SetERC20ApproveChecking(bool approveChecking);
function approve(address spender, uint256 expectedValue, uint256 newValue) public returns (bool);
function increaseAllowance(address spender, uint256 value) public returns (bool);
function decreaseAllowance(address spender, uint256 value, bool strict) public returns (bool);
function setERC20ApproveChecking(bool approveChecking) public;
}
contract FsTKToken {
enum DelegateMode { PublicMsgSender, PublicTxOrigin, PrivateMsgSender, PrivateTxOrigin }
event Consume(address indexed from, uint256 value, bytes32 challenge);
event IncreaseNonce(address indexed from, uint256 nonce);
event SetupDirectDebit(address indexed debtor, address indexed receiver, DirectDebitInfo info);
event TerminateDirectDebit(address indexed debtor, address indexed receiver);
event WithdrawDirectDebitFailure(address indexed debtor, address indexed receiver);
event SetMetadata(string metadata);
event SetLiquid(bool liquidity);
event SetDelegate(bool isDelegateEnable);
event SetDirectDebit(bool isDirectDebitEnable);
struct DirectDebitInfo {
uint256 amount;
uint256 startTime;
uint256 interval;
}
struct DirectDebit {
DirectDebitInfo info;
uint256 epoch;
}
struct Instrument {
uint256 allowance;
DirectDebit directDebit;
}
struct Account {
uint256 balance;
uint256 nonce;
mapping (address => Instrument) instruments;
}
function spendableAllowance(address owner, address spender) public view returns (uint256);
function transfer(uint256[] data) public returns (bool);
function transferAndCall(address to, uint256 value, bytes data) public payable returns (bool);
function nonceOf(address owner) public view returns (uint256);
function increaseNonce() public returns (bool);
function delegateTransferAndCall(
uint256 nonce,
uint256 fee,
uint256 gasAmount,
address to,
uint256 value,
bytes data,
DelegateMode mode,
uint8 v,
bytes32 r,
bytes32 s
) public returns (bool);
function directDebit(address debtor, address receiver) public view returns (DirectDebit);
function setupDirectDebit(address receiver, DirectDebitInfo info) public returns (bool);
function terminateDirectDebit(address receiver) public returns (bool);
function withdrawDirectDebit(address debtor) public returns (bool);
function withdrawDirectDebit(address[] debtors, bool strict) public returns (bool);
}
contract ERC20Like is SecureERC20, FsTKToken {
using AddressExtension for address;
using Math for uint256;
modifier liquid {
require(isLiquid);
_;
}
modifier canUseDirectDebit {
require(isDirectDebitEnable);
_;
}
modifier canDelegate {
require(isDelegateEnable);
_;
}
bool public erc20ApproveChecking;
bool public isLiquid = true;
bool public isDelegateEnable;
bool public isDirectDebitEnable;
string public metadata;
mapping(address => Account) internal accounts;
constructor(string _metadata) public {
metadata = _metadata;
}
function balanceOf(address owner) public view returns (uint256) {
return accounts[owner].balance;
}
function allowance(address owner, address spender) public view returns (uint256) {
return accounts[owner].instruments[spender].allowance;
}
function transfer(address to, uint256 value) public liquid returns (bool) {
Account storage senderAccount = accounts[msg.sender];
senderAccount.balance = senderAccount.balance.sub(value);
accounts[to].balance += value;
emit Transfer(msg.sender, to, value);
return true;
}
function transferFrom(address from, address to, uint256 value) public liquid returns (bool) {
Account storage fromAccount = accounts[from];
Instrument storage senderInstrument = fromAccount.instruments[msg.sender];
fromAccount.balance = fromAccount.balance.sub(value);
senderInstrument.allowance = senderInstrument.allowance.sub(value);
accounts[to].balance += value;
emit Transfer(from, to, value);
return true;
}
function approve(address spender, uint256 value) public returns (bool) {
Instrument storage spenderInstrument = accounts[msg.sender].instruments[spender];
if (erc20ApproveChecking) {
require((value == 0) || (spenderInstrument.allowance == 0));
}
emit Approval(
msg.sender,
spender,
spenderInstrument.allowance = value
);
return true;
}
function setERC20ApproveChecking(bool approveChecking) public {
emit SetERC20ApproveChecking(erc20ApproveChecking = approveChecking);
}
function approve(address spender, uint256 expectedValue, uint256 newValue) public returns (bool) {
Instrument storage spenderInstrument = accounts[msg.sender].instruments[spender];
require(spenderInstrument.allowance == expectedValue);
emit Approval(
msg.sender,
spender,
spenderInstrument.allowance = newValue
);
return true;
}
function increaseAllowance(address spender, uint256 value) public returns (bool) {
Instrument storage spenderInstrument = accounts[msg.sender].instruments[spender];
emit Approval(
msg.sender,
spender,
spenderInstrument.allowance = spenderInstrument.allowance.add(value)
);
return true;
}
function decreaseAllowance(address spender, uint256 value, bool strict) public returns (bool) {
Instrument storage spenderInstrument = accounts[msg.sender].instruments[spender];
uint256 currentValue = spenderInstrument.allowance;
uint256 newValue;
if (strict) {
newValue = currentValue.sub(value);
} else if (value < currentValue) {
newValue = currentValue - value;
}
emit Approval(
msg.sender,
spender,
spenderInstrument.allowance = newValue
);
return true;
}
function setMetadata0(string _metadata) internal {
emit SetMetadata(metadata = _metadata);
}
function setLiquid0(bool liquidity) internal {
emit SetLiquid(isLiquid = liquidity);
}
function setDelegate(bool delegate) public {
emit SetDelegate(isDelegateEnable = delegate);
}
function setDirectDebit(bool directDebit) public {
emit SetDirectDebit(isDirectDebitEnable = directDebit);
}
function spendableAllowance(address owner, address spender) public view returns (uint256) {
Account storage ownerAccount = accounts[owner];
return Math.min(
ownerAccount.instruments[spender].allowance,
ownerAccount.balance
);
}
function transfer(uint256[] data) public liquid returns (bool) {
Account storage senderAccount = accounts[msg.sender];
uint256 totalValue;
for (uint256 i = 0; i < data.length; i++) {
address receiver = address(data[i] >> 96);
uint256 value = data[i] & 0xffffffffffffffffffffffff;
totalValue = totalValue.add(value);
accounts[receiver].balance += value;
emit Transfer(msg.sender, receiver, value);
}
senderAccount.balance = senderAccount.balance.sub(totalValue);
return true;
}
function transferAndCall(
address to,
uint256 value,
bytes data
)
public
payable
liquid
returns (bool)
{
require(
to != address(this) &&
data.length >= 68 &&
transfer(to, value)
);
assembly {
mstore(add(data, 36), value)
mstore(add(data, 68), caller)
}
require(to.call.value(msg.value)(data));
return true;
}
function nonceOf(address owner) public view returns (uint256) {
return accounts[owner].nonce;
}
function increaseNonce() public returns (bool) {
emit IncreaseNonce(msg.sender, accounts[msg.sender].nonce += 1);
}
function delegateTransferAndCall(
uint256 nonce,
uint256 fee,
uint256 gasAmount,
address to,
uint256 value,
bytes data,
DelegateMode mode,
uint8 v,
bytes32 r,
bytes32 s
)
public
liquid
canDelegate
returns (bool)
{
require(to != address(this));
address signer;
address relayer;
if (mode == DelegateMode.PublicMsgSender) {
signer = ecrecover(
keccak256(abi.encodePacked(this, nonce, fee, gasAmount, to, value, data, mode, address(0))),
v,
r,
s
);
relayer = msg.sender;
} else if (mode == DelegateMode.PublicTxOrigin) {
signer = ecrecover(
keccak256(abi.encodePacked(this, nonce, fee, gasAmount, to, value, data, mode, address(0))),
v,
r,
s
);
relayer = tx.origin;
} else if (mode == DelegateMode.PrivateMsgSender) {
signer = ecrecover(
keccak256(abi.encodePacked(this, nonce, fee, gasAmount, to, value, data, mode, msg.sender)),
v,
r,
s
);
relayer = msg.sender;
} else if (mode == DelegateMode.PrivateTxOrigin) {
signer = ecrecover(
keccak256(abi.encodePacked(this, nonce, fee, gasAmount, to, value, data, mode, tx.origin)),
v,
r,
s
);
relayer = tx.origin;
} else {
revert();
}
Account storage signerAccount = accounts[signer];
require(nonce == signerAccount.nonce);
emit IncreaseNonce(signer, signerAccount.nonce += 1);
signerAccount.balance = signerAccount.balance.sub(value.add(fee));
accounts[to].balance += value;
if (fee != 0) {
accounts[relayer].balance += fee;
emit Transfer(signer, relayer, fee);
}
if (!to.isAccount() && data.length >= 68) {
assembly {
mstore(add(data, 36), value)
mstore(add(data, 68), signer)
}
if (to.call.gas(gasAmount)(data)) {
emit Transfer(signer, to, value);
} else {
signerAccount.balance += value;
accounts[to].balance -= value;
}
} else {
emit Transfer(signer, to, value);
}
return true;
}
function directDebit(address debtor, address receiver) public view returns (DirectDebit) {
return accounts[debtor].instruments[receiver].directDebit;
}
function setupDirectDebit(
address receiver,
DirectDebitInfo info
)
public
returns (bool)
{
accounts[msg.sender].instruments[receiver].directDebit = DirectDebit({
info: info,
epoch: 0
});
emit SetupDirectDebit(msg.sender, receiver, info);
return true;
}
function terminateDirectDebit(address receiver) public returns (bool) {
delete accounts[msg.sender].instruments[receiver].directDebit;
emit TerminateDirectDebit(msg.sender, receiver);
return true;
}
function withdrawDirectDebit(address debtor) public liquid canUseDirectDebit returns (bool) {
Account storage debtorAccount = accounts[debtor];
DirectDebit storage debit = debtorAccount.instruments[msg.sender].directDebit;
uint256 epoch = (block.timestamp.sub(debit.info.startTime) / debit.info.interval).add(1);
uint256 amount = epoch.sub(debit.epoch).mul(debit.info.amount);
require(amount > 0);
debtorAccount.balance = debtorAccount.balance.sub(amount);
accounts[msg.sender].balance += amount;
debit.epoch = epoch;
emit Transfer(debtor, msg.sender, amount);
return true;
}
function withdrawDirectDebit(address[] debtors, bool strict) public liquid canUseDirectDebit returns (bool result) {
Account storage receiverAccount = accounts[msg.sender];
result = true;
uint256 total;
for (uint256 i = 0; i < debtors.length; i++) {
address debtor = debtors[i];
Account storage debtorAccount = accounts[debtor];
DirectDebit storage debit = debtorAccount.instruments[msg.sender].directDebit;
uint256 epoch = (block.timestamp.sub(debit.info.startTime) / debit.info.interval).add(1);
uint256 amount = epoch.sub(debit.epoch).mul(debit.info.amount);
require(amount > 0);
uint256 debtorBalance = debtorAccount.balance;
if (amount > debtorBalance) {
if (strict) {
revert();
}
result = false;
emit WithdrawDirectDebitFailure(debtor, msg.sender);
} else {
debtorAccount.balance = debtorBalance - amount;
total += amount;
debit.epoch = epoch;
emit Transfer(debtor, msg.sender, amount);
}
}
receiverAccount.balance += total;
}
}
contract SmartToken is Authorizable, IssuerContract, ERC20Like {
string public name;
string public symbol;
uint256 public totalSupply;
uint8 public constant decimals = 18;
constructor(
address _issuer,
FsTKAuthority _fstkAuthority,
string _name,
string _symbol,
uint256 _totalSupply,
string _metadata
)
Authorizable(_fstkAuthority)
IssuerContract(_issuer)
ERC20Like(_metadata)
public
{
name = _name;
symbol = _symbol;
totalSupply = _totalSupply;
accounts[_issuer].balance = _totalSupply;
emit Transfer(address(0), _issuer, _totalSupply);
}
function setERC20ApproveChecking(bool approveChecking) public onlyIssuer {
super.setERC20ApproveChecking(approveChecking);
}
function setDelegate(bool delegate) public onlyIssuer {
super.setDelegate(delegate);
}
function setDirectDebit(bool directDebit) public onlyIssuer {
super.setDirectDebit(directDebit);
}
function setMetadata(
string infoUrl,
uint256 approveTime,
bytes approveToken
)
public
onlyIssuer
onlyFsTKApproved(keccak256(abi.encodePacked(approveTime, this, msg.sig, infoUrl)), approveTime, approveToken)
{
setMetadata0(infoUrl);
}
function setLiquid(
bool liquidity,
uint256 approveTime,
bytes approveToken
)
public
onlyIssuer
onlyFsTKApproved(keccak256(abi.encodePacked(approveTime, this, msg.sig, liquidity)), approveTime, approveToken)
{
setLiquid0(liquidity);
}
}