Skip to content

Commit

Permalink
complete project
Browse files Browse the repository at this point in the history
  • Loading branch information
namanmanchanda09 committed Oct 9, 2021
0 parents commit 3800252
Show file tree
Hide file tree
Showing 28 changed files with 84,007 additions and 0 deletions.
33 changes: 33 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*

node_modules
.env
coverage
coverage.json
typechain

#Hardhat files
cache
artifacts
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# DeFi Money Streamnig using Sablier

Sablier allows to stream money using smart contracts. Any ERC20 token can be used to stream. In this particular project, only one ERC20 token is supported and can be minted from the app itself.

Install the node modules using

```
npm install
```

Run a local blockchanin using

```
npx hardhat node
```

To deploy the contracts run

```
npx hardhat run --network localhost scripts/deploy.js
```

To start the react server run

```
npm start
```

<figure class="video_container">
<iframe src="https://www.youtube.com/watch?v=sYl4xG_7a4I" frameborder="0" allowfullscreen="true"> </iframe>
</figure>




86 changes: 86 additions & 0 deletions contracts/CarefulMath.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.17;

/**
* @title Careful Math
* @author Compound
* @notice Derived from OpenZeppelin's SafeMath library
* https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/math/SafeMath.sol
*/
contract CarefulMath {

/**
* @dev Possible error codes that we can return
*/
enum MathError {
NO_ERROR,
DIVISION_BY_ZERO,
INTEGER_OVERFLOW,
INTEGER_UNDERFLOW
}

/**
* @dev Multiplies two numbers, returns an error on overflow.
*/
function mulUInt(uint a, uint b) internal pure returns (MathError, uint) {
if (a == 0) {
return (MathError.NO_ERROR, 0);
}

uint c = a * b;

if (c / a != b) {
return (MathError.INTEGER_OVERFLOW, 0);
} else {
return (MathError.NO_ERROR, c);
}
}

/**
* @dev Integer division of two numbers, truncating the quotient.
*/
function divUInt(uint a, uint b) internal pure returns (MathError, uint) {
if (b == 0) {
return (MathError.DIVISION_BY_ZERO, 0);
}

return (MathError.NO_ERROR, a / b);
}

/**
* @dev Subtracts two numbers, returns an error on overflow (i.e. if subtrahend is greater than minuend).
*/
function subUInt(uint a, uint b) internal pure returns (MathError, uint) {
if (b <= a) {
return (MathError.NO_ERROR, a - b);
} else {
return (MathError.INTEGER_UNDERFLOW, 0);
}
}

/**
* @dev Adds two numbers, returns an error on overflow.
*/
function addUInt(uint a, uint b) internal pure returns (MathError, uint) {
uint c = a + b;

if (c >= a) {
return (MathError.NO_ERROR, c);
} else {
return (MathError.INTEGER_OVERFLOW, 0);
}
}

/**
* @dev add a and b and then subtract c
*/
function addThenSubUInt(uint a, uint b, uint c) internal pure returns (MathError, uint) {
(MathError err0, uint sum) = addUInt(a, b);

if (err0 != MathError.NO_ERROR) {
return (err0, 0);
}

return subUInt(sum, c);
}
}
234 changes: 234 additions & 0 deletions contracts/Sablier.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.5.17;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "./CarefulMath.sol";

import "./interfaces/ISablier.sol";
import "./Types.sol";

import "hardhat/console.sol";

contract Sablier is ISablier, ReentrancyGuard, CarefulMath {
using SafeERC20 for IERC20;

uint256 public nextStreamId;

mapping(uint256 => Types.Stream) private streams;
mapping(address => uint256[]) public streamMap;
mapping(address => uint256[]) public streamRecipient;

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() {
nextStreamId = 100000;
}

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 override view 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);
/* `withdrawalAmount` cannot and should not be bigger than `recipientBalance`. */
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);
/* `recipientBalance` cannot and should not be bigger than `remainingBalance`. */
assert(vars.mathErr == MathError.NO_ERROR);
return vars.senderBalance;
}
return 0;
}

/*** Public Effects & Interactions Functions ***/

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);
/* `subUInt` can only return MathError.INTEGER_UNDERFLOW but we know `stopTime` is higher than `startTime`. */
assert(vars.mathErr == MathError.NO_ERROR);

/* Without this, the rate per second would be zero. */
require(deposit >= vars.duration, "deposit smaller than time delta");

/* This condition avoids dealing with remainders */
require(deposit % vars.duration == 0, "deposit not multiple of time delta");

(vars.mathErr, vars.ratePerSecond) = divUInt(deposit, vars.duration);
/* `divUInt` can only return MathError.DIVISION_BY_ZERO but we know `duration` is not zero. */
assert(vars.mathErr == MathError.NO_ERROR);

/* Create and store the stream object. */
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
});

streamMap[msg.sender].push(streamId);
streamRecipient[recipient].push(streamId);
/* Increment the next stream id. */
(vars.mathErr, nextStreamId) = addUInt(nextStreamId, uint256(1));
require(vars.mathErr == MathError.NO_ERROR, "next stream id calculation error");

IERC20(tokenAddress).safeTransferFrom(msg.sender, address(this), deposit);
emit CreateStream(streamId, msg.sender, recipient, deposit, tokenAddress, startTime, stopTime);
return streamId;
}

function withdrawFromStream(uint256 streamId, uint256 amount)
external
nonReentrant
override
streamExists(streamId)
onlySenderOrRecipient(streamId)
returns (bool)
{
require(amount > 0, "amount is zero");
Types.Stream memory stream = streams[streamId];

uint256 balance = balanceOf(streamId, stream.recipient);
require(balance >= amount, "amount exceeds the available balance");

MathError mathErr;
(mathErr, streams[streamId].remainingBalance) = subUInt(stream.remainingBalance, amount);
/**
* `subUInt` can only return MathError.INTEGER_UNDERFLOW but we know that `remainingBalance` is at least
* as big as `amount`.
*/
assert(mathErr == MathError.NO_ERROR);

if (streams[streamId].remainingBalance == 0) delete streams[streamId];

IERC20(stream.tokenAddress).safeTransfer(stream.recipient, amount);
emit WithdrawFromStream(streamId, stream.recipient, amount);
return true;
}


function cancelStream(uint256 streamId)
external
nonReentrant
override
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) token.safeTransfer(stream.recipient, recipientBalance);
if (senderBalance > 0) token.safeTransfer(stream.sender, senderBalance);

emit CancelStream(streamId, stream.sender, stream.recipient, senderBalance, recipientBalance);
return true;
}

function getUserStreamList(address _currentUser)
external
view
returns(uint256[] memory)
{
return streamMap[_currentUser];
}

function getStreamRecipientList(address _currentUser)
external
view
returns(uint256[] memory)
{
return streamRecipient[_currentUser];
}
}
Loading

0 comments on commit 3800252

Please sign in to comment.