Coinbase API BTC 账户丢失

Coinbase API BTC account missing

感谢您的帮助。 在使用币库API的过程中,当我调用
client.getAccounts({}, function(error, accounts){
console.log('accounts', accounts) })
我注意到许多账户,包括以太坊、莱特币,但没有比特币 (BTC) 有人可以帮忙弄清楚为什么吗? 非常感谢!

由于某种原因比特币不在返回的主账户中。您可以使用此调用获取特定帐户。

btcAccount = client.get_account('BTC')

TL;DR

使用 yarn add rxjsnpm install rxjsrxjs 添加到您的项目,然后执行此操作:

import { EMPTY, from, reduce, expand } from 'rxjs';

// Promisify: Wrap API call in a promise
const getAccountsAsync = (options) => {
    return new Promise((resolve, reject) => {
        client.getAccounts(options, (err, accounts, pagination) => {
            if (err) return reject(err);
            resolve({accounts, pagination});
        })
    })
}

// Number of accounts to fetch per request
const paginationLimit = 100;

from(getAccountsAsync({limit: paginationLimit})).pipe(
    // As long as there is a next_uri set, continue recursion
    expand(res => res.pagination.next_uri ? 
        getAccountsAsync({
            limit: paginationLimit,
            next_uri: res.pagination.next_uri, 
            starting_after: res.pagination.next_starting_after
        }) : 
        EMPTY
    ),
    // Filter and concatenate the result of all API calls
    reduce((acc, current) => acc.concat(current.accounts.filter(account => account.balance.amount > 0)), [])
).subscribe(allAccounts => console.log(allAccounts));

说明

只有前 25 个帐户得到 returned 的原因是因为 coinbase API 使用分页,而 25 是默认页面大小(参见 official docs)。

当你调用 getAccounts() 时,你实际上得到了三个 return 值(即使 API 只指定了两个):

client.getAccounts({}, (err, accounts, pagination) => {});

分页对象看起来像这样:

{
  ending_before: null,
  starting_after: null,
  previous_ending_before: null,
  next_starting_after: 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX',
  limit: 25,
  order: 'desc',
  previous_uri: null,
  next_uri: '/v2/accounts?limit=25&starting_after=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
}

您可以像这样增加限制:

client.getAccounts({limit: 100}, (err, accounts, pagination) => {});

但是coinbase目前有175个账户(币种),我们可以提供的最大分页数限制为100。

因此,为了获取其余页面,我们必须进行额外的 API 调用,提供 starting_afternext_uri 作为选项,如下所示:

client.getAccounts({
  limit: 100,
  next_uri: '/v2/accounts?limit=100&starting_after=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX',
  starting_after: 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
}, (err, accounts, pagination) => {});

next_uri

旁注

当已经有 starting_after 时,我们必须提供 next_uri 似乎是多余的。其实docs不用说我们要提供next_uri。但是,如果我省略此值,则会出现以下错误:

ClientError [AuthenticationError]: invalid signature

在深入研究 source code 之后,我发现在检查是否设置了 starting_after 之前,他们会检查是否设置了 next_uri。所以,显然我们必须提供这个值。

解决方案 1 - 递归承诺

要打印您的所有帐户(balance.amount > 0 处的帐户),我们可以先将 API 调用包装在一个承诺中,然后使用递归。可能的解决方案如下所示:

// Promisify: Wrap API call in a promise
const getAccountsAsync = (options) => {
    return new Promise((resolve, reject) => {
        client.getAccounts(options, (err, accounts, pagination) => {
            if (err) return reject(err);
            resolve({accounts, pagination});
        })
    })
}

// Fetch all accounts recursively 
const fetchAllAccounts = (nextUri, lastFetchedId) => {
    getAccountsAsync({limit: 100, next_uri: nextUri, starting_after: lastFetchedId})
        .then(res => {
            // Print your accounts from current batch
            res.accounts.forEach(account => {
                if (account.balance.amount > 0) {
                    console.log(account)     
                }
            });
            
            // Terminate recursion when next_uri is empty
            if (res.pagination.next_uri != null) {
                // Call next batch of 100 accounts, starting after lastFetchedId
                fetchAllAccounts(res.pagination.next_uri, res.pagination.next_starting_after)
            }
        })
        .catch(err => console.log(err));
};

// Initial call
fetchAllAccounts(null, null);

不幸的是,使用这种方法,帐户分布在多个回调中。我们不能轻易与他们合作。如果你想对你的帐户做更多的事情,而不仅仅是将它们打印到控制台,你可以使用 RxJS.

解决方案 2 - 使用 RxJS

更优雅的解决方案是使用 RxJS 中的 expand 函数。同样,我们必须先将 API 调用包装在一个 promise 中。

使用 yarn add rxjsnpm install rxjsrxjs 添加到您的项目,然后执行此操作:

import { EMPTY, from, reduce, expand } from 'rxjs';

// Promisify: Wrap API call in a promise
const getAccountsAsync = (options) => {
    return new Promise((resolve, reject) => {
        client.getAccounts(options, (err, accounts, pagination) => {
            if (err) return reject(err);
            resolve({accounts, pagination});
        })
    })
}

// Number of accounts to fetch per request
const paginationLimit = 100;

from(getAccountsAsync({limit: paginationLimit})).pipe(
    // As long as there is a next_uri set, continue recursion
    expand(res => res.pagination.next_uri ? 
        getAccountsAsync({
            limit: paginationLimit,
            next_uri: res.pagination.next_uri, 
            starting_after: res.pagination.next_starting_after
        }) : 
        EMPTY
    ),
    // Filter and concatenate the result of all API calls
    reduce((acc, current) => acc.concat(current.accounts.filter(account => account.balance.amount > 0)), [])
).subscribe(allAccounts => console.log(allAccounts));

这种方法的优点是,我们可以连接所有 API 调用的结果并将它们合并到一个数组中。