Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sign error for bybit using c# when sending post request

I am trying to send a simple post request to the bybit api and I keep getting the 10004 sign error. Here is the response:

{"ret_code":10004,"ret_msg":"error sign! origin_string[api_key=(my api key)\u0026symbol=BTCUSDT\u0026timestamp=1635967650768]","ext_code":"","ext_info":"","result":null,"time_now":"1635967651.397800"}

This is the code I am using to send the request.

public async static Task<string> cancelAllOrders()
    {
        string ts = await GenerateTimeStamp();
        string paramString = "api_key=" + apiKey + "&symbol=BTCUSDT" + "timestamp=" + ts;
        string sign = CreateSignature(secretKey, paramString);
        CancelOrderContent co = new CancelOrderContent(apiKey, "BTCUSDT", ts, sign);

        var client = new RestClient(ApiUrl + "/v2/private/order/cancelAll");
        var request = new RestRequest();
        request.AddJsonBody(co);
        var response = client.Post(request);
        Trace.WriteLine(response.StatusCode.ToString() + "   " + response);
        return "";
    }

Here is the class I am Serializing to JSON for the body of the request.

public class CancelOrderContent
    {
        public string api_key;
        public string sign;
        public string symbol;
        public string timestamp;

        public CancelOrderContent(string api_key, string symbol, string timestamp,string sign)
        {
            this.api_key = api_key;
            this.symbol = symbol;
            this.timestamp = timestamp;
            this.sign = sign;
        }
    }

Here is the code I am using to create signatures:

public static string CreateSignature(string secret, string message)
        {
            var signatureBytes = Hmacsha256(Encoding.UTF8.GetBytes(secret), Encoding.UTF8.GetBytes(message));

            return ByteArrayToString(signatureBytes);
        }

        private static byte[] Hmacsha256(byte[] keyByte, byte[] messageBytes)
        {
            using (var hash = new HMACSHA256(keyByte))
            {
                return hash.ComputeHash(messageBytes);
            }
        }

I've tried al kinds of methods to fix this but I can't seem to get rid of it.I've tried mutliple endpoints and I still get the same error.

like image 461
Vexatious Avatar asked Sep 05 '25 03:09

Vexatious


1 Answers

2022/01/17 this still works.

Hey @Vexatious I ran into a similar issue with the bybit-api trying to submit orders and I kept receiving a Key denied, insufficient permissions error even though I knew I was setting up my keys properly.

Maybe ByBit changed something? Lib outdated? Who Knows.

One major thing that I noticed is that they require you to Order the params alphabetically before appending the signature to the body of the request.

Edit: Updated because I recognized GET Requests can be equally as confusing. Scroll down to view the example POST Request.


GET and POST requests are handled differently

  1. axios(https://api-testnet.bybit.com/GET?aParam=foo&bParam=bar&sign=sign)
  2. axios(https://api-testnet.bybit.com/POST, {data: queryString})

SIGNATURE

For Both: You must alphabetically arrange the params before generating the signature.

  1. get your query param's on a single object [Including 'api_key', Excluding the API Secret].
  2. Sort the query param's alphabetically.
  3. Iterate over the objects sorted keys, building a QueryString like in the ex below
  4. use hmac sha256 with a hex digest to create the sign (look at ./sign.ts at the bottom)

ex: "aParam=foo&bParam=bar",

That's your sign parameter dealt with.


GET REQUESTS: Append the sign parameter to the end of the QueryString and send'er, might need to use a header

// Might still need {'Content-Type': 'application/x-www-form-urlencoded'}
// header depending on what request lib you're using.

const url = "https://api-testnet.bybit.com/GET?aParam=foo&bParam=bar&sign=" + sign

POST REQUESTS: it is required that the object is sent as request data (still, in the form of a QueryString like above) and not a fully built out Http string similar to the GET Request. Add the sign parameter to the end of the QueryString you generated the signature with, assign that to your request handlers data parameter and fire away!

I did come up with a minimal working version that successfully posted a Spot Order on their testnet, here is the jest test.

./bybit.test.ts

test("WORKING BYBIT TRADE.", async () => {
        const serverTime:number = (await axios.get(`https://api-testnet.bybit.com/spot/v1/time`)).data

        // These need to be within 1000ms of eachother (I'm pree sure, their formula is kinda confusing)
        console.log(`Their Timestamp`, serverTime)
        console.log(`Our Timestamp`, Date.now())

        const queryParams = {
            // Alphabetically ordered
            // (Sign generation function should deal with unordered params using .sort())
            'api_key': bybitApiKey,
            qty:10,
            recvWindow: 10000,
            side:"BUY",
            symbol:"ETHUSDT",
            timestamp: Date.now(),
            type:"MARKET",
        }

        const queryString = querystring.stringify(queryParams)
        const sign = "&sign=" + getSignature(queryParams, bybitSecret)
        const fullQuery = queryString + sign

        console.log(`FullQuery example`, fullQuery) 
        // api_key=...&qty=10&recvWindow=10000&side=BUY&symbol=ETHUSDT&timestamp=1638371037593&type=MARKET&sign=...

        let result = await axios(`https://api-testnet.bybit.com/spot/v1/order`, {
            withCredentials: true,
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
            },
            method: "POST",
            data: fullQuery,
        })

        console.log(`Post Status`, result.status)
        console.log(`Post Body`, result.data)
    })

    /**
        Post Status 200
        
        Post Body {
          ret_code: 0,
          ret_msg: '',
          ext_code: null,
          ext_info: null,
          result: {
            accountId: '...',
            symbol: 'ETHUSDT',
            symbolName: 'ETHUSDT',
            orderLinkId: '...',
            orderId: '...',
            transactTime: '...',
            price: '0',
            origQty: '10',
            executedQty: '0',
            status: 'FILLED',
            timeInForce: 'GTC',
            type: 'MARKET',
            side: 'BUY'
          }
    */
    }

./sign.ts

import crypto from 'crypto'

export function getSignature(parameters: any, secret: string) {
    var orderedParams = "";

    Object.keys(parameters).sort().forEach(function(key) {
        orderedParams += key + "=" + parameters[key] + "&";
    });
    orderedParams = orderedParams.substring(0, orderedParams.length - 1);

    return crypto.createHmac('sha256', secret).update(orderedParams).digest('hex');
}

Hopefully this helps!

like image 94
Kwuasimoto Avatar answered Sep 08 '25 10:09

Kwuasimoto