Skip to content

Commit 8cb594b

Browse files
authored
app: clean up paymaster activity (#470)
* app: clean up paymaster activity add labelAddress to activity hide fees from main activity * opt-in react query devtool * fix up account settings layout * clean up passkey texts * fix specs * one more test fix * one more activity test * type checking, type fixes, only retry 1 in CI * fix more type stuff * fix clean * don't use turbo * build first * try longer default, no retries * nvm, only retry once, 30s timeout
1 parent 3bdacbe commit 8cb594b

34 files changed

+118
-66
lines changed

.env.local.template

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=33fe0df7cd4f445f4e2ba7e0fd6ed314
1616
SNAPLET_HASH_KEY=sendapp
1717
SEND_ACCOUNT_FACTORY_PRIVATE_KEY=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
1818
SECRET_SHOP_PRIVATE_KEY=0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a
19+
# NEXT_PUBLIC_ENABLE_QUERY_DEV_TOOLS=1

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ jobs:
4242
env:
4343
YARN_ENABLE_HARDENED_MODE: 0
4444
SKIP_YARN_POST_INSTALL: 1
45+
- name: Build
46+
run: yarn build
4547
- name: Lint
4648
run: yarn lint
4749

environment.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ declare global {
3131
* Private key for the Send Account Factory wallet client used to create Send Accounts
3232
*/
3333
SEND_ACCOUNT_FACTORY_PRIVATE_KEY: string
34+
/**
35+
* Enables the query dev tool in the browser
36+
*/
37+
NEXT_PUBLIC_ENABLE_QUERY_DEV_TOOLS: string
3438
}
3539
}
3640
/**

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"web:prod": "yarn workspace next-app build",
1616
"web:prod:serve": "yarn workspace next-app serve",
1717
"fix": "manypkg fix",
18-
"postinstall": "test -n \"$SKIP_YARN_POST_INSTALL\" || (yarn check-deps && turbo build --filter='!next-app')",
18+
"postinstall": "test -n \"$SKIP_YARN_POST_INSTALL\" || (yarn check-deps && yarn build)",
1919
"build": "yarn workspaces foreach --all --exclude next-app run build",
2020
"biome:check": "biome check .",
2121
"biome:check:fix": "biome check . --apply",

packages/app/.eslintrc.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
/** @type {import("eslint").Linter.Config} */
22
module.exports = {
33
extends: ['custom', 'plugin:@tanstack/eslint-plugin-query/recommended'],
4-
ignorePatterns: ['.eslintrc.js'],
4+
ignorePatterns: ['.eslintrc.js', 'coverage/**'],
5+
parserOptions: {
6+
project: './tsconfig.json',
7+
tsconfigRootDir: __dirname,
8+
sourceType: 'module',
9+
},
510
}

packages/app/__mocks__/@my/wagmi/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ const mockMyWagmi = {
3434
sendTokenAddress: {
3535
845337: '0x3f14920c99BEB920Afa163031c4e47a3e03B3e4A',
3636
},
37+
tokenPaymasterAddress: {
38+
845337: '0x5e421172B27658f2bD83BCBD13738ADdE00E7CA9',
39+
},
3740
useWriteErc20Transfer: jest.fn().mockReturnValue({
3841
data: '0x123',
3942
writeContract: jest.fn(),
@@ -47,4 +50,5 @@ export const baseMainnetClient = mockMyWagmi.baseMainnetClient
4750
export const baseMainnet = mockMyWagmi.baseMainnet
4851
export const usdcAddress = mockMyWagmi.usdcAddress
4952
export const sendTokenAddress = mockMyWagmi.sendTokenAddress
53+
export const tokenPaymasterAddress = mockMyWagmi.tokenPaymasterAddress
5054
export default mockMyWagmi

packages/app/assets/img/react-native-svg.d.ts

Lines changed: 0 additions & 7 deletions
This file was deleted.

packages/app/features/account/settings/backup/confirm.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,10 @@ const AddPasskeySigner = ({ webauthnCred }: { webauthnCred: Tables<'webauthn_cre
8282
gap={'$2'}
8383
>
8484
<H2 size={'$8'} fontWeight={'300'} color={'$color05'}>
85-
Add Passkey as Signer
85+
Passkey {webauthnCred?.display_name} has been saved
8686
</H2>
8787
<Paragraph size={'$6'} fontWeight={'300'} color={'$color05'}>
88-
Your passkey {webauthnCred?.display_name} has been saved. Add your new passkey as a signer.
89-
This will allow you to sign transactions on your account with your new passkey.
88+
Add your new passkey as a Signer to authorize it to sign transactions on your account.
9089
</Paragraph>
9190
<YStack gap={'$2'} jc="center">
9291
<AddSignerButton webauthnCred={webauthnCred} />

packages/app/features/account/settings/backup/create.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,8 @@ const CreatePasskeyForm = ({
145145
Add Passkey
146146
</H1>
147147
<Paragraph size={'$6'} fontWeight={'300'} color={'$color05'}>
148-
Backup your Send Account by creating a passkey and adding it as a signer to your
149-
account. Passkeys are authorized devices that can sign transactions for your account.
148+
Secure your Send Account by adding up to 20 passkeys. Passkeys are trusted devices
149+
authorized to sign your account&apos;s transactions.
150150
</Paragraph>
151151
</YStack>
152152
)}

packages/app/features/account/settings/backup/index.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ import { useQuery, useQueryClient } from '@tanstack/react-query'
2424
import { IconDots, IconNote, IconX } from 'app/components/icons'
2525
import { SchemaForm } from 'app/utils/SchemaForm'
2626
import { assert } from 'app/utils/assert'
27+
import { byteaToBytes } from 'app/utils/byteaToBytes'
2728
import { formatTimeDate } from 'app/utils/formatTimeDate'
2829
import { COSEECDHAtoXY } from 'app/utils/passkeys'
29-
import { byteaToBytes } from 'app/utils/byteaToBytes'
3030
import { useSendAccount } from 'app/utils/send-accounts/useSendAccounts'
3131
import { useSupabase } from 'app/utils/supabase/useSupabase'
3232
import { throwIf } from 'app/utils/throwIf'
@@ -52,16 +52,16 @@ export const BackupScreen = () => {
5252
$theme-dark={{ color: '$lightGrayTextField' }}
5353
$theme-light={{ color: '$darkGrayTextField' }}
5454
>
55-
Add Passkey as Signer
55+
Add Passkeys as Signers
5656
</H1>
5757

5858
<Paragraph
5959
size={'$5'}
6060
$theme-dark={{ color: '$lightGrayTextField' }}
6161
$theme-light={{ color: '$darkGrayTextField' }}
6262
>
63-
Backup your Send Account by add up to 20 passkeys to your account. Passkeys are authorized
64-
devices that can sign transactions for your account.
63+
Secure your Send Account by adding up to 20 passkeys. Passkeys are trusted devices
64+
authorized to sign your account&apos;s transactions.
6565
</Paragraph>
6666
</YStack>
6767

packages/app/features/account/settings/layout.web.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Separator, XStack, YStack, useMedia } from '@my/ui'
1+
import { Separator, XStack, YStack } from '@my/ui'
22
import { SettingsLinks } from './SettingsLinks'
33

44
export type SettingsLayoutProps = {
@@ -13,9 +13,7 @@ export type SettingsLayoutProps = {
1313
}
1414

1515
export const SettingsLayout = ({ children }: SettingsLayoutProps) => {
16-
const media = useMedia()
17-
18-
return media.gtLg ? (
16+
return (
1917
<XStack f={1} pt={'$4'}>
2018
<YStack
2119
backgroundColor="$color1"
@@ -24,6 +22,8 @@ export const SettingsLayout = ({ children }: SettingsLayoutProps) => {
2422
transition: '200ms ease width',
2523
}}
2624
width="$14"
25+
display="none"
26+
$gtLg={{ display: 'flex' }}
2727
>
2828
<YStack width={'100%'} gap={'$4'}>
2929
<YStack jc={'space-between'} zIndex={4} flex={1} width={'100%'}>
@@ -33,12 +33,10 @@ export const SettingsLayout = ({ children }: SettingsLayoutProps) => {
3333
</YStack>
3434
</YStack>
3535
</YStack>
36-
<Separator vertical />
36+
<Separator display="none" $gtLg={{ display: 'flex' }} vertical />
3737
<YStack f={1} ai="center" ml="$8">
3838
<YStack width="100%">{children}</YStack>
3939
</YStack>
4040
</XStack>
41-
) : (
42-
<YStack width="100%">{children}</YStack>
4341
)
4442
}

packages/app/features/activity/utils/useActivityFeed.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
1+
import { tokenPaymasterAddress } from '@my/wagmi'
12
import type { PostgrestError } from '@supabase/postgrest-js'
23
import {
34
useInfiniteQuery,
45
type InfiniteData,
56
type UseInfiniteQueryResult,
67
} from '@tanstack/react-query'
8+
import { hexToBytea } from 'app/utils/hexToBytea'
79
import { useSupabase } from 'app/utils/supabase/useSupabase'
810
import { throwIf } from 'app/utils/throwIf'
911
import { EventArraySchema, type Activity } from 'app/utils/zod/activity'
1012
import type { ZodError } from 'zod'
1113

14+
const pgPaymasterCondValues = Object.values(tokenPaymasterAddress)
15+
.map((a) => hexToBytea(a))
16+
.join(',')
17+
1218
/**
1319
* Infinite query to fetch activity feed. Filters out activities with no from or to user (not a send app user).
1420
* @param pageSize - number of items to fetch per page
@@ -27,6 +33,8 @@ export function useActivityFeed({
2733
.from('activity_feed')
2834
.select('*')
2935
.or('from_user.not.is.null, to_user.not.is.null') // only show activities with a send app user
36+
.not('data->>t', 'in', `(${pgPaymasterCondValues})`) // filter out paymaster fees for gas
37+
.not('data->>f', 'in', `(${pgPaymasterCondValues})`) // filter out paymaster refunds
3038
.order('created_at', { ascending: false })
3139
.range(from, to)
3240
throwIf(error)

packages/app/features/auth/onboarding/DotLineBorder.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,17 @@ export const DotLineBorder = memo(
99
themed((props: IconProps) => {
1010
const { size: sizeToken, color, ...rest } = props
1111

12-
const size =
13-
// @ts-expect-error because tamagui expects size tokens to not be prefixed with $
14-
typeof sizeToken === 'string' ? getTokens().size[sizeToken] : props.size
12+
const size = typeof sizeToken === 'string' ? getTokens().size[sizeToken] : props.size
1513

1614
return (
15+
// @ts-expect-error some work to do here
1716
<Svg
1817
color={color as ColorTokens | undefined}
1918
fill="none"
20-
width={aspectRatio * size}
21-
height={size}
19+
// biome-ignore lint/suspicious/noExplicitAny: not sure how to get the size from the theme typed correctly
20+
width={(aspectRatio * size) as any}
21+
// biome-ignore lint/suspicious/noExplicitAny: not sure how to get the size from the theme typed correctly
22+
height={size as any}
2223
viewBox="0 0 47 310"
2324
{...rest}
2425
>

packages/app/features/home/screen.test.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { TamaguiProvider, config } from '@my/ui'
33
import { render } from '@testing-library/react-native'
44
import { HomeScreen } from './screen'
55

6+
jest.mock('@my/wagmi')
7+
68
jest.mock('app/utils/useUser', () => ({
79
useUser: jest.fn().mockReturnValue({
810
user: {

packages/app/features/home/utils/useTokenActivityFeed.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,8 @@ import {
77
} from '@tanstack/react-query'
88
import { useSupabase } from 'app/utils/supabase/useSupabase'
99
import { throwIf } from 'app/utils/throwIf'
10-
import {
11-
EventArraySchema,
12-
Events,
13-
SendAccountTransfersEventSchema,
14-
type Activity,
15-
} from 'app/utils/zod/activity'
16-
import { z, type ZodError } from 'zod'
17-
18-
const SendAccountTransfersEvenArraySchema = z.array(SendAccountTransfersEventSchema)
19-
20-
type SendAccountTransfersEventArray = z.infer<typeof SendAccountTransfersEvenArraySchema>
10+
import { EventArraySchema, Events, type Activity } from 'app/utils/zod/activity'
11+
import type { ZodError } from 'zod'
2112

2213
/**
2314
* Infinite query to fetch ERC-20 token activity feed.

packages/app/features/profile/AvatarProfile.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
import { Avatar, type AvatarProps, SizableText } from '@my/ui'
2-
import type { ProfileProp } from './screen'
1+
import type { Functions } from '@my/supabase/database.types'
2+
import { Avatar, SizableText, type AvatarProps } from '@my/ui'
33

4-
export function AvatarProfile({ profile, ...rest }: AvatarProps & { profile: ProfileProp }) {
4+
export function AvatarProfile({
5+
profile,
6+
...rest
7+
}: AvatarProps & { profile: Functions<'profile_lookup'>[number] }) {
58
return (
69
<Avatar testID="avatar" size="$8" br="$4" gap="$2" mx="auto" $gtSm={{ mx: '0' }} {...rest}>
710
<Avatar.Image

packages/app/features/send/confirm/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { assert } from 'app/utils/assert'
1717

1818
import { baseMainnet } from '@my/wagmi'
1919
import { useBalance } from 'wagmi'
20+
// @ts-expect-error some work to do here
2021
import { useSendParams } from 'app/routers/params'
2122
import { useForm } from 'react-hook-form'
2223
import { formFields } from 'app/utils/SchemaForm'
@@ -90,6 +91,7 @@ export function SendConfirm({ profile }: { profile: ProfileProp }) {
9091
const { data: nonce, error: nonceError } = useAccountNonce({ sender: sendAccount?.address })
9192
const { data: userOp } = useGenerateTransferUserOp({
9293
sender: sendAccount?.address,
94+
// @ts-expect-error some work to do here
9395
to: profile?.address,
9496
token: tokenParam === 'eth' ? undefined : tokenParam,
9597
amount: BigInt(amount),
@@ -184,7 +186,7 @@ export function SendConfirm({ profile }: { profile: ProfileProp }) {
184186
lineHeight="$1"
185187
color="$color12"
186188
>
187-
@{profile?.tag_name}
189+
@{profile?.tag}
188190
</Paragraph>
189191
</YStack>
190192
</XStack>

packages/app/features/send/confirm/screen.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ export function SendConfirm({ profile }: { profile: ProfileProp }) {
8181
const { data: nonce, error: nonceError } = useAccountNonce({ sender: sendAccount?.address })
8282
const { data: userOp } = useGenerateTransferUserOp({
8383
sender: sendAccount?.address,
84+
// @ts-expect-error some work to do here
8485
to: profile?.address,
8586
token: queryParams.sendToken === 'eth' ? undefined : queryParams.sendToken,
8687
amount: BigInt(amount),

packages/app/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77
"*.css"
88
],
99
"scripts": {
10-
"lint": "eslint .",
10+
"lint": "tsc --noEmit && eslint .",
1111
"lint:fix": "eslint . --fix",
12+
"clean": "rm -f .tsconfig.tsbuildinfo && rm -rf coverage && rm -rf .turbo",
1213
"test": "jest"
1314
},
1415
"dependencies": {
@@ -71,6 +72,7 @@
7172
"jest": "^29.7.0",
7273
"jest-expo": "^50.0.0",
7374
"nock": "^14.0.0-beta.2",
74-
"react-test-renderer": "^18.2.0"
75+
"react-test-renderer": "^18.2.0",
76+
"typescript": "^5.3.3"
7577
}
7678
}

packages/app/provider/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export function Provider({
2222
<AuthProvider initialSession={initialSession}>
2323
<Providers>
2424
{children}
25-
<ReactQueryDevtools initialIsOpen={false} />
25+
{process.env.NEXT_PUBLIC_ENABLE_QUERY_DEV_TOOLS && <ReactQueryDevtools />}
2626
</Providers>
2727
</AuthProvider>
2828
)

packages/app/provider/tag-search/TagSearchProvider.tsx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export interface TagSearchContextValue {
1818
form: ReturnType<typeof useForm<SearchSchema>>
1919
isLoading: boolean
2020
error: PostgrestError | null
21-
results: Functions<'tag_search'> | null
21+
results: Functions<'tag_search'>[number] | null
2222
}
2323

2424
const TagSearch = createContext<TagSearchContextValue>(null as unknown as TagSearchContextValue)
@@ -37,7 +37,7 @@ async function searchTags({ supabase, query, limit_val, offset_val }: SearchTags
3737
}
3838
abortController = new AbortController()
3939
const { data, error } = await supabase
40-
.rpc('tag_search', { query, limit_val, offset_val })
40+
.rpc('tag_search', { query, limit_val: limit_val ?? 10, offset_val: offset_val ?? 0 })
4141
.abortSignal(abortController.signal)
4242
return { data, error }
4343
}
@@ -47,7 +47,7 @@ export const TagSearchProvider = ({ children }: { children: React.ReactNode }) =
4747
const supabase = useSupabase()
4848
const [isLoading, setIsLoading] = useState(false)
4949
const [error, setError] = useState<PostgrestError | null>(null)
50-
const [results, setResults] = useState<Functions<'tag_search'> | null>(null)
50+
const [results, setResults] = useState<Functions<'tag_search'>[number] | null>(null)
5151

5252
const onSearch = useDebounce(
5353
async function onSearch({ query }: SearchSchema) {
@@ -56,15 +56,14 @@ export const TagSearchProvider = ({ children }: { children: React.ReactNode }) =
5656
setError(null)
5757
const result = await searchTags({ supabase, query, limit_val: 10, offset_val: 0 })
5858
const { data, error } = result
59-
const results = data && data.length > 0 ? data[0] : []
6059
if (error) {
6160
if (error.message.includes('user aborted')) {
6261
return
6362
}
6463
setError(error)
6564
return
6665
}
67-
setResults(results)
66+
setResults(data?.[0] ?? null)
6867
} finally {
6968
setIsLoading(false)
7069
}

packages/app/tsconfig.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"__mocks__",
2020
"tamagui.config.ts"
2121
],
22+
"exclude": ["coverage"],
2223
"compilerOptions": {
2324
"noEmit": true,
2425
"composite": true,

0 commit comments

Comments
 (0)