Repository: ccxt/ccxt (language: python)

Issue:
Cannot get deposits and withdrawals data from Coinbase 
I failed to get a list of deposits and withdrawals from Coinbase. 

- OS: MacOS
- Programming Language version: Python 3.9.7
- CCXT version: 1.88.26

I've tested the following code on several currencies:
```python
test_currency = 'USDT'
exchange = ccxt.coinbase({'apiKey': 'KEY', 'secret': 'SECRET'})
print(exchange.fetch_withdrawals(code=test_code))  # results in []
print(exchange.fetch_deposits(code=test_code))  # results in []
```

From what I can see, the `account id` is obtained correctly, but the deposit and withdrawal endpoints return empty payloads:

`fetch_withdrawals` response:
```
fetch Response: coinbase GET https://api.coinbase.com/v2/accounts/8[CUT]6/withdrawals 200 ResponseHeaders: {[CUT]} ResponseBody: {"pagination":{"ending_before":null,"starting_after":null,"previous_ending_before":null,"next_starting_after":null,"limit":25,"order":"desc","previous_uri":null,"next_uri":null},"data":[]}
```

`fetch_deposits` response:
```
fetch Response: coinbase GET https://api.coinbase.com/v2/accounts/8[CUT]6/deposits 200 ResponseHeaders: {[CUT]} ResponseBody: {"pagination":{"ending_before":null,"starting_after":null,"previous_ending_before":null,"next_starting_after":null,"limit":25,"order":"desc","previous_uri":null,"next_uri":null},"data":[]}
```



Discussion / hints:
Hello @miranska,

After some tests, I believe this is a problem with Coinbase's API engine and not CCXT since, as you can see in the image attached, even using their python SDK I'm getting an empty array for accounts that have had recent deposits/withdrawals. Nonetheless, I've reached support and am waiting for a response from them. I'll let you know as soon as I have more information.

![image](https://user-images.githubusercontent.com/43336371/175355318-0cb981f2-11da-4f1b-8702-2bd5b4888d66.png)

@carlosmiei can you share what follow-ups have you got (if any)?
any update on this?
@dakkusingh Are you still facing this issue? What's your ccxt version?

Fix patch:
diff --git a/ts/src/coinbase.ts b/ts/src/coinbase.ts
index d31a9519e08e..669069a25db1 100644
--- a/ts/src/coinbase.ts
+++ b/ts/src/coinbase.ts
@@ -81,6 +81,7 @@ export default class coinbase extends Exchange {
                 'fetchDepositAddresses': false,
                 'fetchDepositAddressesByNetwork': true,
                 'fetchDeposits': true,
+                'fetchDepositsWithdrawals': true,
                 'fetchFundingHistory': false,
                 'fetchFundingRate': false,
                 'fetchFundingRateHistory': false,
@@ -785,8 +786,15 @@ export default class coinbase extends Exchange {
          * @param {int} [since] the earliest time in ms to fetch withdrawals for
          * @param {int} [limit] the maximum number of withdrawals structures to retrieve
          * @param {object} [params] extra parameters specific to the exchange API endpoint
+         * @param {string} [params.currencyType] "fiat" or "crypto"
          * @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure}
          */
+        let currencyType = undefined;
+        [ currencyType, params ] = this.handleOptionAndParams (params, 'fetchWithdrawals', 'currencyType');
+        if (currencyType === 'crypto') {
+            const results = await this.fetchTransactionsWithMethod ('v2PrivateGetAccountsAccountIdTransactions', code, since, limit, params);
+            return this.filterByArray (results, 'type', 'withdrawal', false);
+        }
         return await this.fetchTransactionsWithMethod ('v2PrivateGetAccountsAccountIdWithdrawals', code, since, limit, params);
     }
 
@@ -800,11 +808,35 @@ export default class coinbase extends Exchange {
          * @param {int} [since] the earliest time in ms to fetch deposits for
          * @param {int} [limit] the maximum number of deposits structures to retrieve
          * @param {object} [params] extra parameters specific to the exchange API endpoint
+         * @param {string} [params.currencyType] "fiat" or "crypto"
          * @returns {object[]} a list of [transaction structures]{@link https://docs.ccxt.com/#/?id=transaction-structure}
          */
+        let currencyType = undefined;
+        [ currencyType, params ] = this.handleOptionAndParams (params, 'fetchWithdrawals', 'currencyType');
+        if (currencyType === 'crypto') {
+            const results = await this.fetchTransactionsWithMethod ('v2PrivateGetAccountsAccountIdTransactions', code, since, limit, params);
+            return this.filterByArray (results, 'type', 'deposit', false);
+        }
         return await this.fetchTransactionsWithMethod ('v2PrivateGetAccountsAccountIdDeposits', code, since, limit, params);
     }
 
+    async fetchDepositsWithdrawals (code: Str = undefined, since: Int = undefined, limit: Int = undefined, params = {}): Promise<Transaction[]> {
+        /**
+         * @method
+         * @name coinbase#fetchDepositsWithdrawals
+         * @description fetch history of deposits and withdrawals
+         * @see https://docs.cdp.coinbase.com/coinbase-app/docs/api-transactions
+         * @param {string} [code] unified currency code for the currency of the deposit/withdrawals, default is undefined
+         * @param {int} [since] timestamp in ms of the earliest deposit/withdrawal, default is undefined
+         * @param {int} [limit] max number of deposit/withdrawals to return, default = 50, Min: 1, Max: 100
+         * @param {object} [params] extra parameters specific to the exchange API endpoint
+         * @returns {object} a list of [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure}
+         */
+        await this.loadMarkets ();
+        const results = await this.fetchTransactionsWithMethod ('v2PrivateGetAccountsAccountIdTransactions', code, since, limit, params);
+        return this.filterByArray (results, 'type', [ 'deposit', 'withdrawal' ], false);
+    }
+
     parseTransactionStatus (status: Str) {
         const statuses: Dict = {
             'created': 'pending',
@@ -929,17 +961,61 @@ export default class coinbase extends Exchange {
         //         "hide_native_amount": false
         //     }
         //
+        //
+        // crypto deposit & withdrawal (using `/transactions` endpoint)
+        //    {
+        //        "amount": {
+        //            "amount": "0.00014200", (negative for withdrawal)
+        //            "currency": "BTC"
+        //        },
+        //        "created_at": "2024-03-29T15:48:30Z",
+        //        "id": "0031a605-241d-514d-a97b-d4b99f3225d3",
+        //        "idem": "092a979b-017e-4403-940a-2ca57811f442", // field present only in case of withdrawal
+        //        "native_amount": {
+        //            "amount": "9.85", (negative for withdrawal)
+        //            "currency": "USD"
+        //        },
+        //        "network": {
+        //            "status": "pending", // if status is `off_blockchain` then no more other fields are present in this object
+        //            "hash": "5jYuvrNsvX2DZoMnzGYzVpYxJLfYu4GSK3xetG1H5LHrSovsuFCFYdFMwNRoiht3s6fBk92MM8QLLnz65xuEFTrE",
+        //            "network_name": "solana",
+        //            "transaction_fee": {
+        //                "amount": "0.000100000",
+        //                "currency": "SOL"
+        //            }
+        //        },
+        //        "resource": "transaction",
+        //        "resource_path": "/v2/accounts/dc504b1c-248e-5b68-a3b0-b991f7fa84e6/transactions/0031a605-241d-514d-a97b-d4b99f3225d3",
+        //        "status": "completed",
+        //        "type": "send",
+        //        "from": { // in some cases, field might be present for deposit
+        //            "id": "7fd10cd7-b091-5cee-ba41-c29e49a7cccf",
+        //            "name": "Coinbase",
+        //            "resource": "user"
+        //        },
+        //        "to": { // field only present for withdrawal
+        //            "address": "5HA12BNthAvBwNYARYf9y5MqqCpB4qhCNFCs1Qw48ACE",
+        //            "resource": "address"
+        //        },
+        //        "description": "C3 - One Time BTC Credit . Reference Case # 123.", //  in some cases, field might be present for deposit
+        //    }
+        //
         const transactionType = this.safeString (transaction, 'type');
         let amountAndCurrencyObject = undefined;
         let feeObject = undefined;
+        const network = this.safeDict (transaction, 'network', {});
         if (transactionType === 'send') {
-            const network = this.safeDict (transaction, 'network', {});
-            amountAndCurrencyObject = this.safeDict (network, 'transaction_amount', {});
+            amountAndCurrencyObject = this.safeDict (network, 'transaction_amount');
             feeObject = this.safeDict (network, 'transaction_fee', {});
         } else {
-            amountAndCurrencyObject = this.safeDict (transaction, 'subtotal', {});
+            amountAndCurrencyObject = this.safeDict (transaction, 'subtotal');
             feeObject = this.safeDict (transaction, 'fee', {});
         }
+        if (amountAndCurrencyObject === undefined) {
+            amountAndCurrencyObject = this.safeDict (transaction, 'amount');
+        }
+        const amountString = this.safeString (amountAndCurrencyObject, 'amount');
+        const amountStringAbs = Precise.stringAbs (amountString);
         let status = this.parseTransactionStatus (this.safeString (transaction, 'status'));
         if (status === undefined) {
             const committed = this.safeBool (transaction, 'committed');
@@ -949,23 +1025,33 @@ export default class coinbase extends Exchange {
         const currencyId = this.safeString (amountAndCurrencyObject, 'currency');
         const feeCurrencyId = this.safeString (feeObject, 'currency');
         const datetime = this.safeString (transaction, 'created_at');
-        const toObject = this.safeDict (transaction, 'to', {});
-        const toAddress = this.safeString (toObject, 'address');
+        const resource = this.safeString (transaction, 'resource');
+        let type = resource;
+        if (!this.inArray (type, [ 'deposit', 'withdrawal' ])) {
+            if (Precise.stringGt (amountString, '0')) {
+                type = 'deposit';
+            } else if (Precise.stringLt (amountString, '0')) {
+                type = 'withdrawal';
+            }
+        }
+        const toObject = this.safeDict (transaction, 'to');
+        const addressTo = this.safeString (toObject, 'address');
+        const networkId = this.safeString (network, 'network_name');
         return {
             'info': transaction,
             'id': id,
-            'txid': id,
+            'txid': this.safeString (network, 'hash', id),
             'timestamp': this.parse8601 (datetime),
             'datetime': datetime,
-            'network': undefined,
-            'address': toAddress,
-            'addressTo': toAddress,
+            'network': this.networkIdToCode (networkId),
+            'address': addressTo,
+            'addressTo': addressTo,
             'addressFrom': undefined,
             'tag': undefined,
             'tagTo': undefined,
             'tagFrom': undefined,
-            'type': this.safeString (transaction, 'resource'),
-            'amount': this.safeNumber (amountAndCurrencyObject, 'amount'),
+            'type': type,
+            'amount': this.parseNumber (amountStringAbs),
             'currency': this.safeCurrencyCode (currencyId, currency),
             'status': status,
             'updated': this.parse8601 (this.safeString (transaction, 'updated_at')),


Test patch:
diff --git a/ts/src/test/static/request/coinbase.json b/ts/src/test/static/request/coinbase.json
index 310afb8a871f..8164b3f33ec7 100644
--- a/ts/src/test/static/request/coinbase.json
+++ b/ts/src/test/static/request/coinbase.json
@@ -618,6 +618,15 @@
                 "url": "https://api.coinbase.com/api/v3/brokerage/accounts?limit=250",
                 "input": []
             }
+        ],
+        "fetchDepositsWithdrawals": [{
+            "description": "spot",
+            "method": "fetchDepositsWithdrawals",
+            "url": "https://api.coinbase.com/v2/accounts/myAccount/transactions",
+            "input": [
+              "USDT"
+            ]
+          }
         ]
     }
 }
diff --git a/ts/src/test/static/response/coinbase.json b/ts/src/test/static/response/coinbase.json
index b66a53a6c6a3..5dd51ebe8a10 100644
--- a/ts/src/test/static/response/coinbase.json
+++ b/ts/src/test/static/response/coinbase.json
@@ -1012,6 +1012,177 @@
             }
           }
         }
+      ],
+      "fetchDepositsWithdrawals": [
+          {
+              "description": "spot",
+              "method": "fetchDepositsWithdrawals",
+              "input": [
+                "USDT"
+              ],
+              "httpResponse": {
+                "data": [
+                  {
+                    "amount": {
+                      "amount": "-2.010000",
+                      "currency": "USDT"
+                    },
+                    "created_at": "2024-11-03T13:50:09Z",
+                    "id": "e457a27e-8ad0-56ee-90d3-94d9a86fc203",
+                    "idem": "8b7f740a-3802-4663-88f6-5ffbc09af030",
+                    "native_amount": {
+                      "amount": "-2.01",
+                      "currency": "USD"
+                    },
+                    "network": {
+                      "hash": "123485af495b4a19521e989b3c67c14cd36e38e7eda755faf8aa4321c8f41be8",
+                      "network_name": "ethereum",
+                      "status": "pending",
+                      "transaction_fee": {
+                        "amount": "1.530199",
+                        "currency": "USDT"
+                      }
+                    },
+                    "resource": "transaction",
+                    "resource_path": "/v2/accounts/952ea6fb-7422-5097-841f-23749355aca5/transactions/e457a27e-8ad0-56ee-90d3-94d9a86fc203",
+                    "status": "completed",
+                    "to": {
+                      "address": "0x22d35481cb74fa959aec61d11a5d4507a6112345",
+                      "resource": "address"
+                    },
+                    "type": "send"
+                  },
+                  {
+                    "amount": {
+                      "amount": "2.010000",
+                      "currency": "USDT"
+                    },
+                    "created_at": "2024-11-03T13:32:02Z",
+                    "id": "45cdc829-ba86-5125-8c53-61b8ff3b2f2a",
+                    "native_amount": {
+                      "amount": "2.01",
+                      "currency": "USD"
+                    },
+                    "network": {
+                      "hash": "432e6504d28630c9fbe44ea0aeb6c8f2ff60d271d92045b24b02d65a8b0c2e3a",
+                      "network_name": "ethereum",
+                      "status": "confirmed"
+                    },
+                    "resource": "transaction",
+                    "resource_path": "/v2/accounts/952ea6fb-7422-5097-841f-23749355aca5/transactions/45cdc829-ba86-5125-8c53-61b8ff3b2f2a",
+                    "status": "completed",
+                    "type": "send"
+                  }
+                ],
+                "pagination": {
+                  "ending_before": null,
+                  "limit": "25",
+                  "next_starting_after": null,
+                  "next_uri": null,
+                  "order": "desc",
+                  "previous_ending_before": null,
+                  "previous_uri": null,
+                  "starting_after": null
+                }
+              },
+              "parsedResponse": [
+                {
+                  "info": {
+                    "amount": {
+                      "amount": "2.010000",
+                      "currency": "USDT"
+                    },
+                    "created_at": "2024-11-03T13:32:02Z",
+                    "id": "45cdc829-ba86-5125-8c53-61b8ff3b2f2a",
+                    "native_amount": {
+                      "amount": "2.01",
+                      "currency": "USD"
+                    },
+                    "network": {
+                      "hash": "432e6504d28630c9fbe44ea0aeb6c8f2ff60d271d92045b24b02d65a8b0c2e3a",
+                      "network_name": "ethereum",
+                      "status": "confirmed"
+                    },
+                    "resource": "transaction",
+                    "resource_path": "/v2/accounts/952ea6fb-7422-5097-841f-23749355aca5/transactions/45cdc829-ba86-5125-8c53-61b8ff3b2f2a",
+                    "status": "completed",
+                    "type": "send"
+                  },
+                  "id": "45cdc829-ba86-5125-8c53-61b8ff3b2f2a",
+                  "txid": "432e6504d28630c9fbe44ea0aeb6c8f2ff60d271d92045b24b02d65a8b0c2e3a",
+                  "timestamp": 1730640722000,
+                  "datetime": "2024-11-03T13:32:02Z",
+                  "network": "ERC20",
+                  "address": null,
+                  "addressTo": null,
+                  "addressFrom": null,
+                  "tag": null,
+                  "tagTo": null,
+                  "tagFrom": null,
+                  "type": "deposit",
+                  "amount": 2.01,
+                  "currency": "USDT",
+                  "status": "ok",
+                  "updated": null,
+                  "fee": {
+                    "cost": null,
+                    "currency": null
+                  }
+                },
+                {
+                  "info": {
+                    "amount": {
+                      "amount": "-2.010000",
+                      "currency": "USDT"
+                    },
+                    "created_at": "2024-11-03T13:50:09Z",
+                    "id": "e457a27e-8ad0-56ee-90d3-94d9a86fc203",
+                    "idem": "8b7f740a-3802-4663-88f6-5ffbc09af030",
+                    "native_amount": {
+                      "amount": "-2.01",
+                      "currency": "USD"
+                    },
+                    "network": {
+                      "hash": "123485af495b4a19521e989b3c67c14cd36e38e7eda755faf8aa4321c8f41be8",
+                      "network_name": "ethereum",
+                      "status": "pending",
+                      "transaction_fee": {
+                        "amount": "1.530199",
+                        "currency": "USDT"
+                      }
+                    },
+                    "resource": "transaction",
+                    "resource_path": "/v2/accounts/952ea6fb-7422-5097-841f-23749355aca5/transactions/e457a27e-8ad0-56ee-90d3-94d9a86fc203",
+                    "status": "completed",
+                    "to": {
+                      "address": "0x22d35481cb74fa959aec61d11a5d4507a6112345",
+                      "resource": "address"
+                    },
+                    "type": "send"
+                  },
+                  "id": "e457a27e-8ad0-56ee-90d3-94d9a86fc203",
+                  "txid": "123485af495b4a19521e989b3c67c14cd36e38e7eda755faf8aa4321c8f41be8",
+                  "timestamp": 1730641809000,
+                  "datetime": "2024-11-03T13:50:09Z",
+                  "network": "ERC20",
+                  "address": "0x22d35481cb74fa959aec61d11a5d4507a6112345",
+                  "addressTo": "0x22d35481cb74fa959aec61d11a5d4507a6112345",
+                  "addressFrom": null,
+                  "tag": null,
+                  "tagTo": null,
+                  "tagFrom": null,
+                  "type": "withdrawal",
+                  "amount": 2.01,
+                  "currency": "USDT",
+                  "status": "ok",
+                  "updated": null,
+                  "fee": {
+                    "cost": 1.530199,
+                    "currency": "USDT"
+                  }
+                }
+              ]
+          }
       ]
     }
 }