From 7d170a654fd1f1e438f84df0f200170dec1fa323 Mon Sep 17 00:00:00 2001 From: keyan Date: Wed, 13 Sep 2023 21:08:03 -0500 Subject: [PATCH] variable downzaps --- api/resolvers/item.js | 4 +- api/typeDefs/item.js | 2 +- components/dont-link-this.js | 17 +-- components/item-act.js | 6 +- components/item-info.js | 2 +- .../migration.sql | 115 ++++++++++++++++++ worker/wallet.js | 2 +- 7 files changed, 133 insertions(+), 15 deletions(-) create mode 100644 prisma/migrations/20230914005420_variable_down_zap/migration.sql diff --git a/api/resolvers/item.js b/api/resolvers/item.js index 4f51c67c..b343e2c9 100644 --- a/api/resolvers/item.js +++ b/api/resolvers/item.js @@ -834,7 +834,7 @@ export default { sats } }, - dontLikeThis: async (parent, { id, hash, hmac }, { me, models }) => { + dontLikeThis: async (parent, { id, sats, hash, hmac }, { me, models }) => { // need to make sure we are logged in if (!me) { throw new GraphQLError('you must be logged in', { extensions: { code: 'FORBIDDEN' } }) @@ -856,7 +856,7 @@ export default { const trx = [ models.$queryRaw`SELECT item_act(${Number(id)}::INTEGER, - ${me.id}::INTEGER, 'DONT_LIKE_THIS', ${DONT_LIKE_THIS_COST}::INTEGER)` + ${me.id}::INTEGER, 'DONT_LIKE_THIS', ${sats || DONT_LIKE_THIS_COST}::INTEGER)` ] if (invoice) { trx.unshift(models.$queryRaw`UPDATE users SET msats = msats + ${invoice.msatsReceived} WHERE id = ${invoice.user.id}`) diff --git a/api/typeDefs/item.js b/api/typeDefs/item.js index 24dd1b31..4eb8af1c 100644 --- a/api/typeDefs/item.js +++ b/api/typeDefs/item.js @@ -33,7 +33,7 @@ export default gql` text: String!, url: String!, maxBid: Int!, status: String, logo: Int, hash: String, hmac: String): Item! upsertPoll(id: ID, sub: String, title: String!, text: String, options: [String!]!, boost: Int, forward: [ItemForwardInput], hash: String, hmac: String): Item! upsertComment(id:ID, text: String!, parentId: ID, hash: String, hmac: String): Item! - dontLikeThis(id: ID!, hash: String, hmac: String): Boolean! + dontLikeThis(id: ID!, sats: Int, hash: String, hmac: String): Boolean! act(id: ID!, sats: Int, hash: String, hmac: String): ItemActResult! pollVote(id: ID!, hash: String, hmac: String): ID! } diff --git a/components/dont-link-this.js b/components/dont-link-this.js index 7e9402dc..90464e32 100644 --- a/components/dont-link-this.js +++ b/components/dont-link-this.js @@ -4,6 +4,7 @@ import { useShowModal } from './modal' import { useToast } from './toast' import { InvoiceModal, payOrLoginError } from './invoice' import { DONT_LIKE_THIS_COST } from '../lib/constants' +import ItemAct from './item-act' export default function DontLikeThisDropdownItem ({ id }) { const toaster = useToast() @@ -11,8 +12,8 @@ export default function DontLikeThisDropdownItem ({ id }) { const [dontLikeThis] = useMutation( gql` - mutation dontLikeThis($id: ID!, $hash: String, $hmac: String) { - dontLikeThis(id: $id, hash: $hash, hmac: $hmac) + mutation dontLikeThis($id: ID!, $sats: Int, $hash: String, $hmac: String) { + dontLikeThis(id: $id, sats: $sats, hash: $hash, hmac: $hmac) }`, { update (cache) { cache.modify({ @@ -31,11 +32,13 @@ export default function DontLikeThisDropdownItem ({ id }) { { try { - await dontLikeThis({ - variables: { id }, - optimisticResponse: { dontLikeThis: true } - }) - toaster.success('item flagged') + showModal(onClose => + { + onClose() + toaster.success('item flagged') + }} itemId={id} act={dontLikeThis} down + />) } catch (error) { console.error(error) if (payOrLoginError(error)) { diff --git a/components/item-act.js b/components/item-act.js index b2e0b7d3..ef50241b 100644 --- a/components/item-act.js +++ b/components/item-act.js @@ -36,7 +36,7 @@ const addCustomTip = (amount) => { window.localStorage.setItem('custom-tips', JSON.stringify(customTips)) } -export default function ItemAct ({ onClose, itemId, act, strike }) { +export default function ItemAct ({ onClose, itemId, act, down, strike }) { const inputRef = useRef(null) const me = useMe() const [oValue, setOValue] = useState() @@ -59,7 +59,7 @@ export default function ItemAct ({ onClose, itemId, act, strike }) { hmac } }) - await strike() + strike && await strike() addCustomTip(Number(amount)) onClose() }, [act]) @@ -88,7 +88,7 @@ export default function ItemAct ({ onClose, itemId, act, strike }) {
- zap + {down && 'down '}zap
) diff --git a/components/item-info.js b/components/item-info.js index 1be280ae..b65f18f8 100644 --- a/components/item-info.js +++ b/components/item-info.js @@ -136,7 +136,7 @@ export default function ItemInfo ({ ots timestamp } - {me && !item.meSats && !item.position && !item.meDontLike && + {me && !item.meSats && !item.position && !item.mine && !item.deletedAt && } {item.mine && !item.position && !item.deletedAt && } diff --git a/prisma/migrations/20230914005420_variable_down_zap/migration.sql b/prisma/migrations/20230914005420_variable_down_zap/migration.sql new file mode 100644 index 00000000..cb6c3fb3 --- /dev/null +++ b/prisma/migrations/20230914005420_variable_down_zap/migration.sql @@ -0,0 +1,115 @@ +DROP FUNCTION weighted_downvotes_after_act(item_id INTEGER, user_id INTEGER, sats INTEGER); +CREATE OR REPLACE FUNCTION weighted_downvotes_after_act(item_id INTEGER, user_id INTEGER, sats INTEGER) RETURNS INTEGER AS $$ +DECLARE + user_trust DOUBLE PRECISION; + sats_past INTEGER; + multiplier DOUBLE PRECISION; +BEGIN + -- grab user's trust who is upvoting + SELECT trust INTO user_trust FROM users WHERE id = user_id; + + -- in order to add this to weightedVotes, we need to do log((satsN+satsPrior)/satsPrior) + -- so compute sats prior + SELECT SUM(msats) / 1000 INTO sats_past + FROM "ItemAct" + WHERE "userId" = user_id AND "itemId" = item_id AND act IN ('DONT_LIKE_THIS'); + + IF sats_past IS NULL OR sats_past = 0 THEN + multiplier := LOG(sats); + ELSE + multiplier := LOG((sats+sats_past)/sats_past::FLOAT); + END IF; + + -- update item + UPDATE "Item" + SET "weightedDownVotes" = "weightedDownVotes" + (user_trust * multiplier) + WHERE id = item_id AND "userId" <> user_id; + + RETURN 0; +END; +$$ LANGUAGE plpgsql; + +-- Update item_act to support multiple dont_like_this +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; + fee_msats BIGINT; + item_act_id INTEGER; + fwd_entry record; -- for loop iterator variable to iterate across forward recipients + fwd_msats BIGINT; -- for loop variable calculating how many msats to give each forward recipient + total_fwd_msats BIGINT := 0; -- accumulator to see how many msats have been forwarded for the act +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 = 'TIP' THEN + -- call to influence weightedVotes ... we need to do this before we record the acts because + -- the priors acts are taken into account + PERFORM weighted_votes_after_tip(item_id, user_id, act_sats); + -- call to denormalize sats and commentSats + PERFORM sats_after_tip(item_id, user_id, act_msats); + + -- take 10% and insert as FEE + fee_msats := CEIL(act_msats * 0.1); + act_msats := act_msats - fee_msats; + + -- save the fee act into item_act_id so we can record referral acts + INSERT INTO "ItemAct" (msats, "itemId", "userId", act, created_at, updated_at) + VALUES (fee_msats, item_id, user_id, 'FEE', now_utc(), now_utc()) + RETURNING id INTO item_act_id; + + -- leave the rest as a tip + INSERT INTO "ItemAct" (msats, "itemId", "userId", act, created_at, updated_at) + VALUES (act_msats, item_id, user_id, 'TIP', now_utc(), now_utc()); + + -- denormalize bounty paid (if applicable) + PERFORM bounty_paid_after_act(item_id, user_id); + + -- add sats to actees' balance and stacked count + FOR fwd_entry IN SELECT "userId", "pct" FROM "ItemForward" WHERE "itemId" = item_id + LOOP + -- fwd_msats represents the sats for this forward recipient from this particular tip action + fwd_msats := act_msats * fwd_entry.pct / 100; + -- keep track of how many msats have been forwarded, so we can give any remaining to OP + total_fwd_msats := fwd_msats + total_fwd_msats; + + UPDATE users + SET msats = msats + fwd_msats, "stackedMsats" = "stackedMsats" + fwd_msats + WHERE id = fwd_entry."userId"; + END LOOP; + + -- Give OP any remaining msats after forwards have been applied + IF act_msats - total_fwd_msats > 0 THEN + UPDATE users + SET msats = msats + act_msats - total_fwd_msats, "stackedMsats" = "stackedMsats" + act_msats - total_fwd_msats + WHERE id = (SELECT "userId" FROM "Item" WHERE id = item_id); + END IF; + ELSE -- BOOST, POLL, DONT_LIKE_THIS, STREAM + -- call to influence if DONT_LIKE_THIS weightedDownVotes + IF act = 'DONT_LIKE_THIS' THEN + PERFORM weighted_downvotes_after_act(item_id, user_id, act_sats); + END IF; + + INSERT INTO "ItemAct" (msats, "itemId", "userId", act, created_at, updated_at) + VALUES (act_msats, item_id, user_id, act, now_utc(), now_utc()) + RETURNING id INTO item_act_id; + END IF; + + -- store referral effects + PERFORM referral_act(item_act_id); + + RETURN 0; +END; +$$; \ No newline at end of file diff --git a/worker/wallet.js b/worker/wallet.js index 81ba8f11..48134faf 100644 --- a/worker/wallet.js +++ b/worker/wallet.js @@ -11,7 +11,7 @@ function checkInvoice ({ boss, models, lnd }) { try { inv = await getInvoice({ id: hash, lnd }) } catch (err) { - console.log(err) + console.log(err, hash) // on lnd related errors, we manually retry so we don't exponentially backoff await boss.send('checkInvoice', { hash }, walletOptions) return