Created
September 26, 2021 19:50
-
-
Save filipesoccol/b5da569d96da76b7661d8c2dc4bb5b40 to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.8.0+commit.c7dfd78e.js&optimize=false&runs=200&gist=
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.7.0; | |
// 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 AsteroidStream 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; | |
address private creator; | |
int96 public maxFlow; | |
string public name; | |
event ExchangeHappened(ISuperToken inputToken, ISuperToken outputToken, int96 flowRate); | |
constructor( | |
ISuperfluid _host, | |
ISuperToken _superToken1, | |
ISuperToken _superToken2, | |
address _creator, | |
int96 _maxFlow, | |
string memory _name | |
) { | |
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; | |
creator = _creator; | |
name = _name; | |
maxFlow = _maxFlow; | |
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) | |
{ | |
// 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, | |
superToken2, | |
user, | |
flowRate, | |
new bytes(0) // placeholder | |
), | |
new bytes(0), // user data | |
_ctx | |
); | |
emit ExchangeHappened(inputToken, superToken2, 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 | |
); | |
} | |
function withdrawDAI() onlyCreator public { | |
ISuperToken(superToken1).transfer(msg.sender, ISuperToken(superToken1).balanceOf(address(this))); | |
} | |
// utilities | |
function _isAccepted(ISuperToken _superToken) private view returns (bool) { | |
return address(_superToken) == address(superToken1); | |
} | |
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 onlyCreator() { | |
require(msg.sender == creator, "Creator: Only creator could call."); | |
_; | |
} | |
modifier onlyExpected(ISuperToken _superToken, address _agreementClass) { | |
require(_isAccepted(_superToken) , "Exchange: not accepted tokens"); | |
require(_isCFAv1(_agreementClass), "Exchange: only CFAv1 supported"); | |
_; | |
} | |
} |
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.7.0; | |
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 { | |
ISuperTokenFactory | |
} | |
from "https://github.com/superfluid-finance/protocol-monorepo/blob/dev/packages/ethereum-contracts/contracts/interfaces/superfluid/ISuperTokenFactory.sol"; | |
import { | |
IConstantFlowAgreementV1 | |
} from "https://github.com/superfluid-finance/protocol-monorepo/blob/dev/packages/ethereum-contracts/contracts/interfaces/agreements/IConstantFlowAgreementV1.sol"; | |
import { INativeSuperToken, NativeSuperTokenProxy } from "./NativeSuperToken.sol"; | |
import { AsteroidStream } from "./AsteroidStream.sol"; | |
contract FluidMinersFactory { | |
event NewAsteroid(address indexed asteroid, address indexed token, int96 maxFlow, uint256 supply); | |
ISuperfluid private _host; // host | |
ISuperToken private _daiToken; // accepted token | |
ISuperTokenFactory private _superTokenFactory; | |
constructor( | |
ISuperfluid host, | |
ISuperToken daiToken, | |
ISuperTokenFactory superTokenFactory | |
) { | |
_host = host; | |
_daiToken = daiToken; | |
_superTokenFactory = superTokenFactory; | |
} | |
function deployAsteroid( | |
uint256 _supply, | |
int96 _maxFlow, | |
string calldata _nameast, | |
string calldata _name, | |
string calldata _symbol | |
) external { | |
// Deploy the Custom Super Token proxy | |
INativeSuperToken newToken = INativeSuperToken(address(new NativeSuperTokenProxy())); | |
// Deploy the machine using the new token address | |
AsteroidStream stream = new AsteroidStream(_host, _daiToken, ISuperToken(address(newToken)), msg.sender, _maxFlow, _nameast); | |
// Set the proxy to use the Super Token logic managed by Superfluid Protocol Governance | |
_superTokenFactory.initializeCustomSuperToken(address(newToken)); | |
// Set up the token and mint tokens | |
newToken.initialize( | |
_name, | |
_symbol, | |
_supply, | |
address(stream) | |
); | |
emit NewAsteroid(address(stream), address(newToken), _maxFlow, _supply); | |
} | |
} |
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: AGPLv3 | |
pragma solidity >=0.7.0; | |
import { | |
ISuperToken | |
} | |
from "https://github.com/superfluid-finance/protocol-monorepo/blob/dev/packages/ethereum-contracts/contracts/interfaces/superfluid/ISuperToken.sol"; | |
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | |
import { Proxy } from "@openzeppelin/contracts/proxy/Proxy.sol"; | |
/** | |
* @title UUPS (Universal Upgradeable Proxy Standard) Shared Library | |
*/ | |
library UUPSUtils { | |
/** | |
* @dev Implementation slot constant. | |
* Using https://eips.ethereum.org/EIPS/eip-1967 standard | |
* Storage slot 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc | |
* (obtained as bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)). | |
*/ | |
bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; | |
/// @dev Get implementation address. | |
function implementation() internal view returns (address impl) { | |
assembly { // solium-disable-line | |
impl := sload(_IMPLEMENTATION_SLOT) | |
} | |
} | |
/// @dev Set new implementation address. | |
function setImplementation(address codeAddress) internal { | |
assembly { | |
// solium-disable-line | |
sstore( | |
_IMPLEMENTATION_SLOT, | |
codeAddress | |
) | |
} | |
} | |
} | |
/** | |
* @dev UUPS (Universal Upgradeable Proxy Standard) Proxy | |
* | |
* NOTE: | |
* - Compliant with [Universal Upgradeable Proxy Standard](https://eips.ethereum.org/EIPS/eip-1822) | |
* - Compiiant with [Standard Proxy Storage Slots](https://eips.ethereum.org/EIPS/eip-1967) | |
* - Implements delegation of calls to other contracts, with proper forwarding of | |
* return values and bubbling of failures. | |
* - It defines a fallback function that delegates all calls to the implementation. | |
*/ | |
contract UUPSProxy is Proxy { | |
/** | |
* @dev Proxy initialization function. | |
* This should only be called once and it is permission-less. | |
* @param initialAddress Initial logic contract code address to be used. | |
*/ | |
function initializeProxy(address initialAddress) external { | |
require(initialAddress != address(0), "UUPSProxy: zero address"); | |
require(UUPSUtils.implementation() == address(0), "UUPSProxy: already initialized"); | |
UUPSUtils.setImplementation(initialAddress); | |
} | |
/// @dev Proxy._implementation implementation | |
function _implementation() internal virtual override view returns (address) | |
{ | |
return UUPSUtils.implementation(); | |
} | |
} | |
/** | |
* @dev Custom super token proxy base contract | |
* | |
* NOTE: | |
* - Because of how solidity is layouting its storages variables and custom | |
* super token inherits the Super Token standard implementation, so it is | |
* required that the custom token proxy would need to pad its implementation | |
* with reserved storages used by the Super Token implementation. | |
* - Refer to SETH.sol for an example how it is used. | |
*/ | |
abstract contract CustomSuperTokenProxyBase is UUPSProxy { | |
// This is the hard-coded number of storage slots used by the super token | |
uint256[32] internal _storagePaddings; | |
} | |
/** | |
* @dev Native SuperToken custom token functions | |
* | |
* @author Superfluid | |
*/ | |
interface INativeSuperTokenCustom { | |
function initialize(string calldata name, string calldata symbol, uint256 initialSupply, address recipient) external; | |
} | |
/** | |
* @dev Native SuperToken full interface | |
* | |
* @author Superfluid | |
*/ | |
interface INativeSuperToken is INativeSuperTokenCustom, ISuperToken { | |
function initialize(string calldata name, string calldata symbol, uint256 initialSupply, address recipient) external override; | |
} | |
/** | |
* @dev Native SuperToken custom super token implementation | |
* | |
* NOTE: | |
* - This is a simple implementation where the supply is pre-minted. | |
* | |
* @author Superfluid | |
*/ | |
contract NativeSuperTokenProxy is INativeSuperTokenCustom, CustomSuperTokenProxyBase { | |
function initialize(string calldata name, string calldata symbol, uint256 totalSupply, address recipient) | |
external override | |
{ | |
ISuperToken(address(this)).initialize( | |
IERC20(address(0x0)), // no underlying/wrapped token | |
18, // shouldn't matter if there's no wrapped token | |
name, | |
symbol | |
); | |
ISuperToken(address(this)).selfMint(recipient, totalSupply, new bytes(0)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment