)
}
diff --git a/prisma/migrations/20231226230356_item_act_negative/migration.sql b/prisma/migrations/20231226230356_item_act_negative/migration.sql
new file mode 100644
index 00000000..a701daeb
--- /dev/null
+++ b/prisma/migrations/20231226230356_item_act_negative/migration.sql
@@ -0,0 +1,88 @@
+-- Update item_act to noop on negative or zero sats
+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();
+
+ IF act_sats <= 0 THEN
+ RETURN 0;
+ END IF;
+
+ 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