Class: Coinbase::Transfer

Inherits:
Object
  • Object
show all
Defined in:
lib/coinbase/transfer.rb

Overview

A representation of a Transfer, which moves an amount of an Asset from a user-controlled Wallet to another address. The fee is assumed to be paid in the native Asset of the Network. Currently only ETH transfers are supported. Transfers should be created through Wallet#transfer or Address#transfer.

Defined Under Namespace

Modules: Status

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(network_id, wallet_id, from_address_id, amount, asset_id, to_address_id, client: Jimson::Client.new(ENV.fetch('BASE_SEPOLIA_RPC_URL', nil))) ⇒ Transfer

Returns a new Transfer object.

Parameters:

  • network_id (Symbol)

    The ID of the Network on which the Transfer originated

  • wallet_id (String)

    The ID of the Wallet from which the Transfer originated

  • from_address_id (String)

    The ID of the address from which the Transfer originated

  • amount (Integer, Float, BigDecimal)

    The amount of the Asset to send. Integers are interpreted as the smallest denomination of the Asset (e.g. Wei for Ether). Floats and BigDecimals are interpreted as the Asset itself (e.g. Ether).

  • asset_id (Symbol)

    The ID of the Asset being transferred. Currently only ETH is supported.

  • to_address_id (String)

    The address to which the Transfer is being sent

  • client (Jimson::Client) (defaults to: Jimson::Client.new(ENV.fetch('BASE_SEPOLIA_RPC_URL', nil)))

    (Optional) The JSON RPC client to use for interacting with the Network

Raises:

  • (ArgumentError)


42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/coinbase/transfer.rb', line 42

def initialize(network_id, wallet_id, from_address_id, amount, asset_id, to_address_id,
               client: Jimson::Client.new(ENV.fetch('BASE_SEPOLIA_RPC_URL', nil)))

  raise ArgumentError, "Unsupported asset: #{asset_id}" if asset_id != :eth

  @network_id = network_id
  @wallet_id = wallet_id
  @from_address_id = from_address_id
  @amount = normalize_eth_amount(amount)
  @asset_id = asset_id
  @to_address_id = to_address_id
  @client = client
end

Instance Attribute Details

#amountObject (readonly)

Returns the value of attribute amount.



13
14
15
# File 'lib/coinbase/transfer.rb', line 13

def amount
  @amount
end

#asset_idObject (readonly)

Returns the value of attribute asset_id.



13
14
15
# File 'lib/coinbase/transfer.rb', line 13

def asset_id
  @asset_id
end

#from_address_idObject (readonly)

Returns the value of attribute from_address_id.



13
14
15
# File 'lib/coinbase/transfer.rb', line 13

def from_address_id
  @from_address_id
end

#network_idObject (readonly)

Returns the value of attribute network_id.



13
14
15
# File 'lib/coinbase/transfer.rb', line 13

def network_id
  @network_id
end

#to_address_idObject (readonly)

Returns the value of attribute to_address_id.



13
14
15
# File 'lib/coinbase/transfer.rb', line 13

def to_address_id
  @to_address_id
end

#wallet_idObject (readonly)

Returns the value of attribute wallet_id.



13
14
15
# File 'lib/coinbase/transfer.rb', line 13

def wallet_id
  @wallet_id
end

Instance Method Details

#statusSymbol

Returns the status of the Transfer.

Returns:

  • (Symbol)

    The status



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/coinbase/transfer.rb', line 81

def status
  begin
    # Create the transaction, and attempt to get the hash to see if it has been signed.
    transaction.hash
  rescue Eth::Signature::SignatureError
    # If the transaction has not been signed, it is still pending.
    return Status::PENDING
  end

  onchain_transaction = @client.eth_getTransactionByHash(transaction_hash)

  if onchain_transaction.nil?
    # If the transaction has not been broadcast, it is still pending.
    Status::PENDING
  elsif onchain_transaction['blockHash'].nil?
    # If the transaction has been broadcast but hasn't been included in a block, it is
    # broadcast.
    Status::BROADCAST
  else
    transaction_receipt = @client.eth_getTransactionReceipt(transaction_hash)

    if transaction_receipt['status'].to_i(16) == 1
      Status::COMPLETE
    else
      Status::FAILED
    end
  end
end

#transactionEth::Tx::Eip1559

Returns the underlying Transfer transaction, creating it if it has not been yet.

Returns:

  • (Eth::Tx::Eip1559)

    The Transfer transaction



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/coinbase/transfer.rb', line 58

def transaction
  return @transaction unless @transaction.nil?

  nonce = @client.eth_getTransactionCount(@from_address_id.to_s, 'latest').to_i(16)
  gas_price = @client.eth_gasPrice.to_i(16)

  params = {
    chain_id: BASE_SEPOLIA.chain_id, # TODO: Don't hardcode Base Sepolia.
    nonce: nonce,
    priority_fee: gas_price, # TODO: Optimize this.
    max_gas_fee: gas_price,
    gas_limit: 21_000, # TODO: Handle multiple currencies.
    from: Eth::Address.new(@from_address_id),
    to: Eth::Address.new(@to_address_id),
    value: (@amount * Coinbase::WEI_PER_ETHER).to_i
  }

  @transaction = Eth::Tx::Eip1559.new(Eth::Tx.validate_eip1559_params(params))
  @transaction
end

#transaction_hashString

Returns the transaction hash of the Transfer, or nil if not yet available.

Returns:

  • (String)

    The transaction hash



131
132
133
134
135
# File 'lib/coinbase/transfer.rb', line 131

def transaction_hash
  "0x#{transaction.hash}"
rescue Eth::Signature::SignatureError
  nil
end

#wait!(interval_seconds = 0.2, timeout_seconds = 10) ⇒ Transfer

Waits until the Transfer is completed or failed by polling the Network at the given interval. Raises a Timeout::Error if the Transfer takes longer than the given timeout.

Parameters:

  • interval_seconds (Integer) (defaults to: 0.2)

    The interval at which to poll the Network, in seconds

  • timeout_seconds (Integer) (defaults to: 10)

    The maximum amount of time to wait for the Transfer to complete, in seconds

Returns:

  • (Transfer)

    The completed Transfer object



115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/coinbase/transfer.rb', line 115

def wait!(interval_seconds = 0.2, timeout_seconds = 10)
  start_time = Time.now

  loop do
    return self if status == Status::COMPLETE || status == Status::FAILED

    raise Timeout::Error, 'Transfer timed out' if Time.now - start_time > timeout_seconds

    self.sleep interval_seconds
  end

  self
end