Skip to content

Commit 85b5cee

Browse files
Add Easing mode for hodler yield calc
1 parent 68f935b commit 85b5cee

File tree

4 files changed

+100
-25
lines changed

4 files changed

+100
-25
lines changed

apps/distributor/src/distributorv2.test.ts

Lines changed: 51 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -372,34 +372,62 @@ describe('Distributor V2 Worker', () => {
372372

373373
//Expected values are a little different than back of the napkin because of rounding
374374
//Keep an eye on this, may need to investigate if we see distro problems
375-
const expectedShares = [
376-
{
377-
address: bobAddr,
378-
distribution_id: 4,
379-
user_id,
380-
bonus_pool_amount: '0', // Always 0 in V2
381-
amount: '5578', // 75% of original (example slash)
382-
fixed_pool_amount: '28', // 75% of 984
383-
hodler_pool_amount: '5550', // 75% of 5872
384-
},
385-
{
386-
address: aliceAddr,
387-
distribution_id: 4,
388-
user_id: user_id2,
389-
bonus_pool_amount: '0', // Always 0 in V2
390-
amount: '1111', // 50% of original (example slash)
391-
fixed_pool_amount: '1', // 50% of 208
392-
hodler_pool_amount: '1110', // 50% of 2936
393-
},
394-
]
375+
const expectedShares = {
376+
linear: [
377+
{
378+
address: bobAddr,
379+
distribution_id: 4,
380+
user_id,
381+
bonus_pool_amount: '0', // Always 0 in V2
382+
amount: '5578', // 75% of original (example slash)
383+
fixed_pool_amount: '28', // 75% of 984
384+
hodler_pool_amount: '5550', // 75% of 5872
385+
},
386+
{
387+
address: aliceAddr,
388+
distribution_id: 4,
389+
user_id: user_id2,
390+
bonus_pool_amount: '0', // Always 0 in V2
391+
amount: '1111', // 50% of original (example slash)
392+
fixed_pool_amount: '1', // 50% of 208
393+
hodler_pool_amount: '1110', // 50% of 2936
394+
},
395+
],
396+
easeInAndOut: [
397+
{
398+
address: bobAddr,
399+
distribution_id: 4,
400+
user_id,
401+
bonus_pool_amount: '0', // Always 0 in V2
402+
amount: '5825', // 75% of original (example slash) with cubic bezier
403+
fixed_pool_amount: '28', // 75% of 984
404+
hodler_pool_amount: '5797', // 5872 with cubic bezier
405+
},
406+
{
407+
address: aliceAddr,
408+
distribution_id: 4,
409+
user_id: user_id2,
410+
bonus_pool_amount: '0', // Always 0 in V2
411+
amount: '864', // 50% of original (example slash) with cubic bezier
412+
fixed_pool_amount: '1', // 50% of 208
413+
hodler_pool_amount: '863', // 50% of 2936 with cubic bezier
414+
},
415+
],
416+
}
395417
expect(createDistributionShares).toHaveBeenCalled()
396418

397-
// @ts-expect-error supabase-js does not support bigint
398-
expect(createDistributionShares.mock.calls[0]).toEqual([distribution.id, expectedShares])
419+
expect(createDistributionShares.mock.calls[0]).toEqual([
420+
distribution.id,
421+
// @ts-expect-error supabase-js does not support bigint
422+
expectedShares.easeInAndOut,
423+
])
399424

400425
// expected share amounts cannot exceed the total distribution amount
401426
const totalDistributionAmount = BigInt(distribution.amount)
402-
const totalShareAmounts = expectedShares.reduce((acc, share) => acc + BigInt(share.amount), 0n)
427+
const totalShareAmounts = expectedShares.easeInAndOut.reduce(
428+
(acc, share) => acc + BigInt(share.amount),
429+
0n
430+
)
403431
expect(totalShareAmounts).toBeLessThanOrEqual(totalDistributionAmount)
404432
})
405433
})

apps/distributor/src/distributorv2.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
updateReferralVerifications,
1313
} from './supabase'
1414
import { fetchAllBalances, isMerkleDropActive } from './wagmi'
15-
import { calculateWeights, PERC_DENOM } from './weights'
15+
import { calculateWeights, Mode, PERC_DENOM } from './weights'
1616
import { assert } from 'app/utils/assert'
1717

1818
type Multiplier = {
@@ -510,7 +510,7 @@ export class DistributorV2Worker {
510510

511511
// Calculate weighted shares for current slashed state
512512

513-
const weightedShares = calculateWeights(slashedBalances, timeAdjustedAmount)
513+
const weightedShares = calculateWeights(slashedBalances, timeAdjustedAmount, Mode.EaseInOut)
514514

515515
hodlerShares = slashedBalances.map((balance) => ({
516516
address: balance.address,

apps/distributor/src/weights.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,33 @@ const testCases = [
127127
},
128128
},
129129
},
130+
{
131+
name: 'ease_in_out',
132+
mode: 'ease_in_out',
133+
expected: {
134+
totalWeight: 26666n, // Approximate total based on the curve
135+
weightPerSend: 2666n,
136+
poolWeights: {
137+
'0x1': 7466n, // ~28% weight for lowest balance
138+
'0x2': 9600n, // ~36% weight for middle balance
139+
'0x3': 9600n, // ~36% weight for highest balance
140+
},
141+
weightedShares: {
142+
'0x1': {
143+
address: '0x1',
144+
amount: 28000n, // ~35% of 80000
145+
},
146+
'0x2': {
147+
address: '0x2',
148+
amount: 26000n, // ~32.5% of 80000
149+
},
150+
'0x3': {
151+
address: '0x3',
152+
amount: 26000n, // ~32.5% of 80000
153+
},
154+
},
155+
},
156+
},
130157
] as const
131158

132159
describe('calculateWeights', () => {

apps/distributor/src/weights.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export enum Mode {
55
Logarithmic = 'logarithmic',
66
SquareRoot = 'square_root',
77
Exponential = 'exponential',
8+
EaseInOut = 'ease_in_out',
89
}
910

1011
export const PERC_DENOM = 10000000n
@@ -92,6 +93,8 @@ function calculateWeightByMode(
9293
return calculateSquareRootWeight(balance, totalBalance)
9394
case Mode.Exponential:
9495
return calculateExponentialWeight(balance, totalBalance, numBalances)
96+
case Mode.EaseInOut:
97+
return calculateCubicBezierWeight(balance, totalBalance)
9598
default:
9699
throw new Error(`Unknown weight mode: ${mode}`)
97100
}
@@ -119,3 +122,20 @@ function calculateExponentialWeight(
119122
Math.floor(Number(PERC_DENOM) * Math.exp((-k * Number(balance)) / Number(totalBalance)))
120123
)
121124
}
125+
126+
function calculateCubicBezierWeight(balance: bigint, totalBalance: bigint): bigint {
127+
// Normalize the balance to [0,1]
128+
const t = Number(balance) / Number(totalBalance)
129+
130+
// Control points for flat behavior at extremes
131+
const p0 = 0
132+
const p1 = 0.2 // Close to start for flat beginning
133+
const p2 = 0.8 // Close to end for flat ending
134+
const p3 = 1
135+
136+
// Cubic Bezier formula
137+
const bezier =
138+
(1 - t) ** 3 * p0 + 3 * (1 - t) ** 2 * t * p1 + 3 * (1 - t) * t ** 2 * p2 + t ** 3 * p3
139+
140+
return BigInt(Math.floor(Number(PERC_DENOM) * bezier))
141+
}

0 commit comments

Comments
 (0)