ton-sdk-ruby 0.0.1
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/bin/ton-sdk-ruby +7 -0
- data/lib/ton-sdk-ruby/bit_array/bit_array.rb +169 -0
- data/lib/ton-sdk-ruby/boc/builder.rb +261 -0
- data/lib/ton-sdk-ruby/boc/cell.rb +362 -0
- data/lib/ton-sdk-ruby/boc/hashmap.rb +398 -0
- data/lib/ton-sdk-ruby/boc/mask.rb +46 -0
- data/lib/ton-sdk-ruby/boc/serializer.rb +428 -0
- data/lib/ton-sdk-ruby/boc/slice.rb +335 -0
- data/lib/ton-sdk-ruby/johnny_mnemonic/ton_mnemonic.rb +144 -0
- data/lib/ton-sdk-ruby/johnny_mnemonic/utils.rb +40 -0
- data/lib/ton-sdk-ruby/johnny_mnemonic/words/english.json +2050 -0
- data/lib/ton-sdk-ruby/providers/provider.rb +41 -0
- data/lib/ton-sdk-ruby/providers/toncenter.rb +71 -0
- data/lib/ton-sdk-ruby/types/address.rb +203 -0
- data/lib/ton-sdk-ruby/types/block.rb +388 -0
- data/lib/ton-sdk-ruby/types/coins.rb +188 -0
- data/lib/ton-sdk-ruby/utils/bits.rb +25 -0
- data/lib/ton-sdk-ruby/utils/checksum.rb +46 -0
- data/lib/ton-sdk-ruby/utils/hash.rb +15 -0
- data/lib/ton-sdk-ruby/utils/helpers.rb +161 -0
- data/lib/ton-sdk-ruby/utils/numbers.rb +42 -0
- data/lib/ton-sdk-ruby/version.rb +4 -0
- data/lib/ton-sdk-ruby.rb +29 -0
- metadata +137 -0
@@ -0,0 +1,41 @@
|
|
1
|
+
module TonSdkRuby
|
2
|
+
class Provider
|
3
|
+
attr_accessor :provider
|
4
|
+
|
5
|
+
def initialize(provider)
|
6
|
+
@provider = provider
|
7
|
+
end
|
8
|
+
|
9
|
+
def get_address_information(address)
|
10
|
+
provider.get_address_information(address)
|
11
|
+
end
|
12
|
+
|
13
|
+
def get_extended_address_information(address)
|
14
|
+
provider.get_extended_address_information(address)
|
15
|
+
end
|
16
|
+
|
17
|
+
def get_address_balance(address)
|
18
|
+
provider.get_address_balance(address)
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_token_data(address)
|
22
|
+
provider.get_token_data(address)
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_transactions(address, archival, limit, lt, hash, to_lt)
|
26
|
+
provider.get_transactions(address, archival, limit, lt, hash, to_lt)
|
27
|
+
end
|
28
|
+
|
29
|
+
def run_get_method(address, method, stack = [])
|
30
|
+
provider.run_get_method(address, method, stack)
|
31
|
+
end
|
32
|
+
|
33
|
+
def send_boc(boc)
|
34
|
+
provider.send_boc(boc)
|
35
|
+
end
|
36
|
+
|
37
|
+
def estimate_fee(address, body, init_code, init_data, ignore_chksig = false)
|
38
|
+
provider.estimate_fee(address, body, init_code, init_data, ignore_chksig)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module TonSdkRuby
|
2
|
+
|
3
|
+
class TonCenter
|
4
|
+
URL = 'https://toncenter.com/api/v2/jsonRPC'
|
5
|
+
attr_accessor :api_key, :url
|
6
|
+
|
7
|
+
def initialize(url = nil, api_key)
|
8
|
+
@url = url || URL
|
9
|
+
@api_key = api_key
|
10
|
+
end
|
11
|
+
|
12
|
+
def send_request(metod, params)
|
13
|
+
headers = {
|
14
|
+
"X-API-Key": api_key
|
15
|
+
}
|
16
|
+
body = jrpc_wrap(metod, params)
|
17
|
+
read_post_json_from_link(url, body, headers)
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_address_information(address)
|
21
|
+
send_request('getAddressInformation', {address: address})
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_address_balance(address)
|
25
|
+
send_request('getAddressBalance', {address: address})
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_address_state(address)
|
29
|
+
send_request('getAddressState', {address: address})
|
30
|
+
end
|
31
|
+
|
32
|
+
def get_token_data(address)
|
33
|
+
send_request('getTokenData', {address: address})
|
34
|
+
end
|
35
|
+
|
36
|
+
def get_transactions(address, archival, limit, lt, hash, to_lt)
|
37
|
+
params = {
|
38
|
+
address: address, archival: archival, limit: limit, lt: lt, hash: hash, to_lt: to_lt
|
39
|
+
}
|
40
|
+
send_request('getTransactions', params)
|
41
|
+
end
|
42
|
+
|
43
|
+
def run_get_method(address, method, stack = [])
|
44
|
+
params = {
|
45
|
+
address: address, method: method, stack: stack
|
46
|
+
}
|
47
|
+
send_request('runGetMethod', params)
|
48
|
+
end
|
49
|
+
|
50
|
+
def send_boc(boc)
|
51
|
+
send_request('sendBoc', {boc: boc})
|
52
|
+
end
|
53
|
+
|
54
|
+
def estimate_fee(address, body, init_code, init_data, ignore_chksig = false)
|
55
|
+
params = {
|
56
|
+
address: address, body: body, init_code: init_code, init_data: init_data, ignore_chksig: ignore_chksig
|
57
|
+
}
|
58
|
+
send_request('estimateFee', params)
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
def jrpc_wrap(method, params = {})
|
63
|
+
{
|
64
|
+
id: '1',
|
65
|
+
jsonrpc: '2.0',
|
66
|
+
method: method,
|
67
|
+
params: params.compact
|
68
|
+
}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module TonSdkRuby
|
4
|
+
|
5
|
+
FLAG_BOUNCEABLE = 0x11
|
6
|
+
FLAG_NON_BOUNCEABLE = 0x51
|
7
|
+
FLAG_TEST_ONLY = 0x80
|
8
|
+
|
9
|
+
class Address
|
10
|
+
NONE = nil
|
11
|
+
|
12
|
+
class Type
|
13
|
+
BASE64 = 'base64'
|
14
|
+
RAW = 'raw'
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :hash, :workchain, :bounceable, :test_only, :type
|
18
|
+
|
19
|
+
def hash
|
20
|
+
Array.new(@hash)
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(address, options = {})
|
24
|
+
options = {workchain: 0, bounceable: false, test_only: false}.merge(options)
|
25
|
+
is_address = Address.is_address?(address)
|
26
|
+
is_encoded = Address.is_encoded?(address)
|
27
|
+
is_raw = Address.is_raw?(address)
|
28
|
+
|
29
|
+
case true
|
30
|
+
when is_address
|
31
|
+
result = Address.parse_address(address)
|
32
|
+
when is_encoded
|
33
|
+
result = Address.parse_encoded(address)
|
34
|
+
when is_raw
|
35
|
+
result = Address.parse_raw(address)
|
36
|
+
else
|
37
|
+
raise 'Address: can\'t parse address. Unknown type.'
|
38
|
+
end
|
39
|
+
|
40
|
+
if result.nil?
|
41
|
+
raise 'Address: can\'t parse address. Unknown type.'
|
42
|
+
end
|
43
|
+
|
44
|
+
@workchain = options[:workchain] || result[:workchain]
|
45
|
+
@bounceable = options[:bounceable] || result[:bounceable]
|
46
|
+
@test_only = options[:test_only] || result[:test_only]
|
47
|
+
@hash = result[:hash]
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.encode_tag(options)
|
51
|
+
bounceable = options[:bounceable]
|
52
|
+
test_only = options[:test_only]
|
53
|
+
tag = bounceable ? FLAG_BOUNCEABLE : FLAG_NON_BOUNCEABLE
|
54
|
+
|
55
|
+
test_only ? (tag | FLAG_TEST_ONLY) : tag
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.decode_tag(tag)
|
59
|
+
data = tag
|
60
|
+
test_only = (data & FLAG_TEST_ONLY) != 0
|
61
|
+
|
62
|
+
if test_only
|
63
|
+
data ^= FLAG_TEST_ONLY
|
64
|
+
end
|
65
|
+
|
66
|
+
if ![FLAG_BOUNCEABLE, FLAG_NON_BOUNCEABLE].include?(data)
|
67
|
+
raise 'Address: bad address tag.'
|
68
|
+
end
|
69
|
+
|
70
|
+
bounceable = data == FLAG_BOUNCEABLE
|
71
|
+
|
72
|
+
{
|
73
|
+
bounceable: bounceable,
|
74
|
+
test_only: test_only
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
def eq(address)
|
79
|
+
address == self ||
|
80
|
+
(bytes_compare(hash, address.hash) && workchain == address.workchain)
|
81
|
+
end
|
82
|
+
|
83
|
+
def to_s(options = {})
|
84
|
+
type = options[:type] || Type::BASE64
|
85
|
+
workchain = options[:workchain] || self.workchain
|
86
|
+
bounceable = options[:bounceable] || self.bounceable
|
87
|
+
test_only = options[:test_only] || self.test_only
|
88
|
+
url_safe = options.key?(:url_safe) ? options[:url_safe] : true
|
89
|
+
|
90
|
+
raise 'Address: workchain must be int8.' unless workchain.is_a?(Numeric) && workchain >= -128 && workchain < 128
|
91
|
+
raise 'Address: bounceable flag must be a boolean.' unless [true, false].include?(bounceable)
|
92
|
+
raise 'Address: testOnly flag must be a boolean.' unless [true, false].include?(test_only)
|
93
|
+
raise 'Address: urlSafe flag must be a boolean.' unless [true, false].include?(url_safe)
|
94
|
+
|
95
|
+
if type == Type::RAW
|
96
|
+
"#{workchain}:#{bytes_to_hex(hash)}"
|
97
|
+
else
|
98
|
+
tag = Address.encode_tag(bounceable: bounceable, test_only: test_only)
|
99
|
+
address = [tag, workchain] + hash
|
100
|
+
checksum = crc16_bytes_be(address)
|
101
|
+
base64 = bytes_to_base64(address + checksum)
|
102
|
+
|
103
|
+
if url_safe
|
104
|
+
base64 = base64.tr('/', '_').tr('+', '-')
|
105
|
+
else
|
106
|
+
base64 = base64.tr('_', '/').tr('-', '+')
|
107
|
+
end
|
108
|
+
|
109
|
+
base64
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def self.is_encoded?(address)
|
118
|
+
re = /^([a-zA-Z0-9_-]{48}|[a-zA-Z0-9\/\+]{48})$/
|
119
|
+
address.is_a?(String) && re.match?(address)
|
120
|
+
end
|
121
|
+
|
122
|
+
def self.is_raw?(address)
|
123
|
+
re = /^-?[0-9]:[a-zA-Z0-9]{64}$/
|
124
|
+
address.is_a?(String) && re.match?(address)
|
125
|
+
end
|
126
|
+
|
127
|
+
def self.parse_encoded(value)
|
128
|
+
base64 = value.tr('-', '+').tr('_', '/')
|
129
|
+
bytes = base64_to_bytes(base64)
|
130
|
+
data = Array.new(bytes)
|
131
|
+
address = data.shift(34)
|
132
|
+
checksum = data.shift(2)
|
133
|
+
crc = crc16_bytes_be(address)
|
134
|
+
|
135
|
+
raise 'Address: can\'t parse address. Wrong checksum.' unless bytes_compare(crc, checksum)
|
136
|
+
|
137
|
+
buffer = address.shift(2).pack('C2')
|
138
|
+
|
139
|
+
tag_and_wc = buffer.unpack('CS>')
|
140
|
+
tag = tag_and_wc.first
|
141
|
+
workchain = tag_and_wc.last.to_i
|
142
|
+
|
143
|
+
hash = address.shift(32)
|
144
|
+
|
145
|
+
decoded_tag = decode_tag(tag)
|
146
|
+
bounceable = decoded_tag[:bounceable]
|
147
|
+
test_only = decoded_tag[:test_only]
|
148
|
+
|
149
|
+
{
|
150
|
+
bounceable: bounceable,
|
151
|
+
test_only: test_only,
|
152
|
+
workchain: workchain,
|
153
|
+
hash: hash,
|
154
|
+
type: Type::BASE64
|
155
|
+
}
|
156
|
+
end
|
157
|
+
|
158
|
+
def self.parse_address(value)
|
159
|
+
workchain = value.workchain
|
160
|
+
bounceable = value.bounceable
|
161
|
+
test_only = value.test_only
|
162
|
+
hash = value.hash.clone
|
163
|
+
type = value.type
|
164
|
+
|
165
|
+
{
|
166
|
+
bounceable: bounceable,
|
167
|
+
test_only: test_only,
|
168
|
+
workchain: workchain,
|
169
|
+
hash: hash,
|
170
|
+
type: type
|
171
|
+
}
|
172
|
+
end
|
173
|
+
|
174
|
+
def self.parse_raw(value)
|
175
|
+
data = value.split(':')
|
176
|
+
workchain = data[0].to_i
|
177
|
+
hash = hex_to_bytes(data[1])
|
178
|
+
bounceable = false
|
179
|
+
test_only = false
|
180
|
+
|
181
|
+
{
|
182
|
+
bounceable: bounceable,
|
183
|
+
test_only: test_only,
|
184
|
+
workchain: workchain,
|
185
|
+
hash: hash,
|
186
|
+
type: Type::RAW
|
187
|
+
}
|
188
|
+
end
|
189
|
+
|
190
|
+
def self.is_address?(address)
|
191
|
+
address.is_a?(Address)
|
192
|
+
end
|
193
|
+
|
194
|
+
def self.is_valid?(address)
|
195
|
+
begin
|
196
|
+
new(address)
|
197
|
+
true
|
198
|
+
rescue
|
199
|
+
false
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
@@ -0,0 +1,388 @@
|
|
1
|
+
module TonSdkRuby
|
2
|
+
class TickTockOptions
|
3
|
+
attr_accessor :tick, :tock
|
4
|
+
|
5
|
+
def initialize(tick, tock)
|
6
|
+
@tick = tick
|
7
|
+
@tock = tock
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class SimpleLibOptions
|
12
|
+
attr_accessor :public, :root
|
13
|
+
|
14
|
+
def initialize(public_value, root_value)
|
15
|
+
@public = public_value
|
16
|
+
@root = root_value
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class TickTock
|
21
|
+
attr_reader :data, :cell
|
22
|
+
|
23
|
+
def initialize(options)
|
24
|
+
@data = options
|
25
|
+
@cell = Builder.new
|
26
|
+
.store_bit(options[:tick])
|
27
|
+
.store_bit(options[:tock])
|
28
|
+
.cell
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.parse(cs)
|
32
|
+
tick = cs.load_bit == 1
|
33
|
+
tock = cs.load_bit == 1
|
34
|
+
options = { tick: tick, tock: tock }
|
35
|
+
new(options)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class SimpleLib
|
40
|
+
attr_reader :data, :cell
|
41
|
+
|
42
|
+
def initialize(options)
|
43
|
+
@data = options
|
44
|
+
@cell = Builder.new
|
45
|
+
.store_bit(options[:public])
|
46
|
+
.store_ref(options[:root])
|
47
|
+
.cell
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.parse(cs)
|
51
|
+
simple_lib = SimpleLib.new(
|
52
|
+
public: cs.load_bit == 1,
|
53
|
+
root: cs.load_ref
|
54
|
+
)
|
55
|
+
simple_lib
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class StateInitOptions
|
60
|
+
attr_accessor :split_depth, :special, :code, :data, :library
|
61
|
+
|
62
|
+
def initialize(options = {})
|
63
|
+
@split_depth = options[:split_depth]
|
64
|
+
@special = options[:special]
|
65
|
+
@code = options[:code]
|
66
|
+
@data = options[:data]
|
67
|
+
@library = options[:library]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class StateInit
|
72
|
+
attr_reader :data, :cell
|
73
|
+
|
74
|
+
def initialize(state_init_options)
|
75
|
+
@data = state_init_options
|
76
|
+
|
77
|
+
b = Builder.new
|
78
|
+
|
79
|
+
# split_depth
|
80
|
+
if data.split_depth
|
81
|
+
b.store_uint(data.split_depth, 5)
|
82
|
+
else
|
83
|
+
b.store_bit(0)
|
84
|
+
end
|
85
|
+
|
86
|
+
# special
|
87
|
+
b.store_maybe_ref(data.special&.cell)
|
88
|
+
# code
|
89
|
+
b.store_maybe_ref(data.code)
|
90
|
+
b.store_maybe_ref(data.data)
|
91
|
+
b.store_dict(data.library)
|
92
|
+
|
93
|
+
@cell = b.cell
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.parse(cs)
|
97
|
+
options = StateInitOptions.new()
|
98
|
+
|
99
|
+
options.split_depth = cs.load_bit.nonzero? ? cs.load_uint(5) : nil
|
100
|
+
options.special = TickTock.parse(cs) if cs.load_bit.nonzero?
|
101
|
+
options.code = cs.load_ref if cs.load_bit.nonzero?
|
102
|
+
options.data = cs.load_ref if cs.load_bit.nonzero?
|
103
|
+
|
104
|
+
deserializers = {
|
105
|
+
key: ->(k) { k },
|
106
|
+
value: ->(v) { SimpleLib.parse(Slice.parse(v.parse)) }
|
107
|
+
}
|
108
|
+
|
109
|
+
options.library = HashmapE.parse(256, cs, deserializers)
|
110
|
+
|
111
|
+
new(options)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
class IntMsgInfo
|
116
|
+
attr_accessor :tag, :ihr_disabled, :bounce, :bounced, :src, :dest, :value,
|
117
|
+
:ihr_fee, :fwd_fee, :created_lt, :created_at
|
118
|
+
|
119
|
+
def initialize(options = {})
|
120
|
+
@tag = 'int_msg_info'
|
121
|
+
@ihr_disabled = options[:ihr_disabled]
|
122
|
+
@bounce = options[:bounce]
|
123
|
+
@bounced = options[:bounced]
|
124
|
+
@src = options[:src]
|
125
|
+
@dest = options[:dest]
|
126
|
+
@value = options[:value]
|
127
|
+
@ihr_fee = options[:ihr_fee]
|
128
|
+
@fwd_fee = options[:fwd_fee]
|
129
|
+
@created_lt = options[:created_lt]
|
130
|
+
@created_at = options[:created_at]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
class ExtInMsgInfo
|
135
|
+
attr_accessor :tag, :src, :dest, :import_fee
|
136
|
+
|
137
|
+
def initialize(options = {})
|
138
|
+
@tag = 'ext_in_msg_info'
|
139
|
+
@src = options[:src]
|
140
|
+
@dest = options[:dest]
|
141
|
+
@import_fee = options[:import_fee]
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
class CommonMsgInfo
|
146
|
+
attr_reader :data, :cell
|
147
|
+
|
148
|
+
def initialize(data)
|
149
|
+
case data.tag
|
150
|
+
when 'int_msg_info'
|
151
|
+
int_msg_info(data)
|
152
|
+
when 'ext_in_msg_info'
|
153
|
+
ext_in_msg_info(data)
|
154
|
+
else
|
155
|
+
raise 'OutAction: unexpected tag'
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
private
|
160
|
+
|
161
|
+
def int_msg_info(data)
|
162
|
+
b = Builder.new
|
163
|
+
.store_bits([0]) # int_msg_info$0
|
164
|
+
.store_bit(data.ihr_disabled || false) # ihr_disabled:Bool
|
165
|
+
.store_bit(data.bounce) # bounce:Bool
|
166
|
+
.store_bit(data.bounced || false) # bounced:Bool
|
167
|
+
.store_address(data.src || Address::NONE) # src:MsgAddressInt
|
168
|
+
.store_address(data.dest) # dest:MsgAddressInt
|
169
|
+
.store_coins(data.value) # value: -> grams:Grams
|
170
|
+
.store_bit(0) # value: -> other:ExtraCurrencyCollection
|
171
|
+
.store_coins(data.ihr_fee || Coins.new(0)) # ihr_fee:Grams
|
172
|
+
.store_coins(data.fwd_fee || Coins.new(0)) # fwd_fee:Grams
|
173
|
+
.store_uint(data.created_lt || 0, 64) # created_lt:uint64
|
174
|
+
.store_uint(data.created_at || 0, 32) # created_at:uint32
|
175
|
+
|
176
|
+
@data = data
|
177
|
+
@cell = b.cell
|
178
|
+
end
|
179
|
+
|
180
|
+
def ext_in_msg_info(data)
|
181
|
+
b = Builder.new
|
182
|
+
.store_bits([1, 0]) # ext_in_msg_info$10
|
183
|
+
.store_address(data.src || Address::NONE) # src:MsgAddress
|
184
|
+
.store_address(data.dest) # dest:MsgAddressExt
|
185
|
+
.store_coins(data.import_fee || Coins.new(0)) # import_fee:Grams
|
186
|
+
|
187
|
+
@data = data
|
188
|
+
@cell = b.cell
|
189
|
+
end
|
190
|
+
|
191
|
+
public
|
192
|
+
|
193
|
+
def self.parse(cs)
|
194
|
+
frst = cs.load_bit
|
195
|
+
|
196
|
+
if frst == 1
|
197
|
+
scnd = cs.load_bit
|
198
|
+
raise 'CommonMsgInfo: ext_out_msg_info unimplemented' if scnd == 1
|
199
|
+
|
200
|
+
return new(ExtInMsgInfo.new(
|
201
|
+
tag: 'ext_in_msg_info',
|
202
|
+
src: cs.load_address,
|
203
|
+
dest: cs.load_address,
|
204
|
+
import_fee: cs.load_coins
|
205
|
+
))
|
206
|
+
end
|
207
|
+
|
208
|
+
if frst == 0
|
209
|
+
data = IntMsgInfo.new({
|
210
|
+
tag: 'int_msg_info',
|
211
|
+
ihr_disabled: cs.load_bit == 1,
|
212
|
+
bounce: cs.load_bit == 1,
|
213
|
+
bounced: cs.load_bit == 1,
|
214
|
+
src: cs.load_address,
|
215
|
+
dest: cs.load_address,
|
216
|
+
value: cs.load_coins
|
217
|
+
})
|
218
|
+
|
219
|
+
# TODO: support with ExtraCurrencyCollection
|
220
|
+
cs.skip_bits(1)
|
221
|
+
|
222
|
+
data.ihr_fee = cs.load_coins
|
223
|
+
data.fwd_fee = cs.load_coins
|
224
|
+
data.created_lt = cs.load_uint(64)
|
225
|
+
data.created_at = cs.load_uint(32)
|
226
|
+
|
227
|
+
return new(data)
|
228
|
+
end
|
229
|
+
|
230
|
+
raise 'CommonMsgInfo: invalid tag'
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
class MessageOptions
|
235
|
+
attr_accessor :info, :init, :body
|
236
|
+
|
237
|
+
def initialize(options = {})
|
238
|
+
@info = options[:info]
|
239
|
+
@init = options[:init]
|
240
|
+
@body = options[:body]
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
class Message
|
245
|
+
attr_reader :data, :cell
|
246
|
+
|
247
|
+
def initialize(options)
|
248
|
+
@data = options
|
249
|
+
b = Builder.new
|
250
|
+
b.store_slice(data.info.cell.parse) # info:CommonMsgInfo
|
251
|
+
|
252
|
+
# init:(Maybe (Either StateInit ^StateInit))
|
253
|
+
if data.init
|
254
|
+
b.store_bits([1, 0])
|
255
|
+
b.store_slice(data.init.cell.parse)
|
256
|
+
else
|
257
|
+
b.store_bit(0)
|
258
|
+
end
|
259
|
+
|
260
|
+
# body:(Either X ^X)
|
261
|
+
if data.body
|
262
|
+
if (b.bits.length + data.body.bits.length + 1 <= 1023) &&
|
263
|
+
(b.refs.length + data.body.refs.length <= 4)
|
264
|
+
b.store_bit(0)
|
265
|
+
b.store_slice(data.body.parse)
|
266
|
+
else
|
267
|
+
b.store_bit(1)
|
268
|
+
b.store_ref(data.body)
|
269
|
+
end
|
270
|
+
else
|
271
|
+
b.store_bit(0)
|
272
|
+
end
|
273
|
+
|
274
|
+
@cell = b.cell
|
275
|
+
end
|
276
|
+
|
277
|
+
def self.parse(cs)
|
278
|
+
data = {}
|
279
|
+
data.info = CommonMsgInfo.parse(cs)
|
280
|
+
|
281
|
+
if cs.load_bit
|
282
|
+
init = cs.load_bit ? cs.load_ref.parse : cs
|
283
|
+
data.init = StateInit.parse(init)
|
284
|
+
end
|
285
|
+
|
286
|
+
if cs.load_bit
|
287
|
+
data.body = cs.load_ref
|
288
|
+
else
|
289
|
+
data.body = Builder.new.store_slice(cs).cell
|
290
|
+
end
|
291
|
+
|
292
|
+
new(data)
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
class ActionSendMsg
|
297
|
+
attr_reader :tag, :mode, :out_msg
|
298
|
+
|
299
|
+
def initialize(options)
|
300
|
+
@tag = 'action_send_msg'
|
301
|
+
@mode = options[:mode]
|
302
|
+
@out_msg = options[:out_msg]
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
class ActionSetCode
|
307
|
+
attr_reader :tag, :new_code
|
308
|
+
|
309
|
+
def initialize(options)
|
310
|
+
@tag = 'action_set_code'
|
311
|
+
@new_code = options[:new_code]
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
class OutAction
|
316
|
+
def initialize(data)
|
317
|
+
case data.tag
|
318
|
+
when 'action_send_msg' then action_send_msg(data)
|
319
|
+
when 'action_set_code' then action_set_code(data)
|
320
|
+
else
|
321
|
+
raise 'OutAction: unexpected tag'
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
private def action_send_msg(data)
|
326
|
+
b = Builder.new
|
327
|
+
b.store_uint(0x0ec3c86d, 32)
|
328
|
+
b.store_uint(data.mode, 8)
|
329
|
+
b.store_ref(data.out_msg.cell)
|
330
|
+
@data = data
|
331
|
+
@cell = b.cell
|
332
|
+
end
|
333
|
+
|
334
|
+
private def action_set_code(data)
|
335
|
+
b = Builder.new
|
336
|
+
b.store_uint(0xad4de08e, 32)
|
337
|
+
b.store_ref(data.new_code)
|
338
|
+
@data = data
|
339
|
+
@cell = b.cell
|
340
|
+
end
|
341
|
+
|
342
|
+
def self.parse(cs)
|
343
|
+
tag = cs.load_uint(32)
|
344
|
+
data = {}
|
345
|
+
|
346
|
+
case tag
|
347
|
+
when 0x0ec3c86d # action_send_msg
|
348
|
+
mode = cs.load_uint(8)
|
349
|
+
out_msg = cs.load_ref.parse
|
350
|
+
data = ActionSendMsg.new({ tag: 'action_send_msg', mode: mode, out_msg: Message.parse(out_msg) })
|
351
|
+
when 0xad4de08e # action_set_code
|
352
|
+
data = ActionSetCode.new({ tag: 'action_set_code', new_code: cs.load_ref })
|
353
|
+
else
|
354
|
+
raise 'OutAction: unexpected tag'
|
355
|
+
end
|
356
|
+
|
357
|
+
OutAction.new(data)
|
358
|
+
end
|
359
|
+
|
360
|
+
attr_reader :data, :cell
|
361
|
+
end
|
362
|
+
|
363
|
+
class OutListOptions
|
364
|
+
attr_reader :actions
|
365
|
+
|
366
|
+
def initialize(options)
|
367
|
+
@actions = options[:actions]
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
class OutList
|
372
|
+
|
373
|
+
attr_reader :cell, :data
|
374
|
+
def initialize(action)
|
375
|
+
@data = action
|
376
|
+
cur = Builder.new.cell
|
377
|
+
|
378
|
+
data.actions.each do |a|
|
379
|
+
cur = Builder.new
|
380
|
+
.store_ref(cur)
|
381
|
+
.store_slice(a.cell.parse)
|
382
|
+
.cell
|
383
|
+
end
|
384
|
+
|
385
|
+
@cell = cur
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|