Skip to content

Commit

Permalink
updates, bugfixes
Browse files Browse the repository at this point in the history
  • Loading branch information
markcarey committed Oct 31, 2024
1 parent 7f01eca commit 41138ff
Showing 1 changed file with 79 additions and 7 deletions.
86 changes: 79 additions & 7 deletions contracts/PoolStreamer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Compatible with OpenZeppelin Contracts ^5.0.0
pragma solidity ^0.8.15;

//import "hardhat/console.sol";

import "@openzeppelin/contracts/access/AccessControl.sol";

interface ISuperFluidPool {
Expand All @@ -12,7 +14,9 @@ interface ISuperFluidPool {

interface IERC20 {
function balanceOf(address account) external view returns (uint256);
function transfer(address recipient, uint256 amount) external returns (bool);
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
function approve(address spender, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
}

Expand All @@ -23,16 +27,17 @@ interface ISuperToken {
function getUnderlyingToken() external view returns (address tokenAddr);
}

interface IGeneralDistributionAgreementV1 {
function getFlowRate(address tokenAddress, address from, address to) external view returns (int96);
function distributeFlow(address superTokenAddress, address from, address poolAddress, int96 requestedFlowRate, bytes calldata ctx) external returns (bytes memory newCtx);
interface GDAv1Forwarder {
function getFlowDistributionFlowRate(address tokenAddress, address from, address to) external view returns (int96);
function distributeFlow(address superTokenAddress, address from, address poolAddress, int96 requestedFlowRate, bytes calldata userData) external returns (bool);
function distribute(address token, address from, address pool, uint256 requestedAmount, bytes calldata userData) external returns (bool);
}

contract PoolStreamer is AccessControl {
bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE");

ISuperFluidPool public pool;
IGeneralDistributionAgreementV1 public gda;
GDAv1Forwarder public gda;
address[] public sources;
int96 public streamPeriodSeconds;

Expand All @@ -41,19 +46,25 @@ contract PoolStreamer is AccessControl {

event StreamUpdated(int96 oldFlowRate, int96 newFlowRate);

constructor(IGeneralDistributionAgreementV1 _gda, ISuperFluidPool _pool, int96 _streamPeriodSeconds) {
constructor(GDAv1Forwarder _gda, ISuperFluidPool _pool, int96 _streamPeriodSeconds) {
gda = _gda;
pool = _pool;
streamPeriodSeconds = _streamPeriodSeconds;
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(MANAGER_ROLE, msg.sender);
_underlyingToken().approve(address(_superToken()), type(uint256).max);
}

function setPool(ISuperFluidPool _pool) external {
require(hasRole(MANAGER_ROLE, msg.sender), "PoolStreamer: must have manager role to set pool");
pool = _pool;
}

function setStreamPeriodSeconds(int96 _streamPeriodSeconds) external {
require(hasRole(MANAGER_ROLE, msg.sender), "PoolStreamer: must have manager role to set stream period seconds");
streamPeriodSeconds = _streamPeriodSeconds;
}

function registerSource(address source) external {
// require underlying balance > 0
require(_underlyingToken().balanceOf(source) > 0, "PoolStreamer: source balance is 0");
Expand Down Expand Up @@ -91,24 +102,34 @@ contract PoolStreamer is AccessControl {
// loop through each source:
for (uint256 i = 0; i < sources.length; i++) {
address source = sources[i];
//console.log("source: %s", source);
// check if source has a positive balance
if (_underlyingToken().balanceOf(source) > 0) {
//console.log("balance: %s", _underlyingToken().balanceOf(source));
// check if source has a positive allowance
if (_underlyingToken().allowance(source, address(this)) > 0) {
//console.log("allowance: %s", _underlyingToken().allowance(source, address(this)));
// amount is the lesser of balance and allowance
uint256 amount = _underlyingToken().balanceOf(source) < _underlyingToken().allowance(source, address(this)) ? _underlyingToken().balanceOf(source) : _underlyingToken().allowance(source, address(this));
//console.log("amount: %s", amount);
// transfer amount to this contract:
_underlyingToken().transferFrom(source, address(this), amount);
//console.log("source balance after transfer: %s", _underlyingToken().balanceOf(source));
}
}
}
//console.log("contract balance: %s", _underlyingToken().balanceOf(address(this)));
// require underlying balance > 0
require(_underlyingToken().balanceOf(address(this)) > 0, "PoolStreamer: contract balance is 0");
//console.log("before upgrade");
// upgrade the underlying balance to super token:
_superToken().upgrade(_underlyingToken().balanceOf(address(this)));
//console.log("after upgrade, superToken balance: %s", _superToken().balanceOf(address(this)));
// distribute the super token to the pool:
int96 newFlowRate = int96(int256(_superToken().balanceOf(address(this))) / streamPeriodSeconds);
emit StreamUpdated(gda.getFlowRate(address(_superToken()), address(this), address(pool)), newFlowRate);
//console.log("newFlowRate: %s", uint256(uint96(newFlowRate)));
emit StreamUpdated(gda.getFlowDistributionFlowRate(address(_superToken()), address(this), address(pool)), newFlowRate);
//console.log("before distributeFlow");
gda.distributeFlow(
address(_superToken()),
address(this),
Expand All @@ -118,6 +139,53 @@ contract PoolStreamer is AccessControl {
);
}

function stopFlow() external {
// require MANAGER_ROLE
require(hasRole(MANAGER_ROLE, msg.sender), "PoolStreamer: must have manager role to stop flow");
_stopFlow();
}
function _stopFlow() internal {
// stop the flow to the pool:
gda.distributeFlow(
address(_superToken()),
address(this),
address(pool),
0,
""
);
}

// stops the flow and distributes the remaining super token balance to the pool
function stopFlowAndDistribute() external {
// require MANAGER_ROLE
require(hasRole(MANAGER_ROLE, msg.sender), "PoolStreamer: must have manager role to stop flow and distribute");
// stop the flow
_stopFlow();
// distribute the super token balance to the pool
gda.distribute(
address(_superToken()),
address(this),
address(pool),
_superToken().balanceOf(address(this)),
""
);
}

// withdraw function: TODO: remove before production?
function withdrawAll() external {
// require MANAGER_ROLE
require(hasRole(MANAGER_ROLE, msg.sender), "PoolStreamer: must have manager role to withdraw");
// stop the flow first because we are withdrawing all
_stopFlow();
uint256 amount = _superToken().balanceOf(address(this));
// require amount > 0
require(amount > 0, "PoolStreamer: amount is 0");
// downgrade amount from super token to underlying token
_superToken().downgrade(amount);
// transfer amount to msg.sender
_underlyingToken().transfer(msg.sender, amount);
}

function superToken() external view returns (address) {
return address(_superToken());
}
Expand All @@ -132,8 +200,12 @@ contract PoolStreamer is AccessControl {
return IERC20(_superToken().getUnderlyingToken());
}

function superTokenBalance() external view returns (uint256) {
return _superToken().balanceOf(address(this));
}

function flowRate() external view returns (int96) {
return gda.getFlowRate(address(_superToken()), address(this), address(pool));
return gda.getFlowDistributionFlowRate(address(_superToken()), address(this), address(pool));
}
function sourceCount() external view returns (uint256) {
return sources.length;
Expand Down

0 comments on commit 41138ff

Please sign in to comment.