From 564998152bdceb91a89555a2088dd96e354e090e Mon Sep 17 00:00:00 2001 From: Andy Espagnolo Date: Mon, 11 Mar 2024 16:12:30 -0300 Subject: [PATCH] feat: use vesting start date as update initial due date reference (#1708) --- src/back/routes/proposal.ts | 2 +- src/entities/Updates/model.test.ts | 205 ++++++++++------------------- src/entities/Updates/model.ts | 24 +--- 3 files changed, 77 insertions(+), 154 deletions(-) diff --git a/src/back/routes/proposal.ts b/src/back/routes/proposal.ts index 996d9078d..8e81dff4a 100644 --- a/src/back/routes/proposal.ts +++ b/src/back/routes/proposal.ts @@ -564,7 +564,7 @@ export async function updateProposalStatus(req: WithAuth { 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( @@ -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, @@ -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, @@ -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)) @@ -188,13 +231,13 @@ 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, }, @@ -202,7 +245,7 @@ describe('UpdateModel', () => { 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, }, @@ -210,7 +253,7 @@ describe('UpdateModel', () => { 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, }, @@ -218,7 +261,7 @@ describe('UpdateModel', () => { 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, }, @@ -226,7 +269,7 @@ describe('UpdateModel', () => { 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, }, @@ -234,7 +277,7 @@ describe('UpdateModel', () => { 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, }, @@ -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( diff --git a/src/entities/Updates/model.ts b/src/entities/Updates/model.ts index ac793c42c..e7b35d245 100644 --- a/src/entities/Updates/model.ts +++ b/src/entities/Updates/model.ts @@ -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' @@ -13,19 +12,12 @@ export default class UpdateModel extends Model { 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({ proposal_id: proposalId, status: UpdateStatus.Pending }) @@ -44,7 +36,7 @@ export default class UpdateModel extends Model { 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) @@ -52,12 +44,6 @@ export default class UpdateModel extends Model { 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() }