module MixinBot::API::Multisig

Constants

RAW_TRANSACTION_ARGUMENTS

kwargs: {

senders: [ uuid ],
receivers: [ uuid ],
threshold: integer,
asset_id: uuid,
amount: string / float,
memo: string,

}

Public Instance Methods

build_inputs(inputs) click to toggle source
# File lib/mixin_bot/api/multisig.rb, line 267
def build_inputs(inputs)
  res = []
  prototype = {
    'Hash' => nil,
    'Index' => nil,
    'Genesis' => nil,
    'Deposit' => nil,
    'Mint' => nil
  }
  inputs.each do |input|
    struc = prototype.dup
    struc['Hash'] = str_to_bin input['hash']
    struc['Index'] = input['index']
    res << struc
  end

  res
end
build_outputs(outputs) click to toggle source
# File lib/mixin_bot/api/multisig.rb, line 286
def build_outputs(outputs)
  res = []
  prototype = {
    'Type' => 0,
    'Amount' => nil,
    'Keys' => nil,
    'Script' => nil,
    'Mask' => nil
  }
  outputs.each do |output|
    struc = prototype.dup
    struc['Type'] = str_to_bin output['type']
    struc['Amount'] = str_to_bin output['amount']
    struc['Keys'] = output['keys'].map(&->(key) { str_to_bin(key) })
    struc['Script'] = str_to_bin output['script']
    struc['Mask'] = str_to_bin output['mask']
    res << struc
  end

  res
end
build_raw_transaction(**kwargs) click to toggle source
# File lib/mixin_bot/api/multisig.rb, line 180
def build_raw_transaction(**kwargs)
  raise ArgumentError, "#{RAW_TRANSACTION_ARGUMENTS.join(', ')} are needed for build raw transaction" unless RAW_TRANSACTION_ARGUMENTS.all? { |param| kwargs.keys.include? param }

  senders        = kwargs[:senders]
  receivers      = kwargs[:receivers]
  amount         = kwargs[:amount]
  threshold      = kwargs[:threshold]
  asset_id       = kwargs[:asset_id]
  utxos          = kwargs[:utxos]
  memo           = kwargs[:memo]
  access_token   = kwargs[:access_token]

  raise 'access_token required!' if access_token.nil? && !senders.include?(client_id)

  # default to use all(first 100) unspent utxo
  utxos ||= multisigs(
    members: senders,
    threshold: threshold,
    state: 'unspent',
    access_token: access_token
  )['data'].filter(
    &lambda { |utxo|
      utxo['asset_id'] == kwargs[:asset_id]
    }
  )

  amount = amount.to_f.round(8)
  input_amount = utxos.map(
    &lambda { |utxo|
      utxo['amount'].to_f
    }
  ).sum.round(8)

  if input_amount < amount
    raise format(
      'not enough amount! %<input_amount>s < %<amount>s',
      input_amount: input_amount,
      amount: amount
    )
  end

  inputs = utxos.map(
    &lambda { |utx|
      {
        'hash' => utx['transaction_hash'],
        'index' => utx['output_index']
      }
    }
  )

  outputs = []
  output0 = create_output(receivers: receivers, index: 0)['data']
  outputs << {
    'amount': format('%<amount>.8f', amount: amount),
    'script': build_threshold_script(receivers.length),
    'mask': output0['mask'],
    'keys': output0['keys']
  }

  if input_amount > amount
    output1 = create_output(receivers: senders, index: 1)['data']
    outputs << {
      'amount': format('%<amount>.8f', amount: input_amount - amount),
      'script': build_threshold_script(threshold.to_i),
      'mask': output1['mask'],
      'keys': output1['keys']
    }
  end

  extra = Digest.hexencode memo.to_s.slice(0, 140)
  tx = {
    version: 1,
    asset: SHA3::Digest::SHA256.hexdigest(asset_id),
    inputs: inputs,
    outputs: outputs,
    extra: extra
  }

  tx.to_json
end
build_threshold_script(threshold) click to toggle source
# File lib/mixin_bot/api/multisig.rb, line 162
def build_threshold_script(threshold)
  s = threshold.to_s(16)
  s = s.length == 1 ? "0#{s}" : s
  raise 'NVALID THRESHOLD' if s.length > 2

  "fffe#{s}"
end
cancel_multisig_request(request_id, pin) click to toggle source
# File lib/mixin_bot/api/multisig.rb, line 112
def cancel_multisig_request(request_id, pin)
  path = format('/multisigs/requests/%<request_id>s/cancel', request_id: request_id)
  payload = {
    pin: encrypt_pin(pin)
  }
  access_token ||= access_token('POST', path, payload.to_json)
  authorization = format('Bearer %<access_token>s', access_token: access_token)
  client.post(path, headers: { 'Authorization': authorization }, json: payload)
end
create_multisig_payment(**kwargs) click to toggle source

pay to the multisig address used for create multisig payment code_id

# File lib/mixin_bot/api/multisig.rb, line 124
def create_multisig_payment(**kwargs)
  path = '/payments'
  payload = {
    asset_id: kwargs[:asset_id],
    amount: kwargs[:amount].to_s,
    trace_id: kwargs[:trace_id] || SecureRandom.uuid,
    memo: kwargs[:memo],
    opponent_multisig: {
      receivers: kwargs[:receivers],
      threshold: kwargs[:threshold]
    }
  }
  access_token = kwargs[:access_token]
  access_token ||= access_token('POST', path, payload.to_json)
  authorization = format('Bearer %<access_token>s', access_token: access_token)
  client.post(path, headers: { 'Authorization': authorization }, json: payload)
end
create_output(receivers:, index:, access_token: nil) click to toggle source
# File lib/mixin_bot/api/multisig.rb, line 54
def create_output(receivers:, index:, access_token: nil)
  path = '/outputs'
  payload = {
    receivers: receivers,
    index: index
  }
  access_token ||= access_token('POST', path, payload.to_json)
  authorization = format('Bearer %<access_token>s', access_token: access_token)
  client.post(path, headers: { 'Authorization': authorization }, json: payload)
end
create_sign_multisig_request(raw, access_token: nil) click to toggle source

transfer from the multisig address create a request for multi sign for now, raw(RAW-TRANSACTION-HEX) can only be generated by Mixin SDK of Golang or Javascript

# File lib/mixin_bot/api/multisig.rb, line 68
def create_sign_multisig_request(raw, access_token: nil)
  path = '/multisigs/requests'
  payload = {
    action: 'sign',
    raw: raw
  }
  access_token ||= access_token('POST', path, payload.to_json)
  authorization = format('Bearer %<access_token>s', access_token: access_token)
  client.post(path, headers: { 'Authorization': authorization }, json: payload)
end
create_unlock_multisig_request(raw, access_token: nil) click to toggle source

transfer from the multisig address create a request for unlock a multi-sign

# File lib/mixin_bot/api/multisig.rb, line 81
def create_unlock_multisig_request(raw, access_token: nil)
  path = '/multisigs/requests'
  payload = {
    action: 'unlock',
    raw: raw
  }
  access_token ||= access_token('POST', path, payload.to_json)
  authorization = format('Bearer %<access_token>s', access_token: access_token)
  client.post(path, headers: { 'Authorization': authorization }, json: payload)
end
multisigs(**kwargs)
Alias for: outputs
outputs(**kwargs) click to toggle source

{“data”:[

{
  "type":"multisig_utxo",
  "user_id":"514ae2ff-c24e-4379-a482-e2c0f798ebb1",
  "utxo_id":"94711ac9-5981-4fe3-8c0e-19622219ea72",
  "asset_id":"965e5c6e-434c-3fa9-b780-c50f43cd955c",
  "transaction_hash":"2e67f3e36ee4b3c13effcc8a9aaafeb8122cad98f72d9ccc04d65a5ada2aa39d",
  "output_index":0,
  "amount":"0.123456",
  "threshold":2,
  "members":[
    "514ae2ff-c24e-4379-a482-e2c0f798ebb1",
    "13ce6c86-307a-5187-98b0-76424cbc0fbf",
    "2b9df368-8e3e-46ce-ac57-e6111e8ff50e",
    "3cb87491-4fa0-4c2f-b387-262b63cbc412"
  ],
  "memo":"难道你是女生",
  "state":"unspent",
  "created_at":"2019-11-03T13:30:43.922655Z",
  "signed_by":"",
  "signed_tx":""
}

]}

# File lib/mixin_bot/api/multisig.rb, line 31
def outputs(**kwargs)
  limit = kwargs[:limit] || 100
  offset = kwargs[:offset] || ''
  state = kwargs[:state] || ''
  members = kwargs[:members] || []
  threshold = kwargs[:threshold] || ''
  access_token = kwargs[:access_token]
  members = SHA3::Digest::SHA256.hexdigest(members&.sort&.join)

  path = format(
    '/multisigs/outputs?limit=%<limit>s&offset=%<offset>s&state=%<state>s&members=%<members>s&threshold=%<threshold>s',
    limit: limit,
    offset: offset,
    state: state,
    members: members,
    threshold: threshold
  )
  access_token ||= access_token('GET', path, '')
  authorization = format('Bearer %<access_token>s', access_token: access_token)
  client.get(path, headers: { 'Authorization': authorization })
end
Also aliased as: multisigs
send_raw_transaction(raw, access_token: nil) click to toggle source

send a signed transaction to main net

# File lib/mixin_bot/api/multisig.rb, line 150
def send_raw_transaction(raw, access_token: nil)
  path = '/external/proxy'
  payload = {
    method: 'sendrawtransaction',
    params: [raw]
  }

  access_token ||= access_token('POST', path, payload.to_json)
  authorization = format('Bearer %<access_token>s', access_token: access_token)
  client.post(path, headers: { 'Authorization': authorization }, json: payload)
end
sign_multisig_request(request_id, pin) click to toggle source
# File lib/mixin_bot/api/multisig.rb, line 92
def sign_multisig_request(request_id, pin)
  path = format('/multisigs/requests/%<request_id>s/sign', request_id: request_id)
  payload = {
    pin: encrypt_pin(pin)
  }
  access_token ||= access_token('POST', path, payload.to_json)
  authorization = format('Bearer %<access_token>s', access_token: access_token)
  client.post(path, headers: { 'Authorization': authorization }, json: payload)
end
str_to_bin(str) click to toggle source
# File lib/mixin_bot/api/multisig.rb, line 261
def str_to_bin(str)
  return if str.nil?

  str.scan(/../).map(&:hex).pack('c*')
end
unlock_multisig_request(request_id, pin) click to toggle source
# File lib/mixin_bot/api/multisig.rb, line 102
def unlock_multisig_request(request_id, pin)
  path = format('/multisigs/requests/%<request_id>s/unlock', request_id: request_id)
  payload = {
    pin: encrypt_pin(pin)
  }
  access_token ||= access_token('POST', path, payload.to_json)
  authorization = format('Bearer %<access_token>s', access_token: access_token)
  client.post(path, headers: { 'Authorization': authorization }, json: payload)
end
verify_multisig(code_id, access_token: nil) click to toggle source
# File lib/mixin_bot/api/multisig.rb, line 142
def verify_multisig(code_id, access_token: nil)
  path = format('/codes/%<code_id>s', code_id: code_id)
  access_token ||= access_token('GET', path, '')
  authorization = format('Bearer %<access_token>s', access_token: access_token)
  client.get(path, headers: { 'Authorization': authorization })
end