Mobile apps + Chrome extension
graph LR
linkStyle default fill:#ffffff
subgraph diagram [Akashic System - AkashicLink - Components]
style diagram fill:#ffffff,stroke:#ffffff
subgraph 7 [AkashicLink]
style 7 fill:#ffffff,stroke:#2e6295,color:#2e6295
11("<div style='font-weight: bold'>Chrome Extension</div><div style='font-size: 70%; margin-top: 0px'>[Component]</div>")
style 11 fill:#85bbf0,stroke:#5d82a8,color:#000000
12("<div style='font-weight: bold'>Android</div><div style='font-size: 70%; margin-top: 0px'>[Component]</div>")
style 12 fill:#85bbf0,stroke:#5d82a8,color:#000000
13("<div style='font-weight: bold'>iOS</div><div style='font-size: 70%; margin-top: 0px'>[Component]</div>")
style 13 fill:#85bbf0,stroke:#5d82a8,color:#000000
14("<div style='font-weight: bold'>Web Preview (dev-only)</div><div style='font-size: 70%; margin-top: 0px'>[Component]</div>")
style 14 fill:#85bbf0,stroke:#5d82a8,color:#000000
end
end
- Chrome extension does not support page reloads - whenever added a reload, perform a platform check as so:
import { isPlatform } from '@ionic/react';
...
isPlatform('mobile') && location.reload();- Install all packages and setup .env file
cp .env.example .env # optionally edit .env to suit your needs
cp .env.example .env.preprod.local # If you need to build Android App. Copy as .env.production.local for production env
yarn install- Start backend in
app/backend - Run
yarn start:dev- You can use the following at root of the monorepo to start all at once
yarn start:all- Build the extension by first building the full app (it moves the
manifest.jsonfile to the correct location), and then run the dev build
yarn build
yarn build:dev-
Go to
browser://extensions/and activate Developer Mode. -
Click Load unpacked and select the
buildfolder. Extension should appear in browser. -
Source file updates will take around 5-6 seconds to recompile. See here for information of when the extensions needs reloading.
- Set the
.envwith
REACT_APP_PUBLIC_URL=https://api.akashicscan.com
REACT_APP_API_BASE_URL=https://api.akashicscan.com/api
- Build extension
yarn build- Zip the
buildfolder
- Install Android Studio, SDK v33 and virtual devices as instructed in Ionic Doc
- Build the App with
yarn sync:android- Open the App automatically with
yarn serve:android- In Android Studio, click "Run" to run the app in virtual device
add IS_LIVE_RELOAD=true in .env.preprod.local
yarn debug:iosor
yarn debug:androidStyling is done with
scssand 80% of what you would need to use can be found in this tutorial.
Any global styles, should be defined in the theme folder and imported in common.scss as so
@use "typography";
@use "layout";
@use "ion-popover";For styling individual components, create a file-of-same-name.scss next to your .tsx file and target specific components adding className in the html:
<IonLabel className="my-class-1"></IonLabel>or
<IonSelect interfaceOptions={{ htmlAttributes: { className: 'my-class-2'}}}>Then use selectors, specificity and inheritance in the .scss file to target these components and their direct and indirect children:
.class1 .class2(for when class2 is a descendant of a class1 element).class1.class2(when an element is both class1 and class2)+,>and other combinators to target direct children, sibling and adjacent components (see here)
Example below will apply the styles to any <IonSelect> and <IonList> that are descendants of a component with class my-custom-class-2 but not across the whole application.
.my-class-2 {
/**
* Add a frame to the selection menu and round the corners
*/
ion-select-popover {
border: 1px solid var(--ion-select-border) !important;
border-radius: 4px;
}
/*
* Ensures that selection menu is small enough to fit
* under the selection button
*/
ion-list {
padding-top: 0px;
padding-bottom: 0px;
border-radius: 3px;
max-height: 120px;
overflow: scroll;
}
}All UI images used by the wallet-extension are stored in public/assets. Only include files that are actually referenced by the app to keep the extension package small.
We are using create-react-app to manage all of the transpiling needed for the different .css, .html, .tsx files (under the hood it uses webpack). While it generally does a good job, we sometimes need to fine tune by overriding the config with craco to:
- Process files outside the root directory. Specifically, instruct how to transpile
.tsfiles in the sister packages inside the monorepo - Turn off optimisation when developing locally
- Fix conflicts in the react version
Open for instructions
- Create your stories in the
./storybook/stories/folder using the following template:
import type { Meta, StoryObj } from "@storybook/react";
import { YOURCOMPONENT } from "../../src/YOUR-FOLDER";
const meta: Meta<typeof BackButton> = {
title: "SUBTREENAME",
component: YOURCOMPONENT,
};
export default meta;
type Story = StoryObj<typeof YOURCOMPONENT>;
export const Story1: Story = {};
export const Story2: Story = {};
export const Story3: Story = {};- To customise the mock requests for each story, add a mock service worker (to either the meta or the individual stories)
export const Story1: Story = {
parameters: {
msw: {
handlers: {
GROUPNAME: [MOCK_REST_IMPLEMENTATIONg],
},
},
},
};- To customise the context that a component receives, specify the context e.g.
import { LocalAccountContext } from '../../src/components/PreferenceProvider';
export const Story1: Story = {
decorators: [
withReactContext({
Context: IMPORT_CONTEXT_FROM ../../src/components/PreferenceProvide,r
initialState: {
localAccounts: [{
identity: "mock-identity",
username: "mock-username"
}]
},
})
],
};- To customise the path that the component is placed on, use the
withMockPathdecorator. Normally you would apply this to themetaobject
import { akashicPayPath } from "../../src/routing/navigation-tabs";
import { urls } from "../../src/constants/urls";
import { withMockPath } from "../utils/mock-path";
const meta: Meta<typeof DashboardComponent> = {
title: "Pages",
component: DashboardComponent,
decorators: [withMockPath(akashicPayPath(urls.loggedFunction))],
};Detailed provider-focused doc: docs/PROVIDER_ARCHITECTURE.md
Note: This architecture description covers the browser extension provider integration path (background/content/injected + website usage). It does not attempt to describe any off-chain services or backend infrastructure.
Flow overview:
Website React (wallet-context) --> provider.request()
Injected Script (injected.ts) --window.postMessage--> Content Script (content.ts)
--> chrome.runtime.sendMessage --> Background (background.ts)
<-- chrome.runtime.sendMessage <--
Injected Script resolves promise & emits events -> React context updates
Key points:
- Multiple environments (
dev,staging,prod) can coexist. Each build registers its provider underwindow.__AKASHIC_PROVIDERS[env]. - No global
window.akashicLinkfallback: the website explicitly selects env viaNEXT_PUBLIC_ENV. - Background manages sessions, permissions per origin, and approval popups.
- Content script enforces allowed-origin gating and injects the provider script only on permitted domains.
- Events (
accountsChanged,disconnect,popupOpened,popupClosed) propagate background -> content -> injected -> dApp listeners.
See the full architecture doc for diagrams, sequence details, and extension RPC method list.