How to set signed key for /api/v3/order and order/test in Binance API? - binance

I'm using NestJs as backend to send and get requests from Binance api, but I'm getting a { code: -1022, msg: 'Signature for this request is not valid.' } when I want to post a new order (buy/sell).
This is how the body of postman looks like:
{
"symbol": "BTCUSDT",
"side": "BUY",
"type": "LIMIT",
"timeInForce": "GTC",
"quantity": "1",
"price": "59806.23000000"
}
This is my method for creating the signed key:
async testTrading(trade: ITradeReqOrderDto)
{
const serverTime = Date.now();
const extraQuery = "symbol="+trade.symbol+"&side="+trade.side+
"&type="+trade.type+"&timeInForce="+trade.timeInForce+"&quantity="+
trade.quantity+"&price="+trade.price+"&recvWindow=5000";
const signature = await this.createSignature(serverTime,extraQuery);
const response = await this.wsService.orderTest(trade, serverTime, this.apiKey, signature);
}
async createSignature(serverTime: number, extraQuery: string): Promise<string>
{
if (extraQuery != null)
{
return await this.cypherService.createSignature(this.secretKey, extraQuery+"&timestamp="+serverTime);
}
return await this.cypherService.createSignature(this.secretKey, "timestamp="+serverTime);
}
Here is where the signed key is created:
async createSignature(secret: string, queryString: string)
{
return await crypto.createHmac('sha256', secret).update(queryString).digest('hex');
}
This is how I'm sending the data to test it:
async orderTest(trade: ITradeReqOrderDto, timestamp: number, apiKey: string, signature: string): Promise<ITradeOrderResDto>
{
try
{
trade.timestamp = timestamp;
trade.signature = signature;
const response = await this.httpService.post("https://api.binance.com/api/v3/order/test", null,
{
params: trade,
headers: { "X-MBX-APIKEY": apiKey, "Content-Type": "application/json"}
}).toPromise();
} catch(error) {
console.log(error);
return { errors: [error]};
}
}
This is the path that is sended to the Binance API /api/v3/order/test:
path: '/api/v3/order/test?symbol=BTCUSDT&side=BUY&type=LIMIT&timeInForce=GTC&quantity=1&price=59806.23000000&timestamp=1618610661033&signature=6f739d53c9cc24b9c489e1d8b4b2577b05b1530806ded7ba8932339fd87243a

NEVERMIND. I just realize that I had an extra parameter which was recvWindow=5000";. Stuck with this for 2 hours but finally got it.

Related

Razorpay not returning payment_id,order_id etc. upon successfull payment

I have one function that is responsible of creating order using razorpay and verify that order .
The code for that function is : -
const paymentMethod = async ()=>{
if(!isAuthenticated())
{
toast.error('Please signin to continue');
history.push('/signin')
// return <Redirect to='/signin' />
return;
}
try{
// console.log(JSON.stringify(total));
const obj = {
amount:total
}
let data = await fetch('http://localhost:3100/api/checkout',{
method:'POST',
headers:{
"content-type":"application/json",
Accept:"application/json"
},
body: JSON.stringify(obj)
})
let {order} = await data.json()
console.log(order)
console.log(order.amount)
const options = {
key: "rzp_test_cXj3ybg8fawS9Y",
amount: order.amount,
currency: "INR",
name: "YO! Merchandise",
description: "Pay Please",
image:yo,
order_id: order.id,
callback_url: "http://localhost:3100/api/paymentverification",
prefill: {
name: "Aakash Tiwari",
email: "tiwaryaakash00#gmail.com",
contact: "8750043604"
},
notes: {
address: "Razorpay Corporate Office"
},
theme: {
color: "#13C962"
}
};
let rzp1 = await new window.Razorpay(options);
rzp1.open();
}
catch(err){
console.log(err)
}
}
But this function when call callback_url upon successfull payment it is not passing the payment_id,order_id etc. other neccessary details.
when i try to console.log there the req.body is always empty.

"Can't Buy With This Account" POST Request Error When Using The Buys Endpoint To Buy Bitcoin

My goal is to try and get a Node app (written in Typescript) to buy bitcoin using my "GBP Wallet" (fiat account). I am currently trying this via using the Axios library, rather than any unofficial Coinbase client libraries out there. I am successfully able to get data that I need from the GET endpoints of the Coinbase API. However, with the following POST endpoint I get an error:
POST https://api.coinbase.com/v2/accounts/:account_id/buys
The error:
[ { id: 'invalid_request', message: "Can't buy with this account" } ]
Here is a simplified example of the code I am trying to run at the moment:
import * as crypto from 'crypto';
import axios, { AxiosError, AxiosRequestConfig, AxiosResponse, Method } from 'axios';
import * as dotenv from 'dotenv';
dotenv.config();
const timer = (ms: number) => new Promise(res => setTimeout(res, ms));
function isNumeric(str: string): boolean {
const noSpacesStr = str.replace(/\s/g, '');
return !isNaN(Number(noSpacesStr)) && noSpacesStr.length !== 0;
}
async function coinbaseApiRequest(url: string, method: Method, data?: any): Promise<AxiosResponse> {
if (process.env.COINBASE_API_KEY === undefined || process.env.COINBASE_API_SECRET === undefined) {
throw new Error('Missing credentials');
}
const stringData = JSON.stringify(data);
const timestamp = Math.floor(Date.now() / 1000);
const message = data === undefined ? timestamp.toString() + method + url : timestamp.toString() + method + url + stringData;
const signature = crypto.createHmac("sha256", process.env.COINBASE_API_SECRET).update(message).digest("hex");
const config: AxiosRequestConfig = {
method,
url: `https://api.coinbase.com${url}`,
headers: {
'CB-ACCESS-SIGN': signature,
'CB-ACCESS-TIMESTAMP': timestamp,
'CB-ACCESS-KEY': process.env.COINBASE_API_KEY,
'CB-VERSION': '2021-05-19',
'Content-Type': 'application/json'
},
data
};
return axios(config);
}
async function getBuyPrice(): Promise<AxiosResponse> {
return coinbaseApiRequest('/v2/prices/BTC-GBP/buy', 'GET');
}
async function getSellPrice(): Promise<AxiosResponse> {
return coinbaseApiRequest('/v2/prices/BTC-GBP/sell', 'GET');
}
async function getGBPAccount(): Promise<AxiosResponse> {
return coinbaseApiRequest('/v2/accounts/GBP', 'GET');
}
async function getBitcoinAccount(): Promise<AxiosResponse> {
return coinbaseApiRequest('/v2/accounts/BTC', 'GET');
}
async function getGBPWalletPaymentMethodID(): Promise<string> {
const paymentMethods = await coinbaseApiRequest('/v2/payment-methods', 'GET');
for (let i = 0; i < paymentMethods.data.data.length; i++) {
const paymentMethod = paymentMethods.data.data[i];
if (paymentMethod.name === 'GBP Wallet' && paymentMethod.type === 'fiat_account') {
return paymentMethod.id;
}
}
throw new Error('GBP wallet has not been found');
}
async function postBuyBitcoin(accountId: string, amount: string, paymentMethod: string): Promise<AxiosResponse> {
if (!isNumeric(amount)) { throw new Error('amount arg is not a valid number'); }
const body = { amount, currency: 'BTC', payment_method: paymentMethod };
return coinbaseApiRequest(`/v2/accounts/${accountId}/buys`, 'POST', body);
}
async function run(): Promise<void> {
try {
const oldGBPAccRes = await getGBPAccount();
const oldBTCAccRes = await getBitcoinAccount();
const paymentMethodID = await getGBPWalletPaymentMethodID();
const buyRes = await postBuyBitcoin(oldGBPAccRes.data.data.id, '0.00001', paymentMethodID);
const newGBPAccRes = await getGBPAccount();
const newBTCAccRes = await getBitcoinAccount();
console.log(`old GBP account response:`);
console.log(oldGBPAccRes.data);
console.log(`old BTC account response:`);
console.log(oldBTCAccRes.data);
console.log(`bitcoin buy response:`);
console.log(buyRes);
console.log(`new GBP account response:`);
console.log(newGBPAccRes.data);
console.log(`new BTC account response:`);
console.log(newBTCAccRes.data);
console.log('Finished payment');
} catch (error: any | AxiosError) {
if (axios.isAxiosError(error)) {
console.log(error.response?.data.errors);
}
}
}
run();
All the GET request functions seem to work fine and return the responses correctly when I run them individually. The only issue I have is with the POST request function. I get the following error logged when running the code above:
[ { id: 'invalid_request', message: "Can't buy with this account" } ]
Not sure why this is happening, and there does not seem to be much on this online relevant to node based implementations, but this seems to be somewhat related. would appreciate some guidance. Thanks in advance.

Axios and Oauth1.0 - 'status: 400, Bad Request'

I'm new on Nodejs and all the modules related with Node. I've been trying to use axios for send a Oauth1.0 Autorization signature, but i'm getting: response: { status: 400, statusText: 'Bad Request', ...}
import { BASE_URL } from '../../../config/config.js';
import axios from 'axios';
import status from 'http-status';
import OAuth from 'oauth-1.0a';
import { createHmac } from 'crypto';
import dotenv from 'dotenv';
dotenv.config();
const CONSUMERKEY = process.env.consumer_key;
const CONSUMERSECRET = process.env.consumer_secret;
const TOKENKEY = process.env.access_token;
const TOKENSECRET = process.env.token_secret;
export const oauth = OAuth({
consumer: {
key: CONSUMERKEY,
secret: CONSUMERSECRET,
},
signature_method: 'HMAC-SHA1',
hash_function(base_string, key) {
return createHmac('sha1', key)
.update(base_string)
.digest('base64')
},
})
export const token = {
key: TOKENKEY,
secret: TOKENSECRET,
}
const doRequest = async (query) => {
const request_data = {
url: `${BASE_URL}`,
method: 'GET',
params: { q: `${query}` },
};
const authHeader = oauth.toHeader(oauth.authorize(request_data, token));
return await axios.get(request_data.url, request_data.params, { headers: authHeader });
};
const searchU = async (term) => {
return await doRequest(`${term}`);
};
export const userS = async (req, res, next) => {
try {
const { query } = req;
const { data } = await searchU(query.q);
const string = JSON.stringify(data);
const Rs = JSON.parse(string);
const response = {
code: 1,
message: 'sucess',
response: Rs
};
res.status(status.OK).send(response);
} catch (error) {
next(error);
if (error.response){
console.log("Response: ");
console.log(error.response);
} else if(error.request){
console.log("Request: ");
console.log(error.request)
} else if(error.message){
console.log("Message: ");
console.log(error.message)
}
}
};
I've been also trying the solution given On this post: but there's no way I can make this work, no idea what i could be doing wron...
When i try the following code (see below), using Request module (which is deprecated) works well, but I really need to do it with Axios...
const request_data = {
url: `${BASE_URL}`,
method: 'GET',
params: { q: `${query}` },
};
const authHeader = oauth.toHeader(oauth.authorize(request_data, token));
request(
{
url: request_data.url,
method: request_data.method,
form: request_data.params,
headers: authHeader,
},
function(error, response, body) {
console.log(JSON.parse(body));
}
)
Any thoughts on what I'm doing wrong on this?? Thank you very much!!
Refer to the following link for the Request Config for Axios. I believe you need to have the query params after the header in the axios.get()
Axios Request Config
Try, the following and see how it goes:-
return await axios.get(request_data.url, { headers: authHeader }, request_data.params);

aws elastic search http request , error 'The bulk request must be terminated by a newline'

I have used this link to create a bulk http request using a JSON.stringified(body) like this:
const body = [ { index: { _index: 'image_2', _type: '_doc', _id: 0 } },
{ imageKey: 'test' },
{ index: { _index: 'image_2', _type: '_doc', _id: 1 } },
{ imageKey: 'test2' } ]
but I keep getting the error
{ statusCode: 400,
body: '{"error":{"root_cause":[{"type":"illegal_argument_exception","reason":"The bulk request must be terminated by a newline [\\\\n]"}],"type":"illegal_argument_exception","reason":"The bulk request must be terminated by a newline [\\\\n]"},"status":400}' }
I already tried this below and it is not working either:
const body = `${JSON.stringified(body)\n}`;
any ideas are appreciated :)
here is my aws elastic search function:
function elasticsearchFetch(elasticsearchDomain, endpointPath, options = {}, region = process.env.AWS_REGION) {
return new Promise((resolve, reject) => {
const { body, method = 'GET' } = options;
const endpoint = new AWS.Endpoint(elasticsearchDomain);
const request = new AWS.HttpRequest(endpoint, region);
request.method = method;
request.path += endpointPath;
request.headers.host = elasticsearchDomain;
if (body) {
request.body = body;
request.headers['Content-Type'] = 'application/json';
request.headers['Content-Length'] = request.body.length;
}
const credentials = new AWS.EnvironmentCredentials('AWS');
const signer = new AWS.Signers.V4(request, 'es');
signer.addAuthorization(credentials, new Date());
const client = new AWS.HttpClient();
client.handleRequest(request, null, (res) => {
res.on('data', (chunk) => {
chunks += chunk;
});
res.on('end', () => resolve({ statusCode: res.statusCode, body: chunks }));
}, error => reject(error));
});
}
and here is the code for my lambda function using the above request signer:
const elasticsearchFetch = require('rms-library/fetch');
exports.handler = async ({ imageID, bulk, products }) => {
if (!Array.isArray(products) || !imageID) {
throw new Error('error in bulk operation');
}
const bulkList = [];
products.forEach((product, i) => {
bulkList.push({ index: { _index: imageID, _type: '_doc', _id: i } });
bulkList.push(product);
});
bulkList.push('');
console.log('bulkList', bulkList);
const result = await elasticsearchFetch.elasticsearch(
process.env.ELASTIC_SEARCH_DOMAIN,
'_bulk',
{ method: 'PUT', body: JSON.stringify(bulkList) },
);
console.log(result);
};
Ok, first of all you need to use POST instead of PUT with the _bulk endpoint and the body cannot be a stringified JSON array.
Try like this instead:
const result = await elasticsearchFetch.elasticsearch(
process.env.ELASTIC_SEARCH_DOMAIN,
'_bulk',
{ method: 'POST', body: bulkList.map(json => {return JSON.stringify(json);}).join('\n') + '\n' },
);

How to authorize an HTTP POST request to execute dataflow template with REST API

I am trying to execute the Cloud Bigtable to Cloud Storage SequenceFile template via REST API in a NodeJS backend server.
I am using axios 0.17.1 to send the request and I'm getting 401 status.
I tried to follow the google documentation however I couldn't figure out how to authorize an HTTP request to run a dataflow template.
I want to be authenticated as a service account and I successfully generated and dowloaded the json file containing the private key.
Can anyone help me by showing an example of sending HTTP POST request to https://dataflow.googleapis.com/v1b3/projects/[YOUR_PROJECT_ID]/templates:launch?gcsPath=gs://dataflow-templates/latest/
You need to generate a jwt from your service account credentials. The jwt can be exchanged for an access token which can then be used to make the request to execute the Dataflow job. Complete example:
import axios from "axios";
import jwt from "jsonwebtoken";
import mem from "mem";
import fs from "fs";
const loadServiceAccount = mem(function(){
// This is a string containing service account credentials
const serviceAccountJson = process.env.GOOGLE_APPLICATION_CREDENTIALS;
if (!serviceAccountJson) {
throw new Error("Missing GCP Credentials");
}
})
const loadCredentials = mem(function() {
loadServiceAccount();
const credentials = JSON.parse(fs.readFileSync("key.json").toString());
return {
projectId: credentials.project_id,
privateKeyId: credentials.private_key_id,
privateKey: credentials.private_key,
clientEmail: credentials.client_email,
};
});
interface ProjectCredentials {
projectId: string;
privateKeyId: string;
privateKey: string;
clientEmail: string;
}
function generateJWT(params: ProjectCredentials) {
const scope = "https://www.googleapis.com/auth/cloud-platform";
const authUrl = "https://www.googleapis.com/oauth2/v4/token";
const issued = new Date().getTime() / 1000;
const expires = issued + 60;
const payload = {
iss: params.clientEmail,
sub: params.clientEmail,
aud: authUrl,
iat: issued,
exp: expires,
scope: scope,
};
const options = {
keyid: params.privateKeyId,
algorithm: "RS256",
};
return jwt.sign(payload, params.privateKey, options);
}
async function getAccessToken(credentials: ProjectCredentials): Promise<string> {
const jwt = generateJWT(credentials);
const authUrl = "https://www.googleapis.com/oauth2/v4/token";
const params = {
grant_type: "urn:ietf:params:oauth:grant-type:jwt-bearer",
assertion: jwt,
};
try {
const response = await axios.post(authUrl, params);
return response.data.access_token;
} catch (error) {
console.error("Failed to get access token", error);
throw error;
}
}
function buildTemplateParams(projectId: string, table: string) {
return {
jobName: `[job-name]`,
parameters: {
bigtableProjectId: projectId,
bigtableInstanceId: "[table-instance]",
bigtableTableId: table,
outputDirectory: `[gs://your-instance]`,
filenamePrefix: `${table}-`,
},
environment: {
zone: "us-west1-a" // omit or define your own,
tempLocation: `[gs://your-instance/temp]`,
},
};
}
async function backupTable(table: string) {
console.info(`Executing backup template for table=${table}`);
const credentials = loadCredentials();
const { projectId } = credentials;
const accessToken = await getAccessToken(credentials);
const baseUrl = "https://dataflow.googleapis.com/v1b3/projects";
const templatePath = "gs://dataflow-templates/latest/Cloud_Bigtable_to_GCS_Avro";
const url = `${baseUrl}/${projectId}/templates:launch?gcsPath=${templatePath}`;
const template = buildTemplateParams(projectId, table);
try {
const response = await axios.post(url, template, {
headers: { Authorization: `Bearer ${accessToken}` },
});
console.log("GCP Response", response.data);
} catch (error) {
console.error(`Failed to execute template for ${table}`, error.message);
}
}
async function run() {
await backupTable("my-table");
}
try {
run();
} catch (err) {
process.exit(1);
}

Resources