You are an AI Agent working in a security-critical Web3 monorepo that encompasses a mobile app wallet, browser extension wallet and standalone websites (src/benzin and src/legends).
react, react-native, react-native-web, typescript, expo (bare workflow), ethers, viem and more (check the root package.json if another package is needed)
- The extension is using manifest version 3 on Chrome, but also works on Firefox using a background script
backgroundin the wallet refers to:- Service worker on Chrome (
src/web/extension-services/background/) - Background script on Firefox (
src/web/extension-services/background/) - Webview worker on mobile (
src/mobile/services/WebViewWorker/)
- Service worker on Chrome (
- Unlike typical manifest version 3 extensions where the service worker is allowed to sleep, this extension is designed to stay alive: the UI periodically sends
ambire-extension-pingmessages, the background responds withambire-extension-pongto prevent the service worker from being suspended, and the background'sinit()function (which bootstraps all controllers) is called on every incoming message - a no-op if already initialized, but essential after a service worker suspension because the JS context is destroyed on sleep andisInitializedresets - The business logic and persistent state is handled primarily using
controllers(JS classes), which usually run in thebackground - The websites run some controllers separately without a
background src/ambire-commonis a git submodule that contains the business logic of the application. Changes inside it are in a separate repo and require a separate commit flowsrc/commoncan be imported by all environments, but environments shouldn't import from other environments (e.g.,web/SHOULD NOT import frommobile/)- There are environment specific files. Be VERY careful when creating files and debugging as they exist in two ways:
- Have the file in the environment folder (e.g.,
mobile/,web/) and import it from there - Use the
.native.tsxor.web.tsxsuffix and import fromcommon/, which automatically resolves to the correct file based on the environment
- Have the file in the environment folder (e.g.,
- Check for typescript errors -
yarn extension:type:check-new - Lint and auto-fix source files:
yarn lint:fix
- Prefer naming variable ways in a self-explanatory way instead of adding comments
- Prefer short circuits to nested if statements for better readability
- Aim for low cyclomatic complexity unless it's not for parts that are handling large sets of data and performance is critical
- Use
yarnin the root directory andnpminsrc/ambire-common(if present) - NEVER run
yarn installdirectly, useyarn setupinstead - Use
useHoverandHoverablePressablefor interactive elements - ALWAYS use
themefromuseTheme()for colors - ALWAYS use
spacingsfrom@common/styles/spacingsfor margins and paddings (unless the spacing is missing in the utility) - ALWAYS use
flexboxfrom@common/styles/utils/flexboxfor flex styling (unless the style is missing in the utility) - ALWAYS use
isDev,isWeb,isMobile,isAndroid,isiOS, etc. from@common/config/envfor environment and platform checks; NEVER use__DEV__outside.native.tsxfiles because it does not exist in web/extension builds - ALWAYS use
useNavigationanduseRoutefrom@common/hooksfor routing; NEVER importreact-router-domorreact-router-nativedirectly - ALWAYS use
useToastfrom@common/hooks/useToastfor toasts; NEVER usewindow.alert,Alert.alert, orconsole.logfor user-facing messages - ALWAYS wrap text in
t()fromuseTranslation()(imported from '@common/config/localization') - ALWAYS use
react-native-modalizewithBottomSheetfor modals and bottom sheets - Tooltips are added using the pattern:
dataSet={createGlobalTooltipDataSet(...)}which creates a global dataset that is picked up by aGlobalTooltipcomponent at the root of the app. - All icons are in src/common/assets/svg/...; ALWAYS use icons from there and report if you can't find a suitable one by adding a comment instead of the icon and asking the human to add it
- This is a security-critical Web3 wallet. Private keys and seed phrases must never be logged or exposed
- The extension uses LavaMoat with SES for the background. If you add/remove dependencies or change import patterns, regenerate the policy with
yarn build:extensions:generate-policyand review the changes
- Ensure that list keys are unique and stable (NEVER use the array index)
- ALWAYS memoize functions, components and complex values with
useMemo,useCallbackandReact.memo. - ALWAYS ensure that subscriptions, event listeners, timers and other side effects are properly cleaned up
- NEVER delete existing comments when updating a code block. If the logic changes and the comment becomes inaccurate, update the comment instead of deleting it. Delete a comment ONLY if the logic it describes is completely removed or the new logic is entirely self-explanatory without the comment
- NEVER swallow errors, log them and handle them appropriately. If the error is unexpected also track it in Sentry with
captureException - NEVER modify git config or run destructive git operations
- NEVER commit unless explicitly requested by user
- NEVER stage changes unless explicitly requested by user
- Avoid regex for parsing strings or business logic. Prefer explicit parsing, small helper functions, existing parsers or available library functions.
- The UI calls
dispatchfromuseControllerto invoke a controller method. This is fire-and-forget —dispatchdoes NOT return a response or the new state - The action travels to the background (via
PortMessengerin the extension,WebViewWorkerin mobile) wherehandleActionsfinds the controller and calls the requested method - The controller method updates its internal state and calls
this.emitUpdate() - The
backgroundhas anonUpdatelistener for every registered controller that serializes the controller state and sends it back to the UI - The UI receives the update, writes it to the
controllerStore, anduseControllerState(viauseSyncExternalStore) triggers a React re-render with the new state - Because of this asynchronous cycle, state changes are NOT immediate after
dispatch. If an action fails silently or the controller doesn't callemitUpdate, the UI will never re-render
- On standalone websites (
benzin,legends) controllers run in the main thread directly anddispatchinvokes methods synchronously, but theemitUpdate→ store → re-render cycle still applies
Personal rules may be added in AGENTS.local.md. This file is gitignored, auto-loaded when present, and ignored when missing.
@AGENTS.local.md