diff --git a/api/dbschema/migrations/00008-m13ngaz.edgeql b/api/dbschema/migrations/00008-m13ngaz.edgeql new file mode 100644 index 000000000..53f372955 --- /dev/null +++ b/api/dbschema/migrations/00008-m13ngaz.edgeql @@ -0,0 +1,10 @@ +CREATE MIGRATION m13ngazbt3dkkbegsr7e2te4y7j4jltf56szjupnmb3ngzd6yq33zq + ONTO m15qj3htiny7zuqceuvj46wrm7yza5xyqmfuadsywsbc6tvh6vb34a +{ + ALTER SCALAR TYPE default::BoundedStr { + DROP CONSTRAINT std::regexp(r'^(?![0oO][xX])[^\n\t]{3,50}$'); + }; + ALTER SCALAR TYPE default::BoundedStr { + CREATE CONSTRAINT std::regexp(r'^(?![0oO][xX])[^\n\t]{2,70}$'); + }; +}; diff --git a/api/dbschema/scalars.esdl b/api/dbschema/scalars.esdl index 47688e1b5..6f99eee0c 100644 --- a/api/dbschema/scalars.esdl +++ b/api/dbschema/scalars.esdl @@ -12,7 +12,7 @@ module default { }; scalar type BoundedStr extending str { - constraint regexp(r'^(?![0oO][xX])[^\n\t]{3,50}$'); + constraint regexp(r'^(?![0oO][xX])[^\n\t]{2,70}$'); } scalar type Bytes extending str { diff --git a/api/src/core/context/context.util.ts b/api/src/core/context/context.util.ts index e00e778aa..4c0309644 100644 --- a/api/src/core/context/context.util.ts +++ b/api/src/core/context/context.util.ts @@ -2,12 +2,10 @@ import { AsyncLocalStorage } from 'async_hooks'; import { uuid } from 'edgedb/dist/codecs/ifaces'; import { Address, UAddress, UUID } from 'lib'; import { GqlContext } from '~/core/apollo/ctx'; -import { type Client as DatabaseClient } from 'edgedb'; export interface Context { afterRequestHooks: AfterRequestHook[]; user?: UserContext; - db?: DatabaseClient; } export interface UserContext { diff --git a/api/src/core/database/database.service.ts b/api/src/core/database/database.service.ts index eb82457d2..3cde1ae72 100644 --- a/api/src/core/database/database.service.ts +++ b/api/src/core/database/database.service.ts @@ -42,13 +42,11 @@ export class DatabaseService implements OnModuleInit { const reqCtx = getContextUnsafe(); if (!reqCtx?.user) return this.DANGEROUS_superuserClient; - reqCtx.db ??= this.__client.withGlobals({ + return this.__client.withGlobals({ current_accounts: reqCtx.user.accounts.map((a) => a.id), current_approver_address: reqCtx.user.approver, // current_user_id: reqCtx.user.id, } satisfies Globals); - - return reqCtx.db; } private async run(f: () => Promise, name = 'inline'): Promise { @@ -74,8 +72,15 @@ export class DatabaseService implements OnModuleInit { getExpr: (params: paramsToParamExprs) => Expr, params: paramsToParamArgs, ) { - const expression = e.params(paramsDef, getExpr as any); - return this.run(() => expression.run(this.client, params as any)) as Promise<$infer>; + try { + const expression = e.params(paramsDef, getExpr as any); + return (await this.run(() => expression.run(this.client, params as any))) as Promise< + $infer + >; + } catch (e) { + if (e instanceof EdgeDBError) Sentry.setExtra('EdgeQL Params', params); + throw e; + } } async queryWith2< @@ -86,8 +91,7 @@ export class DatabaseService implements OnModuleInit { params: paramsToParamArgs, getExpr: (params: paramsToParamExprs) => Expr, ) { - const expression = e.params(paramsDef, getExpr as any); - return this.run(() => expression.run(this.client, params as any)) as Promise<$infer>; + return this.queryWith(paramsDef, getExpr, params); } async exec Promise>( diff --git a/api/src/core/sentry/sentry.interceptor.ts b/api/src/core/sentry/sentry.interceptor.ts index 4dd0a1695..80cf4c15b 100644 --- a/api/src/core/sentry/sentry.interceptor.ts +++ b/api/src/core/sentry/sentry.interceptor.ts @@ -54,6 +54,7 @@ export class SentryInterceptor implements NestInterceptor { scope.setExtra('exceptionData', JSON.stringify(exception, null, 2)); this.addContextExceptionMetadata(scope, context); + scope.setExtra('userContext', getContextUnsafe()?.user); Sentry.captureException(exception); }, diff --git a/api/src/feat/accounts/accounts.service.ts b/api/src/feat/accounts/accounts.service.ts index 6fd85eecb..db73a73a8 100644 --- a/api/src/feat/accounts/accounts.service.ts +++ b/api/src/feat/accounts/accounts.service.ts @@ -35,6 +35,7 @@ import { AccountEvent } from './accounts.model'; import { PolicyInput } from '../policies/policies.input'; import { utils as zkUtils } from 'zksync-ethers'; import { toHex } from 'viem'; +import { insertAccount } from './insert-account.query'; const accountTrigger = (account: UAddress) => `account.updated:${account}`; const accountApproverTrigger = (approver: Address) => `account.updated:approver:${approver}`; @@ -148,15 +149,13 @@ export class AccountsService { ); await this.db.transaction(async () => { - await this.db.query( - e.insert(e.Account, { - id, - address, - name, - implementation, - initialization: { salt, bytecodeHash, aaVersion: 1 }, - }), - ); + await this.db.exec(insertAccount, { + id, + address, + name, + implementation, + initialization: { salt, bytecodeHash, aaVersion: 1 }, + }); await this.policies.propose( { diff --git a/api/src/feat/auth/accounts.cache.service.ts b/api/src/feat/auth/accounts.cache.service.ts index 2ff14b47e..c2e2cd08c 100644 --- a/api/src/feat/auth/accounts.cache.service.ts +++ b/api/src/feat/auth/accounts.cache.service.ts @@ -55,8 +55,9 @@ export class AccountsCacheService implements OnModuleInit { accounts: JSON.parse(cachedAccounts) as UserAccountContext[], }; - const { user } = await this.db.queryWith( + const { user } = await this.db.queryWith2( { approver: e.Address }, + { approver }, ({ approver }) => e.select( e.insert(e.Approver, { address: approver }).unlessConflict((a) => ({ @@ -71,7 +72,6 @@ export class AccountsCacheService implements OnModuleInit { }, }), ), - { approver }, ); const accounts = user.accounts.map( @@ -99,14 +99,11 @@ export class AccountsCacheService implements OnModuleInit { if (!getUserCtx().accounts.find((a) => a.id === account.id)) getUserCtx().accounts.push(account); - const user = await this.db.queryWith( + const user = await this.db.queryWith2( { approver: e.Address }, - ({ approver }) => - e.select(e.Approver, () => ({ - filter_single: { address: approver }, - user: { id: true }, - })).user.id, { approver }, + ({ approver }) => + e.select(e.Approver, () => ({ filter_single: { address: approver } })).user.id, ); if (user) { diff --git a/api/src/feat/policies/existing-policies.edgeql b/api/src/feat/policies/existing-policies.edgeql index afb279a4b..aae17324d 100644 --- a/api/src/feat/policies/existing-policies.edgeql +++ b/api/src/feat/policies/existing-policies.edgeql @@ -1,6 +1,6 @@ with account := (select Account filter .address = $account), keys := array_unpack(>$policyKeys) -select Policy { +select account.$account), keys := array_unpack(>$policyKeys) -select Policy { +select account.$account, select transfer { id, internal, + fee, accountUsers := .account.approvers.user.id } \ No newline at end of file diff --git a/app/src/components/transaction/TransactionDetails.tsx b/app/src/components/transaction/TransactionDetails.tsx index 4c9499457..3e9944915 100644 --- a/app/src/components/transaction/TransactionDetails.tsx +++ b/app/src/components/transaction/TransactionDetails.tsx @@ -48,7 +48,7 @@ export function TransactionDetails(props: TransactionDetailsProps) { } trailing={ } /> diff --git a/app/src/hooks/cloud/useGetCloudApprover.ts b/app/src/hooks/cloud/useGetCloudApprover.ts index dae9133ed..a5a10e4a6 100644 --- a/app/src/hooks/cloud/useGetCloudApprover.ts +++ b/app/src/hooks/cloud/useGetCloudApprover.ts @@ -15,6 +15,7 @@ import { useGetCloudApproverQuery } from '~/api/__generated__/useGetCloudApprove import { signAuthHeaders } from '~/api/auth-manager'; import { UpdateApproverInput } from '~/api/__generated__/useGetCloudApproverMutation.graphql'; import { withHeaders } from '~/api/network/auth'; +import { zBoundStr } from '~/lib/zod'; const PK_PATH = '/approver.private-key'; const SCOPE = CloudStorageScope.AppData; @@ -84,9 +85,7 @@ export function useGetCloudApprover() { await fetchQuery( environment, Query, - { - approver: approver.address, - }, + { approver: approver.address }, { networkCacheConfig: withHeaders(authHeaders) }, ).toPromise() )?.approver.details; @@ -95,7 +94,11 @@ export function useGetCloudApprover() { { input: { address: approver.address, - name: !e?.name ? details?.name : undefined, + name: !e?.name + ? zBoundStr().safeParse( + details?.name?.slice(0, zBoundStr().maxLength ?? undefined), + ) + : undefined, cloud: !e?.cloud ? details?.cloud : undefined, }, }, diff --git a/app/src/lib/zod.ts b/app/src/lib/zod.ts index 6375eaf3f..4147854ed 100644 --- a/app/src/lib/zod.ts +++ b/app/src/lib/zod.ts @@ -60,9 +60,9 @@ export function zBool() { export function zBoundStr() { return z .string() - .min(3) - .max(50) - .regex(/(?![0oO][xX])[^\n\t]{3,50}$/, 'Must not start with 0x'); + .min(2) + .max(70) + .regex(/(?![0oO][xX])[^\n\t]{2,70}$/, 'Must not start with 0x'); } export function zNonEmptyStr() {