reintroduce daily rewards (#1134)

* reintroduce daily rewards

* update reward sponsor

* daily rewards countdown

* update rewards job schedule
This commit is contained in:
Keyan 2024-05-01 09:30:36 -05:00 committed by GitHub
parent 54bbb0cc52
commit fd2008e5d1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 52 additions and 141 deletions

View File

@ -27,7 +27,7 @@ async function getActiveRewards (models) {
return await models.$queryRaw`
SELECT
(sum(total) / 1000)::INT as total,
date_trunc('month', (now() AT TIME ZONE 'America/Chicago') + interval '1 month') AT TIME ZONE 'America/Chicago' as time,
date_trunc('day', (now() AT TIME ZONE 'America/Chicago') + interval '1 day') AT TIME ZONE 'America/Chicago' as time,
json_build_array(
json_build_object('name', 'donations', 'value', (sum(donations) / 1000)::INT),
json_build_object('name', 'fees', 'value', (sum(fees) / 1000)::INT),
@ -36,10 +36,6 @@ async function getActiveRewards (models) {
json_build_object('name', 'anon''s stack', 'value', (sum(anons_stack) / 1000)::INT)
) AS sources
FROM (
(SELECT *
FROM rewards_days
WHERE rewards_days.t >= date_trunc('month', now() AT TIME ZONE 'America/Chicago'))
UNION ALL
(SELECT * FROM rewards_today)
UNION ALL
(SELECT * FROM
@ -79,8 +75,8 @@ async function getRewards (when, models) {
throw new GraphQLError('bad date range', { extensions: { code: 'BAD_USER_INPUT' } })
}
if (new Date(when[0]).getTime() > new Date('2024-03-01').getTime()) {
// after 3/1/2024, we reward monthly on the 1st
if (new Date(when[0]).getTime() > new Date('2024-03-01').getTime() && new Date(when[0]).getTime() < new Date('2024-05-02').getTime()) {
// after 3/1/2024 and until 5/1/2024, we reward monthly on the 1st
if (new Date(when[0]).getUTCDate() !== 1) {
throw new GraphQLError('invalid reward date', { extensions: { code: 'BAD_USER_INPUT' } })
}
@ -159,8 +155,8 @@ export default {
leaderboard: async (parent, args, { models, ...context }) => {
// get to and from using postgres because it's easier to do there
const [{ to, from }] = await models.$queryRaw`
SELECT date_trunc('month', (now() AT TIME ZONE 'America/Chicago')) AT TIME ZONE 'America/Chicago' as from,
(date_trunc('month', (now() AT TIME ZONE 'America/Chicago')) AT TIME ZONE 'America/Chicago') + interval '1 month - 1 second' as to`
SELECT date_trunc('day', (now() AT TIME ZONE 'America/Chicago')) AT TIME ZONE 'America/Chicago' as from,
(date_trunc('day', (now() AT TIME ZONE 'America/Chicago')) AT TIME ZONE 'America/Chicago') + interval '1 day - 1 second' as to`
return await topUsers(parent, { when: 'custom', to: new Date(to).getTime().toString(), from: new Date(from).getTime().toString(), limit: 100 }, { models, ...context })
}
},

View File

@ -37,7 +37,7 @@ export function CompactLongCountdown (props) {
{...props} formatter={props => {
return (
<>
{props.formatted.days
{Number(props.formatted.days) > 0
? ` ${props.formatted.days}d ${props.formatted.hours}h ${props.formatted.minutes}m ${props.formatted.seconds}s`
: ` ${props.formatted.hours}:${props.formatted.minutes}:${props.formatted.seconds}`}
</>

View File

@ -1,101 +1,22 @@
export const proportions = [
0.08122,
0.07,
0.06,
0.05,
0.045,
0.04,
0.035,
0.03,
0.029,
0.028,
0.027,
0.026,
0.025,
0.024,
0.023,
0.022,
0.021,
0.02,
0.019,
0.018,
0.017,
0.016,
0.015,
0.014,
0.013,
0.012,
0.011,
0.01,
0.009,
0.0083,
0.0077,
0.0074,
0.0071,
0.0068,
0.0065,
0.0063,
0.0061,
0.0059,
0.0057,
0.0055,
0.0053,
0.0051,
0.0049,
0.0047,
0.0045,
0.0043,
0.0041,
0.0039,
0.0037,
0.0036,
0.0035,
0.0034,
0.0033,
0.0032,
0.0031,
0.003,
0.0029,
0.0028,
0.0027,
0.0026,
0.0025,
0.0024,
0.0023,
0.0022,
0.0021,
0.002,
0.0019,
0.0018,
0.0017,
0.0016,
0.0015,
0.0014,
0.0013,
0.0012,
0.0011,
0.001,
0.0009,
0.0008,
0.00078,
0.00076,
0.00074,
0.00072,
0.0007,
0.00068,
0.00066,
0.00064,
0.00062,
0.0006,
0.00058,
0.00056,
0.00054,
0.00052,
0.0005,
0.00048,
0.00046,
0.00044,
0.00042,
0.0004,
0.00038
0.07575508, 0.06619601, 0.05835029, 0.05183037, 0.0463526,
0.04170543, 0.0377285, 0.03429843, 0.03131904, 0.02871442,
0.02642405, 0.02439916, 0.0226001, 0.0209944, 0.01955519,
0.01826016, 0.0170906, 0.01603075, 0.01506725, 0.01418874,
0.01338546, 0.01264904, 0.01197222, 0.01134872, 0.01077306,
0.01024046, 0.0097467, 0.00928808, 0.00886135, 0.00846359,
0.00809223, 0.00774497, 0.00741977, 0.0071148, 0.00682839,
0.00655908, 0.00630551, 0.00606648, 0.0058409, 0.00562778,
0.0054262, 0.00523534, 0.00505446, 0.00488287, 0.00471994,
0.0045651, 0.00441782, 0.0042776, 0.00414401, 0.00401663,
0.00389509, 0.00377902, 0.00366811, 0.00356204, 0.00346055,
0.00336337, 0.00327026, 0.003181, 0.00309537, 0.00301318,
0.00293424, 0.0028584, 0.00278548, 0.00271534, 0.00264783,
0.00258284, 0.00252022, 0.00245988, 0.00240169, 0.00234556,
0.00229139, 0.0022391, 0.00218858, 0.00213978, 0.00209259,
0.00204697, 0.00200283, 0.00196012, 0.00191877, 0.00187873,
0.00183994, 0.00180234, 0.0017659, 0.00173056, 0.00169628,
0.00166301, 0.00163072, 0.00159937, 0.00156893, 0.00153935,
0.00151061, 0.00148267, 0.00145551, 0.00142909, 0.00140339,
0.00137839, 0.00135405, 0.00133035, 0.00130728, 0.00128481
]

View File

@ -108,11 +108,10 @@ export default function Rewards ({ ssrData }) {
if (!dat) return <PageLoading />
function EstimatedReward ({ rank }) {
const totalRest = total - 1000000
return (
<div className='text-muted fst-italic'>
<small>
<span>estimated reward: {numWithUnits(rank === 1 ? 1000000 : Math.floor(totalRest * proportions[rank - 2]))}</span>
<span>estimated reward: {numWithUnits(Math.floor(total * proportions[rank - 1]))}</span>
</small>
</div>
)
@ -120,10 +119,9 @@ export default function Rewards ({ ssrData }) {
return (
<Layout footerLinks>
<Link className='text-reset align-self-center' href='https://btcplusplus.dev/conf/atx24?ref=stackernews' target='_blank' rel='noreferrer'>
<Link className='text-reset align-self-center' href='/items/141924'>
<h4 className='pt-3 text-start text-reset' style={{ lineHeight: 1.5, textDecoration: 'underline' }}>
bitcoin++ is a developer-focused conference series.
<div>Join us in Austin May 1-4 for a deep dive into bitcoin script.</div>
rewards are sponsored by ... we are hiring
</h4>
</Link>
<Row className='pb-3'>

View File

@ -7,7 +7,6 @@ import Snl from '@/components/snl'
import { useQuery } from '@apollo/client'
import PageLoading from '@/components/page-loading'
import TerritoryHeader from '@/components/territory-header'
import Link from 'next/link'
export const getServerSideProps = getGetServerSideProps({
query: SUB_ITEMS,
@ -30,14 +29,6 @@ export default function Sub ({ ssrData }) {
<>
<Snl />
</>)}
<small className='pb-3 px-1 text-muted' style={{ marginTop: '-0.25rem', lineHeight: 1.5 }}>
<Link className='text-reset' href='/rewards' style={{ textDecoration: 'underline' }}>
Million Sat Madness
</Link> is sponsored by{' '}
<Link className='text-reset' href='https://btcplusplus.dev/conf/atx24?ref=stackernews' target='_blank' rel='noreferrer' style={{ textDecoration: 'underline' }}>
the Bitcoin++ Conference in Austin May 1-4
</Link>
</small>
<Items ssrData={ssrData} variables={variables} />
</Layout>
)

View File

@ -0,0 +1,15 @@
CREATE OR REPLACE FUNCTION reschedule_earn_job()
RETURNS INTEGER
LANGUAGE plpgsql
AS $$
DECLARE
BEGIN
UPDATE pgboss.schedule set cron = '0 0 * * *' WHERE name = 'earn';
return 0;
EXCEPTION WHEN OTHERS THEN
return 0;
END;
$$;
SELECT reschedule_earn_job();
DROP FUNCTION IF EXISTS reschedule_earn_job;

View File

@ -4,18 +4,17 @@ import { PrismaClient } from '@prisma/client'
import { proportions } from '@/lib/madness.js'
import { SN_NO_REWARDS_IDS } from '@/lib/constants.js'
const TOTAL_UPPER_BOUND_MSATS = 10000000000
const TOTAL_UPPER_BOUND_MSATS = 1_000_000_000
export async function earn ({ name }) {
// grab a greedy connection
const models = new PrismaClient()
try {
// compute how much sn earned got the month
// compute how much sn earned yesterday
const [{ sum: sumDecimal }] = await models.$queryRaw`
SELECT coalesce(sum(total), 0) as sum
FROM rewards_days
WHERE date_trunc('month', rewards_days.t) = date_trunc('month', (now() AT TIME ZONE 'America/Chicago' - interval '1 month'))`
SELECT total as sum
FROM rewards(now() AT TIME ZONE 'America/Chicago' - interval '1 day', now() AT TIME ZONE 'America/Chicago' - interval '1 day', '1 day'::INTERVAL, 'day')`
// XXX primsa will return a Decimal (https://mikemcl.github.io/decimal.js)
// because sum of a BIGINT returns a NUMERIC type (https://www.postgresql.org/docs/13/functions-aggregate.html)
@ -52,11 +51,9 @@ export async function earn ({ name }) {
// get earners { userId, id, type, rank, proportion }
const earners = await models.$queryRaw`
SELECT id AS "userId", sum(proportion) as proportion, ROW_NUMBER() OVER (ORDER BY sum(proportion) DESC) as rank
FROM user_values_days
WHERE date_trunc('month', user_values_days.t) = date_trunc('month', (now() AT TIME ZONE 'America/Chicago' - interval '1 month'))
AND NOT (id = ANY (${SN_NO_REWARDS_IDS}))
GROUP BY id
SELECT id AS "userId", proportion, ROW_NUMBER() OVER (ORDER BY proportion DESC) as rank
FROM user_values_days(now() AT TIME ZONE 'America/Chicago' - interval '1 day', now() AT TIME ZONE 'America/Chicago' - interval '1 day', '1 day'::INTERVAL, 'day')
WHERE NOT (id = ANY (${SN_NO_REWARDS_IDS}))
ORDER BY proportion DESC
LIMIT 100`
@ -69,14 +66,7 @@ export async function earn ({ name }) {
const notifications = {}
for (const [i, earner] of earners.entries()) {
let earnings = 0
if (i === 0) {
// top earner gets 1m sats
earnings = 1_000_000_000
} else {
// everyone else gets a proportion of the total
earnings = Math.floor(parseFloat(proportions[i - 1] * (sum - 1_000_000_000)))
}
const earnings = Math.floor(parseFloat(proportions[i] * sum))
total += earnings
if (total > sum) {
console.log(name, 'total exceeds sum', total, '>', sum)