Super Apps in Depth
Using Super Apps To Create Programmable Cashflows
Last updated
Was this helpful?
Using Super Apps To Create Programmable Cashflows
Last updated
Was this helpful?
Super Apps are smart contracts that are registered with the Superfluid Protocol allowing them to react to Super Agreements (like money streams). If you're familiar with ERC777 hooks, they're like those but for Super Agreements.
For example, upon the tradeable cashflow NFT contract receiving a stream, special callbacks functions within the Super App are triggered which automatically open up a new stream from the NFT contract to the owner of the NFT. The contract 'listens' for a call to a Superfluid super agreement contract (the Constant Flow Agreement contract), and runs a single callback function in response to the following 3 actions:
A flow is opened with the Super App as the receiver
A flow is updated which has the Super App as the receiver
A flow is closed by the Super App's counter party (i.e. if either the sender of a flow into the Super app or the recipient of a flow from the Super App deleted the flow, a callback will be run). As of today, this is only relevant for the case of canceled flows.
When a stream is created into a Super App (this will make the Super App the receiver
), the beforeAgreementCreated
and the afterAgreementCreated
callback may be run. These callbacks can execute any arbitrary logic, as long as this logic fits within the rules of standard smart contract development and the rules of Super Apps (which are explained further later on in this section).
In the case of the tradeable cashflow contract, the logic we include inside of the afterAgreementCreated
callback will open up a money stream from the app to the NFT's owner in an amount that is equal to the flowRate
into the app.
Super Apps like the tradeable cashflow example keep their callback logic simple, while others get more advanced and leverage items like userData
for additional functionality.
Some of the most interesting projects in our ecosystem, such as Ricochet Exchange, make heavy use of Super App callbacks.
For a Super App to be able to use callbacks, it must first be 'registered' with the protocol. This is done using a pattern similar to the ERC1820 registry system.
To register a Super App, you need to add the following code to your Super App's constructor:
Running _host.registerAppWithKey
and passing in the configWord
will enable your Super App to be registered within the Superfluid host contract's Super App manifest. This will allow it to be managed under basic Superfluid governance parameters and ensure that callbacks are run as intended.
The APP_LEVEL_FINAL
must be set as seen above for now. This parameter refers to which (and how many) callbacks will be run within a hypothetical chain of Super Apps.
For example, if a user calls a Super app, then that app calls another Super App, do the Super App callbacks run in both contracts, the second contract in the chain of events, or the first contract in the chain of events? The answer to this question, as of Q4 2021, is that the callbacks are run within the first app only.
The _NOOP
(pronounced 'no-op') designations are also important as they allow you to specify which callbacks you'd like to use in your Super App. Callbacks can run before and/or after an agreement function runs.
If you inherit from the SuperAppBase contract, there's a default implementation for each callback which will revert. It's critical to ensure you have a well-tested implementation for each callback, and that you use the _NOOP
pattern to prevent the callbacks you don't wish to use from running. In the case of an app that only uses the afterAgreement
callbacks, you'll want to add the _NOOP
flag to each callback you won't be using - in this case the beforeAgreement
callbacks. This can be done like so within your configWord
variable.
Finally, you can register your app using _host.registerAppWithKey(configWord)
or with _host.registerAppByFactory(app, configWord)
.
You can do this freely on testnets, but on mainnets pre-approval of Super Apps is needed for now. Navigate to this guide in order to find out how to get that done.
When a user first creates a Superfluid stream, the protocol will take an up front buffer to ensure the protocol's security. These deposits are sized as follows:
On testnets, when the flow is being sent to a recipient that is not a Super App, this amount is 1hr x the flowRate
On testnets, when the flow is being sent to a recipient that is a Super App, the deposit amount can be up to 2hrs x the flowRate
On mainnet, when the flow is being sent to a recipient that is not a Super App, the deposit amount is 4hrs x the flowRate
On mainnet, when the flow is being sent to a recipient that is a Super App, the deposit can be up to 8hrs x the flowRate
(note that this is approximate)
Why can the deposit by up to ~2x when sending a stream to a Super App?
Because Super Apps are designed to enable programmable cashflows, the protocol needs to ensure that Super Apps don't execute logic that could impact the protocol's security. The caller of the stream being sent to the super app is essentially covering the deposit of the Super App.
This higher deposit provides an extra incentive for the protocol to ensure that users sending streams into super apps maintain a balance > 0.
Super App callbacks are run when a Super App is on the receiving end of a callAgreement call (i.e. an operation which executes logic in one of the existing agreement contracts, such as the IDA or CFA)
When Will Super App Callbacks Run?
In the case of the constant flow agreement, Super App callbacks will run when
a stream is created where the app is the receiver
of the stream.
a stream is updated where the app is the receiver
of the stream.
a stream is deleted by a user or contract that is external to the Super App.
Note that in the third case, a stream may be deleted by either the sender or the receiver of that stream. This means that the beforeAgreementTerminated
or afterAgreementTerminated
callback will run if a stream that was being sent into the Super App was deleted by the sender, or if a stream being sent from the Super App to another address was deleted by the receiver of that stream.
Who is Calling the Super App Callback?
Callbacks are called entirely on chain by the Superfluid protocol in response to events. Each time an action is taken inside of the Constant Flow Agreement contract, the protocol will check the Super App registry to determine whether or not the stream will be sent to a Super App (or if the stream being deleted involves a Super App in the event of deletion). If it doesn't involve a Super App, there are no callbacks to run, so the stream is created, updated, or deleted without any other operation.
However, if the protocol finds that the stream indeed does involve a Super App after a check to the Superfluid Super App manifest, it will call the necessary callback(s).
A beforeAgreement
callback will be run before the call to the agreement contract will be run. For example, if there is logic inside of the beforeAgreementCreated
callback within of a Super App, and a user opens a stream into that Super App contract, the logic inside of beforeAgreementCreated
will run before the stream is created.
Similarly, an afterAgreement
callback will be run after the call to the agreement contract is run. For example, if there is logic inside of the afterAgreementCreated
callback within a Super App, and a user opens a stream into that Super App contract, the logic inside of afterAgreementCreated
will run after the stream is created.
One additional thing to note about the beforeAgreement
callbacks is that they are view
functions. So, if you want to, for example, save a variable to state in response to something that happens in the beforeAgreement
callback, you should do the following:
Return the data that you want to save inside the beforeAgreement
callback (this returned value will be passed to the afterAgreement
callback as cbdata
, which we explain below)
Save the variable to state inside of the afterAgreement
callback
Make sure that you have an implementation for both the beforeAgreement
and afterAgreement
callbacks
Breaking Down Each Variable
ISuperToken
- the protocol will pass the Super Token that's being used in the call to the constant flow agreement contract here.
address
- this will be the address of the Constant Flow Agreement contract on the network you're interacting with. You can find more details around these networks inside of the Superfluid network directory.
agreementId
- a bytes32 value that is a hash of the sender and receiver's address of the flow that was created, updated, or deleted.
agreementData
- the address of the sender and receiver of the flow that was created, updated, or deleted - encoded using solidity's abi.encode()
built in function
cbdata
- this contains data that was returned by the beforeAgreement
callback if it was run prior to the calling of afterAgreement
callback. Note: this is only passed to the afterAgreement
callback
ctx
- this contains data about the call to the Constant Flow Agreement contract itself. 'Ctx' is short for 'context' and is explained in depth inside of our tutorial on userData (which you can access inside of the ctx
value).
Super Apps are a powerful concept within the Superfluid ecosystem. They allow for new levels of creativity - specifically related to programmable cash flows.
However, there are specific rules that have been encoded into the protocol which SuperApps must abide by. Super App rules should be obeyed at all cost by developers, or they risk their contract being jailed by the protocol.
What does it mean for an app to be 'jailed' exactly? We apply the term jailed
to refer to a Super App that failed to comply with the set of rules encoded into the Superfluid framework. This does not mean that someone on the Superfluid core team is exerting arbitrary control over your Super Apps.
These rules have been written into the protocol at the software level, and are simply designed to place basic security guardrails on Super Apps. Complex systems in our industry have constraints, and you can think of Super App rules as an extension of those constraints that apply to this subset of the protocol.
💡 These rules are a set of restrictions placed on Super Apps that are built into the protocol to protect users. However, these rules are not comprehensive and cannot guarantee that a SuperApp will be 100% safe. Each user should review the Super Apps they are interacting with, and developers should take care to write secure, well-tested Super App code.
1) Super Apps cannot revert in the termination callback (afterAgreementTerminated()
)
Use the trycatch pattern if performing an action which interacts with other contracts inside of the callback. Doing things like transferring tokens without using the trycatch pattern is dangerous and should be avoided.
Double check internal logic to find any revert possibility in this callback.
2) Super Apps can't became insolvent.
Check if any interaction can lead to insolvency situation.
What is an insolvency situation? This occurs when a Super App tries to continue sending funds that it no longer has. Its super token balance must stay > 0 at minimum. You can learn more about liquidation & solvency in our section on this topic.
3) Gas limit operations within the termination callback (afterAgreementTerminated()
)
There is a limit of gas limit send in a callback function (3000000 gas units)
If the Super App reverts on terminations calls because of an out-of-gas error, it will be jailed.
For legitimate cases where the app reverts for out-of-gas (below the gas limit), the Super App is subject to user decision to send a new transaction with more gas. If the app still reverts, it will be jailed.
To protect against these cases, don't create Super Apps that require too much gas within the termination callback.
4) Incorrect ctx (short for context) data within the termination callback
Any attempt to tamper with the value of ctx
or failing to give the right ctx
will result in a Jailed App.
Any time a protocol function returns a ctx
, that ctx
should be passed to the next called function. It will repeat this process even in the return of the callback itself.
For more information on ctx
and how it works you can check out our tutorial on userData.
You can check if a Super App is jailed by calling isAppJailed
on the Superfluid Host contract on the appropriate network and passing in the Super App address in concern. You can conveniently call this in the Read Contract section on Etherscan for our Host contracts which are all verified (for example, see function #19 on Polygonscan). Host addresses can be found in our Protocol directory.
Also, when a Super App is jailed, it emits a Jail
event (see here). So, you can query all Jail events through our subgraph. The below query returns the addresses of all the jailed Super Apps on a network. Try running it in our Console's subgraph sandbox.