The Resolution
After the pool is closed, the liquidity ladder allocations will be decided through a fair algorithm
Non-technical explanation of the algorithm
The smart contract generates a seed.
Using that seed, the algorithm will select participants in a random order and will allocate their funds on each level. It will start from the Level 1 to the Level 10 (most profitable to less profitable)
If a user allocation is higher than the rest of the space on a given level, his allocation will be distributed in the next most profitable level.
Technical explanation of the algorithm
We have created a script to replicate the logic of the ladder results.
Everyone can execute it independently to check that the election has been done following a fair algorithm.
import BigNumber from "bignumber.js";
// Configure BigNumber for high precision calculations
BigNumber.config({
DECIMAL_PLACES: 10,
ROUNDING_MODE: BigNumber.ROUND_HALF_UP,
});
// Precision-free allocation system using BigNumber for exact calculations
// Level capacities are rounded to 0.1 SOL increments
// Ladder constants (in SOL)
const LADDER_REFERENCE_CAP_SOL = new BigNumber(2000);
const LEVELS_SLOTS_SOL = {
L1: new BigNumber(100),
L2: new BigNumber(138),
L3: new BigNumber(147),
L4: new BigNumber(424),
L5: new BigNumber(396),
L6: new BigNumber(342),
L7: new BigNumber(246),
L8: new BigNumber(90),
L9: new BigNumber(63),
L10: new BigNumber(54),
};
// Round to 0.1 SOL precision (only for level capacities)
function roundTo01SOL(amount: BigNumber): BigNumber {
return amount.decimalPlaces(1, BigNumber.ROUND_HALF_UP);
}
// Generate the random number from the seed
function createLCG(seed: number) {
let state = seed >>> 0;
const a = 1664525;
const c = 1013904223;
const m = 2 ** 32;
return function rand(): number {
state = (a * state + c) % m;
return state / m; // [0, 1)
};
}
function generateParticipantAllocations(
userWallets: string[],
userParticipations: Record<string, BigNumber>, // in SOL
seed: number,
totalLiquidity: BigNumber // in SOL
): Record<
string, // Level
Record<string, BigNumber> // User -> Amount in SOL
> {
let currentLevel = 1;
const allocations: Record<string, Record<string, BigNumber>> = {};
// Initialize all 10 levels
for (let i = 1; i <= 10; i++) {
allocations[i] = {};
}
const unpickedUsers = [...userWallets];
// Create a copy of participations to modify
const remainingParticipations: Record<string, BigNumber> = {};
for (const [user, amount] of Object.entries(userParticipations)) {
remainingParticipations[user] = amount;
}
// Pick a random user wallet until we have picked all of them
const rand = createLCG(seed);
// Calculate overcap multiplier
const overcapMultiplier = totalLiquidity.dividedBy(LADDER_REFERENCE_CAP_SOL);
// Pre-calculate level capacities (only round these, not user participations)
const levelCapacities: Record<number, BigNumber> = {};
for (let i = 1; i <= 10; i++) {
const baseCapacity =
LEVELS_SLOTS_SOL[`L${i}` as keyof typeof LEVELS_SLOTS_SOL];
levelCapacities[i] = roundTo01SOL(
baseCapacity.multipliedBy(overcapMultiplier)
);
}
let currentLevelCapacity = levelCapacities[currentLevel];
while (unpickedUsers.length > 0 && currentLevel <= 10) {
const idx = Math.floor(rand() * unpickedUsers.length);
const user = unpickedUsers.splice(idx, 1)[0];
const participation = remainingParticipations[user!];
if (participation.isGreaterThan(currentLevelCapacity)) {
// User's participation exceeds current level capacity
if (currentLevelCapacity.isGreaterThan(0)) {
allocations[currentLevel][user!] = currentLevelCapacity;
// Put the user back with remaining participation
const remainingParticipation =
participation.minus(currentLevelCapacity);
remainingParticipations[user!] = remainingParticipation;
unpickedUsers.push(user!);
currentLevelCapacity = new BigNumber(0);
} else {
// No capacity left, put user back
unpickedUsers.push(user!);
}
// Move to next level
currentLevel++;
if (currentLevel <= 10) {
currentLevelCapacity = levelCapacities[currentLevel];
}
} else {
// User's participation fits in current level
allocations[currentLevel][user!] = participation;
currentLevelCapacity = currentLevelCapacity.minus(participation);
// Check if level is full and move to next level
if (currentLevelCapacity.isLessThanOrEqualTo(0)) {
currentLevel++;
if (currentLevel <= 10) {
currentLevelCapacity = levelCapacities[currentLevel];
}
}
}
}
return allocations;
}
Here's an example usage to replicate an scenario
// Generate random participations using BigNumber (no rounding on participations)
function generateRandomParticipations(
n: number,
totalParticipation: BigNumber // in SOL
): Record<string, BigNumber> {
if (n <= 0) return {};
// Generate random weights
const weights = Array.from({ length: n }, () => Math.random());
const weightSum = weights.reduce((a, b) => a + b, 0);
const result: Record<string, BigNumber> = {};
let allocatedSum = new BigNumber(0);
// Allocate proportionally (no rounding on participations)
for (let i = 0; i < n - 1; i++) {
const participation = new BigNumber(weights[i])
.dividedBy(weightSum)
.multipliedBy(totalParticipation);
result[`user${i + 1}`] = participation;
allocatedSum = allocatedSum.plus(participation);
}
// Give the last user exactly what's remaining to ensure perfect sum
result[`user${n}`] = totalParticipation.minus(allocatedSum);
return result;
}
// Example usage
const totalSOL = new BigNumber(2222);
const randomParticipations = generateRandomParticipations(1000, totalSOL);
console.log("Sample participations:");
Object.keys(randomParticipations)
.slice(0, 5)
.forEach((key) => {
console.log(`${key}: ${randomParticipations[key].toString()} SOL`);
});
const sum = Object.values(randomParticipations).reduce(
(a, b) => a.plus(b),
new BigNumber(0)
);
console.log(`Total participations sum: ${sum.toString()} SOL`);
const allocations = generateParticipantAllocations(
Object.keys(randomParticipations),
randomParticipations,
1293892349853495, // public seed
totalSOL
);
// Print total sol per level
console.log("Total SOL per level:");
for (const [level, users] of Object.entries(allocations)) {
const totalInLevel = Object.values(users).reduce(
(a, b) => a.plus(b),
new BigNumber(0)
);
const userCount = Object.keys(users).length;
console.log(
`Level ${level}: ${totalInLevel.toString()} SOL (${userCount} users)`
);
}
Last updated