Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions python/coinbase-agentkit/changelog.d/968.bugfix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed asyncio event loop conflicts in CdpEvmWalletProvider, CdpSmartWalletProvider, and CdpSolanaWalletProvider when used within existing async contexts (e.g., Jupyter notebooks, async frameworks)
18 changes: 9 additions & 9 deletions python/coinbase-agentkit/coinbase_agentkit/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,26 +42,29 @@
)

__all__ = [
"AgentKit",
"AgentKitConfig",
"Action",
"ActionProvider",
"create_action",
"basename_action_provider",
"WalletProvider",
"AgentKit",
"AgentKitConfig",
"CdpEvmWalletProvider",
"CdpEvmWalletProviderConfig",
"CdpSmartWalletProvider",
"CdpSmartWalletProviderConfig",
"CdpSolanaWalletProvider",
"CdpSolanaWalletProviderConfig",
"EvmWalletProvider",
"EthAccountWalletProvider",
"EthAccountWalletProviderConfig",
"EvmWalletProvider",
"WalletProvider",
"X402Config",
"__version__",
"aave_action_provider",
"basename_action_provider",
"cdp_api_action_provider",
"cdp_evm_wallet_action_provider",
"cdp_smart_wallet_action_provider",
"compound_action_provider",
"create_action",
"erc20_action_provider",
"erc721_action_provider",
"hyperbolic_action_provider",
Expand All @@ -76,7 +79,4 @@
"weth_action_provider",
"wow_action_provider",
"x402_action_provider",
"X402Config",
"aave_action_provider",
"__version__",
]
Original file line number Diff line number Diff line change
Expand Up @@ -40,48 +40,48 @@
from .x402.x402_action_provider import x402_action_provider, x402ActionProvider

__all__ = [
"AaveActionProvider",
"Action",
"ActionProvider",
"create_action",
"AaveActionProvider",
"aave_action_provider",
"BasenameActionProvider",
"basename_action_provider",
"CdpApiActionProvider",
"cdp_api_action_provider",
"CdpEvmWalletActionProvider",
"cdp_evm_wallet_action_provider",
"CdpSmartWalletActionProvider",
"cdp_smart_wallet_action_provider",
"CompoundActionProvider",
"compound_action_provider",
"ERC20ActionProvider",
"erc20_action_provider",
"Erc721ActionProvider",
"erc721_action_provider",
"HyperbolicActionProvider",
"hyperbolic_action_provider",
"MorphoActionProvider",
"morpho_action_provider",
"NillionActionProvider",
"nillion_action_provider",
"OnrampActionProvider",
"onramp_action_provider",
"PythActionProvider",
"pyth_action_provider",
"SshActionProvider",
"ssh_action_provider",
"SuperfluidActionProvider",
"superfluid_action_provider",
"TwitterActionProvider",
"twitter_action_provider",
"WalletActionProvider",
"wallet_action_provider",
"WethActionProvider",
"weth_action_provider",
"WowActionProvider",
"X402Config",
"aave_action_provider",
"basename_action_provider",
"cdp_api_action_provider",
"cdp_evm_wallet_action_provider",
"cdp_smart_wallet_action_provider",
"compound_action_provider",
"create_action",
"erc20_action_provider",
"erc721_action_provider",
"hyperbolic_action_provider",
"morpho_action_provider",
"nillion_action_provider",
"onramp_action_provider",
"pyth_action_provider",
"ssh_action_provider",
"superfluid_action_provider",
"twitter_action_provider",
"wallet_action_provider",
"weth_action_provider",
"wow_action_provider",
"x402ActionProvider",
"x402_action_provider",
"X402Config",
]
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from .schemas import GetOnrampBuyUrlSchema

__all__ = [
"GetOnrampBuyUrlSchema",
"OnrampActionProvider",
"onramp_action_provider",
"GetOnrampBuyUrlSchema",
]
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

__all__ = [
"SSHConnection",
"SSHConnectionPool",
"SSHConnectionParams",
"SSHConnectionError",
"SSHConnectionParams",
"SSHConnectionPool",
"SSHKeyError",
]
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
from .schemas import X402Config
from .x402_action_provider import x402_action_provider

__all__ = ["x402_action_provider", "X402Config"]
__all__ = ["X402Config", "x402_action_provider"]
16 changes: 8 additions & 8 deletions python/coinbase-agentkit/coinbase_agentkit/network/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,18 @@
)

__all__ = [
"Network",
"CHAIN_ID_TO_NETWORK_ID",
"NETWORK_ID_TO_CHAIN_ID",
"NETWORK_ID_TO_CHAIN",
"mainnet",
"sepolia",
"base_sepolia",
"NETWORK_ID_TO_CHAIN_ID",
"Network",
"arbitrum",
"arbitrum_sepolia",
"optimism_sepolia",
"base",
"arbitrum",
"base_sepolia",
"mainnet",
"optimism",
"polygon_amoy",
"optimism_sepolia",
"polygon",
"polygon_amoy",
"sepolia",
]
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@
from .wallet_provider import WalletProvider

__all__ = [
"WalletProvider",
"CdpEvmWalletProvider",
"CdpEvmWalletProviderConfig",
"CdpSmartWalletProvider",
"CdpSmartWalletProviderConfig",
"CdpSolanaWalletProvider",
"CdpSolanaWalletProviderConfig",
"EvmWalletProvider",
"EthAccountWalletProvider",
"EthAccountWalletProviderConfig",
"EvmWalletProvider",
"WalletProvider",
]
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ def __init__(self, config: CdpEvmWalletProviderConfig):

client = self.get_client()
if config.address:
account = asyncio.run(self._get_account(client, config.address))
account = self._run_async(self._get_account(client, config.address))
else:
account = asyncio.run(self._create_account(client))
account = self._run_async(self._create_account(client))

self._account = account

Expand Down Expand Up @@ -366,8 +366,29 @@ def _run_async(self, coroutine):

"""
try:
loop = asyncio.get_event_loop()
# Check if we're in an existing event loop
loop = asyncio.get_running_loop()
# If we reach this point, there's already a running event loop
# We need to run the coroutine in a new thread with a new event loop
import concurrent.futures

def run_in_thread():
new_loop = asyncio.new_event_loop()
asyncio.set_event_loop(new_loop)
try:
return new_loop.run_until_complete(coroutine)
finally:
new_loop.close()

with concurrent.futures.ThreadPoolExecutor() as executor:
future = executor.submit(run_in_thread)
return future.result()

except RuntimeError:
# No running event loop, safe to create and use a new one
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
return loop.run_until_complete(coroutine)
try:
return loop.run_until_complete(coroutine)
finally:
loop.close()
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,12 @@ async def initialize_accounts():
smart_account = await cdp.evm.create_smart_account(owner=owner)
return owner, smart_account

owner, smart_account = asyncio.run(initialize_accounts())
owner, smart_account = self._run_async(initialize_accounts())
self._address = smart_account.address
self._owner = owner

finally:
asyncio.run(client.close())
self._run_async(client.close())

self._gas_limit_multiplier = (
max(config.gas.gas_limit_multiplier, 1)
Expand Down Expand Up @@ -171,11 +171,32 @@ def _run_async(self, coroutine):

"""
try:
loop = asyncio.get_event_loop()
# Check if we're in an existing event loop
loop = asyncio.get_running_loop()
# If we reach this point, there's already a running event loop
# We need to run the coroutine in a new thread with a new event loop
import concurrent.futures

def run_in_thread():
new_loop = asyncio.new_event_loop()
asyncio.set_event_loop(new_loop)
try:
return new_loop.run_until_complete(coroutine)
finally:
new_loop.close()

with concurrent.futures.ThreadPoolExecutor() as executor:
future = executor.submit(run_in_thread)
return future.result()

except RuntimeError:
# No running event loop, safe to create and use a new one
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
return loop.run_until_complete(coroutine)
try:
return loop.run_until_complete(coroutine)
finally:
loop.close()

async def _get_smart_account(self, cdp):
"""Get the smart account, handling server wallet owners differently.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,11 @@ async def initialize_wallet():
wallet = await cdp.solana.create_account()
return wallet

wallet = asyncio.run(initialize_wallet())
wallet = self._run_async(initialize_wallet())
self._address = wallet.address

finally:
asyncio.run(client.close())
self._run_async(client.close())

except ImportError as e:
raise ImportError(
Expand Down Expand Up @@ -113,11 +113,32 @@ def _run_async(self, coroutine):

"""
try:
loop = asyncio.get_event_loop()
# Check if we're in an existing event loop
loop = asyncio.get_running_loop()
# If we reach this point, there's already a running event loop
# We need to run the coroutine in a new thread with a new event loop
import concurrent.futures

def run_in_thread():
new_loop = asyncio.new_event_loop()
asyncio.set_event_loop(new_loop)
try:
return new_loop.run_until_complete(coroutine)
finally:
new_loop.close()

with concurrent.futures.ThreadPoolExecutor() as executor:
future = executor.submit(run_in_thread)
return future.result()

except RuntimeError:
# No running event loop, safe to create and use a new one
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
return loop.run_until_complete(coroutine)
try:
return loop.run_until_complete(coroutine)
finally:
loop.close()

def get_address(self) -> str:
"""Get the wallet address.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,16 @@ def mocked_wallet_provider(mock_cdp_client, mock_account, mock_web3, mock_wallet
network_id=MOCK_NETWORK_ID,
)

# Patch the async run to return the mock account directly
with patch("asyncio.run", return_value=mock_account):
# Patch _run_async only during init to return the mock account
original_run_async = CdpEvmWalletProvider._run_async
CdpEvmWalletProvider._run_async = lambda self, coro: mock_account
try:
provider = CdpEvmWalletProvider(config)
finally:
CdpEvmWalletProvider._run_async = original_run_async

# Manually set account and wallet attributes
provider._account = mock_account
provider._wallet = mock_wallet
# Manually set account and wallet attributes
provider._account = mock_account
provider._wallet = mock_wallet

yield provider
yield provider
Loading
Loading