Last active
September 15, 2021 13:11
-
-
Save hellwolf/de102ea2cbd49ae30a3ec9795e8bd52f to your computer and use it in GitHub Desktop.
SimpleStreamSwap Super App
This file contains 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.8.7; | |
// STEP 1: Import the right interfaces | |
import { | |
ISuperfluid, | |
ISuperToken, | |
ISuperApp, | |
ISuperAgreement, | |
SuperAppDefinitions | |
} from "https://github.com/superfluid-finance/protocol-monorepo/blob/dev/packages/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol"; | |
import { | |
IConstantFlowAgreementV1 | |
} from "https://github.com/superfluid-finance/protocol-monorepo/blob/dev/packages/ethereum-contracts/contracts/interfaces/agreements/IConstantFlowAgreementV1.sol"; | |
import { | |
SuperAppBase | |
} from "https://github.com/superfluid-finance/protocol-monorepo/blob/dev/packages/ethereum-contracts/contracts/apps/SuperAppBase.sol"; | |
// STEP 2: Create a Super App contract base using SuperAppBase (constants, properties and constructor) | |
/** | |
* @dev A simple stream swap demo exchange your token1 for token2 and vice versa | |
*/ | |
contract SimpleStreamSwap is SuperAppBase { | |
bytes32 private constant _AGREEMENT_TYPE_CFAv1 = keccak256("org.superfluid-finance.agreements.ConstantFlowAgreement.v1"); | |
ISuperfluid private host; | |
IConstantFlowAgreementV1 private cfa; | |
ISuperToken private superToken1; | |
ISuperToken private superToken2; | |
event ExchangeHappened(ISuperToken inputToken, ISuperToken outputToken, int96 flowRate); | |
constructor( | |
ISuperfluid _host, | |
ISuperToken _superToken1, | |
ISuperToken _superToken2 | |
) { | |
require(address(_host) != address(0), "host is nil"); | |
require(address(_superToken1) != address(0), "superToken1 is nil"); | |
require(address(_superToken2) != address(0), "superToken2 is nil"); | |
host = _host; | |
cfa = IConstantFlowAgreementV1(address(host.getAgreementClass(_AGREEMENT_TYPE_CFAv1))); | |
superToken1 = _superToken1; | |
superToken2 = _superToken2; | |
uint256 configWord = | |
SuperAppDefinitions.APP_LEVEL_FINAL | | |
SuperAppDefinitions.BEFORE_AGREEMENT_CREATED_NOOP | | |
SuperAppDefinitions.BEFORE_AGREEMENT_UPDATED_NOOP | | |
SuperAppDefinitions.BEFORE_AGREEMENT_TERMINATED_NOOP; | |
host.registerApp(configWord); | |
} | |
// STEP 3: The stream callbacks | |
/************************************************************************** | |
* 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) | |
{ | |
return _doExchange(ctx, superToken, agreementData, agreementId); | |
} | |
function afterAgreementTerminated( | |
ISuperToken superToken, | |
address agreementClass, | |
bytes32 agreementId, | |
bytes calldata agreementData, | |
bytes calldata /*cbdata*/, | |
bytes calldata ctx | |
) | |
external override | |
onlyHost | |
returns (bytes memory) | |
{ | |
// According to the app basic law, we should never revert in a termination callback | |
if (!_isAccepted(superToken) || !_isCFAv1(agreementClass)) return ctx; | |
return _stopExchange(ctx, superToken, agreementData, agreementId); | |
} | |
function _doExchange( | |
bytes calldata _ctx, | |
ISuperToken inputToken, | |
bytes memory agreementData, | |
bytes32 agreementId | |
) | |
private | |
returns (bytes memory newCtx) | |
{ | |
// select the other token | |
ISuperToken outputToken; | |
if(address(inputToken) == address(superToken1)) outputToken = superToken2; | |
if(address(inputToken) == address(superToken2)) outputToken = superToken1; | |
// get info about the stream | |
(address user, ) = abi.decode(agreementData, (address, address)); | |
(,int96 flowRate,,) = cfa.getFlowByID(inputToken, agreementId); | |
// create flow | |
(newCtx, ) = host.callAgreementWithContext( | |
cfa, | |
abi.encodeWithSelector( | |
cfa.createFlow.selector, | |
outputToken, | |
user, | |
flowRate, | |
new bytes(0) // placeholder | |
), | |
new bytes(0), // user data | |
_ctx | |
); | |
emit ExchangeHappened(inputToken, outputToken, flowRate); | |
} | |
function _stopExchange( | |
bytes calldata _ctx, | |
ISuperToken inputToken, | |
bytes memory agreementData, | |
bytes32 /* agreementId */ | |
) | |
private | |
returns (bytes memory newCtx) | |
{ | |
// select the other token | |
ISuperToken outputToken; | |
if(address(inputToken) == address(superToken1)) outputToken = superToken2; | |
if(address(inputToken) == address(superToken2)) outputToken = superToken1; | |
// get infor about the flow | |
(address user, ) = abi.decode(agreementData, (address, address)); | |
(,int96 flowRate,,) = cfa.getFlow(outputToken, address(this), user); | |
// delete flow | |
(newCtx,) = host.callAgreementWithContext( | |
cfa, | |
abi.encodeWithSelector( | |
cfa.deleteFlow.selector, | |
outputToken, | |
address(this), | |
user, | |
new bytes(0) | |
), | |
new bytes(0), // user data | |
_ctx | |
); | |
} | |
// utilities | |
function _isAccepted(ISuperToken _superToken) private view returns (bool) { | |
return address(_superToken) == address(superToken1) || address(_superToken) == address(superToken2); | |
} | |
function _isCFAv1(address _agreementClass) private view returns (bool) { | |
return ISuperAgreement(_agreementClass).agreementType() == _AGREEMENT_TYPE_CFAv1; | |
} | |
modifier onlyHost() { | |
require(msg.sender == address(host), "RedirectAll: support only one host"); | |
_; | |
} | |
modifier onlyExpected(ISuperToken _superToken, address _agreementClass) { | |
require(_isAccepted(_superToken) , "Exchange: not accepted tokens"); | |
require(_isCFAv1(_agreementClass), "Exchange: only CFAv1 supported"); | |
_; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment