Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
d67a4f5
✨ app: add card limit kyc flow
aguxez Mar 20, 2026
b33f448
⚰️ app: remove expired crypto on-ramps
dieguezguille Jul 23, 2026
f42305d
✨ app: restructure add funds root menu
dieguezguille Jul 23, 2026
f071aae
✨ app: resolve ens name for owner address
dieguezguille Jul 23, 2026
668dbac
✨ app: redesign crypto receive screen
dieguezguille Jul 23, 2026
f4a08d6
✨ app: add cryptocurrencies asset list
dieguezguille Jul 23, 2026
afac302
✨ app: add network selection to receive flow
dieguezguille Jul 23, 2026
93b8899
✨ app: add bridge education sheet
dieguezguille Jul 23, 2026
0a33527
✨ app: add network filter to asset select sheet
dieguezguille Jul 23, 2026
8e1d771
💄 app: redesign bridge quote screen
dieguezguille Jul 23, 2026
f9086cc
✨ app: add supported asset match sheet
dieguezguille Jul 23, 2026
db085e8
✨ app: add request sent screen
dieguezguille Jul 23, 2026
050b007
💄 app: toggle qr inline on receive screen
dieguezguille Jul 23, 2026
dfd7775
💄 app: redesign copy address sheet
dieguezguille Jul 23, 2026
da678fb
✨ app: list allowlisted assets on add funds
dieguezguille Jul 23, 2026
7ba51b6
✨ app: add swap and bridge swap guide sheets
dieguezguille Jul 23, 2026
f63fd45
🔨 wagmi: codegen allowlists for all chains
dieguezguille Jul 23, 2026
48ad403
✨ app: display allowlisted assets per network
dieguezguille Jul 23, 2026
3eb942c
✨ app: add network section info sheets
dieguezguille Jul 23, 2026
b0f075c
⚡️ app: cache deployed bytecode indefinitely
dieguezguille Jul 31, 2026
b14ed33
💄 app: hide defi section
dieguezguille Aug 3, 2026
06a4ea6
🐛 app: fix swap token selection
dieguezguille Jun 16, 2026
91d40d5
🍱 app: add base card svgs
dieguezguille Jun 19, 2026
843649e
💄 app: implement app theme for base
dieguezguille Jun 11, 2026
cb4c24f
🍱 app: add visa card variant for base
dieguezguille Jul 1, 2026
2290865
💄 app: theme visa benefit card for base
dieguezguille Jul 1, 2026
96bea77
➕ app: install expo-file-system expo-sharing
franm91 Jul 1, 2026
6160a7e
✨ app: add card statement
franm91 Jul 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/brave-otters-hide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@exactly/mobile": patch
---

💄 hide defi section
5 changes: 5 additions & 0 deletions .changeset/brave-otters-wave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@exactly/mobile": patch
---

✨ restructure add funds root menu
5 changes: 5 additions & 0 deletions .changeset/brisk-terns-guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@exactly/mobile": patch
---

✨ add swap and bridge swap guide sheets
5 changes: 5 additions & 0 deletions .changeset/calm-badgers-greet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@exactly/mobile": patch
---

✨ resolve ens name for owner address
5 changes: 5 additions & 0 deletions .changeset/calm-herons-store.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@exactly/mobile": patch
---

⚡️ cache deployed bytecode indefinitely
5 changes: 5 additions & 0 deletions .changeset/clever-crews-pay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@exactly/mobile": patch
---

💄 implement app theme for base
5 changes: 5 additions & 0 deletions .changeset/curvy-jobs-sort.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@exactly/mobile": patch
---

✨ add card statement
5 changes: 5 additions & 0 deletions .changeset/deep-owls-emit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@exactly/common": patch
---

🔨 codegen allowlists for all chains
5 changes: 5 additions & 0 deletions .changeset/eager-owls-list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@exactly/mobile": patch
---

✨ add cryptocurrencies asset list
5 changes: 5 additions & 0 deletions .changeset/glad-finches-map.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@exactly/mobile": patch
---

✨ display allowlisted assets per network
5 changes: 5 additions & 0 deletions .changeset/keen-otters-match.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@exactly/mobile": patch
---

✨ add supported asset match sheet
5 changes: 5 additions & 0 deletions .changeset/neat-cranes-copy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@exactly/mobile": patch
---

💄 redesign copy address sheet
5 changes: 5 additions & 0 deletions .changeset/plush-swans-send.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@exactly/mobile": patch
---

✨ add request sent screen
5 changes: 5 additions & 0 deletions .changeset/proud-lions-glow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@exactly/mobile": patch
---

✨ redesign crypto receive screen
5 changes: 5 additions & 0 deletions .changeset/quiet-cranes-teach.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@exactly/mobile": patch
---

✨ add bridge education sheet
5 changes: 5 additions & 0 deletions .changeset/round-moles-count.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@exactly/mobile": patch
---

💄 redesign bridge quote screen
5 changes: 5 additions & 0 deletions .changeset/sly-foxes-sift.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@exactly/mobile": patch
---

✨ add network filter to asset select sheet
5 changes: 5 additions & 0 deletions .changeset/soft-doves-teach.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@exactly/mobile": patch
---

✨ add network section info sheets
5 changes: 5 additions & 0 deletions .changeset/true-bottles-cough.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@exactly/mobile": patch
---

🐛 fix swap token selection
5 changes: 5 additions & 0 deletions .changeset/warm-swifts-flip.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@exactly/mobile": patch
---

💄 toggle qr inline on receive screen
5 changes: 5 additions & 0 deletions .changeset/wet-needles-call.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@exactly/mobile": patch
---

✨ add card limit kyc flow
5 changes: 5 additions & 0 deletions .changeset/wise-crows-allow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@exactly/mobile": patch
---

✨ list allowlisted assets on add funds
5 changes: 5 additions & 0 deletions .changeset/witty-geese-roam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@exactly/mobile": patch
---

✨ add network selection to receive flow
1 change: 1 addition & 0 deletions app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export default {
{ supportedLocales: ["en", "es", "es-AR", "es-CR", "es-GT", "es-HN", "es-NI", "es-PY", "es-SV", "es-UY", "pt"] },
],
"expo-router",
"expo-sharing",
[
"@intercom/intercom-react-native",
{
Expand Down
35 changes: 30 additions & 5 deletions common/wagmi.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { defineConfig, type Plugin } from "@wagmi/cli";
import { foundry, react } from "@wagmi/cli/plugins";
import { execSync } from "node:child_process";
import { readFileSync } from "node:fs";
import { readdirSync, readFileSync } from "node:fs";
import { env } from "node:process";
import { getAddress, type Abi } from "viem";
import { anvil, base, baseSepolia, optimism, optimismSepolia } from "viem/chains";
Expand Down Expand Up @@ -35,9 +35,26 @@ const [proposalManager] = loadBroadcast("ProposalManager").transactions;
const [refunder] = loadBroadcast("Refunder").transactions;
const [exaPreviewer] = loadBroadcast("ExaPreviewer").transactions;
const swapper = (deploy.accounts.swapper as Record<string, string>)[chainId] ?? deploy.accounts.swapper.default;
const allowlist = Object.keys(
(deploy.proposalManager.allowlist as Record<string, Record<string, string>>)[chainId] ?? {},
).map((address) => getAddress(address));
const allowlists = Object.fromEntries(
Object.entries(deploy.proposalManager.allowlist as Record<string, Record<string, string>>).map(([id, tokens]) => {
const network = { [String(optimism.id)]: "optimism", [String(base.id)]: "base" }[id];
const underlying = network
? readdirSync(`node_modules/@exactly/protocol/deployments/${network}`)
.flatMap((file) => /^Market([A-Za-z.]+)\.json$/.exec(file)?.[1] ?? [])
.filter((asset) => asset !== "ETHRouter")
.map((asset) =>
getAddress(
(
JSON.parse(
readFileSync(`node_modules/@exactly/protocol/deployments/${network}/${asset}.json`, "utf8"),
) as Deployment
).address,
),
)
: [];
return [id, [...underlying, ...Object.keys(tokens).map((address) => getAddress(address))]];
}),
);
if (!exaPlugin || !issuerChecker || !proposalManager || !exaPreviewer || !refunder || !swapper) {
throw new Error("missing contracts");
}
Expand Down Expand Up @@ -113,7 +130,12 @@ export default defineConfig([
},
},
),
{ name: "Allowlist", run: () => ({ content: `export const allowlist = ${JSON.stringify(allowlist)} as const` }) },
{
name: "Allowlist",
run: () => ({
content: `export const allowlists: Record<string, readonly string[] | undefined> = ${JSON.stringify(allowlists)}`,
}),
},
foundry({
forge: { build: false },
project: "../contracts",
Expand Down Expand Up @@ -168,6 +190,7 @@ function addresses(
}

function chain(): Plugin {
const isBase = chainId === base.id || chainId === baseSepolia.id;
if (chainId === anvil.id) {
return {
name: "Chain",
Expand All @@ -177,6 +200,7 @@ const chain = anvil as Chain
chain.rpcUrls.alchemy = chain.rpcUrls.default
chain.contracts = { multicall3: { address: "${optimism.contracts.multicall3.address}" } }
chain.blockExplorers = { default: { name: "Otterscan", url: "http://localhost:5100" } }
export const isBase: boolean = ${isBase}
export default chain as Chain & {
contracts: { multicall3: { address: \`0x\${string}\` } }
rpcUrls: { alchemy: { http: readonly [string] } }
Expand All @@ -197,6 +221,7 @@ export default chain as Chain & {
run: () => ({
content: `import { ${importName} } from "@account-kit/infra"
import { type Chain } from "viem/chains"
export const isBase: boolean = ${isBase}
export default ${importName} as Chain & {
contracts: { multicall3: { address: \`0x\${string}\` } }
rpcUrls: { alchemy: { http: readonly [string] } }
Expand Down
103 changes: 60 additions & 43 deletions metro.config.cjs
Original file line number Diff line number Diff line change
@@ -1,48 +1,65 @@
const { getSentryExpoConfig } = require("@sentry/react-native/metro");
const { getExpoTransformer, getReactNativeTransformer } = require("react-native-svg-transformer");
const reactNativeSvgTransformer = require("react-native-svg-transformer/expo");

const path = require("node:path");

const config = getSentryExpoConfig(__dirname, { annotateReactComponents: true });
const rawTransformer = getExpoTransformer() ?? getReactNativeTransformer();
const themeable = /\b(fill|stroke)="#([0-9a-f]{6})"/g;

/** @type {import('metro-config').InputConfigT} */
module.exports = {
...config,
resolver: {
...config.resolver,
extraNodeModules: { crypto: require.resolve("react-native-quick-crypto") },
assetExts: config.resolver?.assetExts?.filter((extension) => extension !== "svg"),
sourceExts: [...(config.resolver?.sourceExts ?? []), "svg"],
blockList: [
...((config.resolver?.blockList &&
(Array.isArray(config.resolver.blockList) ? config.resolver.blockList : [config.resolver.blockList])) ??
[]),
new RegExp(path.join(__dirname, String.raw`\.\w+/`)),
new RegExp(path.join(__dirname, "android/")),
new RegExp(path.join(__dirname, "contracts/")),
new RegExp(path.join(__dirname, "build/")),
new RegExp(path.join(__dirname, "dist/")),
new RegExp(path.join(__dirname, "ios/")),
new RegExp(path.join(__dirname, "public/")),
new RegExp(path.join(__dirname, "server/")),
],
resolveRequest: (context, moduleName, platform) => {
if (moduleName === "tslib") return context.resolveRequest(context, "tslib/tslib.es6.js", platform);
if (
/date-fns\/locale\.(?:js|cjs|mjs)$/.test(context.originModulePath) &&
moduleName.startsWith("./locale/") &&
!/^(?:en|es|pt)(?:-|$)/.test(moduleName.slice("./locale/".length).replace(/\.js$/, ""))
) {
return { type: "empty" };
}
try {
return context.resolveRequest(context, moduleName, platform);
} catch (error) {
if (moduleName.endsWith(".js")) return context.resolveRequest(context, moduleName.slice(0, -3), platform);
throw error;
}
module.exports = function config() {
const { getSentryExpoConfig } = require("@sentry/react-native/metro");
const base = getSentryExpoConfig(__dirname, { annotateReactComponents: true });
/** @type {import('metro-config').InputConfigT} */
const merged = {
...base,
resolver: {
...base.resolver,
extraNodeModules: { crypto: require.resolve("react-native-quick-crypto") },
assetExts: base.resolver?.assetExts?.filter((extension) => extension !== "svg"),
sourceExts: [...(base.resolver?.sourceExts ?? []), "svg"],
blockList: [
...((base.resolver?.blockList &&
(Array.isArray(base.resolver.blockList) ? base.resolver.blockList : [base.resolver.blockList])) ??
[]),
new RegExp(path.join(__dirname, String.raw`\.\w+/`)),
new RegExp(path.join(__dirname, "android/")),
new RegExp(path.join(__dirname, "contracts/")),
new RegExp(path.join(__dirname, "build/")),
new RegExp(path.join(__dirname, "dist/")),
new RegExp(path.join(__dirname, "ios/")),
new RegExp(path.join(__dirname, "public/")),
new RegExp(path.join(__dirname, "server/")),
],
resolveRequest: (context, moduleName, platform) => {
if (moduleName === "tslib") return context.resolveRequest(context, "tslib/tslib.es6.js", platform);
if (
/date-fns\/locale\.(?:js|cjs|mjs)$/.test(context.originModulePath) &&
moduleName.startsWith("./locale/") &&
!/^(?:en|es|pt)(?:-|$)/.test(moduleName.slice("./locale/".length).replace(/\.js$/, ""))
) {
return { type: "empty" };
}
try {
return context.resolveRequest(context, moduleName, platform);
} catch (error) {
if (moduleName.endsWith(".js")) return context.resolveRequest(context, moduleName.slice(0, -3), platform);
throw error;
}
},
},
},
transformer: {
...config.transformer,
babelTransformerPath: require.resolve("react-native-svg-transformer/expo"),
},
transformer: { ...base.transformer, babelTransformerPath: require.resolve("./metro.config.cjs") },
};
return merged;
};

/** @param {import("@sentry/react-native/dist/js/tools/vendor/metro/metroBabelTransformer").BabelTransformerArgs} args */
module.exports.transform = function transform(args) {
if (args.filename.endsWith(".svg")) {
if (args.src.includes(' data-themed=""')) {
const themed = args.src.replaceAll(' data-themed=""', "").replaceAll(themeable, '$1="var(--s$2,#$2)"');
return rawTransformer.transform({ ...args, src: `module.exports = ${JSON.stringify(themed)};` });
}
return reactNativeSvgTransformer.transform(args);
}
return require("@sentry/react-native/dist/js/tools/sentryBabelTransformer").transform(args);
};
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
"expo-camera": "~55.0.9",
"expo-clipboard": "~55.0.8",
"expo-constants": "~55.0.9",
"expo-file-system": "~55.0.23",
"expo-font": "~55.0.4",
"expo-haptics": "~55.0.9",
"expo-image": "~55.0.5",
Expand All @@ -83,6 +84,7 @@
"expo-local-authentication": "~55.0.9",
"expo-localization": "~55.0.9",
"expo-router": "~55.0.5",
"expo-sharing": "~55.0.21",
"expo-status-bar": "~55.0.4",
"expo-system-ui": "~55.0.9",
"expo-updates": "~55.0.13",
Expand Down
Loading