Skip to content

Instantly share code, notes, and snippets.

@pi0neerpat
Last active May 10, 2021 17:21
Show Gist options
  • Select an option

  • Save pi0neerpat/4669f393d5b9cc199c88ab6e9c68686f to your computer and use it in GitHub Desktop.

Select an option

Save pi0neerpat/4669f393d5b9cc199c88ab6e9c68686f 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.7.6+commit.7338295f.js&optimize=false&runs=200&gist=
Instructions: https://docs.superfluid.finance/superfluid/resources/examples/soda-machine
pragma solidity ^0.7.0;
import {
ISuperfluid,
ISuperToken,
ISuperApp,
ISuperAgreement,
SuperAppDefinitions
} from "https://github.com/superfluid-finance/protocol-monorepo/blob/remix-support-soda-machine/packages/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol";
import {
ISuperTokenFactory
}
from "https://github.com/superfluid-finance/protocol-monorepo/blob/remix-support-soda-machine/packages/ethereum-contracts/contracts/interfaces/superfluid/ISuperTokenFactory.sol";
import {
IConstantFlowAgreementV1
} from "https://github.com/superfluid-finance/protocol-monorepo/blob/remix-support-soda-machine/packages/ethereum-contracts/contracts/interfaces/agreements/IConstantFlowAgreementV1.sol";
import { INativeSuperToken, NativeSuperTokenProxy } from "./NativeSuperToken.sol";
import { SodaMachine } from "./SodaMachine.sol";
contract Example {
event NewContract(address _contract);
ISuperfluid private _host; // host
IConstantFlowAgreementV1 private _cfa; // the stored constant flow agreement class address
ISuperToken private _acceptedToken; // accepted token
INativeSuperToken public _sodaToken;
ISuperTokenFactory private _superTokenFactory;
address public _sodaMachine;
uint256 constant TOTAL_SUPPLY = 1000000000000000000000000; // 1 M tokens
constructor(
ISuperfluid host,
IConstantFlowAgreementV1 cfa,
ISuperToken acceptedToken,
ISuperTokenFactory superTokenFactory
) {
_host = host;
_cfa = cfa;
_acceptedToken= acceptedToken;
_superTokenFactory = superTokenFactory;
step1_deploy();
step2_initProxy();
step3_initToken();
}
function step1_deploy() internal {
// Deploy the Custom Super Token proxy
_sodaToken = INativeSuperToken(address(new NativeSuperTokenProxy()));
emit NewContract(address(_sodaToken));
// Deploy the machine using the new SODA token address
_sodaMachine = address(new SodaMachine(_host, _cfa, _acceptedToken, ISuperToken(address(_sodaToken))));
emit NewContract(_sodaMachine);
}
function step2_initProxy() internal {
// Set the proxy to use the Super Token logic managed by Superfluid Protocol Governance
_superTokenFactory.initializeCustomSuperToken(address(_sodaToken));
}
function step3_initToken() internal {
// Set up the token and mint 1M tokens to the machine
_sodaToken.initialize(
"Soda",
"SODA",
TOTAL_SUPPLY,
address(_sodaMachine)
);
}
}
// SPDX-License-Identifier: AGPLv3
pragma solidity ^0.7.0;
import {
ISuperToken,
CustomSuperTokenProxyBase
}
from "https://github.com/superfluid-finance/protocol-monorepo/blob/remix-support-soda-machine/packages/ethereum-contracts/contracts/interfaces/superfluid/CustomSuperTokenProxyBase.sol";
import { IERC20 } from "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.2.0-solc-0.7/contracts/token/ERC20/IERC20.sol";
/**
* @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(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));
}
}
pragma solidity ^0.7.0;
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.2.0-solc-0.7/contracts/token/ERC777/IERC777.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.2.0-solc-0.7/contracts/introspection/IERC1820Registry.sol";
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.2.0-solc-0.7/contracts/token/ERC777/IERC777Recipient.sol";
/**
* @title Simple777Recipient
* @dev Very simple ERC777 Recipient
* see https://forum.openzeppelin.com/t/simple-erc777-token-example/746
*/
contract Simple777Recipient is IERC777Recipient {
IERC1820Registry private _erc1820 = IERC1820Registry(0x1820a4B7618BdE71Dce8cdc73aAB6C95905faD24);
bytes32 constant private TOKENS_RECIPIENT_INTERFACE_HASH = keccak256("ERC777TokensRecipient");
IERC777 private _token;
// event DoneStuff(address operator, address from, address to, uint256 amount, bytes userData, bytes operatorData);
constructor (address token) {
_token = IERC777(token);
_erc1820.setInterfaceImplementer(address(this), TOKENS_RECIPIENT_INTERFACE_HASH, address(this));
}
function tokensReceived(
address,
address,
address,
uint256,
bytes calldata,
bytes calldata
) external override {
require(msg.sender == address(_token), "Simple777Recipient: Invalid token");
// do nothing
// emit DoneStuff(operator, from, to, amount, userData, operatorData);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;
pragma experimental ABIEncoderV2;
import {
ISuperfluid,
ISuperToken,
ISuperApp,
ISuperAgreement,
SuperAppDefinitions
} from "https://github.com/superfluid-finance/protocol-monorepo/blob/remix-support-soda-machine/packages/ethereum-contracts/contracts/interfaces/superfluid/ISuperfluid.sol";
// When you're ready 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-soda-machine/packages/ethereum-contracts/contracts/interfaces/agreements/IConstantFlowAgreementV1.sol";
import {
SuperAppBase
} from "https://github.com/superfluid-finance/protocol-monorepo/blob/remix-support-soda-machine/packages/ethereum-contracts/contracts/apps/SuperAppBase.sol";
import { Simple777Recipient } from "./Simple777Recipient.sol";
contract SodaMachine is Simple777Recipient, SuperAppBase {
ISuperfluid private _host; // host
IConstantFlowAgreementV1 private _cfa; // the stored constant flow agreement class address
ISuperToken private _acceptedToken; // accepted token
address private _receiver;
ISuperToken private _sodaToken;
constructor(
ISuperfluid host,
IConstantFlowAgreementV1 cfa,
ISuperToken acceptedToken,
ISuperToken sodaToken
)
Simple777Recipient(address(sodaToken))
{
assert(address(host) != address(0));
assert(address(cfa) != address(0));
assert(address(acceptedToken) != address(0));
assert(address(sodaToken) != address(0));
_host = host;
_cfa = cfa;
_acceptedToken = acceptedToken;
_sodaToken = sodaToken;
uint256 configWord =
SuperAppDefinitions.APP_LEVEL_FINAL |
SuperAppDefinitions.BEFORE_AGREEMENT_CREATED_NOOP |
SuperAppDefinitions.BEFORE_AGREEMENT_UPDATED_NOOP |
SuperAppDefinitions.BEFORE_AGREEMENT_TERMINATED_NOOP;
_host.registerApp(configWord);
}
/**************************************************************************
* SatisfyFlows Logic
*************************************************************************/
/// @dev If a new stream is opened, or an existing one is opened
function _updateOutflow(bytes calldata ctx, address customer, bytes32 agreementId)
private
returns (bytes memory newCtx)
{
newCtx = ctx;
(,int96 inFlowRate,,) = _cfa.getFlowByID(_acceptedToken, agreementId);
(,int96 outFlowRate,,) = _cfa.getFlow(_sodaToken, address(this), customer);
if (inFlowRate < 0 ) inFlowRate = -inFlowRate; // Fixes issue when inFlowRate is negative
if (outFlowRate != int96(0)){
// @dev if there already exists an outflow, then update it.
(newCtx, ) = _host.callAgreementWithContext(
_cfa,
abi.encodeWithSelector(
_cfa.updateFlow.selector,
_sodaToken,
customer,
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,
_sodaToken,
address(this),
customer,
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,
_sodaToken,
customer,
inFlowRate,
new bytes(0) // placeholder
),
"0x",
newCtx
);
}
}
/**************************************************************************
* 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 customer = _host.decodeCtx(_ctx).msgSender;
return _updateOutflow(_ctx, customer, _agreementId);
}
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 customer = _host.decodeCtx(_ctx).msgSender;
return _updateOutflow(_ctx, customer, _agreementId);
}
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;
(address customer,) = abi.decode(_agreementData, (address, address));
return _updateOutflow(_ctx, customer, _agreementId);
}
function getNetFlow() public view returns (int96) {
return _cfa.getNetFlow(_acceptedToken, address(this));
}
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), "SatisfyFlows: support only one host");
_;
}
modifier onlyExpected(ISuperToken superToken, address agreementClass) {
require(_isSameToken(superToken), "SatisfyFlows: not accepted token");
require(_isCFAv1(agreementClass), "SatisfyFlows: only CFAv1 supported");
_;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment