tronzap-sdk 1.0.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.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +23 -0
  3. data/LICENSE +21 -0
  4. data/README.es.md +396 -0
  5. data/README.md +392 -0
  6. data/README.pt-br.md +392 -0
  7. data/README.ru.md +392 -0
  8. data/lib/tronzap/client.rb +239 -0
  9. data/lib/tronzap/coerce.rb +127 -0
  10. data/lib/tronzap/configuration.rb +140 -0
  11. data/lib/tronzap/errors.rb +150 -0
  12. data/lib/tronzap/http_adapter.rb +168 -0
  13. data/lib/tronzap/models/activate_address_rate.rb +11 -0
  14. data/lib/tronzap/models/address_resources.rb +13 -0
  15. data/lib/tronzap/models/aml_risk_factor.rb +28 -0
  16. data/lib/tronzap/models/bandwidth_rate.rb +28 -0
  17. data/lib/tronzap/models/direct_recharge_rate.rb +38 -0
  18. data/lib/tronzap/models/energy_rate.rb +44 -0
  19. data/lib/tronzap/models/enums.rb +26 -0
  20. data/lib/tronzap/models/resource_amounts.rb +13 -0
  21. data/lib/tronzap/models/timestamp.rb +58 -0
  22. data/lib/tronzap/models/transaction_params.rb +46 -0
  23. data/lib/tronzap/requests/address_activation.rb +28 -0
  24. data/lib/tronzap/requests/aml_check.rb +68 -0
  25. data/lib/tronzap/requests/aml_history.rb +34 -0
  26. data/lib/tronzap/requests/bandwidth_transaction.rb +33 -0
  27. data/lib/tronzap/requests/calculate.rb +32 -0
  28. data/lib/tronzap/requests/check_transaction.rb +48 -0
  29. data/lib/tronzap/requests/energy_transaction.rb +42 -0
  30. data/lib/tronzap/requests/estimate_energy.rb +38 -0
  31. data/lib/tronzap/requests/resource_bundle_transaction.rb +51 -0
  32. data/lib/tronzap/requests/validation.rb +56 -0
  33. data/lib/tronzap/response_decoder.rb +115 -0
  34. data/lib/tronzap/responses/account_balance.rb +20 -0
  35. data/lib/tronzap/responses/address_info.rb +26 -0
  36. data/lib/tronzap/responses/aml_check.rb +56 -0
  37. data/lib/tronzap/responses/aml_history.rb +28 -0
  38. data/lib/tronzap/responses/aml_service.rb +30 -0
  39. data/lib/tronzap/responses/calculation.rb +40 -0
  40. data/lib/tronzap/responses/direct_recharge_info.rb +22 -0
  41. data/lib/tronzap/responses/energy_estimate.rb +44 -0
  42. data/lib/tronzap/responses/service_rates.rb +36 -0
  43. data/lib/tronzap/responses/transaction.rb +44 -0
  44. data/lib/tronzap/version.rb +6 -0
  45. data/lib/tronzap-sdk.rb +4 -0
  46. data/lib/tronzap.rb +47 -0
  47. metadata +107 -0
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tronzap
4
+ module Models
5
+ # A rate energy is delivered at for a direct recharge.
6
+ #
7
+ # @!attribute [r] duration
8
+ # @return [Integer] rental duration in hours
9
+ # @!attribute [r] min_energy
10
+ # @return [Integer] smallest energy amount this rate applies to
11
+ # @!attribute [r] max_energy
12
+ # @return [Integer] largest energy amount this rate applies to
13
+ # @!attribute [r] price
14
+ # @return [BigDecimal] price per unit of energy, in TRX
15
+ # @!attribute [r] price_32k
16
+ # @return [BigDecimal] price of 32 000 energy, in TRX
17
+ # @!attribute [r] price_65k
18
+ # @return [BigDecimal] price of 65 000 energy, in TRX
19
+ # @!attribute [r] price_131k
20
+ # @return [BigDecimal] price of 131 000 energy, in TRX
21
+ DirectRechargeRate = Data.define(:duration, :min_energy, :max_energy,
22
+ :price, :price_32k, :price_65k, :price_131k) do
23
+ # @api private
24
+ def self.from_api(value)
25
+ data = Coerce.object(value, "direct recharge rate")
26
+ new(
27
+ duration: Coerce.integer(data["duration"], "duration"),
28
+ min_energy: Coerce.integer(data["min_energy"], "min_energy"),
29
+ max_energy: Coerce.integer(data["max_energy"], "max_energy"),
30
+ price: Coerce.decimal(data["price"], "price"),
31
+ price_32k: Coerce.decimal(data["price_32k"], "price_32k"),
32
+ price_65k: Coerce.decimal(data["price_65k"], "price_65k"),
33
+ price_131k: Coerce.decimal(data["price_131k"], "price_131k")
34
+ )
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tronzap
4
+ module Models
5
+ # An energy price tier. Energy is priced per unit.
6
+ #
7
+ # @!attribute [r] duration
8
+ # @return [Integer] rental duration in hours
9
+ # @!attribute [r] min_amount
10
+ # @return [Integer] smallest amount this tier applies to
11
+ # @!attribute [r] max_amount
12
+ # @return [Integer] largest amount this tier applies to
13
+ # @!attribute [r] min_energy
14
+ # @return [Integer] smallest energy amount this tier applies to
15
+ # @!attribute [r] max_energy
16
+ # @return [Integer] largest energy amount this tier applies to
17
+ # @!attribute [r] price
18
+ # @return [BigDecimal] price per unit of energy, in TRX
19
+ # @!attribute [r] price_32k
20
+ # @return [BigDecimal] price of 32 000 energy, in TRX
21
+ # @!attribute [r] price_65k
22
+ # @return [BigDecimal] price of 65 000 energy, in TRX
23
+ # @!attribute [r] price_131k
24
+ # @return [BigDecimal] price of 131 000 energy, in TRX
25
+ EnergyRate = Data.define(:duration, :min_amount, :max_amount, :min_energy, :max_energy,
26
+ :price, :price_32k, :price_65k, :price_131k) do
27
+ # @api private
28
+ def self.from_api(value)
29
+ data = Coerce.object(value, "energy rate")
30
+ new(
31
+ duration: Coerce.integer(data["duration"], "duration"),
32
+ min_amount: Coerce.integer(data["min_amount"], "min_amount"),
33
+ max_amount: Coerce.integer(data["max_amount"], "max_amount"),
34
+ min_energy: Coerce.integer(data["min_energy"], "min_energy"),
35
+ max_energy: Coerce.integer(data["max_energy"], "max_energy"),
36
+ price: Coerce.decimal(data["price"], "price"),
37
+ price_32k: Coerce.decimal(data["price_32k"], "price_32k"),
38
+ price_65k: Coerce.decimal(data["price_65k"], "price_65k"),
39
+ price_131k: Coerce.decimal(data["price_131k"], "price_131k")
40
+ )
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tronzap
4
+ # Value objects nested in API responses, and the symbols enum-like fields take.
5
+ #
6
+ # Enum-like fields are symbols. A value this SDK version does not recognise is reported as +:unknown+.
7
+ module Models
8
+ # Services a transaction is created for.
9
+ SERVICES = %i[energy bandwidth resource_bundle activate_address].freeze
10
+
11
+ # Transaction states: +:new+ → +:pending+ → +:success+ or +:failed+.
12
+ TRANSACTION_STATUSES = %i[new pending success failed].freeze
13
+
14
+ # What an AML check screens: a wallet address or a transaction hash.
15
+ AML_CHECK_TYPES = %i[address hash].freeze
16
+
17
+ # Directions of a screened transaction: incoming (+:deposit+) or outgoing (+:withdrawal+) funds.
18
+ AML_DIRECTIONS = %i[deposit withdrawal].freeze
19
+
20
+ # AML check states.
21
+ AML_STATUSES = %i[pending processing completed failed].freeze
22
+
23
+ # AML risk levels.
24
+ AML_RISK_LEVELS = %i[low medium high].freeze
25
+ end
26
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tronzap
4
+ module Models
5
+ # The resources a transaction buys. A resource the transaction does not include is 0.
6
+ #
7
+ # @!attribute [r] energy
8
+ # @return [Integer] energy bought
9
+ # @!attribute [r] bandwidth
10
+ # @return [Integer] bandwidth bought
11
+ ResourceAmounts = Data.define(:energy, :bandwidth)
12
+ end
13
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tronzap
4
+ module Models
5
+ # A timestamp as the API sent it, together with its parsed value.
6
+ #
7
+ # @!attribute [r] raw
8
+ # @return [String] the text exactly as the API sent it
9
+ # @!attribute [r] value
10
+ # @return [Time, nil] the parsed time, or +nil+ when {raw} is in a format the SDK does not recognise
11
+ Timestamp = Data.define(:raw, :value) do
12
+ # Parses a timestamp in any of the formats the API emits: RFC 3339 with or without an offset, a
13
+ # space-separated date and time, a bare date, or Unix seconds. Times without an offset are read as UTC.
14
+ #
15
+ # @param raw [String]
16
+ # @return [Timestamp]
17
+ def self.parse(raw)
18
+ raise ArgumentError, "raw must be a String" unless raw.is_a?(String)
19
+
20
+ new(raw: raw.dup.freeze, value: parse_time(raw.strip))
21
+ end
22
+
23
+ def self.parse_time(text)
24
+ if (match = /\A(\d{4})-(\d{2})-(\d{2})[T\ ](\d{2}):(\d{2})
25
+ (?::(\d{2}(?:\.\d+)?))?\s*(Z|[+-]\d{2}(?::?\d{2})?)?\z/ix.match(text))
26
+ date_time(match)
27
+ elsif (match = /\A(\d{4})-(\d{2})-(\d{2})\z/.match(text))
28
+ calendar_day(Time.utc(*match.captures.map(&:to_i)), *match.captures)
29
+ elsif /\A\d{1,12}\z/.match?(text)
30
+ Time.at(text.to_i).utc
31
+ end
32
+ rescue ArgumentError
33
+ nil
34
+ end
35
+
36
+ def self.date_time(match)
37
+ year, month, day, hour, minute, second, zone = match.captures
38
+ zone = "+00:00" if zone.nil? || zone.casecmp?("z")
39
+ zone = "#{zone}:00" if zone.length == 3
40
+ time = Time.new(year.to_i, month.to_i, day.to_i, hour.to_i, minute.to_i, Rational(second || "0"), zone)
41
+ calendar_day(time, year, month, day)
42
+ end
43
+
44
+ # Time rolls an impossible date such as February 30 over into the next month instead of rejecting it.
45
+ def self.calendar_day(time, year, month, day)
46
+ time if [time.year, time.month, time.day] == [year.to_i, month.to_i, day.to_i]
47
+ end
48
+ private_class_method :parse_time, :date_time, :calendar_day
49
+
50
+ # @return [String] the parsed time in ISO 8601, or the raw text when it could not be parsed
51
+ def to_s
52
+ return raw unless value
53
+
54
+ value.iso8601(value.subsec.zero? ? 0 : 6)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tronzap
4
+ module Models
5
+ # The parameters a transaction was created with.
6
+ #
7
+ # @!attribute [r] address
8
+ # @return [String] the TRON address that receives the resources
9
+ # @!attribute [r] duration
10
+ # @return [Integer] rental duration in hours
11
+ # @!attribute [r] amounts
12
+ # @return [ResourceAmounts] the resources the transaction buys
13
+ # @!attribute [r] activate_address
14
+ # @return [Boolean] whether the transaction also activates the address
15
+ TransactionParams = Data.define(:address, :duration, :amounts, :activate_address) do
16
+ # @api private
17
+ def self.from_api(value, service)
18
+ data = Coerce.object(value, "params")
19
+ new(
20
+ address: Coerce.text(data["address"], "params.address"),
21
+ duration: Coerce.integer(data["duration"], "params.duration"),
22
+ amounts: amounts(data, service),
23
+ activate_address: Coerce.boolean(data["activate_address"], "params.activate_address")
24
+ )
25
+ end
26
+
27
+ # Older transactions carry params.amount / params.energy_amount instead of params.amounts.
28
+ def self.amounts(data, service)
29
+ amounts = Coerce.object(data["amounts"], "params.amounts")
30
+ energy = Coerce.integer(amounts["energy"], "params.amounts.energy")
31
+ bandwidth = Coerce.integer(amounts["bandwidth"], "params.amounts.bandwidth")
32
+ if energy.zero? && bandwidth.zero?
33
+ energy = Coerce.integer(data["energy_amount"], "params.energy_amount")
34
+ amount = Coerce.integer(data["amount"], "params.amount")
35
+ if service == :bandwidth
36
+ bandwidth = amount
37
+ elsif energy.zero?
38
+ energy = amount
39
+ end
40
+ end
41
+ ResourceAmounts.new(energy: energy, bandwidth: bandwidth)
42
+ end
43
+ private_class_method :amounts
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tronzap
4
+ module Requests
5
+ # A TRON address to activate.
6
+ #
7
+ # @!attribute [r] address
8
+ # @return [String] the TRON address to activate
9
+ # @!attribute [r] external_id
10
+ # @return [String, nil] your own ID for the transaction, to look it up with {CheckTransaction.by_external_id}
11
+ AddressActivation = Data.define(:address, :external_id) do
12
+ # @param address [String]
13
+ # @param external_id [String, nil]
14
+ # @raise [ArgumentError]
15
+ def initialize(address:, external_id: nil)
16
+ super(
17
+ address: Validation.string(address, "address"),
18
+ external_id: Validation.optional_string(external_id, "external_id")
19
+ )
20
+ end
21
+
22
+ # @api private
23
+ def body
24
+ Validation.transaction("activate_address", { "address" => address }, external_id)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tronzap
4
+ module Requests
5
+ # An address or a transaction hash to screen.
6
+ #
7
+ # @!attribute [r] type
8
+ # @return [Symbol] what to screen, one of {Models::AML_CHECK_TYPES}
9
+ # @!attribute [r] network
10
+ # @return [String] the network code, for example +TRX+, +BTC+ or +ETH+
11
+ # @!attribute [r] address
12
+ # @return [String] the wallet address
13
+ # @!attribute [r] transaction_hash
14
+ # @return [String, nil] the transaction hash, required when {type} is +:hash+
15
+ # @!attribute [r] direction
16
+ # @return [Symbol, nil] the transaction direction for a hash check, one of {Models::AML_DIRECTIONS}
17
+ AmlCheck = Data.define(:type, :network, :address, :transaction_hash, :direction) do
18
+ # @param type [Symbol, String] +:address+ or +:hash+
19
+ # @param network [String]
20
+ # @param address [String]
21
+ # @param transaction_hash [String, nil]
22
+ # @param direction [Symbol, String, nil] +:deposit+ or +:withdrawal+
23
+ # @raise [ArgumentError]
24
+ def initialize(type:, network:, address:, transaction_hash: nil, direction: nil)
25
+ type = Validation.one_of(type, Models::AML_CHECK_TYPES, "type")
26
+ transaction_hash = Validation.optional_string(transaction_hash, "transaction_hash")
27
+ raise ArgumentError, "transaction_hash is required for a hash check" if type == :hash && !transaction_hash
28
+
29
+ super(
30
+ type: type,
31
+ network: Validation.string(network, "network"),
32
+ address: Validation.string(address, "address"),
33
+ transaction_hash: transaction_hash,
34
+ direction: Validation.optional_one_of(direction, Models::AML_DIRECTIONS, "direction")
35
+ )
36
+ end
37
+
38
+ # Screens a wallet address.
39
+ #
40
+ # @param network [String] the network code, for example +TRX+
41
+ # @param address [String]
42
+ # @return [AmlCheck]
43
+ def self.for_address(network, address)
44
+ new(type: :address, network: network, address: address)
45
+ end
46
+
47
+ # Screens a transaction.
48
+ #
49
+ # @param network [String] the network code, for example +BTC+
50
+ # @param address [String]
51
+ # @param transaction_hash [String]
52
+ # @param direction [Symbol, String, nil] +:deposit+ or +:withdrawal+
53
+ # @return [AmlCheck]
54
+ def self.for_hash(network, address, transaction_hash, direction: nil)
55
+ new(type: :hash, network: network, address: address, transaction_hash: transaction_hash,
56
+ direction: direction)
57
+ end
58
+
59
+ # @api private
60
+ def body
61
+ body = { "type" => type.name, "network" => network, "address" => address }
62
+ body["hash"] = transaction_hash if transaction_hash
63
+ body["direction"] = direction.name if direction
64
+ body
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tronzap
4
+ module Requests
5
+ # A page of AML check history to return.
6
+ #
7
+ # @!attribute [r] page
8
+ # @return [Integer] the page number, starting at 1
9
+ # @!attribute [r] per_page
10
+ # @return [Integer] the page size
11
+ # @!attribute [r] status
12
+ # @return [Symbol, nil] only checks in this state, one of {Models::AML_STATUSES}, or all checks when +nil+
13
+ AmlHistory = Data.define(:page, :per_page, :status) do
14
+ # @param page [Integer]
15
+ # @param per_page [Integer]
16
+ # @param status [Symbol, String, nil]
17
+ # @raise [ArgumentError]
18
+ def initialize(page: 1, per_page: 10, status: nil)
19
+ super(
20
+ page: Validation.positive_integer(page, "page"),
21
+ per_page: Validation.positive_integer(per_page, "per_page"),
22
+ status: Validation.optional_one_of(status, Models::AML_STATUSES, "status")
23
+ )
24
+ end
25
+
26
+ # @api private
27
+ def body
28
+ body = { "page" => page, "per_page" => per_page }
29
+ body["status"] = status.name if status
30
+ body
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tronzap
4
+ module Requests
5
+ # A bandwidth purchase. Bandwidth is rented for one hour.
6
+ #
7
+ # @!attribute [r] address
8
+ # @return [String] the TRON address that receives the bandwidth
9
+ # @!attribute [r] bandwidth
10
+ # @return [Integer] the amount of bandwidth to buy
11
+ # @!attribute [r] external_id
12
+ # @return [String, nil] your own ID for the transaction, to look it up with {CheckTransaction.by_external_id}
13
+ BandwidthTransaction = Data.define(:address, :bandwidth, :external_id) do
14
+ # @param address [String]
15
+ # @param bandwidth [Integer]
16
+ # @param external_id [String, nil]
17
+ # @raise [ArgumentError]
18
+ def initialize(address:, bandwidth:, external_id: nil)
19
+ super(
20
+ address: Validation.string(address, "address"),
21
+ bandwidth: Validation.positive_integer(bandwidth, "bandwidth"),
22
+ external_id: Validation.optional_string(external_id, "external_id")
23
+ )
24
+ end
25
+
26
+ # @api private
27
+ def body
28
+ params = { "address" => address, "amounts" => { "bandwidth" => bandwidth }, "duration" => 1 }
29
+ Validation.transaction("bandwidth", params, external_id)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tronzap
4
+ module Requests
5
+ # An energy purchase to price.
6
+ #
7
+ # @!attribute [r] address
8
+ # @return [String] the TRON address that would receive the energy
9
+ # @!attribute [r] energy
10
+ # @return [Integer] the amount of energy
11
+ # @!attribute [r] duration
12
+ # @return [Integer] rental duration in hours, one of the durations {Client#get_services} lists
13
+ Calculate = Data.define(:address, :energy, :duration) do
14
+ # @param address [String]
15
+ # @param energy [Integer]
16
+ # @param duration [Integer]
17
+ # @raise [ArgumentError]
18
+ def initialize(address:, energy:, duration: 1)
19
+ super(
20
+ address: Validation.string(address, "address"),
21
+ energy: Validation.positive_integer(energy, "energy"),
22
+ duration: Validation.positive_integer(duration, "duration")
23
+ )
24
+ end
25
+
26
+ # @api private
27
+ def body
28
+ { "address" => address, "amount" => energy, "duration" => duration }
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tronzap
4
+ module Requests
5
+ # The transaction to look up, by its TronZap ID, your external ID, or both.
6
+ #
7
+ # @!attribute [r] id
8
+ # @return [String, nil] the transaction ID assigned by TronZap
9
+ # @!attribute [r] external_id
10
+ # @return [String, nil] the external ID you passed when creating the transaction
11
+ CheckTransaction = Data.define(:id, :external_id) do
12
+ # @param id [String, nil]
13
+ # @param external_id [String, nil]
14
+ # @raise [ArgumentError] when neither is given
15
+ def initialize(id: nil, external_id: nil)
16
+ id = Validation.optional_string(id, "id")
17
+ external_id = Validation.optional_string(external_id, "external_id")
18
+ raise ArgumentError, "either id or external_id is required" if id.nil? && external_id.nil?
19
+
20
+ super
21
+ end
22
+
23
+ # Looks a transaction up by its TronZap ID.
24
+ #
25
+ # @param id [String]
26
+ # @return [CheckTransaction]
27
+ def self.by_id(id)
28
+ new(id: id)
29
+ end
30
+
31
+ # Looks a transaction up by the external ID you passed when creating it.
32
+ #
33
+ # @param external_id [String]
34
+ # @return [CheckTransaction]
35
+ def self.by_external_id(external_id)
36
+ new(external_id: external_id)
37
+ end
38
+
39
+ # @api private
40
+ def body
41
+ body = {}
42
+ body["id"] = id if id
43
+ body["external_id"] = external_id if external_id
44
+ body
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tronzap
4
+ module Requests
5
+ # An energy purchase.
6
+ #
7
+ # @!attribute [r] address
8
+ # @return [String] the TRON address that receives the energy
9
+ # @!attribute [r] energy
10
+ # @return [Integer] the amount of energy to buy
11
+ # @!attribute [r] duration
12
+ # @return [Integer] rental duration in hours, one of the durations {Client#get_services} lists
13
+ # @!attribute [r] external_id
14
+ # @return [String, nil] your own ID for the transaction, to look it up with {CheckTransaction.by_external_id}
15
+ # @!attribute [r] activate_address
16
+ # @return [Boolean] whether to activate the address in the same transaction
17
+ EnergyTransaction = Data.define(:address, :energy, :duration, :external_id, :activate_address) do
18
+ # @param address [String]
19
+ # @param energy [Integer]
20
+ # @param duration [Integer]
21
+ # @param external_id [String, nil]
22
+ # @param activate_address [Boolean]
23
+ # @raise [ArgumentError]
24
+ def initialize(address:, energy:, duration: 1, external_id: nil, activate_address: false)
25
+ super(
26
+ address: Validation.string(address, "address"),
27
+ energy: Validation.positive_integer(energy, "energy"),
28
+ duration: Validation.positive_integer(duration, "duration"),
29
+ external_id: Validation.optional_string(external_id, "external_id"),
30
+ activate_address: Validation.boolean(activate_address, "activate_address")
31
+ )
32
+ end
33
+
34
+ # @api private
35
+ def body
36
+ params = { "address" => address, "amounts" => { "energy" => energy }, "duration" => duration }
37
+ params["activate_address"] = true if activate_address
38
+ Validation.transaction("energy", params, external_id)
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tronzap
4
+ # The USDT (TRC20) contract address. The API estimates a transfer of this token when
5
+ # {Requests::EstimateEnergy#contract_address} is not set.
6
+ USDT_CONTRACT_ADDRESS = "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"
7
+
8
+ module Requests
9
+ # A token transfer to estimate the energy for.
10
+ #
11
+ # @!attribute [r] from_address
12
+ # @return [String] the sender's TRON address
13
+ # @!attribute [r] to_address
14
+ # @return [String] the recipient's TRON address
15
+ # @!attribute [r] contract_address
16
+ # @return [String, nil] the token contract address. When not set the API estimates a USDT (TRC20) transfer.
17
+ EstimateEnergy = Data.define(:from_address, :to_address, :contract_address) do
18
+ # @param from_address [String]
19
+ # @param to_address [String]
20
+ # @param contract_address [String, nil]
21
+ # @raise [ArgumentError]
22
+ def initialize(from_address:, to_address:, contract_address: nil)
23
+ super(
24
+ from_address: Validation.string(from_address, "from_address"),
25
+ to_address: Validation.string(to_address, "to_address"),
26
+ contract_address: Validation.optional_string(contract_address, "contract_address")
27
+ )
28
+ end
29
+
30
+ # @api private
31
+ def body
32
+ body = { "from_address" => from_address, "to_address" => to_address }
33
+ body["contract_address"] = contract_address if contract_address
34
+ body
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tronzap
4
+ module Requests
5
+ # A purchase of energy and bandwidth in one transaction.
6
+ #
7
+ # @!attribute [r] address
8
+ # @return [String] the TRON address that receives the resources
9
+ # @!attribute [r] energy
10
+ # @return [Integer] the amount of energy to buy
11
+ # @!attribute [r] bandwidth
12
+ # @return [Integer] the amount of bandwidth to buy
13
+ # @!attribute [r] duration
14
+ # @return [Integer] rental duration in hours
15
+ # @!attribute [r] external_id
16
+ # @return [String, nil] your own ID for the transaction, to look it up with {CheckTransaction.by_external_id}
17
+ # @!attribute [r] activate_address
18
+ # @return [Boolean] whether to activate the address in the same transaction
19
+ ResourceBundleTransaction = Data.define(:address, :energy, :bandwidth, :duration, :external_id,
20
+ :activate_address) do
21
+ # @param address [String]
22
+ # @param energy [Integer]
23
+ # @param bandwidth [Integer]
24
+ # @param duration [Integer]
25
+ # @param external_id [String, nil]
26
+ # @param activate_address [Boolean]
27
+ # @raise [ArgumentError]
28
+ def initialize(address:, energy:, bandwidth:, duration: 1, external_id: nil, activate_address: false)
29
+ super(
30
+ address: Validation.string(address, "address"),
31
+ energy: Validation.positive_integer(energy, "energy"),
32
+ bandwidth: Validation.positive_integer(bandwidth, "bandwidth"),
33
+ duration: Validation.positive_integer(duration, "duration"),
34
+ external_id: Validation.optional_string(external_id, "external_id"),
35
+ activate_address: Validation.boolean(activate_address, "activate_address")
36
+ )
37
+ end
38
+
39
+ # @api private
40
+ def body
41
+ params = {
42
+ "address" => address,
43
+ "amounts" => { "energy" => energy, "bandwidth" => bandwidth },
44
+ "duration" => duration
45
+ }
46
+ params["activate_address"] = true if activate_address
47
+ Validation.transaction("resource_bundle", params, external_id)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tronzap
4
+ # Parameters of {Client} calls. A request validates its values when it is created, so an invalid one raises
5
+ # +ArgumentError+ and never reaches the API.
6
+ module Requests
7
+ # @api private
8
+ module Validation
9
+ module_function
10
+
11
+ def string(value, name)
12
+ raise ArgumentError, "#{name} is required" unless value.is_a?(String) && !value.strip.empty?
13
+
14
+ value.dup.freeze
15
+ end
16
+
17
+ def optional_string(value, name)
18
+ return nil if value.nil?
19
+ raise ArgumentError, "#{name} must be a non-blank String when set" unless value.is_a?(String) &&
20
+ !value.strip.empty?
21
+
22
+ value.dup.freeze
23
+ end
24
+
25
+ def positive_integer(value, name)
26
+ raise ArgumentError, "#{name} must be a positive Integer, got #{value.inspect}" unless value.is_a?(Integer) &&
27
+ value.positive?
28
+
29
+ value
30
+ end
31
+
32
+ def boolean(value, name)
33
+ raise ArgumentError, "#{name} must be true or false, got #{value.inspect}" unless [true, false].include?(value)
34
+
35
+ value
36
+ end
37
+
38
+ def one_of(value, allowed, name)
39
+ symbol = value.is_a?(String) ? allowed.find { |item| item.name == value } : value
40
+ return symbol if allowed.include?(symbol)
41
+
42
+ raise ArgumentError, "#{name} must be one of #{allowed.map(&:inspect).join(", ")}, got #{value.inspect}"
43
+ end
44
+
45
+ def optional_one_of(value, allowed, name)
46
+ value.nil? ? nil : one_of(value, allowed, name)
47
+ end
48
+
49
+ def transaction(service, params, external_id)
50
+ body = { "service" => service, "params" => params }
51
+ body["external_id"] = external_id if external_id
52
+ body
53
+ end
54
+ end
55
+ end
56
+ end