Skip to content

Commit

Permalink
feat: use vesting start date as update initial due date reference (#1708
Browse files Browse the repository at this point in the history
)
  • Loading branch information
andyesp authored Mar 11, 2024
1 parent 1160eed commit 5649981
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 154 deletions.
2 changes: 1 addition & 1 deletion src/back/routes/proposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -564,7 +564,7 @@ export async function updateProposalStatus(req: WithAuth<Request<{ proposal: str
update.vesting_addresses
)
const vestingContractData = await getVestingContractData(vesting_addresses[vesting_addresses.length - 1], id)
await UpdateModel.createPendingUpdates(id, vestingContractData, proposal.configuration.vestingStartDate)
await UpdateModel.createPendingUpdates(id, vestingContractData)
}
} else if (update.status === ProposalStatus.Passed) {
update.passed_by = user
Expand Down
205 changes: 71 additions & 134 deletions src/entities/Updates/model.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import crypto from 'crypto'

import { VestingInfo } from '../../clients/VestingData'
import Time from '../../utils/date/Time'
import { getMonthsBetweenDates } from '../../utils/date/getMonthsBetweenDates'
import { VestingStartDate } from '../Grant/types'

import UpdateModel from './model'
import { UpdateStatus } from './types'
Expand All @@ -26,13 +26,11 @@ describe('UpdateModel', () => {

describe('createPendingUpdates', () => {
describe('for a vesting with a duration of almost 3 months', () => {
const vestingDates = {
vestingStartAt: '2020-01-01 00:00:00z',
vestingFinishAt: '2020-03-31 00:00:00z',
}

describe('when preferred payment date is on the 1st of the month', () => {
const preferredPaymentDate = VestingStartDate.First
describe('when vesting start date is on the 1st of the month', () => {
const vestingDates = {
vestingStartAt: '2020-01-01 00:00:00z',
vestingFinishAt: '2020-03-31 00:00:00z',
} as VestingInfo

it('calculates the correct amount of pending updates', () => {
expect(
Expand All @@ -42,12 +40,12 @@ describe('UpdateModel', () => {
})

it('deletes any pending updates for the proposal', async () => {
await UpdateModel.createPendingUpdates(PROPOSAL_ID, vestingDates, preferredPaymentDate)
await UpdateModel.createPendingUpdates(PROPOSAL_ID, vestingDates)
expect(UpdateModel.delete).toHaveBeenCalledWith({ proposal_id: PROPOSAL_ID, status: UpdateStatus.Pending })
})

it('creates expected pending updates with the correct attributes', async () => {
await UpdateModel.createPendingUpdates(PROPOSAL_ID, vestingDates, preferredPaymentDate)
await UpdateModel.createPendingUpdates(PROPOSAL_ID, vestingDates)
expect(UpdateModel.createMany).toHaveBeenCalledWith([
{
id: UUID,
Expand Down Expand Up @@ -76,26 +74,73 @@ describe('UpdateModel', () => {
])
})
})

describe('when vesting start date is on the 15st of the month', () => {
const vestingDates = {
vestingStartAt: '2020-01-15 00:00:00z',
vestingFinishAt: '2020-04-14 00:00:00z',
} as VestingInfo

it('calculates the correct amount of pending updates', () => {
expect(
getMonthsBetweenDates(new Date(vestingDates.vestingStartAt), new Date(vestingDates.vestingFinishAt))
).toEqual({ months: 2, extraDays: 30 })
expect(UpdateModel.getAmountOfUpdates(vestingDates)).toEqual(3)
})

it('deletes any pending updates for the proposal', async () => {
await UpdateModel.createPendingUpdates(PROPOSAL_ID, vestingDates)
expect(UpdateModel.delete).toHaveBeenCalledWith({ proposal_id: PROPOSAL_ID, status: UpdateStatus.Pending })
})

it('creates expected pending updates with the correct attributes', async () => {
await UpdateModel.createPendingUpdates(PROPOSAL_ID, vestingDates)
expect(UpdateModel.createMany).toHaveBeenCalledWith([
{
id: UUID,
proposal_id: PROPOSAL_ID,
status: UpdateStatus.Pending,
due_date: Time.utc('2020-02-15T00:00:00.000Z').toDate(),
created_at: FAKE_NOW,
updated_at: FAKE_NOW,
},
{
id: UUID,
proposal_id: PROPOSAL_ID,
status: UpdateStatus.Pending,
due_date: Time.utc('2020-03-15T00:00:00.000Z').toDate(),
created_at: FAKE_NOW,
updated_at: FAKE_NOW,
},
{
id: UUID,
proposal_id: PROPOSAL_ID,
status: UpdateStatus.Pending,
due_date: Time.utc('2020-04-15T00:00:00.000Z').toDate(),
created_at: FAKE_NOW,
updated_at: FAKE_NOW,
},
])
})
})
})

describe('for a vesting with a duration of 6 months and some extra days, with a starting date different than the preferred', () => {
const vestingDates = {
vestingStartAt: '2020-11-17 00:00:00z',
vestingStartAt: '2020-11-15 00:00:00z',
vestingFinishAt: '2021-05-31 00:00:00z',
}

describe('when preferred payment date is on the 15th of the month', () => {
const preferredPaymentDate = VestingStartDate.Fifteenth
} as VestingInfo

describe('when vesting start date is on the 15th of the month', () => {
it('calculates the correct amount of pending updates', () => {
expect(
getMonthsBetweenDates(new Date(vestingDates.vestingStartAt), new Date(vestingDates.vestingFinishAt))
).toEqual({ months: 6, extraDays: 14 })
).toEqual({ months: 6, extraDays: 16 })
expect(UpdateModel.getAmountOfUpdates(vestingDates)).toEqual(7)
})

it('creates expected pending updates with the correct attributes', async () => {
await UpdateModel.createPendingUpdates(PROPOSAL_ID, vestingDates, preferredPaymentDate)
await UpdateModel.createPendingUpdates(PROPOSAL_ID, vestingDates)
expect(UpdateModel.createMany).toHaveBeenCalledWith([
{
id: UUID,
Expand Down Expand Up @@ -175,11 +220,9 @@ describe('UpdateModel', () => {
const vestingDates = {
vestingStartAt: '2020-07-01 00:00:00z',
vestingFinishAt: '2021-01-01 00:00:00z',
}

describe('when preferred payment date is on the 15th of the month', () => {
const preferredPaymentDate = VestingStartDate.Fifteenth
} as VestingInfo

describe('when the vesting contract start date is the first day of the month', () => {
it('calculates the correct amount of pending updates', () => {
expect(
getMonthsBetweenDates(new Date(vestingDates.vestingStartAt), new Date(vestingDates.vestingFinishAt))
Expand All @@ -188,53 +231,53 @@ describe('UpdateModel', () => {
})

it('creates expected pending updates with the correct attributes', async () => {
await UpdateModel.createPendingUpdates(PROPOSAL_ID, vestingDates, preferredPaymentDate)
await UpdateModel.createPendingUpdates(PROPOSAL_ID, vestingDates)
expect(UpdateModel.createMany).toHaveBeenCalledWith([
{
id: UUID,
proposal_id: PROPOSAL_ID,
status: UpdateStatus.Pending,
due_date: Time.utc('2020-08-15T00:00:00.000Z').toDate(),
due_date: Time.utc('2020-08-01T00:00:00.000Z').toDate(),
created_at: FAKE_NOW,
updated_at: FAKE_NOW,
},
{
id: UUID,
proposal_id: PROPOSAL_ID,
status: UpdateStatus.Pending,
due_date: Time.utc('2020-09-15T00:00:00.000Z').toDate(),
due_date: Time.utc('2020-09-01T00:00:00.000Z').toDate(),
created_at: FAKE_NOW,
updated_at: FAKE_NOW,
},
{
id: UUID,
proposal_id: PROPOSAL_ID,
status: UpdateStatus.Pending,
due_date: Time.utc('2020-10-15T00:00:00.000Z').toDate(),
due_date: Time.utc('2020-10-01T00:00:00.000Z').toDate(),
created_at: FAKE_NOW,
updated_at: FAKE_NOW,
},
{
id: UUID,
proposal_id: PROPOSAL_ID,
status: UpdateStatus.Pending,
due_date: Time.utc('2020-11-15T00:00:00.000Z').toDate(),
due_date: Time.utc('2020-11-01T00:00:00.000Z').toDate(),
created_at: FAKE_NOW,
updated_at: FAKE_NOW,
},
{
id: UUID,
proposal_id: PROPOSAL_ID,
status: UpdateStatus.Pending,
due_date: Time.utc('2020-12-15T00:00:00.000Z').toDate(),
due_date: Time.utc('2020-12-01T00:00:00.000Z').toDate(),
created_at: FAKE_NOW,
updated_at: FAKE_NOW,
},
{
id: UUID,
proposal_id: PROPOSAL_ID,
status: UpdateStatus.Pending,
due_date: Time.utc('2021-01-15T00:00:00.000Z').toDate(),
due_date: Time.utc('2021-01-01T00:00:00.000Z').toDate(),
created_at: FAKE_NOW,
updated_at: FAKE_NOW,
},
Expand All @@ -244,112 +287,6 @@ describe('UpdateModel', () => {
})
})

describe('getFirstUpdateStartingDate', () => {
const FAKE_TODAY = new Date('2020-01-07 00:01:00z')
const getSubject = (vestingStartDate: string, preferredPaymentDate: VestingStartDate) =>
UpdateModel.getFirstUpdateStartingDate(vestingStartDate, preferredPaymentDate)

beforeEach(() => {
jest.clearAllMocks()
jest.useFakeTimers()
jest.setSystemTime(FAKE_TODAY)
})

describe('when preferred payment date is on the 1st of the month', () => {
const preferredPaymentDate = VestingStartDate.First

describe('when vesting started on the 1st of the current month', () => {
const vestingStartDate = '2020-01-01 00:00:00z'

it('should return the 1st of this month', () => {
expect(getSubject(vestingStartDate, preferredPaymentDate)).toEqual(Time.utc('2020-01-01 00:00:00.000Z'))
})
})
describe('when vesting started on the 15th of the current month', () => {
const vestingStartDate = '2020-01-15 00:00:00z'

it('should return the 1st of this month', () => {
expect(getSubject(vestingStartDate, preferredPaymentDate)).toEqual(Time.utc('2020-01-01 00:00:00.000Z'))
})
})
describe('when vesting started on a random day of the current month', () => {
const vestingStartDate = '2020-01-27 00:00:00z'

it('should return the 1st of this month', () => {
expect(getSubject(vestingStartDate, preferredPaymentDate)).toEqual(Time.utc('2020-01-01 00:00:00.000Z'))
})
})
describe('when vesting starts on the 1st next month', () => {
const vestingStartDate = '2020-02-01 00:00:00z'

it('should return the 1st of next month', () => {
expect(getSubject(vestingStartDate, preferredPaymentDate)).toEqual(Time.utc('2020-02-01 00:00:00.000Z'))
})
})
describe('when vesting starts on the 15th next month', () => {
const vestingStartDate = '2020-02-15 00:00:00z'

it('should return the 1st of next month', () => {
expect(getSubject(vestingStartDate, preferredPaymentDate)).toEqual(Time.utc('2020-02-01 00:00:00.000Z'))
})
})
describe('when vesting starts on any given day of next month', () => {
const vestingStartDate = '2020-02-14 00:00:00z'

it('should return the 1st of next month', () => {
expect(getSubject(vestingStartDate, preferredPaymentDate)).toEqual(Time.utc('2020-02-01 00:00:00.000Z'))
})
})
})

describe('when preferred payment date is on the 15th of the month', () => {
const preferredPaymentDate = VestingStartDate.Fifteenth

describe('when vesting started on the 1st of the current month', () => {
const vestingStartDate = '2020-01-01 00:00:00z'

it('should return the 15th of the current month', () => {
expect(getSubject(vestingStartDate, preferredPaymentDate)).toEqual(Time.utc('2020-01-15 00:00:00.000Z'))
})
})
describe('when vesting started on the 15th of the current month', () => {
const vestingStartDate = '2020-01-15 00:00:00z'

it('should return the 15th of the current month', () => {
expect(getSubject(vestingStartDate, preferredPaymentDate)).toEqual(Time.utc('2020-01-15 00:00:00.000Z'))
})
})
describe('when vesting started on a random day of the current month', () => {
const vestingStartDate = '2020-01-27 00:00:00z'

it('should return the 15th of the current month', () => {
expect(getSubject(vestingStartDate, preferredPaymentDate)).toEqual(Time.utc('2020-01-15 00:00:00.000Z'))
})
})
describe('when vesting starts on the 1st next month', () => {
const vestingStartDate = '2020-02-01 00:00:00z'

it('should return the 15th of next month', () => {
expect(getSubject(vestingStartDate, preferredPaymentDate)).toEqual(Time.utc('2020-02-15 00:00:00.000Z'))
})
})
describe('when vesting starts on the 15th next month', () => {
const vestingStartDate = '2020-02-15 00:00:00z'

it('should return the 15th of next month', () => {
expect(getSubject(vestingStartDate, preferredPaymentDate)).toEqual(Time.utc('2020-02-15 00:00:00.000Z'))
})
})
describe('when vesting starts on any given day of the next month', () => {
const vestingStartDate = '2020-02-14 00:00:00z'

it('should return the 15th of next month', () => {
expect(getSubject(vestingStartDate, preferredPaymentDate)).toEqual(Time.utc('2020-02-15 00:00:00.000Z'))
})
})
})
})

describe('getDueDate', () => {
it('returns the same date for following month plus the index', () => {
expect(UpdateModel.getDueDate(Time.utc('2020-11-01 00:00:00.000Z'), 0)).toEqual(
Expand Down
24 changes: 5 additions & 19 deletions src/entities/Updates/model.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import crypto from 'crypto'
import { Model } from 'decentraland-gatsby/dist/entities/Database/model'

import { VestingDates } from '../../clients/VestingData'
import { type VestingInfo } from '../../clients/VestingData'
import Time from '../../utils/date/Time'
import { getMonthsBetweenDates } from '../../utils/date/getMonthsBetweenDates'
import { VestingStartDate } from '../Grant/types'

import { UpdateAttributes, UpdateStatus } from './types'

Expand All @@ -13,19 +12,12 @@ export default class UpdateModel extends Model<UpdateAttributes> {
static withTimestamps = false
static primaryKey = 'id'

static async createPendingUpdates(
proposalId: string,
vestingDates: VestingDates,
preferredVestingStartDate: VestingStartDate
) {
static async createPendingUpdates(proposalId: string, vestingContractData: VestingInfo) {
if (proposalId.length < 0) throw new Error('Unable to create updates for empty proposal id')

const now = new Date()
const updatesQuantity = this.getAmountOfUpdates(vestingDates)
const firstUpdateStartingDate = this.getFirstUpdateStartingDate(
vestingDates.vestingStartAt,
preferredVestingStartDate
)
const updatesQuantity = this.getAmountOfUpdates(vestingContractData)
const firstUpdateStartingDate = Time.utc(vestingContractData.vestingStartAt).startOf('day')

await UpdateModel.delete<UpdateAttributes>({ proposal_id: proposalId, status: UpdateStatus.Pending })

Expand All @@ -44,20 +36,14 @@ export default class UpdateModel extends Model<UpdateAttributes> {
return await this.createMany(updates)
}

public static getAmountOfUpdates(vestingDates: VestingDates) {
public static getAmountOfUpdates(vestingDates: VestingInfo) {
const exactDuration = getMonthsBetweenDates(
new Date(vestingDates.vestingStartAt),
new Date(vestingDates.vestingFinishAt)
)
return exactDuration.months + (exactDuration.extraDays > 0 ? 1 : 0)
}

public static getFirstUpdateStartingDate(vestingStartDate: string, preferredPaymentDate: VestingStartDate) {
return Time.utc(vestingStartDate)
.set('date', preferredPaymentDate === VestingStartDate.First ? 1 : 15)
.startOf('day')
}

public static getDueDate(startingDate: Time.Dayjs, index: number) {
return startingDate.add(1 + index, 'months').toDate()
}
Expand Down

0 comments on commit 5649981

Please sign in to comment.