gasfree_sdk 0.1.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.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +49 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +149 -0
- data/Rakefile +12 -0
- data/examples/basic_usage.rb +109 -0
- data/examples/demo.rb +140 -0
- data/lib/gasfree_sdk/client.rb +124 -0
- data/lib/gasfree_sdk/errors.rb +81 -0
- data/lib/gasfree_sdk/models/gas_free_address.rb +63 -0
- data/lib/gasfree_sdk/models/provider.rb +51 -0
- data/lib/gasfree_sdk/models/token.rb +42 -0
- data/lib/gasfree_sdk/models/transfer_request.rb +50 -0
- data/lib/gasfree_sdk/models/transfer_response.rb +112 -0
- data/lib/gasfree_sdk/models.rb +13 -0
- data/lib/gasfree_sdk/types.rb +40 -0
- data/lib/gasfree_sdk/version.rb +5 -0
- data/lib/gasfree_sdk.rb +64 -0
- data/sig/gasfree_sdk.rbs +4 -0
- metadata +276 -0
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# GasFree SDK error classes and utilities
|
4
|
+
module GasfreeSdk
|
5
|
+
# Base error class for all SDK errors
|
6
|
+
class Error < StandardError
|
7
|
+
attr_reader :code, :reason
|
8
|
+
|
9
|
+
def initialize(message, code: nil, reason: nil)
|
10
|
+
super(message)
|
11
|
+
@code = code
|
12
|
+
@reason = reason
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Authentication related errors
|
17
|
+
class AuthenticationError < Error; end
|
18
|
+
|
19
|
+
# API related errors
|
20
|
+
class APIError < Error; end
|
21
|
+
|
22
|
+
# Invalid signature errors
|
23
|
+
class InvalidSignatureError < APIError; end
|
24
|
+
|
25
|
+
# Deadline exceeded errors
|
26
|
+
class DeadlineExceededError < APIError; end
|
27
|
+
|
28
|
+
# Insufficient balance errors
|
29
|
+
class InsufficientBalanceError < APIError; end
|
30
|
+
|
31
|
+
# Address not found errors
|
32
|
+
class AddressNotFoundError < APIError; end
|
33
|
+
|
34
|
+
# Transfer not found errors
|
35
|
+
class TransferNotFoundError < APIError; end
|
36
|
+
|
37
|
+
class << self
|
38
|
+
# Map error codes to specific error classes
|
39
|
+
ERROR_CODE_MAP = {
|
40
|
+
"INVALID_SIGNATURE" => InvalidSignatureError,
|
41
|
+
"DEADLINE_EXCEEDED" => DeadlineExceededError,
|
42
|
+
"INSUFFICIENT_BALANCE" => InsufficientBalanceError,
|
43
|
+
"ADDRESS_NOT_FOUND" => AddressNotFoundError,
|
44
|
+
"TRANSFER_NOT_FOUND" => TransferNotFoundError
|
45
|
+
}.freeze
|
46
|
+
|
47
|
+
# Factory method to create appropriate error instances
|
48
|
+
# @param code [String] Error code from API
|
49
|
+
# @param reason [String] Error reason from API
|
50
|
+
# @param message [String] Error message from API
|
51
|
+
# @return [Error] Appropriate error instance
|
52
|
+
def build_error(code:, reason:, message:)
|
53
|
+
error_class = determine_error_class(code, reason)
|
54
|
+
error_class.new(message, code: code, reason: reason)
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def determine_error_class(code, reason)
|
60
|
+
return ERROR_CODE_MAP[code] if ERROR_CODE_MAP.key?(code)
|
61
|
+
|
62
|
+
case reason
|
63
|
+
when /signature/i then InvalidSignatureError
|
64
|
+
when /deadline/i then DeadlineExceededError
|
65
|
+
when /balance/i then InsufficientBalanceError
|
66
|
+
when /not found/i
|
67
|
+
determine_not_found_error(reason)
|
68
|
+
else
|
69
|
+
APIError
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def determine_not_found_error(reason)
|
74
|
+
case reason
|
75
|
+
when /address/i then AddressNotFoundError
|
76
|
+
when /transfer/i then TransferNotFoundError
|
77
|
+
else APIError
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GasfreeSdk
|
4
|
+
module Models
|
5
|
+
# Represents an asset in a GasFree account
|
6
|
+
class Asset < Dry::Struct
|
7
|
+
transform_keys(&:to_sym)
|
8
|
+
|
9
|
+
# @!attribute [r] token_address
|
10
|
+
# @return [String] The token contract address
|
11
|
+
attribute :token_address, Types::Address
|
12
|
+
|
13
|
+
# @!attribute [r] token_symbol
|
14
|
+
# @return [String] The token symbol
|
15
|
+
attribute :token_symbol, Types::String
|
16
|
+
|
17
|
+
# @!attribute [r] activate_fee
|
18
|
+
# @return [String] The activation fee in the smallest unit
|
19
|
+
attribute :activate_fee, Types::Amount
|
20
|
+
|
21
|
+
# @!attribute [r] transfer_fee
|
22
|
+
# @return [String] The transfer fee in the smallest unit
|
23
|
+
attribute :transfer_fee, Types::Amount
|
24
|
+
|
25
|
+
# @!attribute [r] decimal
|
26
|
+
# @return [Integer] The token's decimal places
|
27
|
+
attribute :decimal, Types::Integer.constrained(gteq: 0)
|
28
|
+
|
29
|
+
# @!attribute [r] frozen
|
30
|
+
# @return [String] Amount currently frozen in pending transfers
|
31
|
+
attribute :frozen, Types::Amount
|
32
|
+
end
|
33
|
+
|
34
|
+
# Represents a GasFree account
|
35
|
+
class GasFreeAddress < Dry::Struct
|
36
|
+
transform_keys(&:to_sym)
|
37
|
+
|
38
|
+
# @!attribute [r] account_address
|
39
|
+
# @return [String] The user's EOA address
|
40
|
+
attribute :account_address, Types::Address
|
41
|
+
|
42
|
+
# @!attribute [r] gas_free_address
|
43
|
+
# @return [String] The GasFree account address
|
44
|
+
attribute :gas_free_address, Types::Address
|
45
|
+
|
46
|
+
# @!attribute [r] active
|
47
|
+
# @return [Boolean] Whether the account is activated
|
48
|
+
attribute :active, Types::Bool
|
49
|
+
|
50
|
+
# @!attribute [r] nonce
|
51
|
+
# @return [Integer] The recommended nonce for the next transfer
|
52
|
+
attribute :nonce, Types::Nonce
|
53
|
+
|
54
|
+
# @!attribute [r] allow_submit
|
55
|
+
# @return [Boolean] Whether new transfers can be submitted
|
56
|
+
attribute :allow_submit, Types::Bool
|
57
|
+
|
58
|
+
# @!attribute [r] assets
|
59
|
+
# @return [Array<Asset>] List of assets in the account
|
60
|
+
attribute :assets, Types::Array.of(Asset)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GasfreeSdk
|
4
|
+
module Models
|
5
|
+
# Configuration for a service provider
|
6
|
+
class ProviderConfig < Dry::Struct
|
7
|
+
transform_keys(&:to_sym)
|
8
|
+
|
9
|
+
# @!attribute [r] max_pending_transfer
|
10
|
+
# @return [Integer] Maximum number of pending transfers allowed
|
11
|
+
attribute :max_pending_transfer, Types::Integer.constrained(gteq: 0)
|
12
|
+
|
13
|
+
# @!attribute [r] min_deadline_duration
|
14
|
+
# @return [Integer] Minimum deadline duration in seconds
|
15
|
+
attribute :min_deadline_duration, Types::Integer.constrained(gteq: 0)
|
16
|
+
|
17
|
+
# @!attribute [r] max_deadline_duration
|
18
|
+
# @return [Integer] Maximum deadline duration in seconds
|
19
|
+
attribute :max_deadline_duration, Types::Integer.constrained(gteq: 0)
|
20
|
+
|
21
|
+
# @!attribute [r] default_deadline_duration
|
22
|
+
# @return [Integer] Default deadline duration in seconds
|
23
|
+
attribute :default_deadline_duration, Types::Integer.constrained(gteq: 0)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Represents a GasFree service provider
|
27
|
+
class Provider < Dry::Struct
|
28
|
+
transform_keys(&:to_sym)
|
29
|
+
|
30
|
+
# @!attribute [r] address
|
31
|
+
# @return [String] The provider's address
|
32
|
+
attribute :address, Types::Address
|
33
|
+
|
34
|
+
# @!attribute [r] name
|
35
|
+
# @return [String] The provider's name
|
36
|
+
attribute :name, Types::String
|
37
|
+
|
38
|
+
# @!attribute [r] icon
|
39
|
+
# @return [String] URL to the provider's icon
|
40
|
+
attribute :icon, Types::String
|
41
|
+
|
42
|
+
# @!attribute [r] website
|
43
|
+
# @return [String] The provider's website
|
44
|
+
attribute :website, Types::String
|
45
|
+
|
46
|
+
# @!attribute [r] config
|
47
|
+
# @return [ProviderConfig] The provider's configuration
|
48
|
+
attribute :config, ProviderConfig
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GasfreeSdk
|
4
|
+
module Models
|
5
|
+
# Represents a token supported by GasFree
|
6
|
+
class Token < Dry::Struct
|
7
|
+
transform_keys(&:to_sym)
|
8
|
+
|
9
|
+
# @!attribute [r] token_address
|
10
|
+
# @return [String] The token contract address
|
11
|
+
attribute :token_address, Types::Address
|
12
|
+
|
13
|
+
# @!attribute [r] created_at
|
14
|
+
# @return [Time] When the token was added to GasFree
|
15
|
+
attribute :created_at, Types::JSON::Time
|
16
|
+
|
17
|
+
# @!attribute [r] updated_at
|
18
|
+
# @return [Time] When the token was last updated
|
19
|
+
attribute :updated_at, Types::JSON::Time
|
20
|
+
|
21
|
+
# @!attribute [r] activate_fee
|
22
|
+
# @return [String] The activation fee in the smallest unit of the token
|
23
|
+
attribute :activate_fee, Types::Amount
|
24
|
+
|
25
|
+
# @!attribute [r] transfer_fee
|
26
|
+
# @return [String] The transfer fee in the smallest unit of the token
|
27
|
+
attribute :transfer_fee, Types::Amount
|
28
|
+
|
29
|
+
# @!attribute [r] supported
|
30
|
+
# @return [Boolean] Whether the token is currently supported
|
31
|
+
attribute :supported, Types::Bool
|
32
|
+
|
33
|
+
# @!attribute [r] symbol
|
34
|
+
# @return [String] The token symbol (e.g., "USDT")
|
35
|
+
attribute :symbol, Types::String
|
36
|
+
|
37
|
+
# @!attribute [r] decimal
|
38
|
+
# @return [Integer] The token's decimal places
|
39
|
+
attribute :decimal, Types::Integer.constrained(gteq: 0)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GasfreeSdk
|
4
|
+
module Models
|
5
|
+
# Represents a GasFree transfer request
|
6
|
+
class TransferRequest < Dry::Struct
|
7
|
+
transform_keys(&:to_sym)
|
8
|
+
|
9
|
+
# @!attribute [r] token
|
10
|
+
# @return [String] The token contract address
|
11
|
+
attribute :token, Types::Address
|
12
|
+
|
13
|
+
# @!attribute [r] service_provider
|
14
|
+
# @return [String] The service provider's address
|
15
|
+
attribute :service_provider, Types::Address
|
16
|
+
|
17
|
+
# @!attribute [r] user
|
18
|
+
# @return [String] The user's EOA address
|
19
|
+
attribute :user, Types::Address
|
20
|
+
|
21
|
+
# @!attribute [r] receiver
|
22
|
+
# @return [String] The recipient's address
|
23
|
+
attribute :receiver, Types::Address
|
24
|
+
|
25
|
+
# @!attribute [r] value
|
26
|
+
# @return [String] The transfer amount in smallest unit
|
27
|
+
attribute :value, Types::Amount
|
28
|
+
|
29
|
+
# @!attribute [r] max_fee
|
30
|
+
# @return [String] Maximum fee limit in smallest unit
|
31
|
+
attribute :max_fee, Types::Amount
|
32
|
+
|
33
|
+
# @!attribute [r] deadline
|
34
|
+
# @return [Integer] Transfer expiration timestamp
|
35
|
+
attribute :deadline, Types::Timestamp
|
36
|
+
|
37
|
+
# @!attribute [r] version
|
38
|
+
# @return [Integer] Signature version
|
39
|
+
attribute :version, Types::Version
|
40
|
+
|
41
|
+
# @!attribute [r] nonce
|
42
|
+
# @return [Integer] Transfer nonce
|
43
|
+
attribute :nonce, Types::Nonce
|
44
|
+
|
45
|
+
# @!attribute [r] sig
|
46
|
+
# @return [String] User's signature
|
47
|
+
attribute :sig, Types::String
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module GasfreeSdk
|
4
|
+
module Models
|
5
|
+
# Represents a GasFree transfer response
|
6
|
+
class TransferResponse < Dry::Struct
|
7
|
+
transform_keys(&:to_sym)
|
8
|
+
|
9
|
+
# @!attribute [r] id
|
10
|
+
# @return [String] The transfer trace ID
|
11
|
+
attribute :id, Types::String
|
12
|
+
|
13
|
+
# @!attribute [r] created_at
|
14
|
+
# @return [Time] When the transfer was created
|
15
|
+
attribute :created_at, Types::JSON::Time
|
16
|
+
|
17
|
+
# @!attribute [r] updated_at
|
18
|
+
# @return [Time] When the transfer was last updated
|
19
|
+
attribute :updated_at, Types::JSON::Time
|
20
|
+
|
21
|
+
# @!attribute [r] account_address
|
22
|
+
# @return [String] The user's EOA address
|
23
|
+
attribute :account_address, Types::Address
|
24
|
+
|
25
|
+
# @!attribute [r] gas_free_address
|
26
|
+
# @return [String] The GasFree account address
|
27
|
+
attribute :gas_free_address, Types::Address
|
28
|
+
|
29
|
+
# @!attribute [r] provider_address
|
30
|
+
# @return [String] The service provider's address
|
31
|
+
attribute :provider_address, Types::Address
|
32
|
+
|
33
|
+
# @!attribute [r] target_address
|
34
|
+
# @return [String] The recipient's address
|
35
|
+
attribute :target_address, Types::Address
|
36
|
+
|
37
|
+
# @!attribute [r] token_address
|
38
|
+
# @return [String] The token contract address
|
39
|
+
attribute :token_address, Types::Address
|
40
|
+
|
41
|
+
# @!attribute [r] amount
|
42
|
+
# @return [String] The transfer amount
|
43
|
+
attribute :amount, Types::Amount
|
44
|
+
|
45
|
+
# @!attribute [r] max_fee
|
46
|
+
# @return [String] Maximum fee limit
|
47
|
+
attribute :max_fee, Types::Amount
|
48
|
+
|
49
|
+
# @!attribute [r] signature
|
50
|
+
# @return [String] User's signature
|
51
|
+
attribute :signature, Types::String
|
52
|
+
|
53
|
+
# @!attribute [r] nonce
|
54
|
+
# @return [Integer] Transfer nonce
|
55
|
+
attribute :nonce, Types::Nonce
|
56
|
+
|
57
|
+
# @!attribute [r] expired_at
|
58
|
+
# @return [Time] When the transfer expires
|
59
|
+
attribute :expired_at, Types::JSON::Time
|
60
|
+
|
61
|
+
# @!attribute [r] state
|
62
|
+
# @return [String] Current transfer state
|
63
|
+
attribute :state, Types::State
|
64
|
+
|
65
|
+
# @!attribute [r] estimated_activate_fee
|
66
|
+
# @return [String] Estimated activation fee
|
67
|
+
attribute? :estimated_activate_fee, Types::Amount
|
68
|
+
|
69
|
+
# @!attribute [r] estimated_transfer_fee
|
70
|
+
# @return [String] Estimated transfer fee
|
71
|
+
attribute? :estimated_transfer_fee, Types::Amount
|
72
|
+
|
73
|
+
# @!attribute [r] txn_hash
|
74
|
+
# @return [String] On-chain transaction hash
|
75
|
+
attribute? :txn_hash, Types::Hash
|
76
|
+
|
77
|
+
# @!attribute [r] txn_block_num
|
78
|
+
# @return [Integer] Block number containing the transaction
|
79
|
+
attribute? :txn_block_num, Types::Integer
|
80
|
+
|
81
|
+
# @!attribute [r] txn_block_timestamp
|
82
|
+
# @return [Integer] Block timestamp in milliseconds
|
83
|
+
attribute? :txn_block_timestamp, Types::Integer
|
84
|
+
|
85
|
+
# @!attribute [r] txn_state
|
86
|
+
# @return [String] On-chain transaction state
|
87
|
+
attribute? :txn_state, Types::String.constrained(
|
88
|
+
included_in: %w[INIT NOT_ON_CHAIN ON_CHAIN SOLIDITY ON_CHAIN_FAILED]
|
89
|
+
)
|
90
|
+
|
91
|
+
# @!attribute [r] txn_activate_fee
|
92
|
+
# @return [String] Actual activation fee
|
93
|
+
attribute? :txn_activate_fee, Types::Amount
|
94
|
+
|
95
|
+
# @!attribute [r] txn_transfer_fee
|
96
|
+
# @return [String] Actual transfer fee
|
97
|
+
attribute? :txn_transfer_fee, Types::Amount
|
98
|
+
|
99
|
+
# @!attribute [r] txn_total_fee
|
100
|
+
# @return [String] Total actual fee
|
101
|
+
attribute? :txn_total_fee, Types::Amount
|
102
|
+
|
103
|
+
# @!attribute [r] txn_amount
|
104
|
+
# @return [String] Actual transferred amount
|
105
|
+
attribute? :txn_amount, Types::Amount
|
106
|
+
|
107
|
+
# @!attribute [r] txn_total_cost
|
108
|
+
# @return [String] Total cost including fees
|
109
|
+
attribute? :txn_total_cost, Types::Amount
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "models/token"
|
4
|
+
require_relative "models/provider"
|
5
|
+
require_relative "models/gas_free_address"
|
6
|
+
require_relative "models/transfer_request"
|
7
|
+
require_relative "models/transfer_response"
|
8
|
+
|
9
|
+
module GasfreeSdk
|
10
|
+
# Models namespace
|
11
|
+
module Models
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry-types"
|
4
|
+
|
5
|
+
module GasfreeSdk
|
6
|
+
# Custom types for the SDK
|
7
|
+
module Types
|
8
|
+
include Dry.Types()
|
9
|
+
|
10
|
+
# Address type with validation
|
11
|
+
Address = Types::String.constrained(format: /\A(0x)?[0-9a-fA-F]{40}\z/)
|
12
|
+
|
13
|
+
# Hash type with validation
|
14
|
+
Hash = Types::String.constrained(format: /\A(0x)?[0-9a-fA-F]{64}\z/)
|
15
|
+
|
16
|
+
# Amount type (string representation of a number)
|
17
|
+
Amount = Types::String.constrained(format: /\A[0-9]+\z/)
|
18
|
+
|
19
|
+
# Timestamp type (Unix timestamp)
|
20
|
+
Timestamp = Types::Integer.constrained(gteq: 0)
|
21
|
+
|
22
|
+
# Chain ID type
|
23
|
+
ChainId = Types::Integer.constrained(gteq: 1)
|
24
|
+
|
25
|
+
# Nonce type
|
26
|
+
Nonce = Types::Integer.constrained(gteq: 0)
|
27
|
+
|
28
|
+
# Version type
|
29
|
+
Version = Types::Integer.constrained(included_in: [1])
|
30
|
+
|
31
|
+
# State type
|
32
|
+
State = Types::String.constrained(included_in: %w[
|
33
|
+
WAITING
|
34
|
+
INPROGRESS
|
35
|
+
CONFIRMING
|
36
|
+
SUCCEED
|
37
|
+
FAILED
|
38
|
+
])
|
39
|
+
end
|
40
|
+
end
|
data/lib/gasfree_sdk.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry-configurable"
|
4
|
+
require "dry-struct"
|
5
|
+
require "dry-types"
|
6
|
+
require "dry-validation"
|
7
|
+
require "faraday"
|
8
|
+
require "faraday/retry"
|
9
|
+
require "eth"
|
10
|
+
|
11
|
+
require_relative "gasfree_sdk/version"
|
12
|
+
require_relative "gasfree_sdk/types"
|
13
|
+
require_relative "gasfree_sdk/client"
|
14
|
+
require_relative "gasfree_sdk/errors"
|
15
|
+
require_relative "gasfree_sdk/models"
|
16
|
+
|
17
|
+
# Main module for GasFree SDK
|
18
|
+
module GasfreeSdk
|
19
|
+
extend Dry::Configurable
|
20
|
+
|
21
|
+
# Default API endpoint
|
22
|
+
setting :api_endpoint, default: "https://open.gasfree.io/tron/"
|
23
|
+
setting :api_key, default: nil
|
24
|
+
setting :api_secret, default: nil
|
25
|
+
setting :default_chain_id, default: 1 # Ethereum mainnet
|
26
|
+
setting :default_deadline_duration, default: 180 # 3 minutes
|
27
|
+
setting :logger, default: Logger.new($stdout)
|
28
|
+
setting :retry_options, default: {
|
29
|
+
max: 3,
|
30
|
+
interval: 0.5,
|
31
|
+
interval_randomness: 0.5,
|
32
|
+
backoff_factor: 2
|
33
|
+
}
|
34
|
+
|
35
|
+
class << self
|
36
|
+
# Configure the SDK
|
37
|
+
# @yield [config] Configuration block
|
38
|
+
# @example
|
39
|
+
# GasfreeSdk.configure do |config|
|
40
|
+
# config.api_key = "your-api-key"
|
41
|
+
# config.api_secret = "your-api-secret"
|
42
|
+
# config.api_endpoint = "https://open.gasfree.io/tron/"
|
43
|
+
# end
|
44
|
+
def configure
|
45
|
+
yield config
|
46
|
+
end
|
47
|
+
|
48
|
+
# Create a new client instance
|
49
|
+
# @return [GasfreeSdk::Client]
|
50
|
+
def client
|
51
|
+
@client ||= Client.new
|
52
|
+
end
|
53
|
+
|
54
|
+
# Reset the client instance
|
55
|
+
# @return [void]
|
56
|
+
def reset_client!
|
57
|
+
@client = nil
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Base error class for all SDK errors
|
62
|
+
class Error < StandardError; end
|
63
|
+
# Your code goes here...
|
64
|
+
end
|
data/sig/gasfree_sdk.rbs
ADDED