Skip to content

Commit 7f404d0

Browse files
Feat: Add STREAM emissions (#137)
* Add Streamflow adapter and protocol token emission * add protocol id --------- Co-authored-by: Reynardo Etenia Wongso <[email protected]>
1 parent d874f30 commit 7f404d0

File tree

2 files changed

+113
-0
lines changed

2 files changed

+113
-0
lines changed

adapters/streamflow/index.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { CliffAdapterResult } from "../../types/adapters";
2+
3+
type UnlockScheduleEntry = {
4+
ts: number;
5+
value: string;
6+
}
7+
8+
type LabelStats = {
9+
tokensLocked: string;
10+
tokensDeposited: string;
11+
tokensUnlocked: string;
12+
total: number;
13+
scheduled: number;
14+
completed: number;
15+
paused: number;
16+
canceled: number;
17+
active: number;
18+
unlockSchedule: UnlockScheduleEntry[];
19+
}
20+
21+
type StreamflowStats = {
22+
byLabel: {
23+
[labelName: string]: LabelStats;
24+
};
25+
}
26+
27+
type TokenInfo = {
28+
decimals: number;
29+
}
30+
31+
type StreamflowAPIResponse = {
32+
stats: StreamflowStats;
33+
info: TokenInfo;
34+
defaultLabel: string;
35+
}
36+
37+
export async function fetchStreamStats(mint: string): Promise<StreamflowAPIResponse> {
38+
return await fetch(`https://api.streamflow.finance/v2/api/token-dashboards/SOLANA/${mint}`).then(r => r.json());
39+
}
40+
41+
export async function processUnlockSchedules(mint: string): Promise<{ [labelName: string]: CliffAdapterResult[] }> {
42+
const data = await fetchStreamStats(mint);
43+
const result: { [labelName: string]: CliffAdapterResult[] } = {};
44+
const tokenDecimals = data.info.decimals;
45+
46+
Object.entries(data.stats.byLabel).filter(([labelName]) => labelName !== data.defaultLabel).forEach(([labelName, labelStats]) => {
47+
const unlockSchedule = labelStats.unlockSchedule;
48+
49+
if (unlockSchedule.length === 0) {
50+
result[labelName] = [];
51+
return;
52+
}
53+
54+
let previousValue = 0;
55+
const sections: CliffAdapterResult[] = [];
56+
57+
unlockSchedule.forEach((entry) => {
58+
const currentValue = parseFloat(entry.value) / 10 ** tokenDecimals;
59+
const unlockAmount = currentValue - previousValue;
60+
61+
if (unlockAmount > 0) {
62+
sections.push({
63+
type: "cliff",
64+
start: entry.ts,
65+
amount: unlockAmount,
66+
});
67+
}
68+
69+
previousValue = currentValue;
70+
});
71+
72+
result[labelName] = sections;
73+
});
74+
75+
return result;
76+
}

protocols/streamflow.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { processUnlockSchedules } from "../adapters/streamflow";
2+
import { Protocol } from "../types/adapters";
3+
4+
const STREAM_MINT = "STREAMribRwybYpMmSYoCsQUdr6MZNXEqHgm7p1gu9M";
5+
const CHAIN = "solana";
6+
7+
async function createStreamflowProtocol(): Promise<Protocol> {
8+
const unlockSchedules = await processUnlockSchedules(STREAM_MINT);
9+
10+
return {
11+
...unlockSchedules,
12+
meta: {
13+
sources: [
14+
"https://app.streamflow.finance/project-dashboard/solana/mainnet/STREAMribRwybYpMmSYoCsQUdr6MZNXEqHgm7p1gu9M",
15+
"https://docs.streamflow.finance/en/articles/9869372-stream-token",
16+
],
17+
token: `${CHAIN}:${STREAM_MINT}`,
18+
protocolIds: ["2608"],
19+
notes: [
20+
"Token unlock schedule data is fetched from the Streamflow Finance API",
21+
"Data includes all labels and their respective unlock schedules",
22+
"Sections are dynamically created based on the labels returned by the API",
23+
],
24+
chain: CHAIN,
25+
},
26+
categories:
27+
{
28+
insiders:["Contributors"],
29+
privateSale:["Investors"],
30+
noncirculating:["Ecosystem"],
31+
}
32+
};
33+
}
34+
35+
const streamflow = createStreamflowProtocol;
36+
37+
export default streamflow;

0 commit comments

Comments
 (0)