Python: Permit2 approval in CdpSmartWalletActionProvider.swap always reports failure (integer vs string receipt status)
Maintainer thường phản hồi trong vòng 1 ngày
Đánh giá
- Độ khó
- 2/5
- Thời gian dự kiến
- 1-3 giờ
- Mức phù hợp với người mới
- 74/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Đặc tả rõ ràng
- Mức độ hoạt động
- Sôi nổi
- Công nghệ
- python
- Lĩnh vực
- backend, blockchain, testing
Hướng nghiên cứu
Đọc CdpSmartWalletActionProvider.swap() và CdpSmartWalletProvider.wait_for_transaction_receipt để xác nhận kiểu trạng thái của biên nhận phê duyệt. Sau đó kiểm tra tests/action_providers/cdp/test_cdp_smart_wallet_action_provider.py, nơi run_until_complete được mô phỏng bỏ qua coroutine, và chạy cách tái hiện được mô tả trong issue. Công việc hoàn tất khi trạng thái số nguyên thành công 1 được chấp nhận và bài kiểm thử phê duyệt chạy nhánh liên quan.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
CdpSmartWalletActionProvider.swap() sends a Permit2 approval transaction and then
checks the receipt:
receipt = wallet_provider.wait_for_transaction_receipt(approval_tx_hash)
if receipt.status != "complete":
return {"success": False, "error": "Approval transaction failed"}
CdpSmartWalletProvider.wait_for_transaction_receipt returns
self._web3.eth.wait_for_transaction_receipt(...), a web3.py receipt whose status is
the integer 1 on success and 0 on revert. 1 != "complete" is always true, so a
successful approval is always reported as a failure.
This costs money rather than just being wrong. The approval is sent before the check
and approves the maximum:
args=[PERMIT2_ADDRESS, 2**256 - 1], # Max uint256
So each attempt spends gas and grants an unlimited Permit2 allowance on-chain, and the
caller is told it failed. An agent that retries approves again. The swap never completes
through this path.
Why the existing test does not catch it. In
tests/action_providers/cdp/test_cdp_smart_wallet_action_provider.py,
test_swap_with_approval_transaction sets
mock_loop.run_until_complete.return_value = async_result, so the _execute_swap
coroutine containing the check never runs — pytest reports
RuntimeWarning: coroutine '_execute_swap' was never awaited. Changing the mocked
receipt status from "complete" to 1 leaves the test passing, because neither value
is ever read.
Reproduction. A test that actually executes the coroutine:
"""EmberTail A1: the Permit2 approval path always reports failure.
cdp_smart_wallet_action_provider.swap() sends an approval transaction, then checks
receipt = wallet_provider.wait_for_transaction_receipt(approval_tx_hash)
if receipt.status != "complete":
return {"success": False, "error": "Approval transaction failed"}
CdpSmartWalletProvider.wait_for_transaction_receipt returns
self._web3.eth.wait_for_transaction_receipt(...), a web3.py receipt whose `status` is the
INTEGER 1 on success. `1 != "complete"` is always true, so a successful approval is
reported as a failure.
AgentKit's own test for this path mocks asyncio's run_until_complete to return a canned
success dict, so the coroutine containing this check never executes. This test runs it.
pytest tests/embertail_a1_approval_status.py -q
"""
from contextlib import asynccontextmanager
from unittest.mock import AsyncMock, Mock, patch
import pytest
from coinbase_agentkit.action_providers.cdp.cdp_smart_wallet_action_provider import (
CdpSmartWalletActionProvider,
)
from coinbase_agentkit.network import Network
FROM_TOKEN = "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"
TO_TOKEN = "0x4200000000000000000000000000000000000006"
def _provider_with(receipt_status):
"""A smart-wallet provider whose approval receipt carries `receipt_status`."""
wallet = Mock()
wallet.get_network.return_value = Network(protocol_family="evm", network_id="base-mainnet")
wallet.send_transaction.return_value = "0xapproval"
wallet.wait_for_transaction_receipt.return_value = Mock(status=receipt_status)
wallet._paymaster_url = None
# A quote that reports an allowance issue, so the approval branch is taken.
quote = Mock()
quote.liquidity_available = True
issues = Mock(spec=["allowance"]) # spec so hasattr(issues, "balance") is False
issues.allowance = Mock(required_allowance="1", current_allowance="0")
quote.issues = issues
quote.execute = AsyncMock(return_value=Mock(user_op_hash="0xuserop"))
smart_account = Mock()
smart_account.quote_swap = AsyncMock(return_value=quote)
# The user-operation receipt genuinely is a string status; only the approval
# receipt above comes from web3.py.
smart_account.wait_for_user_operation = AsyncMock(return_value=Mock(status="complete"))
wallet._get_smart_account = AsyncMock(return_value=smart_account)
@asynccontextmanager
async def _client(*_a, **_k):
yield Mock()
wallet.get_client.return_value = _client()
return wallet
def _run_swap(wallet):
provider = CdpSmartWalletActionProvider()
with (
patch.object(provider, "_get_client", return_value=wallet.get_client.return_value),
patch(
"coinbase_agentkit.action_providers.cdp.cdp_smart_wallet_action_provider.get_token_details",
return_value={
"from_token_decimals": 6,
"to_token_decimals": 18,
"from_token_name": "USDC",
"to_token_name": "WETH",
},
),
patch(
"coinbase_agentkit.action_providers.cdp.cdp_smart_wallet_action_provider.Web3"
) as web3,
):
web3.return_value.eth.contract.return_value.encodeABI.return_value = "0xdata"
return provider.swap(
wallet,
{
"from_token": FROM_TOKEN,
"to_token": TO_TOKEN,
"from_amount": "1",
"slippage_bps": 100,
},
)
def test_integer_receipt_status_reports_a_successful_approval_as_failed():
"""What the real provider returns: web3.py's integer status 1."""
result = _run_swap(_provider_with(1))
print(f"\n receipt.status = 1 (what web3.py returns) -> {result}")
assert "Approval transaction failed" in str(result), (
"expected the always-false comparison to report failure"
)
def test_string_receipt_status_succeeds():
"""The control: the status the code is written for. Shows the test harness is sound."""
result = _run_swap(_provider_with("complete"))
print(f"\n receipt.status = 'complete' (what the code expects) -> {result}")
assert "Approval transaction failed" not in str(result)
Results: with status = 1, the result is
{"success": false, "error": "Approval transaction failed"}. With status = "complete"
it passes the approval branch.
Suggested fix. Accept both shapes:
status = getattr(receipt, "status", None)
if status not in (1, "complete", "success"):
return {"success": False, "error": "Approval transaction failed"}
and give the existing test a real event loop so the branch is covered.
Note that line 322 in the same function compares a user operation receipt to
"complete", which is correct — only the web3.py receipt at the approval step is
affected.
Found by EmberTail (https://embertail.xyz). The analysis was assisted by AI tools; the result above comes from running the code. We would be glad to open a pull request with the fix and a covering test.
Abhinav
EmberTail
- Ngôn ngữ chính
- TypeScript
- Star
- 1.3k
- Fork
- 871
- Chỉ số merge pull request
- Không có pull request nào được merge trong 30 ngày
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của coinbase/agentkit
-
Moonwell redeem fails on Base Sepolia and uses 18 decimals for cbBTCCó thể đã có người làm @sebxt đã nhận 3 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 85/100
coinbase/agentkit#1578 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
ERC20 transfer reports "Transferred" when the transaction reverts (receipt status ignored)Có thể đã có người làm @AgenticFinanceGraph đã nhận 12 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 65/100
coinbase/agentkit#1558 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
[TypeScript] `ViemWalletProvider` rejects a wallet client from a newer viem than the exactly pinned one (TS2345, `tokens` missing on `Client`)Có thể đã có người làm @PrabalParihar đã nhận 3 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
coinbase/agentkit#1532 · 3 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
coinbase-agentkit 0.7.x fails on fresh install: imports solana.rpc.api, which solana>=0.37 removedCó thể đã có người làm @JulienKervarrec đã nhận 30 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
Maintainer thường phản hồi trong vòng 1 ngày
-
[Showcase / Integration] Algernon x402 Oracle: B2B Compliance & Security Verification on BaseĐang mở
Độ khó 3/5 1-2 ngày Mức phù hợp với người mới 53/100
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của coinbase/agentkit
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
braintrustdata/braintrust-sdk-javascript#2659 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
body-parser 2.3.0 in the published ai-dial-chat-bff image is vulnerable (CVE-2026-111181, HIGH)Có thể đã có người làm @illya-havsiyevych đã nhận hôm nay. Đang mởbug
Độ khó 1/5 1-3 giờ Mức phù hợp với người mới 87/100
epam/ai-dial-chat#9566 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 83/100
Maintainer thường phản hồi trong vòng 1 ngày
-
はさみアイコンを修正するĐang mởbug
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 68/100
-
resource.reload() while params is in error switches the resource to 'idle' instead of retryingĐang mởarea: core
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
Maintainer thường phản hồi trong vòng 1 ngày