diff --git a/packages/api/src/routers/_app.ts b/packages/api/src/routers/_app.ts
index cf4b4e089..2dd287a15 100644
--- a/packages/api/src/routers/_app.ts
+++ b/packages/api/src/routers/_app.ts
@@ -7,6 +7,7 @@ import { tagRouter } from './tag'
import { secretShopRouter } from './secretShop'
import { sendAccountRouter } from './sendAccount'
import { accountRecoveryRouter } from './account-recovery/router'
+import { referralsRouter } from './referrals'
export const appRouter = createTRPCRouter({
chainAddress: chainAddressRouter,
@@ -16,6 +17,7 @@ export const appRouter = createTRPCRouter({
distribution: distributionRouter,
secretShop: secretShopRouter,
sendAccount: sendAccountRouter,
+ referrals: referralsRouter,
})
export type AppRouter = typeof appRouter
diff --git a/packages/api/src/routers/referrals.ts b/packages/api/src/routers/referrals.ts
new file mode 100644
index 000000000..4d2435415
--- /dev/null
+++ b/packages/api/src/routers/referrals.ts
@@ -0,0 +1,44 @@
+import { TRPCError } from '@trpc/server'
+import { createTRPCRouter, protectedProcedure } from '../trpc'
+import debug from 'debug'
+import { supabaseAdmin } from 'app/utils/supabase/admin'
+const log = debug('api:routers:referrals')
+
+export const referralsRouter = createTRPCRouter({
+ getReferred: protectedProcedure.query(async ({ ctx }) => {
+ const { user } = ctx.session
+ if (!user) {
+ throw new TRPCError({
+ code: 'UNAUTHORIZED',
+ message: 'Unauthorized',
+ })
+ }
+ const { data: referred, error } = await supabaseAdmin
+ .from('referrals')
+ .select('*')
+ .eq('referred_id', user.id)
+ .single()
+ if (error && error.code !== 'PGRST116') {
+ log('referrals error', error)
+ throw new TRPCError({
+ code: 'INTERNAL_SERVER_ERROR',
+ message: error.message,
+ })
+ }
+ if (!referred) return null
+ const { data: referrerSendAccount, error: referrerError } = await supabaseAdmin
+ .from('send_accounts')
+ .select('*')
+ .eq('id', referred.referrer_id)
+ .single()
+ if (referrerError && referrerError.code !== 'PGRST116') {
+ log('referrals error', referrerError)
+ throw new TRPCError({
+ code: 'INTERNAL_SERVER_ERROR',
+ message: referrerError.message,
+ })
+ }
+
+ return { referred, referrerSendAccount }
+ }),
+})
diff --git a/packages/app/features/account/rewards/activity/__snapshots__/screen.test.tsx.snap b/packages/app/features/account/rewards/activity/__snapshots__/screen.test.tsx.snap
index b57f8420e..b80e1886d 100644
--- a/packages/app/features/account/rewards/activity/__snapshots__/screen.test.tsx.snap
+++ b/packages/app/features/account/rewards/activity/__snapshots__/screen.test.tsx.snap
@@ -1,18 +1,61 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ActivityRewardsScreen renders: ActivityRewardsScreen 1`] = `
-[
+
+
-
-
-
-
-
-
- Unlock
-
- Extra Rewards
-
-
- Register at least 1 Sendtag, maintain the minimum balance,
-
- avoid selling, and refer others for a bonus multiplier.
-
-
+ />
@@ -175,1182 +122,686 @@ exports[`ActivityRewardsScreen renders: ActivityRewardsScreen 1`] = `
accessibilityRole="header"
style={
{
- "color": "#FFFFFF",
+ "color": "white",
"fontFamily": "System",
- "fontSize": 23,
- "fontWeight": "600",
- "lineHeight": 27,
+ "fontSize": 30,
+ "fontWeight": "700",
+ "lineHeight": 34,
"marginBottom": 0,
"marginLeft": 0,
"marginRight": 0,
"marginTop": 0,
- "paddingRight": 7,
"textTransform": "none",
"userSelect": "auto",
}
}
suppressHighlighting={true}
>
- July Rewards
+ Unlock
+
+ Extra Rewards
-
+ Register at least 1 Sendtag, maintain the minimum balance,
+
+ avoid selling, and refer others for a bonus multiplier.
+
+
+
+
+
+ July Rewards
+
+
+
+
+
-
- July 2024
-
-
+
-
+ Your SEND Balance
+
+
-
-
-
-
-
-
-
+ 0 SEND
+
+
-
- Your SEND Balance
-
- 0 SEND
+ Min. Balance
-
-
-
-
- Min. Balance
-
-
-
+
-
+
-
-
-
-
-
-
+
+
+
+
+
-
+
- Sendtag Registered
-
-
+
-
-
-
+
-
+
-
-
-
+ }
+ />
+
+
-
+
+
-
- Perks
-
-
-
+ Perks
+
-
- Multiplier
-
-
+
+
+
-
+ }
+ suppressHighlighting={true}
+ >
+ Multiplier
+
-
- Estimated July
-
-
-
-
- 1 SEND
-
-
-
-
+ />
- ,
-
+
+ Total July
+
+
+ 1 SEND
+
-
- Select Month
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- July 2024
-
-
-
-
-
-
-
+
-
+
-
- ,
-]
+
+
`;
diff --git a/packages/app/features/account/rewards/activity/screen.test.tsx b/packages/app/features/account/rewards/activity/screen.test.tsx
index 001736317..44c9973ea 100644
--- a/packages/app/features/account/rewards/activity/screen.test.tsx
+++ b/packages/app/features/account/rewards/activity/screen.test.tsx
@@ -8,8 +8,8 @@ jest.mock('app/utils/distributions', () => ({
{
number: 7,
chain_id: 845337,
- qualification_end: new Date(Date.UTC(2024, 6, 30, 11, 59, 59)),
- timezone_adjusted_qualification_end: new Date(Date.UTC(2024, 6, 30, 11, 59, 59)),
+ qualification_end: new Date(2024, 6, 30, 11, 59, 59),
+ timezone_adjusted_qualification_end: new Date(2024, 6, 30, 11, 59, 59),
distribution_shares: [
{
amount: 1,
@@ -117,7 +117,7 @@ jest.mock('app/utils/useSendAccountBalances', () => ({
describe('ActivityRewardsScreen', () => {
it('renders', async () => {
jest.useFakeTimers()
- jest.setSystemTime(Date.UTC(2024, 6, 12))
+ jest.setSystemTime(Date.UTC(2024, 7, 12))
render(
@@ -125,9 +125,9 @@ describe('ActivityRewardsScreen', () => {
)
await act(async () => {
- jest.advanceTimersByTime(2000)
+ jest.advanceTimersByTime(5000)
})
- expect(screen.getByTestId('SelectDistributionDate')).toBeVisible()
+
expect(screen.toJSON()).toMatchSnapshot('ActivityRewardsScreen')
})
})
diff --git a/packages/app/features/account/screen.tsx b/packages/app/features/account/screen.tsx
index b4aac8bbb..d57fc05e7 100644
--- a/packages/app/features/account/screen.tsx
+++ b/packages/app/features/account/screen.tsx
@@ -20,7 +20,6 @@ import {
IconShare,
IconBadgeCheck,
IconArrowRight,
- IconQRFull,
IconLeaderboard,
} from 'app/components/icons'
import { getReferralHref } from 'app/utils/getReferralLink'
diff --git a/packages/app/features/account/sendtag/checkout/checkout-form.tsx b/packages/app/features/account/sendtag/checkout/checkout-form.tsx
index ee88ee0b4..6953909a9 100644
--- a/packages/app/features/account/sendtag/checkout/checkout-form.tsx
+++ b/packages/app/features/account/sendtag/checkout/checkout-form.tsx
@@ -44,6 +44,7 @@ import { CheckoutTagSchema } from './CheckoutTagSchema'
import { ConfirmButton } from './components/checkout-confirm-button'
import { SendTagPricingDialog, SendTagPricingTooltip } from './SendTagPricingDialog'
import formatAmount from 'app/utils/formatAmount'
+import { api } from 'app/utils/api'
export const CheckoutForm = () => {
const user = useUser()
@@ -56,6 +57,7 @@ export const CheckoutForm = () => {
const has5Tags = user?.tags?.length === 5
const media = useMedia()
const router = useRouter()
+ const { data: referred, isLoading: isLoadingReferred } = api.referrals.getReferred.useQuery()
async function createSendTag({ name }: z.infer) {
if (!user.user) return console.error('No user')
@@ -277,9 +279,16 @@ export const CheckoutForm = () => {
)
}}
-
-
-
+ {(() => {
+ switch (true) {
+ case isLoadingReferred:
+ return
+ case !referred:
+ return
+ default:
+ return null
+ }
+ })()}
{hasPendingTags && (
@@ -389,8 +398,8 @@ function ReferredBy() {
{referrer && (
-
-
+
+
@@ -409,8 +418,8 @@ function ReferredBy() {
}
})()}
-
-
+
+
)}
{referrerError && (
diff --git a/packages/app/features/account/sendtag/checkout/checkout-utils.ts b/packages/app/features/account/sendtag/checkout/checkout-utils.ts
index d064a44a7..f54abfa3a 100644
--- a/packages/app/features/account/sendtag/checkout/checkout-utils.ts
+++ b/packages/app/features/account/sendtag/checkout/checkout-utils.ts
@@ -8,6 +8,7 @@ import {
import type { SupabaseClient } from '@supabase/supabase-js'
import { queryOptions, useQuery } from '@tanstack/react-query'
import { reward, total } from 'app/data/sendtags'
+import { api } from 'app/utils/api'
import { assert } from 'app/utils/assert'
import { useSendAccount } from 'app/utils/send-accounts'
import { useSupabase } from 'app/utils/supabase/useSupabase'
@@ -45,7 +46,6 @@ export function useReferralCode() {
queryFn: () => getCookie(REFERRAL_COOKIE_NAME) || null,
})
}
-
/**
* Fetches the referrer profile by referral code or tag.
* If the referrer is the same as the profile, returns null. The referrer should also have a send account and sendtag.
@@ -182,10 +182,13 @@ export function useSendtagCheckout() {
const pendingTags = usePendingTags() ?? []
const amountDue = useMemo(() => total(pendingTags ?? []), [pendingTags])
const { data: referrer } = useReferrer()
+ const { data: referred } = api.referrals.getReferred.useQuery()
const { data: reward } = useReferralReward({ tags: pendingTags })
+ const referrerAddress = referred?.referrerSendAccount?.address ?? referrer?.address ?? zeroAddress
+
const checkoutArgs = useMemo(
- () => [amountDue, referrer?.address ?? zeroAddress, reward ?? 0n] as const,
- [amountDue, referrer, reward]
+ () => [amountDue, referrerAddress ?? zeroAddress, reward ?? 0n] as const,
+ [amountDue, referrerAddress, reward]
)
const calls = useMemo(
() => [
diff --git a/packages/app/features/leaderboard/__snapshots__/screen.test.tsx.snap b/packages/app/features/leaderboard/__snapshots__/screen.test.tsx.snap
index 5d88c3d9a..4695fc2fd 100644
--- a/packages/app/features/leaderboard/__snapshots__/screen.test.tsx.snap
+++ b/packages/app/features/leaderboard/__snapshots__/screen.test.tsx.snap
@@ -355,45 +355,6 @@ exports[`LeaderboardScreen renders leaderboard screen: LeaderboardScreen 1`] = `
-
-
-
diff --git a/packages/contracts/script/anvil-add-send-merkle-drop-fixtures.ts b/packages/contracts/script/anvil-add-send-merkle-drop-fixtures.ts
index 8b6faf685..fe1b0ee27 100755
--- a/packages/contracts/script/anvil-add-send-merkle-drop-fixtures.ts
+++ b/packages/contracts/script/anvil-add-send-merkle-drop-fixtures.ts
@@ -84,7 +84,6 @@ void (async function main() {
// process.exit(1)
// return
- // biome-ignore lint/correctness/noUnreachable: still cooking
console.log(chalk.blue('Geting distribution merkle root from API'))
const { root, total } = await fetch('http://localhost:3050/distributor/merkle', {
method: 'POST',
diff --git a/packages/playwright/tests/account-sendtag-checkout.onboarded.spec.ts b/packages/playwright/tests/account-sendtag-checkout.onboarded.spec.ts
index 807c9fe55..6bb382664 100644
--- a/packages/playwright/tests/account-sendtag-checkout.onboarded.spec.ts
+++ b/packages/playwright/tests/account-sendtag-checkout.onboarded.spec.ts
@@ -130,6 +130,16 @@ const checkReferralCodeVisibility = async (
await expect(referredBy).toBeVisible()
}
+const checkReferralCodeHidden = async (
+ checkoutPage: CheckoutPage,
+ referrer: { tags: string[]; referral_code: string }
+) => {
+ const refcode = checkoutPage.page.getByLabel('Referral Code:')
+ const referredBy = checkoutPage.page.getByText(`/${referrer.tags[0]}`)
+ await expect(refcode).toBeHidden()
+ await expect(referredBy).toBeHidden()
+}
+
const verifyCheckoutReceipt = async (
supabase: SupabaseClient,
tagsToRegister: string[],
@@ -268,7 +278,7 @@ test('can refer a tag', async ({
send_id: myProfile.send_id,
},
data: {
- tags: tagsToRegister,
+ tags: [tagsToRegister[0]],
},
})
@@ -299,7 +309,7 @@ test('can refer multiple tags in separate transactions', async ({
await confirmTags(checkoutPage, firstTags)
await verifyTagsInDatabase(supabase, firstTags)
await verifyCheckoutReceipt(supabase, firstTags, referrerSendAccount.address)
- await verifyActivityFeed(supabase, {
+ await expect(supabase).toHaveEventInActivityFeed({
event_name: 'referrals',
from_user: {
send_id: referrer.send_id,
@@ -310,7 +320,7 @@ test('can refer multiple tags in separate transactions', async ({
send_id: myProfile.send_id,
},
data: {
- tags: firstTags,
+ tags: [firstTags[0]],
},
})
const firstRewardAmount = await verifyReferralReward(
@@ -327,7 +337,7 @@ test('can refer multiple tags in separate transactions', async ({
for (const tagName of secondTags) {
await addPendingTag(checkoutPage, tagName)
}
- await checkReferralCodeVisibility(checkoutPage, {
+ await checkReferralCodeHidden(checkoutPage, {
referral_code: referrer.referral_code,
tags: referrerTags,
})
@@ -339,20 +349,6 @@ test('can refer multiple tags in separate transactions', async ({
await confirmTags(checkoutPage, secondTags)
await verifyTagsInDatabase(supabase, [...firstTags, ...secondTags])
await verifyCheckoutReceipt(supabase, secondTags, referrerSendAccount.address)
- await verifyActivityFeed(supabase, {
- event_name: 'referrals',
- from_user: {
- send_id: referrer.send_id,
- tags: referrerTags,
- },
- to_user: {
- id: myProfile.id,
- send_id: myProfile.send_id,
- },
- data: {
- tags: secondTags,
- },
- })
const secondRewardAmount = await verifyReferralReward(
referrerSendAccount.address as `0x${string}`,
secondTags,
@@ -368,7 +364,7 @@ test('can refer multiple tags in separate transactions', async ({
expect(leaderboardData?.[0]).toBeTruthy()
assert(!!leaderboardData?.[0], 'leaderboard data not found')
expect(leaderboardData[0].rewards_usdc).toBe((firstRewardAmount + secondRewardAmount).toString())
- expect(leaderboardData[0].referrals).toBe((firstTags.length + secondTags.length).toString())
+ expect(leaderboardData[0].referrals).toBe((1).toString())
log('leaderboard', leaderboardData)
log('done')
diff --git a/supabase/database-generated.types.ts b/supabase/database-generated.types.ts
index e7e1194bb..f9746d87a 100644
--- a/supabase/database-generated.types.ts
+++ b/supabase/database-generated.types.ts
@@ -429,7 +429,7 @@ export type Database = {
{
foreignKeyName: "referrals_referred_id_fkey"
columns: ["referred_id"]
- isOneToOne: false
+ isOneToOne: true
referencedRelation: "profiles"
referencedColumns: ["id"]
},
diff --git a/supabase/migrations/20241025230716_update_referral_logic_in_confirm_sendtag_function.sql b/supabase/migrations/20241025230716_update_referral_logic_in_confirm_sendtag_function.sql
new file mode 100644
index 000000000..cb101b4cd
--- /dev/null
+++ b/supabase/migrations/20241025230716_update_referral_logic_in_confirm_sendtag_function.sql
@@ -0,0 +1,110 @@
+SET check_function_bodies = OFF;
+
+CREATE OR REPLACE FUNCTION public.confirm_tags(tag_names citext[], event_id text, referral_code_input text)
+ RETURNS void
+ LANGUAGE plpgsql
+ SECURITY DEFINER
+ SET search_path TO 'public'
+ AS $function$
+DECLARE
+ tag_owner_ids uuid[];
+ distinct_user_ids int;
+ tag_owner_id uuid;
+ referrer_id uuid;
+ _event_id alias FOR $2;
+BEGIN
+ -- Check if the tags exist and fetch their owners.
+ SELECT
+ array_agg(user_id) INTO tag_owner_ids
+ FROM
+ public.tags
+ WHERE
+ name = ANY (tag_names)
+ AND status = 'pending'::public.tag_status;
+ -- If any of the tags do not exist or are not in pending status, throw an error.
+ IF array_length(tag_owner_ids, 1) <> array_length(tag_names, 1) THEN
+ RAISE EXCEPTION 'One or more tags do not exist or are not in pending status.';
+ END IF;
+ -- Check if all tags belong to the same user
+ SELECT
+ count(DISTINCT user_id) INTO distinct_user_ids
+ FROM
+ unnest(tag_owner_ids) AS user_id;
+ IF distinct_user_ids <> 1 THEN
+ RAISE EXCEPTION 'Tags must belong to the same user.';
+ END IF;
+ -- Fetch single user_id
+ SELECT DISTINCT
+ user_id INTO tag_owner_id
+ FROM
+ unnest(tag_owner_ids) AS user_id;
+ IF event_id IS NULL OR event_id = '' THEN
+ RAISE EXCEPTION 'Receipt event ID is required for paid tags.';
+ END IF;
+ -- Ensure event_id matches the sender
+ IF (
+ SELECT
+ count(DISTINCT scr.sender)
+ FROM
+ public.sendtag_checkout_receipts scr
+ JOIN send_accounts sa ON decode(substring(sa.address, 3), 'hex') = scr.sender
+ WHERE
+ scr.event_id = _event_id AND sa.user_id = tag_owner_id) <> 1 THEN
+ RAISE EXCEPTION 'Receipt event ID does not match the sender';
+ END IF;
+ -- save receipt event_id
+ INSERT INTO public.receipts(
+ event_id,
+ user_id)
+ VALUES (
+ _event_id,
+ tag_owner_id);
+ -- Associate the tags with the onchain event
+ INSERT INTO public.tag_receipts(
+ tag_name,
+ event_id)
+ SELECT
+ unnest(tag_names),
+ event_id;
+ -- Confirm the tags
+ UPDATE
+ public.tags
+ SET
+ status = 'confirmed'::public.tag_status
+ WHERE
+ name = ANY (tag_names)
+ AND status = 'pending'::public.tag_status;
+ -- Create referral code redemption (only if it doesn't exist)
+ IF referral_code_input IS NOT NULL AND referral_code_input <> '' THEN
+ SELECT
+ id INTO referrer_id
+ FROM
+ public.profiles
+ WHERE
+ referral_code = referral_code_input;
+ IF referrer_id IS NOT NULL AND referrer_id <> tag_owner_id THEN
+ -- Referrer cannot be the tag owner.
+ -- Check if a referral already exists for this user
+ IF NOT EXISTS (
+ SELECT
+ 1
+ FROM
+ public.referrals
+ WHERE
+ referred_id = tag_owner_id) THEN
+ -- Insert only one referral for the user
+ INSERT INTO public.referrals(
+ referrer_id,
+ referred_id,
+ tag)
+ SELECT
+ referrer_id,
+ tag_owner_id,
+ unnest(tag_names)
+ LIMIT 1;
+ END IF;
+ END IF;
+END IF;
+END;
+$function$;
+
diff --git a/supabase/migrations/20241025231201_keep_only_first_referral.sql b/supabase/migrations/20241025231201_keep_only_first_referral.sql
new file mode 100644
index 000000000..3357dcd0b
--- /dev/null
+++ b/supabase/migrations/20241025231201_keep_only_first_referral.sql
@@ -0,0 +1,21 @@
+-- Update existing referrals to keep only the oldest referral for each user
+WITH ranked_referrals AS (
+ SELECT
+ r.*,
+ ROW_NUMBER() OVER (PARTITION BY referred_id ORDER BY t.created_at) AS rn
+ FROM
+ public.referrals r
+ JOIN public.tags t ON r.tag = t.name)
+DELETE FROM public.referrals
+WHERE id IN (
+ SELECT
+ id
+ FROM
+ ranked_referrals
+ WHERE
+ rn > 1);
+
+-- Add UNIQUE constraint on referred_id
+ALTER TABLE public.referrals
+ ADD CONSTRAINT unique_referred_id UNIQUE (referred_id);
+
diff --git a/supabase/migrations/20241025234650_update_leaderboard_referrals_all_time.sql b/supabase/migrations/20241025234650_update_leaderboard_referrals_all_time.sql
new file mode 100644
index 000000000..ad11b05d4
--- /dev/null
+++ b/supabase/migrations/20241025234650_update_leaderboard_referrals_all_time.sql
@@ -0,0 +1,67 @@
+-- Update the referral leaderboard table with data from the referrals and sendtag_checkout_receipts tables
+UPDATE
+ private.leaderboard_referrals_all_time l
+SET
+ referrals = tmp.referrals,
+ rewards_usdc = tmp.rewards_usdc,
+ updated_at = now()
+FROM (
+ SELECT
+ p.id AS user_id,
+ coalesce(count(DISTINCT r.referred_id), 0) AS referrals,
+ coalesce(sum(scr.reward), 0) AS rewards_usdc
+ FROM
+ profiles p
+ LEFT JOIN send_accounts sa ON p.id = sa.user_id
+ LEFT JOIN (
+ SELECT
+ referrer,
+ sum(reward) AS reward
+ FROM
+ sendtag_checkout_receipts
+ GROUP BY
+ referrer) scr ON decode(substr(sa.address, 3), 'hex') = scr.referrer
+ LEFT JOIN referrals r ON r.referrer_id = p.id
+GROUP BY
+ p.id
+HAVING
+ count(DISTINCT r.referred_id) > 0
+ OR sum(scr.reward) > 0) AS tmp
+WHERE
+ l.user_id = tmp.user_id;
+
+-- Insert new records for users not already in the leaderboard
+INSERT INTO private.leaderboard_referrals_all_time(
+ user_id,
+ referrals,
+ rewards_usdc,
+ updated_at)
+SELECT
+ p.id AS user_id,
+ coalesce(count(DISTINCT r.referred_id), 0) AS referrals,
+ coalesce(sum(scr.reward), 0) AS rewards_usdc,
+ now() AS updated_at
+FROM
+ profiles p
+ LEFT JOIN send_accounts sa ON p.id = sa.user_id
+ LEFT JOIN (
+ SELECT
+ referrer,
+ sum(reward) AS reward
+ FROM
+ sendtag_checkout_receipts
+ GROUP BY
+ referrer) scr ON decode(substr(sa.address, 3), 'hex') = scr.referrer
+ LEFT JOIN referrals r ON r.referrer_id = p.id
+WHERE
+ p.id NOT IN (
+ SELECT
+ user_id
+ FROM
+ private.leaderboard_referrals_all_time)
+GROUP BY
+ p.id
+HAVING
+ count(DISTINCT r.referred_id) > 0
+ OR sum(scr.reward) > 0;
+
diff --git a/supabase/tests/tag_referrals_test.sql b/supabase/tests/tag_referrals_test.sql
index 9414e510b..35126406c 100644
--- a/supabase/tests/tag_referrals_test.sql
+++ b/supabase/tests/tag_referrals_test.sql
@@ -1,34 +1,41 @@
-- Tag referrals test
BEGIN;
-
-SELECT plan(7);
-
+SELECT
+ plan(9);
CREATE EXTENSION "basejump-supabase_test_helpers";
-
GRANT USAGE ON SCHEMA tests TO service_role;
-
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA tests TO service_role;
-
-- Creating a test user
-SELECT tests.create_supabase_user('bob');
-
-SELECT tests.create_supabase_user('alice');
-
-INSERT INTO send_accounts (user_id, address, chain_id, init_code)
+SELECT
+ tests.create_supabase_user('bob');
+SELECT
+ tests.create_supabase_user('alice');
+SELECT
+ tests.create_supabase_user('bob2');
+INSERT INTO send_accounts(
+ user_id,
+ address,
+ chain_id,
+ init_code)
VALUES (
- tests.get_supabase_uid('bob'),
+ tests.get_supabase_uid(
+ 'bob'),
'0xb0b0000000000000000000000000000000000000',
1,
- '\\x00112233445566778899AABBCCDDEEFF'
-),
+ '\\x00112233445566778899AABBCCDDEEFF'),
(
- tests.get_supabase_uid('alice'),
+ tests.get_supabase_uid(
+ 'alice'),
'0xa71ce00000000000000000000000000000000000',
1,
- '\\x00112233445566778899AABBCCDDEEFF'
-);
-
-INSERT INTO sendtag_checkout_receipts (
+ '\\x00112233445566778899AABBCCDDEEFF'),
+(
+ tests.get_supabase_uid(
+ 'bob2'),
+ '0xb0b2000000000000000000000000000000000000',
+ 1,
+ '\\x00112233445566778899AABBCCDDEEFF');
+INSERT INTO sendtag_checkout_receipts(
chain_id,
log_addr,
tx_hash,
@@ -42,8 +49,7 @@ INSERT INTO sendtag_checkout_receipts (
sender,
amount,
referrer,
- reward
-)
+ reward)
VALUES (
8453,
'\x5afe000000000000000000000000000000000000',
@@ -58,8 +64,7 @@ VALUES (
'\xb0b0000000000000000000000000000000000000',
1,
'\x0000000000000000000000000000000000000000',
- 0
-),
+ 0),
( -- confirm for sendtag @alice
8453,
'\x5afe000000000000000000000000000000000000',
@@ -74,8 +79,7 @@ VALUES (
'\xa71ce00000000000000000000000000000000000',
1,
'\x0000000000000000000000000000000000000000',
- 0
-),
+ 0),
( -- confirm for sendtag @wonderland
8453,
'\x5afe000000000000000000000000000000000000',
@@ -90,8 +94,8 @@ VALUES (
'\xa71ce00000000000000000000000000000000000',
1,
'\x0000000000000000000000000000000000000000',
- 0
-), ( -- confirm for sendtag @whiterabbit
+ 0),
+( -- confirm for sendtag @whiterabbit
8453,
'\x5afe000000000000000000000000000000000000',
'\x1234567890123456789012345678901234567890123456789012345678901234',
@@ -105,174 +109,203 @@ VALUES (
'\xa71ce00000000000000000000000000000000000',
1,
'\x0000000000000000000000000000000000000000',
- 0
-);
-
+ 0),
+( -- confirm for sendtag @redroses
+ 8453,
+ '\x5afe000000000000000000000000000000000000',
+ '\x1234567890123456789012345678901234567890123456789012345678901234',
+ 'sendtag_checkout_receipts',
+ 'redroses',
+ 5,
+ 0,
+ 0,
+ 0,
+ 1234567890,
+ '\xa71ce00000000000000000000000000000000000',
+ 1,
+ '\x0000000000000000000000000000000000000000',
+ 0);
-- Inserting a tag for test user
-INSERT INTO tags (name, user_id)
+INSERT INTO tags(
+ name,
+ user_id)
VALUES (
'alice',
- tests.get_supabase_uid('alice')
-);
-
+ tests.get_supabase_uid(
+ 'alice'));
+INSERT INTO tags(
+ name,
+ user_id)
+VALUES (
+ 'redroses',
+ tests.get_supabase_uid(
+ 'alice'));
-- Confirm tags with the service role
-SELECT tests.clear_authentication();
-
-SELECT set_config('role', 'service_role', true);
-
-SELECT confirm_tags(
- '{alice}',
- (
- SELECT event_id
- FROM sendtag_checkout_receipts
- WHERE sender = '\xa71ce00000000000000000000000000000000000' AND src_name = 'alice'
- ),
- (
- SELECT referral_code
- FROM public.profiles
- WHERE id = tests.get_supabase_uid('bob')
- )
-);
-
+SELECT
+ tests.clear_authentication();
+SELECT
+ set_config('role', 'service_role', TRUE);
+SELECT
+ confirm_tags('{alice}',(
+ SELECT
+ event_id
+ FROM sendtag_checkout_receipts
+ WHERE
+ sender = '\xa71ce00000000000000000000000000000000000'
+ AND src_name = 'alice'),(
+ SELECT
+ referral_code
+ FROM public.profiles
+ WHERE
+ id = tests.get_supabase_uid('bob')));
-- Verify that the tags were confirmed
-SELECT isnt_empty(
- $$
- SELECT *
- FROM tags
- WHERE status = 'confirmed'::tag_status
- and user_id = tests.get_supabase_uid('alice') $$,
- 'Tags should be confirmed'
-);
-
-SELECT isnt_empty(
- $test$
- SELECT tag
- FROM referrals
- WHERE referrer_id = tests.get_supabase_uid('bob')
- and referred_id = tests.get_supabase_uid('alice') $test$,
- 'Referral should be created'
-);
-
+SELECT
+ isnt_empty($$
+ SELECT
+ * FROM tags
+ WHERE
+ status = 'confirmed'::tag_status
+ AND user_id = tests.get_supabase_uid('alice') $$, 'Tags should be confirmed');
+SELECT
+ isnt_empty($test$
+ SELECT
+ tag FROM referrals
+ WHERE
+ referrer_id = tests.get_supabase_uid('bob')
+ AND referred_id = tests.get_supabase_uid('alice') $test$, 'Referral should be created');
+-- Verify user cannot have two referrers
+SELECT
+ confirm_tags('{redroses}',(
+ SELECT
+ event_id
+ FROM sendtag_checkout_receipts
+ WHERE
+ sender = '\xa71ce00000000000000000000000000000000000'
+ AND src_name = 'redroses'),(
+ SELECT
+ referral_code
+ FROM public.profiles
+ WHERE
+ id = tests.get_supabase_uid('bob2')));
+-- Verify that the tags were confirmed
+SELECT
+ isnt_empty($$
+ SELECT
+ * FROM tags
+ WHERE
+ status = 'confirmed'::tag_status
+ AND user_id = tests.get_supabase_uid('alice') $$, 'Tags should be confirmed');
+-- Verify no referral was created
+SELECT
+ is_empty($test$
+ SELECT
+ tag FROM referrals
+ WHERE
+ referrer_id = tests.get_supabase_uid('bob2')
+ AND referred_id = tests.get_supabase_uid('alice') $test$, 'Referral should not be created');
-- Verify user can see referral activity
-SELECT tests.authenticate_as('bob');
-
-SELECT results_eq(
- $$
- SELECT data->>'tags', (from_user).tags, (to_user).tags
- FROM activity_feed
- WHERE event_name = 'referrals'
- $$,
- $$
- VALUES ('["alice"]',
- null::text[],
- '{"alice"}'::text[]) $$,
- 'verify referral activity was created'
-);
-
+SELECT
+ tests.authenticate_as('bob');
+SELECT
+ results_eq($$
+ SELECT
+ data ->> 'tags',(from_user).tags,(to_user).tags FROM activity_feed
+ WHERE
+ event_name = 'referrals' $$, $$
+ VALUES ('["alice"]', NULL::text[], '{"alice","redroses"}'::text[]) $$, 'verify referral activity was created');
-- admin deleting referral should delete activity
-SELECT tests.clear_authentication();
-
-SELECT set_config('role', 'service_role', true);
-
+SELECT
+ tests.clear_authentication();
+SELECT
+ set_config('role', 'service_role', TRUE);
DELETE FROM referrals
-WHERE
- referrer_id = tests.get_supabase_uid('bob')
+WHERE referrer_id = tests.get_supabase_uid('bob')
AND referred_id = tests.get_supabase_uid('alice');
-
-SELECT results_eq(
- $$
- SELECT COUNT(*)::integer
- FROM activity
- WHERE event_name = 'referrals' and event_id = sha256(decode(replace(tests.get_supabase_uid('alice')::text, '-', ''), 'hex'))::text
- $$,
- $$
- VALUES (0) $$,
- 'verify referral activity was deleted'
-);
-
+SELECT
+ results_eq($$
+ SELECT
+ COUNT(*)::integer FROM activity
+ WHERE
+ event_name = 'referrals'
+ AND event_id = sha256(decode(replace(tests.get_supabase_uid('alice')::text, '-', ''), 'hex'))::text $$, $$
+ VALUES (0) $$, 'verify referral activity was deleted');
-- Verify invalid referral code still confirms tags
-SELECT tests.authenticate_as('alice');
-
-INSERT INTO tags (name, user_id)
+SELECT
+ tests.authenticate_as('alice');
+INSERT INTO tags(
+ name,
+ user_id)
VALUES (
'wonderland',
- tests.get_supabase_uid('alice')
-);
-
+ tests.get_supabase_uid(
+ 'alice'));
-- Confirm tags with the service role
-SELECT tests.clear_authentication();
-
-SELECT set_config('role', 'service_role', true);
-
-SELECT confirm_tags(
- '{wonderland}',
- (
- SELECT event_id
- FROM sendtag_checkout_receipts
- WHERE sender = '\xa71ce00000000000000000000000000000000000' AND src_name = 'wonderland'
- ),
- 'invalid'
-);
-
+SELECT
+ tests.clear_authentication();
+SELECT
+ set_config('role', 'service_role', TRUE);
+SELECT
+ confirm_tags('{wonderland}',(
+ SELECT
+ event_id
+ FROM sendtag_checkout_receipts
+ WHERE
+ sender = '\xa71ce00000000000000000000000000000000000'
+ AND src_name = 'wonderland'), 'invalid');
-- Verify that the tags were confirmed
-SELECT isnt_empty(
- $$
- SELECT *
- FROM tags
- WHERE status = 'confirmed'::tag_status
- and user_id = tests.get_supabase_uid('alice')
- and name = 'wonderland' $$,
- 'Tags should be confirmed'
-);
-
+SELECT
+ isnt_empty($$
+ SELECT
+ * FROM tags
+ WHERE
+ status = 'confirmed'::tag_status
+ AND user_id = tests.get_supabase_uid('alice')
+ AND name = 'wonderland' $$, 'Tags should be confirmed');
-- Verify passing my own referral code does not create a referral
-SELECT tests.authenticate_as('alice');
-
-INSERT INTO tags (name, user_id)
+SELECT
+ tests.authenticate_as('alice');
+INSERT INTO tags(
+ name,
+ user_id)
VALUES (
'whiterabbit',
- tests.get_supabase_uid('alice')
-);
-
+ tests.get_supabase_uid(
+ 'alice'));
-- Confirm tags with the service role
-SELECT tests.clear_authentication();
-
-SELECT set_config('role', 'service_role', true);
-
-SELECT confirm_tags(
- '{whiterabbit}',
- (
- SELECT event_id
- FROM sendtag_checkout_receipts
- WHERE sender = '\xa71ce00000000000000000000000000000000000' AND src_name = 'whiterabbit'
- ),
- (
- SELECT referral_code
- FROM public.profiles
- WHERE id = tests.get_supabase_uid('alice')
- )
-);
-
+SELECT
+ tests.clear_authentication();
+SELECT
+ set_config('role', 'service_role', TRUE);
+SELECT
+ confirm_tags('{whiterabbit}',(
+ SELECT
+ event_id
+ FROM sendtag_checkout_receipts
+ WHERE
+ sender = '\xa71ce00000000000000000000000000000000000'
+ AND src_name = 'whiterabbit'),(
+ SELECT
+ referral_code
+ FROM public.profiles
+ WHERE
+ id = tests.get_supabase_uid('alice')));
-- Verify that the tags were confirmed
-SELECT isnt_empty(
- $$
- SELECT *
- FROM tags
- WHERE status = 'confirmed'::tag_status
- and user_id = tests.get_supabase_uid('alice')
- and name = 'whiterabbit' $$,
- 'Tags should be confirmed'
-);
-
+SELECT
+ isnt_empty($$
+ SELECT
+ * FROM tags
+ WHERE
+ status = 'confirmed'::tag_status
+ AND user_id = tests.get_supabase_uid('alice')
+ AND name = 'whiterabbit' $$, 'Tags should be confirmed');
-- Verify no referral was created
-SELECT is_empty(
- $$
- SELECT *
- FROM referrals
- WHERE referrer_id = tests.get_supabase_uid('alice') $$,
- 'Referral should not be created'
-);
-
-SELECT finish();
-
+SELECT
+ is_empty($$
+ SELECT
+ * FROM referrals
+ WHERE
+ referrer_id = tests.get_supabase_uid('alice') $$, 'Referral should not be created');
+SELECT
+ finish();
ROLLBACK;
+