[Enhancement]: Updating the vesting terms object to allow programmatic generation of vesting schedules with cliffs
Description of Enhancement :
Update the schema of the vesting_conditions object to include a variable called cliff_condition which references another vesting_condition and add an additional value to the trigger.type variable called CLIFF_CONDITION
Why is this Needed?
The current implementation of vesting_conditions is based on a linear branched flow through the next_condition_ids array. In the example below you would resolve the vesting start date condition then move to the cliff condition and then to the recurring vesting period condition. The problem emerges when we try to generate an actual vesting schedule from this process and determine the quantity of shares for each vesting period.
Using our classic example of 18 shares over four months, what is the number of shares that would be given at a 2 month cliff? Is it 9 shares or is it 8 shares or is it 10 shares? All three are possible depending on the allocation type. If allocation_type is CUMULATIVE_ROUNDING or CUMULATIVE_ROUNDDOWN the cliff quantity would be 9 shares. If allocation_type is BACK_LOADED or BACK_LOADED_TO_SINGLE_TRANCHE, it would be 8 shares. If allocation_type is FRONT_LOADED or FRONT_LOADED_TO_SINGLE_TRANCHE, it would be 10 shares.
With the current implementation, you need to skip over the cliff_condition and calculate the monthly_vesting_condition first and then know to go back to the cliff_condition and apply it. There is no way I can see that a system would be able to do this because there is nothing specifically signposting the cliff_condition as the cliff of the monthly_vesting_condition
The new proposed implementation is an attempt to solve it.
CURRENT IMPLEMENTATION OF VESTING CONDITIONS
{
"id": "f58fa866-be71-4d79-b52a-ea5379a71551",
"object_type": "VESTING_TERMS",
"name": "Four Month / Two Month Cliff",
"description": "50% of the total number of shares shall vest on the two-month anniversary of this Agreement, and an additional 1/4th of the total number of Shares shall then vest on the corresponding day of each month thereafter, until all of the Shares have been released on the fourth anniversary of this Agreement.",
"allocation_type": "CUMULATIVE_ROUNDING",
"vesting_conditions": [
{
"id": "start_condition",
"quantity": "0",
"trigger": {
"type": "VESTING_START_DATE"
},
"next_condition_ids": ["cliff_condition"]
},
{
"id": "cliff_condition",
"description": "50% payout at 2 month",
"portion": {
"numerator": "2",
"denominator": "4"
},
"trigger": {
"type": "VESTING_SCHEDULE_RELATIVE",
"period": {
"length": 2,
"type": "MONTHS",
"occurrences": 1,
"day_of_month": "VESTING_START_DAY_OR_LAST_DAY_OF_MONTH"
},
"relative_to_condition_id": "start_condition"
},
"next_condition_ids": ["monthly_vesting_condition"]
},
{
"id": "monthly_vesting_condition",
"description": "1/4 payout each month thereafter",
"portion": {
"numerator": "1",
"denominator": "4"
},
"trigger": {
"type": "VESTING_SCHEDULE_RELATIVE",
"period": {
"length": 1,
"type": "MONTHS",
"occurrences": 2,
"day_of_month": "VESTING_START_DAY_OR_LAST_DAY_OF_MONTH"
},
"relative_to_condition_id": "cliff_condition"
},
"next_condition_ids": []
}
]
}
PROPOSED IMPLEMENTATION
Essentially, we want to be able to signpost conditions as cliffs related to specific relative vesting conditions. The following implementation would provide systems the ability to track and calculate cliffs. Here is the above example vesting terms in the proposed implementation.
{
"id": "four_month_monthly_two_month_cliff_cumulative_round_down",
"object_type": "VESTING_TERMS",
"name": "Four Month / Two Month Cliff - Cumulative Round Down",
"description": "50% of the total number of shares shall vest on the two-month anniversary of this Agreement, and an additional 1/4th of the total number of Shares shall then vest on the corresponding day of each month thereafter, until all of the Shares have been released on the fourth anniversary of this Agreement.",
"allocation_type": "CUMULATIVE_ROUND_DOWN",
"vesting_conditions": [
{
"id": "start_condition",
"portion": {
"numerator": "0",
"denominator": "4"
}, "trigger": {
"type": "VESTING_START_DATE"
},
"next_condition_ids": ["monthly_vesting_condition"]
},
{
"id": "monthly_vesting_condition",
"description": "1/4 payout each month",
"portion": {
"numerator": "1",
"denominator": "4"
},
"trigger": {
"type": "VESTING_SCHEDULE_RELATIVE",
"period": {
"length": 1,
"type": "MONTHS",
"occurrences": 4,
"day_of_month": "VESTING_START_DAY_OR_LAST_DAY_OF_MONTH"
},
"relative_to_condition_id": "start_condition"
},
"cliff_condition": "cliff_condition",
"next_condition_ids": []
},
{
"id": "cliff_condition",
"description": "Cliff payout at 2nd month",
"trigger": {
"type": "CLIFF_CONDITION",
"period": {
"type": "MONTHS",
"length": 2
},
"relative_to_condition_id": "monthly_vesting_condition"
}
}
]
}
In this implementation, we point from the start_condition directly to the monthly_vesting_condition which is used to calculate the vesting period without a cliff as an intermediate step. The monthly_vesting_condition has a new optional cliff_condition variable which points the system to a cliff condition if required. The cliff_condition object has a description of the cliff with its unique type of trigger and the duration of the cliff. The cliff condition would be used to roll up the appropriate vesting periods into a single vesting period with the correct quantity based on the allocation_type.
@arthur-clara, @sachin-shrestha and @JSv4 to discuss.
I think the description of the vesting schedule in this example should be "50% of the total number of shares shall vest on the two-month anniversary of ~~this Agreement~~ the vesting start date, and an additional 1/4th of the total number of Shares shall then vest on the corresponding day of each month thereafter, until all of the Shares have been released on the ~~fourth~~ four-month anniversary of ~~this Agreement~~ the vesting start date"
Perhaps we could simplify this by making cliff_condition simply an integer, rather than creating a new Vesting Condition object?
I think the only operative piece of information within the period of the proposed cliff_condition object was the length field. Because the type should always be the same as the type of the VestingScheduleRelativeTrigger anyway.
The resulting implementation would be:
{
"id": "four_month_monthly_two_month_cliff_cumulative_round_down",
"object_type": "VESTING_TERMS",
"name": "Four Month / Two Month Cliff - Cumulative Round Down",
"description": "50% of the total number of shares shall vest on the two-month anniversary of this Agreement, and an additional 1/4th of the total number of Shares shall then vest on the corresponding day of each month thereafter, until all of the Shares have been released on the fourth anniversary of this Agreement.",
"allocation_type": "CUMULATIVE_ROUND_DOWN",
"vesting_conditions": [
{
"id": "start_condition",
"portion": {
"numerator": "0",
"denominator": "4"
},
"trigger": {
"type": "VESTING_START_DATE"
},
"next_condition_ids": ["monthly_vesting_condition"]
},
{
"id": "monthly_vesting_condition",
"description": "1/4 payout each month",
"portion": {
"numerator": "1",
"denominator": "4"
},
"trigger": {
"type": "VESTING_SCHEDULE_RELATIVE",
"period": {
"length": 1,
"type": "MONTHS",
"occurrences": 4,
"day_of_month": "VESTING_START_DAY_OR_LAST_DAY_OF_MONTH"
},
"relative_to_condition_id": "start_condition"
},
"cliff_condition": 2,
"next_condition_ids": []
},
}
]
}
Proposed implementation from #540
Vesting Period Primitive Type
Current Implementation
{
"$schema": "http://json-schema.org/draft-07/schema",
"$id": "https://schema.opencaptablecoalition.com/v/1.2.0/primitives/types/vesting/VestingPeriod.schema.json",
"title": "Primitive - Vesting Period Type",
"description": "Abstract type describing the fields common to all periods of time (e.g. 3 months, 365 days) for use in Vesting Terms",
"type": "object",
"properties": {
"length": {
"description": "The quantity of `type` units of time; e.g. for 3 months, this would be `3`; for 30 days, this would be `30`",
"type": "integer",
"minimum": 0
},
"type": {
"description": "The unit of time for the period, e.g. `MONTHS` or `DAYS`",
"$ref": "https://schema.opencaptablecoalition.com/v/1.2.0/enums/PeriodType.schema.json"
},
"occurrences": {
"description": "The number of times this vesting period triggers. If vesting occurs monthly for 36 months, for example, this would be `36`",
"type": "integer",
"minimum": 1
}
},
"required": ["length", "type", "occurrences"],
"$comment": "Copyright © 2024 Open Cap Table Coalition (https://opencaptablecoalition.com) / Original File: https://github.com/Open-Cap-Table-Coalition/Open-Cap-Format-OCF/tree/v1.2.0/schema/primitives/types/vesting/VestingPeriod.schema.json"
}
Proposed Implementation
Add the following to the properties object:
"cliff_installment": {
"description": "If specified, the 1-indexed vesting installment at which the cliff condition occurs. If this field is not provided or less than 2, it is treated as if no cliff applies.",
"type": "integer",
}