mixin_bot 0.12.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/lib/mixin_bot/api/address.rb +21 -0
 - data/lib/mixin_bot/api/app.rb +5 -11
 - data/lib/mixin_bot/api/asset.rb +9 -16
 - data/lib/mixin_bot/api/attachment.rb +27 -22
 - data/lib/mixin_bot/api/auth.rb +34 -56
 - data/lib/mixin_bot/api/blaze.rb +4 -3
 - data/lib/mixin_bot/api/conversation.rb +29 -49
 - data/lib/mixin_bot/api/encrypted_message.rb +19 -19
 - data/lib/mixin_bot/api/inscription.rb +71 -0
 - data/lib/mixin_bot/api/legacy_collectible.rb +140 -0
 - data/lib/mixin_bot/api/legacy_multisig.rb +87 -0
 - data/lib/mixin_bot/api/legacy_output.rb +50 -0
 - data/lib/mixin_bot/api/legacy_payment.rb +31 -0
 - data/lib/mixin_bot/api/legacy_snapshot.rb +39 -0
 - data/lib/mixin_bot/api/legacy_transaction.rb +173 -0
 - data/lib/mixin_bot/api/legacy_transfer.rb +42 -0
 - data/lib/mixin_bot/api/me.rb +13 -17
 - data/lib/mixin_bot/api/message.rb +13 -10
 - data/lib/mixin_bot/api/multisig.rb +17 -222
 - data/lib/mixin_bot/api/output.rb +48 -0
 - data/lib/mixin_bot/api/payment.rb +9 -20
 - data/lib/mixin_bot/api/pin.rb +57 -65
 - data/lib/mixin_bot/api/rpc.rb +12 -14
 - data/lib/mixin_bot/api/snapshot.rb +15 -29
 - data/lib/mixin_bot/api/tip.rb +43 -0
 - data/lib/mixin_bot/api/transaction.rb +295 -60
 - data/lib/mixin_bot/api/transfer.rb +69 -31
 - data/lib/mixin_bot/api/user.rb +88 -53
 - data/lib/mixin_bot/api/withdraw.rb +52 -53
 - data/lib/mixin_bot/api.rb +81 -46
 - data/lib/mixin_bot/cli/api.rb +149 -5
 - data/lib/mixin_bot/cli/utils.rb +14 -4
 - data/lib/mixin_bot/cli.rb +13 -10
 - data/lib/mixin_bot/client.rb +74 -127
 - data/lib/mixin_bot/configuration.rb +98 -0
 - data/lib/mixin_bot/nfo.rb +174 -0
 - data/lib/mixin_bot/transaction.rb +524 -0
 - data/lib/mixin_bot/utils/address.rb +121 -0
 - data/lib/mixin_bot/utils/crypto.rb +218 -0
 - data/lib/mixin_bot/utils/decoder.rb +56 -0
 - data/lib/mixin_bot/utils/encoder.rb +63 -0
 - data/lib/mixin_bot/utils.rb +8 -109
 - data/lib/mixin_bot/uuid.rb +41 -0
 - data/lib/mixin_bot/version.rb +1 -1
 - data/lib/mixin_bot.rb +39 -14
 - data/lib/mvm/bridge.rb +2 -19
 - data/lib/mvm/client.rb +11 -33
 - data/lib/mvm/nft.rb +4 -4
 - data/lib/mvm/registry.rb +9 -9
 - data/lib/mvm/scan.rb +3 -5
 - data/lib/mvm.rb +5 -6
 - metadata +77 -103
 - data/lib/mixin_bot/api/collectible.rb +0 -138
 - data/lib/mixin_bot/utils/nfo.rb +0 -176
 - data/lib/mixin_bot/utils/transaction.rb +0 -478
 - data/lib/mixin_bot/utils/uuid.rb +0 -43
 
| 
         @@ -0,0 +1,71 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module MixinBot
         
     | 
| 
      
 4 
     | 
    
         
            +
              class API
         
     | 
| 
      
 5 
     | 
    
         
            +
                module Inscription
         
     | 
| 
      
 6 
     | 
    
         
            +
                  def collection(hash)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    path = "/safe/inscriptions/collections/#{hash}"
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                    client.get path
         
     | 
| 
      
 10 
     | 
    
         
            +
                  end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  def collectible(hash)
         
     | 
| 
      
 13 
     | 
    
         
            +
                    path = "/safe/inscriptions/items/#{hash}"
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                    client.get path
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  def collectibles(members: [], access_token: nil)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    unspent_outputs = safe_outputs(state: :unspent, members:, access_token:)['data']
         
     | 
| 
      
 20 
     | 
    
         
            +
                    unspent_outputs.select { |output| output['inscription_hash'].present? }
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  def create_collectible_transfer(utxo, **kwargs)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    # verify collectible
         
     | 
| 
      
 25 
     | 
    
         
            +
                    utxo = utxo.with_indifferent_access
         
     | 
| 
      
 26 
     | 
    
         
            +
                    raise MixinBot::ArgumentError, 'not a valid collectible' unless utxo['inscription_hash'].present?
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                    # verify members
         
     | 
| 
      
 29 
     | 
    
         
            +
                    members = [kwargs[:members]].flatten.compact
         
     | 
| 
      
 30 
     | 
    
         
            +
                    raise ArgumentError, 'members required' if members.blank?
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                    threshold = kwargs[:threshold] || members.length
         
     | 
| 
      
 33 
     | 
    
         
            +
                    request_id = kwargs[:request_id] || kwargs[:trace_id] || SecureRandom.uuid
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                    memo = kwargs[:memo] || ''
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                    # build transaction
         
     | 
| 
      
 38 
     | 
    
         
            +
                    tx = build_safe_transaction(
         
     | 
| 
      
 39 
     | 
    
         
            +
                      utxos: [utxo],
         
     | 
| 
      
 40 
     | 
    
         
            +
                      receivers: [{
         
     | 
| 
      
 41 
     | 
    
         
            +
                        members:,
         
     | 
| 
      
 42 
     | 
    
         
            +
                        threshold:,
         
     | 
| 
      
 43 
     | 
    
         
            +
                        amount: utxo['amount']
         
     | 
| 
      
 44 
     | 
    
         
            +
                      }],
         
     | 
| 
      
 45 
     | 
    
         
            +
                      extra: memo
         
     | 
| 
      
 46 
     | 
    
         
            +
                    )
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                    # encode transaction
         
     | 
| 
      
 49 
     | 
    
         
            +
                    raw = MixinBot.utils.encode_raw_transaction tx
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                    # verify transaction
         
     | 
| 
      
 52 
     | 
    
         
            +
                    request = create_safe_transaction_request(request_id, raw)['data']
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                    # sign transaction
         
     | 
| 
      
 55 
     | 
    
         
            +
                    spend_key = MixinBot.utils.decode_key(kwargs[:spend_key]) || config.spend_key
         
     | 
| 
      
 56 
     | 
    
         
            +
                    signed_raw = MixinBot.api.sign_safe_transaction(
         
     | 
| 
      
 57 
     | 
    
         
            +
                      raw:,
         
     | 
| 
      
 58 
     | 
    
         
            +
                      utxos: [utxo],
         
     | 
| 
      
 59 
     | 
    
         
            +
                      request: request[0],
         
     | 
| 
      
 60 
     | 
    
         
            +
                      spend_key:
         
     | 
| 
      
 61 
     | 
    
         
            +
                    )
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                    # submit transaction
         
     | 
| 
      
 64 
     | 
    
         
            +
                    send_safe_transaction(
         
     | 
| 
      
 65 
     | 
    
         
            +
                      request_id,
         
     | 
| 
      
 66 
     | 
    
         
            +
                      signed_raw
         
     | 
| 
      
 67 
     | 
    
         
            +
                    )
         
     | 
| 
      
 68 
     | 
    
         
            +
                  end
         
     | 
| 
      
 69 
     | 
    
         
            +
                end
         
     | 
| 
      
 70 
     | 
    
         
            +
              end
         
     | 
| 
      
 71 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,140 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module MixinBot
         
     | 
| 
      
 4 
     | 
    
         
            +
              class API
         
     | 
| 
      
 5 
     | 
    
         
            +
                module LegacyCollectible
         
     | 
| 
      
 6 
     | 
    
         
            +
                  NFT_ASSET_MIXIN_ID = '1700941284a95f31b25ec8c546008f208f88eee4419ccdcdbe6e3195e60128ca'
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                  def legacy_collectible(id, access_token: nil)
         
     | 
| 
      
 9 
     | 
    
         
            +
                    path = "/collectibles/tokens/#{id}"
         
     | 
| 
      
 10 
     | 
    
         
            +
                    client.get path, access_token:
         
     | 
| 
      
 11 
     | 
    
         
            +
                  end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  def legacy_collection(id, access_token: nil)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    path = "/collectibles/collections/#{id}"
         
     | 
| 
      
 15 
     | 
    
         
            +
                    client.get path, access_token:
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  def legacy_collectibles(**kwargs)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    limit = kwargs[:limit] || 100
         
     | 
| 
      
 20 
     | 
    
         
            +
                    offset = kwargs[:offset] || ''
         
     | 
| 
      
 21 
     | 
    
         
            +
                    state = kwargs[:state] || ''
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                    members = kwargs[:members] || [config.app_id]
         
     | 
| 
      
 24 
     | 
    
         
            +
                    threshold = kwargs[:threshold] || members.length
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                    members = SHA3::Digest::SHA256.hexdigest(members&.sort&.join)
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                    access_token = kwargs[:access_token]
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                    path = '/collectibles/outputs'
         
     | 
| 
      
 31 
     | 
    
         
            +
                    params = {
         
     | 
| 
      
 32 
     | 
    
         
            +
                      limit:,
         
     | 
| 
      
 33 
     | 
    
         
            +
                      offset:,
         
     | 
| 
      
 34 
     | 
    
         
            +
                      state:,
         
     | 
| 
      
 35 
     | 
    
         
            +
                      members:,
         
     | 
| 
      
 36 
     | 
    
         
            +
                      threshold:
         
     | 
| 
      
 37 
     | 
    
         
            +
                    }
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                    client.get path, **params, access_token:
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  COLLECTABLE_REQUEST_ACTIONS = %i[sign unlock].freeze
         
     | 
| 
      
 43 
     | 
    
         
            +
                  def create_collectible_request(action, raw, access_token: nil)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    raise ArgumentError, "request action is limited in #{COLLECTABLE_REQUEST_ACTIONS.join(', ')}" unless COLLECTABLE_REQUEST_ACTIONS.include? action.to_sym
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                    path = '/collectibles/requests'
         
     | 
| 
      
 47 
     | 
    
         
            +
                    payload = {
         
     | 
| 
      
 48 
     | 
    
         
            +
                      action:,
         
     | 
| 
      
 49 
     | 
    
         
            +
                      raw:
         
     | 
| 
      
 50 
     | 
    
         
            +
                    }
         
     | 
| 
      
 51 
     | 
    
         
            +
                    client.post path, **payload, access_token:
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  def create_sign_collectible_request(raw, access_token: nil)
         
     | 
| 
      
 55 
     | 
    
         
            +
                    create_collectible_request 'sign', raw, access_token:
         
     | 
| 
      
 56 
     | 
    
         
            +
                  end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                  def create_unlock_collectible_request(raw, access_token: nil)
         
     | 
| 
      
 59 
     | 
    
         
            +
                    create_collectible_request 'unlock', raw, access_token:
         
     | 
| 
      
 60 
     | 
    
         
            +
                  end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                  def sign_collectible_request(request_id, pin = nil)
         
     | 
| 
      
 63 
     | 
    
         
            +
                    pin ||= config.pin
         
     | 
| 
      
 64 
     | 
    
         
            +
                    raise ArgumentError, 'pin is needed for sign collectible request' if pin.blank?
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                    path = format('/collectibles/requests/%<request_id>s/sign', request_id:)
         
     | 
| 
      
 67 
     | 
    
         
            +
                    payload =
         
     | 
| 
      
 68 
     | 
    
         
            +
                      if pin.length > 6
         
     | 
| 
      
 69 
     | 
    
         
            +
                        pin_base64 =  encrypt_tip_pin(pin, 'TIP:COLLECTIBLE:REQUEST:SIGN:', request_id)
         
     | 
| 
      
 70 
     | 
    
         
            +
                        {
         
     | 
| 
      
 71 
     | 
    
         
            +
                          pin_base64:
         
     | 
| 
      
 72 
     | 
    
         
            +
                        }
         
     | 
| 
      
 73 
     | 
    
         
            +
                      else
         
     | 
| 
      
 74 
     | 
    
         
            +
                        {
         
     | 
| 
      
 75 
     | 
    
         
            +
                          pin: encrypt_pin(pin)
         
     | 
| 
      
 76 
     | 
    
         
            +
                        }
         
     | 
| 
      
 77 
     | 
    
         
            +
                      end
         
     | 
| 
      
 78 
     | 
    
         
            +
                    client.post path, **payload
         
     | 
| 
      
 79 
     | 
    
         
            +
                  end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                  def unlock_collectible_request(request_id, pin = nil)
         
     | 
| 
      
 82 
     | 
    
         
            +
                    pin ||= config.pin
         
     | 
| 
      
 83 
     | 
    
         
            +
                    raise ArgumentError, 'pin is needed for sign collectible request' if pin.blank?
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                    path = format('/collectibles/requests/%<request_id>s/unlock', request_id:)
         
     | 
| 
      
 86 
     | 
    
         
            +
                    payload =
         
     | 
| 
      
 87 
     | 
    
         
            +
                      if pin.length > 6
         
     | 
| 
      
 88 
     | 
    
         
            +
                        {
         
     | 
| 
      
 89 
     | 
    
         
            +
                          pin_base64: encrypt_tip_pin(pin, 'TIP:COLLECTIBLE:REQUEST:UNLOCK:', request_id)
         
     | 
| 
      
 90 
     | 
    
         
            +
                        }
         
     | 
| 
      
 91 
     | 
    
         
            +
                      else
         
     | 
| 
      
 92 
     | 
    
         
            +
                        {
         
     | 
| 
      
 93 
     | 
    
         
            +
                          pin: encrypt_pin(pin)
         
     | 
| 
      
 94 
     | 
    
         
            +
                        }
         
     | 
| 
      
 95 
     | 
    
         
            +
                      end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                    client.post path, **payload
         
     | 
| 
      
 98 
     | 
    
         
            +
                  end
         
     | 
| 
      
 99 
     | 
    
         
            +
                end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                # collectible = {
         
     | 
| 
      
 102 
     | 
    
         
            +
                #   type: 'non_fungible_output',
         
     | 
| 
      
 103 
     | 
    
         
            +
                #   user_id: '',
         
     | 
| 
      
 104 
     | 
    
         
            +
                #   output_id: '',
         
     | 
| 
      
 105 
     | 
    
         
            +
                #   token_id: '',
         
     | 
| 
      
 106 
     | 
    
         
            +
                #   transaction_hash: '',
         
     | 
| 
      
 107 
     | 
    
         
            +
                #   output_index: '',
         
     | 
| 
      
 108 
     | 
    
         
            +
                #   amount: 1,
         
     | 
| 
      
 109 
     | 
    
         
            +
                #   senders: [],
         
     | 
| 
      
 110 
     | 
    
         
            +
                #   sender_threshold: 1,
         
     | 
| 
      
 111 
     | 
    
         
            +
                #   receivers: [],
         
     | 
| 
      
 112 
     | 
    
         
            +
                #   receivers_threshold: 1,
         
     | 
| 
      
 113 
     | 
    
         
            +
                #   state: 'unspent'
         
     | 
| 
      
 114 
     | 
    
         
            +
                # }
         
     | 
| 
      
 115 
     | 
    
         
            +
                COLLECTIBLE_TRANSACTION_ARGUMENTS = %i[collectible nfo receivers receivers_threshold].freeze
         
     | 
| 
      
 116 
     | 
    
         
            +
                def build_collectible_transaction(**kwargs)
         
     | 
| 
      
 117 
     | 
    
         
            +
                  raise ArgumentError, "#{COLLECTIBLE_TRANSACTION_ARGUMENTS.join(', ')} are needed for build collectible transaction" unless COLLECTIBLE_TRANSACTION_ARGUMENTS.all? { |param| kwargs.keys.include? param }
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
                  kwargs = kwargs.with_indifferent_access
         
     | 
| 
      
 120 
     | 
    
         
            +
                  collectible = kwargs['collectible']
         
     | 
| 
      
 121 
     | 
    
         
            +
                  raise 'collectible is spent' if collectible['state'] == 'spent'
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                  build_raw_transaction(
         
     | 
| 
      
 124 
     | 
    
         
            +
                    utxos: [collectible],
         
     | 
| 
      
 125 
     | 
    
         
            +
                    senders: collectible['receivers'],
         
     | 
| 
      
 126 
     | 
    
         
            +
                    senders_threshold: collectible['receivers_threshold'],
         
     | 
| 
      
 127 
     | 
    
         
            +
                    receivers: kwargs['receivers'],
         
     | 
| 
      
 128 
     | 
    
         
            +
                    receivers_threshold: kwargs['receivers_threshold'],
         
     | 
| 
      
 129 
     | 
    
         
            +
                    extra: [kwargs['nfo']].pack('H*'),
         
     | 
| 
      
 130 
     | 
    
         
            +
                    amount: 1,
         
     | 
| 
      
 131 
     | 
    
         
            +
                    asset_mixin_id: NFT_ASSET_MIXIN_ID,
         
     | 
| 
      
 132 
     | 
    
         
            +
                    hint: kwargs['hint']
         
     | 
| 
      
 133 
     | 
    
         
            +
                  )
         
     | 
| 
      
 134 
     | 
    
         
            +
                end
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                def nft_memo(collection, token_id, meta)
         
     | 
| 
      
 137 
     | 
    
         
            +
                  MixinBot.utils.nft_memo collection, token_id, meta
         
     | 
| 
      
 138 
     | 
    
         
            +
                end
         
     | 
| 
      
 139 
     | 
    
         
            +
              end
         
     | 
| 
      
 140 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,87 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module MixinBot
         
     | 
| 
      
 4 
     | 
    
         
            +
              class API
         
     | 
| 
      
 5 
     | 
    
         
            +
                module LegacyMultisig
         
     | 
| 
      
 6 
     | 
    
         
            +
                  MULTISIG_REQUEST_ACTIONS = %i[sign unlock].freeze
         
     | 
| 
      
 7 
     | 
    
         
            +
                  def create_multisig_request(action, raw, access_token: nil)
         
     | 
| 
      
 8 
     | 
    
         
            +
                    raise ArgumentError, "request action is limited in #{MULTISIG_REQUEST_ACTIONS.join(', ')}" unless MULTISIG_REQUEST_ACTIONS.include? action.to_sym
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                    path = '/multisigs/requests'
         
     | 
| 
      
 11 
     | 
    
         
            +
                    payload = {
         
     | 
| 
      
 12 
     | 
    
         
            +
                      action:,
         
     | 
| 
      
 13 
     | 
    
         
            +
                      raw:
         
     | 
| 
      
 14 
     | 
    
         
            +
                    }
         
     | 
| 
      
 15 
     | 
    
         
            +
                    client.post path, **payload, access_token:
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  # transfer from the multisig address
         
     | 
| 
      
 19 
     | 
    
         
            +
                  def create_sign_multisig_request(raw, access_token: nil)
         
     | 
| 
      
 20 
     | 
    
         
            +
                    create_multisig_request 'sign', raw, access_token:
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  # transfer from the multisig address
         
     | 
| 
      
 24 
     | 
    
         
            +
                  # create a request for unlock a multi-sign
         
     | 
| 
      
 25 
     | 
    
         
            +
                  def create_unlock_multisig_request(raw, access_token: nil)
         
     | 
| 
      
 26 
     | 
    
         
            +
                    create_multisig_request 'unlock', raw, access_token:
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  def sign_multisig_request(request_id, pin = nil)
         
     | 
| 
      
 30 
     | 
    
         
            +
                    pin ||= config.pin
         
     | 
| 
      
 31 
     | 
    
         
            +
                    path = format('/multisigs/requests/%<request_id>s/sign', request_id:)
         
     | 
| 
      
 32 
     | 
    
         
            +
                    payload =
         
     | 
| 
      
 33 
     | 
    
         
            +
                      if pin.length > 6
         
     | 
| 
      
 34 
     | 
    
         
            +
                        {
         
     | 
| 
      
 35 
     | 
    
         
            +
                          pin_base64: encrypt_tip_pin(pin, 'TIP:MULTISIG:REQUEST:SIGN:', request_id)
         
     | 
| 
      
 36 
     | 
    
         
            +
                        }
         
     | 
| 
      
 37 
     | 
    
         
            +
                      else
         
     | 
| 
      
 38 
     | 
    
         
            +
                        {
         
     | 
| 
      
 39 
     | 
    
         
            +
                          pin: encrypt_pin(pin)
         
     | 
| 
      
 40 
     | 
    
         
            +
                        }
         
     | 
| 
      
 41 
     | 
    
         
            +
                      end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                    client.post path, **payload
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                  def unlock_multisig_request(request_id, pin = nil)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    pin ||= config.pin
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                    path = format('/multisigs/requests/%<request_id>s/unlock', request_id:)
         
     | 
| 
      
 50 
     | 
    
         
            +
                    payload =
         
     | 
| 
      
 51 
     | 
    
         
            +
                      if pin.length > 6
         
     | 
| 
      
 52 
     | 
    
         
            +
                        {
         
     | 
| 
      
 53 
     | 
    
         
            +
                          pin_base64: encrypt_tip_pin(pin, 'TIP:MULTISIG:REQUEST:UNLOCK:', request_id)
         
     | 
| 
      
 54 
     | 
    
         
            +
                        }
         
     | 
| 
      
 55 
     | 
    
         
            +
                      else
         
     | 
| 
      
 56 
     | 
    
         
            +
                        {
         
     | 
| 
      
 57 
     | 
    
         
            +
                          pin: encrypt_pin(pin)
         
     | 
| 
      
 58 
     | 
    
         
            +
                        }
         
     | 
| 
      
 59 
     | 
    
         
            +
                      end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                    client.post path, **payload
         
     | 
| 
      
 62 
     | 
    
         
            +
                  end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                  # pay to the multisig address
         
     | 
| 
      
 65 
     | 
    
         
            +
                  # used for create multisig payment code_id
         
     | 
| 
      
 66 
     | 
    
         
            +
                  def create_multisig_payment(**kwargs)
         
     | 
| 
      
 67 
     | 
    
         
            +
                    path = '/payments'
         
     | 
| 
      
 68 
     | 
    
         
            +
                    payload = {
         
     | 
| 
      
 69 
     | 
    
         
            +
                      asset_id: kwargs[:asset_id],
         
     | 
| 
      
 70 
     | 
    
         
            +
                      amount: format('%.8f', kwargs[:amount].to_d),
         
     | 
| 
      
 71 
     | 
    
         
            +
                      trace_id: kwargs[:trace_id] || SecureRandom.uuid,
         
     | 
| 
      
 72 
     | 
    
         
            +
                      memo: kwargs[:memo],
         
     | 
| 
      
 73 
     | 
    
         
            +
                      opponent_multisig: {
         
     | 
| 
      
 74 
     | 
    
         
            +
                        receivers: kwargs[:receivers],
         
     | 
| 
      
 75 
     | 
    
         
            +
                        threshold: kwargs[:threshold]
         
     | 
| 
      
 76 
     | 
    
         
            +
                      }
         
     | 
| 
      
 77 
     | 
    
         
            +
                    }
         
     | 
| 
      
 78 
     | 
    
         
            +
                    client.post path, **payload, access_token: kwargs[:access_token]
         
     | 
| 
      
 79 
     | 
    
         
            +
                  end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                  def verify_multisig(code_id, access_token: nil)
         
     | 
| 
      
 82 
     | 
    
         
            +
                    path = format('/codes/%<code_id>s', code_id:)
         
     | 
| 
      
 83 
     | 
    
         
            +
                    client.get path, access_token:
         
     | 
| 
      
 84 
     | 
    
         
            +
                  end
         
     | 
| 
      
 85 
     | 
    
         
            +
                end
         
     | 
| 
      
 86 
     | 
    
         
            +
              end
         
     | 
| 
      
 87 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,50 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module MixinBot
         
     | 
| 
      
 4 
     | 
    
         
            +
              class API
         
     | 
| 
      
 5 
     | 
    
         
            +
                module LegacyOutput
         
     | 
| 
      
 6 
     | 
    
         
            +
                  def legacy_outputs(**kwargs)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    limit = kwargs[:limit] || 100
         
     | 
| 
      
 8 
     | 
    
         
            +
                    offset = kwargs[:offset] || ''
         
     | 
| 
      
 9 
     | 
    
         
            +
                    state = kwargs[:state] || ''
         
     | 
| 
      
 10 
     | 
    
         
            +
                    members = kwargs[:members] || []
         
     | 
| 
      
 11 
     | 
    
         
            +
                    threshold = kwargs[:threshold] || ''
         
     | 
| 
      
 12 
     | 
    
         
            +
                    access_token = kwargs[:access_token]
         
     | 
| 
      
 13 
     | 
    
         
            +
                    members = SHA3::Digest::SHA256.hexdigest(members&.sort&.join)
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                    path = '/multisigs/outputs'
         
     | 
| 
      
 16 
     | 
    
         
            +
                    params = {
         
     | 
| 
      
 17 
     | 
    
         
            +
                      limit:,
         
     | 
| 
      
 18 
     | 
    
         
            +
                      offset:,
         
     | 
| 
      
 19 
     | 
    
         
            +
                      state:,
         
     | 
| 
      
 20 
     | 
    
         
            +
                      members:,
         
     | 
| 
      
 21 
     | 
    
         
            +
                      threshold:
         
     | 
| 
      
 22 
     | 
    
         
            +
                    }.compact_blank
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                    client.get path, **params, access_token:
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
                  alias multisigs legacy_outputs
         
     | 
| 
      
 27 
     | 
    
         
            +
                  alias multisig_outputs legacy_outputs
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  def create_output(receivers:, index:, hint: nil, access_token: nil)
         
     | 
| 
      
 30 
     | 
    
         
            +
                    path = '/outputs'
         
     | 
| 
      
 31 
     | 
    
         
            +
                    payload = {
         
     | 
| 
      
 32 
     | 
    
         
            +
                      receivers:,
         
     | 
| 
      
 33 
     | 
    
         
            +
                      index:,
         
     | 
| 
      
 34 
     | 
    
         
            +
                      hint:
         
     | 
| 
      
 35 
     | 
    
         
            +
                    }
         
     | 
| 
      
 36 
     | 
    
         
            +
                    client.post path, **payload, access_token:
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  def build_output(receivers:, index:, amount:, threshold:, hint: nil)
         
     | 
| 
      
 40 
     | 
    
         
            +
                    _output = create_output(receivers:, index:, hint:)
         
     | 
| 
      
 41 
     | 
    
         
            +
                    {
         
     | 
| 
      
 42 
     | 
    
         
            +
                      amount: format('%.8f', amount.to_d.to_r),
         
     | 
| 
      
 43 
     | 
    
         
            +
                      script: build_threshold_script(threshold),
         
     | 
| 
      
 44 
     | 
    
         
            +
                      mask: _output['mask'],
         
     | 
| 
      
 45 
     | 
    
         
            +
                      keys: _output['keys']
         
     | 
| 
      
 46 
     | 
    
         
            +
                    }
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
              end
         
     | 
| 
      
 50 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,31 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module MixinBot
         
     | 
| 
      
 4 
     | 
    
         
            +
              class API
         
     | 
| 
      
 5 
     | 
    
         
            +
                module LegacyPayment
         
     | 
| 
      
 6 
     | 
    
         
            +
                  def pay_url(**kwargs)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    format(
         
     | 
| 
      
 8 
     | 
    
         
            +
                      'https://mixin.one/pay?recipient=%<recipient_id>s&asset=%<asset>s&amount=%<amount>s&trace=%<trace>s&memo=%<memo>s',
         
     | 
| 
      
 9 
     | 
    
         
            +
                      recipient_id: kwargs[:recipient_id],
         
     | 
| 
      
 10 
     | 
    
         
            +
                      asset: kwargs[:asset_id],
         
     | 
| 
      
 11 
     | 
    
         
            +
                      amount: kwargs[:amount].to_s,
         
     | 
| 
      
 12 
     | 
    
         
            +
                      trace: kwargs[:trace],
         
     | 
| 
      
 13 
     | 
    
         
            +
                      memo: kwargs[:memo]
         
     | 
| 
      
 14 
     | 
    
         
            +
                    )
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                  # https://developers.mixin.one/api/alpha-mixin-network/verify-payment/
         
     | 
| 
      
 18 
     | 
    
         
            +
                  def verify_payment(**kwargs)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    path = '/payments'
         
     | 
| 
      
 20 
     | 
    
         
            +
                    payload = {
         
     | 
| 
      
 21 
     | 
    
         
            +
                      asset_id: kwargs[:asset_id],
         
     | 
| 
      
 22 
     | 
    
         
            +
                      opponent_id: kwargs[:opponent_id],
         
     | 
| 
      
 23 
     | 
    
         
            +
                      amount: kwargs[:amount].to_s,
         
     | 
| 
      
 24 
     | 
    
         
            +
                      trace_id: kwargs[:trace]
         
     | 
| 
      
 25 
     | 
    
         
            +
                    }
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                    client.post path, **payload
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,39 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module MixinBot
         
     | 
| 
      
 4 
     | 
    
         
            +
              class API
         
     | 
| 
      
 5 
     | 
    
         
            +
                module LegacySnapshot
         
     | 
| 
      
 6 
     | 
    
         
            +
                  def network_snapshots(**kwargs)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    path = '/network/snapshots'
         
     | 
| 
      
 8 
     | 
    
         
            +
                    params = {
         
     | 
| 
      
 9 
     | 
    
         
            +
                      limit: kwargs[:limit],
         
     | 
| 
      
 10 
     | 
    
         
            +
                      offset: kwargs[:offset],
         
     | 
| 
      
 11 
     | 
    
         
            +
                      asset: kwargs[:asset],
         
     | 
| 
      
 12 
     | 
    
         
            +
                      order: kwargs[:order]
         
     | 
| 
      
 13 
     | 
    
         
            +
                    }
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                    client.get path, **params, access_token: kwargs[:access_token]
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  def snapshots(**kwargs)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    path = '/snapshots'
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                    params = {
         
     | 
| 
      
 22 
     | 
    
         
            +
                      limit: kwargs[:limit],
         
     | 
| 
      
 23 
     | 
    
         
            +
                      offset: kwargs[:offset],
         
     | 
| 
      
 24 
     | 
    
         
            +
                      asset: kwargs[:asset],
         
     | 
| 
      
 25 
     | 
    
         
            +
                      opponent: kwargs[:opponent],
         
     | 
| 
      
 26 
     | 
    
         
            +
                      order: kwargs[:order]
         
     | 
| 
      
 27 
     | 
    
         
            +
                    }
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                    client.get path, **params, access_token: kwargs[:access_token]
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  def network_snapshot(snapshot_id, **kwargs)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    path = format('/network/snapshots/%<snapshot_id>s', snapshot_id:)
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                    client.get path, access_token: kwargs[:access_token]
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,173 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module MixinBot
         
     | 
| 
      
 4 
     | 
    
         
            +
              class API
         
     | 
| 
      
 5 
     | 
    
         
            +
                module LegacyTransaction
         
     | 
| 
      
 6 
     | 
    
         
            +
                  LEGACY_TX_VERSION = 0x04
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                  # use safe transaction protocol instead
         
     | 
| 
      
 9 
     | 
    
         
            +
                  # kwargs:
         
     | 
| 
      
 10 
     | 
    
         
            +
                  # {
         
     | 
| 
      
 11 
     | 
    
         
            +
                  #   senders: [ uuid ],
         
     | 
| 
      
 12 
     | 
    
         
            +
                  #   senders_threshold: integer,
         
     | 
| 
      
 13 
     | 
    
         
            +
                  #   receivers: [ uuid ],
         
     | 
| 
      
 14 
     | 
    
         
            +
                  #   receivers_threshold: integer,
         
     | 
| 
      
 15 
     | 
    
         
            +
                  #   asset_id: uuid,
         
     | 
| 
      
 16 
     | 
    
         
            +
                  #   amount: string / float,
         
     | 
| 
      
 17 
     | 
    
         
            +
                  #   memo: string,
         
     | 
| 
      
 18 
     | 
    
         
            +
                  # }
         
     | 
| 
      
 19 
     | 
    
         
            +
                  RAW_TRANSACTION_ARGUMENTS = %i[utxos senders senders_threshold receivers receivers_threshold amount].freeze
         
     | 
| 
      
 20 
     | 
    
         
            +
                  def build_raw_transaction(**kwargs)
         
     | 
| 
      
 21 
     | 
    
         
            +
                    raise ArgumentError, "#{RAW_TRANSACTION_ARGUMENTS.join(', ')} are needed for build raw transaction" unless RAW_TRANSACTION_ARGUMENTS.all? { |param| kwargs.keys.include? param }
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                    senders             = kwargs[:senders]
         
     | 
| 
      
 24 
     | 
    
         
            +
                    senders_threshold   = kwargs[:senders_threshold]
         
     | 
| 
      
 25 
     | 
    
         
            +
                    receivers           = kwargs[:receivers]
         
     | 
| 
      
 26 
     | 
    
         
            +
                    receivers_threshold = kwargs[:receivers_threshold]
         
     | 
| 
      
 27 
     | 
    
         
            +
                    amount              = kwargs[:amount]
         
     | 
| 
      
 28 
     | 
    
         
            +
                    asset_id            = kwargs[:asset_id]
         
     | 
| 
      
 29 
     | 
    
         
            +
                    asset_mixin_id      = kwargs[:asset_mixin_id]
         
     | 
| 
      
 30 
     | 
    
         
            +
                    utxos               = kwargs[:utxos]
         
     | 
| 
      
 31 
     | 
    
         
            +
                    extra               = kwargs[:extra]
         
     | 
| 
      
 32 
     | 
    
         
            +
                    access_token        = kwargs[:access_token]
         
     | 
| 
      
 33 
     | 
    
         
            +
                    outputs             = kwargs[:outputs] || []
         
     | 
| 
      
 34 
     | 
    
         
            +
                    hint                = kwargs[:hint]
         
     | 
| 
      
 35 
     | 
    
         
            +
                    version             = kwargs[:version] || LEGACY_TX_VERSION
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                    raise 'access_token required!' if access_token.nil? && !senders.include?(config.app_id)
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                    amount = amount.to_d.round(8)
         
     | 
| 
      
 40 
     | 
    
         
            +
                    input_amount = utxos.map(
         
     | 
| 
      
 41 
     | 
    
         
            +
                      &lambda { |utxo|
         
     | 
| 
      
 42 
     | 
    
         
            +
                        utxo['amount'].to_d
         
     | 
| 
      
 43 
     | 
    
         
            +
                      }
         
     | 
| 
      
 44 
     | 
    
         
            +
                    ).sum
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                    if input_amount < amount
         
     | 
| 
      
 47 
     | 
    
         
            +
                      raise format(
         
     | 
| 
      
 48 
     | 
    
         
            +
                        'not enough amount! %<input_amount>s < %<amount>s',
         
     | 
| 
      
 49 
     | 
    
         
            +
                        input_amount:,
         
     | 
| 
      
 50 
     | 
    
         
            +
                        amount:
         
     | 
| 
      
 51 
     | 
    
         
            +
                      )
         
     | 
| 
      
 52 
     | 
    
         
            +
                    end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                    inputs = utxos.map(
         
     | 
| 
      
 55 
     | 
    
         
            +
                      &lambda { |utx|
         
     | 
| 
      
 56 
     | 
    
         
            +
                        {
         
     | 
| 
      
 57 
     | 
    
         
            +
                          'hash' => utx['transaction_hash'],
         
     | 
| 
      
 58 
     | 
    
         
            +
                          'index' => utx['output_index']
         
     | 
| 
      
 59 
     | 
    
         
            +
                        }
         
     | 
| 
      
 60 
     | 
    
         
            +
                      }
         
     | 
| 
      
 61 
     | 
    
         
            +
                    )
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                    if outputs.empty?
         
     | 
| 
      
 64 
     | 
    
         
            +
                      receivers_threshold = 1 if receivers.size == 1
         
     | 
| 
      
 65 
     | 
    
         
            +
                      output0 = build_output(
         
     | 
| 
      
 66 
     | 
    
         
            +
                        receivers:,
         
     | 
| 
      
 67 
     | 
    
         
            +
                        index: 0,
         
     | 
| 
      
 68 
     | 
    
         
            +
                        amount:,
         
     | 
| 
      
 69 
     | 
    
         
            +
                        threshold: receivers_threshold,
         
     | 
| 
      
 70 
     | 
    
         
            +
                        hint:
         
     | 
| 
      
 71 
     | 
    
         
            +
                      )
         
     | 
| 
      
 72 
     | 
    
         
            +
                      outputs.push output0
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
                      if input_amount > amount
         
     | 
| 
      
 75 
     | 
    
         
            +
                        output1 = build_output(
         
     | 
| 
      
 76 
     | 
    
         
            +
                          receivers: senders,
         
     | 
| 
      
 77 
     | 
    
         
            +
                          index: 1,
         
     | 
| 
      
 78 
     | 
    
         
            +
                          amount: input_amount - amount,
         
     | 
| 
      
 79 
     | 
    
         
            +
                          threshold: senders_threshold,
         
     | 
| 
      
 80 
     | 
    
         
            +
                          hint:
         
     | 
| 
      
 81 
     | 
    
         
            +
                        )
         
     | 
| 
      
 82 
     | 
    
         
            +
                        outputs.push output1
         
     | 
| 
      
 83 
     | 
    
         
            +
                      end
         
     | 
| 
      
 84 
     | 
    
         
            +
                    end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                    asset = asset_mixin_id || SHA3::Digest::SHA256.hexdigest(asset_id)
         
     | 
| 
      
 87 
     | 
    
         
            +
                    {
         
     | 
| 
      
 88 
     | 
    
         
            +
                      version:,
         
     | 
| 
      
 89 
     | 
    
         
            +
                      asset:,
         
     | 
| 
      
 90 
     | 
    
         
            +
                      inputs:,
         
     | 
| 
      
 91 
     | 
    
         
            +
                      outputs:,
         
     | 
| 
      
 92 
     | 
    
         
            +
                      extra:
         
     | 
| 
      
 93 
     | 
    
         
            +
                    }
         
     | 
| 
      
 94 
     | 
    
         
            +
                  end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                  # use safe transaction protocol instead
         
     | 
| 
      
 97 
     | 
    
         
            +
                  MULTISIG_TRANSACTION_ARGUMENTS = %i[asset_id receivers threshold amount].freeze
         
     | 
| 
      
 98 
     | 
    
         
            +
                  def create_multisig_transaction(pin, **options)
         
     | 
| 
      
 99 
     | 
    
         
            +
                    raise ArgumentError, "#{MULTISIG_TRANSACTION_ARGUMENTS.join(', ')} are needed for create multisig transaction" unless MULTISIG_TRANSACTION_ARGUMENTS.all? { |param| options.keys.include? param }
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                    asset_id = options[:asset_id]
         
     | 
| 
      
 102 
     | 
    
         
            +
                    receivers = options[:receivers].sort
         
     | 
| 
      
 103 
     | 
    
         
            +
                    threshold = options[:threshold]
         
     | 
| 
      
 104 
     | 
    
         
            +
                    amount = format('%.8f', options[:amount].to_d.to_r)
         
     | 
| 
      
 105 
     | 
    
         
            +
                    memo = options[:memo]
         
     | 
| 
      
 106 
     | 
    
         
            +
                    trace_id = options[:trace_id] || SecureRandom.uuid
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                    path = '/transactions'
         
     | 
| 
      
 109 
     | 
    
         
            +
                    payload = {
         
     | 
| 
      
 110 
     | 
    
         
            +
                      asset_id:,
         
     | 
| 
      
 111 
     | 
    
         
            +
                      opponent_multisig: {
         
     | 
| 
      
 112 
     | 
    
         
            +
                        receivers:,
         
     | 
| 
      
 113 
     | 
    
         
            +
                        threshold:
         
     | 
| 
      
 114 
     | 
    
         
            +
                      },
         
     | 
| 
      
 115 
     | 
    
         
            +
                      amount:,
         
     | 
| 
      
 116 
     | 
    
         
            +
                      trace_id:,
         
     | 
| 
      
 117 
     | 
    
         
            +
                      memo:
         
     | 
| 
      
 118 
     | 
    
         
            +
                    }
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                    if pin.length > 6
         
     | 
| 
      
 121 
     | 
    
         
            +
                      payload[:pin_base64] = encrypt_tip_pin(pin, 'TIP:TRANSACTION:CREATE:', asset_id, receivers.join, threshold, amount, trace_id, memo)
         
     | 
| 
      
 122 
     | 
    
         
            +
                    else
         
     | 
| 
      
 123 
     | 
    
         
            +
                      payload[:pin] = encrypt_pin(pin)
         
     | 
| 
      
 124 
     | 
    
         
            +
                    end
         
     | 
| 
      
 125 
     | 
    
         
            +
             
     | 
| 
      
 126 
     | 
    
         
            +
                    client.post path, **payload
         
     | 
| 
      
 127 
     | 
    
         
            +
                  end
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
                  # use safe transaction protocol instead
         
     | 
| 
      
 130 
     | 
    
         
            +
                  MAINNET_TRANSACTION_ARGUMENTS = %i[asset_id opponent_id amount].freeze
         
     | 
| 
      
 131 
     | 
    
         
            +
                  def create_mainnet_transaction(pin, **options)
         
     | 
| 
      
 132 
     | 
    
         
            +
                    raise ArgumentError, "#{MAINNET_TRANSACTION_ARGUMENTS.join(', ')} are needed for create main net transactions" unless MAINNET_TRANSACTION_ARGUMENTS.all? { |param| options.keys.include? param }
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                    asset_id = options[:asset_id]
         
     | 
| 
      
 135 
     | 
    
         
            +
                    opponent_id = options[:opponent_id]
         
     | 
| 
      
 136 
     | 
    
         
            +
                    amount = format('%.8f', options[:amount].to_d)
         
     | 
| 
      
 137 
     | 
    
         
            +
                    memo = options[:memo]
         
     | 
| 
      
 138 
     | 
    
         
            +
                    trace_id = options[:trace_id] || SecureRandom.uuid
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
                    path = '/transactions'
         
     | 
| 
      
 141 
     | 
    
         
            +
                    payload = {
         
     | 
| 
      
 142 
     | 
    
         
            +
                      asset_id:,
         
     | 
| 
      
 143 
     | 
    
         
            +
                      opponent_id:,
         
     | 
| 
      
 144 
     | 
    
         
            +
                      amount:,
         
     | 
| 
      
 145 
     | 
    
         
            +
                      trace_id:,
         
     | 
| 
      
 146 
     | 
    
         
            +
                      memo:
         
     | 
| 
      
 147 
     | 
    
         
            +
                    }
         
     | 
| 
      
 148 
     | 
    
         
            +
             
     | 
| 
      
 149 
     | 
    
         
            +
                    if pin.length > 6
         
     | 
| 
      
 150 
     | 
    
         
            +
                      payload[:pin_base64] = encrypt_tip_pin(pin, 'TIP:TRANSACTION:CREATE:', asset_id, opponent_id, amount, trace_id, memo)
         
     | 
| 
      
 151 
     | 
    
         
            +
                    else
         
     | 
| 
      
 152 
     | 
    
         
            +
                      payload[:pin] = encrypt_pin(pin)
         
     | 
| 
      
 153 
     | 
    
         
            +
                    end
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
                    client.post path, **payload
         
     | 
| 
      
 156 
     | 
    
         
            +
                  end
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
                  # use safe transaction protocol instead
         
     | 
| 
      
 159 
     | 
    
         
            +
                  def transactions(**options)
         
     | 
| 
      
 160 
     | 
    
         
            +
                    path = '/external/transactions'
         
     | 
| 
      
 161 
     | 
    
         
            +
                    params = {
         
     | 
| 
      
 162 
     | 
    
         
            +
                      limit: options[:limit],
         
     | 
| 
      
 163 
     | 
    
         
            +
                      offset: options[:offset],
         
     | 
| 
      
 164 
     | 
    
         
            +
                      asset: options[:asset],
         
     | 
| 
      
 165 
     | 
    
         
            +
                      destination: options[:destination],
         
     | 
| 
      
 166 
     | 
    
         
            +
                      tag: options[:tag]
         
     | 
| 
      
 167 
     | 
    
         
            +
                    }
         
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
      
 169 
     | 
    
         
            +
                    client.get path, **params
         
     | 
| 
      
 170 
     | 
    
         
            +
                  end
         
     | 
| 
      
 171 
     | 
    
         
            +
                end
         
     | 
| 
      
 172 
     | 
    
         
            +
              end
         
     | 
| 
      
 173 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,42 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module MixinBot
         
     | 
| 
      
 4 
     | 
    
         
            +
              class API
         
     | 
| 
      
 5 
     | 
    
         
            +
                module LegacyTransfer
         
     | 
| 
      
 6 
     | 
    
         
            +
                  TRANSFER_ARGUMENTS = %i[asset_id opponent_id amount].freeze
         
     | 
| 
      
 7 
     | 
    
         
            +
                  def create_transfer(pin, **kwargs)
         
     | 
| 
      
 8 
     | 
    
         
            +
                    raise ArgumentError, "#{TRANSFER_ARGUMENTS.join(', ')} are needed for create transfer" unless TRANSFER_ARGUMENTS.all? { |param| kwargs.keys.include? param }
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                    asset_id = kwargs[:asset_id]
         
     | 
| 
      
 11 
     | 
    
         
            +
                    opponent_id = kwargs[:opponent_id]
         
     | 
| 
      
 12 
     | 
    
         
            +
                    amount = format('%.8f', kwargs[:amount].to_d.to_r).gsub(/\.?0+$/, '')
         
     | 
| 
      
 13 
     | 
    
         
            +
                    trace_id = kwargs[:trace_id] || SecureRandom.uuid
         
     | 
| 
      
 14 
     | 
    
         
            +
                    memo = kwargs[:memo] || ''
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                    payload = {
         
     | 
| 
      
 17 
     | 
    
         
            +
                      asset_id:,
         
     | 
| 
      
 18 
     | 
    
         
            +
                      opponent_id:,
         
     | 
| 
      
 19 
     | 
    
         
            +
                      amount:,
         
     | 
| 
      
 20 
     | 
    
         
            +
                      trace_id:,
         
     | 
| 
      
 21 
     | 
    
         
            +
                      memo:
         
     | 
| 
      
 22 
     | 
    
         
            +
                    }
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                    if pin.length > 6
         
     | 
| 
      
 25 
     | 
    
         
            +
                      pin_base64 = encrypt_tip_pin pin, 'TIP:TRANSFER:CREATE:', asset_id, opponent_id, amount, trace_id, memo
         
     | 
| 
      
 26 
     | 
    
         
            +
                      payload[:pin_base64] = pin_base64
         
     | 
| 
      
 27 
     | 
    
         
            +
                    else
         
     | 
| 
      
 28 
     | 
    
         
            +
                      encrypted_pin = encrypt_pin pin
         
     | 
| 
      
 29 
     | 
    
         
            +
                      payload[:pin] = encrypted_pin
         
     | 
| 
      
 30 
     | 
    
         
            +
                    end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                    path = '/transfers'
         
     | 
| 
      
 33 
     | 
    
         
            +
                    client.post path, **payload
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  def transfer(trace_id, access_token: nil)
         
     | 
| 
      
 37 
     | 
    
         
            +
                    path = format('/transfers/trace/%<trace_id>s', trace_id:)
         
     | 
| 
      
 38 
     | 
    
         
            +
                    client.get path, access_token:
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
              end
         
     | 
| 
      
 42 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/mixin_bot/api/me.rb
    CHANGED
    
    | 
         @@ -3,37 +3,33 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            module MixinBot
         
     | 
| 
       4 
4 
     | 
    
         
             
              class API
         
     | 
| 
       5 
5 
     | 
    
         
             
                module Me
         
     | 
| 
       6 
     | 
    
         
            -
                  # https://developers.mixin.one/api/beta-mixin-message/read-profile/
         
     | 
| 
       7 
6 
     | 
    
         
             
                  def me(access_token: nil)
         
     | 
| 
       8 
7 
     | 
    
         
             
                    path = '/me'
         
     | 
| 
       9 
     | 
    
         
            -
                     
     | 
| 
       10 
     | 
    
         
            -
                    authorization = format('Bearer %<access_token>s', access_token: access_token)
         
     | 
| 
       11 
     | 
    
         
            -
                    client.get(path, headers: { 'Authorization': authorization })
         
     | 
| 
      
 8 
     | 
    
         
            +
                    client.get path, access_token:
         
     | 
| 
       12 
9 
     | 
    
         
             
                  end
         
     | 
| 
       13 
     | 
    
         
            -
                  alias read_me me
         
     | 
| 
       14 
10 
     | 
    
         | 
| 
       15 
     | 
    
         
            -
                  # https://developers.mixin.one/api/beta-mixin-message/update-profile/
         
     | 
| 
       16 
11 
     | 
    
         
             
                  # avatar_base64:
         
     | 
| 
       17 
     | 
    
         
            -
                  # 
     | 
| 
       18 
     | 
    
         
            -
                  def update_me( 
     | 
| 
      
 12 
     | 
    
         
            +
                  #   String: Base64 of image, supports format png, jpeg and gif, base64 image size > 1024.
         
     | 
| 
      
 13 
     | 
    
         
            +
                  def update_me(**kwargs)
         
     | 
| 
       19 
14 
     | 
    
         
             
                    path = '/me'
         
     | 
| 
       20 
15 
     | 
    
         
             
                    payload = {
         
     | 
| 
       21 
     | 
    
         
            -
                      full_name: full_name,
         
     | 
| 
       22 
     | 
    
         
            -
                      avatar_base64: avatar_base64
         
     | 
| 
      
 16 
     | 
    
         
            +
                      full_name: kwargs[:full_name],
         
     | 
| 
      
 17 
     | 
    
         
            +
                      avatar_base64: kwargs[:avatar_base64],
         
     | 
| 
      
 18 
     | 
    
         
            +
                      access_token: kwargs[:access_token]
         
     | 
| 
       23 
19 
     | 
    
         
             
                    }
         
     | 
| 
       24 
     | 
    
         
            -
                     
     | 
| 
       25 
     | 
    
         
            -
                    authorization = format('Bearer %<access_token>s', access_token: access_token)
         
     | 
| 
       26 
     | 
    
         
            -
                    client.post(path, headers: { 'Authorization': authorization }, json: payload)
         
     | 
| 
      
 20 
     | 
    
         
            +
                    client.post path, **payload
         
     | 
| 
       27 
21 
     | 
    
         
             
                  end
         
     | 
| 
       28 
22 
     | 
    
         | 
| 
       29 
23 
     | 
    
         
             
                  # https://developers.mixin.one/api/beta-mixin-message/friends/
         
     | 
| 
       30 
24 
     | 
    
         
             
                  def friends(access_token: nil)
         
     | 
| 
       31 
25 
     | 
    
         
             
                    path = '/friends'
         
     | 
| 
       32 
     | 
    
         
            -
                     
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
       34 
     | 
    
         
            -
             
     | 
| 
      
 26 
     | 
    
         
            +
                    client.get path, access_token:
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  def safe_me(access_token: nil)
         
     | 
| 
      
 30 
     | 
    
         
            +
                    path = '/safe/me'
         
     | 
| 
      
 31 
     | 
    
         
            +
                    client.get path, access_token:
         
     | 
| 
       35 
32 
     | 
    
         
             
                  end
         
     | 
| 
       36 
     | 
    
         
            -
                  alias read_friends friends
         
     | 
| 
       37 
33 
     | 
    
         
             
                end
         
     | 
| 
       38 
34 
     | 
    
         
             
              end
         
     | 
| 
       39 
35 
     | 
    
         
             
            end
         
     |