|
| 1 | +from typing import Dict, Any, Optional, List |
| 2 | +import os |
| 3 | +from cdp import * |
| 4 | +from cdp.client.models.webhook import WebhookEventType, WebhookEventFilter |
| 5 | + |
| 6 | + |
| 7 | +class CDPPlugin: |
| 8 | + """ |
| 9 | + Coinbase Developer Platform Plugin for interacting with CDP |
| 10 | + """ |
| 11 | + |
| 12 | + def __init__(self) -> None: |
| 13 | + """Initialize the CDP plugin""" |
| 14 | + self.id: str = "cdp_plugin" |
| 15 | + self.name: str = "CDP Plugin" |
| 16 | + self.api_key_name = os.environ.get("CDP_API_KEY_NAME") |
| 17 | + self.api_key_private_key = os.environ.get("CDP_API_KEY_PRIVATE_KEY") |
| 18 | + self.network = "base-sepolia" # Default to testnet |
| 19 | + self.wallet = None |
| 20 | + |
| 21 | + def initialize(self, use_server_signer: bool = False): |
| 22 | + """Initialize the plugin""" |
| 23 | + if not self.api_key_name: |
| 24 | + raise ValueError("CDP_API_KEY_NAME environment variable is required") |
| 25 | + if not self.api_key_private_key: |
| 26 | + raise ValueError("CDP_API_KEY_PRIVATE_KEY environment variable is required") |
| 27 | + |
| 28 | + # Configure CDP |
| 29 | + Cdp.configure(self.api_key_name, self.api_key_private_key) |
| 30 | + if use_server_signer: |
| 31 | + Cdp.use_server_signer = True |
| 32 | + |
| 33 | + def create_wallet(self) -> Dict[str, Any]: |
| 34 | + """Create a new wallet""" |
| 35 | + self.wallet = Wallet.create(self.network) |
| 36 | + return { |
| 37 | + "wallet_id": self.wallet.id, |
| 38 | + "address": self.wallet.default_address.address_id |
| 39 | + } |
| 40 | + |
| 41 | + def import_wallet(self, wallet_id: str, seed_file: str = None) -> None: |
| 42 | + """Import an existing wallet""" |
| 43 | + self.wallet = Wallet.fetch(wallet_id) |
| 44 | + if seed_file: |
| 45 | + self.wallet.load_seed(seed_file) |
| 46 | + |
| 47 | + def get_wallet_balance(self) -> Dict[str, float]: |
| 48 | + """Get wallet balances""" |
| 49 | + if not self.wallet: |
| 50 | + raise ValueError("No wallet initialized") |
| 51 | + return { |
| 52 | + "eth": float(self.wallet.default_address.balance("eth")), |
| 53 | + "usdc": float(self.wallet.default_address.balance("usdc")) |
| 54 | + } |
| 55 | + |
| 56 | + def request_testnet_funds(self, currency: str = "eth") -> Dict[str, Any]: |
| 57 | + """Request testnet funds from faucet""" |
| 58 | + if not self.wallet: |
| 59 | + raise ValueError("No wallet initialized") |
| 60 | + tx = self.wallet.faucet(currency) |
| 61 | + tx.wait() |
| 62 | + return { |
| 63 | + "transaction_id": tx.id, |
| 64 | + "status": tx.status |
| 65 | + } |
| 66 | + |
| 67 | + def transfer( |
| 68 | + self, |
| 69 | + amount: float, |
| 70 | + currency: str, |
| 71 | + to_address: str, |
| 72 | + gasless: bool = False, |
| 73 | + skip_batching: bool = False |
| 74 | + ) -> Dict[str, Any]: |
| 75 | + """ |
| 76 | + Transfer funds to another address |
| 77 | + """ |
| 78 | + if not self.wallet: |
| 79 | + raise ValueError("No wallet initialized") |
| 80 | + |
| 81 | + tx = self.wallet.transfer( |
| 82 | + amount, |
| 83 | + currency.lower(), |
| 84 | + to_address, |
| 85 | + gasless=gasless, |
| 86 | + skip_batching=skip_batching |
| 87 | + ).wait() |
| 88 | + |
| 89 | + return { |
| 90 | + "transaction_id": tx.id, |
| 91 | + "status": tx.status |
| 92 | + } |
| 93 | + |
| 94 | + def trade( |
| 95 | + self, |
| 96 | + amount: float, |
| 97 | + from_currency: str, |
| 98 | + to_currency: str |
| 99 | + ) -> Dict[str, Any]: |
| 100 | + """ |
| 101 | + Trade between currencies |
| 102 | + """ |
| 103 | + if not self.wallet: |
| 104 | + raise ValueError("No wallet initialized") |
| 105 | + |
| 106 | + trade = self.wallet.trade( |
| 107 | + amount, |
| 108 | + from_currency.lower(), |
| 109 | + to_currency.lower() |
| 110 | + ).wait() |
| 111 | + |
| 112 | + return { |
| 113 | + "trade_id": trade.id, |
| 114 | + "status": trade.status |
| 115 | + } |
| 116 | + |
| 117 | + def get_transfer_history(self) -> List[Dict[str, Any]]: |
| 118 | + """Get transfer history""" |
| 119 | + if not self.wallet: |
| 120 | + raise ValueError("No wallet initialized") |
| 121 | + return list(self.wallet.default_address.transfers()) |
| 122 | + |
| 123 | + def get_trade_history(self) -> List[Dict[str, Any]]: |
| 124 | + """Get trade history""" |
| 125 | + if not self.wallet: |
| 126 | + raise ValueError("No wallet initialized") |
| 127 | + return list(self.wallet.default_address.trades()) |
| 128 | + |
| 129 | + def create_webhook( |
| 130 | + self, |
| 131 | + notification_url: str, |
| 132 | + event_type: str = WebhookEventType.ERC20_TRANSFER, |
| 133 | + network: str = None |
| 134 | + ) -> Dict[str, Any]: |
| 135 | + """Create a webhook for notifications""" |
| 136 | + if not self.wallet: |
| 137 | + raise ValueError("No wallet initialized") |
| 138 | + |
| 139 | + webhook = self.wallet.create_webhook( |
| 140 | + notification_url, |
| 141 | + event_type=event_type, |
| 142 | + network_id=network or self.network |
| 143 | + ) |
| 144 | + |
| 145 | + return { |
| 146 | + "webhook_id": webhook.id, |
| 147 | + "status": webhook.status |
| 148 | + } |
| 149 | + |
| 150 | + def export_wallet(self, file_path: str, encrypt: bool = True) -> None: |
| 151 | + """Export wallet data to file""" |
| 152 | + if not self.wallet: |
| 153 | + raise ValueError("No wallet initialized") |
| 154 | + self.wallet.save_seed(file_path, encrypt=encrypt) |
0 commit comments