paint-brush
Stream Scheduling With Superfluid & Gelatoby@donoso
1,673 reads
1,673 reads

Stream Scheduling With Superfluid & Gelato

by Javier DonosoAugust 19th, 2022
Read on Terminal Reader
Read this story w/o Javascript

Too Long; Didn't Read

Learn how to automate Start/Stop Superfluid streams in Super Apps by using smart contract automation Gelato Network.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail

Coins Mentioned

Mention Thumbnail
Mention Thumbnail
featured image - Stream Scheduling With Superfluid & Gelato
Javier Donoso HackerNoon profile picture


🚀 Learn how to automate Start/Stop Superfluid streams in Super Apps.


One of the most extraordinary things about web3 is that we are still at an early stage and as we speak, the web3 ecosystem is growing with new protocols, toolings, etc., and regardless of our level of expertise, we can contribute to helping others as we are learning. This blog aims to help devs to use the gelato network with smart contracts and specifically for using Gelato automation with Superfluid Super Apps.


Superfluid Protocol among other features allows users to stream money/tokens to users/contracts with only one transaction and minimal capital lockup. The caveat is that you have to remember to stop the stream, if not the stream will continue endlessly. Please visit Superfluid website for further info https://www.superfluid.finance/home


Gelato Network allows you to execute tasks at a future point in time (for ex-web2 would be similar to a kind of scheduler). The main use case is for repeated tasks. For Superfluid streams we will only need one execution (start or stop) therefore we will have to deploy the task and at the same time the “cancel Task”. Please visit Gelato website for further info https://www.gelato.network/



This project showcases general-purpose examples of automating smart contracts with gelato as well as the application for superfluid streams presenting a very simple 3+3 Step guide to create programmable cash flows


In order to do that we have deployed a showcase dapp at https://gelato-superapp.web.app. we will interact with two main verified and deployed on mumbai contracts :

The code can be found in this repository



3+3 Steps Guide to Gelatofy a Smart Contract

Here you will find 3 + 3 easiest steps to add the gelato infrastructure and very easily automate your smart contract tasks:

  1. Wire Gelato infrastructure

  2. Gelato Funding Strategy

  3. Gelato Business Logic

    3.1) Task Creation

    3.2) Checker Condition

    3.3) Executable Function


Step 1: Wire Gelato infrastructure

In order to interact with the gelato network, we will need to interact with the Gelato contracts (addresses can be found here). We will interact with three contracts:


Ops Contract: The main contract for creating, canceling, getting transaction Fees, transfer fees, etc. We will inherit the contract OpsReady.sol , we will pass in the constructor the gelato ops and gelato treasury addresses and we are good to go!.


import {OpsReady} from "./gelato/OpsReady.sol";
import {IOps} from "./gelato/IOps.sol";
import {ITaskTreasury} from "./gelato/ITaskTreasury.sol";

contract PartyApp is OpsReady, Ownable {

  constructor(address payable _ops, address payable _treasury)
    OpsReady(_ops, payable(_treasury)){  }


Gelato Treasury Contract: We will require to interact with the gelato treasury contract for fund/withdrawal depending on our funding strategy (see step 2)


Gelato Network: We will interact with this contract in our tests… more on Bonus Track #2

The OpsReady.sol contract, as well as the interfaces, can be found in gelato-files within the repository. You can just copy them and it works!


Step 2: Gelato Funding Strategy

Gelato executes transactions at a future point in time, therefore we need to choose a way of funding the transactions that Gelato is going to execute on our behalf.There are two alternatives to do that:


1) Fund the Gelato Treasury Contract: as long as you have a positive balance in the treasury contract, the future transactions will be executed. If we choose this alternative we need to interact with the Gelato Treasury Contract funding it.


  function fundGelato(uint256 amount) public payable {
    require(msg.value == amount, "NO_FUNDING");
    ITaskTreasury(treasury).depositFunds{value: amount}(address(this),ETH,amount);
  }

  function withdrawGelato() public onlyOwner {
    uint256 maxAmount = ITaskTreasury(treasury).userTokenBalance(address(this),ETH);

    ITaskTreasury(treasury).withdrawFunds(payable(msg.sender), ETH, maxAmount);
  }


2) Fund every transaction: Our smart contract will transfer in every future transaction the amount requested to pay the fees. In this case, we won’t need to interact with the Gelato Treasury Contract but we must maintain a positive balance in our contract.


Step 3: Gelato Business Logic

There are three main parts/steps that we have to define within the business logic:

3.1) Create the Task: We will have to define a task to tell Gelato what we want to be executed.


  function createTask() public {
    require(taskIdByUser[msg.sender] == bytes32(0), "TASK_STILL_ACTIVE");

    bytes32 taskId = IOps(ops).createTask(
      address(this), /// Contract executing the task
      this.startParty.selector, /// Executable function's selector
      address(this), /// Resolver contract, in our case will be the same
      abi.encodeWithSelector(this.checkerStartParty.selector) /// Checker Condition
    );

    taskIdByUser[msg.sender] = taskId;
  }


3.2) Checker Condition: In order to execute a future transaction, we will have to provide Gelato with the condition to check whether Gelato can execute or not


  function checkerStartParty()
    external
    view
    returns (bool canExec, bytes memory execPayload)
  {
    canExec = headachePresent == false;
    execPayload = abi.encodeWithSelector(this.startParty.selector);
  }


3.3) Executable Function: Function that will be executed when conditions and/or time are met.


  function startParty() external onlyOps {
    require(headachePresent == false, "NOT_READY");
    lastPartyStart = block.timestamp;
    headachePresent = true;
  }


https://gist.github.com/donoso-eth/0678b0613db36c6c332063c9f39cddfd


Party App demo!

Our First Gelato App is going to be very simple. It checks whether our gelato party has started or not. The party will always start if we don’t have a “headache” from the previous party.

We can set manually when our “headache” is over and then the Gelato network will be able to execute the start party, setting a new start party timestamp and setting our “headache to true”

Our screen is divided into two parts depending on our funding strategy.

This contract allows three uses cases:

  1. Create a simple task funded with treasury: Details can be seen above

Once the task is created we can go to the gelato ops website and we will see our task waiting for execution

And after successful execution, the ops dashboard will show us something like this.

  1. Create a simple task funded with treasury and cancel after the first execution (we are going to use this pattern later on with our Superfluid streams).

    https://gist.github.com/donoso-eth/517e2dca691980de506229cbe27eee62

  2. Create a simple task paying each transaction (not treasury funding)

    https://gist.github.com/donoso-eth/340ca96b53005dd457defab0991a735c



Gelato Super App Demo

Our Gelato super App will help us make superfluid streams programmable by automating the stop or the start of a stream.


We will implement the gelato Infrastructure (Step 1) like in the party app and we choose Treasury funding as a Funding Strategy (Step 2)


We will demo stopping a stream after some predefined minutes. Our Business Logic will look like this:

https://gist.github.com/donoso-eth/e4fc0145ed75ed84309da1a467175931


Now if you have funded the treasury, have started a stream and the gelato has already been executed stopping the stream you have unlocked your Bonus!!



Bonus Track #1: Go to Sleep and the Stream will Start.

So far we have achieved to stop a stream automatically, but following the same logic, I may want to start streaming to a new employee exactly at 00:00 AM on the first day of work and I don’t want to stay awake every day because hopefully, we are going to hire a lot of employees!


.. then CFA ACL (CFA Access Control List) to the rescue, with a simple call to the CFA constant agreement either from the SDK or directly to the contract you may want to allow our Super App to start a stream on your behalf when a certain time is elapsed and a “Gelato Task” is executed.


In this use case, we are concatenating two tasks, when the first one is executed, the stream will be started, the task canceled, and another task to stop the stream will be created.

https://gist.github.com/donoso-eth/5491f5f94409f9648fa196c3ed24ca3d



Bonus Track#2: Master Hardhat Fork and Sky Rocket your Development Speed

One of the pleasant experiences in blockchain development is the ability to one-click creation of…..


As our dapps grow in complexity we will require to interact with existing on-chain contracts and if we want to remain local we will have to mock/deploy protocols. In some cases that can be cumbersome and we will spend precious development time. The best alternative to overcome this issue is to work with a fork, with a very simple configuration within our hardhat.config.ts (thks Sam Flamini and Hilmar X for pointing out the right direction).

https://gist.github.com/donoso-eth/1dbfc57d3d40679e324048e1d7c01ab4


and then with the npx hardhat node --network "hardhat" we will be able to create a “virtual” copy of the blockchain we are targeting. The blocks we create from now on will be stored locally and for the older ones we will query the “real” blockchain with our RPC provider (above we are using alchemy)


In our example, we are working with mumbai, the Superfluid contracts (Host, ConstanFlowAgreeent, Supertoken…), as well as the Gelato contracts (Network, OPS, and Treasury), exist with the state they had at the block number of the fork allowing us to interact with them as we would interact with the “real” blockchain.


Now, you have your local “mumbai” with the additional helper methods to truly master the chain:

https://gist.github.com/donoso-eth/148d031bbc57e6a3b6708367a52b0069


In our Dapp we are working with Gelato contracts. The “Executors” will take care to automate our tasks, but in our local “virtual” chain there are no executors… who will execute our tasks?. And the answer to that is very simple, we are going to execute the task ourselves. We can do that because we master our local blockchain, we can be whoever we want to be and we choose to be the executor. Hardhat provides us with the fantastic method hardhat_impersonateAcount and we will use it to become locally “the executor”.


Here we can see the details of the implementation for executing gelato tasks impersonating the Gelato NetWork contract

https://gist.github.com/donoso-eth/88b3e43f5957cf1e6cdbcd326067fee0


We will need to build the params to be able to execute the task, if we look in Ops interface we see that we need the following params


  /// @notice Execution API called by Gelato
  /// @param _txFee Fee paid to Gelato for execution, deducted on the TaskTreasury
  /// @param _feeToken Token used to pay for the execution. ETH = 0xeeeeee...
  /// @param _taskCreator On which contract should Gelato check when to execute the tx
  /// @param _useTaskTreasuryFunds If msg.sender's balance on TaskTreasury should pay for the tx
  /// @param _revertOnFailure To revert or not if call to execAddress fails
  /// @param _execAddress On which contract should Gelato execute the tx
  /// @param _execData Data used to execute the tx, queried from the Resolver by Gelato
  function exec(
    uint256 _txFee,
    address _feeToken,
    address _taskCreator,
    bool _useTaskTreasuryFunds,
    bool _revertOnFailure,
    bytes32 _resolverHash,
    address _execAddress,
    bytes calldata _execData
  ) external;


Using one of our examples in the party app we will have:

https://gist.github.com/donoso-eth/bacd13ccb9718d70f618eca6c82587f7


The implementation in our dapp tests can be seen in the code. We use this technique in our contract tests.

Conclusion

As we have seen, it is relatively simple to wire the gelato network into our smart contracts and unlock the full potential of automation. Specifically for Superfluid streams gelato can allow Super Apps to do stream-scheduling.


Hope this article can help other devs to quickly get up and running with gelato and apply it to Superfluid streams.


You can connect with me on Twitter @donoso_eth


Also published here.