Skip to content

Conversation

@ignaciosantise
Copy link
Collaborator

@ignaciosantise ignaciosantise commented Oct 8, 2025

Summary

Prep for multichain release


Note

Adds multichain capabilities (Bitcoin, Solana) with new adapters and connectors, updates AppKit/UI and wallet frameworks, and expands onramp/auth/connecting views across web, native, and scaffold.

  • Multichain support:
    • Add packages/bitcoin and packages/solana adapters, providers, utils, and types.
    • Update common types (packages/common/src/types/**) and adapters (EvmAdapter, BitcoinBaseAdapter, SolanaBaseAdapter).
  • Core:
    • Enhance controllers (AccountController, NetworkController, OnRampController, RouterController, etc.) and utilities (ApiUtil, NetworkUtil, RouterUtil, StorageUtil).
  • AppKit:
    • Expand modal/views and partials (connect, networks, onramp, swap, account) and hooks (useAccount, useProvider, useAppKit*).
    • Add UI flows for SIWX/sign, network switch, swap, onramp checkout/settings/transaction.
  • Wallet and connectors:
    • Update packages/wagmi, packages/ethers, packages/ethers5, packages/coinbase* with connectors/clients/configs.
    • Add wallet frame/webview components in packages/wallet.
  • UI library:
    • Add/extend components (wui-modal, buttons, lists, QR, OTP, tabs, loading) and assets.
  • Apps:
    • Native app updates (views for Ethers/Solana/Bitcoin actions, account, events; utils for SIWE, storage, wagmi).
    • Gallery app config updates.
  • Scaffold:
    • Update connect/onramp/swap/network views and utilities.
  • SIWE/SIWX:
    • Add SIWX utilities in AppKit and SIWE package updates (controller, scaffold view).
  • CI/Releng:
    • Add/update GitHub Actions workflows (alpha/snapshot/changesets, auto-review) and release scripts.
    • Yarn and repo config housekeeping (ignore files, rc, settings).

Written by Cursor Bugbot for commit 08113ea. This will update automatically on new commits. Configure here.

ignaciosantise and others added 30 commits July 18, 2025 17:34
…tworks without adapters, added namespace to useAccount hook
@changeset-bot
Copy link

changeset-bot bot commented Oct 8, 2025

🦋 Changeset detected

Latest commit: 08113ea

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 9 packages
Name Type
@reown/appkit-coinbase-react-native Patch
@reown/appkit-bitcoin-react-native Patch
@reown/appkit-react-native Patch
@reown/appkit-common-react-native Patch
@reown/appkit-ethers-react-native Patch
@reown/appkit-solana-react-native Patch
@reown/appkit-wagmi-react-native Patch
@reown/appkit-core-react-native Patch
@reown/appkit-ui-react-native Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@claude
Copy link

claude bot commented Oct 8, 2025

Claude finished @ignaciosantise's task —— View job


Code Review Complete

Reviewed PR #477: prep for multichain release (444 commits, 100 files changed)


Critical Issues

1. Race Condition in syncAccounts (AppKit.ts:479-490)

adapters.forEach(async adapter => {
  const namespace = adapter.getSupportedNamespace();
  // ...async operations
});

Issue: Using async callback in forEach creates untracked promises. Async operations won't be awaited.

Fix: Use for...of loop or Promise.all:

await Promise.all(adapters.map(async adapter => {
  const namespace = adapter.getSupportedNamespace();
  // ...
}));

Files: packages/appkit/src/AppKit.ts:479


2. Async Operation Without Await in setConnection (AppKit.ts:598)

adapters.forEach(async adapter => {
  // async operations that are never awaited
});

Issue: Same forEach + async issue - promises are created but never tracked or awaited.

Fix: Change to synchronous forEach or properly await:

adapters.forEach(adapter => {
  const namespace = adapter.getSupportedNamespace();
  // ...sync operations only
});

Files: packages/appkit/src/AppKit.ts:598


3. Unsafe Type Assertion in BitcoinUtil (BitcoinUtil.ts:64-65)

psbt.addOutput({
  address: params.senderAddress,
  value: BigInt(change)  // 'change' is number, should validate
});

Issue: Converting number to BigInt without validation. If change is a float or NaN, this will throw.

Fix: Add validation:

if (!Number.isInteger(change)) {
  throw new Error('Change amount must be an integer');
}
psbt.addOutput({
  address: params.senderAddress,
  value: BigInt(change)
});

Files: apps/native/src/utils/BitcoinUtil.ts:64


4. Hardcoded API Endpoint Without Error Handling (SolanaActionsView.tsx:68,123)

const connection = new Connection('https://api.mainnet-beta.solana.com', 'confirmed');

Issue: Hardcoded mainnet endpoint used regardless of chainId. If user is on testnet, transactions will fail silently or confusingly.

Fix: Derive endpoint from chainId:

const endpoint = chainId === '5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp' 
  ? 'https://api.mainnet-beta.solana.com'
  : 'https://api.devnet.solana.com';
const connection = new Connection(endpoint, 'confirmed');

Files: apps/native/src/views/SolanaActionsView.tsx:68, 123, 170


5. Security: Package Version Manipulation Without Rollback on Error (publish-initial-versions.js:130-133)

parsedPackageJson.version = '0.0.1';
fs.writeFileSync(packageJsonPath, JSON.stringify(parsedPackageJson, null, 2));
runCommand('yarn', ['npm', 'publish', ...], { cwd: pkg.dir });

Issue: If publish fails, the finally block restores package.json, but if the process is killed, package.json remains at 0.0.1.

Fix: Use git-based restoration or atomic operations. Consider using npm version commands instead of manual manipulation.
Files: .github/scripts/publish-initial-versions.js:130-133


High Priority Issues

6. Memory Leak: Missing Cleanup for Event Subscriptions (AppKit.ts:632-686)

private subscribeToAdapterEvents(adapter: BlockchainAdapter): void {
  adapter.on('accountsChanged', ...);
  adapter.on('chainChanged', ...);
  // No cleanup/unsubscribe mechanism
}

Issue: Event listeners are added but never removed. If adapters are recreated or connections change, old listeners persist.

Fix: Store references and add cleanup:

private unsubscribeFromAdapterEvents(adapter: BlockchainAdapter): void {
  adapter.removeAllListeners();
}

Files: packages/appkit/src/AppKit.ts:632-686


7. Unused Variable Warning (SolanaActionsView.tsx:16)

const isConnected = true;  // Hardcoded, never changes

Issue: Variable isConnected is hardcoded to true and never used for actual connection state.

Fix: Either remove or connect to actual state:

const { isConnected } = useAccount();

Files: apps/native/src/views/SolanaActionsView.tsx:16


8. Console.log Left in Production Code (BitcoinActionsView.tsx:101)

// eslint-disable-next-line no-console
console.log('error', error);

Issue: Debug console.log should use LogController for consistency.

Fix:

LogController.sendError(error, 'BitcoinActionsView', 'signPsbt');

Files: apps/native/src/views/BitcoinActionsView.tsx:101


9. Error Handling Inconsistency - Silent Failures (AppKit.ts:556)

} catch (error) {
  // ignore
  LogController.sendError(error, 'AppKit.ts', 'refreshBalance');
}

Issue: Comment says "ignore" but error is logged. Inconsistent error handling pattern.

Fix: Either handle the error meaningfully or document why it's safe to ignore.
Files: packages/appkit/src/AppKit.ts:556


10. Missing Dependency in useMemo (useProvider.ts:42)

const returnValue = useMemo(() => {
  if (!connection || !connection.adapter) {
    return { provider: undefined, providerType: undefined };
  }
  // ...
}, [connection]);

Issue: connection is from useSnapshot, which returns a proxy. The memo may not invalidate correctly if nested properties change.

Fix: Access specific properties:

const { connection } = useSnapshot(ConnectionsController.state);
const adapter = connection?.adapter;

const returnValue = useMemo(() => {
  if (!adapter) return { provider: undefined, providerType: undefined };
  // ...
}, [adapter]);

Files: packages/appkit/src/hooks/useProvider.ts:42


Medium Priority Issues

11. Commented Out Code Should Be Removed (w3m-router/index.tsx:34-51)

// import { UiUtil } from '../../utils/UiUtil';
// import { useRouteTransition } from '../../hooks/useRouteTransition';
// const { animateTransition, getAnimatedStyle } = useRouteTransition();
// useEffect(() => {
//   UiUtil.setRouteTransition(animateTransition);
// }, [animateTransition]);

Issue: Large blocks of commented code reduce readability. If this is for future work, create a TODO or remove.

Fix: Remove commented code or add clear TODO with issue reference.
Files: packages/appkit/src/modal/w3m-router/index.tsx:34-51


12. Inefficient Array Operations (AllWalletsList.tsx:29-49)

let combinedWallets = [...installed, ...featured, ...recommended, ...wallets];
const certifiedIndex = combinedWallets.findLastIndex(...);
if (certifiedIndex > -1) {
  const nonCertifiedWallets = combinedWallets.splice(certifiedIndex + 1);
  combinedWallets = combinedWallets.concat(customWallets ?? [], nonCertifiedWallets);
}
// Then deduplicate
const uniqueWallets = Array.from(
  new Map(combinedWallets.map(wallet => [wallet?.id, wallet])).values()
);

Issue: Creates temporary arrays multiple times, then deduplicates. Could deduplicate first.

Fix: Use Set for deduplication during concatenation for O(n) instead of O(n²).
Files: packages/appkit/src/partials/w3m-all-wallets-list/index.tsx:29-49


13. TypeScript: Missing Null Check (BitcoinUtil.ts:49)

const payment = this.getPaymentByAddress(params.senderAddress, network);
if (!payment.output) {
  throw new Error('Invalid payment output');
}

Issue: Checks payment.output but getPaymentByAddress could theoretically return undefined payment object.

Fix: Add null check:

const payment = this.getPaymentByAddress(params.senderAddress, network);
if (!payment || !payment.output) {
  throw new Error('Invalid payment output');
}

Files: apps/native/src/utils/BitcoinUtil.ts:49-55


14. Incomplete Error Context (BitcoinUtil.ts:129)

} catch (e) {
  // eslint-disable-next-line no-console
  console.error('Error fetching fee rate', e);
}
return defaultFeeRate;

Issue: Error is logged to console instead of LogController. No tracking or reporting.

Fix: Use LogController for consistency with project patterns.
Files: apps/native/src/utils/BitcoinUtil.ts:129


15. Missing Input Validation (WalletConnectConnector.ts:209)

override switchNetwork(network: AppKitNetwork): Promise<void> {
  if (!network) throw new Error('No network provided');
  let caipNetworkId = network.caipNetworkId ?? `eip155:${network.id}`;
  // ...
}

Issue: Assumes network.id exists if caipNetworkId is undefined. Should validate both.

Fix:

if (!network || (!network.caipNetworkId && !network.id)) {
  throw new Error('Invalid network: missing caipNetworkId or id');
}

Files: packages/appkit/src/connectors/WalletConnectConnector.ts:209


Low Priority / Code Quality

16. Magic Numbers Without Constants (BitcoinUtil.ts:138-143)

const estimatedSize = 10 + 148 * utxos.length + 34 * 2;

Issue: Magic numbers make code harder to understand and maintain.

Fix:

const FIXED_OVERHEAD = 10;
const INPUT_SIZE = 148;
const OUTPUT_SIZE = 34;
const estimatedSize = FIXED_OVERHEAD + INPUT_SIZE * utxos.length + OUTPUT_SIZE * 2;

Files: apps/native/src/utils/BitcoinUtil.ts:138-143


17. Type Safety: any Type Usage (SolanaActionsView.tsx:106)

} catch (error: any) {

Issue: Using any defeats TypeScript's type safety.

Fix: Use unknown:

} catch (error: unknown) {
  onSignError(error as Error, 'Sign Transaction failed');
}

Files: apps/native/src/views/SolanaActionsView.tsx:106


18. Dead Code: Commented Tokens Config (App.tsx:76-83)

// tokens: {
//   'eip155:1': {
//     address: '0xdAC17F958D2ee523a2206206994597C13D831ec7'
//   },
//   ...
// }

Issue: Commented configuration suggests incomplete feature or should be removed.

Fix: Remove or document why it's commented.
Files: apps/native/App.tsx:76-83


19. Inconsistent Error Messages (BitcoinActionsView.tsx:23,29,61)

ToastUtils.showErrorToast('Sign failed', 'No provider found');
ToastUtils.showErrorToast('Sign failed', 'No address found');
ToastUtils.showErrorToast('Sign failed', 'The selected chain is not bip122');

Issue: Generic "Sign failed" title for different functions. Should be more specific.

Fix: Use specific titles: "Sign Message Failed", "Sign PSBT Failed".
Files: apps/native/src/views/BitcoinActionsView.tsx:23, 29, 61, 67


Positive Observations

Well-structured multichain architecture with clean adapter pattern
Comprehensive error logging using LogController
Strong TypeScript typing throughout most of the codebase
Good use of React Native performance patterns (memo, useMemo, useCallback)
Proper singleton pattern implementation in AppKit
Comprehensive Bitcoin PSBT handling with multiple payment types
Good separation of concerns between packages


Recommendations

  1. Address Critical Issues First: Focus on race conditions (issues Initial SDK logic #1, Provider logic + Controllers #2) and the type conversion issue (feat: Landscape mode #3)
  2. Add E2E Tests: For multichain flows, especially Bitcoin and Solana transactions
  3. Document Network Configuration: Add clear docs on how chainId maps to network endpoints
  4. Consider Adding: Cleanup lifecycle methods for event listeners and intervals
  5. Review Publication Script: Consider safer alternatives to manual package.json manipulation

Summary

This is a substantial PR adding multichain support (Bitcoin, Solana, EVM). The architecture is solid, but there are several issues that should be addressed before merging:

  • 2 Critical race conditions with async/forEach
  • 1 Critical type safety issue in Bitcoin utils
  • 1 Critical wrong network endpoint in Solana
  • Several High Priority memory leaks and inconsistencies

The code quality is generally good with strong typing and proper patterns, but needs cleanup of commented code and better error handling consistency.


@sonarqubecloud
Copy link

sonarqubecloud bot commented Oct 8, 2025

@socket-security
Copy link

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
[email protected] has Obfuscated code.

Confidence: 0.94

Location: Package overview

From: yarn.locknpm/[email protected]

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at [email protected].

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/[email protected]. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

}

return selector + encodedParams;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Address Prefix Validation Error

In encodeERC20Function, addresses are processed with slice(2) to remove the '0x' prefix. This happens without validating if the address actually starts with '0x'. If an address lacks the prefix, slice(2) incorrectly removes the first two characters, resulting in malformed transaction data.

Fix in Cursor Fix in Web


if (!namespace || !chain || !address) {
throw new Error('Invalid address');
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Redundant Validation in fetchTransactions

The accountAddress?.split(':') ?? [] expression is overly defensive. The fetchTransactions function already validates accountAddress earlier, ensuring it's a defined string before this line. This makes the optional chaining and nullish coalescing redundant.

Fix in Cursor Fix in Web

this.checkOnRampBack();
this.checkSocialLoginBack();
this.checkOnRampBackLoading();
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: RouterUtil this Context Issue

The RouterUtil object's checkBack method calls other internal methods using this. As RouterUtil is an object literal, this will be undefined or incorrect, preventing these calls from executing.

Fix in Cursor Fix in Web

open: context.appKit.open.bind(context.appKit),
close: context.appKit.close.bind(context.appKit),
disconnect: (namespace?: ChainNamespace) =>
context.appKit!.disconnect.bind(context.appKit!)(namespace),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Redundant Non-Null Assertions in disconnect Function

The disconnect function in useAppKit uses non-null assertions on context.appKit. These are redundant because a prior check within the useMemo callback already ensures appKit is available.

Fix in Cursor Fix in Web

sendToken() {
if (this.state.token?.address && this.state.sendTokenAmount && this.state.receiverAddress) {
state.loading = true;
async sendToken() {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Async Method Call Without Await

The sendToken method was changed to async. Callers that don't await this method may continue execution prematurely, which could lead to unexpected behavior.

Fix in Cursor Fix in Web

? `${activeCaipNetworkId}:${ConstantsUtil.NATIVE_TOKEN_ADDRESS[activeNamespace]}`
: undefined;

const isNetworkToken = SwapController.state.sourceToken?.address === networkTokenAddress;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Token Address Comparison Fails

Comparing networkTokenAddress (CAIP-10 format) with sourceToken.address (plain hex) results in isNetworkToken always being false. This prevents the 'max' button from correctly deducting gas fees for native tokens.

Fix in Cursor Fix in Web

@ignaciosantise ignaciosantise merged commit 5a73c77 into main Oct 8, 2025
30 checks passed
@github-actions github-actions bot locked and limited conversation to collaborators Oct 8, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants