-
Notifications
You must be signed in to change notification settings - Fork 804
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[superfluid-vesting] Added strategy for Superfluid Vesting Schedules (#…
…1671) * added strategy for vesting scheduler * added subgraph urls, more accurate accounting
- Loading branch information
Showing
5 changed files
with
183 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 successful 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 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
import { subgraphRequest } from '../../utils'; | ||
import { getAddress } from '@ethersproject/address'; | ||
|
||
export const author = 'd10r'; | ||
export const version = '0.1.0'; | ||
|
||
const SUBGRAPH_URL_MAP = { | ||
'10': | ||
'https://subgrapher.snapshot.org/subgraph/arbitrum/6YMD95vYriDkmTJewC2vYubqVZrc6vdk3Sp3mR3YCQUw', | ||
'8453': | ||
'https://subgrapher.snapshot.org/subgraph/arbitrum/4Zp6n8jcsJMBNa3GY9RZwoK4SLjoagwXGq6GhUQNMgSM', | ||
'11155420': | ||
'https://subgrapher.snapshot.org/subgraph/arbitrum/5UctBWuaQgr2HVSG6XtAKt5shyjg9snGvD6F424FcjMN' | ||
}; | ||
|
||
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'); | ||
} | ||
|
||
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, | ||
remainderAmount: 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); | ||
const remainderAmount = BigInt(schedule.remainderAmount); | ||
|
||
// the initial vesting amount | ||
const fullAmount = cliffAmount + flowRate * (endDate - cliffAndFlowDate) + remainderAmount; | ||
|
||
// 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), | ||
// in theory remainingAmount could become negative, thus we cap at 0 | ||
Math.max(Number(item.remainingAmount) / 1e18, 0) | ||
]) | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"] | ||
Check failure on line 28 in src/strategies/superfluid-vesting/schema.json GitHub Actions / lint / Lint
|
||
} | ||
}, | ||
"required": ["superTokenAddress", "vestingSenderAddress"], | ||
"additionalProperties": false | ||
} | ||
} | ||
} |