diff --git a/src/app/collective-rewards/allocations/context/AllocationsContext.tsx b/src/app/collective-rewards/allocations/context/AllocationsContext.tsx index 5105687f..3cd28e25 100644 --- a/src/app/collective-rewards/allocations/context/AllocationsContext.tsx +++ b/src/app/collective-rewards/allocations/context/AllocationsContext.tsx @@ -10,6 +10,7 @@ import { Address, zeroAddress } from 'viem' import { useAccount } from 'wagmi' import { createActions } from './allocationsActions' import { useGetBackerRewards } from '../hooks/useBuildersWithBackerRewardPercentage' +import { validateAllocationsState } from './utils' export type Allocations = { [K: Address]: bigint @@ -146,18 +147,6 @@ export const AllocationsContextProvider: FC<{ children: ReactNode }> = ({ childr }, {} as Builders) }, [rawBuilders, backerRewards]) - /** - * Retrieval functions - */ - const getBuilder = useCallback((address: Address) => builders[address], [builders]) - - const isValidState = useCallback(() => { - const { balance, cumulativeAllocation, amountToAllocate } = backer - // FIXME: verify that the initial state has changed compared to what we want to save - - return cumulativeAllocation <= balance && amountToAllocate <= balance - }, [backer]) - /** * Reactive state updates */ @@ -240,6 +229,22 @@ export const AllocationsContextProvider: FC<{ children: ReactNode }> = ({ childr } }, [rawAllocations, rawBuilders, totalOnchainAllocation, selections, votingPower, isContextLoading]) + /** + * Getters + */ + const getBuilder = useCallback((address: Address) => builders[address], [builders]) + + const isValidState = useCallback( + () => + validateAllocationsState({ + backer, + initialAllocations: initialState.allocations, + currentAllocations: allocations, + totalOnchainAllocation: totalOnchainAllocation as bigint, + }), + [backer, allocations, totalOnchainAllocation, initialState.allocations], + ) + const state: State = useMemo(() => { return { selections, diff --git a/src/app/collective-rewards/allocations/context/utils.ts b/src/app/collective-rewards/allocations/context/utils.ts new file mode 100644 index 00000000..2ac8a6e9 --- /dev/null +++ b/src/app/collective-rewards/allocations/context/utils.ts @@ -0,0 +1,29 @@ +import { Address } from 'viem' +import { Allocations, Backer } from './AllocationsContext' + +export type ValidateAllocationsStateParams = { + backer: Pick + initialAllocations: Allocations + currentAllocations: Allocations + totalOnchainAllocation: bigint +} +export const validateAllocationsState = ({ + backer, + initialAllocations, + currentAllocations, + totalOnchainAllocation = 0n, +}: ValidateAllocationsStateParams): boolean => { + const { balance, cumulativeAllocation, amountToAllocate } = backer + + if (cumulativeAllocation > balance || amountToAllocate > balance) { + return false + } + + if (totalOnchainAllocation === cumulativeAllocation) { + return Object.entries(initialAllocations).some(([builder, allocation]) => { + return allocation !== currentAllocations[builder as Address] + }) + } + + return true +} diff --git a/src/app/collective-rewards/allocations/context/utis.test.ts b/src/app/collective-rewards/allocations/context/utis.test.ts new file mode 100644 index 00000000..87497096 --- /dev/null +++ b/src/app/collective-rewards/allocations/context/utis.test.ts @@ -0,0 +1,83 @@ +import { describe, expect, it } from 'vitest' +import { Allocations, Backer } from './AllocationsContext' +import { validateAllocationsState, ValidateAllocationsStateParams } from './utils' + +describe('Allocations context utils', () => { + describe('validateAllocationsState', () => { + const backer: Backer = { + balance: 1000n, + cumulativeAllocation: 10n, + amountToAllocate: 10n, + allocationsCount: 2, + } + const initialAllocations: Allocations = { + '0x1': 3n, + } + const currentAllocations: Allocations = { + '0x1': 5n, + '0x2': 5n, + } + const inputs: ValidateAllocationsStateParams = { + backer, + initialAllocations, + currentAllocations, + totalOnchainAllocation: 3n, + } + + it('should return false if cumulative allocation > balance', () => { + const isValidState = validateAllocationsState({ + ...inputs, + backer: { + amountToAllocate: 1n, + cumulativeAllocation: 10n, + balance: 5n, + }, + }) + + expect(isValidState).toBe(false) + }) + + it('should return false if amount to allocate > balance', () => { + const isValidState = validateAllocationsState({ + ...inputs, + backer: { + cumulativeAllocation: 1n, + amountToAllocate: 10n, + balance: 5n, + }, + }) + + expect(isValidState).toBe(false) + }) + + it('should return true if totalOnchainAllocation and cumulativeAllocation are different', () => { + const isValidState = validateAllocationsState({ + ...inputs, + totalOnchainAllocation: 1n, + backer: { + ...backer, + cumulativeAllocation: 2n, + }, + }) + + expect(isValidState).toBe(true) + }) + + it('should return false if initialAllocations and currentAllocations are the same', () => { + const isValidState = validateAllocationsState({ + ...inputs, + totalOnchainAllocation: 1n, + backer: { + ...backer, + cumulativeAllocation: 1n, + }, + initialAllocations, + currentAllocations: { + ...initialAllocations, + }, + }) + + expect(isValidState).toBe(false) + }) + }) +})