nicehash/rest-clients-demo

problem with api - javascript client withdraw / api.post

mamrrmam opened this issue · 3 comments

Would like to use the api to make withdrawals.

I am using the javascript client.

This is in my api.js

1st i call getTime and then try to call api.post()

async function nicehashpost(url, body) {
    try {
        await api.getTime();

        const res = await api.post(url, body);
        return res;
    } catch (error) {
        console.error('Error in nicehashpost:', error);
        throw error;
    }

This is the context of the call using POST to withdraw

async function withdrawFunds(amount) {
    if (amount < threshold) {
        console.error("Withdrawal amount too low. Must be at least ${threshold}")
        return
    }
    formattedAmount = amount.toFixed(8)
    response = await nicehashpost('/main/api/v2/accounting/withdrawal', {
        body:{"currency":"BTC","amount":formattedAmount,"withdrawalAddressId":"identifier-number","walletType":"BLOCKCHAIN"}
        })
    console.log(response);
    return response;
}

Not sure why I am getting this response:

{
  error_id: 'some-error-id-number',
  errors: [ { code: 2000, message: 'Invalid session (1009)' } ]
} 

Any help would be appreciated.

try getting wallet balances first. Are you using it like this?

import config from './config' // https://github.com/nicehash/rest-clients-demo/blob/master/javascript/config.js
import Api from './api' // https://github.com/nicehash/rest-clients-demo/blob/master/javascript/api.js

var log = function () {
	return console.log(...arguments);
}

const api = new Api(config);

// get server time - required
api.getTime()
	.then(() => {
		log('server time', api.time)
		log('--')
	})
	.then(() => api.get('/main/api/v2/accounting/accounts2'))
	.then(res => {
 	     log('accounts', res);
	})
	.catch(err => {
		log('ERROR', err.error || err);
	})

btw, you don't have to call getTime before every call.

@xrado
I had originally tried to wrap both api.get and api.post as functions like this:

async function nicehashget(url) {
    try {
        // Wait for the time
        await api.getTime();
        //console.log('server time', api.time);
        //console.log('--');

        // Get the response and return it
        const res = await api.get(url);
        return res;
    } catch (error) {
        console.error('Error in nicehashget:', error);
        throw error;
    }
}

async function nicehashpost(url, body) {
    try {
        // Wait for the time
        await api.getTime();
        //console.log('server time', api.time);
        //console.log('--');

        // Get the response and return it
        const res = await api.post(url, body);
        return res;
    } catch (error) {
        console.error('Error in nicehashpost:', error);
        throw error;
    }
}

So I can easily write functions for individual api GET or POST method cases, for instance.

// fetch balance of BTC wallet
async function getBalance() {
    const response = await nicehashget('/main/api/v2/accounting/account2/BTC');
    //console.log('Balance:', response.available);
    return response.available;
}

"getBalance" works well. I think my issue is that in the case of my wrapper that passes api.post, the url, body are not being formatted properly, or that there may be an issue with headers. But I am not certain.

async function withdrawFunds(amount) {
    if (amount < threshold) {
        console.error("Withdrawal amount too low. Must be at least ${threshold}")
        return
    }
    formattedAmount = amount.toFixed(8)
    response = await nicehashpost('/main/api/v2/accounting/withdrawal', {
        body:{"currency":"BTC","amount":formattedAmount,"withdrawalAddressId":"identifier-number","walletType":"BLOCKCHAIN"}
        })
    console.log(response);
    return response;
}

withdrawalAddresses();

In this case, withdrawFunds(0.0005) for example would return the error errors: [ { code: 2000, message: 'Invalid session (1009)' } ]

Maybe this is just a question of formatting more as it was originally written as you say. But in any case, could you show me how you would call CREATE WITHDRAWAL with your method so I can work from a working example? With dummy values obv, I just need the formatting to understand how the body part of the call fits in here.

Thanks.

For those who are wondering about this issue, here is a fix:

First, modify the API call method in api.js

        apiCall(method, path, {query, body, time} = {}) {
                if(this.localTimeDiff === null) {
                        return Promise.reject(new Error('Get server time first .getTime()'));
                }

                // query in path
                var [pathOnly,pathQuery] = path.split('?');
                if(pathQuery) query = {...qs.parse(pathQuery), ...query};

                const nonce = createNonce();
                const timestamp = (time || (+new Date() + this.localTimeDiff)).toString();
                const requestHeaders = {
                    "Accept": "application/json, text/plain, */*",
                    "Content-Type": method === "GET" ? undefined : "application/json",
                    "X-Time": timestamp,
                    "X-Nonce": nonce,
                    "X-Organization-Id": this.org,
                    "X-Auth": getAuthHeader(
                        this.key,
                        this.secret,
                        timestamp,
                        nonce,
                        this.org,
                        {
                            method,
                            path: pathOnly,
                            query,
                            body,
                        },
                    ),
                    "X-Request-Id": nonce,
                    "X-User-Agent": "NHNodeClient",
                    "X-User-Lang": this.locale,
                };

                // Uncomment to Log Request Details
//                console.log('Making API Call:', method, `${this.host}${pathOnly}?${qs.stringify(query)}`);
//                console.log('Request Headers:', requestHeaders);
//               if (body) {
//                    console.log('Request Body:', body);
//               }

                return fetch(`${this.host}${pathOnly}?${qs.stringify(query)}`, {
                        method: method,
                        headers: requestHeaders,
                        body: body ? JSON.stringify(body) : null,
                    })
                    .then(res => {
                        // Log Response Details
                        console.log('Response:', res);
                        console.log('Response Headers:', res.headers);
                        return res.json();
                    })
        }

As was alluded to in other posts, there is an issue with the order of the headers in the post method, as well as the fact that you have to set Accept. Further, this allows for conditional setting of Content-Type only for non GET methods in the api call.

This API method will allow you to set general call functions in your script like so:

async function nicehashget(url) {
    try {
        // Uncomment to wait and log the time to check connection in debugging
//        await api.getTime();
//       console.log('server time', api.time);
//        console.log('--');

        // Get the response and return it
        const res = await api.get(url);
        return res;
    } catch (error) {
        console.error('Error in nicehashget:', error);
        throw error;
    }
}

async function nicehashpost(url, body) {
    try {
        //Uncomment to wait and log the time to check connection in debugging
//        await api.getTime();
//       console.log('server time', api.time);
//        console.log('--');

        const res = await api.post(url, { body });
        return res;
    } catch (error) {
        console.error(error);
        throw error;
    }
}

// You can write similar ones for DELETE or PUT, or modify to add query in place of body, etc.

And finally as an example of how to call this function inside a wrapper for using the CREATE WITHDRAWAL REST method:

async function withdrawFunds(amount, threshold = 0.00050000) {
    if (amount < threshold) {
        console.error("Withdrawal amount too low. Must be at least 0.00050000");
        return;
    }
    let formattedAmount = amount.toFixed(8);
    const body = {
        currency: "BTC",
        amount: formattedAmount,
        withdrawalAddressId: "xyzxyz123-xyz125-ghtypsc-142354htwefng", //replace with address id from whitelist
        userNote: "",
        walletType: "BLOCKCHAIN",  //specifies a blockchain withdrawal but can change as needed
    };

    try {
        const response = await nicehashpost('/main/api/v2/accounting/withdrawal', body);
        console.log(response);
        return response;
    } catch (error) {
        console.error("Error in withdrawFunds:", error);
        throw error;
    }
}