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.
@@ -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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GasfreeSdk
4
+ VERSION = "0.1.0"
5
+ end
@@ -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
@@ -0,0 +1,4 @@
1
+ module GasfreeSdk
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end