Search code examples
rubyapisignaturebinance

Ruby - Binance API: Signature is not signing correctly


I am attempting to connect to the Binance API.

https://github.com/binance-exchange/binance-official-api-docs/blob/master/rest-api.md

I am getting denied access. I suspect the issue might not be the 'signature'

If any one has experience with signing rest requests to Binance, a point in the right direction of where I am going wrong would be appreciated.

error {"code":-1022,"msg":"Signature for this request is not valid."}

  def get_time
    endpoint = "/api/v1/time"
    uri = @url + endpoint
    uri = URI(uri)
    response = Net::HTTP.get(uri)
    data = JSON.parse(response)
    data["serverTime"]
  end

  def get_amount
    query = URI.encode_www_form("timestamp"=> get_time)
    signature = sig(query)
    query = URI.encode_www_form("timestamp"=> get_time, "signature" => signature)
    endpoint = "/api/v3/account"
    uri = @url + endpoint + '?' + query

    uri = URI(uri)
    req = Net::HTTP::Get.new(uri)
    req['X-MBX-APIKEY'] = @api_key

    res = Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme == 'https') do |http|
      http.request(req)
    end

    puts "Sig: #{signature}"
    puts "www: #{uri}" 
    res.body
  end

  def sig(query)
    digest = OpenSSL::Digest::SHA256.new
    OpenSSL::HMAC.hexdigest(digest, @api_secret, query)
  end

Solution

  • Seems like you're calling get_time twice so that may be your problem as the signed request documentation indicates signature should contain all of your query params and request body concatenated. When you call get_time the 2nd time, the timestamp has changed AFTER you created the signature with the 1st timestamp.

    Try this instead

      def get_amount
        timestamp = get_time
        query = URI.encode_www_form("timestamp"=> timestamp)
        signature = sig(query)
        query = URI.encode_www_form("timestamp"=> timestamp, "signature" => signature)
        endpoint = "/api/v3/account"
        uri = @url + endpoint + '?' + query
    
        uri = URI(uri)
        req = Net::HTTP::Get.new(uri)
        req['X-MBX-APIKEY'] = @api_key
    
        res = Net::HTTP.start(uri.hostname, uri.port, :use_ssl => uri.scheme == 'https') do |http|
          http.request(req)
        end
    
        puts "Sig: #{signature}"
        puts "www: #{uri}" 
        res.body
      end
    

    On a side note, your get_time method could be 1 line:

    def get_time
      (Time.now.to_f * 1000).to_i.to_s
    end