文件 1 的 10:CarefulMath.sol
pragma solidity =0.7.6;
contract CarefulMath {
enum MathError {
NO_ERROR,
DIVISION_BY_ZERO,
INTEGER_OVERFLOW,
INTEGER_UNDERFLOW
}
function mulUInt(uint256 a, uint256 b)
internal
pure
returns (MathError, uint256)
{
if (a == 0) {
return (MathError.NO_ERROR, 0);
}
uint256 c = a * b;
if (c / a != b) {
return (MathError.INTEGER_OVERFLOW, 0);
} else {
return (MathError.NO_ERROR, c);
}
}
function divUInt(uint256 a, uint256 b)
internal
pure
returns (MathError, uint256)
{
if (b == 0) {
return (MathError.DIVISION_BY_ZERO, 0);
}
return (MathError.NO_ERROR, a / b);
}
function subUInt(uint256 a, uint256 b)
internal
pure
returns (MathError, uint256)
{
if (b <= a) {
return (MathError.NO_ERROR, a - b);
} else {
return (MathError.INTEGER_UNDERFLOW, 0);
}
}
function addUInt(uint256 a, uint256 b)
internal
pure
returns (MathError, uint256)
{
uint256 c = a + b;
if (c >= a) {
return (MathError.NO_ERROR, c);
} else {
return (MathError.INTEGER_OVERFLOW, 0);
}
}
function addThenSubUInt(
uint256 a,
uint256 b,
uint256 c
) internal pure returns (MathError, uint256) {
(MathError err0, uint256 sum) = addUInt(a, b);
if (err0 != MathError.NO_ERROR) {
return (err0, 0);
}
return subUInt(sum, c);
}
}
文件 2 的 10: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;
}
}
文件 3 的 10:Exponential.sol
pragma solidity =0.7.6;
import "./CarefulMath.sol";
contract Exponential is CarefulMath {
uint256 constant expScale = 1e18;
uint256 constant halfExpScale = expScale / 2;
uint256 constant mantissaOne = expScale;
struct Exp {
uint256 mantissa;
}
function getExp(uint256 num, uint256 denom)
internal
pure
returns (MathError, Exp memory)
{
(MathError err0, uint256 scaledNumerator) = mulUInt(num, expScale);
if (err0 != MathError.NO_ERROR) {
return (err0, Exp({ mantissa: 0 }));
}
(MathError err1, uint256 rational) = divUInt(scaledNumerator, denom);
if (err1 != MathError.NO_ERROR) {
return (err1, Exp({ mantissa: 0 }));
}
return (MathError.NO_ERROR, Exp({ mantissa: rational }));
}
function addExp(Exp memory a, Exp memory b)
internal
pure
returns (MathError, Exp memory)
{
(MathError error, uint256 result) = addUInt(a.mantissa, b.mantissa);
return (error, Exp({ mantissa: result }));
}
function subExp(Exp memory a, Exp memory b)
internal
pure
returns (MathError, Exp memory)
{
(MathError error, uint256 result) = subUInt(a.mantissa, b.mantissa);
return (error, Exp({ mantissa: result }));
}
function mulScalar(Exp memory a, uint256 scalar)
internal
pure
returns (MathError, Exp memory)
{
(MathError err0, uint256 scaledMantissa) = mulUInt(a.mantissa, scalar);
if (err0 != MathError.NO_ERROR) {
return (err0, Exp({ mantissa: 0 }));
}
return (MathError.NO_ERROR, Exp({ mantissa: scaledMantissa }));
}
function mulScalarTruncate(Exp memory a, uint256 scalar)
internal
pure
returns (MathError, uint256)
{
(MathError err, Exp memory product) = mulScalar(a, scalar);
if (err != MathError.NO_ERROR) {
return (err, 0);
}
return (MathError.NO_ERROR, truncate(product));
}
function mulScalarTruncateAddUInt(
Exp memory a,
uint256 scalar,
uint256 addend
) internal pure returns (MathError, uint256) {
(MathError err, Exp memory product) = mulScalar(a, scalar);
if (err != MathError.NO_ERROR) {
return (err, 0);
}
return addUInt(truncate(product), addend);
}
function divScalar(Exp memory a, uint256 scalar)
internal
pure
returns (MathError, Exp memory)
{
(MathError err0, uint256 descaledMantissa) =
divUInt(a.mantissa, scalar);
if (err0 != MathError.NO_ERROR) {
return (err0, Exp({ mantissa: 0 }));
}
return (MathError.NO_ERROR, Exp({ mantissa: descaledMantissa }));
}
function divScalarByExp(uint256 scalar, Exp memory divisor)
internal
pure
returns (MathError, Exp memory)
{
(MathError err0, uint256 numerator) = mulUInt(expScale, scalar);
if (err0 != MathError.NO_ERROR) {
return (err0, Exp({ mantissa: 0 }));
}
return getExp(numerator, divisor.mantissa);
}
function divScalarByExpTruncate(uint256 scalar, Exp memory divisor)
internal
pure
returns (MathError, uint256)
{
(MathError err, Exp memory fraction) = divScalarByExp(scalar, divisor);
if (err != MathError.NO_ERROR) {
return (err, 0);
}
return (MathError.NO_ERROR, truncate(fraction));
}
function mulExp(Exp memory a, Exp memory b)
internal
pure
returns (MathError, Exp memory)
{
(MathError err0, uint256 doubleScaledProduct) =
mulUInt(a.mantissa, b.mantissa);
if (err0 != MathError.NO_ERROR) {
return (err0, Exp({ mantissa: 0 }));
}
(MathError err1, uint256 doubleScaledProductWithHalfScale) =
addUInt(halfExpScale, doubleScaledProduct);
if (err1 != MathError.NO_ERROR) {
return (err1, Exp({ mantissa: 0 }));
}
(MathError err2, uint256 product) =
divUInt(doubleScaledProductWithHalfScale, expScale);
assert(err2 == MathError.NO_ERROR);
return (MathError.NO_ERROR, Exp({ mantissa: product }));
}
function mulExp(uint256 a, uint256 b)
internal
pure
returns (MathError, Exp memory)
{
return mulExp(Exp({ mantissa: a }), Exp({ mantissa: b }));
}
function mulExp3(
Exp memory a,
Exp memory b,
Exp memory c
) internal pure returns (MathError, Exp memory) {
(MathError err, Exp memory ab) = mulExp(a, b);
if (err != MathError.NO_ERROR) {
return (err, ab);
}
return mulExp(ab, c);
}
function divExp(Exp memory a, Exp memory b)
internal
pure
returns (MathError, Exp memory)
{
return getExp(a.mantissa, b.mantissa);
}
function truncate(Exp memory exp) internal pure returns (uint256) {
return exp.mantissa / expScale;
}
function lessThanExp(Exp memory left, Exp memory right)
internal
pure
returns (bool)
{
return left.mantissa < right.mantissa;
}
function lessThanOrEqualExp(Exp memory left, Exp memory right)
internal
pure
returns (bool)
{
return left.mantissa <= right.mantissa;
}
function greaterThanExp(Exp memory left, Exp memory right)
internal
pure
returns (bool)
{
return left.mantissa > right.mantissa;
}
function isZeroExp(Exp memory value) internal pure returns (bool) {
return value.mantissa == 0;
}
}
文件 4 的 10:IERC1620.sol
pragma solidity =0.7.6;
interface IERC1620 {
event CreateStream(
uint256 indexed streamId,
address indexed sender,
address indexed recipient,
uint256 deposit,
address tokenAddress,
uint256 startTime,
uint256 stopTime
);
event WithdrawFromStream(
uint256 indexed streamId,
address indexed recipient,
uint256 amount
);
event CancelStream(
uint256 indexed streamId,
address indexed sender,
address indexed recipient,
uint256 senderBalance,
uint256 recipientBalance
);
function balanceOf(uint256 streamId, address who)
external
view
returns (uint256 balance);
function getStream(uint256 streamId)
external
view
returns (
address sender,
address recipient,
uint256 deposit,
address token,
uint256 startTime,
uint256 stopTime,
uint256 remainingBalance,
uint256 ratePerSecond
);
function createStream(
address recipient,
uint256 deposit,
address tokenAddress,
uint256 startTime,
uint256 stopTime
) external returns (uint256 streamId);
function withdrawFromStream(uint256 streamId, uint256 funds)
external
returns (bool);
function cancelStream(uint256 streamId) external returns (bool);
}
文件 5 的 10: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
);
}
文件 6 的 10: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;
}
}
文件 7 的 10:Pausable.sol
pragma solidity >=0.6.0 <0.8.0;
import "./Context.sol";
abstract contract Pausable is Context {
event Paused(address account);
event Unpaused(address account);
bool private _paused;
constructor () internal {
_paused = false;
}
function paused() public view virtual returns (bool) {
return _paused;
}
modifier whenNotPaused() {
require(!paused(), "Pausable: paused");
_;
}
modifier whenPaused() {
require(paused(), "Pausable: not paused");
_;
}
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
文件 8 的 10:ReentrancyGuard.sol
pragma solidity >=0.6.0 <0.8.0;
abstract contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor () internal {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
}
文件 9 的 10:Sablier.sol
pragma solidity =0.7.6;
import "../openzeppelin/utils/Pausable.sol";
import "../openzeppelin/access/Ownable.sol";
import "../openzeppelin/token/ERC20/IERC20.sol";
import "../openzeppelin/utils/ReentrancyGuard.sol";
import "./compound/Exponential.sol";
import "./interfaces/IERC1620.sol";
import "./Types.sol";
contract Sablier is IERC1620, Exponential, ReentrancyGuard {
mapping(address => uint256) private earnings;
Exp public fee;
uint256 public nextStreamId;
mapping(uint256 => Types.Stream) private streams;
modifier onlySenderOrRecipient(uint256 streamId) {
require(
msg.sender == streams[streamId].sender ||
msg.sender == streams[streamId].recipient,
"caller is not the sender or the recipient of the stream"
);
_;
}
modifier streamExists(uint256 streamId) {
require(streams[streamId].isEntity, "stream does not exist");
_;
}
constructor() public {
nextStreamId = 1;
}
function isEntity(uint256 streamId) external view returns (bool) {
return streams[streamId].isEntity;
}
function getStream(uint256 streamId)
external
view
override
streamExists(streamId)
returns (
address sender,
address recipient,
uint256 deposit,
address tokenAddress,
uint256 startTime,
uint256 stopTime,
uint256 remainingBalance,
uint256 ratePerSecond
)
{
sender = streams[streamId].sender;
recipient = streams[streamId].recipient;
deposit = streams[streamId].deposit;
tokenAddress = streams[streamId].tokenAddress;
startTime = streams[streamId].startTime;
stopTime = streams[streamId].stopTime;
remainingBalance = streams[streamId].remainingBalance;
ratePerSecond = streams[streamId].ratePerSecond;
}
function deltaOf(uint256 streamId)
public
view
streamExists(streamId)
returns (uint256 delta)
{
Types.Stream memory stream = streams[streamId];
if (block.timestamp <= stream.startTime) return 0;
if (block.timestamp < stream.stopTime)
return block.timestamp - stream.startTime;
return stream.stopTime - stream.startTime;
}
struct BalanceOfLocalVars {
MathError mathErr;
uint256 recipientBalance;
uint256 withdrawalAmount;
uint256 senderBalance;
}
function balanceOf(uint256 streamId, address who)
public
view
override
streamExists(streamId)
returns (uint256 balance)
{
Types.Stream memory stream = streams[streamId];
BalanceOfLocalVars memory vars;
uint256 delta = deltaOf(streamId);
(vars.mathErr, vars.recipientBalance) = mulUInt(
delta,
stream.ratePerSecond
);
require(
vars.mathErr == MathError.NO_ERROR,
"recipient balance calculation error"
);
if (stream.deposit > stream.remainingBalance) {
(vars.mathErr, vars.withdrawalAmount) = subUInt(
stream.deposit,
stream.remainingBalance
);
assert(vars.mathErr == MathError.NO_ERROR);
(vars.mathErr, vars.recipientBalance) = subUInt(
vars.recipientBalance,
vars.withdrawalAmount
);
assert(vars.mathErr == MathError.NO_ERROR);
}
if (who == stream.recipient) return vars.recipientBalance;
if (who == stream.sender) {
(vars.mathErr, vars.senderBalance) = subUInt(
stream.remainingBalance,
vars.recipientBalance
);
assert(vars.mathErr == MathError.NO_ERROR);
return vars.senderBalance;
}
return 0;
}
struct CreateStreamLocalVars {
MathError mathErr;
uint256 duration;
uint256 ratePerSecond;
}
function createStream(
address recipient,
uint256 deposit,
address tokenAddress,
uint256 startTime,
uint256 stopTime
) public override returns (uint256) {
require(recipient != address(0x00), "stream to the zero address");
require(recipient != address(this), "stream to the contract itself");
require(recipient != msg.sender, "stream to the caller");
require(deposit > 0, "deposit is zero");
require(
startTime >= block.timestamp,
"start time before block.timestamp"
);
require(stopTime > startTime, "stop time before the start time");
CreateStreamLocalVars memory vars;
(vars.mathErr, vars.duration) = subUInt(stopTime, startTime);
assert(vars.mathErr == MathError.NO_ERROR);
require(deposit >= vars.duration, "deposit smaller than time delta");
require(
deposit % vars.duration == 0,
"deposit not multiple of time delta"
);
(vars.mathErr, vars.ratePerSecond) = divUInt(deposit, vars.duration);
assert(vars.mathErr == MathError.NO_ERROR);
uint256 streamId = nextStreamId;
streams[streamId] = Types.Stream({
remainingBalance: deposit,
deposit: deposit,
isEntity: true,
ratePerSecond: vars.ratePerSecond,
recipient: recipient,
sender: msg.sender,
startTime: startTime,
stopTime: stopTime,
tokenAddress: tokenAddress
});
(vars.mathErr, nextStreamId) = addUInt(nextStreamId, uint256(1));
require(
vars.mathErr == MathError.NO_ERROR,
"next stream id calculation error"
);
require(
IERC20(tokenAddress).transferFrom(
msg.sender,
address(this),
deposit
),
"token transfer failure"
);
emit CreateStream(
streamId,
msg.sender,
recipient,
deposit,
tokenAddress,
startTime,
stopTime
);
return streamId;
}
struct WithdrawFromStreamLocalVars {
MathError mathErr;
}
function withdrawFromStream(uint256 streamId, uint256 amount)
external
override
nonReentrant
streamExists(streamId)
onlySenderOrRecipient(streamId)
returns (bool)
{
require(amount > 0, "amount is zero");
Types.Stream memory stream = streams[streamId];
WithdrawFromStreamLocalVars memory vars;
uint256 balance = balanceOf(streamId, stream.recipient);
require(balance >= amount, "amount exceeds the available balance");
(vars.mathErr, streams[streamId].remainingBalance) = subUInt(
stream.remainingBalance,
amount
);
assert(vars.mathErr == MathError.NO_ERROR);
if (streams[streamId].remainingBalance == 0) delete streams[streamId];
require(
IERC20(stream.tokenAddress).transfer(stream.recipient, amount),
"token transfer failure"
);
emit WithdrawFromStream(streamId, stream.recipient, amount);
return true;
}
function cancelStream(uint256 streamId)
external
override
nonReentrant
streamExists(streamId)
onlySenderOrRecipient(streamId)
returns (bool)
{
Types.Stream memory stream = streams[streamId];
uint256 senderBalance = balanceOf(streamId, stream.sender);
uint256 recipientBalance = balanceOf(streamId, stream.recipient);
delete streams[streamId];
IERC20 token = IERC20(stream.tokenAddress);
if (recipientBalance > 0)
require(
token.transfer(stream.recipient, recipientBalance),
"recipient token transfer failure"
);
if (senderBalance > 0)
require(
token.transfer(stream.sender, senderBalance),
"sender token transfer failure"
);
emit CancelStream(
streamId,
stream.sender,
stream.recipient,
senderBalance,
recipientBalance
);
return true;
}
}
文件 10 的 10:Types.sol
pragma solidity =0.7.6;
library Types {
struct Stream {
uint256 deposit;
uint256 ratePerSecond;
uint256 remainingBalance;
uint256 startTime;
uint256 stopTime;
address recipient;
address sender;
address tokenAddress;
bool isEntity;
}
}
{
"compilationTarget": {
"contracts/sablierhq/Sablier.sol": "Sablier"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"streamId","type":"uint256"},{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"senderBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"recipientBalance","type":"uint256"}],"name":"CancelStream","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"streamId","type":"uint256"},{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"deposit","type":"uint256"},{"indexed":false,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"startTime","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"stopTime","type":"uint256"}],"name":"CreateStream","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"streamId","type":"uint256"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawFromStream","type":"event"},{"inputs":[{"internalType":"uint256","name":"streamId","type":"uint256"},{"internalType":"address","name":"who","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"streamId","type":"uint256"}],"name":"cancelStream","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deposit","type":"uint256"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"stopTime","type":"uint256"}],"name":"createStream","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"streamId","type":"uint256"}],"name":"deltaOf","outputs":[{"internalType":"uint256","name":"delta","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fee","outputs":[{"internalType":"uint256","name":"mantissa","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"streamId","type":"uint256"}],"name":"getStream","outputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"deposit","type":"uint256"},{"internalType":"address","name":"tokenAddress","type":"address"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"stopTime","type":"uint256"},{"internalType":"uint256","name":"remainingBalance","type":"uint256"},{"internalType":"uint256","name":"ratePerSecond","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"streamId","type":"uint256"}],"name":"isEntity","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextStreamId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"streamId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawFromStream","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]