//SPDX-License-Identifier: Unlicense pragma solidity ^0.8.4; import {ISuperfluid, ISuperAgreement, ISuperToken, ISuperApp, SuperAppDefinitions} from "@superfluid-finance/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol"; import {IConstantFlowAgreementV1} from "@superfluid-finance/ethereum-contracts/contracts/interfaces/agreements/IConstantFlowAgreementV1.sol"; import {SuperAppBase} from "@superfluid-finance/ethereum-contracts/contracts/apps/SuperAppBase.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; contract SuperApp is SuperAppBase { using Counters for Counters.Counter; Counters.Counter public _proposalIds; ISuperfluid private _host; // host IConstantFlowAgreementV1 private _cfa; // the stored constant flow agreement class address ISuperToken private _acceptedToken; // accepted token address private _receiver; struct Proposal { address sender; string message; string proposalUri; } constructor( ISuperfluid host, IConstantFlowAgreementV1 cfa, ISuperToken acceptedToken ) { require(address(host) != address(0), "host is zero address"); require(address(cfa) != address(0), "cfa is zero address"); require( address(acceptedToken) != address(0), "acceptedToken is zero address" ); _host = host; _cfa = cfa; _acceptedToken = acceptedToken; uint256 configWord = SuperAppDefinitions.APP_LEVEL_FINAL | SuperAppDefinitions.BEFORE_AGREEMENT_CREATED_NOOP | SuperAppDefinitions.BEFORE_AGREEMENT_UPDATED_NOOP | SuperAppDefinitions.BEFORE_AGREEMENT_TERMINATED_NOOP; _host.registerApp(configWord); } /************************************************************************** * 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) { (address sender, ) = abi.decode(_agreementData, (address, address)); (, int96 inFlowRate, , ) = _cfa.getFlow( _acceptedToken, sender, address(this) ); return _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) { (address sender, ) = abi.decode(_agreementData, (address, address)); (, int96 inFlowRate, , ) = _cfa.getFlow( _acceptedToken, sender, address(this) ); return _ctx; } function afterAgreementTerminated( ISuperToken, /*superToken*/ address, /*agreementClass*/ bytes32, // _agreementId, bytes calldata _agreementData, bytes calldata, /*cbdata*/ bytes calldata _ctx ) external virtual override returns (bytes memory newCtx) { (address sender, ) = abi.decode(_agreementData, (address, address)); return _ctx; } /************************************************************************** * SuperApp Helpers & modifiers *************************************************************************/ 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"); _; } }