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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 20a368c1b6e6f12801569a0874cdef79af724a074f4a31a51843a3f569ca411d
|
4
|
+
data.tar.gz: eab16b631f5d5b4f3a84caa263cd4bfbcdc153f299da52c8d765f6da6ad4da3f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1405d5c293cac1560e6c4ee8313be4214e3d51c2ff5e7f80c5d2b83c47b6556bbf188ffedb339c8f21c1914c2a8457e564ae26004d7b1f5623a55062fde086be
|
7
|
+
data.tar.gz: 064c36e078a3ef3de405c09a8d3783e1000bd86dc29064f9735de547aaad6b71658641f30a74224b37b223bc30e292dda0543a98e4d9e6186d696373c1bb2710
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 3.3
|
3
|
+
SuggestExtensions: false
|
4
|
+
Exclude:
|
5
|
+
- 'vendor/**/*'
|
6
|
+
- 'bin/**/*'
|
7
|
+
|
8
|
+
require:
|
9
|
+
- rubocop-rake
|
10
|
+
- rubocop-rspec
|
11
|
+
|
12
|
+
Layout/LineLength:
|
13
|
+
Max: 120
|
14
|
+
Exclude:
|
15
|
+
- 'gasfree_sdk.gemspec'
|
16
|
+
- 'examples/**/*'
|
17
|
+
|
18
|
+
Metrics/BlockLength:
|
19
|
+
Exclude:
|
20
|
+
- 'spec/**/*'
|
21
|
+
- 'examples/**/*'
|
22
|
+
|
23
|
+
Metrics/MethodLength:
|
24
|
+
Max: 15
|
25
|
+
Exclude:
|
26
|
+
- 'spec/**/*'
|
27
|
+
|
28
|
+
Metrics/CyclomaticComplexity:
|
29
|
+
Max: 10
|
30
|
+
Exclude:
|
31
|
+
- 'spec/**/*'
|
32
|
+
|
33
|
+
Style/Documentation:
|
34
|
+
Exclude:
|
35
|
+
- 'spec/**/*'
|
36
|
+
- 'examples/**/*'
|
37
|
+
- 'lib/gasfree_sdk/errors.rb'
|
38
|
+
|
39
|
+
RSpec/ExampleLength:
|
40
|
+
Max: 30
|
41
|
+
|
42
|
+
RSpec/MultipleExpectations:
|
43
|
+
Max: 5
|
44
|
+
|
45
|
+
Style/StringLiterals:
|
46
|
+
EnforcedStyle: double_quotes
|
47
|
+
|
48
|
+
Style/StringLiteralsInInterpolation:
|
49
|
+
EnforcedStyle: double_quotes
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2025 madmatvey
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
# GasfreeSdk
|
2
|
+
|
3
|
+
Ruby SDK for interacting with the GasFree.io API, enabling gasless transfers of TRC-20/ERC-20 tokens.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- Supports gasless transfers of TRC-20/ERC-20 tokens
|
8
|
+
- Handles address generation and transaction signing
|
9
|
+
- Provides a clean, Ruby-like interface to the GasFree API
|
10
|
+
- Built with dry-rb for robust type checking and validation
|
11
|
+
- Comprehensive RSpec test coverage
|
12
|
+
- Thread-safe configuration
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
Add this line to your application's Gemfile:
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
gem 'gasfree_sdk'
|
20
|
+
```
|
21
|
+
|
22
|
+
And then execute:
|
23
|
+
|
24
|
+
```bash
|
25
|
+
bundle install
|
26
|
+
```
|
27
|
+
|
28
|
+
Or install it yourself as:
|
29
|
+
|
30
|
+
```bash
|
31
|
+
gem install gasfree_sdk
|
32
|
+
```
|
33
|
+
|
34
|
+
## Configuration
|
35
|
+
|
36
|
+
Configure the SDK with your API credentials:
|
37
|
+
|
38
|
+
```ruby
|
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/" # Optional, defaults to mainnet
|
43
|
+
config.default_chain_id = 1 # Optional, defaults to Ethereum mainnet
|
44
|
+
end
|
45
|
+
```
|
46
|
+
|
47
|
+
## Usage
|
48
|
+
|
49
|
+
### Initialize Client
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
client = GasfreeSdk.client
|
53
|
+
```
|
54
|
+
|
55
|
+
### Get Supported Tokens
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
tokens = client.tokens
|
59
|
+
tokens.each do |token|
|
60
|
+
puts "Token: #{token.symbol}"
|
61
|
+
puts "Address: #{token.token_address}"
|
62
|
+
puts "Activation Fee: #{token.activate_fee}"
|
63
|
+
puts "Transfer Fee: #{token.transfer_fee}"
|
64
|
+
end
|
65
|
+
```
|
66
|
+
|
67
|
+
### Get Service Providers
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
providers = client.providers
|
71
|
+
providers.each do |provider|
|
72
|
+
puts "Provider: #{provider.name}"
|
73
|
+
puts "Address: #{provider.address}"
|
74
|
+
puts "Max Pending Transfers: #{provider.config.max_pending_transfer}"
|
75
|
+
end
|
76
|
+
```
|
77
|
+
|
78
|
+
### Get GasFree Account Info
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
account = client.address("0x1234...")
|
82
|
+
puts "GasFree Address: #{account.gas_free_address}"
|
83
|
+
puts "Active: #{account.active}"
|
84
|
+
puts "Nonce: #{account.nonce}"
|
85
|
+
|
86
|
+
account.assets.each do |asset|
|
87
|
+
puts "Token: #{asset.token_symbol}"
|
88
|
+
puts "Frozen Amount: #{asset.frozen}"
|
89
|
+
end
|
90
|
+
```
|
91
|
+
|
92
|
+
### Submit GasFree Transfer
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
request = GasfreeSdk::Models::TransferRequest.new(
|
96
|
+
token: "0x1234...", # Token address
|
97
|
+
service_provider: "0x5678...", # Provider address
|
98
|
+
user: "0x9abc...", # User's EOA address
|
99
|
+
receiver: "0xdef0...", # Recipient address
|
100
|
+
value: "1000000", # Amount in smallest unit
|
101
|
+
max_fee: "100000", # Maximum fee in smallest unit
|
102
|
+
deadline: Time.now.to_i + 180, # 3 minutes from now
|
103
|
+
version: 1,
|
104
|
+
nonce: 0,
|
105
|
+
sig: "0x..." # User's signature
|
106
|
+
)
|
107
|
+
|
108
|
+
response = client.submit_transfer(request)
|
109
|
+
puts "Transfer ID: #{response.id}"
|
110
|
+
puts "State: #{response.state}"
|
111
|
+
```
|
112
|
+
|
113
|
+
### Check Transfer Status
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
status = client.transfer_status("transfer-id")
|
117
|
+
puts "State: #{status.state}"
|
118
|
+
puts "Transaction Hash: #{status.txn_hash}" if status.txn_hash
|
119
|
+
```
|
120
|
+
|
121
|
+
## Error Handling
|
122
|
+
|
123
|
+
The SDK provides detailed error classes for different types of errors:
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
begin
|
127
|
+
client.submit_transfer(request)
|
128
|
+
rescue GasfreeSdk::InsufficientBalanceError => e
|
129
|
+
puts "Insufficient balance: #{e.message}"
|
130
|
+
rescue GasfreeSdk::DeadlineExceededError => e
|
131
|
+
puts "Deadline exceeded: #{e.message}"
|
132
|
+
rescue GasfreeSdk::APIError => e
|
133
|
+
puts "API error (#{e.code}): #{e.message}"
|
134
|
+
end
|
135
|
+
```
|
136
|
+
|
137
|
+
## Development
|
138
|
+
|
139
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
140
|
+
|
141
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
142
|
+
|
143
|
+
## Contributing
|
144
|
+
|
145
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/madmatvey/gasfree_sdk.
|
146
|
+
|
147
|
+
## License
|
148
|
+
|
149
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "gasfree_sdk"
|
6
|
+
require "eth"
|
7
|
+
|
8
|
+
# Configure the SDK
|
9
|
+
GasfreeSdk.configure do |config|
|
10
|
+
config.api_key = ENV["GASFREE_API_KEY"] || "your-api-key"
|
11
|
+
config.api_secret = ENV["GASFREE_API_SECRET"] || "your-api-secret"
|
12
|
+
config.api_endpoint = ENV["GASFREE_API_ENDPOINT"] || "https://open.gasfree.io/tron/"
|
13
|
+
end
|
14
|
+
|
15
|
+
# Check if we have real API credentials
|
16
|
+
if GasfreeSdk.config.api_key == "your-api-key"
|
17
|
+
puts "WARNING: Using placeholder API credentials. Set GASFREE_API_KEY and " \
|
18
|
+
"GASFREE_API_SECRET environment variables for real usage."
|
19
|
+
puts "This example will demonstrate the SDK structure but API calls will fail.\n\n"
|
20
|
+
end
|
21
|
+
|
22
|
+
# Initialize client
|
23
|
+
client = GasfreeSdk.client
|
24
|
+
|
25
|
+
# Get supported tokens
|
26
|
+
puts "Supported Tokens:"
|
27
|
+
tokens = client.tokens
|
28
|
+
tokens.each do |token|
|
29
|
+
puts " #{token.symbol} (#{token.token_address})"
|
30
|
+
puts " Activation Fee: #{token.activate_fee}"
|
31
|
+
puts " Transfer Fee: #{token.transfer_fee}"
|
32
|
+
end
|
33
|
+
|
34
|
+
# Get service providers
|
35
|
+
puts "\nService Providers:"
|
36
|
+
providers = client.providers
|
37
|
+
providers.each do |provider|
|
38
|
+
puts " #{provider.name} (#{provider.address})"
|
39
|
+
puts " Max Pending Transfers: #{provider.config.max_pending_transfer}"
|
40
|
+
puts " Default Deadline: #{provider.config.default_deadline_duration}s"
|
41
|
+
end
|
42
|
+
|
43
|
+
# Create a new key pair for testing
|
44
|
+
key = Eth::Key.new
|
45
|
+
puts "\nTest Account:"
|
46
|
+
puts " Address: #{key.address}"
|
47
|
+
|
48
|
+
# Get GasFree account info
|
49
|
+
puts "\nGasFree Account Info:"
|
50
|
+
begin
|
51
|
+
account = client.address(key.address)
|
52
|
+
puts " GasFree Address: #{account.gas_free_address}"
|
53
|
+
puts " Active: #{account.active}"
|
54
|
+
puts " Nonce: #{account.nonce}"
|
55
|
+
puts " Assets:"
|
56
|
+
account.assets.each do |asset|
|
57
|
+
puts " #{asset.token_symbol}: #{asset.frozen} frozen"
|
58
|
+
end
|
59
|
+
rescue GasfreeSdk::APIError => e
|
60
|
+
puts " Error: #{e.message}"
|
61
|
+
end
|
62
|
+
|
63
|
+
# Example transfer request (this will fail as it's just a demo)
|
64
|
+
token = tokens.first
|
65
|
+
provider = providers.first
|
66
|
+
|
67
|
+
puts "\nSubmitting Transfer Request:"
|
68
|
+
begin
|
69
|
+
# Create message to sign
|
70
|
+
message = {
|
71
|
+
token: token.token_address,
|
72
|
+
service_provider: provider.address,
|
73
|
+
user: key.address,
|
74
|
+
receiver: "0x1234567890123456789012345678901234567890",
|
75
|
+
value: "1000000",
|
76
|
+
max_fee: token.transfer_fee,
|
77
|
+
deadline: Time.now.to_i + provider.config.default_deadline_duration,
|
78
|
+
version: 1,
|
79
|
+
nonce: 0
|
80
|
+
}
|
81
|
+
|
82
|
+
# Sign the message
|
83
|
+
sig = key.sign(Eth::Util.keccak256(message.to_json))
|
84
|
+
|
85
|
+
# Create and submit transfer request
|
86
|
+
request = GasfreeSdk::Models::TransferRequest.new(
|
87
|
+
message.merge(sig: sig)
|
88
|
+
)
|
89
|
+
|
90
|
+
response = client.submit_transfer(request)
|
91
|
+
puts " Transfer ID: #{response.id}"
|
92
|
+
puts " State: #{response.state}"
|
93
|
+
puts " Estimated Fees:"
|
94
|
+
puts " Activation: #{response.estimated_activate_fee}"
|
95
|
+
puts " Transfer: #{response.estimated_transfer_fee}"
|
96
|
+
|
97
|
+
# Monitor transfer status
|
98
|
+
puts "\nMonitoring Transfer Status:"
|
99
|
+
5.times do
|
100
|
+
status = client.transfer_status(response.id)
|
101
|
+
puts " State: #{status.state}"
|
102
|
+
puts " Transaction Hash: #{status.txn_hash}" if status.txn_hash
|
103
|
+
break if %w[SUCCEED FAILED].include?(status.state)
|
104
|
+
|
105
|
+
sleep 2
|
106
|
+
end
|
107
|
+
rescue GasfreeSdk::APIError => e
|
108
|
+
puts " Error (#{e.code}): #{e.message}"
|
109
|
+
end
|
data/examples/demo.rb
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "gasfree_sdk"
|
6
|
+
|
7
|
+
puts "GasFree SDK Demonstration"
|
8
|
+
puts "========================="
|
9
|
+
|
10
|
+
# Show SDK version
|
11
|
+
puts "SDK Version: #{GasfreeSdk::VERSION}"
|
12
|
+
|
13
|
+
# Configure the SDK
|
14
|
+
GasfreeSdk.configure do |config|
|
15
|
+
config.api_key = "demo-api-key"
|
16
|
+
config.api_secret = "demo-api-secret"
|
17
|
+
config.api_endpoint = "https://demo.gasfree.io/"
|
18
|
+
end
|
19
|
+
|
20
|
+
puts "Configuration:"
|
21
|
+
puts " API Endpoint: #{GasfreeSdk.config.api_endpoint}"
|
22
|
+
puts " API Key: #{GasfreeSdk.config.api_key[0..3]}..."
|
23
|
+
puts " Chain ID: #{GasfreeSdk.config.default_chain_id}"
|
24
|
+
puts ""
|
25
|
+
|
26
|
+
# Show client creation
|
27
|
+
client = GasfreeSdk.client
|
28
|
+
puts "Client created: #{client.class}"
|
29
|
+
puts ""
|
30
|
+
|
31
|
+
# Demonstrate model creation
|
32
|
+
puts "Model Examples:"
|
33
|
+
puts "==============="
|
34
|
+
|
35
|
+
# Token model
|
36
|
+
token = GasfreeSdk::Models::Token.new(
|
37
|
+
token_address: "0x1234567890123456789012345678901234567890",
|
38
|
+
created_at: "2024-01-01T00:00:00Z",
|
39
|
+
updated_at: "2024-01-01T00:00:00Z",
|
40
|
+
activate_fee: "1000000",
|
41
|
+
transfer_fee: "500000",
|
42
|
+
supported: true,
|
43
|
+
symbol: "USDT",
|
44
|
+
decimal: 6
|
45
|
+
)
|
46
|
+
|
47
|
+
puts "Token:"
|
48
|
+
puts " Symbol: #{token.symbol}"
|
49
|
+
puts " Address: #{token.token_address}"
|
50
|
+
puts " Activation Fee: #{token.activate_fee}"
|
51
|
+
puts " Transfer Fee: #{token.transfer_fee}"
|
52
|
+
puts ""
|
53
|
+
|
54
|
+
# Provider model
|
55
|
+
provider = GasfreeSdk::Models::Provider.new(
|
56
|
+
address: "0x1234567890123456789012345678901234567890",
|
57
|
+
name: "Demo Provider",
|
58
|
+
icon: "",
|
59
|
+
website: "https://demo.provider.com",
|
60
|
+
config: {
|
61
|
+
max_pending_transfer: 1,
|
62
|
+
min_deadline_duration: 60,
|
63
|
+
max_deadline_duration: 600,
|
64
|
+
default_deadline_duration: 180
|
65
|
+
}
|
66
|
+
)
|
67
|
+
|
68
|
+
puts "Provider:"
|
69
|
+
puts " Name: #{provider.name}"
|
70
|
+
puts " Address: #{provider.address}"
|
71
|
+
puts " Max Pending: #{provider.config.max_pending_transfer}"
|
72
|
+
puts " Default Deadline: #{provider.config.default_deadline_duration}s"
|
73
|
+
puts ""
|
74
|
+
|
75
|
+
# Transfer request model
|
76
|
+
transfer_request = GasfreeSdk::Models::TransferRequest.new(
|
77
|
+
token: token.token_address,
|
78
|
+
service_provider: provider.address,
|
79
|
+
user: "0x1111111111111111111111111111111111111111",
|
80
|
+
receiver: "0x2222222222222222222222222222222222222222",
|
81
|
+
value: "1000000",
|
82
|
+
max_fee: "100000",
|
83
|
+
deadline: Time.now.to_i + 180,
|
84
|
+
version: 1,
|
85
|
+
nonce: 0,
|
86
|
+
sig: "0x#{"a" * 128}"
|
87
|
+
)
|
88
|
+
|
89
|
+
puts "Transfer Request:"
|
90
|
+
puts " Token: #{transfer_request.token}"
|
91
|
+
puts " Provider: #{transfer_request.service_provider}"
|
92
|
+
puts " User: #{transfer_request.user}"
|
93
|
+
puts " Receiver: #{transfer_request.receiver}"
|
94
|
+
puts " Value: #{transfer_request.value}"
|
95
|
+
puts " Max Fee: #{transfer_request.max_fee}"
|
96
|
+
puts " Deadline: #{Time.at(transfer_request.deadline)}"
|
97
|
+
puts " Version: #{transfer_request.version}"
|
98
|
+
puts ""
|
99
|
+
|
100
|
+
# Transfer response model
|
101
|
+
transfer_response = GasfreeSdk::Models::TransferResponse.new(
|
102
|
+
id: "demo-transfer-id",
|
103
|
+
created_at: "2024-01-01T00:00:00Z",
|
104
|
+
updated_at: "2024-01-01T00:00:00Z",
|
105
|
+
account_address: transfer_request.user,
|
106
|
+
gas_free_address: "0x3333333333333333333333333333333333333333",
|
107
|
+
provider_address: provider.address,
|
108
|
+
target_address: transfer_request.receiver,
|
109
|
+
token_address: token.token_address,
|
110
|
+
amount: transfer_request.value,
|
111
|
+
max_fee: transfer_request.max_fee,
|
112
|
+
signature: transfer_request.sig,
|
113
|
+
nonce: transfer_request.nonce,
|
114
|
+
expired_at: "2024-01-01T00:03:00Z",
|
115
|
+
state: "WAITING"
|
116
|
+
)
|
117
|
+
|
118
|
+
puts "Transfer Response:"
|
119
|
+
puts " ID: #{transfer_response.id}"
|
120
|
+
puts " State: #{transfer_response.state}"
|
121
|
+
puts " Amount: #{transfer_response.amount}"
|
122
|
+
puts " Expires At: #{transfer_response.expired_at}"
|
123
|
+
puts ""
|
124
|
+
|
125
|
+
# Show error classes
|
126
|
+
puts "Error Classes:"
|
127
|
+
puts "=============="
|
128
|
+
puts " Base: #{GasfreeSdk::Error}"
|
129
|
+
puts " Authentication: #{GasfreeSdk::AuthenticationError}"
|
130
|
+
puts " API: #{GasfreeSdk::APIError}"
|
131
|
+
puts " Deadline Exceeded: #{GasfreeSdk::DeadlineExceededError}"
|
132
|
+
puts " Invalid Signature: #{GasfreeSdk::InvalidSignatureError}"
|
133
|
+
puts ""
|
134
|
+
|
135
|
+
puts "Demo completed successfully!"
|
136
|
+
puts ""
|
137
|
+
puts "For real usage:"
|
138
|
+
puts "1. Set GASFREE_API_KEY and GASFREE_API_SECRET environment variables"
|
139
|
+
puts "2. Use examples/basic_usage.rb for a complete workflow example"
|
140
|
+
puts "3. See README.md for full documentation"
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "base64"
|
4
|
+
require "openssl"
|
5
|
+
require "time"
|
6
|
+
|
7
|
+
module GasfreeSdk
|
8
|
+
# Client for interacting with the GasFree API
|
9
|
+
class Client
|
10
|
+
# @return [Faraday::Connection] The HTTP client
|
11
|
+
attr_reader :connection
|
12
|
+
|
13
|
+
# Initialize a new GasFree client
|
14
|
+
def initialize
|
15
|
+
@connection = Faraday.new(url: GasfreeSdk.config.api_endpoint) do |f|
|
16
|
+
f.request :json
|
17
|
+
f.request :retry, GasfreeSdk.config.retry_options
|
18
|
+
f.response :json
|
19
|
+
f.adapter Faraday.default_adapter
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Get all supported tokens
|
24
|
+
# @return [Array<Token>] List of supported tokens
|
25
|
+
def tokens
|
26
|
+
response = get("/api/v1/config/token/all")
|
27
|
+
response.dig("data", "tokens").map { |token| Models::Token.new(token) }
|
28
|
+
end
|
29
|
+
|
30
|
+
# Get all service providers
|
31
|
+
# @return [Array<Provider>] List of service providers
|
32
|
+
def providers
|
33
|
+
response = get("/api/v1/config/provider/all")
|
34
|
+
response.dig("data", "providers").map { |provider| Models::Provider.new(provider) }
|
35
|
+
end
|
36
|
+
|
37
|
+
# Get GasFree account info
|
38
|
+
# @param account_address [String] The user's EOA address
|
39
|
+
# @return [GasFreeAddress] The GasFree account info
|
40
|
+
def address(account_address)
|
41
|
+
response = get("/api/v1/address/#{account_address}")
|
42
|
+
Models::GasFreeAddress.new(response["data"])
|
43
|
+
end
|
44
|
+
|
45
|
+
# Submit a GasFree transfer
|
46
|
+
# @param request [TransferRequest] The transfer request
|
47
|
+
# @return [TransferResponse] The transfer response
|
48
|
+
def submit_transfer(request)
|
49
|
+
response = post("/api/v1/gasfree/submit", request.to_h)
|
50
|
+
Models::TransferResponse.new(response["data"])
|
51
|
+
end
|
52
|
+
|
53
|
+
# Get transfer status
|
54
|
+
# @param trace_id [String] The transfer trace ID
|
55
|
+
# @return [TransferResponse] The transfer status
|
56
|
+
def transfer_status(trace_id)
|
57
|
+
response = get("/api/v1/gasfree/#{trace_id}")
|
58
|
+
Models::TransferResponse.new(response["data"])
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# Make a GET request
|
64
|
+
# @param path [String] The API path
|
65
|
+
# @param params [Hash] Query parameters
|
66
|
+
# @return [Hash] The response data
|
67
|
+
def get(path, params = {})
|
68
|
+
timestamp = Time.now.to_i
|
69
|
+
response = connection.get(path, params) do |req|
|
70
|
+
sign_request(req, "GET", path, timestamp)
|
71
|
+
end
|
72
|
+
handle_response(response)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Make a POST request
|
76
|
+
# @param path [String] The API path
|
77
|
+
# @param body [Hash] Request body
|
78
|
+
# @return [Hash] The response data
|
79
|
+
def post(path, body)
|
80
|
+
timestamp = Time.now.to_i
|
81
|
+
response = connection.post(path) do |req|
|
82
|
+
req.body = body
|
83
|
+
sign_request(req, "POST", path, timestamp)
|
84
|
+
end
|
85
|
+
handle_response(response)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Sign an API request
|
89
|
+
# @param request [Faraday::Request] The request to sign
|
90
|
+
# @param method [String] HTTP method
|
91
|
+
# @param path [String] Request path
|
92
|
+
# @param timestamp [Integer] Request timestamp
|
93
|
+
def sign_request(request, method, path, timestamp)
|
94
|
+
api_key = GasfreeSdk.config.api_key
|
95
|
+
api_secret = GasfreeSdk.config.api_secret
|
96
|
+
|
97
|
+
raise AuthenticationError, "API key and secret are required" if api_key.nil? || api_secret.nil?
|
98
|
+
|
99
|
+
message = "#{method}#{path}#{timestamp}"
|
100
|
+
signature = Base64.strict_encode64(
|
101
|
+
OpenSSL::HMAC.digest("SHA256", api_secret, message)
|
102
|
+
)
|
103
|
+
|
104
|
+
request.headers["Timestamp"] = timestamp.to_s
|
105
|
+
request.headers["Authorization"] = "ApiKey #{api_key}:#{signature}"
|
106
|
+
end
|
107
|
+
|
108
|
+
# Handle API response
|
109
|
+
# @param response [Faraday::Response] The API response
|
110
|
+
# @return [Hash] The response data
|
111
|
+
# @raise [APIError] If the response indicates an error
|
112
|
+
def handle_response(response)
|
113
|
+
data = response.body
|
114
|
+
|
115
|
+
return data if data["code"] == 200
|
116
|
+
|
117
|
+
raise GasfreeSdk.build_error(
|
118
|
+
code: data["code"],
|
119
|
+
reason: data["reason"],
|
120
|
+
message: data["message"]
|
121
|
+
)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|