Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

Python: Permit2 approval in CdpSmartWalletActionProvider.swap always reports failure (integer vs string receipt status)

Đang mở Phù hợp với người mới
#1,592 0 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

@0xLittleDream đang làm issue này rồi.

Từ ngày 24/10/2026.

  • #1597 của @0xLittleDream — đang mở

Đá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

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. 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.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của coinbase/agentkit

Tất cả issue của coinbase/agentkit

Issue tương tự

Thêm issue về TypeScript

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.