【问题标题】:Sign error for bybit using c# when sending post request发送post请求时使用c#为bybit签名错误
【发布时间】:2021-11-03 19:42:37
【问题描述】:

我正在尝试向 bybit api 发送一个简单的发布请求,但我不断收到 10004 签名错误。 回复如下:

{"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"}

这是我用来发送请求的代码。

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 "";
    }

这是我为请求正文序列化为 JSON 的类。

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;
        }
    }

这是我用来创建签名的代码:

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);
            }
        }

我尝试了各种方法来解决此问题,但似乎无法摆脱它。我尝试了多个端点,但仍然遇到相同的错误。

【问题讨论】:

    标签: c# rest post cryptocurrency bybit


    【解决方案1】:

    2022/01/17 这仍然有效。

    嘿@Vexatious 我在尝试提交订单时遇到了类似的问题,并且我一直收到 Key denied,权限不足的错误,即使我知道我正确设置了我的密钥。

    也许 ByBit 改变了什么?库过时了?谁知道呢。

    我注意到的一件主要事情是,他们要求您在将签名附加到请求正文之前按字母顺序排列参数。

    编辑:更新是因为我认识到 GET 请求同样令人困惑。向下滚动以查看示例 POST 请求。


    GETPOST 请求的处理方式不同

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

    签名

    For Both:您必须在生成签名之前按字母顺序排列参数。

    1. 在单个对象上获取您的查询参数 [包括“api_key”,不包括 API 密钥]。
    2. 按字母顺序对查询参数进行排序。
    3. 遍历对象排序键,构建一个 QueryString,如下例所示
    4. 使用带有十六进制摘要的 hmac sha256 创建标志(查看底部的 ./sign.ts)

    例如:“aParam=foo&bParam=bar”,

    这是您处理的 sign 参数。


    GET REQUESTS:将符号参数附加到 QueryString 的末尾 和发送者,可能需要使用标题

    // 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:要求对象作为请求数据(仍然以上述QueryString 的形式)而不是完全构建的Http 发送类似于 GET 请求的字符串。将 sign 参数添加到您生成签名的 QueryString 的末尾,将其分配给您的请求处理程序数据参数并开火!

    我确实想出了一个最小的工作版本,它成功地在他们的测试网上发布了一个 Spot Order,这是一个开玩笑的测试。

    ./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');
    }
    

    希望这会有所帮助!

    【讨论】:

      【解决方案2】:

      我刚刚为 ByBit api 解决了这个问题,但使用的是 Javascript 而不是 C#。不过我确实知道 C#,所以希望这对你有用。

      使用 POST 的 ByBit API 端点需要与 GET 请求中相同的数据 HMAC 加密。您可以:

      • 对序列化为 JSON 的对象进行签名,
      • 然后将 [object].sign = "xxxx" 添加到 POST 之前的对象中。

      这对你的 C# 类来说是个棘手的问题。您拥有带有 sign 属性的 CancelOrderContent。这将使用空白值序列化键“符号”。但是,ByBit API 不会接受,因为签名的数据会有所不同。

      要么你必须

      • 序列化没有“sign”键的对象 (CancelOrderContentNosignkey),
      • 将属性从 CancelOrderContentNosignkey 复制到新的 CancelOrderContent
      • 将签名哈希字符串添加到 CancelOrderContent object.sign 属性中
      • 然后使用签名键/值对发布序列化对象,

      或者...

      • 像现在一样序列化对象,
      • 但随后使用序列化字符串删除,"sign":'' 部分。
      • 然后签署该字符串,
      • 然后将符号值添加到对象中,
      • 再次将其序列化为 JSON,然后
      • 将其作为数据发布。

      我相信这会奏效,因为我必须这样做才能让它在 JS 中工作。但是,由于没有类,因此添加符号键/值更容易。

      对此的一种变体是将 CancelOrderContent 设为动态类型,在序列化/签名之前您不会添加“签名”键/值。

      注意,当您手动将对象序列化为 JSON 时(不要使用 paramString),理论上序列化器应该与 RestRequest.AddJsonBody() 中使用或配置的序列化器相同

      对不起,我没有 C# 代码,但这应该可以。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多