Module: Runar::SDK

Extended by:
State
Defined in:
lib/runar/sdk/state.rb,
lib/runar/sdk/types.rb,
lib/runar/sdk/signer.rb,
lib/runar/sdk/calling.rb,
lib/runar/sdk/codegen.rb,
lib/runar/sdk/contract.rb,
lib/runar/sdk/oppushtx.rb,
lib/runar/sdk/provider.rb,
lib/runar/sdk/deployment.rb,
lib/runar/sdk/local_signer.rb,
lib/runar/sdk/rpc_provider.rb,
lib/runar/sdk/anf_interpreter.rb

Defined Under Namespace

Modules: ANFInterpreter, Codegen, State Classes: ABI, ABIMethod, ABIParam, CallOptions, ConstructorSlot, DeployOptions, ExternalSigner, LocalSigner, MockProvider, MockSigner, OutputSpec, PreparedCall, Provider, RPCProvider, RunarArtifact, RunarContract, Signer, StateField, TerminalOutput, TransactionData, TxInput, TxOutput, Utxo

Constant Summary collapse

Transaction =

Backward-compatibility alias — existing code using Transaction continues to work.

TransactionData
SIGHASH_ALL_FORKID =
0x41

Constants included from State

State::TYPE_WIDTHS

Class Method Summary collapse

Methods included from State

deserialize_state, encode_push_data, encode_script_int, extract_state_from_script, find_last_op_return, serialize_state

Class Method Details

.address_to_pubkey_hash(address) ⇒ String

Extract the 20-byte pubkey hash from a Base58Check P2PKH address.

Returns:

  • (String)

    lowercase hex

Raises:

  • (ArgumentError)


214
215
216
217
218
219
# File 'lib/runar/sdk/deployment.rb', line 214

def address_to_pubkey_hash(address)
  decoded = base58_decode(address)
  raise ArgumentError, "invalid address length: #{decoded.bytesize}" unless decoded.bytesize == 25

  decoded[1, 20].unpack1('H*')
end

.base58_decode(encoded) ⇒ Object



197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/runar/sdk/deployment.rb', line 197

def base58_decode(encoded)
  num = 0
  encoded.each_char { |c| num = (num * 58) + BASE58_ALPHABET.index(c) }

  result = []
  while num > 0
    num, rem = num.divmod(256)
    result.unshift(rem)
  end

  # Leading '1' characters map to zero bytes.
  pad = encoded.chars.take_while { |c| c == '1' }.length
  ([0] * pad + result).pack('C*')
end

.build_call_transaction(current_utxo, unlocking_script, new_locking_script, new_satoshis, change_address, change_script = '', additional_utxos = nil, fee_rate: 100, options: nil) ⇒ Array(String, Integer, Integer)

Build a raw (partially-signed) transaction that spends a contract UTXO.

Input 0 is the contract’s current UTXO, carrying the provided unlocking_script (which may be empty when building before signing). Additional contract inputs from options[:additional_contract_inputs] are appended next, each with their own unlocking script. P2PKH funding inputs from additional_utxos follow with empty scriptSigs.

Output 0 is the new contract continuation output. When options[:contract_outputs] is present it replaces the single continuation output with multiple outputs (token-split pattern). A P2PKH change output is appended when the remaining balance is positive.

Parameters:

  • current_utxo (Utxo)

    the contract UTXO being spent

  • unlocking_script (String)

    hex-encoded scriptSig for input 0 (may be empty)

  • new_locking_script (String)

    hex-encoded locking script for the continuation output

  • new_satoshis (Integer)

    satoshis for the continuation output (0 = carry forward)

  • change_address (String)

    Base58Check address or 40-char hex pubkey hash for change

  • change_script (String) (defaults to: '')

    pre-built change script hex (overrides change_address)

  • additional_utxos (Array<Utxo>, nil) (defaults to: nil)

    P2PKH UTXOs used to fund fees

  • fee_rate (Integer) (defaults to: 100)

    satoshis per kilobyte (minimum 1)

  • options (Hash, nil) (defaults to: nil)

    optional extensions:

    • :contract_outputs [Array<Hash>] each {script:, satoshis:} for multi-output

    • :additional_contract_inputs [Array<Hash>] each {utxo:, unlocking_script:}

Returns:

  • (Array(String, Integer, Integer))
    tx_hex, input_count, change_amount


37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/runar/sdk/calling.rb', line 37

def build_call_transaction(
  current_utxo,
  unlocking_script,
  new_locking_script,
  new_satoshis,
  change_address,
  change_script = '',
  additional_utxos = nil,
  fee_rate: 100,
  options: nil
)
  opts                   = options || {}
  extra_contract_inputs  = opts[:additional_contract_inputs] || []
  additional             = additional_utxos || []

  all_utxos = [current_utxo] + extra_contract_inputs.map { |ci| ci[:utxo] } + additional
  total_input = all_utxos.sum(&:satoshis)

  # Resolve contract outputs — multi-output takes priority over single.
  resolved_outputs =
    if opts[:contract_outputs] && !opts[:contract_outputs].empty?
      opts[:contract_outputs]
    elsif !new_locking_script.empty?
      sats = new_satoshis.positive? ? new_satoshis : current_utxo.satoshis
      [{ script: new_locking_script, satoshis: sats }]
    else
      []
    end

  contract_output_sats = resolved_outputs.sum { |co| co[:satoshis] }

  # Estimate transaction size for fee calculation.
  #
  # Input 0: prevTxid(32) + prevIndex(4) + scriptSig varint + scriptSig + sequence(4)
  unlock_byte_len = unlocking_script.length / 2
  input0_size = 32 + 4 + varint_byte_size(unlock_byte_len) + unlock_byte_len + 4

  extra_inputs_size = extra_contract_inputs.sum do |ci|
    ci_len = ci[:unlocking_script].length / 2
    32 + 4 + varint_byte_size(ci_len) + ci_len + 4
  end

  # P2PKH funding inputs are unsigned at construction time (~148 bytes each).
  additional_inputs_size = additional.length * P2PKH_INPUT_SIZE

  inputs_size = input0_size + extra_inputs_size + additional_inputs_size

  outputs_size = resolved_outputs.sum do |co|
    s_len = co[:script].length / 2
    8 + varint_byte_size(s_len) + s_len
  end

  # Include change output in size estimate only when a recipient is specified.
  has_change_recipient = !change_address.to_s.empty? || !change_script.to_s.empty?
  outputs_size += P2PKH_OUTPUT_SIZE if has_change_recipient

  estimated_size = TX_OVERHEAD + inputs_size + outputs_size
  rate           = [1, fee_rate].max
  fee            = (estimated_size * rate + 999) / 1000
  change         = total_input - contract_output_sats - fee

  # Build raw transaction bytes as a hex string.
  tx = +''

  # Version
  tx << to_le32(1)

  # Input count
  tx << encode_varint(all_utxos.length)

  # Input 0: contract UTXO with (possibly empty) unlocking script.
  tx << reverse_hex(current_utxo.txid)
  tx << to_le32(current_utxo.output_index)
  tx << encode_varint(unlock_byte_len)
  tx << unlocking_script
  tx << 'ffffffff'

  # Additional contract inputs with their own unlocking scripts.
  extra_contract_inputs.each do |ci|
    ci_utxo   = ci[:utxo]
    ci_script = ci[:unlocking_script]
    tx << reverse_hex(ci_utxo.txid)
    tx << to_le32(ci_utxo.output_index)
    tx << encode_varint(ci_script.length / 2)
    tx << ci_script
    tx << 'ffffffff'
  end

  # P2PKH funding inputs — unsigned, empty scriptSig.
  additional.each do |utxo|
    tx << reverse_hex(utxo.txid)
    tx << to_le32(utxo.output_index)
    tx << '00'
    tx << 'ffffffff'
  end

  # Output count
  has_change = change.positive? && has_change_recipient
  output_count = resolved_outputs.length + (has_change ? 1 : 0)
  tx << encode_varint(output_count)

  # Contract continuation outputs.
  resolved_outputs.each do |co|
    s = co[:script]
    tx << to_le64(co[:satoshis])
    tx << encode_varint(s.length / 2)
    tx << s
  end

  # Change output.
  if has_change
    actual_change_script = change_script.to_s.empty? ? build_p2pkh_script(change_address) : change_script
    tx << to_le64(change)
    tx << encode_varint(actual_change_script.length / 2)
    tx << actual_change_script
  end

  # Locktime
  tx << to_le32(0)

  [tx, all_utxos.length, has_change ? change : 0]
end

.build_deploy_transaction(locking_script, utxos, satoshis, change_address, change_script = '', fee_rate: 100) ⇒ Array(String, Integer)

Build an unsigned deployment transaction.

Parameters:

  • locking_script (String)

    hex-encoded contract locking script

  • utxos (Array<Utxo>)

    funding UTXOs (must not be empty)

  • satoshis (Integer)

    value to lock in the contract output

  • change_address (String)

    Base58Check address or 40-char hex pubkey hash for change

  • change_script (String) (defaults to: '')

    pre-built change script hex (overrides change_address when present)

  • fee_rate (Integer) (defaults to: 100)

    satoshis per kilobyte (minimum 1)

Returns:

  • (Array(String, Integer))
    tx_hex, input_count

Raises:

  • (ArgumentError)


26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/runar/sdk/deployment.rb', line 26

def build_deploy_transaction(locking_script, utxos, satoshis, change_address,
                             change_script = '', fee_rate: 100)
  raise ArgumentError, 'build_deploy_transaction: no UTXOs provided' if utxos.empty?

  total_input = utxos.sum(&:satoshis)
  fee         = estimate_deploy_fee(utxos.length, locking_script.length / 2, fee_rate)
  change      = total_input - satoshis - fee

  if change < 0
    raise ArgumentError,
          "build_deploy_transaction: insufficient funds. " \
          "Need #{satoshis + fee} sats, have #{total_input}"
  end

  tx = +''

  # Version (4 bytes, little-endian)
  tx << to_le32(1)

  # Input count (varint)
  tx << encode_varint(utxos.length)

  # Inputs (unsigned — empty scriptSig)
  utxos.each do |utxo|
    tx << reverse_hex(utxo.txid)
    tx << to_le32(utxo.output_index)
    tx << '00'         # empty scriptSig (varint 0)
    tx << 'ffffffff'   # sequence
  end

  # Output count (varint)
  has_change   = change > 0
  output_count = has_change ? 2 : 1
  tx << encode_varint(output_count)

  # Output 0: contract locking script
  tx << to_le64(satoshis)
  tx << encode_varint(locking_script.length / 2)
  tx << locking_script

  # Output 1: change (omitted when change is zero)
  if has_change
    actual_change_script = change_script.empty? ? build_p2pkh_script(change_address) : change_script
    tx << to_le64(change)
    tx << encode_varint(actual_change_script.length / 2)
    tx << actual_change_script
  end

  # Locktime (4 bytes, little-endian)
  tx << to_le32(0)

  [tx, utxos.length]
end

.build_p2pkh_script(address) ⇒ String

Build a standard P2PKH locking script.

Accepts either a 40-character hex pubkey hash or a Base58Check P2PKH address. Returns the script as hex: 76a914{20-byte-hash}88ac.

Parameters:

  • address (String)

    40-char hex pubkey hash or Base58Check address

Returns:

  • (String)

    hex-encoded P2PKH script



131
132
133
134
135
136
137
138
139
140
# File 'lib/runar/sdk/deployment.rb', line 131

def build_p2pkh_script(address)
  pub_key_hash =
    if address.length == 40 && hex_string?(address)
      address
    else
      address_to_pubkey_hash(address)
    end

  "76a914#{pub_key_hash}88ac"
end

.compute_op_push_tx(tx_hex, input_index, subscript_hex, satoshis, code_separator_index = -1)) ⇒ Array(String, String)

Compute the OP_PUSH_TX DER signature and BIP-143 preimage for a transaction input.

Convenience wrapper combining compute_preimage and sign_preimage_k1. When code_separator_index is provided and non-negative, only the portion of the subscript after the OP_CODESEPARATOR byte is used as the scriptCode.

Parameters:

  • tx_hex (String)

    raw transaction hex

  • input_index (Integer)

    index of the input being signed

  • subscript_hex (String)

    hex-encoded locking script

  • satoshis (Integer)

    value of the UTXO being spent

  • code_separator_index (Integer, nil) (defaults to: -1))

    byte offset of OP_CODESEPARATOR, or -1/nil

Returns:

  • (Array(String, String))
    sig_hex, preimage_hex


82
83
84
85
86
87
# File 'lib/runar/sdk/oppushtx.rb', line 82

def compute_op_push_tx(tx_hex, input_index, subscript_hex, satoshis, code_separator_index = -1)
  effective_subscript = get_subscript(subscript_hex, code_separator_index)
  preimage_hex = compute_preimage(tx_hex, input_index, effective_subscript, satoshis)
  sig_hex = sign_preimage_k1(preimage_hex)
  [sig_hex, preimage_hex]
end

.compute_preimage(tx_hex, input_index, subscript_hex, satoshis, sighash_type = SIGHASH_ALL_FORKID) ⇒ String

Compute the BIP-143 sighash preimage for a transaction input.

Parses the raw transaction hex, then assembles the ten-field BIP-143 preimage for the given input. Returns the preimage as a lowercase hex string (208 bytes = 416 hex chars for typical transactions).

Parameters:

  • tx_hex (String)

    raw transaction hex

  • input_index (Integer)

    index of the input being signed

  • subscript_hex (String)

    hex-encoded subscript (locking script or the portion after OP_CODESEPARATOR)

  • satoshis (Integer)

    value of the UTXO being spent

  • sighash_type (Integer) (defaults to: SIGHASH_ALL_FORKID)

    sighash type (default: SIGHASH_ALL|FORKID = 0x41)

Returns:

  • (String)

    BIP-143 preimage as hex



41
42
43
44
45
46
# File 'lib/runar/sdk/oppushtx.rb', line 41

def compute_preimage(tx_hex, input_index, subscript_hex, satoshis, sighash_type = SIGHASH_ALL_FORKID)
  tx = parse_raw_tx([tx_hex].pack('H*'))
  subscript = [subscript_hex].pack('H*')

  bip143_preimage(tx, input_index, subscript, satoshis, sighash_type).unpack1('H*')
end

.double_sha256(hex) ⇒ String

Double-SHA256 hash (SHA256(SHA256(data))).

Parameters:

  • hex (String)

    hex-encoded input data

Returns:

  • (String)

    32-byte digest as lowercase hex



114
115
116
117
# File 'lib/runar/sdk/oppushtx.rb', line 114

def double_sha256(hex)
  bytes = [hex].pack('H*')
  Digest::SHA256.hexdigest(Digest::SHA256.digest(bytes))
end

.encode_varint(n) ⇒ String

Encode an integer as a Bitcoin-style varint (hex string).

Parameters:

  • n (Integer)

Returns:

  • (String)

    hex



151
152
153
154
155
156
157
158
159
160
161
# File 'lib/runar/sdk/deployment.rb', line 151

def encode_varint(n)
  if n < 0xFD
    format('%02x', n)
  elsif n <= 0xFFFF
    'fd' + [n].pack('v').unpack1('H*')
  elsif n <= 0xFFFFFFFF
    'fe' + [n].pack('V').unpack1('H*')
  else
    'ff' + [n].pack('Q<').unpack1('H*')
  end
end

.estimate_deploy_fee(num_inputs, locking_script_byte_len, fee_rate = 100) ⇒ Integer

Estimate the fee for a deploy transaction.

Accounts for: overhead, P2PKH inputs, the contract output (variable-size script), and one P2PKH change output.

Parameters:

  • num_inputs (Integer)

    number of inputs

  • locking_script_byte_len (Integer)

    byte length of the locking script

  • fee_rate (Integer) (defaults to: 100)

    satoshis per kilobyte (minimum 1)

Returns:

  • (Integer)

    estimated fee in satoshis



115
116
117
118
119
120
121
122
# File 'lib/runar/sdk/deployment.rb', line 115

def estimate_deploy_fee(num_inputs, locking_script_byte_len, fee_rate = 100)
  rate               = [1, fee_rate].max
  inputs_size        = num_inputs * P2PKH_INPUT_SIZE
  contract_out_size  = 8 + varint_byte_size(locking_script_byte_len) + locking_script_byte_len
  change_output_size = P2PKH_OUTPUT_SIZE
  tx_size            = TX_OVERHEAD + inputs_size + contract_out_size + change_output_size
  (tx_size * rate + 999) / 1000
end

.get_subscript(script_hex, code_separator_index) ⇒ String

Extract the subscript for BIP-143 sighash computation.

When code_separator_index is nil or -1, the full script is returned. Otherwise everything after the OP_CODESEPARATOR at the given byte offset is returned (i.e. bytes from code_separator_index 1+ onward).

Parameters:

  • script_hex (String)

    hex-encoded locking script

  • code_separator_index (Integer, nil)

    byte offset of the OP_CODESEPARATOR, or -1 / nil to use the full script

Returns:

  • (String)

    hex-encoded subscript



99
100
101
102
103
104
105
106
107
108
# File 'lib/runar/sdk/oppushtx.rb', line 99

def get_subscript(script_hex, code_separator_index)
  return script_hex if code_separator_index.nil? || code_separator_index.negative?

  # code_separator_index is the byte offset of the OP_CODESEPARATOR opcode
  # itself; the subscript begins at the next byte.
  trim_pos = (code_separator_index + 1) * 2
  return script_hex if trim_pos > script_hex.length

  script_hex[trim_pos..]
end

.hex_string?(str) ⇒ Boolean

Return true if str is a valid hexadecimal string.

Returns:



190
191
192
# File 'lib/runar/sdk/deployment.rb', line 190

def hex_string?(str)
  str.match?(/\A[0-9a-fA-F]+\z/)
end

.insert_unlocking_script(tx_hex, input_index, unlock_script) ⇒ String

Replace the scriptSig of a specific input within a raw transaction.

Parses the hex-encoded transaction, locates the scriptSig at input_index, substitutes it with unlock_script, and returns the modified transaction as hex. All other fields remain unchanged.

Parameters:

  • tx_hex (String)

    hex-encoded raw transaction

  • input_index (Integer)

    zero-based index of the input to update

  • unlock_script (String)

    hex-encoded replacement scriptSig

Returns:

  • (String)

    modified transaction hex

Raises:

  • (ArgumentError)

    when input_index is out of range



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/runar/sdk/calling.rb', line 171

def insert_unlocking_script(tx_hex, input_index, unlock_script)
  pos = 0

  # Skip version (4 bytes = 8 hex chars).
  pos += 8

  # Read input count varint.
  input_count, ic_hex_len = read_varint_hex(tx_hex, pos)
  pos += ic_hex_len

  if input_index >= input_count
    raise ArgumentError,
          "insert_unlocking_script: input index #{input_index} out of range " \
          "(#{input_count} inputs)"
  end

  input_count.times do |i|
    # prevTxid (32 bytes = 64 hex chars) + prevOutputIndex (4 bytes = 8 hex chars)
    pos += 64 + 8

    # Read scriptSig length varint.
    script_len, sl_hex_len = read_varint_hex(tx_hex, pos)

    if i == input_index
      new_byte_len = unlock_script.length / 2
      new_varint   = encode_varint(new_byte_len)
      before       = tx_hex[0, pos]
      after        = tx_hex[pos + sl_hex_len + script_len * 2..]
      return "#{before}#{new_varint}#{unlock_script}#{after}"
    end

    # Skip scriptSig bytes + sequence (4 bytes = 8 hex chars).
    pos += sl_hex_len + script_len * 2 + 8
  end

  raise ArgumentError,
        "insert_unlocking_script: input index #{input_index} out of range"
end

.read_varint_hex(hex_str, pos) ⇒ Array(Integer, Integer)

Read a Bitcoin-style varint from a hex string at position pos.

Parameters:

  • hex_str (String)

    hex-encoded data

  • pos (Integer)

    character offset (not byte offset)

Returns:

  • (Array(Integer, Integer))
    value, hex_chars_consumed


219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/runar/sdk/calling.rb', line 219

def read_varint_hex(hex_str, pos)
  first = hex_str[pos, 2].to_i(16)

  case first
  when 0...0xFD
    [first, 2]
  when 0xFD
    lo = hex_str[pos + 2, 2].to_i(16)
    hi = hex_str[pos + 4, 2].to_i(16)
    [lo | (hi << 8), 6]
  when 0xFE
    value = [hex_str[pos + 2, 8]].pack('H*').unpack1('V')
    [value, 10]
  else # 0xFF
    value = [hex_str[pos + 2, 16]].pack('H*').unpack1('Q<')
    [value, 18]
  end
end

.reverse_hex(hex_str) ⇒ Object

Reverse the byte order of a hex string (converts txid to wire format).



185
186
187
# File 'lib/runar/sdk/deployment.rb', line 185

def reverse_hex(hex_str)
  [hex_str].pack('H*').reverse.unpack1('H*')
end

.select_utxos(utxos, target_satoshis, locking_script_byte_len, fee_rate: 100) ⇒ Array<Utxo>

Select the minimum set of UTXOs needed to fund a deployment using a largest-first strategy. Returns the selected subset; raises ArgumentError when funds are insufficient.

Parameters:

  • utxos (Array<Utxo>)

    available UTXOs

  • target_satoshis (Integer)

    amount to place in the contract output

  • locking_script_byte_len (Integer)

    byte length of the locking script

  • fee_rate (Integer) (defaults to: 100)

    satoshis per kilobyte

Returns:

Raises:

  • (ArgumentError)


89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/runar/sdk/deployment.rb', line 89

def select_utxos(utxos, target_satoshis, locking_script_byte_len, fee_rate: 100)
  sorted   = utxos.sort_by { |u| -u.satoshis }
  selected = []
  total    = 0

  sorted.each do |utxo|
    selected << utxo
    total += utxo.satoshis
    fee = estimate_deploy_fee(selected.length, locking_script_byte_len, fee_rate)
    return selected if total >= target_satoshis + fee
  end

  raise ArgumentError,
        "select_utxos: insufficient funds. " \
        "Need #{target_satoshis} sats plus fee, have #{total}"
end

.sign_preimage_k1(preimage_hex) ⇒ String

Compute the OP_PUSH_TX DER signature for a preimage.

Hashes the preimage with double-SHA256, then signs with private key d=1 and nonce k=1. Returns the DER-encoded signature with the sighash byte appended, hex-encoded.

Parameters:

  • preimage_hex (String)

    BIP-143 preimage as hex (from compute_preimage)

Returns:

  • (String)

    DER signature + sighash byte, hex-encoded



56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/runar/sdk/oppushtx.rb', line 56

def sign_preimage_k1(preimage_hex)
  hash = double_sha256(preimage_hex)
  hash_bytes = [hash].pack('H*')

  r, s = ecdsa_sign_k1(hash_bytes)

  # Enforce low-S normalisation.
  half_n = ECPrimitives::SECP256K1_N >> 1
  s = ECPrimitives::SECP256K1_N - s if s > half_n

  der_encode(r, s).unpack1('H*') + format('%02x', SIGHASH_ALL_FORKID)
end

.to_le32(n) ⇒ Object

Encode a 32-bit unsigned integer as 4 little-endian bytes (hex string).



175
176
177
# File 'lib/runar/sdk/deployment.rb', line 175

def to_le32(n)
  [n].pack('V').unpack1('H*')
end

.to_le64(n) ⇒ Object

Encode a 64-bit unsigned integer as 8 little-endian bytes (hex string).



180
181
182
# File 'lib/runar/sdk/deployment.rb', line 180

def to_le64(n)
  [n].pack('Q<').unpack1('H*')
end

.varint_byte_size(n) ⇒ Integer

Return the byte size of a varint encoding for n.

Parameters:

  • n (Integer)

Returns:

  • (Integer)


166
167
168
169
170
171
172
# File 'lib/runar/sdk/deployment.rb', line 166

def varint_byte_size(n)
  return 1 if n < 0xFD
  return 3 if n <= 0xFFFF
  return 5 if n <= 0xFFFFFFFF

  9
end