Compare commits

..

No commits in common. "fd2008e5d1a2a8521a330a097b5aaf51bba6ce3d" and "be20500c06480ca0115432b6bb91d3a669e55282" have entirely different histories.

11 changed files with 146 additions and 57 deletions

View File

@ -27,7 +27,7 @@ async function getActiveRewards (models) {
return await models.$queryRaw`
SELECT
(sum(total) / 1000)::INT as total,
date_trunc('day', (now() AT TIME ZONE 'America/Chicago') + interval '1 day') AT TIME ZONE 'America/Chicago' as time,
date_trunc('month', (now() AT TIME ZONE 'America/Chicago') + interval '1 month') 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,6 +36,10 @@ 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
@ -75,8 +79,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() && 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]).getTime() > new Date('2024-03-01').getTime()) {
// after 3/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' } })
}
@ -155,8 +159,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('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`
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`
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 (
<>
{Number(props.formatted.days) > 0
{props.formatted.days
? ` ${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

@ -186,7 +186,7 @@ export function MeDropdown ({ me, dropNavKey }) {
<Link href='/wallet' passHref legacyBehavior>
<Dropdown.Item eventKey='wallet'>wallet</Dropdown.Item>
</Link>
<Link href='/satistics?inc=invoice,withdrawal,stacked,spent' passHref legacyBehavior>
<Link href='/satistics/history?inc=invoice,withdrawal,stacked,spent' passHref legacyBehavior>
<Dropdown.Item eventKey='satistics'>satistics</Dropdown.Item>
</Link>
<Dropdown.Divider />

View File

@ -64,7 +64,7 @@ export default function OffCanvas ({ me, dropNavKey }) {
<Link href='/wallet' passHref legacyBehavior>
<Dropdown.Item eventKey='wallet'>wallet</Dropdown.Item>
</Link>
<Link href='/satistics?inc=invoice,withdrawal,stacked,spent' passHref legacyBehavior>
<Link href='/satistics/history/?inc=invoice,withdrawal,stacked,spent' passHref legacyBehavior>
<Dropdown.Item eventKey='satistics'>satistics</Dropdown.Item>
</Link>
<Dropdown.Divider />

View File

@ -1,22 +1,101 @@
export const proportions = [
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
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
]

View File

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

View File

@ -7,7 +7,7 @@ import PageLoading from '@/components/page-loading'
import dynamic from 'next/dynamic'
import { numWithUnits } from '@/lib/format'
import { UsageHeader } from '@/components/usage-header'
import { SatisticsHeader } from '..'
import { SatisticsHeader } from '../history'
import { WhenComposedChartSkeleton, WhenAreaChartSkeleton } from '@/components/charts-skeletons'
import OverlayTrigger from 'react-bootstrap/OverlayTrigger'
import Tooltip from 'react-bootstrap/Tooltip'

View File

@ -177,7 +177,7 @@ export function SatisticsHeader () {
activeKey={activeKey}
>
<Nav.Item>
<Link href='/satistics?inc=invoice,withdrawal,stacked,spent' passHref legacyBehavior>
<Link href='/satistics/history?inc=invoice,withdrawal,stacked,spent' passHref legacyBehavior>
<Nav.Link eventKey='history'>history</Nav.Link>
</Link>
</Nav.Item>
@ -207,7 +207,7 @@ export default function Satistics ({ ssrData }) {
}
const incstr = [...inc].join(',')
router.push(`/satistics?inc=${incstr}`)
router.push(`/satistics/history?inc=${incstr}`)
}
function included (filter) {

View File

@ -7,6 +7,7 @@ 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,
@ -29,6 +30,14 @@ 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

@ -1,15 +0,0 @@
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,17 +4,18 @@ 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 = 1_000_000_000
const TOTAL_UPPER_BOUND_MSATS = 10000000000
export async function earn ({ name }) {
// grab a greedy connection
const models = new PrismaClient()
try {
// compute how much sn earned yesterday
// compute how much sn earned got the month
const [{ sum: sumDecimal }] = await models.$queryRaw`
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')`
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'))`
// 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)
@ -51,9 +52,11 @@ export async function earn ({ name }) {
// get earners { userId, id, type, rank, proportion }
const earners = await models.$queryRaw`
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}))
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
ORDER BY proportion DESC
LIMIT 100`
@ -66,7 +69,14 @@ export async function earn ({ name }) {
const notifications = {}
for (const [i, earner] of earners.entries()) {
const earnings = Math.floor(parseFloat(proportions[i] * sum))
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)))
}
total += earnings
if (total > sum) {
console.log(name, 'total exceeds sum', total, '>', sum)