sats to msats
This commit is contained in:
		
							parent
							
								
									249bcce04c
								
							
						
					
					
						commit
						1bf747c7c0
					
				@ -33,10 +33,10 @@ export default {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      return await models.$queryRaw(
 | 
					      return await models.$queryRaw(
 | 
				
			||||||
        `SELECT date_trunc('month', "ItemAct".created_at) AS time,
 | 
					        `SELECT date_trunc('month', "ItemAct".created_at) AS time,
 | 
				
			||||||
        sum(CASE WHEN act = 'STREAM' THEN "ItemAct".sats ELSE 0 END) as jobs,
 | 
					        floor(sum(CASE WHEN act = 'STREAM' THEN "ItemAct".msats ELSE 0 END)/1000) as jobs,
 | 
				
			||||||
        sum(CASE WHEN act IN ('VOTE', 'POLL') AND "Item"."userId" = "ItemAct"."userId" THEN "ItemAct".sats ELSE 0 END) as fees,
 | 
					        floor(sum(CASE WHEN act IN ('VOTE', 'POLL') AND "Item"."userId" = "ItemAct"."userId" THEN "ItemAct".msats ELSE 0 END)/1000) as fees,
 | 
				
			||||||
        sum(CASE WHEN act = 'BOOST' THEN "ItemAct".sats ELSE 0 END) as boost,
 | 
					        floor(sum(CASE WHEN act = 'BOOST' THEN "ItemAct".msats ELSE 0 END)/1000) as boost,
 | 
				
			||||||
        sum(CASE WHEN act = 'TIP' THEN "ItemAct".sats ELSE 0 END) as tips
 | 
					        floor(sum(CASE WHEN act = 'TIP' THEN "ItemAct".msats ELSE 0 END)/1000) as tips
 | 
				
			||||||
        FROM "ItemAct"
 | 
					        FROM "ItemAct"
 | 
				
			||||||
        JOIN "Item" on "ItemAct"."itemId" = "Item".id
 | 
					        JOIN "Item" on "ItemAct"."itemId" = "Item".id
 | 
				
			||||||
        WHERE date_trunc('month', now_utc()) <> date_trunc('month',  "ItemAct".created_at)
 | 
					        WHERE date_trunc('month', now_utc()) <> date_trunc('month',  "ItemAct".created_at)
 | 
				
			||||||
@ -60,17 +60,17 @@ export default {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
    stackedGrowth: async (parent, args, { models }) => {
 | 
					    stackedGrowth: async (parent, args, { models }) => {
 | 
				
			||||||
      return await models.$queryRaw(
 | 
					      return await models.$queryRaw(
 | 
				
			||||||
        `SELECT time, sum(airdrop) as rewards, sum(post) as posts, sum(comment) as comments
 | 
					        `SELECT time, floor(sum(airdrop)/1000) as rewards, floor(sum(post)/1000) as posts, floor(sum(comment)/1000) as comments
 | 
				
			||||||
        FROM
 | 
					        FROM
 | 
				
			||||||
        ((SELECT date_trunc('month', "ItemAct".created_at) AS time, 0 as airdrop,
 | 
					        ((SELECT date_trunc('month', "ItemAct".created_at) AS time, 0 as airdrop,
 | 
				
			||||||
          CASE WHEN "Item"."parentId" IS NULL THEN 0 ELSE "ItemAct".sats END as comment,
 | 
					          CASE WHEN "Item"."parentId" IS NULL THEN 0 ELSE "ItemAct".msats END as comment,
 | 
				
			||||||
          CASE WHEN "Item"."parentId" IS NULL THEN "ItemAct".sats ELSE 0 END as post
 | 
					          CASE WHEN "Item"."parentId" IS NULL THEN "ItemAct".msats ELSE 0 END as post
 | 
				
			||||||
          FROM "ItemAct"
 | 
					          FROM "ItemAct"
 | 
				
			||||||
          JOIN "Item" on "ItemAct"."itemId" = "Item".id AND "Item"."userId" <> "ItemAct"."userId"
 | 
					          JOIN "Item" on "ItemAct"."itemId" = "Item".id AND "Item"."userId" <> "ItemAct"."userId"
 | 
				
			||||||
          WHERE date_trunc('month', now_utc()) <> date_trunc('month', "ItemAct".created_at) AND
 | 
					          WHERE date_trunc('month', now_utc()) <> date_trunc('month', "ItemAct".created_at) AND
 | 
				
			||||||
          "ItemAct".act IN ('VOTE', 'TIP'))
 | 
					          "ItemAct".act IN ('VOTE', 'TIP'))
 | 
				
			||||||
        UNION ALL
 | 
					        UNION ALL
 | 
				
			||||||
        (SELECT date_trunc('month', created_at) AS time, msats / 1000 as airdrop, 0 as post, 0 as comment
 | 
					        (SELECT date_trunc('month', created_at) AS time, msats as airdrop, 0 as post, 0 as comment
 | 
				
			||||||
          FROM "Earn"
 | 
					          FROM "Earn"
 | 
				
			||||||
          WHERE date_trunc('month', now_utc()) <> date_trunc('month', created_at))) u
 | 
					          WHERE date_trunc('month', now_utc()) <> date_trunc('month', created_at))) u
 | 
				
			||||||
        GROUP BY time
 | 
					        GROUP BY time
 | 
				
			||||||
@ -121,10 +121,10 @@ export default {
 | 
				
			|||||||
    spentWeekly: async (parent, args, { models }) => {
 | 
					    spentWeekly: async (parent, args, { models }) => {
 | 
				
			||||||
      const [stats] = await models.$queryRaw(
 | 
					      const [stats] = await models.$queryRaw(
 | 
				
			||||||
        `SELECT json_build_array(
 | 
					        `SELECT json_build_array(
 | 
				
			||||||
          json_build_object('name', 'jobs', 'value', sum(CASE WHEN act = 'STREAM' THEN "ItemAct".sats ELSE 0 END)),
 | 
					          json_build_object('name', 'jobs', 'value', floor(sum(CASE WHEN act = 'STREAM' THEN "ItemAct".msats ELSE 0 END)/1000)),
 | 
				
			||||||
          json_build_object('name', 'fees', 'value', sum(CASE WHEN act in ('VOTE', 'POLL') AND "Item"."userId" = "ItemAct"."userId" THEN "ItemAct".sats ELSE 0 END)),
 | 
					          json_build_object('name', 'fees', 'value', floor(sum(CASE WHEN act in ('VOTE', 'POLL') AND "Item"."userId" = "ItemAct"."userId" THEN "ItemAct".msats ELSE 0 END)/1000)),
 | 
				
			||||||
          json_build_object('name', 'boost', 'value', sum(CASE WHEN act = 'BOOST' THEN "ItemAct".sats ELSE 0 END)),
 | 
					          json_build_object('name', 'boost', 'value',floor(sum(CASE WHEN act = 'BOOST' THEN "ItemAct".msats ELSE 0 END)/1000)),
 | 
				
			||||||
          json_build_object('name', 'tips', 'value', sum(CASE WHEN act = 'TIP' THEN "ItemAct".sats ELSE 0 END))) as array
 | 
					          json_build_object('name', 'tips', 'value', floor(sum(CASE WHEN act = 'TIP' THEN "ItemAct".msats ELSE 0 END)/1000))) as array
 | 
				
			||||||
        FROM "ItemAct"
 | 
					        FROM "ItemAct"
 | 
				
			||||||
        JOIN "Item" on "ItemAct"."itemId" = "Item".id
 | 
					        JOIN "Item" on "ItemAct"."itemId" = "Item".id
 | 
				
			||||||
        WHERE "ItemAct".created_at >= now_utc() - interval '1 week'`)
 | 
					        WHERE "ItemAct".created_at >= now_utc() - interval '1 week'`)
 | 
				
			||||||
@ -134,20 +134,20 @@ export default {
 | 
				
			|||||||
    stackedWeekly: async (parent, args, { models }) => {
 | 
					    stackedWeekly: async (parent, args, { models }) => {
 | 
				
			||||||
      const [stats] = await models.$queryRaw(
 | 
					      const [stats] = await models.$queryRaw(
 | 
				
			||||||
        `SELECT json_build_array(
 | 
					        `SELECT json_build_array(
 | 
				
			||||||
          json_build_object('name', 'rewards', 'value', sum(airdrop)),
 | 
					          json_build_object('name', 'rewards', 'value', floor(sum(airdrop)/1000)),
 | 
				
			||||||
          json_build_object('name', 'posts', 'value', sum(post)),
 | 
					          json_build_object('name', 'posts', 'value', floor(sum(post)/1000)),
 | 
				
			||||||
          json_build_object('name', 'comments', 'value', sum(comment))
 | 
					          json_build_object('name', 'comments', 'value', floor(sum(comment)/1000))
 | 
				
			||||||
        ) as array
 | 
					        ) as array
 | 
				
			||||||
        FROM
 | 
					        FROM
 | 
				
			||||||
        ((SELECT 0 as airdrop,
 | 
					        ((SELECT 0 as airdrop,
 | 
				
			||||||
          CASE WHEN "Item"."parentId" IS NULL THEN 0 ELSE "ItemAct".sats END as comment,
 | 
					          CASE WHEN "Item"."parentId" IS NULL THEN 0 ELSE "ItemAct".msats END as comment,
 | 
				
			||||||
          CASE WHEN "Item"."parentId" IS NULL THEN "ItemAct".sats ELSE 0 END as post
 | 
					          CASE WHEN "Item"."parentId" IS NULL THEN "ItemAct".msats ELSE 0 END as post
 | 
				
			||||||
          FROM "ItemAct"
 | 
					          FROM "ItemAct"
 | 
				
			||||||
          JOIN "Item" on "ItemAct"."itemId" = "Item".id AND "Item"."userId" <> "ItemAct"."userId"
 | 
					          JOIN "Item" on "ItemAct"."itemId" = "Item".id AND "Item"."userId" <> "ItemAct"."userId"
 | 
				
			||||||
          WHERE  "ItemAct".created_at >= now_utc() - interval '1 week' AND
 | 
					          WHERE  "ItemAct".created_at >= now_utc() - interval '1 week' AND
 | 
				
			||||||
          "ItemAct".act IN ('VOTE', 'TIP'))
 | 
					          "ItemAct".act IN ('VOTE', 'TIP'))
 | 
				
			||||||
        UNION ALL
 | 
					        UNION ALL
 | 
				
			||||||
        (SELECT msats / 1000 as airdrop, 0 as post, 0 as comment
 | 
					        (SELECT msats as airdrop, 0 as post, 0 as comment
 | 
				
			||||||
          FROM "Earn"
 | 
					          FROM "Earn"
 | 
				
			||||||
          WHERE  created_at >= now_utc() - interval '1 week')) u`)
 | 
					          WHERE  created_at >= now_utc() - interval '1 week')) u`)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -8,6 +8,7 @@ import {
 | 
				
			|||||||
  BOOST_MIN, ITEM_SPAM_INTERVAL, MAX_POLL_NUM_CHOICES,
 | 
					  BOOST_MIN, ITEM_SPAM_INTERVAL, MAX_POLL_NUM_CHOICES,
 | 
				
			||||||
  MAX_TITLE_LENGTH, ITEM_FILTER_THRESHOLD, DONT_LIKE_THIS_COST
 | 
					  MAX_TITLE_LENGTH, ITEM_FILTER_THRESHOLD, DONT_LIKE_THIS_COST
 | 
				
			||||||
} from '../../lib/constants'
 | 
					} from '../../lib/constants'
 | 
				
			||||||
 | 
					import { msatsToSats } from '../../lib/format'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function comments (me, models, id, sort) {
 | 
					async function comments (me, models, id, sort) {
 | 
				
			||||||
  let orderBy
 | 
					  let orderBy
 | 
				
			||||||
@ -74,7 +75,7 @@ async function topOrderClause (sort, me, models) {
 | 
				
			|||||||
    case 'comments':
 | 
					    case 'comments':
 | 
				
			||||||
      return 'ORDER BY ncomments DESC'
 | 
					      return 'ORDER BY ncomments DESC'
 | 
				
			||||||
    case 'sats':
 | 
					    case 'sats':
 | 
				
			||||||
      return 'ORDER BY sats DESC'
 | 
					      return 'ORDER BY msats DESC'
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      return await topOrderByWeightedSats(me, models)
 | 
					      return await topOrderByWeightedSats(me, models)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -690,6 +691,12 @@ export default {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  Item: {
 | 
					  Item: {
 | 
				
			||||||
 | 
					    sats: async (item, args, { models }) => {
 | 
				
			||||||
 | 
					      return msatsToSats(item.msats)
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    commentSats: async (item, args, { models }) => {
 | 
				
			||||||
 | 
					      return msatsToSats(item.commentMsats)
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    isJob: async (item, args, { models }) => {
 | 
					    isJob: async (item, args, { models }) => {
 | 
				
			||||||
      return item.subName === 'jobs'
 | 
					      return item.subName === 'jobs'
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
@ -771,10 +778,7 @@ export default {
 | 
				
			|||||||
      return comments(me, models, item.id, 'hot')
 | 
					      return comments(me, models, item.id, 'hot')
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    upvotes: async (item, args, { models }) => {
 | 
					    upvotes: async (item, args, { models }) => {
 | 
				
			||||||
      const { sum: { sats } } = await models.itemAct.aggregate({
 | 
					      const count = await models.itemAct.count({
 | 
				
			||||||
        sum: {
 | 
					 | 
				
			||||||
          sats: true
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        where: {
 | 
					        where: {
 | 
				
			||||||
          itemId: Number(item.id),
 | 
					          itemId: Number(item.id),
 | 
				
			||||||
          userId: {
 | 
					          userId: {
 | 
				
			||||||
@ -784,12 +788,12 @@ export default {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return sats || 0
 | 
					      return count
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    boost: async (item, args, { models }) => {
 | 
					    boost: async (item, args, { models }) => {
 | 
				
			||||||
      const { sum: { sats } } = await models.itemAct.aggregate({
 | 
					      const { sum: { msats } } = await models.itemAct.aggregate({
 | 
				
			||||||
        sum: {
 | 
					        sum: {
 | 
				
			||||||
          sats: true
 | 
					          msats: true
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        where: {
 | 
					        where: {
 | 
				
			||||||
          itemId: Number(item.id),
 | 
					          itemId: Number(item.id),
 | 
				
			||||||
@ -797,7 +801,7 @@ export default {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return sats || 0
 | 
					      return (msats && msatsToSats(msats)) || 0
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    wvotes: async (item) => {
 | 
					    wvotes: async (item) => {
 | 
				
			||||||
      return item.weightedVotes - item.weightedDownVotes
 | 
					      return item.weightedVotes - item.weightedDownVotes
 | 
				
			||||||
@ -805,9 +809,9 @@ export default {
 | 
				
			|||||||
    meSats: async (item, args, { me, models }) => {
 | 
					    meSats: async (item, args, { me, models }) => {
 | 
				
			||||||
      if (!me) return 0
 | 
					      if (!me) return 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const { sum: { sats } } = await models.itemAct.aggregate({
 | 
					      const { sum: { msats } } = await models.itemAct.aggregate({
 | 
				
			||||||
        sum: {
 | 
					        sum: {
 | 
				
			||||||
          sats: true
 | 
					          msats: true
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        where: {
 | 
					        where: {
 | 
				
			||||||
          itemId: Number(item.id),
 | 
					          itemId: Number(item.id),
 | 
				
			||||||
@ -823,7 +827,7 @@ export default {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return sats || 0
 | 
					      return (msats && msatsToSats(msats)) || 0
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    meDontLike: async (item, args, { me, models }) => {
 | 
					    meDontLike: async (item, args, { me, models }) => {
 | 
				
			||||||
      if (!me) return false
 | 
					      if (!me) return false
 | 
				
			||||||
@ -1010,7 +1014,7 @@ export const SELECT =
 | 
				
			|||||||
  "Item".text, "Item".url, "Item"."userId", "Item"."fwdUserId", "Item"."parentId", "Item"."pinId", "Item"."maxBid",
 | 
					  "Item".text, "Item".url, "Item"."userId", "Item"."fwdUserId", "Item"."parentId", "Item"."pinId", "Item"."maxBid",
 | 
				
			||||||
  "Item".company, "Item".location, "Item".remote,
 | 
					  "Item".company, "Item".location, "Item".remote,
 | 
				
			||||||
  "Item"."subName", "Item".status, "Item"."uploadId", "Item"."pollCost",
 | 
					  "Item"."subName", "Item".status, "Item"."uploadId", "Item"."pollCost",
 | 
				
			||||||
  "Item".sats, "Item".ncomments, "Item"."commentSats", "Item"."lastCommentAt", "Item"."weightedVotes",
 | 
					  "Item".msats, "Item".ncomments, "Item"."commentMsats", "Item"."lastCommentAt", "Item"."weightedVotes",
 | 
				
			||||||
  "Item"."weightedDownVotes", "Item".freebie, ltree2text("Item"."path") AS "path"`
 | 
					  "Item"."weightedDownVotes", "Item".freebie, ltree2text("Item"."path") AS "path"`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function newTimedOrderByWeightedSats (me, models, num) {
 | 
					async function newTimedOrderByWeightedSats (me, models, num) {
 | 
				
			||||||
 | 
				
			|||||||
@ -106,7 +106,7 @@ export default {
 | 
				
			|||||||
        if (meFull.noteItemSats) {
 | 
					        if (meFull.noteItemSats) {
 | 
				
			||||||
          queries.push(
 | 
					          queries.push(
 | 
				
			||||||
            `(SELECT "Item".id::TEXT, MAX("ItemAct".created_at) AS "sortTime",
 | 
					            `(SELECT "Item".id::TEXT, MAX("ItemAct".created_at) AS "sortTime",
 | 
				
			||||||
              sum("ItemAct".sats) as "earnedSats", 'Votification' AS type
 | 
					              floor(sum("ItemAct".msats)/1000) as "earnedSats", 'Votification' AS type
 | 
				
			||||||
              FROM "Item"
 | 
					              FROM "Item"
 | 
				
			||||||
              JOIN "ItemAct" ON "ItemAct"."itemId" = "Item".id
 | 
					              JOIN "ItemAct" ON "ItemAct"."itemId" = "Item".id
 | 
				
			||||||
              WHERE "ItemAct"."userId" <> $1
 | 
					              WHERE "ItemAct"."userId" <> $1
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
import { AuthenticationError, UserInputError } from 'apollo-server-errors'
 | 
					import { AuthenticationError, UserInputError } from 'apollo-server-errors'
 | 
				
			||||||
import { decodeCursor, LIMIT, nextCursorEncoded } from '../../lib/cursor'
 | 
					import { decodeCursor, LIMIT, nextCursorEncoded } from '../../lib/cursor'
 | 
				
			||||||
 | 
					import { msatsToSats } from '../../lib/format'
 | 
				
			||||||
import { createMentions, getItem, SELECT, updateItem, filterClause } from './item'
 | 
					import { createMentions, getItem, SELECT, updateItem, filterClause } from './item'
 | 
				
			||||||
import serialize from './serial'
 | 
					import serialize from './serial'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -92,7 +93,7 @@ export default {
 | 
				
			|||||||
      let users
 | 
					      let users
 | 
				
			||||||
      if (sort === 'spent') {
 | 
					      if (sort === 'spent') {
 | 
				
			||||||
        users = await models.$queryRaw(`
 | 
					        users = await models.$queryRaw(`
 | 
				
			||||||
          SELECT users.*, sum("ItemAct".sats) as spent
 | 
					          SELECT users.*, floor(sum("ItemAct".msats)/1000) as spent
 | 
				
			||||||
          FROM "ItemAct"
 | 
					          FROM "ItemAct"
 | 
				
			||||||
          JOIN users on "ItemAct"."userId" = users.id
 | 
					          JOIN users on "ItemAct"."userId" = users.id
 | 
				
			||||||
          WHERE "ItemAct".created_at <= $1
 | 
					          WHERE "ItemAct".created_at <= $1
 | 
				
			||||||
@ -125,16 +126,16 @@ export default {
 | 
				
			|||||||
          LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset)
 | 
					          LIMIT ${LIMIT}`, decodedCursor.time, decodedCursor.offset)
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        users = await models.$queryRaw(`
 | 
					        users = await models.$queryRaw(`
 | 
				
			||||||
          SELECT u.id, u.name, u."photoId", sum(amount) as stacked
 | 
					          SELECT u.id, u.name, u."photoId", floor(sum(amount)/1000) as stacked
 | 
				
			||||||
          FROM
 | 
					          FROM
 | 
				
			||||||
          ((SELECT users.*, "ItemAct".sats as amount
 | 
					          ((SELECT users.*, "ItemAct".msats as amount
 | 
				
			||||||
            FROM "ItemAct"
 | 
					            FROM "ItemAct"
 | 
				
			||||||
            JOIN "Item" on "ItemAct"."itemId" = "Item".id
 | 
					            JOIN "Item" on "ItemAct"."itemId" = "Item".id
 | 
				
			||||||
            JOIN users on "Item"."userId" = users.id
 | 
					            JOIN users on "Item"."userId" = users.id
 | 
				
			||||||
            WHERE act <> 'BOOST' AND "ItemAct"."userId" <> users.id AND "ItemAct".created_at <= $1
 | 
					            WHERE act <> 'BOOST' AND "ItemAct"."userId" <> users.id AND "ItemAct".created_at <= $1
 | 
				
			||||||
            ${within('ItemAct', when)})
 | 
					            ${within('ItemAct', when)})
 | 
				
			||||||
          UNION ALL
 | 
					          UNION ALL
 | 
				
			||||||
          (SELECT users.*, "Earn".msats/1000 as amount
 | 
					          (SELECT users.*, "Earn".msats as amount
 | 
				
			||||||
            FROM "Earn"
 | 
					            FROM "Earn"
 | 
				
			||||||
            JOIN users on users.id = "Earn"."userId"
 | 
					            JOIN users on users.id = "Earn"."userId"
 | 
				
			||||||
            WHERE "Earn".msats > 0 ${within('Earn', when)})) u
 | 
					            WHERE "Earn".msats > 0 ${within('Earn', when)})) u
 | 
				
			||||||
@ -422,22 +423,22 @@ export default {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
      if (!when) {
 | 
					      if (!when) {
 | 
				
			||||||
        // forever
 | 
					        // forever
 | 
				
			||||||
        return Math.floor((user.stackedMsats || 0) / 1000)
 | 
					        return (user.stackedMsats && msatsToSats(user.stackedMsats)) || 0
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        const [{ stacked }] = await models.$queryRaw(`
 | 
					        const [{ stacked }] = await models.$queryRaw(`
 | 
				
			||||||
          SELECT sum(amount) as stacked
 | 
					          SELECT sum(amount) as stacked
 | 
				
			||||||
          FROM
 | 
					          FROM
 | 
				
			||||||
          ((SELECT sum("ItemAct".sats) as amount
 | 
					          ((SELECT sum("ItemAct".msats) as amount
 | 
				
			||||||
            FROM "ItemAct"
 | 
					            FROM "ItemAct"
 | 
				
			||||||
            JOIN "Item" on "ItemAct"."itemId" = "Item".id
 | 
					            JOIN "Item" on "ItemAct"."itemId" = "Item".id
 | 
				
			||||||
            WHERE act <> 'BOOST' AND "ItemAct"."userId" <> $2 AND "Item"."userId" = $2
 | 
					            WHERE act <> 'BOOST' AND "ItemAct"."userId" <> $2 AND "Item"."userId" = $2
 | 
				
			||||||
            AND "ItemAct".created_at >= $1)
 | 
					            AND "ItemAct".created_at >= $1)
 | 
				
			||||||
          UNION ALL
 | 
					          UNION ALL
 | 
				
			||||||
          (SELECT sum("Earn".msats/1000) as amount
 | 
					          (SELECT sum("Earn".msats) as amount
 | 
				
			||||||
            FROM "Earn"
 | 
					            FROM "Earn"
 | 
				
			||||||
            WHERE "Earn".msats > 0 AND "Earn"."userId" = $2
 | 
					            WHERE "Earn".msats > 0 AND "Earn"."userId" = $2
 | 
				
			||||||
            AND "Earn".created_at >= $1)) u`, withinDate(when), Number(user.id))
 | 
					            AND "Earn".created_at >= $1)) u`, withinDate(when), Number(user.id))
 | 
				
			||||||
        return stacked || 0
 | 
					        return (stacked && msatsToSats(stacked)) || 0
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    spent: async (user, { when }, { models }) => {
 | 
					    spent: async (user, { when }, { models }) => {
 | 
				
			||||||
@ -445,9 +446,9 @@ export default {
 | 
				
			|||||||
        return user.spent
 | 
					        return user.spent
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const { sum: { sats } } = await models.itemAct.aggregate({
 | 
					      const { sum: { msats } } = await models.itemAct.aggregate({
 | 
				
			||||||
        sum: {
 | 
					        sum: {
 | 
				
			||||||
          sats: true
 | 
					          msats: true
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        where: {
 | 
					        where: {
 | 
				
			||||||
          userId: user.id,
 | 
					          userId: user.id,
 | 
				
			||||||
@ -457,13 +458,13 @@ export default {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return sats || 0
 | 
					      return (msats && msatsToSats(msats)) || 0
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    sats: async (user, args, { models, me }) => {
 | 
					    sats: async (user, args, { models, me }) => {
 | 
				
			||||||
      if (me?.id !== user.id) {
 | 
					      if (me?.id !== user.id) {
 | 
				
			||||||
        return 0
 | 
					        return 0
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return Math.floor(user.msats / 1000.0)
 | 
					      return msatsToSats(user.msats)
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    bio: async (user, args, { models }) => {
 | 
					    bio: async (user, args, { models }) => {
 | 
				
			||||||
      return getItem(user, { id: user.bioId }, { models })
 | 
					      return getItem(user, { id: user.bioId }, { models })
 | 
				
			||||||
 | 
				
			|||||||
@ -5,6 +5,7 @@ import { decodeCursor, LIMIT, nextCursorEncoded } from '../../lib/cursor'
 | 
				
			|||||||
import lnpr from 'bolt11'
 | 
					import lnpr from 'bolt11'
 | 
				
			||||||
import { SELECT } from './item'
 | 
					import { SELECT } from './item'
 | 
				
			||||||
import { lnurlPayDescriptionHash } from '../../lib/lnurl'
 | 
					import { lnurlPayDescriptionHash } from '../../lib/lnurl'
 | 
				
			||||||
 | 
					import { msatsToSats } from '../../lib/format'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function getInvoice (parent, { id }, { me, models }) {
 | 
					export async function getInvoice (parent, { id }, { me, models }) {
 | 
				
			||||||
  if (!me) {
 | 
					  if (!me) {
 | 
				
			||||||
@ -93,7 +94,7 @@ export default {
 | 
				
			|||||||
      if (include.has('stacked')) {
 | 
					      if (include.has('stacked')) {
 | 
				
			||||||
        queries.push(
 | 
					        queries.push(
 | 
				
			||||||
          `(SELECT ('stacked' || "Item".id) as id, "Item".id as "factId", NULL as bolt11,
 | 
					          `(SELECT ('stacked' || "Item".id) as id, "Item".id as "factId", NULL as bolt11,
 | 
				
			||||||
          MAX("ItemAct".created_at) as "createdAt", sum("ItemAct".sats) * 1000 as msats,
 | 
					          MAX("ItemAct".created_at) as "createdAt", sum("ItemAct".msats) as msats,
 | 
				
			||||||
          0 as "msatsFee", NULL as status, 'stacked' as type
 | 
					          0 as "msatsFee", NULL as status, 'stacked' as type
 | 
				
			||||||
          FROM "ItemAct"
 | 
					          FROM "ItemAct"
 | 
				
			||||||
          JOIN "Item" on "ItemAct"."itemId" = "Item".id
 | 
					          JOIN "Item" on "ItemAct"."itemId" = "Item".id
 | 
				
			||||||
@ -114,7 +115,7 @@ export default {
 | 
				
			|||||||
      if (include.has('spent')) {
 | 
					      if (include.has('spent')) {
 | 
				
			||||||
        queries.push(
 | 
					        queries.push(
 | 
				
			||||||
          `(SELECT ('spent' || "Item".id) as id, "Item".id as "factId", NULL as bolt11,
 | 
					          `(SELECT ('spent' || "Item".id) as id, "Item".id as "factId", NULL as bolt11,
 | 
				
			||||||
          MAX("ItemAct".created_at) as "createdAt", sum("ItemAct".sats) * 1000 as msats,
 | 
					          MAX("ItemAct".created_at) as "createdAt", sum("ItemAct".msats) as msats,
 | 
				
			||||||
          0 as "msatsFee", NULL as status, 'spent' as type
 | 
					          0 as "msatsFee", NULL as status, 'spent' as type
 | 
				
			||||||
          FROM "ItemAct"
 | 
					          FROM "ItemAct"
 | 
				
			||||||
          JOIN "Item" on "ItemAct"."itemId" = "Item".id
 | 
					          JOIN "Item" on "ItemAct"."itemId" = "Item".id
 | 
				
			||||||
@ -254,10 +255,14 @@ export default {
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Withdrawl: {
 | 
					  Withdrawl: {
 | 
				
			||||||
    satsPaying: w => Math.floor(w.msatsPaying / 1000),
 | 
					    satsPaying: w => msatsToSats(w.msatsPaying),
 | 
				
			||||||
    satsPaid: w => Math.floor(w.msatsPaid / 1000),
 | 
					    satsPaid: w => msatsToSats(w.msatsPaid),
 | 
				
			||||||
    satsFeePaying: w => Math.floor(w.msatsFeePaying / 1000),
 | 
					    satsFeePaying: w => msatsToSats(w.msatsFeePaying),
 | 
				
			||||||
    satsFeePaid: w => Math.floor(w.msatsFeePaid / 1000)
 | 
					    satsFeePaid: w => msatsToSats(w.msatsFeePaid)
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Invoice: {
 | 
				
			||||||
 | 
					    satsReceived: i => msatsToSats(i.msatsReceived)
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Fact: {
 | 
					  Fact: {
 | 
				
			||||||
@ -271,7 +276,9 @@ export default {
 | 
				
			|||||||
        WHERE id = $1`, Number(fact.factId))
 | 
					        WHERE id = $1`, Number(fact.factId))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return item
 | 
					      return item
 | 
				
			||||||
    }
 | 
					    },
 | 
				
			||||||
 | 
					    sats: fact => msatsToSats(fact.msats),
 | 
				
			||||||
 | 
					    satsFee: fact => msatsToSats(fact.msatsFee)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -285,7 +292,7 @@ async function createWithdrawal (parent, { invoice, maxFee }, { me, models, lnd
 | 
				
			|||||||
    throw new UserInputError('could not decode invoice')
 | 
					    throw new UserInputError('could not decode invoice')
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!decoded.mtokens || Number(decoded.mtokens) <= 0) {
 | 
					  if (!decoded.mtokens || BigInt(decoded.mtokens) <= 0) {
 | 
				
			||||||
    throw new UserInputError('your invoice must specify an amount')
 | 
					    throw new UserInputError('your invoice must specify an amount')
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -21,7 +21,7 @@ export default gql`
 | 
				
			|||||||
    expiresAt: String!
 | 
					    expiresAt: String!
 | 
				
			||||||
    cancelled: Boolean!
 | 
					    cancelled: Boolean!
 | 
				
			||||||
    confirmedAt: String
 | 
					    confirmedAt: String
 | 
				
			||||||
    msatsReceived: Int
 | 
					    satsReceived: Int
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  type Withdrawl {
 | 
					  type Withdrawl {
 | 
				
			||||||
@ -29,13 +29,9 @@ export default gql`
 | 
				
			|||||||
    createdAt: String!
 | 
					    createdAt: String!
 | 
				
			||||||
    hash: String!
 | 
					    hash: String!
 | 
				
			||||||
    bolt11: String!
 | 
					    bolt11: String!
 | 
				
			||||||
    msatsPaying: Int!
 | 
					 | 
				
			||||||
    satsPaying: Int!
 | 
					    satsPaying: Int!
 | 
				
			||||||
    msatsPaid: Int
 | 
					 | 
				
			||||||
    satsPaid: Int
 | 
					    satsPaid: Int
 | 
				
			||||||
    msatsFeePaying: Int!
 | 
					 | 
				
			||||||
    satsFeePaying: Int!
 | 
					    satsFeePaying: Int!
 | 
				
			||||||
    msatsFeePaid: Int
 | 
					 | 
				
			||||||
    satsFeePaid: Int
 | 
					    satsFeePaid: Int
 | 
				
			||||||
    status: String
 | 
					    status: String
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@ -45,8 +41,8 @@ export default gql`
 | 
				
			|||||||
    factId: ID!
 | 
					    factId: ID!
 | 
				
			||||||
    bolt11: String
 | 
					    bolt11: String
 | 
				
			||||||
    createdAt: String!
 | 
					    createdAt: String!
 | 
				
			||||||
    msats: Int!
 | 
					    sats: Int!
 | 
				
			||||||
    msatsFee: Int
 | 
					    satsFee: Int
 | 
				
			||||||
    status: String
 | 
					    status: String
 | 
				
			||||||
    type: String!
 | 
					    type: String!
 | 
				
			||||||
    description: String
 | 
					    description: String
 | 
				
			||||||
 | 
				
			|||||||
@ -5,7 +5,7 @@ export function Invoice ({ invoice }) {
 | 
				
			|||||||
  let status = 'waiting for you'
 | 
					  let status = 'waiting for you'
 | 
				
			||||||
  if (invoice.confirmedAt) {
 | 
					  if (invoice.confirmedAt) {
 | 
				
			||||||
    variant = 'confirmed'
 | 
					    variant = 'confirmed'
 | 
				
			||||||
    status = `${invoice.msatsReceived / 1000} sats deposited`
 | 
					    status = `${invoice.satsReceived} sats deposited`
 | 
				
			||||||
  } else if (invoice.cancelled) {
 | 
					  } else if (invoice.cancelled) {
 | 
				
			||||||
    variant = 'failed'
 | 
					    variant = 'failed'
 | 
				
			||||||
    status = 'cancelled'
 | 
					    status = 'cancelled'
 | 
				
			||||||
 | 
				
			|||||||
@ -12,7 +12,7 @@ import React, { useEffect, useState } from 'react'
 | 
				
			|||||||
import GithubSlugger from 'github-slugger'
 | 
					import GithubSlugger from 'github-slugger'
 | 
				
			||||||
import LinkIcon from '../svgs/link.svg'
 | 
					import LinkIcon from '../svgs/link.svg'
 | 
				
			||||||
import Thumb from '../svgs/thumb-up-fill.svg'
 | 
					import Thumb from '../svgs/thumb-up-fill.svg'
 | 
				
			||||||
import {toString} from 'mdast-util-to-string'
 | 
					import { toString } from 'mdast-util-to-string'
 | 
				
			||||||
import copy from 'clipboard-copy'
 | 
					import copy from 'clipboard-copy'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function myRemarkPlugin () {
 | 
					function myRemarkPlugin () {
 | 
				
			||||||
@ -32,8 +32,6 @@ function myRemarkPlugin () {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function Heading ({ h, slugger, noFragments, topLevel, children, node, ...props }) {
 | 
					function Heading ({ h, slugger, noFragments, topLevel, children, node, ...props }) {
 | 
				
			||||||
  const [copied, setCopied] = useState(false)
 | 
					  const [copied, setCopied] = useState(false)
 | 
				
			||||||
  const [id] = useState(noFragments ? undefined : slugger.slug(toString(node).replace(/[^\w\-\s]+/gi, '')))
 | 
					  const [id] = useState(noFragments ? undefined : slugger.slug(toString(node).replace(/[^\w\-\s]+/gi, '')))
 | 
				
			||||||
@ -44,20 +42,20 @@ function Heading ({ h, slugger, noFragments, topLevel, children, node, ...props
 | 
				
			|||||||
    <div className={styles.heading}>
 | 
					    <div className={styles.heading}>
 | 
				
			||||||
      {React.createElement(h, { id, ...props }, children)}
 | 
					      {React.createElement(h, { id, ...props }, children)}
 | 
				
			||||||
      {!noFragments && topLevel &&
 | 
					      {!noFragments && topLevel &&
 | 
				
			||||||
      <a className={`${styles.headingLink} ${copied ? styles.copied : ''}`} href={`#${id}`}>
 | 
					        <a className={`${styles.headingLink} ${copied ? styles.copied : ''}`} href={`#${id}`}>
 | 
				
			||||||
        <Icon
 | 
					          <Icon
 | 
				
			||||||
          onClick={() => {
 | 
					            onClick={() => {
 | 
				
			||||||
            const location = new URL(window.location)
 | 
					              const location = new URL(window.location)
 | 
				
			||||||
            location.hash = `${id}`
 | 
					              location.hash = `${id}`
 | 
				
			||||||
            copy(location.href)
 | 
					              copy(location.href)
 | 
				
			||||||
            setTimeout(() => setCopied(false), 1500)
 | 
					              setTimeout(() => setCopied(false), 1500)
 | 
				
			||||||
            setCopied(true)
 | 
					              setCopied(true)
 | 
				
			||||||
          }}
 | 
					            }}
 | 
				
			||||||
          width={18}
 | 
					            width={18}
 | 
				
			||||||
          height={18}
 | 
					            height={18}
 | 
				
			||||||
          className='fill-grey'
 | 
					            className='fill-grey'
 | 
				
			||||||
        />
 | 
					          />
 | 
				
			||||||
      </a>}
 | 
					        </a>}
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -66,7 +64,7 @@ export default function Text ({ topLevel, noFragments, nofollow, children }) {
 | 
				
			|||||||
  // all the reactStringReplace calls are to facilitate search highlighting
 | 
					  // all the reactStringReplace calls are to facilitate search highlighting
 | 
				
			||||||
  const slugger = new GithubSlugger()
 | 
					  const slugger = new GithubSlugger()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const HeadingWrapper = (props) => Heading({ topLevel, slugger, noFragments, ...props})
 | 
					  const HeadingWrapper = (props) => Heading({ topLevel, slugger, noFragments, ...props })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <div className={styles.text}>
 | 
					    <div className={styles.text}>
 | 
				
			||||||
@ -108,10 +106,10 @@ export default function Text ({ topLevel, noFragments, nofollow, children }) {
 | 
				
			|||||||
            // map: fix any highlighted links
 | 
					            // map: fix any highlighted links
 | 
				
			||||||
            children = children?.map(e =>
 | 
					            children = children?.map(e =>
 | 
				
			||||||
              typeof e === 'string'
 | 
					              typeof e === 'string'
 | 
				
			||||||
              ? reactStringReplace(e, /:high\[([^\]]+)\]/g, (match, i) => {
 | 
					                ? reactStringReplace(e, /:high\[([^\]]+)\]/g, (match, i) => {
 | 
				
			||||||
                  return <mark key={`mark-${match}-${i}`}>{match}</mark>
 | 
					                    return <mark key={`mark-${match}-${i}`}>{match}</mark>
 | 
				
			||||||
                })
 | 
					                  })
 | 
				
			||||||
              : e)
 | 
					                : e)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return (
 | 
					            return (
 | 
				
			||||||
              /*  eslint-disable-next-line */
 | 
					              /*  eslint-disable-next-line */
 | 
				
			||||||
 | 
				
			|||||||
@ -7,7 +7,7 @@ export const INVOICE = gql`
 | 
				
			|||||||
    invoice(id: $id) {
 | 
					    invoice(id: $id) {
 | 
				
			||||||
      id
 | 
					      id
 | 
				
			||||||
      bolt11
 | 
					      bolt11
 | 
				
			||||||
      msatsReceived
 | 
					      satsReceived
 | 
				
			||||||
      cancelled
 | 
					      cancelled
 | 
				
			||||||
      confirmedAt
 | 
					      confirmedAt
 | 
				
			||||||
      expiresAt
 | 
					      expiresAt
 | 
				
			||||||
@ -40,8 +40,8 @@ export const WALLET_HISTORY = gql`
 | 
				
			|||||||
        factId
 | 
					        factId
 | 
				
			||||||
        type
 | 
					        type
 | 
				
			||||||
        createdAt
 | 
					        createdAt
 | 
				
			||||||
        msats
 | 
					        sats
 | 
				
			||||||
        msatsFee
 | 
					        satsFee
 | 
				
			||||||
        status
 | 
					        status
 | 
				
			||||||
        type
 | 
					        type
 | 
				
			||||||
        description
 | 
					        description
 | 
				
			||||||
 | 
				
			|||||||
@ -9,3 +9,10 @@ export const abbrNum = n => {
 | 
				
			|||||||
export const fixedDecimal = (n, f) => {
 | 
					export const fixedDecimal = (n, f) => {
 | 
				
			||||||
  return Number.parseFloat(n).toFixed(f)
 | 
					  return Number.parseFloat(n).toFixed(f)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const msatsToSats = msats => {
 | 
				
			||||||
 | 
					  if (msats === null || msats === undefined) {
 | 
				
			||||||
 | 
					    return null
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return Number(BigInt(msats) / 1000n)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -49,10 +49,12 @@ function MyApp ({ Component, pageProps: { session, ...props } }) {
 | 
				
			|||||||
    // this nodata var will get passed to the server on back/foward and
 | 
					    // this nodata var will get passed to the server on back/foward and
 | 
				
			||||||
    // 1. prevent data from reloading and 2. perserve scroll
 | 
					    // 1. prevent data from reloading and 2. perserve scroll
 | 
				
			||||||
    // (2) is not possible while intercepting nav with beforePopState
 | 
					    // (2) is not possible while intercepting nav with beforePopState
 | 
				
			||||||
    router.replace({
 | 
					    if (router.isReady) {
 | 
				
			||||||
      pathname: router.pathname,
 | 
					      router.replace({
 | 
				
			||||||
      query: { ...router.query, nodata: true }
 | 
					        pathname: router.pathname,
 | 
				
			||||||
    }, router.asPath, { ...router.options, scroll: false })
 | 
					        query: { ...router.query, nodata: true }
 | 
				
			||||||
 | 
					      }, router.asPath, { ...router.options, scroll: false })
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  }, [router.asPath])
 | 
					  }, [router.asPath])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /*
 | 
					  /*
 | 
				
			||||||
 | 
				
			|||||||
@ -19,7 +19,10 @@ function LoadInvoice () {
 | 
				
			|||||||
    pollInterval: 1000,
 | 
					    pollInterval: 1000,
 | 
				
			||||||
    variables: { id: router.query.id }
 | 
					    variables: { id: router.query.id }
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
  if (error) return <div>error</div>
 | 
					  if (error) {
 | 
				
			||||||
 | 
					    console.log(error)
 | 
				
			||||||
 | 
					    return <div>error</div>
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  if (!data || loading) {
 | 
					  if (!data || loading) {
 | 
				
			||||||
    return <LnQRSkeleton status='loading' />
 | 
					    return <LnQRSkeleton status='loading' />
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -213,7 +213,7 @@ export default function Satistics ({ data: { me, walletHistory: { facts, cursor
 | 
				
			|||||||
                    <td className={styles.description}>
 | 
					                    <td className={styles.description}>
 | 
				
			||||||
                      <Detail fact={f} />
 | 
					                      <Detail fact={f} />
 | 
				
			||||||
                    </td>
 | 
					                    </td>
 | 
				
			||||||
                    <td className={`${styles.sats} ${satusClass(f.status)}`}>{Math.floor(f.msats / 1000)}</td>
 | 
					                    <td className={`${styles.sats} ${satusClass(f.status)}`}>{Math.floor(f.sats)}</td>
 | 
				
			||||||
                  </tr>
 | 
					                  </tr>
 | 
				
			||||||
                </Wrapper>
 | 
					                </Wrapper>
 | 
				
			||||||
              )
 | 
					              )
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										43
									
								
								prisma/migrations/20221110190205_msats_bigint/migration.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								prisma/migrations/20221110190205_msats_bigint/migration.sql
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,43 @@
 | 
				
			|||||||
 | 
					-- AlterTable
 | 
				
			||||||
 | 
					ALTER TABLE "Earn" ALTER COLUMN "msats" SET DATA TYPE BIGINT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- AlterTable
 | 
				
			||||||
 | 
					ALTER TABLE "Invoice" ALTER COLUMN "msatsRequested" SET DATA TYPE BIGINT,
 | 
				
			||||||
 | 
					ALTER COLUMN "msatsReceived" SET DATA TYPE BIGINT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- AlterTable
 | 
				
			||||||
 | 
					ALTER TABLE "Item"
 | 
				
			||||||
 | 
					ALTER COLUMN "commentSats" SET DATA TYPE BIGINT,
 | 
				
			||||||
 | 
					ALTER COLUMN "sats" SET DATA TYPE BIGINT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- AlterTable
 | 
				
			||||||
 | 
					ALTER TABLE "Item" RENAME COLUMN "commentSats" TO "commentMsats";
 | 
				
			||||||
 | 
					ALTER TABLE "Item" RENAME COLUMN "sats" TO "msats";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- update to msats
 | 
				
			||||||
 | 
					UPDATE "Item" SET
 | 
				
			||||||
 | 
					"commentMsats" = "commentMsats" * 1000,
 | 
				
			||||||
 | 
					"msats" = "msats" * 1000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- AlterTable
 | 
				
			||||||
 | 
					ALTER TABLE "ItemAct"
 | 
				
			||||||
 | 
					ALTER COLUMN "sats" SET DATA TYPE BIGINT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- AlterTable
 | 
				
			||||||
 | 
					ALTER TABLE "ItemAct" RENAME COLUMN "sats" TO "msats";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- update to msats
 | 
				
			||||||
 | 
					UPDATE "ItemAct" SET
 | 
				
			||||||
 | 
					"msats" = "msats" * 1000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- AlterTable
 | 
				
			||||||
 | 
					ALTER TABLE "Withdrawl" ALTER COLUMN "msatsPaying" SET DATA TYPE BIGINT,
 | 
				
			||||||
 | 
					ALTER COLUMN "msatsPaid" SET DATA TYPE BIGINT,
 | 
				
			||||||
 | 
					ALTER COLUMN "msatsFeePaying" SET DATA TYPE BIGINT,
 | 
				
			||||||
 | 
					ALTER COLUMN "msatsFeePaid" SET DATA TYPE BIGINT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- AlterTable
 | 
				
			||||||
 | 
					ALTER TABLE "users" ALTER COLUMN "msats" SET DEFAULT 0,
 | 
				
			||||||
 | 
					ALTER COLUMN "msats" SET DATA TYPE BIGINT,
 | 
				
			||||||
 | 
					ALTER COLUMN "stackedMsats" SET DEFAULT 0,
 | 
				
			||||||
 | 
					ALTER COLUMN "stackedMsats" SET DATA TYPE BIGINT;
 | 
				
			||||||
							
								
								
									
										376
									
								
								prisma/migrations/20221110224543_msats_funcs/migration.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										376
									
								
								prisma/migrations/20221110224543_msats_funcs/migration.sql
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,376 @@
 | 
				
			|||||||
 | 
					-- item_act should take sats but treat them as msats
 | 
				
			||||||
 | 
					CREATE OR REPLACE FUNCTION item_act(item_id INTEGER, user_id INTEGER, act "ItemActType", act_sats INTEGER)
 | 
				
			||||||
 | 
					RETURNS INTEGER
 | 
				
			||||||
 | 
					LANGUAGE plpgsql
 | 
				
			||||||
 | 
					AS $$
 | 
				
			||||||
 | 
					DECLARE
 | 
				
			||||||
 | 
					    user_msats BIGINT;
 | 
				
			||||||
 | 
					    act_msats BIGINT;
 | 
				
			||||||
 | 
					BEGIN
 | 
				
			||||||
 | 
					    PERFORM ASSERT_SERIALIZED();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    act_msats := act_sats * 1000;
 | 
				
			||||||
 | 
					    SELECT msats INTO user_msats FROM users WHERE id = user_id;
 | 
				
			||||||
 | 
					    IF act_msats > user_msats THEN
 | 
				
			||||||
 | 
					        RAISE EXCEPTION 'SN_INSUFFICIENT_FUNDS';
 | 
				
			||||||
 | 
					    END IF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    -- deduct msats from actor
 | 
				
			||||||
 | 
					    UPDATE users SET msats = msats - act_msats WHERE id = user_id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    IF act = 'VOTE' OR act = 'TIP' THEN
 | 
				
			||||||
 | 
					        -- add sats to actee's balance and stacked count
 | 
				
			||||||
 | 
					        UPDATE users
 | 
				
			||||||
 | 
					        SET msats = msats + act_msats, "stackedMsats" = "stackedMsats" + act_msats
 | 
				
			||||||
 | 
					        WHERE id = (SELECT COALESCE("fwdUserId", "userId") FROM "Item" WHERE id = item_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        -- if they have already voted, this is a tip
 | 
				
			||||||
 | 
					        IF EXISTS (SELECT 1 FROM "ItemAct" WHERE "itemId" = item_id AND "userId" = user_id AND "ItemAct".act = 'VOTE') THEN
 | 
				
			||||||
 | 
					            INSERT INTO "ItemAct" (msats, "itemId", "userId", act, created_at, updated_at)
 | 
				
			||||||
 | 
					            VALUES (act_msats, item_id, user_id, 'TIP', now_utc(), now_utc());
 | 
				
			||||||
 | 
					        ELSE
 | 
				
			||||||
 | 
					            -- else this is a vote with a possible extra tip
 | 
				
			||||||
 | 
					            INSERT INTO "ItemAct" (msats, "itemId", "userId", act, created_at, updated_at)
 | 
				
			||||||
 | 
					                VALUES (1000, item_id, user_id, 'VOTE', now_utc(), now_utc());
 | 
				
			||||||
 | 
					            act_msats := act_msats - 1000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            -- if we have sats left after vote, leave them as a tip
 | 
				
			||||||
 | 
					            IF act_msats > 0 THEN
 | 
				
			||||||
 | 
					                INSERT INTO "ItemAct" (msats, "itemId", "userId", act, created_at, updated_at)
 | 
				
			||||||
 | 
					                    VALUES (act_msats, item_id, user_id, 'TIP', now_utc(), now_utc());
 | 
				
			||||||
 | 
					            END IF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            RETURN 1;
 | 
				
			||||||
 | 
					        END IF;
 | 
				
			||||||
 | 
					    ELSE -- BOOST, POLL, DONT_LIKE_THIS
 | 
				
			||||||
 | 
					        INSERT INTO "ItemAct" (msats, "itemId", "userId", act, created_at, updated_at)
 | 
				
			||||||
 | 
					        VALUES (act_msats, item_id, user_id, act, now_utc(), now_utc());
 | 
				
			||||||
 | 
					    END IF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    RETURN 0;
 | 
				
			||||||
 | 
					END;
 | 
				
			||||||
 | 
					$$;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- when creating free item, set freebie flag so can be optionally viewed
 | 
				
			||||||
 | 
					CREATE OR REPLACE FUNCTION create_item(
 | 
				
			||||||
 | 
					    title TEXT, url TEXT, text TEXT, boost INTEGER,
 | 
				
			||||||
 | 
					    parent_id INTEGER, user_id INTEGER, fwd_user_id INTEGER,
 | 
				
			||||||
 | 
					    spam_within INTERVAL)
 | 
				
			||||||
 | 
					RETURNS "Item"
 | 
				
			||||||
 | 
					LANGUAGE plpgsql
 | 
				
			||||||
 | 
					AS $$
 | 
				
			||||||
 | 
					DECLARE
 | 
				
			||||||
 | 
					    user_msats BIGINT;
 | 
				
			||||||
 | 
					    cost_msats BIGINT;
 | 
				
			||||||
 | 
					    free_posts INTEGER;
 | 
				
			||||||
 | 
					    free_comments INTEGER;
 | 
				
			||||||
 | 
					    freebie BOOLEAN;
 | 
				
			||||||
 | 
					    item "Item";
 | 
				
			||||||
 | 
					    med_votes FLOAT;
 | 
				
			||||||
 | 
					BEGIN
 | 
				
			||||||
 | 
					    PERFORM ASSERT_SERIALIZED();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    SELECT msats, "freePosts", "freeComments"
 | 
				
			||||||
 | 
					    INTO user_msats, free_posts, free_comments
 | 
				
			||||||
 | 
					    FROM users WHERE id = user_id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cost_msats := 1000 * POWER(10, item_spam(parent_id, user_id, spam_within));
 | 
				
			||||||
 | 
					    -- it's only a freebie if it's a 1 sat cost, they have < 1 sat, boost = 0, and they have freebies left
 | 
				
			||||||
 | 
					    freebie := (cost_msats <= 1000) AND (user_msats < 1000) AND (boost = 0) AND ((parent_id IS NULL AND free_posts > 0) OR (parent_id IS NOT NULL AND free_comments > 0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    IF NOT freebie AND cost_msats > user_msats THEN
 | 
				
			||||||
 | 
					        RAISE EXCEPTION 'SN_INSUFFICIENT_FUNDS';
 | 
				
			||||||
 | 
					    END IF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    -- get this user's median item score
 | 
				
			||||||
 | 
					    SELECT COALESCE(percentile_cont(0.5) WITHIN GROUP(ORDER BY "weightedVotes" - "weightedDownVotes"), 0) INTO med_votes FROM "Item" WHERE "userId" = user_id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    -- if their median votes are positive, start at 0
 | 
				
			||||||
 | 
					    -- if the median votes are negative, start their post with that many down votes
 | 
				
			||||||
 | 
					    -- basically: if their median post is bad, presume this post is too
 | 
				
			||||||
 | 
					    IF med_votes >= 0 THEN
 | 
				
			||||||
 | 
					        med_votes := 0;
 | 
				
			||||||
 | 
					    ELSE
 | 
				
			||||||
 | 
					        med_votes := ABS(med_votes);
 | 
				
			||||||
 | 
					    END IF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    INSERT INTO "Item" (title, url, text, "userId", "parentId", "fwdUserId", freebie, "weightedDownVotes", created_at, updated_at)
 | 
				
			||||||
 | 
					    VALUES (title, url, text, user_id, parent_id, fwd_user_id, freebie, med_votes, now_utc(), now_utc()) RETURNING * INTO item;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    IF freebie THEN
 | 
				
			||||||
 | 
					        IF parent_id IS NULL THEN
 | 
				
			||||||
 | 
					            UPDATE users SET "freePosts" = "freePosts" - 1 WHERE id = user_id;
 | 
				
			||||||
 | 
					        ELSE
 | 
				
			||||||
 | 
					            UPDATE users SET "freeComments" = "freeComments" - 1 WHERE id = user_id;
 | 
				
			||||||
 | 
					        END IF;
 | 
				
			||||||
 | 
					    ELSE
 | 
				
			||||||
 | 
					        UPDATE users SET msats = msats - cost_msats WHERE id = user_id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        INSERT INTO "ItemAct" (msats, "itemId", "userId", act, created_at, updated_at)
 | 
				
			||||||
 | 
					        VALUES (cost_msats, item.id, user_id, 'VOTE', now_utc(), now_utc());
 | 
				
			||||||
 | 
					    END IF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    IF boost > 0 THEN
 | 
				
			||||||
 | 
					        PERFORM item_act(item.id, user_id, 'BOOST', boost);
 | 
				
			||||||
 | 
					    END IF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    RETURN item;
 | 
				
			||||||
 | 
					END;
 | 
				
			||||||
 | 
					$$;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CREATE OR REPLACE FUNCTION run_auction(item_id INTEGER) RETURNS void AS $$
 | 
				
			||||||
 | 
					    DECLARE
 | 
				
			||||||
 | 
					        bid_msats BIGINT;
 | 
				
			||||||
 | 
					        user_msats BIGINT;
 | 
				
			||||||
 | 
					        user_id INTEGER;
 | 
				
			||||||
 | 
					        item_status "Status";
 | 
				
			||||||
 | 
					        status_updated_at timestamp(3);
 | 
				
			||||||
 | 
					    BEGIN
 | 
				
			||||||
 | 
					        PERFORM ASSERT_SERIALIZED();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        -- extract data we need
 | 
				
			||||||
 | 
					        SELECT "maxBid" * 1000, "userId", status, "statusUpdatedAt" INTO bid_msats, user_id, item_status, status_updated_at FROM "Item" WHERE id = item_id;
 | 
				
			||||||
 | 
					        SELECT msats INTO user_msats FROM users WHERE id = user_id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        -- 0 bid items expire after 30 days unless updated
 | 
				
			||||||
 | 
					        IF bid_msats = 0 THEN
 | 
				
			||||||
 | 
					            IF item_status <> 'STOPPED' THEN
 | 
				
			||||||
 | 
					                IF status_updated_at < now_utc() - INTERVAL '30 days' THEN
 | 
				
			||||||
 | 
					                    UPDATE "Item" SET status = 'STOPPED', "statusUpdatedAt" = now_utc() WHERE id = item_id;
 | 
				
			||||||
 | 
					                ELSEIF item_status = 'NOSATS' THEN
 | 
				
			||||||
 | 
					                    UPDATE "Item" SET status = 'ACTIVE' WHERE id = item_id;
 | 
				
			||||||
 | 
					                END IF;
 | 
				
			||||||
 | 
					            END IF;
 | 
				
			||||||
 | 
					            RETURN;
 | 
				
			||||||
 | 
					        END IF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        -- check if user wallet has enough sats
 | 
				
			||||||
 | 
					        IF bid_msats > user_msats THEN
 | 
				
			||||||
 | 
					            -- if not, set status = NOSATS and statusUpdatedAt to now_utc if not already set
 | 
				
			||||||
 | 
					            IF item_status <> 'NOSATS' THEN
 | 
				
			||||||
 | 
					                UPDATE "Item" SET status = 'NOSATS', "statusUpdatedAt" = now_utc() WHERE id = item_id;
 | 
				
			||||||
 | 
					            END IF;
 | 
				
			||||||
 | 
					        ELSE
 | 
				
			||||||
 | 
					            -- if so, deduct from user
 | 
				
			||||||
 | 
					            UPDATE users SET msats = msats - bid_msats WHERE id = user_id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            -- create an item act
 | 
				
			||||||
 | 
					            INSERT INTO "ItemAct" (msats, "itemId", "userId", act, created_at, updated_at)
 | 
				
			||||||
 | 
					            VALUES (bid_msats, item_id, user_id, 'STREAM', now_utc(), now_utc());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            -- update item status = ACTIVE and statusUpdatedAt = now_utc if NOSATS
 | 
				
			||||||
 | 
					            IF item_status = 'NOSATS' THEN
 | 
				
			||||||
 | 
					                UPDATE "Item" SET status = 'ACTIVE', "statusUpdatedAt" = now_utc() WHERE id = item_id;
 | 
				
			||||||
 | 
					            END IF;
 | 
				
			||||||
 | 
					        END IF;
 | 
				
			||||||
 | 
					    END;
 | 
				
			||||||
 | 
					$$ LANGUAGE plpgsql;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					-- on item act denormalize sats and comment sats
 | 
				
			||||||
 | 
					CREATE OR REPLACE FUNCTION sats_after_act() RETURNS TRIGGER AS $$
 | 
				
			||||||
 | 
					DECLARE
 | 
				
			||||||
 | 
					    item "Item";
 | 
				
			||||||
 | 
					BEGIN
 | 
				
			||||||
 | 
					    SELECT * FROM "Item" WHERE id = NEW."itemId" INTO item;
 | 
				
			||||||
 | 
					    IF item."userId" = NEW."userId" THEN
 | 
				
			||||||
 | 
					        RETURN NEW;
 | 
				
			||||||
 | 
					    END IF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    UPDATE "Item"
 | 
				
			||||||
 | 
					    SET "msats" = "msats" + NEW.msats
 | 
				
			||||||
 | 
					    WHERE id = item.id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    UPDATE "Item"
 | 
				
			||||||
 | 
					    SET "commentMsats" = "commentMsats" + NEW.msats
 | 
				
			||||||
 | 
					    WHERE id <> item.id and path @> item.path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    RETURN NEW;
 | 
				
			||||||
 | 
					END;
 | 
				
			||||||
 | 
					$$ LANGUAGE plpgsql;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DROP TRIGGER IF EXISTS sats_after_act_trigger ON "ItemAct";
 | 
				
			||||||
 | 
					CREATE TRIGGER sats_after_act_trigger
 | 
				
			||||||
 | 
					    AFTER INSERT ON "ItemAct"
 | 
				
			||||||
 | 
					    FOR EACH ROW
 | 
				
			||||||
 | 
					    WHEN (NEW.act = 'VOTE' or NEW.act = 'TIP')
 | 
				
			||||||
 | 
					    EXECUTE PROCEDURE sats_after_act();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CREATE OR REPLACE FUNCTION boost_after_act() RETURNS TRIGGER AS $$
 | 
				
			||||||
 | 
					BEGIN
 | 
				
			||||||
 | 
					    -- update item
 | 
				
			||||||
 | 
					    UPDATE "Item" SET boost = boost + FLOOR(NEW.msats / 1000) WHERE id = NEW."itemId";
 | 
				
			||||||
 | 
					    RETURN NEW;
 | 
				
			||||||
 | 
					END;
 | 
				
			||||||
 | 
					$$ LANGUAGE plpgsql;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DROP TRIGGER IF EXISTS boost_after_act ON "ItemAct";
 | 
				
			||||||
 | 
					CREATE TRIGGER boost_after_act
 | 
				
			||||||
 | 
					    AFTER INSERT ON "ItemAct"
 | 
				
			||||||
 | 
					    FOR EACH ROW
 | 
				
			||||||
 | 
					    WHEN (NEW.act = 'BOOST')
 | 
				
			||||||
 | 
					    EXECUTE PROCEDURE boost_after_act();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DROP FUNCTION IF EXISTS create_invoice(TEXT, TEXT, timestamp(3) without time zone, INTEGER, INTEGER);
 | 
				
			||||||
 | 
					CREATE OR REPLACE FUNCTION create_invoice(hash TEXT, bolt11 TEXT, expires_at timestamp(3) without time zone, msats_req BIGINT, user_id INTEGER)
 | 
				
			||||||
 | 
					RETURNS "Invoice"
 | 
				
			||||||
 | 
					LANGUAGE plpgsql
 | 
				
			||||||
 | 
					AS $$
 | 
				
			||||||
 | 
					DECLARE
 | 
				
			||||||
 | 
					    invoice "Invoice";
 | 
				
			||||||
 | 
					    limit_reached BOOLEAN;
 | 
				
			||||||
 | 
					    too_much BOOLEAN;
 | 
				
			||||||
 | 
					BEGIN
 | 
				
			||||||
 | 
					    PERFORM ASSERT_SERIALIZED();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    SELECT count(*) >= 10, coalesce(sum("msatsRequested"),0)+coalesce(max(users.msats), 0)+msats_req > 1000000000 INTO limit_reached, too_much
 | 
				
			||||||
 | 
					    FROM "Invoice"
 | 
				
			||||||
 | 
					    JOIN users on "userId" = users.id
 | 
				
			||||||
 | 
					    WHERE "userId" = user_id AND "expiresAt" > now_utc() AND "confirmedAt" is null AND cancelled = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    -- prevent more than 10 pending invoices
 | 
				
			||||||
 | 
					    IF limit_reached THEN
 | 
				
			||||||
 | 
					        RAISE EXCEPTION 'SN_INV_PENDING_LIMIT';
 | 
				
			||||||
 | 
					    END IF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    -- prevent pending invoices + msats from exceeding 1,000,000 sats
 | 
				
			||||||
 | 
					    IF too_much THEN
 | 
				
			||||||
 | 
					        RAISE EXCEPTION 'SN_INV_EXCEED_BALANCE';
 | 
				
			||||||
 | 
					    END IF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    INSERT INTO "Invoice" (hash, bolt11, "expiresAt", "msatsRequested", "userId", created_at, updated_at)
 | 
				
			||||||
 | 
					    VALUES (hash, bolt11, expires_at, msats_req, user_id, now_utc(), now_utc()) RETURNING * INTO invoice;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    INSERT INTO pgboss.job (name, data, retrylimit, retrybackoff, startafter)
 | 
				
			||||||
 | 
					    VALUES ('checkInvoice', jsonb_build_object('hash', hash), 21, true, now() + interval '10 seconds');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    RETURN invoice;
 | 
				
			||||||
 | 
					END;
 | 
				
			||||||
 | 
					$$;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DROP FUNCTION IF EXISTS confirm_invoice(TEXT, INTEGER);
 | 
				
			||||||
 | 
					CREATE OR REPLACE FUNCTION confirm_invoice(lnd_id TEXT, lnd_received BIGINT)
 | 
				
			||||||
 | 
					RETURNS INTEGER
 | 
				
			||||||
 | 
					LANGUAGE plpgsql
 | 
				
			||||||
 | 
					AS $$
 | 
				
			||||||
 | 
					DECLARE
 | 
				
			||||||
 | 
					    user_id INTEGER;
 | 
				
			||||||
 | 
					    confirmed_at TIMESTAMP;
 | 
				
			||||||
 | 
					BEGIN
 | 
				
			||||||
 | 
					    PERFORM ASSERT_SERIALIZED();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    SELECT "userId", "confirmedAt" INTO user_id, confirmed_at FROM "Invoice" WHERE hash = lnd_id;
 | 
				
			||||||
 | 
					    IF confirmed_at IS NULL THEN
 | 
				
			||||||
 | 
					        UPDATE "Invoice" SET "msatsReceived" = lnd_received, "confirmedAt" = now_utc(), updated_at = now_utc()
 | 
				
			||||||
 | 
					        WHERE hash = lnd_id;
 | 
				
			||||||
 | 
					        UPDATE users SET msats = msats + lnd_received WHERE id = user_id;
 | 
				
			||||||
 | 
					    END IF;
 | 
				
			||||||
 | 
					    RETURN 0;
 | 
				
			||||||
 | 
					END;
 | 
				
			||||||
 | 
					$$;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DROP FUNCTION IF EXISTS create_withdrawl(TEXT, TEXT, INTEGER, INTEGER, TEXT);
 | 
				
			||||||
 | 
					CREATE OR REPLACE FUNCTION create_withdrawl(lnd_id TEXT, invoice TEXT, msats_amount BIGINT, msats_max_fee BIGINT, username TEXT)
 | 
				
			||||||
 | 
					RETURNS "Withdrawl"
 | 
				
			||||||
 | 
					LANGUAGE plpgsql
 | 
				
			||||||
 | 
					AS $$
 | 
				
			||||||
 | 
					DECLARE
 | 
				
			||||||
 | 
					    user_id INTEGER;
 | 
				
			||||||
 | 
					    user_msats BIGINT;
 | 
				
			||||||
 | 
					    withdrawl "Withdrawl";
 | 
				
			||||||
 | 
					BEGIN
 | 
				
			||||||
 | 
					    PERFORM ASSERT_SERIALIZED();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    SELECT msats, id INTO user_msats, user_id FROM users WHERE name = username;
 | 
				
			||||||
 | 
					    IF (msats_amount + msats_max_fee) > user_msats THEN
 | 
				
			||||||
 | 
					        RAISE EXCEPTION 'SN_INSUFFICIENT_FUNDS';
 | 
				
			||||||
 | 
					    END IF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    IF EXISTS (SELECT 1 FROM "Withdrawl" WHERE hash = lnd_id AND status IS NULL) THEN
 | 
				
			||||||
 | 
					        RAISE EXCEPTION 'SN_PENDING_WITHDRAWL_EXISTS';
 | 
				
			||||||
 | 
					    END IF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    IF EXISTS (SELECT 1 FROM "Withdrawl" WHERE hash = lnd_id AND status = 'CONFIRMED') THEN
 | 
				
			||||||
 | 
					        RAISE EXCEPTION 'SN_CONFIRMED_WITHDRAWL_EXISTS';
 | 
				
			||||||
 | 
					    END IF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    INSERT INTO "Withdrawl" (hash, bolt11, "msatsPaying", "msatsFeePaying", "userId", created_at, updated_at)
 | 
				
			||||||
 | 
					    VALUES (lnd_id, invoice, msats_amount, msats_max_fee, user_id, now_utc(), now_utc()) RETURNING * INTO withdrawl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    UPDATE users SET msats = msats - msats_amount - msats_max_fee WHERE id = user_id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    INSERT INTO pgboss.job (name, data, retrylimit, retrybackoff, startafter)
 | 
				
			||||||
 | 
					    VALUES ('checkWithdrawal', jsonb_build_object('id', withdrawl.id, 'hash', lnd_id), 21, true, now() + interval '10 seconds');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    RETURN withdrawl;
 | 
				
			||||||
 | 
					END;
 | 
				
			||||||
 | 
					$$;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DROP FUNCTION IF EXISTS confirm_withdrawl(INTEGER, INTEGER, INTEGER);
 | 
				
			||||||
 | 
					CREATE OR REPLACE FUNCTION confirm_withdrawl(wid INTEGER, msats_paid BIGINT, msats_fee_paid BIGINT)
 | 
				
			||||||
 | 
					RETURNS INTEGER
 | 
				
			||||||
 | 
					LANGUAGE plpgsql
 | 
				
			||||||
 | 
					AS $$
 | 
				
			||||||
 | 
					DECLARE
 | 
				
			||||||
 | 
					    msats_fee_paying BIGINT;
 | 
				
			||||||
 | 
					    user_id INTEGER;
 | 
				
			||||||
 | 
					BEGIN
 | 
				
			||||||
 | 
					    PERFORM ASSERT_SERIALIZED();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    IF EXISTS (SELECT 1 FROM "Withdrawl" WHERE id = wid AND status IS NULL) THEN
 | 
				
			||||||
 | 
					        SELECT "msatsFeePaying", "userId" INTO msats_fee_paying, user_id
 | 
				
			||||||
 | 
					        FROM "Withdrawl" WHERE id = wid AND status IS NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        UPDATE "Withdrawl"
 | 
				
			||||||
 | 
					        SET status = 'CONFIRMED', "msatsPaid" = msats_paid,
 | 
				
			||||||
 | 
					        "msatsFeePaid" = msats_fee_paid, updated_at = now_utc()
 | 
				
			||||||
 | 
					        WHERE id = wid AND status IS NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        UPDATE users SET msats = msats + (msats_fee_paying - msats_fee_paid) WHERE id = user_id;
 | 
				
			||||||
 | 
					    END IF;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    RETURN 0;
 | 
				
			||||||
 | 
					END;
 | 
				
			||||||
 | 
					$$;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					CREATE OR REPLACE FUNCTION reverse_withdrawl(wid INTEGER, wstatus "WithdrawlStatus")
 | 
				
			||||||
 | 
					RETURNS INTEGER
 | 
				
			||||||
 | 
					LANGUAGE plpgsql
 | 
				
			||||||
 | 
					AS $$
 | 
				
			||||||
 | 
					DECLARE
 | 
				
			||||||
 | 
					    msats_fee_paying BIGINT;
 | 
				
			||||||
 | 
					    msats_paying BIGINT;
 | 
				
			||||||
 | 
					    user_id INTEGER;
 | 
				
			||||||
 | 
					BEGIN
 | 
				
			||||||
 | 
					    PERFORM ASSERT_SERIALIZED();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    IF EXISTS (SELECT 1 FROM "Withdrawl" WHERE id = wid AND status IS NULL) THEN
 | 
				
			||||||
 | 
					        SELECT "msatsPaying", "msatsFeePaying", "userId" INTO msats_paying, msats_fee_paying, user_id
 | 
				
			||||||
 | 
					        FROM "Withdrawl" WHERE id = wid AND status IS NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        UPDATE "Withdrawl" SET status = wstatus, updated_at = now_utc() WHERE id = wid AND status IS NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        UPDATE users SET msats = msats + msats_paying + msats_fee_paying WHERE id = user_id;
 | 
				
			||||||
 | 
					    END IF;
 | 
				
			||||||
 | 
					    RETURN 0;
 | 
				
			||||||
 | 
					END;
 | 
				
			||||||
 | 
					$$;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DROP FUNCTION IF EXISTS earn(INTEGER, INTEGER, TIMESTAMP(3), "EarnType", INTEGER, INTEGER);
 | 
				
			||||||
 | 
					CREATE OR REPLACE FUNCTION earn(user_id INTEGER, earn_msats BIGINT, created_at TIMESTAMP(3),
 | 
				
			||||||
 | 
					    type "EarnType", type_id INTEGER, rank INTEGER)
 | 
				
			||||||
 | 
					RETURNS void AS $$
 | 
				
			||||||
 | 
					DECLARE
 | 
				
			||||||
 | 
					BEGIN
 | 
				
			||||||
 | 
					    PERFORM ASSERT_SERIALIZED();
 | 
				
			||||||
 | 
					    -- insert into earn
 | 
				
			||||||
 | 
					    INSERT INTO "Earn" (msats, "userId", created_at, type, "typeId", rank)
 | 
				
			||||||
 | 
					    VALUES (earn_msats, user_id, created_at, type, type_id, rank);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    -- give the user the sats
 | 
				
			||||||
 | 
					    UPDATE users
 | 
				
			||||||
 | 
					    SET msats = msats + earn_msats, "stackedMsats" = "stackedMsats" + earn_msats
 | 
				
			||||||
 | 
					    WHERE id = user_id;
 | 
				
			||||||
 | 
					END;
 | 
				
			||||||
 | 
					$$ LANGUAGE plpgsql;
 | 
				
			||||||
@ -30,8 +30,8 @@ model User {
 | 
				
			|||||||
  inviteId        String?
 | 
					  inviteId        String?
 | 
				
			||||||
  bio             Item?       @relation(fields: [bioId], references: [id])
 | 
					  bio             Item?       @relation(fields: [bioId], references: [id])
 | 
				
			||||||
  bioId           Int?
 | 
					  bioId           Int?
 | 
				
			||||||
  msats           Int         @default(0)
 | 
					  msats           BigInt      @default(0)
 | 
				
			||||||
  stackedMsats    Int         @default(0)
 | 
					  stackedMsats    BigInt      @default(0)
 | 
				
			||||||
  freeComments    Int         @default(5)
 | 
					  freeComments    Int         @default(5)
 | 
				
			||||||
  freePosts       Int         @default(2)
 | 
					  freePosts       Int         @default(2)
 | 
				
			||||||
  checkedNotesAt  DateTime?
 | 
					  checkedNotesAt  DateTime?
 | 
				
			||||||
@ -105,8 +105,8 @@ model Earn {
 | 
				
			|||||||
  createdAt DateTime @default(now()) @map(name: "created_at")
 | 
					  createdAt DateTime @default(now()) @map(name: "created_at")
 | 
				
			||||||
  updatedAt DateTime @default(now()) @updatedAt @map(name: "updated_at")
 | 
					  updatedAt DateTime @default(now()) @updatedAt @map(name: "updated_at")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  msats  Int
 | 
					  msats  BigInt
 | 
				
			||||||
  user   User @relation(fields: [userId], references: [id])
 | 
					  user   User   @relation(fields: [userId], references: [id])
 | 
				
			||||||
  userId Int
 | 
					  userId Int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  type   EarnType?
 | 
					  type   EarnType?
 | 
				
			||||||
@ -192,13 +192,13 @@ model Item {
 | 
				
			|||||||
  bio     Boolean @default(false)
 | 
					  bio     Boolean @default(false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // denormalized self stats
 | 
					  // denormalized self stats
 | 
				
			||||||
  weightedVotes     Float @default(0)
 | 
					  weightedVotes     Float  @default(0)
 | 
				
			||||||
  weightedDownVotes Float @default(0)
 | 
					  weightedDownVotes Float  @default(0)
 | 
				
			||||||
  sats              Int   @default(0)
 | 
					  msats             BigInt @default(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // denormalized comment stats
 | 
					  // denormalized comment stats
 | 
				
			||||||
  ncomments     Int       @default(0)
 | 
					  ncomments     Int       @default(0)
 | 
				
			||||||
  commentSats   Int       @default(0)
 | 
					  commentMsats  BigInt    @default(0)
 | 
				
			||||||
  lastCommentAt DateTime?
 | 
					  lastCommentAt DateTime?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // if sub is null, this is the main sub
 | 
					  // if sub is null, this is the main sub
 | 
				
			||||||
@ -317,7 +317,7 @@ model ItemAct {
 | 
				
			|||||||
  id        Int         @id @default(autoincrement())
 | 
					  id        Int         @id @default(autoincrement())
 | 
				
			||||||
  createdAt DateTime    @default(now()) @map(name: "created_at")
 | 
					  createdAt DateTime    @default(now()) @map(name: "created_at")
 | 
				
			||||||
  updatedAt DateTime    @updatedAt @map(name: "updated_at")
 | 
					  updatedAt DateTime    @updatedAt @map(name: "updated_at")
 | 
				
			||||||
  sats      Int
 | 
					  msats     BigInt
 | 
				
			||||||
  act       ItemActType
 | 
					  act       ItemActType
 | 
				
			||||||
  item      Item        @relation(fields: [itemId], references: [id])
 | 
					  item      Item        @relation(fields: [itemId], references: [id])
 | 
				
			||||||
  itemId    Int
 | 
					  itemId    Int
 | 
				
			||||||
@ -356,8 +356,8 @@ model Invoice {
 | 
				
			|||||||
  bolt11         String
 | 
					  bolt11         String
 | 
				
			||||||
  expiresAt      DateTime
 | 
					  expiresAt      DateTime
 | 
				
			||||||
  confirmedAt    DateTime?
 | 
					  confirmedAt    DateTime?
 | 
				
			||||||
  msatsRequested Int
 | 
					  msatsRequested BigInt
 | 
				
			||||||
  msatsReceived  Int?
 | 
					  msatsReceived  BigInt?
 | 
				
			||||||
  cancelled      Boolean   @default(false)
 | 
					  cancelled      Boolean   @default(false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @@index([createdAt])
 | 
					  @@index([createdAt])
 | 
				
			||||||
@ -382,10 +382,10 @@ model Withdrawl {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  hash           String
 | 
					  hash           String
 | 
				
			||||||
  bolt11         String
 | 
					  bolt11         String
 | 
				
			||||||
  msatsPaying    Int
 | 
					  msatsPaying    BigInt
 | 
				
			||||||
  msatsPaid      Int?
 | 
					  msatsPaid      BigInt?
 | 
				
			||||||
  msatsFeePaying Int
 | 
					  msatsFeePaying BigInt
 | 
				
			||||||
  msatsFeePaid   Int?
 | 
					  msatsFeePaid   BigInt?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  status WithdrawlStatus?
 | 
					  status WithdrawlStatus?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -10,17 +10,14 @@ function earn ({ models }) {
 | 
				
			|||||||
    console.log('running', name)
 | 
					    console.log('running', name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // compute how much sn earned today
 | 
					    // compute how much sn earned today
 | 
				
			||||||
    let [{ sum }] = await models.$queryRaw`
 | 
					    const [{ sum }] = await models.$queryRaw`
 | 
				
			||||||
        SELECT sum("ItemAct".sats)
 | 
					        SELECT sum("ItemAct".msats)
 | 
				
			||||||
        FROM "ItemAct"
 | 
					        FROM "ItemAct"
 | 
				
			||||||
        JOIN "Item" on "ItemAct"."itemId" = "Item".id
 | 
					        JOIN "Item" on "ItemAct"."itemId" = "Item".id
 | 
				
			||||||
        WHERE ("ItemAct".act in ('BOOST', 'STREAM')
 | 
					        WHERE ("ItemAct".act in ('BOOST', 'STREAM')
 | 
				
			||||||
          OR ("ItemAct".act IN ('VOTE','POLL') AND "Item"."userId" = "ItemAct"."userId"))
 | 
					          OR ("ItemAct".act IN ('VOTE','POLL') AND "Item"."userId" = "ItemAct"."userId"))
 | 
				
			||||||
          AND "ItemAct".created_at > now_utc() - INTERVAL '1 day'`
 | 
					          AND "ItemAct".created_at > now_utc() - INTERVAL '1 day'`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // convert to msats
 | 
					 | 
				
			||||||
    sum = sum * 1000
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /*
 | 
					    /*
 | 
				
			||||||
      How earnings work:
 | 
					      How earnings work:
 | 
				
			||||||
      1/3: top 21% posts over last 36 hours, scored on a relative basis
 | 
					      1/3: top 21% posts over last 36 hours, scored on a relative basis
 | 
				
			||||||
@ -56,7 +53,7 @@ function earn ({ models }) {
 | 
				
			|||||||
      ),
 | 
					      ),
 | 
				
			||||||
      upvoters AS (
 | 
					      upvoters AS (
 | 
				
			||||||
          SELECT "ItemAct"."userId", item_ratios.id, item_ratios.ratio, item_ratios."parentId",
 | 
					          SELECT "ItemAct"."userId", item_ratios.id, item_ratios.ratio, item_ratios."parentId",
 | 
				
			||||||
              sum("ItemAct".sats) as tipped, min("ItemAct".created_at) as acted_at
 | 
					              sum("ItemAct".msats) as tipped, min("ItemAct".created_at) as acted_at
 | 
				
			||||||
          FROM item_ratios
 | 
					          FROM item_ratios
 | 
				
			||||||
          JOIN "ItemAct" on "ItemAct"."itemId" = item_ratios.id
 | 
					          JOIN "ItemAct" on "ItemAct"."itemId" = item_ratios.id
 | 
				
			||||||
          WHERE act IN ('VOTE','TIP')
 | 
					          WHERE act IN ('VOTE','TIP')
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user