Skip to content

Commit

Permalink
added strategy for vesting scheduler
Browse files Browse the repository at this point in the history
  • Loading branch information
d10r committed Jan 2, 2025
1 parent b173a93 commit e33481b
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 1 deletion.
4 changes: 3 additions & 1 deletion src/strategies/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,7 @@ import * as sacraSubgraph from './sacra-subgraph';
import * as fountainhead from './fountainhead';
import * as naymsStaking from './nayms-staking';
import * as morphoDelegation from './morpho-delegation';
import * as superfluidVesting from './superfluid-vesting';

const strategies = {
'delegatexyz-erc721-balance-of': delegatexyzErc721BalanceOf,
Expand Down Expand Up @@ -942,7 +943,8 @@ const strategies = {
'sacra-subgraph': sacraSubgraph,
fountainhead,
'nayms-staking': naymsStaking,
'morpho-delegation': morphoDelegation
'morpho-delegation': morphoDelegation,
'superfluid-vesting': superfluidVesting
};

Object.keys(strategies).forEach(function (strategyName) {
Expand Down
28 changes: 28 additions & 0 deletions src/strategies/superfluid-vesting/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Superfluid Vesting

Superfluid Vesting is done in the typical Superfluid way, not requiring prior capital lockup.
The Vesting Scheduler contract allows for the creation of Vesting Schedules which are then automatically executed.

Vesting Schedules are created by the _vesting sender_.
This sender, by creating a schedule, expresses the intent to provide the promised funds, as specified in the schedule.
In order for this intent to become executable, the sender also needs to
- grant the necessary ACL permissions to the VestingScheduler contract
- have enough funds available when needed

Note: In order to create a vesting schedule with hard guarantees of sucessful execution, a vesting sender needs to be a contract which is pre-funded and has no means to withdraw funds.

## Voting

In order to map vesting schedules to voting power, we need to monitor vesting schedules.
We need to restrict the schedules taken into consideration to those originating from a known and trusted vesting sender. Otherwise anybody could trivially cheat and gain more voting power by creating schedules for themselves.

With a trusted vesting sender defined, we can enumerate the vesting schedules created by it (where it is the _sender_) via subgraph query.
The total vesting amount of a vesting schedule is to be calculated as `cliffAmount + (endDate - startDate) * flowRate)`.
We need to subtract from this amount the already vested amount in order to not double-count it. This is assuming that another strategy accounts for the voting power of the already vested portion.

## Dev

Run test with
```
yarn test --strategy=superfluid-vesting
```
21 changes: 21 additions & 0 deletions src/strategies/superfluid-vesting/examples.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[
{
"name": "Example query",
"strategy": {
"name": "superfluid-vesting",
"params": {
"superTokenAddress": "0xe58267cd7299c29a1b77F4E66Cd12Dd24a2Cd2FD",
"vestingSenderAddress": "0xd7086bf0754383c065d81b62fc2e874373660caa"
}
},
"network": "8453",
"addresses": [
"0xf8a025B42B07db05638FE596cce339707ec3cC71",
"0x389E3d1c46595aF7335F8C6D3e403ce2E8a9cf8A",
"0x264Ff25e609363cf738e238CBc7B680300509BED",
"0x5782BD439d3019F61bFac53f6358C30c3566737C",
"0x4ee5D45eB79aEa04C02961a2e543bbAf5cec81B3"
],
"snapshot": 24392394
}
]
91 changes: 91 additions & 0 deletions src/strategies/superfluid-vesting/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { subgraphRequest } from '../../utils';
import { getAddress } from '@ethersproject/address';

export const author = 'd10r';
export const version = '0.1.0';

const SUBGRAPH_URL_MAP = {
'8453':
'https://subgrapher.snapshot.org/subgraph/arbitrum/4Zp6n8jcsJMBNa3GY9RZwoK4SLjoagwXGq6GhUQNMgSM'
};

export async function strategy(
space,
network,
provider,
addresses,
options,
snapshot
): Promise<Record<string, number>> {

const subgraphUrl = options.subgraphUrl || SUBGRAPH_URL_MAP[network];
if (!subgraphUrl) {
throw new Error('Subgraph URL not specified');
}

console.log(`subgraphUrl: ${subgraphUrl}`);

const query = {
vestingSchedules: {
__args: {
where: {
superToken: options.superTokenAddress,
sender: options.vestingSenderAddress,
endExecutedAt: null,
failedAt: null,
deletedAt: null
},
orderBy: 'cliffAndFlowDate',
orderDirection: 'asc'
},
cliffAmount: true,
cliffAndFlowDate: true,
endDate: true,
flowRate: true,
cliffAndFlowExecutedAt: true,
receiver: true
}
};

if (snapshot !== 'latest') {
// @ts-ignore
query.vestingSchedules.__args.block = { number: snapshot };
}

// Get block timestamp - needed for calculating the remaining amount
const blockTag = typeof snapshot === 'number' ? snapshot : 'latest';
const block = await provider.getBlock(blockTag);
const timestamp = block.timestamp;

const subgraphResult = await subgraphRequest(subgraphUrl, query);

const processedMap = subgraphResult.vestingSchedules.map((schedule) => {
const endDate = BigInt(schedule.endDate);
const cliffAndFlowDate = BigInt(schedule.cliffAndFlowDate);
const flowRate = BigInt(schedule.flowRate);
const cliffAmount = BigInt(schedule.cliffAmount);

// the initial vesting amount
const fullAmount = cliffAmount + flowRate * (endDate - cliffAndFlowDate);

// the remaining amount which hasn't yet vested
let remainingAmount = fullAmount;
if (schedule.cliffAndFlowExecutedAt !== null) {
remainingAmount -= cliffAmount + flowRate * (BigInt(timestamp) - cliffAndFlowDate);
}

return {
schedule: schedule,
fullAmount: fullAmount,
remainingAmount: remainingAmount
}
});

// create a map of the remaining amounts
return Object.fromEntries(
processedMap.map(item => [
getAddress(item.schedule.receiver),
Number(item.remainingAmount) / 1e18
])
);
}
35 changes: 35 additions & 0 deletions src/strategies/superfluid-vesting/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "#/definitions/Strategy",
"definitions": {
"Strategy": {
"title": "Strategy",
"type": "object",
"properties": {
"superTokenAddress": {
"type": "string",
"title": "Super Token contract address",
"examples": ["e.g. 0x6C210F071c7246C452CAC7F8BaA6dA53907BbaE1"],
"pattern": "^0x[a-fA-F0-9]{40}$",
"minLength": 42,
"maxLength": 42
},
"vestingSenderAddress": {
"type": "string",
"title": "Vesting sender address",
"examples": ["e.g. 0x6C210F071c7246C452CAC7F8BaA6dA53907BbaE1"],
"pattern": "^0x[a-fA-F0-9]{40}$",
"minLength": 42,
"maxLength": 42
},
"subgraphUrl": {
"type": "string",
"title": "Subgraph URL (optional - if not already known to the strategy)",
"examples": ["e.g. https://subgrapher.snapshot.org/subgraph/arbitrum/4Zp6n8jcsJMBNa3GY9RZwoK4SLjoagwXGq6GhUQNMgSM"]
}
},
"required": ["superTokenAddress", "vestingSenderAddress"],
"additionalProperties": false
}
}
}

0 comments on commit e33481b

Please sign in to comment.