Skip to content

feat(money-account-utils): add money account transaction batch builders - #9680

Open
Jwhiles wants to merge 4 commits into
mainfrom
feat/add-hooks
Open

feat(money-account-utils): add money account transaction batch builders#9680
Jwhiles wants to merge 4 commits into
mainfrom
feat/add-hooks

Conversation

@Jwhiles

@Jwhiles Jwhiles commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Explanation

Stacked on #9397.

Ports the Money Account deposit and withdrawal transaction batch builders from metamask-mobile (app/components/UI/Money/utils/moneyAccountTransactions.ts) into this package, so mobile and extension encode the vault calls identically rather than each keeping its own copy.

What moved: the ABIs, the calldata encoders, the slippage and share arithmetic, the mUSD deposit-asset resolution, and the two contract reads (previewDeposit on the lens, getRate on the accountant).

What deliberately did not move: the four client wrappers (updateMoneyAccountDepositTokenAmount, updateMoneyAccountWithdrawTokenAmount, getMoneyAccountDepositTransactionsData, getMoneyAccountWithdrawTransactionsData). Those read vault config, provider and recipient from mobile's Redux/Engine singletons and return a Confirmations-owned type, so they stay in the client and delegate to these builders.

Signatures are unchanged from mobile, including the injected provider, so client call sites only change their import path. @ethersproject/{abi,abstract-provider,contracts} v5 is used directly rather than the ethers umbrella, matching the convention already established by money-account-balance-service (which reads the same lens and accountant contracts).

References

N/A

Checklist

  • I've updated the test suite for new or updated code as appropriate (100% coverage maintained)
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed, highlighting breaking changes as necessary
  • I've prepared draft pull requests for clients and ensured that new or referenced constants are available in those clients

🤖 Generated with Claude Code


Note

High Risk
Encodes on-chain vault deposit/withdraw flows that move mUSD; incorrect share/slippage math or calldata could cause reverts or wrong batch composition, though behavior is ported from mobile with targeted rounding fixes and broad unit tests.

Overview
Ports Money Account vault deposit and withdraw batch encoding from MetaMask Mobile into @metamask/money-account-utils, so clients share one implementation for approve/deposit and withdraw/transfer calldata.

Deposits: buildMoneyAccountDepositBatch returns keyed approveTx + depositTx, reads previewDeposit on the lens (skipped for zero amount), and sets minimumMint via 0.2% slippage (applySlippage). buildMoneyAccountDepositPlaceholderBatch resolves targets and TransactionTypes only—no provider or calldata—for Pay placeholder batches.

Withdrawals: buildMoneyAccountWithdrawBatch returns withdrawTx + transferTx, fetches the accountant rate (skipped at zero amount), converts assets to shares with getSharesForWithdrawal (ceiling division) to avoid MinimumAssetsNotMet reverts, and sets minimumAssets to amount - 1 as rounding defense; the user-facing payout still uses the full amount on the ERC-20 transfer.

Also adds getMoneyAccountDepositAssetId, exports TELLER_ABI and related types, adds @ethersproject/* v5 dependencies, and types MUSD_TOKEN_ASSET_ID_BY_CHAIN as CaipAssetType.

Reviewed by Cursor Bugbot for commit a42e8d0. Bugbot is set up for automated code reviews on this repo. Configure here.

@Jwhiles
Jwhiles requested review from a team as code owners July 28, 2026 13:46
@Jwhiles
Jwhiles force-pushed the feat/money-account-utils-package branch 3 times, most recently from bc4b92b to d90583a Compare July 30, 2026 12:51
Base automatically changed from feat/money-account-utils-package to main July 30, 2026 13:45
Jwhiles and others added 4 commits July 31, 2026 10:32
Ports the Money Account deposit and withdrawal batch builders from
metamask-mobile (`app/components/UI/Money/utils/moneyAccountTransactions.ts`)
so both clients encode the vault calls identically.

The client-resident wrappers around these builders stay in mobile: they read
vault config, provider and recipient from Redux/Engine singletons, which do
not belong in a shared package. Everything below that — ABIs, calldata
encoding, slippage and share arithmetic, and the two `previewDeposit`/`getRate`
reads — moves here, keeping the existing signatures so call sites only change
their import path.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The description predated the rescope: activity parsing and classification
were never extracted, and the vault transaction builders now are.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Address review feedback on the ported vault builders:

- Replace the `initialiseWithoutData` flag with a dedicated
  `buildMoneyAccountDepositPlaceholderBatch`. The flag path still awaited
  `previewDeposit` and discarded the result, and required five vault
  addresses it never used; the new builder is synchronous, takes only
  `chainId` and `tellerAddress`, and performs no vault reads.
- Make `MoneyAccountTxParams.params.data` required, with the no-calldata
  case as `MoneyAccountPlaceholderTxParams`, so callers of the encoding
  builders no longer have to narrow an optional field.
- Return `CaipAssetType | undefined` from `getMoneyAccountDepositAssetId`
  rather than silently defaulting to Monad, so an unsupported chain stays
  distinguishable. Clients apply their own default at the call site.
- Type `MUSD_TOKEN_ASSET_ID_BY_CHAIN` values as `CaipAssetType` and the
  deposit options' addresses as `Hex`, removing the internal casts.
- Fix withdraw comments describing the redeemed asset as USDC; the
  builders encode mUSD throughout.

Also reconcile the changelog with the released 1.0.0 of the package.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Jwhiles

Jwhiles commented Jul 31, 2026

Copy link
Copy Markdown
Contributor Author

@metamaskbot publish-preview

@github-actions

Copy link
Copy Markdown
Contributor

Preview builds have been published. Learn how to use preview builds in other projects.

Expand for full list of packages and versions.
@metamask-previews/account-tree-controller@7.5.5-preview-a42e8d0d2
@metamask-previews/accounts-controller@39.0.5-preview-a42e8d0d2
@metamask-previews/address-book-controller@7.1.2-preview-a42e8d0d2
@metamask-previews/ai-controllers@0.8.0-preview-a42e8d0d2
@metamask-previews/analytics-controller@1.2.1-preview-a42e8d0d2
@metamask-previews/analytics-data-regulation-controller@0.0.0-preview-a42e8d0d2
@metamask-previews/announcement-controller@8.1.0-preview-a42e8d0d2
@metamask-previews/app-metadata-controller@2.0.1-preview-a42e8d0d2
@metamask-previews/approval-controller@9.0.2-preview-a42e8d0d2
@metamask-previews/assets-controller@11.3.1-preview-a42e8d0d2
@metamask-previews/assets-controllers@110.0.2-preview-a42e8d0d2
@metamask-previews/authenticated-user-storage@3.0.1-preview-a42e8d0d2
@metamask-previews/base-controller@9.1.0-preview-a42e8d0d2
@metamask-previews/base-data-service@0.1.3-preview-a42e8d0d2
@metamask-previews/bitcoin-regtest-up@1.0.0-preview-a42e8d0d2
@metamask-previews/bridge-controller@78.0.1-preview-a42e8d0d2
@metamask-previews/bridge-status-controller@74.6.0-preview-a42e8d0d2
@metamask-previews/build-utils@3.0.4-preview-a42e8d0d2
@metamask-previews/chain-agnostic-permission@1.7.0-preview-a42e8d0d2
@metamask-previews/chomp-api-service@4.0.0-preview-a42e8d0d2
@metamask-previews/claims-controller@0.5.3-preview-a42e8d0d2
@metamask-previews/client-controller@1.0.1-preview-a42e8d0d2
@metamask-previews/client-utils@1.5.0-preview-a42e8d0d2
@metamask-previews/compliance-controller@2.1.0-preview-a42e8d0d2
@metamask-previews/composable-controller@12.0.1-preview-a42e8d0d2
@metamask-previews/config-registry-controller@1.0.0-preview-a42e8d0d2
@metamask-previews/connectivity-controller@0.3.0-preview-a42e8d0d2
@metamask-previews/controller-utils@12.3.0-preview-a42e8d0d2
@metamask-previews/core-backend@8.0.0-preview-a42e8d0d2
@metamask-previews/delegation-controller@3.0.2-preview-a42e8d0d2
@metamask-previews/earn-controller@12.2.2-preview-a42e8d0d2
@metamask-previews/eip-5792-middleware@3.0.5-preview-a42e8d0d2
@metamask-previews/eip-7702-internal-rpc-middleware@0.1.1-preview-a42e8d0d2
@metamask-previews/eip1193-permission-middleware@2.0.1-preview-a42e8d0d2
@metamask-previews/ens-controller@19.1.5-preview-a42e8d0d2
@metamask-previews/eth-block-tracker@15.0.1-preview-a42e8d0d2
@metamask-previews/eth-json-rpc-middleware@23.1.3-preview-a42e8d0d2
@metamask-previews/eth-json-rpc-provider@6.0.1-preview-a42e8d0d2
@metamask-previews/foundryup@1.0.1-preview-a42e8d0d2
@metamask-previews/gas-fee-controller@26.3.0-preview-a42e8d0d2
@metamask-previews/gator-permissions-controller@5.0.0-preview-a42e8d0d2
@metamask-previews/geolocation-controller@0.1.3-preview-a42e8d0d2
@metamask-previews/java-tron-up@1.0.0-preview-a42e8d0d2
@metamask-previews/json-rpc-engine@10.5.0-preview-a42e8d0d2
@metamask-previews/json-rpc-middleware-stream@8.0.8-preview-a42e8d0d2
@metamask-previews/keyring-controller@27.1.0-preview-a42e8d0d2
@metamask-previews/local-node-utils@1.0.0-preview-a42e8d0d2
@metamask-previews/logging-controller@8.0.2-preview-a42e8d0d2
@metamask-previews/message-manager@14.1.2-preview-a42e8d0d2
@metamask-previews/messenger@2.0.0-preview-a42e8d0d2
@metamask-previews/messenger-cli@0.2.0-preview-a42e8d0d2
@metamask-previews/money-account-api-data-service@0.4.0-preview-a42e8d0d2
@metamask-previews/money-account-balance-service@2.4.0-preview-a42e8d0d2
@metamask-previews/money-account-controller@0.3.3-preview-a42e8d0d2
@metamask-previews/money-account-upgrade-controller@3.0.0-preview-a42e8d0d2
@metamask-previews/money-account-utils@1.0.0-preview-a42e8d0d2
@metamask-previews/multichain-account-service@13.0.0-preview-a42e8d0d2
@metamask-previews/multichain-api-middleware@4.0.1-preview-a42e8d0d2
@metamask-previews/multichain-network-controller@3.2.1-preview-a42e8d0d2
@metamask-previews/multichain-transactions-controller@7.1.1-preview-a42e8d0d2
@metamask-previews/name-controller@9.1.2-preview-a42e8d0d2
@metamask-previews/network-connection-banner-controller@0.1.1-preview-a42e8d0d2
@metamask-previews/network-controller@34.0.0-preview-a42e8d0d2
@metamask-previews/network-enablement-controller@6.0.0-preview-a42e8d0d2
@metamask-previews/notification-services-controller@26.0.0-preview-a42e8d0d2
@metamask-previews/passkey-controller@3.0.0-preview-a42e8d0d2
@metamask-previews/permission-controller@13.1.1-preview-a42e8d0d2
@metamask-previews/permission-log-controller@5.1.0-preview-a42e8d0d2
@metamask-previews/perps-controller@10.0.0-preview-a42e8d0d2
@metamask-previews/phishing-controller@17.3.0-preview-a42e8d0d2
@metamask-previews/platform-api-docs@0.0.0-preview-a42e8d0d2
@metamask-previews/polling-controller@16.0.8-preview-a42e8d0d2
@metamask-previews/preferences-controller@23.1.0-preview-a42e8d0d2
@metamask-previews/profile-metrics-controller@4.0.2-preview-a42e8d0d2
@metamask-previews/profile-sync-controller@28.3.0-preview-a42e8d0d2
@metamask-previews/ramps-controller@18.0.0-preview-a42e8d0d2
@metamask-previews/rate-limit-controller@7.0.1-preview-a42e8d0d2
@metamask-previews/react-data-query@0.2.2-preview-a42e8d0d2
@metamask-previews/remote-feature-flag-controller@4.2.2-preview-a42e8d0d2
@metamask-previews/sample-controllers@5.0.3-preview-a42e8d0d2
@metamask-previews/seedless-onboarding-controller@10.1.0-preview-a42e8d0d2
@metamask-previews/selected-network-controller@26.1.5-preview-a42e8d0d2
@metamask-previews/sentinel-api-service@1.0.0-preview-a42e8d0d2
@metamask-previews/shield-controller@5.1.3-preview-a42e8d0d2
@metamask-previews/signature-controller@39.2.8-preview-a42e8d0d2
@metamask-previews/smart-transactions-controller@25.0.1-preview-a42e8d0d2
@metamask-previews/snap-account-service@2.1.0-preview-a42e8d0d2
@metamask-previews/social-controllers@2.7.0-preview-a42e8d0d2
@metamask-previews/solana-test-validator-up@1.0.0-preview-a42e8d0d2
@metamask-previews/stellar-quickstart-up@0.0.0-preview-a42e8d0d2
@metamask-previews/storage-service@1.0.2-preview-a42e8d0d2
@metamask-previews/subscription-controller@6.2.1-preview-a42e8d0d2
@metamask-previews/transaction-controller@69.3.0-preview-a42e8d0d2
@metamask-previews/transaction-pay-controller@26.0.1-preview-a42e8d0d2
@metamask-previews/user-operation-controller@41.2.7-preview-a42e8d0d2
@metamask-previews/wallet@8.1.0-preview-a42e8d0d2
@metamask-previews/wallet-cli@0.0.0-preview-a42e8d0d2

Comment on lines +458 to +480
const shareAmount =
amount === 0n
? 0n
: getSharesForWithdrawal(
amount,
await getVaultRate({ accountantAddress, provider }),
);
// Allow 1-unit slippage on minimumAssets as defense-in-depth against
// rounding: the contract's mulDivDown can truncate assetsOut by up to
// 1 unit relative to the requested amount. This tolerance is safe
// because ceiling division in getSharesForWithdrawal already guarantees
// assetsOut >= amount; the 1-unit slack here is a second line of
// defense, not a standalone fix. The subsequent ERC-20 transfer uses
// the original `amount`, so the tolerance does not affect how much the
// user receives — it only prevents a spurious revert from the teller's
// MinimumAssetsNotMet check.
const minimumAssets = amount > 0n ? amount - 1n : 0n;
const withdrawData = buildWithdrawData(
musdAddress,
shareAmount,
minimumAssets,
moneyAccountAddress,
);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Zero-amount withdraw placeholders encode shareAmount = 0, which Veda Teller rejects with ZeroShares.
Unlike deposit placeholders (which use a dedicated builder that omits calldata), withdraw placeholders may fail during simulation or submission before re-encoding.

Would it be worth adding a buildMoneyAccountWithdrawPlaceholderBatch, mirroring the deposit placeholder builder, to solve this ?

Returning something like
withdrawTx: teller target/type, no data,
transferTx: mUSD target/type, no data,
No amount, provider, accountant, recipient, or money-account address required

Then make buildMoneyAccountWithdrawBatch reject amount === 0n, so it cannot produce known-reverting calldata.

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.

2 participants