Last active
May 10, 2021 17:19
-
-
Save pi0neerpat/2d2c1e51a21ff5496c69397454c1eee5 to your computer and use it in GitHub Desktop.
Superfluid App Example: tradeable-cashflow
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
pragma solidity ^0.7.0; | |
import { | |
ISuperfluid, | |
ISuperToken, | |
ISuperApp, | |
ISuperAgreement, | |
SuperAppDefinitions | |
} from "https://github.com/superfluid-finance/protocol-monorepo/blob/remix-support/packages/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol"; | |
// When ready to move to leave Remix, change imports to follow this pattern: | |
// "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol"; | |
import { | |
IConstantFlowAgreementV1 | |
} from "https://github.com/superfluid-finance/protocol-monorepo/blob/remix-support/packages/ethereum-contracts/contracts/interfaces/agreements/IConstantFlowAgreementV1.sol"; | |
import { | |
SuperAppBase | |
} from "https://github.com/superfluid-finance/protocol-monorepo/blob/remix-support/packages/ethereum-contracts/contracts/apps/SuperAppBase.sol"; | |
contract RedirectAll is SuperAppBase { | |
ISuperfluid private _host; // host | |
IConstantFlowAgreementV1 private _cfa; // the stored constant flow agreement class address | |
ISuperToken private _acceptedToken; // accepted token | |
address private _receiver; | |
constructor( | |
ISuperfluid host, | |
IConstantFlowAgreementV1 cfa, | |
ISuperToken acceptedToken, | |
address receiver) { | |
assert(address(host) != address(0)); | |
assert(address(cfa) != address(0)); | |
assert(address(acceptedToken) != address(0)); | |
assert(address(receiver) != address(0)); | |
//assert(!_host.isApp(ISuperApp(receiver))); | |
_host = host; | |
_cfa = cfa; | |
_acceptedToken = acceptedToken; | |
_receiver = receiver; | |
uint256 configWord = | |
SuperAppDefinitions.APP_LEVEL_FINAL | | |
SuperAppDefinitions.BEFORE_AGREEMENT_CREATED_NOOP | | |
SuperAppDefinitions.BEFORE_AGREEMENT_UPDATED_NOOP | | |
SuperAppDefinitions.BEFORE_AGREEMENT_TERMINATED_NOOP; | |
_host.registerApp(configWord); | |
} | |
/************************************************************************** | |
* Redirect Logic | |
*************************************************************************/ | |
function currentReceiver() | |
external view | |
returns ( | |
uint256 startTime, | |
address receiver, | |
int96 flowRate | |
) | |
{ | |
if (_receiver != address(0)) { | |
(startTime, flowRate,,) = _cfa.getFlow(_acceptedToken, address(this), _receiver); | |
receiver = _receiver; | |
} | |
} | |
event ReceiverChanged(address receiver); //what is this? | |
/// @dev If a new stream is opened, or an existing one is opened | |
function _updateOutflow(bytes calldata ctx) | |
private | |
returns (bytes memory newCtx) | |
{ | |
newCtx = ctx; | |
// @dev This will give me the new flowRate, as it is called in after callbacks | |
int96 netFlowRate = _cfa.getNetFlow(_acceptedToken, address(this)); | |
(,int96 outFlowRate,,) = _cfa.getFlow(_acceptedToken, address(this), _receiver); | |
int96 inFlowRate = netFlowRate + outFlowRate; | |
if (inFlowRate < 0 ) inFlowRate = -inFlowRate; // Fixes issue when inFlowRate is negative | |
// @dev If inFlowRate === 0, then delete existing flow. | |
if (outFlowRate != int96(0)){ | |
(newCtx, ) = _host.callAgreementWithContext( | |
_cfa, | |
abi.encodeWithSelector( | |
_cfa.updateFlow.selector, | |
_acceptedToken, | |
_receiver, | |
inFlowRate, | |
new bytes(0) // placeholder | |
), | |
"0x", | |
newCtx | |
); | |
} else if (inFlowRate == int96(0)) { | |
// @dev if inFlowRate is zero, delete outflow. | |
(newCtx, ) = _host.callAgreementWithContext( | |
_cfa, | |
abi.encodeWithSelector( | |
_cfa.deleteFlow.selector, | |
_acceptedToken, | |
address(this), | |
_receiver, | |
new bytes(0) // placeholder | |
), | |
"0x", | |
newCtx | |
); | |
} else { | |
// @dev If there is no existing outflow, then create new flow to equal inflow | |
(newCtx, ) = _host.callAgreementWithContext( | |
_cfa, | |
abi.encodeWithSelector( | |
_cfa.createFlow.selector, | |
_acceptedToken, | |
_receiver, | |
inFlowRate, | |
new bytes(0) // placeholder | |
), | |
"0x", | |
newCtx | |
); | |
} | |
} | |
// @dev Change the Receiver of the total flow | |
function _changeReceiver( address newReceiver ) internal { | |
require(newReceiver != address(0), "New receiver is zero address"); | |
// @dev because our app is registered as final, we can't take downstream apps | |
require(!_host.isApp(ISuperApp(newReceiver)), "New receiver can not be a superApp"); | |
if (newReceiver == _receiver) return ; | |
// @dev delete flow to old receiver | |
_host.callAgreement( | |
_cfa, | |
abi.encodeWithSelector( | |
_cfa.deleteFlow.selector, | |
_acceptedToken, | |
address(this), | |
_receiver, | |
new bytes(0) | |
), | |
"0x" | |
); | |
// @dev create flow to new receiver | |
_host.callAgreement( | |
_cfa, | |
abi.encodeWithSelector( | |
_cfa.createFlow.selector, | |
_acceptedToken, | |
newReceiver, | |
_cfa.getNetFlow(_acceptedToken, address(this)), | |
new bytes(0) | |
), | |
"0x" | |
); | |
// @dev set global receiver to new receiver | |
_receiver = newReceiver; | |
emit ReceiverChanged(_receiver); | |
} | |
/************************************************************************** | |
* SuperApp callbacks | |
*************************************************************************/ | |
function afterAgreementCreated( | |
ISuperToken _superToken, | |
address _agreementClass, | |
bytes32, // _agreementId, | |
bytes calldata /*_agreementData*/, | |
bytes calldata ,// _cbdata, | |
bytes calldata _ctx | |
) | |
external override | |
onlyExpected(_superToken, _agreementClass) | |
onlyHost | |
returns (bytes memory newCtx) | |
{ | |
return _updateOutflow(_ctx); | |
} | |
function afterAgreementUpdated( | |
ISuperToken _superToken, | |
address _agreementClass, | |
bytes32 ,//_agreementId, | |
bytes calldata /*_agreementData*/, | |
bytes calldata ,//_cbdata, | |
bytes calldata _ctx | |
) | |
external override | |
onlyExpected(_superToken, _agreementClass) | |
onlyHost | |
returns (bytes memory newCtx) | |
{ | |
return _updateOutflow(_ctx); | |
} | |
function afterAgreementTerminated( | |
ISuperToken _superToken, | |
address _agreementClass, | |
bytes32 ,//_agreementId, | |
bytes calldata /*_agreementData*/, | |
bytes calldata ,//_cbdata, | |
bytes calldata _ctx | |
) | |
external override | |
onlyHost | |
returns (bytes memory newCtx) | |
{ | |
// According to the app basic law, we should never revert in a termination callback | |
if (!_isSameToken(_superToken) || !_isCFAv1(_agreementClass)) return _ctx; | |
return _updateOutflow(_ctx); | |
} | |
function _isSameToken(ISuperToken superToken) private view returns (bool) { | |
return address(superToken) == address(_acceptedToken); | |
} | |
function _isCFAv1(address agreementClass) private view returns (bool) { | |
return ISuperAgreement(agreementClass).agreementType() | |
== keccak256("org.superfluid-finance.agreements.ConstantFlowAgreement.v1"); | |
} | |
modifier onlyHost() { | |
require(msg.sender == address(_host), "RedirectAll: support only one host"); | |
_; | |
} | |
modifier onlyExpected(ISuperToken superToken, address agreementClass) { | |
require(_isSameToken(superToken), "RedirectAll: not accepted token"); | |
require(_isCFAv1(agreementClass), "RedirectAll: only CFAv1 supported"); | |
_; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//SPDX-License-Identifier: MIT | |
pragma solidity ^0.7.0; | |
import {RedirectAll, ISuperToken, IConstantFlowAgreementV1, ISuperfluid} from "./RedirectAll.sol"; | |
import {ERC721} from "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.2.0-solc-0.7/contracts/token/ERC721/ERC721.sol"; | |
/* Hello and welcome to your first Super App! | |
* In order to deploy this contract, you'll need a few things | |
* Get the deployed SF addresses here: https://docs.superfluid.finance/superfluid/resources/networks | |
* or using the js-sdk as shown here https://docs.superfluid.finance/superfluid/protocol-tutorials/setup-local-environment | |
*/ | |
contract TradeableCashflow is ERC721, RedirectAll { | |
constructor ( | |
address owner, | |
string memory _name, | |
string memory _symbol, | |
ISuperfluid host, | |
IConstantFlowAgreementV1 cfa, | |
ISuperToken acceptedToken | |
) | |
ERC721 ( _name, _symbol ) | |
RedirectAll ( | |
host, | |
cfa, | |
acceptedToken, | |
owner | |
) | |
{ | |
_mint(owner, 1); | |
} | |
//now I will insert a nice little hook in the _transfer, including the RedirectAll function I need | |
function _beforeTokenTransfer( | |
address /*from*/, | |
address to, | |
uint256 /*tokenId*/ | |
) internal override { | |
_changeReceiver(to); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment