Skip to content

Commit

Permalink
added ixo reward program
Browse files Browse the repository at this point in the history
  • Loading branch information
mccallofthewild committed Oct 12, 2021
1 parent 726d189 commit 0ae236d
Show file tree
Hide file tree
Showing 12 changed files with 295 additions and 91 deletions.
2 changes: 1 addition & 1 deletion js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
},
"scripts": {
"client": "PORT=3001 FAST_REFRESH=false react-scripts start",
"server": "node -r dotenv/config --trace-warnings --max-old-space-size=13958 server 2>&1 | tee /tmp/cryptoecon.log",
"server": "node -r dotenv/config --trace-warnings --max-old-space-size=33958 server 2>&1 | tee /tmp/cryptoecon.log",
"dev": "tsnd -r dotenv/config --max-old-space-size=16384 ./server",
"test-process": "node -r dotenv/config --trace-warnings --max-old-space-size=16384 ./server/worker/process.childprocess.test.js",
"refresh-lm-snapshot": "source .env && curl -H 'Content-Type: application/json' -H \"$HEADER_SECRET\" -X POST -d '{\"query\": \"query GetSnapshot { snapshots_new(limit: 1 order_by: {id: desc}) { snapshot_data } }\"}' $SNAPSHOT_URL -o ./snapshots/snapshot_lm_latest.trash.json",
Expand Down
16 changes: 15 additions & 1 deletion js/server/config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
function createConfig({
startsAt,
durationInWeeks,
weeksToTotalMaturity,
intervalDurationMinutes,
initialRowan,
initialRewardMultiplier,
Expand Down Expand Up @@ -58,7 +59,9 @@ function createConfig({
DEPOSITS_ALLOWED_DURATION_MS,
MULTIPLIER_MATURITY:
REWARD_ACCRUAL_DURATION_MS / 1000 / 60 / EVENT_INTERVAL_MINUTES, // 6 weeks in in 200minute intervals,
NUMBER_OF_INTERVALS_TO_RUN: REWARD_ACCRUAL_DURATION_INTERVAL_COUNT * 2, // duration of bucket drain + duration to latest possible multiplier maturity
NUMBER_OF_INTERVALS_TO_RUN:
REWARD_ACCRUAL_DURATION_INTERVAL_COUNT *
(weeksToTotalMaturity / durationInWeeks), // duration of bucket drain + duration to latest possible multiplier maturity
REWARD_ACCRUAL_DURATION_INTERVAL_COUNT,
INITIAL_REWARD_MULTIPLIER: initialRewardMultiplier,
};
Expand All @@ -70,20 +73,31 @@ module.exports = {
initialRowan: 10_000_000,
startsAt: '2021-08-24T20:06:15.000Z',
durationInWeeks: 6,
weeksToTotalMaturity: 12,
intervalDurationMinutes: 200,
initialRewardMultiplier: 0.25,
}),
harvest: createConfig({
initialRowan: 40_000_000,
startsAt: '2021-10-04T00:00:00.000Z',
durationInWeeks: 6,
weeksToTotalMaturity: 6.1,
intervalDurationMinutes: 59,
initialRewardMultiplier: 1,
}),
bonus_v1: createConfig({
initialRowan: 1_000_000,
startsAt: '2021-10-05T19:00:00.000Z',
durationInWeeks: 2,
weeksToTotalMaturity: 2.1,
intervalDurationMinutes: 60,
initialRewardMultiplier: 1,
}),
bonus_v1_ixo: createConfig({
initialRowan: 500_000,
startsAt: '2021-10-12T13:29:01.255Z',
durationInWeeks: 2,
weeksToTotalMaturity: 2.1,
intervalDurationMinutes: 60,
initialRewardMultiplier: 1,
}),
Expand Down
13 changes: 13 additions & 0 deletions js/server/core/load/loadLiquidityMinersSnapshot.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
const { TESTNET } = require('../../constants/snapshot-source-names');
const slonik = require('slonik');
const { getDatabase } = require('./utils/getDatabase');
const lmHarvestStartingState = require('./lm-harvest-starting-state.json');
/*
WARNING: DO NOT ADD MORE QUERIES OR FIELDS TO THE GRAPHQL QUERY.
QUERIES ARE CACHED USING THE LENGTH OF THE TEXT CONTENT OF THE RESPONSE OBJECT
Expand Down Expand Up @@ -88,6 +89,18 @@ const getSQLQueryByNetwork = (network, rewardProgram) => {
for (let snapshot of snapshots_new) {
const liquidityByTokens = snapshot.snapshot_data[snapshot.address];
for (let token in liquidityByTokens) {
let startingUserState = 0;
const startingPoolState = lmHarvestStartingState[token];
if (
!startingPoolState &&
Object.values(liquidityByTokens).filter((v) => v.length > 0)
.length > 2
) {
debugger;
}
if (startingPoolState) {
startingUserState = startingPoolState[snapshot.address] || 0;
}
let liquidityDeltaEvents = liquidityByTokens[token];
let total = 0;
for (let deltaEvent of liquidityDeltaEvents) {
Expand Down
4 changes: 3 additions & 1 deletion js/server/core/process/augmentVSData.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,9 @@ exports.augmentVSData = (
const userAtTimestamp = timestamp.users[user.address] || new User();
if (userAtTimestamp.totalAccruedCommissionsAndClaimableRewards) {
accum[user.address] =
userAtTimestamp.totalAccruedCommissionsAndClaimableRewards;
userAtTimestamp.totalAccruedCommissionsAndClaimableRewards +
userAtTimestamp.dispensed +
userAtTimestamp.claimedCommissionsAndRewardsAwaitingDispensation;
}
return accum;
}, {});
Expand Down
115 changes: 113 additions & 2 deletions js/server/core/transform/lm-util.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ const { getTimeIndex } = require('../../util/getTimeIndex');
const { DelegateEvent } = require('../types');
const moment = require('moment');
const fetch = require('cross-fetch').fetch;
const lmHarvestStartingState = require('../load/lm-harvest-starting-state.json');

global.ACCOUNT_FOR_INITIAL_POOL_STATE = true;
const getLMTimeseriesFinalIndex = (snapshotData) => {
// Snapshot timeseries (generated by Vanir) overshoots by 1 extra interval. Account for this.
const finalIndex = [
Expand Down Expand Up @@ -50,14 +52,114 @@ function remapLMAddresses(addresses, deltaCoeff, rewardProgram) {
EVENT_INTERVAL_MINUTES,
amount: interval.delta, //* deltaCoeff,
delegateAddress: address,
token: token,
rawTimestamp: interval.unix_timestamp * 1000,
});
})
.filter((e) => e.amount !== 0);
}).filter((events) => events.length !== 0);
return addressTokenEvents;
});

const rawEvents = _.flattenDeep(mapped);
let rawEvents = _.flattenDeep(mapped);

if (global.ACCOUNT_FOR_INITIAL_POOL_STATE) {
rawEvents = rawEvents
.sort((a, b) => a.amount - b.amount)
.sort((a, b) => a.rawTimestamp - b.rawTimestamp);
let rawTimestampGroupedEvents = _.groupBy(rawEvents, 'rawTimestamp');
_.mapValues(rawTimestampGroupedEvents, (timeIntervalEvents) => {
return timeIntervalEvents.map((event) => {
if (event.amount < 0 && rewardProgram === 'harvest') {
const subtractMaxFromUserPool = (token) => {
// initial token balances for all users
const tokenBalances = lmHarvestStartingState[token];
// initial token balance for current user
const remainingInitialRemovableBalance = tokenBalances
? tokenBalances[event.delegateAddress] || 0
: 0;
// normalized initial token balance for user
const convertedVal = +remainingInitialRemovableBalance / 10 ** 18;
// subtract (add the negative) the withdrawal amount from the initial amount
let amountRemainingAfterRemoval = convertedVal + event.amount;
// if the result is negative, the withdrawal exceeded the initial amount
if (amountRemainingAfterRemoval < 0) {
// if the token existed at the starting time
if (lmHarvestStartingState[token])
// the initial amount has now been zero'ed out
lmHarvestStartingState[token][event.delegateAddress] = 0;
// the remaining withdrawal = the amount that it exceeded the initial amount
event.amount = amountRemainingAfterRemoval;
} else {
// if the token existed at program genesis
if (lmHarvestStartingState[token])
// if there is a positive amount remaining, set to that positive amount.
lmHarvestStartingState[token][event.delegateAddress] = BigInt(
Math.floor(amountRemainingAfterRemoval * 10 ** 18)
).toString();
// the withdrawal is covered by the initial amount
event.amount = 0;
}
};
// if the event doesn't have a pool
if (event.token === 'rowan') {
// guess that the pool is the first non-rowan token that this user also removed
const externalAssetEvents =
timeIntervalEvents.length > 1
? timeIntervalEvents.filter(
(ev) =>
ev.token !== 'rowan' &&
ev.amount < 0 &&
ev.delegateAddress === event.delegateAddress
)
: [];
// if there is a non-rowan withdrawal
if (externalAssetEvents.length) {
// subtract this rowan amount from those
while (event.amount < 0 && externalAssetEvents.length) {
if (
event.delegateAddress ===
'sif1vcd6gx3jqng2m7tw6k7e766skayv5rkhn3levl'
) {
// debugger;
}
subtractMaxFromUserPool(externalAssetEvents.pop().token);
}
} else {
for (let tokenKey in lmHarvestStartingState) {
if (
lmHarvestStartingState[tokenKey][event.delegateAddress] > 0
) {
subtractMaxFromUserPool(tokenKey);
}
if (
event.delegateAddress ===
'sif1vcd6gx3jqng2m7tw6k7e766skayv5rkhn3levl'
) {
// debugger;
}
if (event.amount === 0) {
break;
}
}
}
} else {
subtractMaxFromUserPool(event.token);
}
}
});
});

const regendudeinitial = Object.entries(lmHarvestStartingState)
.map(([k, v]) => {
return v['sif1vcd6gx3jqng2m7tw6k7e766skayv5rkhn3levl'];
})
.filter((v) => !!v);
const regendude = rawEvents.filter(
(ev) =>
ev.delegateAddress === 'sif1vcd6gx3jqng2m7tw6k7e766skayv5rkhn3levl'
);
}
let allTimeIntervalEvents = _.groupBy(rawEvents, 'timestamp');
allTimeIntervalEvents = _.mapValues(
allTimeIntervalEvents,
Expand All @@ -82,12 +184,21 @@ function remapLMAddresses(addresses, deltaCoeff, rewardProgram) {
return _.mapValues(
timeIntervalAddressEvents,
(addressEvents, address) => {
if (global.ACCOUNT_FOR_INITIAL_POOL_STATE) {
addressEvents = addressEvents.sort((a, b) => {
return a.rawTimestamp - b.rawTimestamp;
});
}
return [
DelegateEvent.fromJSON({
timestamp: parseInt(timeInterval),
delegateAddress: address,
amount: addressEvents.reduce((accum, addressEvent) => {
return accum + parseFloat(addressEvent.amount);
const nextVal = accum + parseFloat(addressEvent.amount);
if (global.ACCOUNT_FOR_INITIAL_POOL_STATE) {
return Math.max(0, nextVal);
}
return nextVal;
}, 0),
}),
];
Expand Down
12 changes: 7 additions & 5 deletions js/server/core/types/DelegateEvent.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
class DelegateEvent {
constructor () {
constructor() {
this.timestamp = 0;
this.delegateAddress = null;
this.validatorRewardAddress = null;
this.validatorStakeAddress = null;
this.commission = 0;
this.amount = 0;
this.token = '';
this.rawTimestamp = 0;
}

clearTimestamp () {
clearTimestamp() {
this.timestamp = undefined;
}

static fromJSON (props) {
static fromJSON(props) {
return Object.assign(new this(), props);
}

cloneWith (props) {
cloneWith(props) {
return Object.assign(Object.assign(new DelegateEvent(), this), props);
}
}

module.exports = {
DelegateEvent
DelegateEvent,
};
15 changes: 13 additions & 2 deletions js/server/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,19 @@ const server = new ApolloServer({
distributionPattern: 'LINEAR',
},
{
displayName: `Sif's Bonus Pool`,
description: `Immediately earn and claim rewards by pooling JUNØ.`,
displayName: `Sif's IXO Bonus Pool`,
description: `Earn Rowan every 60 minutes and claim rewards immediately by pooling IXO.`,
rewardProgramType: 'lm',
rewardProgramName: 'bonus_v1_ixo',
incentivizedPoolSymbols: ['ixo'],
documentationURL:
'https://docs.sifchain.finance/resources/rewards-programs',
isUniversal: false,
distributionPattern: 'LINEAR',
},
{
displayName: `Sif's Junø Bonus Pool`,
description: `Earn Rowan every 60 minutes and claim rewards immediately by pooling JUNØ.`,
rewardProgramType: 'lm',
rewardProgramName: 'bonus_v1',
incentivizedPoolSymbols: ['juno'],
Expand Down
29 changes: 25 additions & 4 deletions js/server/worker/process.childprocess.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const { BackgroundProcessor } = require('./process.childprocess');
const _ = require('lodash');
const { MAINNET } = require('../constants/snapshot-source-names');
const { GET_LM_CURRENT_APY_SUMMARY } = require('../constants/action-names');
const { getTimeIndex } = require('../util/getTimeIndex');

// simple test setup
const describe = async (description, describer) => {
Expand Down Expand Up @@ -39,11 +40,29 @@ const describe = async (description, describer) => {
console.groupEnd();
};

const runTests = (type, parsedData, network) => {
const runTests = (type, parsedData, network, programName) => {
const finalGlobalTimestampState =
parsedData.processedData[parsedData.processedData.length - 1];
const users = Object.values(finalGlobalTimestampState.users);

const totalValuePerUser = Object.entries(
parsedData.processedData[getTimeIndex('now', programName)].users
).reduce((prev, [addr, curr]) => {
if (!curr) return prev;
prev[addr] =
curr.totalAccruedCommissionsAndClaimableRewards +
curr.claimedCommissionsAndRewardsAwaitingDispensation +
curr.forfeitedCommissions +
curr.forfeited +
curr.dispensed;
return prev;
}, {});

require('fs').writeFileSync(
'./userExitStates.new.json',
Buffer.from(JSON.stringify(totalValuePerUser, null, 2))
);

const totalPoolDominanceRatio = _.sum(
_.flattenDeep(
_.map(users, (u) => u.tickets.map((t) => t.poolDominanceRatio))
Expand Down Expand Up @@ -88,17 +107,19 @@ const runTests = (type, parsedData, network) => {
};

const bp = new BackgroundProcessor();
// const bp2 = new BackgroundProcessor();
const programName = 'bonus_v1_ixo';
bp.reloadAndReprocessSnapshots({
network: MAINNET,
rewardProgram: 'bonus_v1',
rewardProgram: programName,
})
// test reload caching
// .then(async () => bp.reloadAndReprocessSnapshots({ network: MAINNET }))
.then(async () => {
await runTests('lm', bp.lmDataParsed, MAINNET);
await runTests('lm', bp.lmDataParsed, MAINNET, programName);
console.log(
bp.dispatch(GET_LM_CURRENT_APY_SUMMARY, {
programName: 'bonus_v1',
programName: programName,
})
);
// await runTests('vs', bp.vsDataParsed, MAINNET);
Expand Down
Loading

0 comments on commit 0ae236d

Please sign in to comment.