Skip to content

[🧠 Smart Account] EIP-5792 atomic batch TX assembly + store-based fee estimation#1913

Merged
jungcome7 merged 8 commits intoroy/evm-smart-accountfrom
roy/keplr-2028
Apr 7, 2026
Merged

[🧠 Smart Account] EIP-5792 atomic batch TX assembly + store-based fee estimation#1913
jungcome7 merged 8 commits intoroy/evm-smart-accountfrom
roy/keplr-2028

Conversation

@jungcome7
Copy link
Copy Markdown
Collaborator

@jungcome7 jungcome7 commented Apr 6, 2026

요약

PR #1909wallet_sendCalls 핸들러 위에, dApp의 calls 배열을 EVM 트랜잭션으로 조립하는 로직을 추가하고, 수수료 추정을 background 메시지에서 store 방식으로 전환합니다.

dApp: wallet_sendCalls({ calls: [approve, swap] })
  │
  ▼
handleSendCalls
  ├─ 검증 + capability 확인 + nonce 조회 (기존)
  ├─ batchCallsMap: origin/strategy/apiVersion 저장, origin 격리  ← NEW
  └─ InternalSendCallsRequest { calls, chainCapabilities }
      → UI(KEPLR-2029)에서 조립 + 가스 추정 + 서명

UI가 사용할 인프라:
  createAtomicBatchTransaction(calls)  → execute() calldata (stores-eth)  ← NEW
  buildDelegationStateOverride()       → stateOverride (stores-eth)       ← NEW
  simulateGas(addr, tx, stateOverride) → gasUsed (authorizationList 지원)  ← NEW
  computeEIP1559TxFees()               → maxFeePerGas (reactive)          ← 기존 유틸 활용

변경사항

  • TX 조립 함수 (stores-eth/batch.ts): ox/erc7821 Execute.encodeData로 calls를 execute(bytes32,bytes) calldata로 변환. 미업그레이드 계정용 stateOverride 생성 함수도 추가.
  • simulateGas authorizationList 지원 (stores-eth/account/base.ts): EIP-7702 가스 추정이 store 레이어에서 가능하도록 1줄 추가.
  • 수수료 추정 store 전환 (use-smart-account-fee.ts): EstimateSmartAccountFeeMsg를 제거하고 simulateGas + computeEIP1559TxFees로 교체. 일반 EVM sign과 동일 패턴.
  • batchCallsMap 개선 (service.ts): origin 격리, strategy/apiVersion 기반 응답, 하드코딩 제거.

store로 변경한 이유

기존 background 메시지 방식은 한번 추정하면 끝이라, 사용자가 confirm 페이지에 머무는 동안 네트워크 변동이 반영 안 됨. store 방식은 MobX reactive query로 fee가 자동 갱신되며, 일반 EVM sign 페이지와 동일한 패턴이 되어 코드베이스 일관성도 개선.

🤖 Generated with Claude Code

@jungcome7 jungcome7 requested a review from a team as a code owner April 6, 2026 05:41
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
keplr-wallet-extension Ready Ready Preview, Comment Apr 7, 2026 4:50am

Request Review

@jungcome7 jungcome7 self-assigned this Apr 6, 2026
@jungcome7 jungcome7 marked this pull request as draft April 6, 2026 05:44
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b896c570f2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

): UnsignedEVMTransaction {
const data = Execute.encodeData(
calls.map((call) => ({
to: (call.to ?? EVM_ZERO_ADDRESS) as `0x${string}`,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Preserve contract deploy calls when to is omitted

validateEIP5792Call currently treats missing to as valid (including the contract-deploy case), but this mapping coerces call.to to the zero address. That changes deploy intents into a normal CALL to 0x000...000, so bundles that omit to will never deploy and may transfer ETH to an unrecoverable address. Please reject unsupported deploy calls explicitly or encode a real creation path instead of defaulting to zero.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

수정했습니다. execute()는 CALL opcode를 사용하므로 배치 내 컨트랙트 배포(to 생략)는 불가. zero address 기본값 대신 명시적으로 거부하도록 변경.

416f3b110

Comment on lines +1462 to +1466
filledBatchTx = await fillUnsignedEVMTx(
origin,
evmInfo,
selectedAddress,
rawBatchTx,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Skip atomic prebuild when atomic status is unsupported

handleSendCalls still permits atomicRequired = false when capability status is "unsupported", but this new unconditional fillUnsignedEVMTx call pre-estimates an atomic execute(...) transaction before interaction. For unsupported delegations/account code, that estimate can fail and throw immediately, which blocks the intended non-atomic fallback path and rejects otherwise valid wallet_sendCalls requests. Gate prebuild/estimation to atomic-capable states or defer it until an atomic strategy is selected.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

이전 커밋(preBuiltTx 패턴)에서는 해당했지만, store 방식으로 전환하면서 handleSendCalls에서 fillUnsignedEVMTx 호출을 제거했습니다. 현재 코드에서는 사전 조립 없이 calls만 UI에 전달하므로 이 이슈는 해당 없습니다.

jungcome7 and others added 3 commits April 6, 2026 15:39
Pass authorizationList from UnsignedEVMTransaction to eth_estimateGas
params, enabling EIP-7702 gas estimation through the store layer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ments

- Add createAtomicBatchTransaction using ox/erc7821 Execute.encodeData
- Add buildDelegationStateOverride for non-upgraded account gas estimation
- Export helper functions for UI consumption
- Simplify handleSendCalls (no pre-built TX, gas estimated by UI)
- Add origin/strategy/apiVersion to batchCallsMap
- Fix atomic hardcoding and version hardcoding in handleGetCallsStatus
- Add origin isolation in handleGetCallsStatus

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…o store

Remove EstimateSmartAccountFeeMsg and use stores-eth simulateGas +
hooks-evm computeEIP1559TxFees for reactive fee estimation. This
aligns with the regular EVM sign page pattern and enables automatic
fee updates when network conditions change.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@jungcome7 jungcome7 changed the title [🧠 Smart Account] EIP-5792 atomic batch TX assembly + stateOverride gas simulation [🧠 Smart Account] EIP-5792 atomic batch TX assembly + store-based fee estimation Apr 6, 2026
Contract deployment (missing `to`) is not supported in execute()
batch since it uses CALL opcode, not CREATE. Explicitly reject
instead of silently defaulting to zero address.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@jungcome7
Copy link
Copy Markdown
Collaborator Author

@codex review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 416f3b1102

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +97 to +99
const feePerGas = txFees?.maxFeePerGas ?? txFees?.gasPrice;
if (!feePerGas || feePerGas.isZero()) return null;
const gasLimit = Math.ceil(feeState.gasUsed * 1.3);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Mark missing gas-price data as fee-estimation failure

When gas simulation succeeds but fee queries are still empty or errored, feePerGas becomes zero/undefined and this path returns null instead of transitioning to an error state. In that case isFailed stays false, so the confirm page can enable Approve while fee text shows failure and insufficientBalance is skipped (because estimatedFeeWei is null), which can lead to avoidable upgrade/downgrade tx failures from underfunded accounts.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

수정했습니다. !feeDisplayapproveDisabled 조건에 추가하여, fee 쿼리가 로드되지 않은 상태에서 승인 버튼이 활성화되지 않도록 했습니다.

cb42016ad

…es-eth

createAtomicBatchTransaction and buildDelegationStateOverride are
UI-only utilities (not used in background). Move to stores-eth
where other EVM utilities like serializeEVMTransaction live.

Decouple from background-specific dependencies (ALLOWED_DELEGATORS,
buildDummyAuthorizationList) by accepting authorizationList as param.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@jungcome7 jungcome7 marked this pull request as ready for review April 6, 2026 07:32
Add !feeDisplay to approveDisabled guard to prevent approval when
gas simulation succeeded but fee queries have not resolved yet.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cb42016ad3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +97 to +99
const feePerGas = txFees?.maxFeePerGas ?? txFees?.gasPrice;
if (!feePerGas || feePerGas.isZero()) return null;
const gasLimit = Math.ceil(feeState.gasUsed * 1.3);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Mark missing fee-per-gas data as estimation failure

When gas simulation succeeds but fee queries are still empty, this branch returns null and keeps feeState in success. In this commit confirm.tsx also disables approval on !feeDisplay, so users can end up with a permanently disabled Approve button and no retry path (isFailed stays false) whenever eth_feeHistory/eth_gasPrice data is temporarily unavailable, even though the request itself is valid.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

txFees는 MobX reactive query 기반이라 RPC 응답 오면 자동 갱신됩니다. 영구 교착은 발생하지 않습니다.

@jungcome7 jungcome7 marked this pull request as draft April 6, 2026 08:34
@jungcome7 jungcome7 marked this pull request as ready for review April 6, 2026 09:25
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cb42016ad3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

]);

// Fee from reactive queries (feeHistory percentile + baseFee margin, fillUnsignedEVMTx와 동일 로직)
const ethereumQueries = queriesStore.get(chainId).ethereum;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Guard fee-query setup behind chain validation

This new unconditional lookup queriesStore.get(chainId).ethereum runs even when the confirm route params are invalid, but useConfirmRoute only checks for non-empty strings and redirects asynchronously in an effect. If chainId is missing/stale (or otherwise unknown), constructing these Ethereum queries calls chainStore.getModularChain(chainId) and throws, so the confirm page crashes before redirect instead of failing gracefully. Please gate this lookup with the same defensive checks used above (or wrap it in try/catch) so invalid URLs don't hard-crash the page.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

queriesStore.get(chainId)는 잘못된 chainId에도 throw하지 않고 lazy하게 인스턴스를 생성합니다. 바로 다음 줄에서 ethereumQueries ? ... : undefined로 가드되어 있고, isValid false일 때 useRedirectIfInvalid + useEffect early return으로 이미 처리됩니다.

Let the error propagate so the error boundary catches it and shows
the cache reset page, since a missing chainId is a critical failure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Simulate L1 data fee via simulateOpStackL1Fee for chains with
"op-stack-l1-data-fee" feature and include it in the total estimated fee.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Member

@piatoss3612 piatoss3612 left a comment

Choose a reason for hiding this comment

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

lgtm

@jungcome7 jungcome7 merged commit a8e9df0 into roy/evm-smart-account Apr 7, 2026
9 checks passed
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