Class: Glueby::Contract::Token

Inherits:
Object
  • Object
show all
Defined in:
lib/glueby/contract/token.rb

Overview

This class represents custom token issued by application user. Application users can

  • issue their own tokens.

  • send to other users.

  • make the tokens disable.

Examples:

alice = Glueby::Wallet.create bob = Glueby::Wallet.create

Use ‘Glueby::Internal::Wallet#receive_address` to generate the address of bob bob.internal_wallet.receive_address

> ‘1CY6TSSARn8rAFD9chCghX5B7j4PKR8S1a’

Issue token = Token.issue!(issuer: alice, amount: 100) token.amount(wallet: alice)

> 100

Send token.transfer!(sender: alice, receiver_address: ‘1CY6TSSARn8rAFD9chCghX5B7j4PKR8S1a’, amount: 1) token.amount(wallet: alice)

> 99

token.amount(wallet: bob)

> 1

Burn token.burn!(sender: alice, amount: 10) token.amount(wallet: alice)

> 89

token.burn!(sender: alice) token.amount(wallet: alice)

> 0

Reissue token.reissue!(issuer: alice, amount: 100) token.amount(wallet: alice)

> 100

Issue with metadata / Get metadata token = Token.issue!(issuer: alice, amount: 100, metadata: ‘metadata’) token.metadata

> “metadata”

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(color_id:) ⇒ Token

Generate Token Instance

Parameters:

  • color_id (String)


439
440
441
# File 'lib/glueby/contract/token.rb', line 439

def initialize(color_id:)
  @color_id = color_id
end

Instance Attribute Details

#color_idObject (readonly)

Returns the value of attribute color_id.



230
231
232
# File 'lib/glueby/contract/token.rb', line 230

def color_id
  @color_id
end

Class Method Details

.issue!(issuer:, token_type: Tapyrus::Color::TokenTypes::REISSUABLE, amount: 1, split: 1, fee_estimator: FeeEstimator::Fixed.new, metadata: nil) ⇒ Array<token, Array<tx>>

Issue new token with specified amount and token type. REISSUABLE token can be reissued with #reissue! method, and NON_REISSUABLE and NFT token can not. Amount is set to 1 when the token type is NFT

Parameters:

  • issuer (Glueby::Wallet)
  • token_type (TokenTypes) (defaults to: Tapyrus::Color::TokenTypes::REISSUABLE)
  • amount (Integer) (defaults to: 1)
  • split (Integer) (defaults to: 1)

    The tx outputs should be split by specified number.

  • fee_estimator (Glueby::Contract::FeeEstimator) (defaults to: FeeEstimator::Fixed.new)
  • metadata (String) (defaults to: nil)

    The data to be stored in blockchain.

Returns:

  • (Array<token, Array<tx>>)

    Tuple of tx array and token object

Raises:

  • (InsufficientFunds)

    if wallet does not have enough TPC to send transaction.

  • (InvalidAmount)

    if amount is not positive integer.

  • (InvalidSplit)

    if split is greater than 1 for NFT token.

  • (UnsupportedTokenType)

    if token is not supported.



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/glueby/contract/token.rb', line 68

def issue!(issuer:, token_type: Tapyrus::Color::TokenTypes::REISSUABLE, amount: 1, split: 1, fee_estimator: FeeEstimator::Fixed.new, metadata: nil)
  raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
  raise Glueby::Contract::Errors::InvalidSplit if token_type == Tapyrus::Color::TokenTypes::NFT && split > 1

  txs, color_id = case token_type
                 when Tapyrus::Color::TokenTypes::REISSUABLE
                   issue_reissuable_token(issuer: issuer, amount: amount, split: split, fee_estimator: fee_estimator, metadata: )
                 when Tapyrus::Color::TokenTypes::NON_REISSUABLE
                   issue_non_reissuable_token(issuer: issuer, amount: amount, split: split, fee_estimator: fee_estimator, metadata: )
                 when Tapyrus::Color::TokenTypes::NFT
                   issue_nft_token(issuer: issuer, metadata: )
                 else
                   raise Glueby::Contract::Errors::UnsupportedTokenType
                 end
  [new(color_id: color_id), txs]
end

.only_finalized?Boolean

Returns:

  • (Boolean)


85
86
87
# File 'lib/glueby/contract/token.rb', line 85

def only_finalized?
  Glueby::AR::SystemInformation.use_only_finalized_utxo?
end

.parse_from_payload(payload) ⇒ Glueby::Contract::Token

Restore token from payload

Parameters:

  • payload (String)

Returns:



426
427
428
429
430
431
432
433
434
435
# File 'lib/glueby/contract/token.rb', line 426

def self.parse_from_payload(payload)
  color_id, script_pubkey = payload.unpack('a33a*')
  color_id = Tapyrus::Color::ColorIdentifier.parse_from_payload(color_id)
  if color_id.type == Tapyrus::Color::TokenTypes::REISSUABLE
    raise ArgumentError, 'script_pubkey should not be empty' if script_pubkey.empty?
    script_pubkey = Tapyrus::Script.parse_from_payload(script_pubkey)
    Glueby::Contract::AR::ReissuableToken.create!(color_id: color_id.to_hex, script_pubkey: script_pubkey.to_hex)
  end
  new(color_id: color_id)
end

Instance Method Details

#amount(wallet:) ⇒ Integer

Return balance of token in the specified wallet.

Parameters:

Returns:

  • (Integer)

    amount of utxo value associated with this token.



370
371
372
373
374
375
# File 'lib/glueby/contract/token.rb', line 370

def amount(wallet:)
  amount, _utxos = wallet
                     .internal_wallet
                     .collect_colored_outputs(color_id, nil, nil, only_finalized?)
  amount
end

#burn!(sender:, amount: 0, fee_estimator: FeeEstimator::Fixed.new) ⇒ Object

Burn token If amount is not specified or 0, burn all token associated with the wallet.

Parameters:

Raises:

  • (InsufficientFunds)

    if wallet does not have enough TPC to send transaction.

  • (InsufficientTokens)

    if wallet does not have enough token to send transaction.

  • (InvalidAmount)

    if amount is not positive integer.



345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
# File 'lib/glueby/contract/token.rb', line 345

def burn!(sender:, amount: 0, fee_estimator: FeeEstimator::Fixed.new)
  raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
  balance = sender.balances(only_finalized?)[color_id.to_hex]
  raise Glueby::Contract::Errors::InsufficientTokens unless balance
  raise Glueby::Contract::Errors::InsufficientTokens if balance < amount

  builder = Internal::ContractBuilder
          .new(
            sender_wallet: sender.internal_wallet,
            fee_estimator: fee_estimator,
            use_unfinalized_utxo: !only_finalized?,
            use_auto_fulfill_inputs: true
          )
          .burn(amount, color_id)
          .change_address(sender.internal_wallet.receive_address, color_id)

  Glueby::AR.transaction(isolation: :read_committed) do
    tx = builder.build
    sender.internal_wallet.broadcast(tx)
  end
end

#metadataString

Return metadata for this token

Returns:

  • (String)

    metadata. if token is not associated with metadata, return nil



379
380
381
# File 'lib/glueby/contract/token.rb', line 379

def 
  &.
end

#multi_transfer!(sender:, receivers:, fee_estimator: FeeEstimator::Fixed.new) ⇒ Array<String, tx>

Send the tokens to multiple wallets

Parameters:

  • sender (Glueby::Wallet)

    wallet to send this token

  • receivers (Array<Hash>)

    array of hash, which keys are :address and :amount

  • fee_estimator (Glueby::Contract::FeeEstimator) (defaults to: FeeEstimator::Fixed.new)

Returns:

  • (Array<String, tx>)

    Tuple of color_id and tx object

Raises:

  • (InsufficientFunds)

    if wallet does not have enough TPC to send transaction.

  • (InsufficientTokens)

    if wallet does not have enough token to send.

  • (InvalidAmount)

    if amount is not positive integer.



311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
# File 'lib/glueby/contract/token.rb', line 311

def multi_transfer!(sender:, receivers:, fee_estimator: FeeEstimator::Fixed.new)
  receivers.each do |r|
    raise Glueby::Contract::Errors::InvalidAmount unless r[:amount].positive?
  end

  txb = Internal::ContractBuilder
          .new(
            sender_wallet: sender.internal_wallet,
            fee_estimator: fee_estimator,
            use_unfinalized_utxo: !only_finalized?,
            use_auto_fulfill_inputs: true
          )
          .change_address(sender.internal_wallet.receive_address, color_id)

  receivers.each do |r|
    txb.pay(r[:address], r[:amount].to_i, color_id)
  end

  tx = nil
  Glueby::AR.transaction(isolation: :read_committed) do
    tx = sender.internal_wallet.broadcast(txb.build)
  end
  [color_id, tx]
end

#p2c_addressString

Return pay to contract address

Returns:

  • (String)

    p2c_address. if token is not associated with metadata, return nil



385
386
387
# File 'lib/glueby/contract/token.rb', line 385

def p2c_address
  &.p2c_address
end

#payment_baseString

Return public key used to generate pay to contract address

Returns:

  • (String)

    payment_base. if token is not associated with metadata, return nil



391
392
393
# File 'lib/glueby/contract/token.rb', line 391

def payment_base
  &.payment_base
end

#reissue!(issuer:, amount:, split: 1, fee_estimator: FeeEstimator::Fixed.new) ⇒ Array<String, tx>

Re-issue the token with specified amount. A wallet can issue the token only when it is REISSUABLE token.

Parameters:

Returns:

  • (Array<String, tx>)

    Tuple of color_id and tx object

Raises:

  • (InsufficientFunds)

    if wallet does not have enough TPC to send transaction.

  • (InvalidAmount)

    if amount is not positive integer.

  • (InvalidTokenType)

    if token is not reissuable.

  • (UnknownScriptPubkey)

    when token is reissuable but it doesn’t know script pubkey to issue token.



243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
# File 'lib/glueby/contract/token.rb', line 243

def reissue!(issuer:, amount:, split: 1, fee_estimator: FeeEstimator::Fixed.new)
  raise Glueby::Contract::Errors::InvalidAmount unless amount.positive?
  raise Glueby::Contract::Errors::InvalidTokenType unless token_type == Tapyrus::Color::TokenTypes::REISSUABLE

   = Glueby::Contract::AR::TokenMetadata.find_by(color_id: color_id.to_hex)
  raise Glueby::Contract::Errors::UnknownScriptPubkey unless valid_reissuer?(wallet: issuer, token_metadata: )

  txb = Internal::ContractBuilder.new(
    sender_wallet: issuer.internal_wallet,
    fee_estimator: fee_estimator,
    use_auto_fulfill_inputs: true
  )

  if 
    txb.add_p2c_utxo_to!(
      metadata: ,
      amount: FeeEstimator::Fixed.new.fixed_fee,
      p2c_address: .p2c_address,
      payment_base: .payment_base,
      only_finalized: only_finalized?,
      fee_estimator: Contract::FeeEstimator::Fixed.new
    )
  else
    txb.add_utxo_to!(
      address: @script_pubkey.to_addr,
      amount: FeeEstimator::Fixed.new.fixed_fee,
      only_finalized: only_finalized?,
      fee_estimator: Contract::FeeEstimator::Fixed.new
    )
  end

  tx = txb.reissuable_split(@script_pubkey, issuer.internal_wallet.receive_address, amount, split)
          .build

  tx = issuer.internal_wallet.broadcast(tx)

  [color_id, tx]
end

#script_pubkeyString

Return the script_pubkey of the token from ActiveRecord

Returns:

  • (String)

    script_pubkey



410
411
412
# File 'lib/glueby/contract/token.rb', line 410

def script_pubkey
  @script_pubkey ||= Glueby::Contract::AR::ReissuableToken.script_pubkey(@color_id.to_hex)
end

#to_payloadString

Return serialized payload

Returns:

  • (String)

    payload



416
417
418
419
420
421
# File 'lib/glueby/contract/token.rb', line 416

def to_payload
  payload = +''
  payload << @color_id.to_payload
  payload << @script_pubkey.to_payload if script_pubkey
  payload
end

#token_metadataGlueby::Contract::AR::TokenMetadata

Return Glueby::Contract::AR::TokenMetadata instance for this token



397
398
399
400
# File 'lib/glueby/contract/token.rb', line 397

def 
  return @token_metadata if defined? @token_metadata
  @token_metadata = Glueby::Contract::AR::TokenMetadata.find_by(color_id: color_id.to_hex)
end

#token_typeTapyrus::Color::TokenTypes

Return token type

Returns:

  • (Tapyrus::Color::TokenTypes)


404
405
406
# File 'lib/glueby/contract/token.rb', line 404

def token_type
  color_id.type
end

#transfer!(sender:, receiver_address:, amount: 1, fee_estimator: FeeEstimator::Fixed.new) ⇒ Array<String, tx>

Send the token to other wallet

Parameters:

  • sender (Glueby::Wallet)

    wallet to send this token

  • receiver_address (String)

    address to receive this token

  • amount (Integer) (defaults to: 1)
  • fee_estimator (Glueby::Contract::FeeEstimator) (defaults to: FeeEstimator::Fixed.new)

Returns:

  • (Array<String, tx>)

    Tuple of color_id and tx object

Raises:

  • (InsufficientFunds)

    if wallet does not have enough TPC to send transaction.

  • (InsufficientTokens)

    if wallet does not have enough token to send.

  • (InvalidAmount)

    if amount is not positive integer.



292
293
294
295
296
297
298
299
300
# File 'lib/glueby/contract/token.rb', line 292

def transfer!(sender:, receiver_address:, amount: 1, fee_estimator: FeeEstimator::Fixed.new)
  multi_transfer!(
    sender: sender,
    receivers: [{
      address: receiver_address,
      amount: amount
    }],
    fee_estimator: fee_estimator)
end