Skip to content

Conversation

@NeOMakinG
Copy link
Collaborator

@NeOMakinG NeOMakinG commented Nov 30, 2025

Description

Same as TRON but for monad:

  • Use public RPC
  • Use multicall and process tokens in batch so we can get balances in 1 call currently

It does contain:

  • Account derivation for native
  • Send for both ERC20 and native asset
  • monad assets from coingecko and prices
  • Swap using relay to monad/longtail
  • TX status update using the public RPC

Issue (if applicable)

Velocity baby

Risk

Medium but high as chain + swapper, at the convenience of the reviewer

High Risk PRs Require 2 approvals

What protocols, transaction types, wallets or contract interactions might be affected by this PR?

Testing

  • Usual new chain testing:
    -- Send from both native and erc20
    -- Balances update check
    -- Swap from any chain to Monad and monad longtail
    -- Swap between monad assets
    -- Verify balances are updating

Engineering

Operations

  • 🏁 My feature is behind a flag and doesn't require operations testing (yet)

Screenshots (if applicable)

https://discord.com/channels/554694662431178782/1444473308346056734/1444512704252481630

  • tested send from native and ERC20

Summary by CodeRabbit

  • New Features

    • Added support for the Monad blockchain: view accounts, balances, ERC‑20 tokens, fee handling, and chain-specific adapters/plugins.
  • Configuration

    • New feature flag to opt‑in Monad and environment variables to configure Monad RPC/Unchained endpoints.
  • Integration

    • Monad wired into asset data, swap routing, explorers, ledger handling, and tooling (clients, multisig/fetching utilities).
  • Preferences & Tests

    • Feature flag exposed in preferences and included in test mocks.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 30, 2025

Warning

Rate limit exceeded

@gomesalexandre has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 13 minutes and 5 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 3863668 and cd502fc.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (1)
  • package.json (2 hunks)
📝 Walkthrough

Walkthrough

Adds Monad blockchain support across configuration, types, adapters, asset generation, plugin registration, feature flags, wallet/ledger checks, and RPC client wiring; introduces a Monad EVM chain adapter and related utilities, environment variables, and asset-generation entries.

Changes

Cohort / File(s) Summary
Environment & Config
\.env, \.env.development, \.env.production, src/config.ts, src/constants/chains.ts
Added VITE_FEATURE_MONAD and Monad endpoint vars (HTTP/WS/Node URL); added validators and feature-flag gating for MonadMainnet.
Type System & CAIP Constants
packages/types/src/base.ts, packages/caip/src/constants.ts, packages/caip/src/adapters/coingecko/index.ts
Introduced KnownChainIds.MonadMainnet, monadChainId/monadAssetId, Coingecko mappings and platform enum entry for Monad.
Chain Adapter (EVM)
packages/chain-adapters/src/evm/EvmBaseAdapter.ts, packages/chain-adapters/src/evm/index.ts, packages/chain-adapters/src/evm/monad/*
Added Monad to evmChainIds, exported monad namespace; new Monad ChainAdapter implementation with multicall token balance batching, fee estimation, broadcast, and related types.
RPC / Clients / CSPs
packages/contracts/src/ethersProviderSingleton.ts, packages/contracts/src/viemClient.ts, headers/csps/chains/monad.ts, headers/csps/index.ts
Wired VITE_MONAD_NODE_URL into ethers provider and created viem public client for monad; added CSP connect-src directive for Monad node.
Asset Data & Generation
packages/utils/src/assetData/baseAssets.ts, packages/utils/src/assetData/getBaseAsset.ts, packages/utils/src/chainIdToFeeAssetId.ts, scripts/generateAssetData/monad/index.ts, scripts/generateAssetData/coingecko.ts, scripts/generateAssetData/generateAssetData.ts
Added Monad base asset and metadata, fee-asset mapping, and a monad asset generator integrated into the asset-generation pipeline.
Utilities & Helpers
packages/utils/src/getAssetNamespaceFromChainId.ts, packages/utils/src/getChainShortName.ts, packages/utils/src/getNativeFeeAssetReference.ts, src/lib/utils/monad.ts
Added Monad-specific namespace/short-name/native-fee mappings and utilities for Monad chain adapter detection and tx status polling (eth_getTransactionReceipt).
Swapper Integration
packages/swapper/src/swappers/RelaySwapper/constant.ts, packages/swapper/src/swappers/RelaySwapper/utils/relayTokenToAssetId.ts
Added monadChainId and viem monad.id mapping to RelaySwapper mappings and token->asset logic.
Wallet / Ledger Support
packages/chain-adapters/src/utils/ledgerAppGate.ts, src/hooks/useWalletSupportsChain/useWalletSupportsChain.ts, src/lib/account/evm.ts
Routed MonadMainnet to Ethereum ledger app, added supportsMonad checks, and gated EVM account derivation for wallets that don't support Monad.
Plugin System
src/plugins/monad/index.tsx, src/plugins/activePlugins.ts, src/context/PluginProvider/PluginProvider.tsx
New Monad plugin that registers a Monad ChainAdapter (preloaded known ERC20 tokens) and feature-flag gating; plugin added to active plugins and provider gating updated.
Feature Flags & Preferences
src/state/slices/preferencesSlice/preferencesSlice.ts, src/test/mocks/store.ts
Added Monad feature flag to preferences and mock store; initialized from config.
State & Mappings
src/state/slices/opportunitiesSlice/mappings.ts, src/state/slices/portfolioSlice/utils/index.ts, src/lib/portals/constants.ts, src/lib/asset-service/service/AssetService.ts, src/hooks/useActionCenterSubscribers/useSendActionSubscriber.tsx
Added Monad to DeFi opportunities mapping, portfolio label mapping, portals mapping; asset-service now filters Monad assets by feature flag; send-action subscriber polls Monad tx status.
Dependencies / Manifests
package.json
Bumped multiple @shapeshiftoss/hdwallet-* packages and upgraded viem; version bumps noted in manifest.

Sequence Diagram(s)

sequenceDiagram
  participant UI as Client/UI
  participant PP as PluginProvider
  participant Plugin as Monad Plugin
  participant Adapter as MonadChainAdapter
  participant Viem as Viem PublicClient
  participant Node as Monad RPC Node

  UI->>PP: Request to enable chain / perform tx
  PP->>Plugin: instantiate/register (if VITE_FEATURE_MONAD)
  Plugin->>Adapter: build Adapter with VITE_MONAD_NODE_URL + knownTokens
  UI->>Adapter: getAccount / broadcastTransaction / getFeeData
  Adapter->>Viem: use public client for RPC calls (multicall, eth_call, eth_sendRawTx)
  Viem->>Node: HTTP/WS RPC requests to VITE_MONAD_NODE_URL
  Node-->>Viem: RPC responses
  Viem-->>Adapter: returns balances / receipt / txHash
  Adapter-->>UI: account info / tx result / fee data
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Files warranting extra attention:
    • packages/chain-adapters/src/evm/monad/MonadChainAdapter.ts — multicall batching, fallback behavior, error handling, and RPC interactions.
    • packages/contracts/src/viemClient.ts & ethersProviderSingleton.ts — public client mappings and env-var wiring.
    • scripts/generateAssetData/* — asset generation flow and integration of new module.
    • src/lib/utils/monad.ts — tx status polling correctness and error mapping.
    • Feature-flag propagation points: src/lib/asset-service/service/AssetService.ts, PluginProvider, preferences slice.

Possibly related PRs

Suggested labels

high risk

Suggested reviewers

  • premiumjibles

Poem

🐰 Monad hops into the glade,
New nodes and tokens join the trade,
Adapters dance and plugins sing,
Flags lift up a shiny thing.
Hop, hop, MON — a rabbit brings the ring. 🎉

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: implement monad' clearly and concisely summarizes the main objective of the PR, which is to implement Monad blockchain support across the application.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@NeOMakinG NeOMakinG marked this pull request as ready for review November 30, 2025 17:00
@NeOMakinG NeOMakinG requested a review from a team as a code owner November 30, 2025 17:00
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/constants/chains.ts (1)

8-25: Add MonadMainnet feature‑flag gating to knownChainIds

Defining SECOND_CLASS_CHAINS with KnownChainIds.MonadMainnet is fine, but knownChainIds currently doesn’t respect the Monad feature flag. Other chains (e.g. Tron) are excluded when their flag is off; Monad should follow the same pattern so it doesn’t appear in knownChainIds when VITE_FEATURE_MONAD (and the corresponding enabledFlags entry) is disabled.

Consider adding a guard like this (adjusting the flag key name to match preferencesSlice):

if (chainId === KnownChainIds.MonadMainnet && !enabledFlags.Monad) return false

This keeps knownChainIds in sync with feature‑flag expectations for the new chain.

🧹 Nitpick comments (9)
.env.production (1)

30-31: LGTM! Monad environment configuration follows the established pattern.

The new Monad-specific environment variables mirror the structure used for other EVM chains. The use of public RPC endpoints aligns with the PR objectives.

Note: Static analysis flagged key ordering (UnorderedKey), but this is a cosmetic issue. Consider alphabetizing keys within each section for consistency, but this is purely optional.

Also applies to: 60-60

src/lib/utils/monad.ts (2)

19-28: Consider more descriptive error message.

The generic error message "invalid chain adapter" could be improved for debugging purposes.

Apply this diff:

   if (!isMonadChainAdapter(adapter)) {
-    throw Error('invalid chain adapter')
+    throw Error(`Expected Monad chain adapter, but got adapter for chain: ${adapter?.getChainId?.() ?? 'unknown'}`)
   }

30-61: Add timeout and structured logging to RPC call.

The fetch call lacks a timeout, which could cause indefinite blocking. Additionally, per coding guidelines, structured logging is preferred over console.error.

Apply this diff:

+import { moduleLogger } from '@/lib/logger'
+
+const logger = moduleLogger('monad')
+
 export const getMonadTransactionStatus = async (txHash: string): Promise<TxStatus> => {
   const rpcUrl = getConfig().VITE_MONAD_NODE_URL
 
   try {
-    const response = await fetch(rpcUrl, {
+    const controller = new AbortController()
+    const timeoutId = setTimeout(() => controller.abort(), 30000)
+    
+    const response = await fetch(rpcUrl, {
       method: 'POST',
       headers: { 'Content-Type': 'application/json' },
       body: JSON.stringify({
         jsonrpc: '2.0',
         method: 'eth_getTransactionReceipt',
         params: [txHash],
         id: 1,
       }),
+      signal: controller.signal,
     })
+    
+    clearTimeout(timeoutId)
 
     if (!response.ok) return TxStatus.Unknown
 
     const data = await response.json()
     const receipt = data?.result
 
     if (!receipt) return TxStatus.Pending
 
     // status is 0x0 for failed, 0x1 for confirmed
     if (receipt.status === '0x1') return TxStatus.Confirmed
     if (receipt.status === '0x0') return TxStatus.Failed
 
     return TxStatus.Unknown
   } catch (error) {
-    console.error('Error fetching Monad transaction status:', error)
+    logger.error({ error, txHash }, 'Error fetching Monad transaction status')
     return TxStatus.Unknown
   }
 }
.env (1)

230-230: LGTM! Feature flag follows the established pattern.

The VITE_FEATURE_MONAD flag is appropriately defaulted to false in the base environment file, with development environments enabling it as needed.

Note: Static analysis flagged key ordering, but this is cosmetic. Consider sorting feature flags alphabetically for consistency, but this is purely optional.

.env.development (1)

34-36: Monad env vars are fine; optionally fix key ordering for dotenv‑linter

The new Monad HTTP/WS URLs, node URL, and VITE_FEATURE_MONAD=true fit the existing patterns and match the intended feature‑flag behavior for dev.

If dotenv-linter is part of your checks and you want a clean run, you may want to move:

  • VITE_UNCHAINED_MONAD_HTTP_URL / VITE_UNCHAINED_MONAD_WS_URL above the Optimism entries, and
  • VITE_MONAD_NODE_URL above VITE_OPTIMISM_NODE_URL,
  • VITE_FEATURE_MONAD above VITE_FEATURE_TRON,

to restore the expected key ordering.

Also applies to: 64-65, 90-92

src/state/slices/preferencesSlice/preferencesSlice.ts (1)

21-33: Monad feature flag is correctly added and initialized

Monad is added to FeatureFlags and wired to getConfig().VITE_FEATURE_MONAD in initialState.featureFlags, matching the existing pattern for other chain flags and the new config validator.

If this preferences slice is part of persisted state, you may want to consider a small migration step to explicitly seed featureFlags.Monad for existing users, though at runtime a missing key will effectively behave as false in the current checks.

Also applies to: 154-167

packages/chain-adapters/src/evm/EvmBaseAdapter.ts (1)

18-21: Monad integration into EvmBaseAdapter is sound; consider tweaking nativeCurrency name

  • KnownChainIds.MonadMainnet is correctly added to evmChainIds, and assertSupportsChain now delegates to supportsMonad(wallet) for the appropriate chain reference.
  • The assertSwitchChain targetNetwork entry for Monad uses the right explorer and wires into the existing EIP‑3085 flow as expected.

Optional: to stay consistent with baseAssets and other networks, you might set name: 'Monad' (keeping symbol: 'MON') in the Monad targetNetwork entry, so nativeCurrency.name matches the actual asset name shown elsewhere.

Also applies to: 70-81, 153-182, 256-260

packages/chain-adapters/src/evm/monad/MonadChainAdapter.ts (2)

99-102: Fragile enum key lookup.

The indirect enum key lookup via indexOf is brittle and harder to maintain. Consider returning the key directly.

 getName() {
-  const enumIndex = Object.values(ChainAdapterDisplayName).indexOf(ChainAdapterDisplayName.Monad)
-  return Object.keys(ChainAdapterDisplayName)[enumIndex]
+  return 'Monad'
 }

275-296: No fee tier differentiation.

All fee tiers (fast, average, slow) return identical values. If Monad supports priority fees, consider applying multipliers to differentiate tiers. If not, this is acceptable as-is.

- Fix fee tier bug: use ...slow instead of ...fast in slow tier chainSpecific
- Add feature flag gating for Monad assets in AssetService
- Add feature flag gating for Monad in knownChainIds filtering
- Simplify getName() method to return 'Monad' directly
- Bump hdwallet packages to 1.62.17-monad.0 for Monad support

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
packages/chain-adapters/src/evm/monad/MonadChainAdapter.ts (2)

95-101: Optional: add explicit return types for display/name methods

To align with the TypeScript guidelines about explicit return types, consider annotating these two methods:

getDisplayName(): ChainAdapterDisplayName { ... }
getName(): string { ... }

Behavior is fine as-is; this is just for consistency.


339-356: Optional: simplify sanctions check for readability in broadcastTransaction

The current Promise.all usage mixes a Promise and a boolean (receiverAddress !== CONTRACT_INTERACTION && assertAddressNotSanctioned(receiverAddress)), which works but is a bit opaque. For clarity and slightly stricter typing, consider:

const checks = [assertAddressNotSanctioned(senderAddress)]
if (receiverAddress !== CONTRACT_INTERACTION) {
  checks.push(assertAddressNotSanctioned(receiverAddress))
}
await Promise.all(checks)

Semantics stay identical; this just avoids the boolean short‑circuit inside Promise.all.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between faf603f and 845b8a0.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (4)
  • package.json (2 hunks)
  • packages/chain-adapters/src/evm/monad/MonadChainAdapter.ts (1 hunks)
  • src/constants/chains.ts (2 hunks)
  • src/lib/asset-service/service/AssetService.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx,js,jsx}: Never assume a library is available - always check imports/package.json first
Prefer composition over inheritance
Write self-documenting code with clear variable and function names
Keep functions small and focused on a single responsibility
Avoid deep nesting - use early returns instead
Prefer procedural and easy to understand code
Never expose, log, or commit secrets, API keys, or credentials
Validate all inputs, especially user inputs
Handle errors gracefully with meaningful messages
Don't silently catch and ignore exceptions
Log errors appropriately for debugging
Provide fallback behavior when possible
Use appropriate data structures for the task
Never add code comments unless explicitly requested
When modifying code, do not add comments that reference previous implementations or explain what changed. Comments should only describe the current logic and functionality.
Use meaningful names for branches, variables, and functions
Always run yarn lint --fix and yarn type-check after making changes
Avoid let variable assignments - prefer const with inline IIFE switch statements or extract to functions for conditional logic

Files:

  • src/constants/chains.ts
  • src/lib/asset-service/service/AssetService.ts
  • packages/chain-adapters/src/evm/monad/MonadChainAdapter.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Avoid useEffect where practical - use it only when necessary and following best practices
Avoid 'any' types - use specific type annotations instead
For default values with user overrides, use computed values (useMemo) instead of useEffect - pattern: userSelected ?? smartDefault ?? fallback
When function parameters are unused due to interface requirements, refactor the interface or implementation to remove them rather than prefixing with underscore
Sanitize data before displaying to prevent XSS
Memoize aggressively - wrap component variables in useMemo and callbacks in useCallback where possible
For static JSX icon elements (e.g., <TbCopy />) that don't depend on state/props, define them as constants outside the component to avoid re-renders instead of using useMemo
Account for light/dark mode using useColorModeValue hook
Account for responsive mobile designs in all UI components
When applying styles, use the existing standards and conventions of the codebase
Use Chakra UI components and conventions
All copy/text must use translation keys - never hardcode strings
Use the translation hook: useTranslate() from react-polyglot
Use useFeatureFlag('FlagName') hook to access feature flag values in components
Prefer type over interface for type definitions
Use strict typing - avoid any
Use Nominal types for domain identifiers (e.g., WalletId, AccountId)
Import types from @shapeshiftoss/caip for chain/account/asset IDs
Use useAppSelector for Redux state
Use useAppDispatch for Redux actions
Memoize expensive computations with useMemo
Memoize callbacks with useCallback

**/*.{ts,tsx}: Use Result<T, E> pattern for error handling in swappers and APIs; ALWAYS use Ok() and Err() from @sniptt/monads; AVOID throwing within swapper API implementations
ALWAYS use custom error classes from @shapeshiftoss/errors with meaningful error codes for internationalization and relevant details in error objects
ALWAYS wrap async op...

Files:

  • src/constants/chains.ts
  • src/lib/asset-service/service/AssetService.ts
  • packages/chain-adapters/src/evm/monad/MonadChainAdapter.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/naming-conventions.mdc)

**/*.{js,jsx,ts,tsx}: Use camelCase for variables, functions, and methods with descriptive names that explain the purpose
Use verb prefixes for functions that perform actions (e.g., fetch, validate, execute, update, calculate)
Use UPPER_SNAKE_CASE for constants and configuration values with descriptive names
Use handle prefix for event handlers with descriptive names in camelCase
Use descriptive boolean variable names with is, has, can, should prefixes
Use named exports for components, functions, and utilities instead of default exports
Use descriptive import names and avoid renaming imports unless necessary
Avoid non-descriptive variable names like data, item, obj, and single-letter variable names except in loops
Avoid abbreviations in names unless they are widely understood
Avoid generic function names like fn, func, or callback

Files:

  • src/constants/chains.ts
  • src/lib/asset-service/service/AssetService.ts
  • packages/chain-adapters/src/evm/monad/MonadChainAdapter.ts
🧠 Learnings (32)
📓 Common learnings
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10323
File: src/components/ButtonWalletPredicate/ButtonWalletPredicate.tsx:7-7
Timestamp: 2025-08-27T09:47:06.275Z
Learning: In shapeshift/web project, NeOMakinG consistently prefers to defer UI/UX improvements and refactoring work (like the Drawer.Close hack fix in ButtonWalletPredicate.tsx) to follow-up PRs rather than expanding the scope of feature PRs, even when the improvements would enhance robustness.
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10323
File: src/pages/RFOX/components/Stake/components/StakeSummary.tsx:112-114
Timestamp: 2025-08-22T13:00:44.879Z
Learning: NeOMakinG prefers to keep PR changes minimal and focused on the core objectives, avoiding cosmetic or defensive code improvements that aren't directly related to the PR scope, even when they would improve robustness.
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10128
File: .cursor/rules/error-handling.mdc:266-274
Timestamp: 2025-07-29T10:35:22.059Z
Learning: NeOMakinG prefers less nitpicky suggestions on documentation and best practices files, finding overly detailed suggestions on minor implementation details (like console.error vs logger.error) too granular for cursor rules documentation.
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10380
File: src/pages/Dashboard/components/AccountList/AccountTable.tsx:60-0
Timestamp: 2025-09-02T08:34:08.157Z
Learning: NeOMakinG prefers code review comments to focus only on actual PR changes, not pre-existing code issues, unless there are critical security or correctness concerns directly related to the new functionality.
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10234
File: src/components/MultiHopTrade/hooks/useGetTradeQuotes/hooks/useTrackTradeQuotes.ts:42-86
Timestamp: 2025-08-08T11:41:22.794Z
Learning: NeOMakinG prefers not to include refactors in move-only PRs; such suggestions should be deferred to follow-up issues instead of being applied within the same PR.
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10380
File: src/components/Table/Table.theme.ts:177-180
Timestamp: 2025-09-02T12:38:46.940Z
Learning: NeOMakinG prefers to defer technical debt and CSS correctness issues (like improper hover selectors) to follow-up PRs when the current PR is already large and focused on major feature implementation, even when the issues are valid from a usability/technical perspective.
📚 Learning: 2025-11-24T21:20:57.909Z
Learnt from: CR
Repo: shapeshift/web PR: 0
File: .cursor/rules/swapper.mdc:0-0
Timestamp: 2025-11-24T21:20:57.909Z
Learning: Applies to packages/swapper/src/swappers/*/utils/constants.ts : Define supported chain IDs for each swapper in utils/constants.ts with both 'sell' and 'buy' properties following the pattern: SupportedChainIds type

Applied to files:

  • src/constants/chains.ts
  • src/lib/asset-service/service/AssetService.ts
📚 Learning: 2025-10-23T14:27:19.073Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10857
File: src/plugins/walletConnectToDapps/eventsManager/useWalletConnectEventsHandler.ts:101-104
Timestamp: 2025-10-23T14:27:19.073Z
Learning: In WalletConnect wallet_switchEthereumChain and wallet_addEthereumChain requests, the chainId parameter is always present as per the protocol spec. Type guards checking for missing chainId in these handlers (like `if (!evmNetworkIdHex) return`) are solely for TypeScript compiler satisfaction, not real runtime edge cases.

Applied to files:

  • src/constants/chains.ts
  • src/lib/asset-service/service/AssetService.ts
📚 Learning: 2025-11-24T21:20:04.979Z
Learnt from: CR
Repo: shapeshift/web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T21:20:04.979Z
Learning: Feature flags are stored in Redux state under `preferences.featureFlags`

Applied to files:

  • src/constants/chains.ts
📚 Learning: 2025-08-22T13:02:38.078Z
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10323
File: src/pages/Explore/Explore.tsx:91-92
Timestamp: 2025-08-22T13:02:38.078Z
Learning: In the ShapeShift web codebase, feature flags are consistently used as string literals with the useFeatureFlag hook (e.g., useFeatureFlag('RfoxFoxEcosystemPage')), not as enums. The hook accepts keyof FeatureFlags which provides type safety through the TypeScript type system rather than requiring an enum. This is the established pattern used throughout the entire codebase.

Applied to files:

  • src/constants/chains.ts
📚 Learning: 2025-11-24T21:20:04.979Z
Learnt from: CR
Repo: shapeshift/web PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-24T21:20:04.979Z
Learning: Applies to **/*.{ts,tsx} : Import types from `shapeshiftoss/caip` for chain/account/asset IDs

Applied to files:

  • src/lib/asset-service/service/AssetService.ts
  • packages/chain-adapters/src/evm/monad/MonadChainAdapter.ts
📚 Learning: 2025-11-24T21:20:57.909Z
Learnt from: CR
Repo: shapeshift/web PR: 0
File: .cursor/rules/swapper.mdc:0-0
Timestamp: 2025-11-24T21:20:57.909Z
Learning: Applies to packages/swapper/src/swappers/*/*.ts : Implement filterBuyAssetsBySellAssetId method to filter assets by supported chain IDs in the buy property

Applied to files:

  • src/lib/asset-service/service/AssetService.ts
📚 Learning: 2025-09-08T15:53:09.362Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10442
File: src/components/TradeAssetSearch/components/GroupedAssetList/GroupedAssetList.tsx:34-35
Timestamp: 2025-09-08T15:53:09.362Z
Learning: In DefaultAssetList.tsx, the GroupedAssetList component already receives the activeChainId prop correctly on line ~58, contrary to automated analysis that may flag it as missing.

Applied to files:

  • src/lib/asset-service/service/AssetService.ts
📚 Learning: 2025-11-24T21:20:57.909Z
Learnt from: CR
Repo: shapeshift/web PR: 0
File: .cursor/rules/swapper.mdc:0-0
Timestamp: 2025-11-24T21:20:57.909Z
Learning: Applies to packages/swapper/src/swappers/*/*.ts : Implement filterAssetIdsBySellable method to filter assets by supported chain IDs in the sell property

Applied to files:

  • src/lib/asset-service/service/AssetService.ts
📚 Learning: 2025-09-04T17:29:59.479Z
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10380
File: src/components/TradeAssetSearch/hooks/useGetPopularAssetsQuery.tsx:28-33
Timestamp: 2025-09-04T17:29:59.479Z
Learning: In shapeshift/web, the useGetPopularAssetsQuery function in src/components/TradeAssetSearch/hooks/useGetPopularAssetsQuery.tsx intentionally uses primaryAssets[assetId] instead of falling back to assets[assetId]. The design distributes primary assets across chains by iterating through their related assets and adding the primary asset to each related asset's chain. This ensures primary assets appear in all chains where they have related assets, supporting the grouped asset system.

Applied to files:

  • src/lib/asset-service/service/AssetService.ts
📚 Learning: 2025-08-05T23:36:13.214Z
Learnt from: premiumjibles
Repo: shapeshift/web PR: 10187
File: src/state/slices/preferencesSlice/selectors.ts:21-25
Timestamp: 2025-08-05T23:36:13.214Z
Learning: The AssetId type from 'shapeshiftoss/caip' package is a string type alias, so it can be used directly as a return type for cache key resolvers in re-reselect selectors without needing explicit string conversion.

Applied to files:

  • src/lib/asset-service/service/AssetService.ts
📚 Learning: 2025-08-29T18:09:45.982Z
Learnt from: kaladinlight
Repo: shapeshift/web PR: 10376
File: vite.config.mts:136-137
Timestamp: 2025-08-29T18:09:45.982Z
Learning: In the ShapeShift web repository vite.config.mts, the commonjsOptions.exclude configuration using bare package name strings like ['shapeshiftoss/caip', 'shapeshiftoss/types'] works correctly for excluding specific packages from CommonJS transformation, despite theoretical concerns about module ID matching patterns.

Applied to files:

  • src/lib/asset-service/service/AssetService.ts
  • package.json
📚 Learning: 2025-11-20T12:00:45.005Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 11078
File: src/setupVitest.ts:11-15
Timestamp: 2025-11-20T12:00:45.005Z
Learning: In shapeshift/web, src/setupVitest.ts must redirect 'ethers' to 'ethers5' for shapeshiftoss/hdwallet-trezor (and -trezor-connect), same as ledger and shapeshift-multichain. Removing 'trezor' from the regex causes CI/Vitest failures due to ethers v6 vs v5 API differences.

Applied to files:

  • src/lib/asset-service/service/AssetService.ts
  • package.json
📚 Learning: 2025-08-08T15:00:49.887Z
Learnt from: NeOMakinG
Repo: shapeshift/web PR: 10231
File: src/components/AssetSearch/components/AssetList.tsx:2-2
Timestamp: 2025-08-08T15:00:49.887Z
Learning: Project shapeshift/web: NeOMakinG prefers avoiding minor a11y/UI nitpicks (e.g., adding aria-hidden to decorative icons in empty states like src/components/AssetSearch/components/AssetList.tsx) within feature PRs; defer such suggestions to a follow-up instead of blocking the PR.

Applied to files:

  • src/lib/asset-service/service/AssetService.ts
📚 Learning: 2025-11-19T22:20:25.661Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10767
File: package.json:324-324
Timestamp: 2025-11-19T22:20:25.661Z
Learning: In shapeshift/web package.json, the resolution "gridplus-sdk/bs58check": "2.1.2" is intentional and must not be removed. It forces gridplus-sdk's transitive bs58check dependency from 4.0.0 down to 2.1.2 because bs58check 4.0.0 breaks legacy address validation (due to bs58 v6.0.0 and noble/hash vs 2.1.2's bs58 v4.0.0 and create-hash).

Applied to files:

  • package.json
📚 Learning: 2025-08-08T20:16:12.898Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10222
File: package.json:202-202
Timestamp: 2025-08-08T20:16:12.898Z
Learning: In shapeshift/web, the semver package must be included in dependencies (not devDependencies) because hdwallet packages have transient dependencies that require semver but don't ship it themselves. This ensures semver is available at runtime for hdwallet functionality.

Applied to files:

  • package.json
📚 Learning: 2025-08-08T20:27:02.203Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10222
File: package.json:202-202
Timestamp: 2025-08-08T20:27:02.203Z
Learning: In shapeshift/web, while gomesalexandre generally prefers pinned dependencies for security, semver with a caret range (^7.7.2) is acceptable as an exception since it's already a transient dependency of many packages and is being made explicit for hdwallet support.

Applied to files:

  • package.json
📚 Learning: 2025-11-25T21:43:10.838Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 11170
File: patches/@shapeshiftoss+bitcoinjs-lib+7.0.0-shapeshift.0.patch:9-19
Timestamp: 2025-11-25T21:43:10.838Z
Learning: In shapeshift/web, gomesalexandre will not expand PR scope to fix latent bugs in unused API surface (like bitcoinjs-lib patch validation methods) when comprehensive testing proves the actual used code paths work correctly, preferring to avoid costly hdwallet/web verdaccio publish cycles and full regression testing for conceptual issues with zero runtime impact.

Applied to files:

  • package.json
  • packages/chain-adapters/src/evm/monad/MonadChainAdapter.ts
📚 Learning: 2025-11-19T16:59:50.569Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 11012
File: src/context/WalletProvider/Vultisig/components/Connect.tsx:24-59
Timestamp: 2025-11-19T16:59:50.569Z
Learning: In src/context/WalletProvider/*/components/Connect.tsx files across the ShapeShift web codebase, the established pattern for handling null/undefined adapter from getAdapter() is to simply check `if (adapter) { ... }` without an else clause. All wallet Connect components (Coinbase, Keplr, Phantom, Ledger, MetaMask, WalletConnectV2, KeepKey, Vultisig) follow this pattern—they reset loading state after the if block but do not show error messages when adapter is null. This is an intentional design decision and should be maintained for consistency.

Applied to files:

  • package.json
  • packages/chain-adapters/src/evm/monad/MonadChainAdapter.ts
📚 Learning: 2025-08-08T10:23:16.843Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10232
File: packages/unchained-client/openapitools.json:61-61
Timestamp: 2025-08-08T10:23:16.843Z
Learning: In shapeshift/web, for temporary “monkey patch” PRs (e.g., packages/unchained-client/openapitools.json using jsDelivr CDN refs like cosmos/mayachain), gomesalexandre is fine with branch-based URLs and does not want SHA pinning. Treat this as a scoped exception to their general preference for pinned dependencies/refs.

Applied to files:

  • package.json
📚 Learning: 2025-11-24T21:20:57.909Z
Learnt from: CR
Repo: shapeshift/web PR: 0
File: .cursor/rules/swapper.mdc:0-0
Timestamp: 2025-11-24T21:20:57.909Z
Learning: Applies to packages/swapper/**/*.ts : Use TypeScript with explicit types (e.g., SupportedChainIds) for all code in the Swapper system

Applied to files:

  • packages/chain-adapters/src/evm/monad/MonadChainAdapter.ts
📚 Learning: 2025-09-12T13:44:17.019Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/hooks/useSimulateEvmTransaction.ts:0-0
Timestamp: 2025-09-12T13:44:17.019Z
Learning: gomesalexandre prefers letting chain adapter errors throw naturally in useSimulateEvmTransaction rather than adding explicit error handling for missing adapters, consistent with his fail-fast approach and dismissal of defensive validation as "stale" in WalletConnect transaction simulation flows.

Applied to files:

  • packages/chain-adapters/src/evm/monad/MonadChainAdapter.ts
📚 Learning: 2025-10-15T15:57:39.956Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10810
File: src/plugins/walletConnectToDapps/utils/tenderly/index.ts:212-0
Timestamp: 2025-10-15T15:57:39.956Z
Learning: gomesalexandre uses discriminated union patterns (e.g., `isEIP1559 ? { max_fee_per_gas, max_priority_fee_per_gas } : { gas_price }`) in WalletConnect flows without additional validation guards, trusting that the runtime data structure ensures mutual exclusivity between EIP-1559 and legacy gas pricing fields.

Applied to files:

  • packages/chain-adapters/src/evm/monad/MonadChainAdapter.ts
📚 Learning: 2025-09-10T15:35:46.223Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10458
File: src/plugins/walletConnectToDapps/components/modals/EIP155SignTypedDataConfirmation.tsx:55-55
Timestamp: 2025-09-10T15:35:46.223Z
Learning: gomesalexandre prefers fail-fast early returns over graceful degradation when critical data is missing in WalletConnect flows (like peer metadata in EIP155SignTypedDataConfirmation.tsx). He favors "safety first, always double-wrap" approach and believes missing peer metadata indicates bigger problems that should be surfaced explicitly rather than masked with partial UI rendering.

Applied to files:

  • packages/chain-adapters/src/evm/monad/MonadChainAdapter.ts
📚 Learning: 2025-09-17T22:40:30.149Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10569
File: src/plugins/walletConnectToDapps/components/WalletConnectSigningModal/WalletConnectModalSigningFooter.tsx:121-129
Timestamp: 2025-09-17T22:40:30.149Z
Learning: gomesalexandre maintains strict scope discipline even for style/UI PRs in shapeshift/web, declining functionally correct UX improvements (like keeping Cancel button enabled during gas simulation loading) when they fall outside the PR's stated styling objectives, demonstrating his consistent pattern of deferring valid but tangential improvements to separate efforts.

Applied to files:

  • packages/chain-adapters/src/evm/monad/MonadChainAdapter.ts
📚 Learning: 2025-09-12T13:43:50.695Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/hooks/useSimulateEvmTransaction.ts:91-95
Timestamp: 2025-09-12T13:43:50.695Z
Learning: gomesalexandre dismissed gas calculation overflow validation in useSimulateEvmTransaction hook as "stale", preferring to trust Tenderly's simulation data without defensive validation checks, consistent with his established pattern of relying on external service quality over defensive programming in WalletConnect flows.

Applied to files:

  • packages/chain-adapters/src/evm/monad/MonadChainAdapter.ts
📚 Learning: 2025-09-12T10:35:51.632Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/utils/tenderly/index.ts:33-45
Timestamp: 2025-09-12T10:35:51.632Z
Learning: gomesalexandre consistently dismisses CodeRabbit suggestions about replacing console.error/console.warn with structured logging in API integration code, preferring simple console logging for debugging Tenderly transaction simulation APIs in WalletConnect flows.

Applied to files:

  • packages/chain-adapters/src/evm/monad/MonadChainAdapter.ts
📚 Learning: 2025-09-12T11:56:19.437Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/utils/tenderly/index.ts:0-0
Timestamp: 2025-09-12T11:56:19.437Z
Learning: gomesalexandre rejected verbose try/catch error handling for address validation in Tenderly integration (PR #10461), calling the approach "ugly" but still implemented safety measures in commit ad7e424b89, preferring cleaner safety implementations over defensive programming patterns.

Applied to files:

  • packages/chain-adapters/src/evm/monad/MonadChainAdapter.ts
📚 Learning: 2025-09-11T22:53:19.837Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/components/modals/EIP155TransactionConfirmation.tsx:27-31
Timestamp: 2025-09-11T22:53:19.837Z
Learning: gomesalexandre trusts Tenderly's data quality and doesn't want defensive validation for gas values (transaction?.gasLimit ?? transaction?.gas) in WalletConnect flows, preferring to rely on the external service providing valid hex values.

Applied to files:

  • packages/chain-adapters/src/evm/monad/MonadChainAdapter.ts
📚 Learning: 2025-09-13T16:45:17.166Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 10461
File: src/plugins/walletConnectToDapps/components/WalletConnectSigningModal/StructuredMessage/StructuredMessage.tsx:0-0
Timestamp: 2025-09-13T16:45:17.166Z
Learning: gomesalexandre appreciates safety-focused suggestions for UI rendering in WalletConnect components, specifically defensive programming approaches that prevent null/undefined values from displaying as literal "null"/"undefined" strings in the user interface.

Applied to files:

  • packages/chain-adapters/src/evm/monad/MonadChainAdapter.ts
📚 Learning: 2025-08-26T19:04:38.672Z
Learnt from: kaladinlight
Repo: shapeshift/web PR: 10369
File: packages/chain-adapters/src/cosmossdk/CosmosSdkBaseAdapter.ts:167-176
Timestamp: 2025-08-26T19:04:38.672Z
Learning: In packages/chain-adapters/src/cosmossdk/CosmosSdkBaseAdapter.ts, when processing assets from data.assets.reduce(), the team prefers using empty catch blocks to gracefully skip any assets that fail processing, rather than specific error type handling, to avoid useless noise and ensure robust asset filtering.

Applied to files:

  • packages/chain-adapters/src/evm/monad/MonadChainAdapter.ts
📚 Learning: 2025-11-12T12:49:17.895Z
Learnt from: gomesalexandre
Repo: shapeshift/web PR: 11016
File: packages/swapper/src/swappers/NearIntentsSwapper/swapperApi/getTradeQuote.ts:109-125
Timestamp: 2025-11-12T12:49:17.895Z
Learning: In packages/chain-adapters/src/evm/utils.ts, the getErc20Data function already includes a guard that returns an empty string when contractAddress is undefined (line 8: `if (!contractAddress) return ''`). This built-in handling means callers don't need to conditionally invoke getErc20Data—it safely handles both ERC20 tokens and native assets.

Applied to files:

  • packages/chain-adapters/src/evm/monad/MonadChainAdapter.ts
🧬 Code graph analysis (2)
src/lib/asset-service/service/AssetService.ts (1)
packages/caip/src/constants.ts (1)
  • monadChainId (71-71)
packages/chain-adapters/src/evm/monad/MonadChainAdapter.ts (8)
packages/caip/src/assetId/assetId.ts (1)
  • AssetId (17-17)
packages/chain-adapters/src/evm/EvmBaseAdapter.ts (1)
  • ChainAdapterArgs (91-98)
packages/types/src/base.ts (1)
  • RootBip44Params (5-9)
packages/caip/src/constants.ts (2)
  • ASSET_REFERENCE (125-146)
  • monadAssetId (18-18)
packages/chain-adapters/src/types.ts (3)
  • Account (55-61)
  • GetFeeDataInput (318-325)
  • FeeDataEstimate (103-107)
packages/chain-adapters/src/evm/types.ts (1)
  • GasFeeDataEstimate (76-80)
packages/chain-adapters/src/utils/validateAddress.ts (1)
  • assertAddressNotSanctioned (7-24)
packages/chain-adapters/src/error/ErrorHandler.ts (1)
  • ErrorHandler (28-73)
🔇 Additional comments (10)
src/lib/asset-service/service/AssetService.ts (1)

10-10: Monad asset gating is correctly wired and consistent with existing chains

The monadChainId import and the VITE_FEATURE_MONAD‑based filter follow the same pattern as the other chain feature flags, ensuring Monad assets stay hidden when the feature is disabled while preserving asset ordering. No issues from this change.

Also applies to: 49-50

src/constants/chains.ts (2)

8-11: Monad added consistently to SECOND_CLASS_CHAINS

Including KnownChainIds.MonadMainnet alongside Tron follows the existing pattern for second‑class EVM-ish chains and looks correct.


24-24: Feature-flag gating for MonadMainnet matches existing pattern

The KnownChainIds.MonadMainnet check against enabledFlags.Monad mirrors the other chain gates and will correctly hide Monad when the feature flag is off.

package.json (2)

103-120: HDWallet monad builds kept in lockstep

All @shapeshiftoss/hdwallet-* packages are upgraded uniformly to 1.62.17-monad.0, which is what we want for consistent wallet behavior across vendors.


220-220: Viem upgrade approved; ensure EVM flows are re-verified

Bumping viem to ^2.40.3 is reasonable, but since it’s widely used across EVM adapters, WalletConnect, and swappers, make sure CI plus critical EVM flows (send/swap on main EVM chains + Monad) are exercised after this change.

packages/chain-adapters/src/evm/monad/MonadChainAdapter.ts (5)

56-93: Core Monad adapter wiring (BIP44, chain id, provider) looks correct

  • rootBip44Params uses coinType: Number(ASSET_REFERENCE.Monad) (60) and accountNumber: 0, matching the “EVM-style” derivation used elsewhere in caip.
  • assetId: monadAssetId and chainId: KnownChainIds.MonadMainnet align with the CAIP constants.
  • Using a dedicated JsonRpcProvider + Multicall3 contract while passing dummy unchained providers into EvmBaseAdapter is a pragmatic way to reuse the base adapter without depending on an unchained backend.

No issues from a correctness standpoint here.


111-147: Account + token balance handling is sound

  • Native balance and nonce are fetched directly from the Monad RPC via getBalance and getTransactionCount.
  • Known tokens are injected via the constructor, then resolved with multicall (plus an individual-call fallback) and filtered to non-zero balances before being returned in chainSpecific.tokens.

This matches expectations for an RPC-backed EVM adapter and should work well with the asset-service plugin wiring.


155-272: Multicall batching with graceful fallback is robust

  • Token balances are fetched in batches of BATCH_SIZE via Multicall3, with allowFailure: true so a single bad token doesn’t poison the whole batch.
  • Any multicall failure logs a warning and falls back to per-token balanceOf calls, with per-token try/catch to skip problematic contracts.

This is a good balance between performance and resilience for Monad’s token set.


274-337: Gas fee data + fee estimates follow the shared EVM pattern

  • getFeeData uses estimateGas on the RPC and combines that with getFeeData into fast/average/slow tiers, all currently sharing the same values, which is acceptable until Monad exposes differentiated pricing.
  • The tx fee calculation bnOrZero(maxFeePerGas ?? gasPrice).times(gasLimitString).toFixed(0) is consistent with other EVM adapters and handles both EIP-1559 and legacy gas.

Given this underpins swaps and sends, it’s worth double-checking on testnet/mainnet that the estimated fees are in the expected range for a few representative Monad transactions.


371-382: Tx history and parseTx stubs are acceptable; confirm no current consumers depend on them

Returning an empty history and rejecting parseTx with a clear error makes it explicit that Monad doesn’t yet support these surfaces. That’s fine so long as no callers assume real history/parsing for Monad accounts today.

Please just ensure any UI or services that consume tx history/parseTx either gate on chain support or tolerate the empty result / rejection for Monad.

Copy link
Contributor

@gomesalexandre gomesalexandre left a comment

Choose a reason for hiding this comment

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

Retest after bump looking gucci, get in https://jam.dev/c/77ebe523-584b-4512-adf7-ac08121f9e33

@gomesalexandre gomesalexandre enabled auto-merge (squash) December 1, 2025 10:35
@gomesalexandre gomesalexandre merged commit 7edeac2 into develop Dec 1, 2025
4 checks passed
@gomesalexandre gomesalexandre deleted the monad-web branch December 1, 2025 10:43
gomesalexandre added a commit that referenced this pull request Dec 2, 2025
- Import hyperEvm from viem/chains instead of custom definition
- Use hyperEvm.id in viemClient and RelaySwapper
- Requires viem 2.40.1+ (we have 2.40.3)

Follows Monad pattern from PR #11241.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@coderabbitai coderabbitai bot mentioned this pull request Dec 29, 2025
1 task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants