Skip to content

Commit

Permalink
feat: function plot for Lockup Linear (#27)
Browse files Browse the repository at this point in the history
* feat: function plot for Lockup Linear

build: add "function-plot" as dep
refactor: polish Stream Types
refactor: uncollapse Protocol Concepts

* feat: add function graphs

* refactor: refine function plots

---------

Co-authored-by: maxdesalle <max@sablier.finance>
  • Loading branch information
PaulRBerg and maxdesalle committed Jun 28, 2023
1 parent 4cef81b commit 4f33bd0
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,22 @@ sidebar_position: 2
title: "Types of Streams"
---

import FunctionPlot from "@site/src/components/FunctionPlot";

## Lockup Linear

Lockup Linear streams are the simplest type of stream in Sablier. They follow a straight line that goes up and to the
right on a graph, which corresponds to the identity function $f(x) = x$
([see on Desmos](https://www.desmos.com/calculator/hfqmupvqe3)).
Lockup Linear streams are the simplest type of stream in Sablier. The streamed amount over time follows a straight line
that goes up and to the right on a graph, which corresponds to the identity function $f(x) = x$:

<FunctionPlot options={{ data: [{ fn: "x", color: "#f77423" }] }} />

With this type of stream, the payment rate remains constant, meaning that the same fraction of the deposit amount is
streamed to the recipient every second. This provides greater predictability and is easy to understand because of how
intuitive it is. Imagine a diagonal line going up and to the right – that's how simple it is.

:::tip

You will hear the term **"Lockup"** a lot when dealing with Sablier. This is a term coined by us to describe the
You will often hear the term **"Lockup"** when dealing with Sablier. This is a term coined by us to describe the
requirement that the sender must lock up a certain amount of assets at the beginning of the stream.

:::
Expand All @@ -25,7 +28,25 @@ requirement that the sender must lock up a certain amount of assets at the begin

It is possible to attach a "cliff" to a Lockup Linear stream, which sets a cut-off point for releasing assets. Prior to
the cliff, the recipient cannot withdraw any assets, but the stream continues to accrue them. After the cliff, the
constant payment rate per second kicks in ([see on Desmos](https://www.desmos.com/calculator/kigb45ntwp)).
constant payment rate per second kicks in.

<FunctionPlot
options={{
data: [
{ fn: "0", range: [0, 25], color: "#f77423" },
{ fn: "x", range: [25, 100], color: "#f77423" },
{
points: [
[25, 0],
[25, 25],
],
fnType: "points",
graphType: "polyline",
color: "#f77423",
},
],
}}
/>

This feature is especially useful for vesting ERC-20 assets as it allows you to have, for example, a 1-year cliff, and
then 3 additional years of linear streaming. If the stream is for an employee, you can make it cancellable so that if
Expand All @@ -37,8 +58,8 @@ streamed.
Lockup Dynamic streams are what makes Sablier so unique, since they enable the creation of any type of streaming curve,
including non-linear ones.

On our [user interface](https://app.sablier.com), we support only a few streaming models, such as exponential streams,
but the potential for innovation is limitless when you interact programmatically with the contracts. As an example, one
On our [user interface](https://app.sablier.com), we support only a few streaming models (the ones enumarated below),
but the potential for innovation is limitless when you interact programmatically with the contracts. For example, one
could design a logarithmic stream that emulates the $f(x) = log(x)$ function.

These streams are powered by a number of user-provided "segments", which we will cover in the next article. What is
Expand All @@ -48,16 +69,35 @@ supporting any custom streaming curve.
### Exponential

A fantastic use case for Lockup Dynamic is Exponential streams, a streaming model under which the recipient receives
increasingly more tokens as time moves forward ([see on Desmos](https://www.desmos.com/calculator/p0mxvfotlk)).
increasingly more tokens as time moves forward.

<FunctionPlot options={{ data: [{ fn: "(x/70)^6 * 10", color: "#f77423" }] }} />

Exponentials are a great fit if you are looking to airdrop tokens, because your community members will receive the
majority of the tokens towards the end of the stream instead of receiving the tokens all at once (no streaming) or in a
linear fashion (Lockup Linear). This incentivizes long-term behavior and a constructive attitude.

### Exponential Cliff

Another use case for Lockup Dynamic is a variation of the previous design: an Exponential Cliff
([see on Desmos](https://www.desmos.com/calculator/z8ugxfnlqh)).
Another use case for Lockup Dynamic is a variation of the previous design: an Exponential Cliff.

<FunctionPlot
options={{
data: [
{ fn: "0", range: [0, 50], color: "#f77423" },
{ fn: "(x/70)^6 * 10 + 18.672", color: "#f77423", range: [50, 100] },
{
points: [
[50, 0],
[50, 20],
],
fnType: "points",
graphType: "polyline",
color: "#f77423",
},
],
}}
/>

The stream starts with a cliff (which can be how long you want), a specific amount instantly unlocked when the cliff
ends, and then the rest of the stream is exponentially streamed.
Expand All @@ -69,8 +109,28 @@ larger number of tokens.
### Traditional Unlock

Because Lockup Dynamic is so flexible, it can even be used to create a traditional vesting contract with periodic
unlocks ([see on Desmos](https://www.desmos.com/calculator/ptj2mdnpzx)). In this case, the "streaming" rate would be not
be by the second, but by the week, month, or year.
unlocks. In this case, the "streaming" rate would be not be by the second, but by the week, month, or year.

<FunctionPlot
options={{
data: [
...Array.from({ length: 10 }, (_, i) => ({
color: "#f77423",
fn: `${i * 10}`,
range: [i * 10, (i + 1) * 10],
})),
...Array.from({ length: 9 }, (_, i) => ({
color: "#f77423",
fnType: "points",
graphType: "polyline",
points: [
[(i + 1) * 10, i * 10],
[(i + 1) * 10, i * 10 + 9.8],
],
})),
],
}}
/>

After each period, a specific amount becomes unlocked and and available for the recipient to withdraw. Past unlocks
accumulate, so if the recipient doesn't withdraw them, they will be able to withdraw them later.
Expand Down
2 changes: 1 addition & 1 deletion docs/concepts/protocol/_category_.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"collapsed": true,
"collapsed": false,
"label": "Protocol Concepts",
"position": 4
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@mdx-js/react": "^1.6.22",
"clsx": "^1.2.1",
"docusaurus-theme-github-codeblock": "^1.1.4",
"function-plot": "^1.23.3",
"hast-util-is-element": "1.1.0",
"prism-react-renderer": "^1.3.5",
"react": "^17.0.2",
Expand Down
81 changes: 76 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions src/components/FunctionPlot.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import functionPlot, { FunctionPlotOptions } from "function-plot";
import React, { useEffect, useRef } from "react";

export interface FunctionPlotProps {
options?: FunctionPlotOptions;
}

// See https://github.com/mauriciopoppe/function-plot
const FunctionPlot: React.FC<FunctionPlotProps> = React.memo(function ({ options }: FunctionPlotProps) {
const rootEl = useRef(null);

useEffect(() => {
try {
functionPlot({
...options,
disableZoom: true,
grid: true,
xAxis: { domain: [0, 100], label: "x - time" },
yAxis: { domain: [0, 100], label: "y - earnings" },
target: rootEl.current,
});
} catch (err) {
console.error("FunctionPlot error: ", err);
}
}, [options, rootEl]);

return <div ref={rootEl} />;
});

FunctionPlot.displayName = "FunctionPlot";

export default FunctionPlot;
2 changes: 1 addition & 1 deletion src/components/LinkPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ interface LinkPreviewProps {
title: string;
}

export default function LinkPreview(props: LinkPreviewProps): JSX.Element {
export default function LinkPreview(props: LinkPreviewProps) {
const renderIcon = useCallback(() => {
switch (props.icon) {
case "github":
Expand Down

0 comments on commit 4f33bd0

Please sign in to comment.