module Glueby::Contract::TxBuilder

Constants

FUNDING_TX_AMOUNT

The amount of output in funding tx for Reissuable token.

Public Instance Methods

collect_colored_outputs(results, color_id, amount = 0) click to toggle source

Returns the set of utxos that satisfies the specified amount and has the specified color_id. if amount is not specified or 0, return all utxos with color_id @param results [Array] response of Glueby::Internal::Wallet#list_unspent @param color_id [Tapyrus::Color::ColorIdentifier] color identifier @param amount [Integer]

# File lib/glueby/contract/tx_builder.rb, line 173
def collect_colored_outputs(results, color_id, amount = 0)
  results = results.inject([0, []]) do |sum, output|
    next sum unless output[:color_id] == color_id.to_hex

    new_sum = sum[0] + output[:amount]
    new_outputs = sum[1] << output
    return [new_sum, new_outputs] if new_sum >= amount && amount.positive?

    [new_sum, new_outputs]
  end
  raise Glueby::Contract::Errors::InsufficientTokens if amount.positive?

  results
end
create_burn_tx(color_id:, sender:, amount: 0, fee_estimator: FixedFeeEstimator.new) click to toggle source
# File lib/glueby/contract/tx_builder.rb, line 127
def create_burn_tx(color_id:, sender:, amount: 0, fee_estimator: FixedFeeEstimator.new)
  tx = Tapyrus::Tx.new

  utxos = sender.internal_wallet.list_unspent
  sum_token, outputs = collect_colored_outputs(utxos, color_id, amount)
  fill_input(tx, outputs)

  fill_change_token(tx, sender, sum_token - amount, color_id) if amount.positive?

  fee = fee_estimator.fee(dummy_tx(tx))

  dust = 600 # in case that the wallet has output which has just fee amount.
  sum_tpc, outputs = sender.internal_wallet.collect_uncolored_outputs(fee + dust)
  fill_input(tx, outputs)

  fill_change_tpc(tx, sender, sum_tpc - fee)
  sender.internal_wallet.sign_tx(tx)
end
create_funding_tx(wallet:, script: nil, fee_estimator: FixedFeeEstimator.new) click to toggle source

Create new public key, and new transaction that sends TPC to it

# File lib/glueby/contract/tx_builder.rb, line 14
def create_funding_tx(wallet:, script: nil, fee_estimator: FixedFeeEstimator.new)
  tx = Tapyrus::Tx.new
  fee = fee_estimator.fee(dummy_tx(tx))

  sum, outputs = wallet.internal_wallet.collect_uncolored_outputs(fee + FUNDING_TX_AMOUNT)
  fill_input(tx, outputs)

  receiver_script = script ? script : Tapyrus::Script.parse_from_addr(wallet.internal_wallet.receive_address)
  tx.outputs << Tapyrus::TxOut.new(value: FUNDING_TX_AMOUNT, script_pubkey: receiver_script)

  fill_change_tpc(tx, wallet, sum - fee - FUNDING_TX_AMOUNT)
  wallet.internal_wallet.sign_tx(tx)
end
create_issue_tx_for_nft_token(issuer:, fee_estimator: FixedFeeEstimator.new) click to toggle source
# File lib/glueby/contract/tx_builder.rb, line 55
def create_issue_tx_for_nft_token(issuer:, fee_estimator: FixedFeeEstimator.new)
  create_issue_tx_from_out_point(token_type: Tapyrus::Color::TokenTypes::NFT, issuer: issuer, amount: 1, fee_estimator: fee_estimator)
end
create_issue_tx_for_non_reissuable_token(issuer:, amount:, fee_estimator: FixedFeeEstimator.new) click to toggle source
# File lib/glueby/contract/tx_builder.rb, line 51
def create_issue_tx_for_non_reissuable_token(issuer:, amount:, fee_estimator: FixedFeeEstimator.new)
  create_issue_tx_from_out_point(token_type: Tapyrus::Color::TokenTypes::NON_REISSUABLE, issuer: issuer, amount: amount, fee_estimator: fee_estimator)
end
create_issue_tx_for_reissuable_token(funding_tx:, issuer:, amount:, fee_estimator: FixedFeeEstimator.new) click to toggle source
# File lib/glueby/contract/tx_builder.rb, line 28
def create_issue_tx_for_reissuable_token(funding_tx:, issuer:, amount:, fee_estimator: FixedFeeEstimator.new)
  tx = Tapyrus::Tx.new

  out_point = Tapyrus::OutPoint.from_txid(funding_tx.txid, 0)
  tx.inputs << Tapyrus::TxIn.new(out_point: out_point)
  output = funding_tx.outputs.first

  receiver_script = Tapyrus::Script.parse_from_payload(output.script_pubkey.to_payload)
  color_id = Tapyrus::Color::ColorIdentifier.reissuable(receiver_script)
  receiver_colored_script = receiver_script.add_color(color_id)
  tx.outputs << Tapyrus::TxOut.new(value: amount, script_pubkey: receiver_colored_script)

  fee = fee_estimator.fee(dummy_tx(tx))
  fill_change_tpc(tx, issuer, output.value - fee)
  prev_txs = [{
    txid: funding_tx.txid,
    vout: 0,
    scriptPubKey: output.script_pubkey.to_hex,
    amount: output.value
  }]
  issuer.internal_wallet.sign_tx(tx, prev_txs)
end
create_issue_tx_from_out_point(token_type:, issuer:, amount:, fee_estimator: FixedFeeEstimator.new) click to toggle source
# File lib/glueby/contract/tx_builder.rb, line 59
def create_issue_tx_from_out_point(token_type:, issuer:, amount:, fee_estimator: FixedFeeEstimator.new)
  tx = Tapyrus::Tx.new

  fee = fee_estimator.fee(dummy_issue_tx_from_out_point)
  sum, outputs = issuer.internal_wallet.collect_uncolored_outputs(fee)
  fill_input(tx, outputs)

  out_point = tx.inputs.first.out_point
  color_id = case token_type
  when Tapyrus::Color::TokenTypes::NON_REISSUABLE
    Tapyrus::Color::ColorIdentifier.non_reissuable(out_point)
  when Tapyrus::Color::TokenTypes::NFT
    Tapyrus::Color::ColorIdentifier.nft(out_point)
  else
    raise Glueby::Contract::Errors::UnsupportedTokenType  
  end

  receiver_script = Tapyrus::Script.parse_from_addr(issuer.internal_wallet.receive_address)
  receiver_colored_script = receiver_script.add_color(color_id)
  tx.outputs << Tapyrus::TxOut.new(value: amount, script_pubkey: receiver_colored_script)

  fill_change_tpc(tx, issuer, sum - fee)
  issuer.internal_wallet.sign_tx(tx)
end
create_reissue_tx(funding_tx:, issuer:, amount:, color_id:, fee_estimator: FixedFeeEstimator.new) click to toggle source
# File lib/glueby/contract/tx_builder.rb, line 84
def create_reissue_tx(funding_tx:, issuer:, amount:, color_id:, fee_estimator: FixedFeeEstimator.new)
  tx = Tapyrus::Tx.new

  out_point = Tapyrus::OutPoint.from_txid(funding_tx.txid, 0)
  tx.inputs << Tapyrus::TxIn.new(out_point: out_point)
  output = funding_tx.outputs.first

  receiver_script = Tapyrus::Script.parse_from_payload(output.script_pubkey.to_payload)
  receiver_colored_script = receiver_script.add_color(color_id)
  tx.outputs << Tapyrus::TxOut.new(value: amount, script_pubkey: receiver_colored_script)

  fee = fee_estimator.fee(dummy_tx(tx))
  fill_change_tpc(tx, issuer, output.value - fee)
  prev_txs = [{
    txid: funding_tx.txid,
    vout: 0,
    scriptPubKey: output.script_pubkey.to_hex,
    amount: output.value
  }]
  issuer.internal_wallet.sign_tx(tx, prev_txs)
end
create_transfer_tx(color_id:, sender:, receiver_address:, amount:, fee_estimator: FixedFeeEstimator.new) click to toggle source
# File lib/glueby/contract/tx_builder.rb, line 106
def create_transfer_tx(color_id:, sender:, receiver_address:, amount:, fee_estimator: FixedFeeEstimator.new)
  tx = Tapyrus::Tx.new

  utxos = sender.internal_wallet.list_unspent
  sum_token, outputs = collect_colored_outputs(utxos, color_id, amount)
  fill_input(tx, outputs)

  receiver_script = Tapyrus::Script.parse_from_addr(receiver_address)
  receiver_colored_script = receiver_script.add_color(color_id)
  tx.outputs << Tapyrus::TxOut.new(value: amount, script_pubkey: receiver_colored_script)

  fill_change_token(tx, sender, sum_token - amount, color_id)

  fee = fee_estimator.fee(dummy_tx(tx))
  sum_tpc, outputs = sender.internal_wallet.collect_uncolored_outputs(fee)
  fill_input(tx, outputs)

  fill_change_tpc(tx, sender, sum_tpc - fee)
  sender.internal_wallet.sign_tx(tx)
end
dummy_issue_tx_from_out_point() click to toggle source

Add dummy inputs and outputs to tx for issue non-reissuable transaction and nft transaction

# File lib/glueby/contract/tx_builder.rb, line 208
def dummy_issue_tx_from_out_point
  tx = Tapyrus::Tx.new
  receiver_colored_script = Tapyrus::Script.parse_from_payload('21c20000000000000000000000000000000000000000000000000000000000000000bc76a914000000000000000000000000000000000000000088ac'.htb)
  tx.outputs << Tapyrus::TxOut.new(value: 0, script_pubkey: receiver_colored_script)
  dummy_tx(tx)
end
dummy_tx(tx) click to toggle source

Add dummy inputs and outputs to tx

# File lib/glueby/contract/tx_builder.rb, line 189
def dummy_tx(tx)
  dummy = Tapyrus::Tx.parse_from_payload(tx.to_payload)

  # dummy input for tpc
  out_point = Tapyrus::OutPoint.new('00' * 32, 0)
  dummy.inputs << Tapyrus::TxIn.new(out_point: out_point)

  # Add script_sig to all intpus
  dummy.inputs.each do |input|
    input.script_sig = Tapyrus::Script.parse_from_payload('000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'.htb)
  end

  # dummy output to return change
  change_script = Tapyrus::Script.to_p2pkh('0000000000000000000000000000000000000000')
  dummy.outputs << Tapyrus::TxOut.new(value: 0, script_pubkey: change_script)
  dummy
end
fill_change_token(tx, wallet, change, color_id) click to toggle source
# File lib/glueby/contract/tx_builder.rb, line 160
def fill_change_token(tx, wallet, change, color_id)
  return unless change.positive?

  change_script = Tapyrus::Script.parse_from_addr(wallet.internal_wallet.change_address)
  change_colored_script = change_script.add_color(color_id)
  tx.outputs << Tapyrus::TxOut.new(value: change, script_pubkey: change_colored_script)
end
fill_change_tpc(tx, wallet, change) click to toggle source
# File lib/glueby/contract/tx_builder.rb, line 153
def fill_change_tpc(tx, wallet, change)
  return unless change.positive?

  change_script = Tapyrus::Script.parse_from_addr(wallet.internal_wallet.change_address)
  tx.outputs << Tapyrus::TxOut.new(value: change, script_pubkey: change_script)
end
fill_input(tx, outputs) click to toggle source
# File lib/glueby/contract/tx_builder.rb, line 146
def fill_input(tx, outputs)
  outputs.each do |output|
    out_point = Tapyrus::OutPoint.new(output[:txid].rhex, output[:vout])
    tx.inputs << Tapyrus::TxIn.new(out_point: out_point)
  end
end
receive_address(wallet:) click to toggle source
# File lib/glueby/contract/tx_builder.rb, line 9
def receive_address(wallet:)
  wallet.receive_address
end