Calculate the tax, and show a table of all transactions

This commit is contained in:
Austin Chen 2022-03-31 23:31:48 -07:00
parent 7ffef0294a
commit 432575ae41
2 changed files with 132 additions and 52 deletions

View File

@ -5,7 +5,11 @@ import { User } from '../../common/user'
import { formatMoney } from '../../common/util/format'
import { useUser } from '../hooks/use-user'
import { buyLeaderboardSlot } from '../lib/firebase/api-call'
import { Transaction, writeTransaction } from '../lib/firebase/transactions'
import {
SlotData,
Transaction,
writeTransaction,
} from '../lib/firebase/transactions'
import { AmountInput } from './amount-input'
import { Avatar } from './avatar'
import { Col } from './layout/col'
@ -18,10 +22,11 @@ export function Manaboard(props: {
title: string
users: User[]
values: number[]
createdTimes: number[]
className?: string
}) {
// TODO: Ideally, highlight your own entry on the leaderboard
let { title, users, className, values } = props
let { title, users, className, values, createdTimes } = props
const [expanded, setExpanded] = useState(false)
if (!expanded) {
@ -67,6 +72,7 @@ export function Manaboard(props: {
title={`${title}`}
holder={user}
value={values[index]}
createdTime={createdTimes[index]}
/>
</Row>
</td>
@ -96,8 +102,9 @@ export function BuySlotModal(props: {
holder: User
slot: number
value: number
createdTime: number
}) {
const { slot, title, holder, value } = props
const { slot, title, holder, value, createdTime } = props
const user = useUser()
const [open, setOpen] = useState(false)
@ -109,10 +116,29 @@ export function BuySlotModal(props: {
}
}, [user])
const onBuy = async () => {
// Feel free to change this. - James
const slotId = `${title}-${slot}`
await buyLeaderboardSlot({ slotId, reassessValue: newValue })
// const onBuy = async () => {
// // Feel free to change this. - James
// const slotId = `${title}-${slot}`
// await buyLeaderboardSlot({ slotId, reassessValue: newValue })
// }
async function onBuy() {
if (user) {
// Start transactions, but don't block
const buyData = { slot, newValue, message }
const buyTxn = buyTransaction({
buyer: user,
holder,
amount: value,
buyData,
})
await Promise.all([
writeTransaction(buyTxn),
writeTransaction(taxTransaction({ holder, slot, value, createdTime })),
])
setOpen(false)
}
}
return (
@ -148,22 +174,7 @@ export function BuySlotModal(props: {
label={ENV_CONFIG.moneyMoniker}
/>
<button
className="btn btn-primary"
onClick={() => {
if (user) {
buySlot({
holder,
buyer: user,
amount: value,
slot,
message,
newValue,
})
setOpen(false)
}
}}
>
<button className="btn btn-primary" onClick={onBuy}>
Buy Slot ({formatMoney(value)})
</button>
<div className="-mt-2 text-sm">
@ -181,19 +192,16 @@ export function BuySlotModal(props: {
)
}
async function buySlot(options: {
holder: User
function buyTransaction(options: {
buyer: User
holder: User
buyData: SlotData
amount: number
slot: number
message: string
newValue: number
}) {
const { holder, buyer, amount, slot, message, newValue } = options
const createdTime = Date.now()
const buyTransaction: Transaction = {
}): Transaction {
const { buyer, holder, buyData, amount } = options
return {
id: '',
createdTime,
createdTime: Date.now(),
fromId: buyer.id,
fromName: buyer.name,
@ -205,20 +213,30 @@ async function buySlot(options: {
toUsername: holder.username,
toAvatarUrl: holder.avatarUrl,
amount: amount,
amount,
category: 'BUY_LEADERBOARD_SLOT',
description: `${buyer.name} bought a slot from ${holder.name}`,
data: {
slot,
message,
newValue,
},
data: buyData,
}
}
const feeTransaction: Transaction = {
function taxTransaction(options: {
holder: User
slot: number
value: number
createdTime: number
}): Transaction {
const { holder, slot, value, createdTime } = options
const APRIL_FOOLS_PT = 1648796400000
const elapsedMs = Date.now() - (createdTime || APRIL_FOOLS_PT)
const elapsedHours = elapsedMs / 1000 / 60 / 60
const tax = elapsedHours * (value / 10)
return {
id: '',
createdTime,
createdTime: Date.now(),
fromId: holder.id,
fromName: holder.name,
@ -231,16 +249,12 @@ async function buySlot(options: {
toUsername: 'ManifoldMarkets',
toAvatarUrl: 'https://manifold.markets/logo-bg-white.png',
amount: 10, // TODO: Calculate fee
amount: tax,
category: 'LEADERBOARD_TAX',
description: `${holder.name} paid M$ 10 in fees`,
data: {
slot,
},
}
await Promise.all([
writeTransaction(buyTransaction),
writeTransaction(feeTransaction),
])
}

View File

@ -11,6 +11,11 @@ import { Title } from '../components/title'
import { useTransactions } from '../hooks/use-transactions'
import { SlotData, Transaction } from '../lib/firebase/transactions'
import { Grid, _ as r } from 'gridjs-react'
import 'gridjs/dist/theme/mermaid.css'
import { html } from 'gridjs'
import dayjs from 'dayjs'
export const getStaticProps = fromPropz(getStaticPropz)
export async function getStaticPropz() {
const [topTraders, topCreators] = await Promise.all([
@ -94,11 +99,12 @@ function Explanation() {
</div>
)
}
// TODOs
// [ ] Correctly calculate tax
// [ ] List history of purchases at the bottom
// [ ] Restrict to at most buying one slot per user?
// [ ] Deduct amount from user's balance, either in UX or for real
// [ ] Draw attention to leaderboard
// [ ] Show total returned to Manifold
// [ ] Restrict to at most buying one slot per user?
export default function Manaboards(props: {
topTraders: User[]
topCreators: User[]
@ -112,6 +118,7 @@ export default function Manaboards(props: {
const values = Array.from(Array(topTraders.length).keys())
.map((i) => i + 1)
.reverse()
const createdTimes = new Array(topTraders.length).fill(0)
// Find the most recent purchases of each slot, and replace the entries in topTraders
const transactions = useTransactions() ?? []
@ -124,6 +131,7 @@ export default function Manaboards(props: {
const slot = data.slot
topTraders[slot - 1] = buyer
values[slot - 1] = data.newValue
createdTimes[slot - 1] = txn.createdTime
}
}
@ -161,10 +169,68 @@ export default function Manaboards(props: {
</p>
</div>
<Col className="mt-6 items-center gap-10">
<Manaboard title="" users={topTraders} values={values} />
<Col className="mt-6 gap-10">
<Manaboard
title=""
users={topTraders}
values={values}
createdTimes={createdTimes}
/>
{/* <Manaboard title="🏅 Top creators" users={topCreators} /> */}
<div className="text-sm">
<Title text={'Transaction log'} />
<TransactionsTable txns={_.reverse(sortedTxns)} />
</div>
</Col>
</Page>
)
}
function TransactionsTable(props: { txns: Transaction[] }) {
const { txns } = props
return (
<Grid
data={txns}
search={true}
// sort={true}
pagination={{
enabled: true,
limit: 25,
}}
columns={[
{
id: 'data',
name: 'Slot',
formatter: (cell) => (cell as SlotData).slot,
},
{
id: 'category',
name: 'Type',
formatter: (cell) =>
cell === 'BUY_LEADERBOARD_SLOT' ? 'Buy' : 'Tax',
},
{
id: 'amount',
name: 'Amount',
formatter: (cell) => formatMoney(cell as number),
},
{
id: 'fromUsername',
name: 'From',
},
{ id: 'toUsername', name: 'To' },
{
id: 'createdTime',
name: 'Time',
formatter: (cell) =>
html(
`<span class="whitespace-nowrap">${dayjs(cell as number).format(
'h:mma'
)}</span>`
),
},
]}
/>
)
}