Class: Coinbase::SmartContract

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

Overview

A representation of a SmartContract on the blockchain. rubocop:disable Metrics/ClassLength

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(model) ⇒ SmartContract

Returns a new SmartContract object.

Parameters:



313
314
315
316
317
# File 'lib/coinbase/smart_contract.rb', line 313

def initialize(model)
  raise unless model.is_a?(Coinbase::Client::SmartContract)

  @model = model
end

Class Method Details

.create_multi_token_contract(address_id:, wallet_id:, uri:) ⇒ SmartContract

Creates a new ERC1155 multi-token contract, that can subsequently be deployed to the blockchain.

Parameters:

  • address_id (String)

    The address ID of deployer

  • wallet_id (String)

    The wallet ID of the deployer

  • uri (String)

    The URI for the token metadata

Returns:

  • (SmartContract)

    The new ERC1155 Multi-Token SmartContract object



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/coinbase/smart_contract.rb', line 116

def create_multi_token_contract(
  address_id:,
  wallet_id:,
  uri:
)
  contract = Coinbase.call_api do
    smart_contracts_api.create_smart_contract(
      wallet_id,
      address_id,
      {
        type: Coinbase::Client::SmartContractType::ERC1155,
        options: Coinbase::Client::MultiTokenContractOptions.new(
          uri: uri
        ).to_body
      }
    )
  end

  new(contract)
end

.create_nft_contract(address_id:, wallet_id:, name:, symbol:, base_uri:) ⇒ SmartContract

Creates a new ERC721 token contract, that can subsequently be deployed to the blockchain.

Parameters:

  • address_id (String)

    The address ID of deployer

  • wallet_id (String)

    The wallet ID of the deployer

  • name (String)

    The name of the token

  • symbol (String)

    The symbol of the token

  • base_uri (String)

    The base URI for the token metadata

Returns:



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/smart_contract.rb', line 85

def create_nft_contract(
  address_id:,
  wallet_id:,
  name:,
  symbol:,
  base_uri:
)
  contract = Coinbase.call_api do
    smart_contracts_api.create_smart_contract(
      wallet_id,
      address_id,
      {
        type: Coinbase::Client::SmartContractType::ERC721,
        options: Coinbase::Client::NFTContractOptions.new(
          name: name,
          symbol: symbol,
          base_uri: base_uri
        ).to_body
      }
    )
  end

  new(contract)
end

.create_token_contract(address_id:, wallet_id:, name:, symbol:, total_supply:) ⇒ SmartContract

Creates a new ERC20 token contract, that can subsequently be deployed to the blockchain.

Parameters:

  • address_id (String)

    The address ID of deployer

  • wallet_id (String)

    The wallet ID of the deployer

  • name (String)

    The name of the token

  • symbol (String)

    The symbol of the token

  • total_supply (String)

    The total supply of the token, denominate in whole units.

Returns:



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/coinbase/smart_contract.rb', line 52

def create_token_contract(
  address_id:,
  wallet_id:,
  name:,
  symbol:,
  total_supply:
)
  contract = Coinbase.call_api do
    smart_contracts_api.create_smart_contract(
      wallet_id,
      address_id,
      {
        type: Coinbase::Client::SmartContractType::ERC20,
        options: Coinbase::Client::TokenContractOptions.new(
          name: name,
          symbol: symbol,
          total_supply: BigDecimal(total_supply).to_i.to_s
        ).to_body
      }
    )
  end

  new(contract)
end

.listObject



200
201
202
203
204
205
206
207
208
# File 'lib/coinbase/smart_contract.rb', line 200

def list
  Coinbase::Pagination.enumerate(
    lambda { |page|
      smart_contracts_api.list_smart_contracts(page: page)
    }
  ) do |smart_contract|
    new(smart_contract)
  end
end

.list_events(network_id:, protocol_name:, contract_address:, contract_name:, event_name:, from_block_height:, to_block_height:) ⇒ Enumerable<Coinbase::ContractEvent>

Returns a list of ContractEvents for the provided network, contract, and event details.

Parameters:

  • network_id (Symbol)

    The network ID

  • protocol_name (String)

    The protocol name

  • contract_address (String)

    The contract address

  • contract_name (String)

    The contract name

  • event_name (String)

    The event name

  • from_block_height (Integer)

    The start block height

  • to_block_height (Integer)

    The end block height

Returns:



17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/coinbase/smart_contract.rb', line 17

def list_events(
  network_id:,
  protocol_name:,
  contract_address:,
  contract_name:,
  event_name:,
  from_block_height:,
  to_block_height:
)
  Coinbase::Pagination.enumerate(
    lambda { |page|
      list_events_page(
        network_id,
        protocol_name,
        contract_address,
        contract_name,
        event_name,
        from_block_height,
        to_block_height,
        page
      )
    }
  ) do |contract_event|
    Coinbase::ContractEvent.new(contract_event)
  end
end

.normalize_abi(abi) ⇒ Array<Hash>

Normalizes an ABI from a String or Array of Hashes to an Array of Hashes.

Parameters:

  • abi (String, Array)

    The ABI to normalize

Returns:

  • (Array<Hash>)

    The normalized ABI

Raises:

  • (ArgumentError)

    If the ABI is not a valid JSON string or Array



214
215
216
217
218
219
220
221
222
# File 'lib/coinbase/smart_contract.rb', line 214

def normalize_abi(abi)
  return abi if abi.is_a?(Array)

  raise ArgumentError, 'ABI must be an Array or a JSON string' unless abi.is_a?(String)

  JSON.parse(abi)
rescue JSON::ParserError
  raise ArgumentError, 'Invalid ABI JSON'
end

.read(contract_address:, method:, network: Coinbase.default_network, abi: nil, args: {}) ⇒ Object

Reads data from a deployed smart contract.

Parameters:

  • network (Coinbase::Network, Symbol) (defaults to: Coinbase.default_network)

    The Network or Network ID of the Asset

  • contract_address (String)

    The address of the deployed contract

  • method (String)

    The name of the method to call on the contract

  • abi (Array, nil) (defaults to: nil)

    The ABI of the contract. If nil, the method will attempt to use a cached ABI

  • args (Hash) (defaults to: {})

    The arguments to pass to the contract method. The keys should be the argument names, and the values should be the argument values.

Returns:

  • (Object)

    The result of the contract call, converted to an appropriate Ruby type

Raises:

  • (Coinbase::ApiError)

    If there’s an error in the API call



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/coinbase/smart_contract.rb', line 176

def read(
  contract_address:,
  method:,
  network: Coinbase.default_network,
  abi: nil,
  args: {}
)
  network = Coinbase::Network.from_id(network)

  response = Coinbase.call_api do
    smart_contracts_api.read_contract(
      network.normalized_id,
      contract_address,
      {
        method: method,
        args: (args || {}).to_json,
        abi: abi&.to_json
      }.compact
    )
  end

  convert_solidity_value(response)
end

.register(contract_address:, abi:, name: nil, network: Coinbase.default_network) ⇒ Object

Registers an externally deployed smart contract with the API.

Parameters:

  • contract_address (String)

    The address of the deployed contract

  • abi (Array, String)

    The ABI of the contract

  • network (Coinbase::Network, Symbol) (defaults to: Coinbase.default_network)

    The Network or Network ID the contract is deployed on

  • name (String, nil) (defaults to: nil)

    The optional name of the contract



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/coinbase/smart_contract.rb', line 142

def register(
  contract_address:,
  abi:,
  name: nil,
  network: Coinbase.default_network
)
  network = Coinbase::Network.from_id(network)

  normalized_abi = normalize_abi(abi)

  smart_contract = Coinbase.call_api do
    smart_contracts_api.register_smart_contract(
      network.normalized_id,
      contract_address,
      register_smart_contract_request: {
        abi: normalized_abi.to_json,
        contract_name: name
      }.compact
    )
  end

  new(smart_contract)
end

Instance Method Details

#abiArray<Hash>

Returns the ABI of the Smart Contract.

Returns:

  • (Array<Hash>)

    The ABI



354
355
356
# File 'lib/coinbase/smart_contract.rb', line 354

def abi
  JSON.parse(@model.abi)
end

#contract_addressString

Returns the contract address of the SmartContract.

Returns:

  • (String)

    The contract address



335
336
337
# File 'lib/coinbase/smart_contract.rb', line 335

def contract_address
  @model.contract_address
end

#deploy!SmartContract

Deploys the signed SmartContract to the blockchain.

Returns:

Raises:



430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
# File 'lib/coinbase/smart_contract.rb', line 430

def deploy!
  raise ManageExternalContractError, 'deploy' if external?
  raise TransactionNotSignedError unless transaction.signed?

  @model = Coinbase.call_api do
    smart_contracts_api.deploy_smart_contract(
      wallet_id,
      deployer_address,
      id,
      { signed_payload: transaction.signature }
    )
  end

  @transaction = Coinbase::Transaction.new(@model.transaction)

  self
end

#deployer_addressString?

Returns the address of the deployer of the SmartContract, if deployed via CDP. Returns nil for externally registered contracts.

Returns:

  • (String, nil)

    The deployer address



348
349
350
# File 'lib/coinbase/smart_contract.rb', line 348

def deployer_address
  @model.deployer_address
end

#external?Boolean

Returns whether the SmartContract is an externally registered contract or a CDP managed contract.

Returns:

  • (Boolean)

    Whether the SmartContract is external



380
381
382
# File 'lib/coinbase/smart_contract.rb', line 380

def external?
  @model.is_external
end

#idString

Returns the SmartContract ID. NOTE: This is not the contract address and is primarily used before the contract is deployed.

Returns:

  • (String)

    The SmartContract ID



323
324
325
# File 'lib/coinbase/smart_contract.rb', line 323

def id
  @model.smart_contract_id
end

#inspectString

Same as to_s.

Returns:

  • (String)

    a string representation of the SmartContract



490
491
492
# File 'lib/coinbase/smart_contract.rb', line 490

def inspect
  to_s
end

#nameString

Returns the name of the SmartContract.

Returns:

  • (String)

    The contract name



341
342
343
# File 'lib/coinbase/smart_contract.rb', line 341

def name
  @model.contract_name
end

#networkCoinbase::Network

Returns the Network of the SmartContract.

Returns:



329
330
331
# File 'lib/coinbase/smart_contract.rb', line 329

def network
  @network ||= Coinbase::Network.from_id(@model.network_id)
end

#optionsCoinbase::Client::SmartContractOptions?

Returns the options of the SmartContract, if deployed via CDP. Returns nil for externally registered contracts.

Returns:



374
375
376
# File 'lib/coinbase/smart_contract.rb', line 374

def options
  @model.options
end

#reloadSmartContract

Reloads the Smart Contract model with the latest version from the server side.

Returns:

  • (SmartContract)

    The most recent version of Smart Contract from the server

Raises:



450
451
452
453
454
455
456
457
458
459
460
# File 'lib/coinbase/smart_contract.rb', line 450

def reload
  raise ManageExternalContractError, 'reload' if external?

  @model = Coinbase.call_api do
    smart_contracts_api.get_smart_contract(wallet_id, deployer_address, id)
  end

  @transaction = Coinbase::Transaction.new(@model.transaction) if @model.transaction

  self
end

#sign(key) ⇒ SmartContract

Signs the SmartContract deployment transaction with the given key. This is required before broadcasting the SmartContract.

Parameters:

  • key (Eth::Key)

    The key to sign the SmartContract with

Returns:

Raises:

  • (RuntimeError)

    If the key is not an Eth::Key

  • (RuntimeError)

    If the SmartContract is external

  • (Coinbase::AlreadySignedError)

    If the SmartContract has already been signed



419
420
421
422
423
424
# File 'lib/coinbase/smart_contract.rb', line 419

def sign(key)
  raise ManageExternalContractError, 'sign' if external?
  raise unless key.is_a?(Eth::Key)

  transaction.sign(key)
end

#statusString

Returns the status of the SmartContract, if deployed via CDP.

Returns:

  • (String)

    The status



392
393
394
# File 'lib/coinbase/smart_contract.rb', line 392

def status
  transaction&.status
end

#to_sString

Returns a string representation of the SmartContract.

Returns:

  • (String)

    a string representation of the SmartContract



496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
# File 'lib/coinbase/smart_contract.rb', line 496

def to_s
  Coinbase.pretty_print_object(
    self.class,
    **{
      network: network.id,
      contract_address: contract_address,
      type: type,
      name: name,
      # Fields only present for CDP managed contracts.
      status: status,
      deployer_address: deployer_address,
      options: options.nil? ? nil : Coinbase.pretty_print_object('Options', **options)
    }.compact
  )
end

#transactionCoinbase::Transaction

Returns the transaction, if deployed via CDP.

Returns:



386
387
388
# File 'lib/coinbase/smart_contract.rb', line 386

def transaction
  @transaction ||= @model.transaction.nil? ? nil : Coinbase::Transaction.new(@model.transaction)
end

#typeCoinbase::Client::SmartContractType

Returns the type of the SmartContract.

Returns:



367
368
369
# File 'lib/coinbase/smart_contract.rb', line 367

def type
  @model.type
end

#update(name: nil, abi: nil) ⇒ Object



396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
# File 'lib/coinbase/smart_contract.rb', line 396

def update(name: nil, abi: nil)
  req = {}
  req[:contract_name] = name unless name.nil?
  req[:abi] = self.class.normalize_abi(abi).to_json unless abi.nil?

  @model = Coinbase.call_api do
    smart_contracts_api.update_smart_contract(
      network.normalized_id,
      contract_address,
      update_smart_contract_request: req
    )
  end

  self
end

#wait!(interval_seconds = 0.2, timeout_seconds = 20) ⇒ SmartContract

Waits until the Smart Contract deployment is signed or failed by polling the server at the given interval. deployment to land on-chain, in seconds

Parameters:

  • interval_seconds (Integer) (defaults to: 0.2)

    The interval at which to poll the server, in seconds

  • timeout_seconds (Integer) (defaults to: 20)

    The maximum amount of time to wait for the Smart Contract,

Returns:

Raises:

  • (Timeout::Error)

    if the Contract Invocation takes longer than the given timeout



468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
# File 'lib/coinbase/smart_contract.rb', line 468

def wait!(interval_seconds = 0.2, timeout_seconds = 20)
  raise ManageExternalContractError, 'wait!' if external?

  start_time = Time.now

  loop do
    reload

    return self if transaction.terminal_state?

    if Time.now - start_time > timeout_seconds
      raise Timeout::Error, 'SmartContract deployment timed out. Try waiting again.'
    end

    self.sleep interval_seconds
  end

  self
end

#wallet_idString

Returns the ID of the wallet that deployed the SmartContract, if deployed via CDP. Returns nil for externally registered contracts.

Returns:

  • (String)

    The wallet ID



361
362
363
# File 'lib/coinbase/smart_contract.rb', line 361

def wallet_id
  @model.wallet_id
end