rubidity 0.9.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/CHANGELOG.md +3 -0
- data/Manifest.txt +9 -0
- data/README.md +603 -0
- data/Rakefile +30 -0
- data/lib/rubidity/builder.rb +215 -0
- data/lib/rubidity/codegen.rb +203 -0
- data/lib/rubidity/proc.rb +42 -0
- data/lib/rubidity/version.rb +23 -0
- data/lib/rubidity.rb +42 -0
- metadata +108 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e4839b48c7fbebac1bbc15722701b60bc26494cba550ddea5385c8c2d9e5b76d
|
4
|
+
data.tar.gz: 5e8ca8ed8c4865aa857f86554574af2b58297038551587d004ef2104f523dfc0
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b3e6ba134973d9e02957a4c8232e102dba2f47bba5da411deda393b24273b066cdd079ec15c2c5f874de84557b623f2117f33bdfb4fbd2692674b7658ccf0f97
|
7
|
+
data.tar.gz: 2b186987456561a83229bc20a1e50316ce218c1207d17d8bc1a5267c3fcfb41d7ef97315a06fdaad82e630858d1d2ee5122f389c4ab16b275b964ea0d198a3c9
|
data/CHANGELOG.md
ADDED
data/Manifest.txt
ADDED
data/README.md
ADDED
@@ -0,0 +1,603 @@
|
|
1
|
+
# Rubidity "Classic / O.G." Contract Builder
|
2
|
+
|
3
|
+
**DISCLAIMER: the rubidity gem version is different
|
4
|
+
from the rubidity built into the facet vm / app and i (Gerald Bauer)
|
5
|
+
am NOT affiliated with facet computing inc. (middlemarch et al) or paid to work on the rubidity gem.**
|
6
|
+
|
7
|
+
|
8
|
+
rubidity gem - rubidity "classic / o.g." contract builder; trying the impossible and square the circle, that is, a rubidity "classic / o.g." dsl builder generating rubysol "more ruby-ish" contract classes.
|
9
|
+
|
10
|
+
|
11
|
+
* home :: [github.com/s6ruby/rubidity](https://github.com/s6ruby/rubidity)
|
12
|
+
* bugs :: [github.com/s6ruby/rubidity/issues](https://github.com/s6ruby/rubidity/issues)
|
13
|
+
* gem :: [rubygems.org/gems/rubidity](https://rubygems.org/gems/rubidity)
|
14
|
+
* rdoc :: [rubydoc.info/gems/rubidity](http://rubydoc.info/gems/rubidity)
|
15
|
+
|
16
|
+
|
17
|
+
|
18
|
+
## What's Solidity?! What's Rubidity?! What's Rubysol?!
|
19
|
+
|
20
|
+
See [**Solidity - Contract Application Binary Interface (ABI) Specification** »](https://docs.soliditylang.org/en/latest/abi-spec.html)
|
21
|
+
|
22
|
+
See [**Rubidity - Ruby for Layer 1 (L1) Contracts / Protocols with "Off-Chain" Indexer** »](https://github.com/s6ruby/rubidity)
|
23
|
+
|
24
|
+
See [**Rubysol - Ruby for Layer 1 (L1) Contracts / Protocols with "Off-Chain" Indexer** »](https://github.com/s6ruby/rubidity/tree/master/rubysol)
|
25
|
+
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
## Usage
|
31
|
+
|
32
|
+
[Contract Sample №1 - HelloWorld](#contract-sample-1---helloworld) •
|
33
|
+
[Contract Sample №2 - PublicMintERC20](#contract-sample-2---publicminterc20) •
|
34
|
+
[Contract Sample №3 - SupplyChain](#contract-sample-3---supplychain)
|
35
|
+
|
36
|
+
|
37
|
+
### Contract Sample №1 - HelloWorld
|
38
|
+
|
39
|
+
Let's try the HelloWorld contract.
|
40
|
+
|
41
|
+
[HelloWorld](contracts/HelloWorld.rb) - main contract in rubidity classic / o.g. style
|
42
|
+
|
43
|
+
``` ruby
|
44
|
+
pragma :rubidity, "1.0.0"
|
45
|
+
|
46
|
+
contract :HelloWorld do
|
47
|
+
function :getHelloWorld, {}, :public, :pure, returns: :string do
|
48
|
+
return "Hello, world!"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
53
|
+
Now let's square the circle and try the impossible.
|
54
|
+
Let's run the HelloWorld contract.
|
55
|
+
|
56
|
+
|
57
|
+
``` ruby
|
58
|
+
require 'rubidity'
|
59
|
+
|
60
|
+
# load (parse) and generate contract classes
|
61
|
+
Contract.load( 'HelloWorld' )
|
62
|
+
|
63
|
+
|
64
|
+
# try out contract classes
|
65
|
+
pp HelloWorld
|
66
|
+
pp HelloWorld.name
|
67
|
+
|
68
|
+
contract = HelloWorld.new
|
69
|
+
pp contract
|
70
|
+
|
71
|
+
pp contract.getHelloWorld
|
72
|
+
#=> "Hello, world!"
|
73
|
+
```
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
### Contract Sample №2 - PublicMintERC20
|
79
|
+
|
80
|
+
Let's try the PublicMintERC20 contract.
|
81
|
+
|
82
|
+
|
83
|
+
[ERC20](contracts/ERC20.rb) - base contract in rubidity classic / o.g. style
|
84
|
+
|
85
|
+
``` ruby
|
86
|
+
pragma :rubidity, "1.0.0"
|
87
|
+
|
88
|
+
contract :ERC20, abstract: true do
|
89
|
+
event :Transfer, { from: :address, to: :address, amount: :uint256 }
|
90
|
+
event :Approval, { owner: :address, spender: :address, amount: :uint256 }
|
91
|
+
|
92
|
+
string :public, :name
|
93
|
+
string :public, :symbol
|
94
|
+
uint8 :public, :decimals
|
95
|
+
|
96
|
+
uint256 :public, :totalSupply
|
97
|
+
|
98
|
+
mapping ({ address: :uint256 }), :public, :balanceOf
|
99
|
+
mapping ({ address: mapping(address: :uint256) }), :public, :allowance
|
100
|
+
|
101
|
+
|
102
|
+
constructor(name: :string, symbol: :string, decimals: :uint8) do
|
103
|
+
s.name = name
|
104
|
+
s.symbol = symbol
|
105
|
+
s.decimals = decimals
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
function :approve, { spender: :address, amount: :uint256 }, :public, :virtual, returns: :bool do
|
110
|
+
s.allowance[msg.sender][spender] = amount
|
111
|
+
|
112
|
+
emit :Approval, owner: msg.sender, spender: spender, amount: amount
|
113
|
+
|
114
|
+
return true
|
115
|
+
end
|
116
|
+
|
117
|
+
function :transfer, { to: :address, amount: :uint256 }, :public, :virtual, returns: :bool do
|
118
|
+
require(s.balanceOf[msg.sender] >= amount, 'Insufficient balance')
|
119
|
+
|
120
|
+
s.balanceOf[msg.sender] -= amount
|
121
|
+
s.balanceOf[to] += amount
|
122
|
+
|
123
|
+
emit :Transfer, from: msg.sender, to: to, amount: amount
|
124
|
+
|
125
|
+
return true
|
126
|
+
end
|
127
|
+
|
128
|
+
function :transferFrom, {
|
129
|
+
from: :address,
|
130
|
+
to: :address,
|
131
|
+
amount: :uint256
|
132
|
+
}, :public, :virtual, returns: :bool do
|
133
|
+
allowed = s.allowance[from][msg.sender]
|
134
|
+
|
135
|
+
require(s.balanceOf[from] >= amount, 'Insufficient balance')
|
136
|
+
require(allowed >= amount, 'Insufficient allowance')
|
137
|
+
|
138
|
+
s.allowance[from][msg.sender] = allowed - amount
|
139
|
+
|
140
|
+
s.balanceOf[from] -= amount
|
141
|
+
s.balanceOf[to] += amount
|
142
|
+
|
143
|
+
emit :Transfer, from: from, to: to, amount: amount
|
144
|
+
|
145
|
+
return true
|
146
|
+
end
|
147
|
+
|
148
|
+
function :_mint, { to: :address, amount: :uint256 }, :internal, :virtual do
|
149
|
+
s.totalSupply += amount
|
150
|
+
s.balanceOf[to] += amount
|
151
|
+
|
152
|
+
emit :Transfer, from: address(0), to: to, amount: amount
|
153
|
+
end
|
154
|
+
|
155
|
+
function :_burn, { from: :address, amount: :uint256 }, :internal, :virtual do
|
156
|
+
s.balanceOf[from] -= amount
|
157
|
+
s.totalSupply -= amount
|
158
|
+
|
159
|
+
emit :Transfer, from: from, to: address(0), amount: amount
|
160
|
+
end
|
161
|
+
end
|
162
|
+
```
|
163
|
+
|
164
|
+
|
165
|
+
[PublicMintERC20](contracts/PublicMintERC20.rb) - main contract in rubidity classic / o.g. style
|
166
|
+
|
167
|
+
``` ruby
|
168
|
+
pragma :rubidity, "1.0.0"
|
169
|
+
|
170
|
+
import './ERC20'
|
171
|
+
|
172
|
+
contract :PublicMintERC20, is: :ERC20 do
|
173
|
+
uint256 :public, :maxSupply
|
174
|
+
uint256 :public, :perMintLimit
|
175
|
+
|
176
|
+
constructor(
|
177
|
+
name: :string,
|
178
|
+
symbol: :string,
|
179
|
+
maxSupply: :uint256,
|
180
|
+
perMintLimit: :uint256,
|
181
|
+
decimals: :uint8
|
182
|
+
) do
|
183
|
+
super( name: name, symbol: symbol, decimals: decimals )
|
184
|
+
s.maxSupply = maxSupply
|
185
|
+
s.perMintLimit = perMintLimit
|
186
|
+
end
|
187
|
+
|
188
|
+
function :mint, { amount: :uint256 }, :public do
|
189
|
+
require(amount > 0, 'Amount must be positive')
|
190
|
+
require(amount <= s.perMintLimit, 'Exceeded mint limit')
|
191
|
+
|
192
|
+
require(s.totalSupply + amount <= s.maxSupply, 'Exceeded max supply')
|
193
|
+
|
194
|
+
_mint(to: msg.sender, amount: amount)
|
195
|
+
end
|
196
|
+
|
197
|
+
function :airdrop, { to: :address, amount: :uint256 }, :public do
|
198
|
+
require(amount > 0, 'Amount must be positive')
|
199
|
+
require(amount <= s.perMintLimit, 'Exceeded mint limit')
|
200
|
+
|
201
|
+
require(s.totalSupply + amount <= s.maxSupply, 'Exceeded max supply')
|
202
|
+
|
203
|
+
_mint(to: to, amount: amount)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
```
|
207
|
+
|
208
|
+
|
209
|
+
|
210
|
+
Now let's square the circle and try the impossible.
|
211
|
+
Let's run the PublicMintERC20 contract.
|
212
|
+
|
213
|
+
|
214
|
+
``` ruby
|
215
|
+
require 'rubidity'
|
216
|
+
|
217
|
+
# load (parse) and generate contract classes
|
218
|
+
Contract.load( 'PublicMintERC20' )
|
219
|
+
|
220
|
+
|
221
|
+
# try out contract classes
|
222
|
+
pp ERC20
|
223
|
+
pp PublicMintERC20
|
224
|
+
|
225
|
+
pp ERC20.name
|
226
|
+
pp PublicMintERC20.name
|
227
|
+
|
228
|
+
pp ERC20::Transfer # "scoped" (typed) Event class
|
229
|
+
pp ERC20::Approval # "scoped" (typed) Event class
|
230
|
+
pp ERC20::Transfer.name
|
231
|
+
pp ERC20::Approval.name
|
232
|
+
|
233
|
+
pp ERC20::Transfer.new( from: address(0), to: address(0), amount: 0)
|
234
|
+
pp ERC20::Approval.new( owner: address(0), spender: address(0), amount: 0)
|
235
|
+
|
236
|
+
|
237
|
+
contract = ERC20.new
|
238
|
+
pp contract
|
239
|
+
contract.constructor( name: 'My Fun Token',
|
240
|
+
symbol: 'FUN',
|
241
|
+
decimals: 18 )
|
242
|
+
|
243
|
+
pp contract
|
244
|
+
|
245
|
+
|
246
|
+
contract = PublicMintERC20.new
|
247
|
+
pp contract
|
248
|
+
contract.constructor( name: 'My Fun Token',
|
249
|
+
symbol: 'FUN',
|
250
|
+
maxSupply: 21000000,
|
251
|
+
perMintLimit: 10000,
|
252
|
+
decimals: 18 )
|
253
|
+
pp contract
|
254
|
+
pp contract.serialize
|
255
|
+
|
256
|
+
|
257
|
+
# test drive with alice, bob & charlie address
|
258
|
+
|
259
|
+
alice = '0x'+'a'*40 # e.g. '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
260
|
+
bob = '0x'+'b'*40 # e.g. '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'
|
261
|
+
charlie = '0x'+'c'*40 # e.g. '0xcccccccccccccccccccccccccccccccccccccccc'
|
262
|
+
|
263
|
+
pp alice
|
264
|
+
pp bob
|
265
|
+
pp charlie
|
266
|
+
|
267
|
+
|
268
|
+
# try mint
|
269
|
+
Runtime.msg.sender = alice
|
270
|
+
|
271
|
+
contract.mint( 1000 )
|
272
|
+
contract.mint( 100 )
|
273
|
+
|
274
|
+
pp contract.serialize
|
275
|
+
|
276
|
+
|
277
|
+
|
278
|
+
Runtime.msg.sender = bob
|
279
|
+
|
280
|
+
contract.mint( 500 )
|
281
|
+
contract.mint( 10 )
|
282
|
+
|
283
|
+
pp contract.serialize
|
284
|
+
|
285
|
+
|
286
|
+
pp contract.totalSupply
|
287
|
+
pp contract.balanceOf( alice )
|
288
|
+
pp contract.balanceOf( bob )
|
289
|
+
pp contract.serialize
|
290
|
+
|
291
|
+
|
292
|
+
|
293
|
+
# try transfer
|
294
|
+
Runtime.msg.sender = alice
|
295
|
+
|
296
|
+
contract.transfer( to: bob, amount: 111 )
|
297
|
+
contract.transfer( to: charlie, amount: 11 )
|
298
|
+
|
299
|
+
|
300
|
+
Runtime.msg.sender = bob
|
301
|
+
|
302
|
+
contract.transfer( to: alice, amount: 11 )
|
303
|
+
contract.transfer( to: charlie, amount: 111 )
|
304
|
+
|
305
|
+
|
306
|
+
|
307
|
+
pp contract.totalSupply
|
308
|
+
pp contract.balanceOf( alice )
|
309
|
+
pp contract.balanceOf( bob )
|
310
|
+
pp contract.balanceOf( charlie )
|
311
|
+
|
312
|
+
pp contract.serialize
|
313
|
+
#=> {:name=>"My Fun Token",
|
314
|
+
# :symbol=>"FUN",
|
315
|
+
# :decimals=>18,
|
316
|
+
# :totalSupply=>1610,
|
317
|
+
# :balanceOf=>
|
318
|
+
# {"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"=>989,
|
319
|
+
# "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"=>499,
|
320
|
+
# "0xcccccccccccccccccccccccccccccccccccccccc"=>122},
|
321
|
+
# :allowance=>{},
|
322
|
+
# :maxSupply=>21000000,
|
323
|
+
# :perMintLimit=>10000}
|
324
|
+
|
325
|
+
# and so on.
|
326
|
+
```
|
327
|
+
|
328
|
+
that's it.
|
329
|
+
|
330
|
+
|
331
|
+
|
332
|
+
### Contract Sample №3 - SupplyChain
|
333
|
+
|
334
|
+
Let's try the SupplyChain contract.
|
335
|
+
|
336
|
+
[SupplyChain](contracts/SupplyChain.rb) - main contract in rubidity classic / o.g. style
|
337
|
+
|
338
|
+
|
339
|
+
``` ruby
|
340
|
+
pragma :rubidity, "1.0.0"
|
341
|
+
|
342
|
+
contract :SupplyChain do
|
343
|
+
|
344
|
+
event :Transfer, { productId: :uint32 }
|
345
|
+
|
346
|
+
struct :Product, { modelNumber: :string,
|
347
|
+
partNumber: :string,
|
348
|
+
serialNumber: :string,
|
349
|
+
productOwner: :address,
|
350
|
+
cost: :uint32,
|
351
|
+
mfgTimestamp: :timestamp }
|
352
|
+
|
353
|
+
struct :Participant, { userName: :string,
|
354
|
+
password: :string,
|
355
|
+
participantType: :string,
|
356
|
+
participantAddress: :address }
|
357
|
+
|
358
|
+
struct :Registration, { productId: :uint32,
|
359
|
+
ownerId: :uint32,
|
360
|
+
productOwner: :address,
|
361
|
+
trxTimestamp: :timestamp }
|
362
|
+
|
363
|
+
uint32 :public, :p_id # (last) product id
|
364
|
+
uint32 :public, :u_id # (last) participant id
|
365
|
+
uint32 :public, :r_id # (last) registration id
|
366
|
+
|
367
|
+
mapping ({ uint32: :Product }), :public, :products
|
368
|
+
mapping ({ uint32: :Participant }), :public, :participants
|
369
|
+
mapping ({ uint32: :Registration }), :public, :registrations
|
370
|
+
|
371
|
+
# movement track for a product
|
372
|
+
mapping ({ uint32: array( :uint32 ) }), :public, :productTrack
|
373
|
+
|
374
|
+
|
375
|
+
function :createParticipant, { name: :string,
|
376
|
+
pass: :string,
|
377
|
+
addr: :address,
|
378
|
+
type: :string }, :public, returns: :uint32 do
|
379
|
+
userId = s.u_id += 1
|
380
|
+
s.participants[ userId ].userName = name
|
381
|
+
s.participants[ userId ].password = pass
|
382
|
+
s.participants[ userId ].participantAddress = addr
|
383
|
+
s.participants[ userId ].participantType = type
|
384
|
+
return userId
|
385
|
+
end
|
386
|
+
|
387
|
+
function :getParticipantDetails, { id: :uint32 }, :public, :view, returns: [:string,:address,:string] do
|
388
|
+
return [s.participants[ id ].userName,
|
389
|
+
s.participants[ id ].participantAddress,
|
390
|
+
s.participants[ id ].participantType]
|
391
|
+
end
|
392
|
+
|
393
|
+
function :createProduct, { ownerId: :uint32,
|
394
|
+
modelNumber: :string,
|
395
|
+
partNumber: :string,
|
396
|
+
serialNumber: :string,
|
397
|
+
productCost: :uint32 }, :public, returns: :uint32 do
|
398
|
+
require( s.participants[ ownerId ].participantType == "manufacturer", "must be manufacturer" )
|
399
|
+
|
400
|
+
productId = s.p_id += 1
|
401
|
+
s.products[ productId ].modelNumber = modelNumber
|
402
|
+
s.products[ productId ].partNumber = partNumber
|
403
|
+
s.products[ productId ].serialNumber = serialNumber
|
404
|
+
s.products[ productId ].cost = productCost
|
405
|
+
s.products[ productId ].productOwner = s.participants[ownerId].participantAddress
|
406
|
+
s.products[ productId ].mfgTimestamp = block.timestamp
|
407
|
+
return productId
|
408
|
+
end
|
409
|
+
|
410
|
+
function :getProductDetails, { id: :uint32 }, :public, :view, returns: [:string,:string,:string,:uint32,:address,:timestamp] do
|
411
|
+
return [s.products[ id ].modelNumber,
|
412
|
+
s.products[ id ].partNumber,
|
413
|
+
s.products[ id ].serialNumber,
|
414
|
+
s.products[ id ].cost,
|
415
|
+
s.products[ id ].productOwner,
|
416
|
+
s.products[ id ].mfgTimestamp]
|
417
|
+
end
|
418
|
+
|
419
|
+
function :transferToOwner, { user1Id: :uint32,
|
420
|
+
user2Id: :uint32,
|
421
|
+
prodId: :uint32 }, :public, returns: :bool do
|
422
|
+
require( msg.sender == s.products[prodId].productOwner, "only owner" )
|
423
|
+
|
424
|
+
p1 = s.participants[ user1Id ]
|
425
|
+
p2 = s.participants[ user2Id ]
|
426
|
+
registrationId = s.r_id += 1
|
427
|
+
|
428
|
+
s.registrations[ registrationId ].productId = prodId
|
429
|
+
s.registrations[ registrationId ].productOwner = p2.participantAddress
|
430
|
+
s.registrations[ registrationId ].ownerId = user2Id
|
431
|
+
s.registrations[ registrationId ].trxTimestamp = block.timestamp
|
432
|
+
|
433
|
+
s.products[ prodId ].productOwner = p2.participantAddress
|
434
|
+
s.productTrack[ prodId ].push( registrationId )
|
435
|
+
|
436
|
+
emit :Transfer, prodId
|
437
|
+
|
438
|
+
return true
|
439
|
+
end
|
440
|
+
|
441
|
+
function :getProductTrack, { prodId: :uint32 }, :public, :view, returns: array(:uint32) do
|
442
|
+
return s.productTrack[ prodId ]
|
443
|
+
end
|
444
|
+
|
445
|
+
function :getRegistrationDetails, { regId: :uint32 }, :public, :view, returns: [:uint32, :uint32, :address, :timestamp] do
|
446
|
+
r = s.registrations[ regId ]
|
447
|
+
|
448
|
+
return [r.productId,
|
449
|
+
r.ownerId,
|
450
|
+
r.productOwner,
|
451
|
+
r.trxTimestamp]
|
452
|
+
end
|
453
|
+
end
|
454
|
+
```
|
455
|
+
|
456
|
+
Now let's square the circle and try the impossible.
|
457
|
+
Let's run the SupplyChain contract.
|
458
|
+
|
459
|
+
|
460
|
+
``` ruby
|
461
|
+
require 'rubidity'
|
462
|
+
|
463
|
+
# load (parse) and generate contract classes
|
464
|
+
Contract.load( 'SupplyChain' )
|
465
|
+
|
466
|
+
# try out contract classes
|
467
|
+
pp SupplyChain
|
468
|
+
pp SupplyChain.name
|
469
|
+
|
470
|
+
pp SupplyChain::Transfer
|
471
|
+
pp SupplyChain::Transfer.name
|
472
|
+
|
473
|
+
pp SupplyChain::Transfer.new( productId: 111 )
|
474
|
+
|
475
|
+
pp SupplyChain::Product
|
476
|
+
pp SupplyChain::Participant
|
477
|
+
pp SupplyChain::Registration
|
478
|
+
pp SupplyChain::Product.name
|
479
|
+
pp SupplyChain::Participant.name
|
480
|
+
pp SupplyChain::Registration.name
|
481
|
+
|
482
|
+
|
483
|
+
alice = '0x'+'a'*40 # e.g. '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
|
484
|
+
bob = '0x'+'b'*40 # e.g. '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'
|
485
|
+
charlie = '0x'+'c'*40 # e.g. '0xcccccccccccccccccccccccccccccccccccccccc'
|
486
|
+
|
487
|
+
pp alice
|
488
|
+
pp bob
|
489
|
+
pp charlie
|
490
|
+
|
491
|
+
pp SupplyChain::Product.new( modelNumber: 'model1',
|
492
|
+
partNumber: 'part1',
|
493
|
+
serialNumber: 'serial1',
|
494
|
+
productOwner: alice,
|
495
|
+
cost: 1000,
|
496
|
+
mfgTimestamp: 1698756267 )
|
497
|
+
|
498
|
+
pp SupplyChain::Participant.new( userName: 'alice',
|
499
|
+
password: 'passa',
|
500
|
+
participantType: 'manufacturer',
|
501
|
+
participantAddress: alice )
|
502
|
+
|
503
|
+
pp SupplyChain::Registration.new( productId: 1,
|
504
|
+
ownerId: 1,
|
505
|
+
productOwner: alice,
|
506
|
+
trxTimestamp: 1698756267 )
|
507
|
+
|
508
|
+
|
509
|
+
contract = SupplyChain.new
|
510
|
+
pp contract
|
511
|
+
|
512
|
+
pp contract.p_id
|
513
|
+
pp contract.u_id
|
514
|
+
pp contract.r_id
|
515
|
+
|
516
|
+
pp contract.serialize
|
517
|
+
#=>
|
518
|
+
# {:p_id=>0, :u_id=>0, :r_id=>0,
|
519
|
+
# :products=>{},
|
520
|
+
# :participants=>{},
|
521
|
+
# :registrations=>{},
|
522
|
+
# :productTrack=>{}}
|
523
|
+
|
524
|
+
|
525
|
+
pp contract.createParticipant( name: 'alice',
|
526
|
+
pass: 'passa',
|
527
|
+
addr: alice,
|
528
|
+
type: 'manufacturer' )
|
529
|
+
|
530
|
+
pp contract.createParticipant( name: 'bob',
|
531
|
+
pass: 'passb',
|
532
|
+
addr: bob,
|
533
|
+
type: 'supplier' )
|
534
|
+
|
535
|
+
pp contract.createParticipant( name: 'charlie',
|
536
|
+
pass: 'passc',
|
537
|
+
addr: charlie,
|
538
|
+
type: 'consumer' )
|
539
|
+
|
540
|
+
pp contract.getParticipantDetails( 1 )
|
541
|
+
pp contract.getParticipantDetails( 2 )
|
542
|
+
pp contract.getParticipantDetails( 3 )
|
543
|
+
|
544
|
+
|
545
|
+
|
546
|
+
Runtime.block.timestamp = 1698846375
|
547
|
+
|
548
|
+
pp contract.createProduct( ownerId: 1,
|
549
|
+
modelNumber: 'prod1',
|
550
|
+
partNumber: '100',
|
551
|
+
serialNumber: '123',
|
552
|
+
productCost: 11 )
|
553
|
+
|
554
|
+
pp contract.getProductDetails( 1 )
|
555
|
+
|
556
|
+
|
557
|
+
Runtime.msg.sender = alice
|
558
|
+
Runtime.block.timestamp = 1698847541
|
559
|
+
|
560
|
+
pp contract.transferToOwner( user1Id: 1,
|
561
|
+
user2Id: 2,
|
562
|
+
prodId: 1 )
|
563
|
+
|
564
|
+
Runtime.msg.sender = bob
|
565
|
+
Runtime.block.timestamp = 1698848314
|
566
|
+
|
567
|
+
pp contract.transferToOwner( user1Id: 2,
|
568
|
+
user2Id: 3,
|
569
|
+
prodId: 1 )
|
570
|
+
|
571
|
+
|
572
|
+
pp contract.getRegistrationDetails( 1 )
|
573
|
+
pp contract.getRegistrationDetails( 2 )
|
574
|
+
|
575
|
+
pp contract.getProductTrack( 1 )
|
576
|
+
|
577
|
+
pp contract.serialize
|
578
|
+
#=> {:p_id=>1,
|
579
|
+
# :u_id=>3,
|
580
|
+
# :r_id=>2,
|
581
|
+
# :products=>{1=>["prod1", "100", "123", "0xcccccccccccccccccccccccccccccccccccccccc", 11, 1698846375]},
|
582
|
+
# :participants=>
|
583
|
+
# {1=>["alice", "passa", "manufacturer", "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"],
|
584
|
+
# 2=>["bob", "passb", "supplier", "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"],
|
585
|
+
# 3=>["charlie", "passc", "consumer", "0xcccccccccccccccccccccccccccccccccccccccc"]},
|
586
|
+
# :registrations=>
|
587
|
+
# {1=>[1, 2, "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", 1698847541],
|
588
|
+
# 2=>[1, 3, "0xcccccccccccccccccccccccccccccccccccccccc", 1698848314]},
|
589
|
+
# :productTrack=>{1=>[1, 2]}}
|
590
|
+
```
|
591
|
+
|
592
|
+
that's it.
|
593
|
+
|
594
|
+
|
595
|
+
|
596
|
+
|
597
|
+
## Questions? Comments?
|
598
|
+
|
599
|
+
Join us in the [Rubidity & Rubysol (community) discord (chat server)](https://discord.gg/3JRnDUap6y). Yes you can.
|
600
|
+
Your questions and commentary welcome.
|
601
|
+
|
602
|
+
Or post them over at the [Help & Support](https://github.com/geraldb/help) page. Thanks.
|
603
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'hoe'
|
2
|
+
require './lib/rubidity/version.rb'
|
3
|
+
|
4
|
+
|
5
|
+
Hoe.spec 'rubidity' do
|
6
|
+
|
7
|
+
self.version = Rubysol::Module::Rubidity::VERSION
|
8
|
+
|
9
|
+
self.summary = 'rubidity gem - rubidity "classic / o.g." contract builder; trying the impossible and square the circle, that is, a rubidity "classic / o.g." dsl builder generating rubysol "more ruby-ish" contract classes'
|
10
|
+
self.description = summary
|
11
|
+
|
12
|
+
self.urls = { home: 'https://github.com/s6ruby/rubidity' }
|
13
|
+
|
14
|
+
self.author = 'Gerald Bauer'
|
15
|
+
self.email = 'gerald.bauer@gmail.com'
|
16
|
+
|
17
|
+
# switch extension to .markdown for gihub formatting
|
18
|
+
self.readme_file = 'README.md'
|
19
|
+
self.history_file = 'CHANGELOG.md'
|
20
|
+
|
21
|
+
self.extra_deps = [
|
22
|
+
['rubysol', '>= 0.1.0'],
|
23
|
+
]
|
24
|
+
|
25
|
+
self.licenses = ['Public Domain']
|
26
|
+
|
27
|
+
self.spec_extras = {
|
28
|
+
required_ruby_version: '>= 2.3'
|
29
|
+
}
|
30
|
+
end
|
@@ -0,0 +1,215 @@
|
|
1
|
+
######
|
2
|
+
# reading rubidity classic/o.g. "dsl"
|
3
|
+
|
4
|
+
|
5
|
+
class Builder
|
6
|
+
## auto-add extension and import path for now - why? why not?
|
7
|
+
def self.load_file( path )
|
8
|
+
code = File.open( "contracts/#{path}.rb", 'r:utf-8' ) { |f| f.read }
|
9
|
+
basename = File.basename( path )
|
10
|
+
lineno = 1
|
11
|
+
load( code, basename, lineno )
|
12
|
+
end
|
13
|
+
|
14
|
+
## note: try adding filename and lineno to get source for block!!!
|
15
|
+
def self.load( code, filename, lineno )
|
16
|
+
|
17
|
+
## todo/fix: add filename to @paths too (to avoid possible recursive loading!!)
|
18
|
+
|
19
|
+
builder = Builder.new
|
20
|
+
builder.instance_eval( code, filename, lineno )
|
21
|
+
builder
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
@source = Source.new
|
26
|
+
## only load path once; keep track of loaded imports
|
27
|
+
## change paths to contracts or such - why? why not?
|
28
|
+
@paths = []
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
attr_reader :source
|
33
|
+
|
34
|
+
|
35
|
+
## examples:
|
36
|
+
## pragma :rubidity, "1.0.0"
|
37
|
+
def pragma( ... )
|
38
|
+
## ignore for now
|
39
|
+
end
|
40
|
+
|
41
|
+
## examples:
|
42
|
+
## import './ERC20'
|
43
|
+
def import( path )
|
44
|
+
puts "==> importing >#{path}<..."
|
45
|
+
code = File.open( "contracts/#{path}.rb", 'r:utf-8' ) { |f| f.read }
|
46
|
+
|
47
|
+
basename = File.basename( path )
|
48
|
+
|
49
|
+
if @paths.include?( basename )
|
50
|
+
puts " skipping import; already loaded"
|
51
|
+
else
|
52
|
+
@paths << basename
|
53
|
+
lineno = 1 ## note: starting at line 1 (NOT 0!!!)
|
54
|
+
instance_eval( code, basename, lineno )
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
def contract( name, is: [], abstract: false, &body )
|
61
|
+
contract = ContractDef.new( name, is: is, abstract: abstract )
|
62
|
+
contract.instance_eval( &body )
|
63
|
+
|
64
|
+
@source.contracts << contract
|
65
|
+
end
|
66
|
+
end # class Builder
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
class Source ## rename to unit (source unit) or service or ?? - why? why not?
|
71
|
+
|
72
|
+
attr_reader :contracts
|
73
|
+
|
74
|
+
def initialize
|
75
|
+
@contracts = []
|
76
|
+
end
|
77
|
+
|
78
|
+
def generate
|
79
|
+
CodegenClassic.generate( self )
|
80
|
+
end
|
81
|
+
end # class Source
|
82
|
+
|
83
|
+
|
84
|
+
|
85
|
+
class ContractDef
|
86
|
+
attr_reader :name, :is, :abstract,
|
87
|
+
:events, :structs,
|
88
|
+
:storage, :functions
|
89
|
+
def initialize( name, is: [], abstract: false )
|
90
|
+
@name = name
|
91
|
+
@is = if is.is_a?( Symbol )
|
92
|
+
[is]
|
93
|
+
elsif is.is_a?( Array )
|
94
|
+
is
|
95
|
+
else
|
96
|
+
raise ArgumentError, "symbol or array expected; got #{is.inspect}"
|
97
|
+
end
|
98
|
+
@abstract = abstract
|
99
|
+
@events = {}
|
100
|
+
@structs = {}
|
101
|
+
@storage = {}
|
102
|
+
@functions = {}
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
def event( name, args )
|
107
|
+
@events[name] = args
|
108
|
+
end
|
109
|
+
|
110
|
+
def struct( name, args )
|
111
|
+
@structs[name] = args
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
[:string,
|
116
|
+
:uint8, :uint32, :uint112, :uint256,
|
117
|
+
:address
|
118
|
+
].each do |type|
|
119
|
+
define_method(type) do |*args|
|
120
|
+
|
121
|
+
if args.size == 0
|
122
|
+
## assume type helper to convert :string to string,
|
123
|
+
## :address to address, and such
|
124
|
+
puts " turn :#{type} into #{type}"
|
125
|
+
return type
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
type = type
|
130
|
+
name = args.pop.to_sym
|
131
|
+
@storage[ name ] = { type: type, args: args }
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def mapping( *args )
|
136
|
+
key_type, value_type = args.shift.first
|
137
|
+
|
138
|
+
if args.last.is_a?( Symbol )
|
139
|
+
name = args.pop
|
140
|
+
@storage[ name ] = { type: :mapping,
|
141
|
+
key_type: key_type,
|
142
|
+
value_type: value_type,
|
143
|
+
args: args }
|
144
|
+
else
|
145
|
+
{ type: :mapping,
|
146
|
+
key_type: key_type,
|
147
|
+
value_type: value_type,
|
148
|
+
args: args }
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def array( *args )
|
153
|
+
sub_type = args.shift
|
154
|
+
|
155
|
+
if args.last.is_a?( Symbol )
|
156
|
+
name = args.pop
|
157
|
+
@storage[ name ] = { type: :array,
|
158
|
+
sub_type: sub_type,
|
159
|
+
args: args }
|
160
|
+
else
|
161
|
+
{ type: :array,
|
162
|
+
sub_type: sub_type,
|
163
|
+
args: args }
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
|
168
|
+
def function(name, args, *options, returns: nil, &body )
|
169
|
+
## check if access to source is possible?
|
170
|
+
puts
|
171
|
+
# puts " function body: #{body.class.name}"
|
172
|
+
#=> Proc
|
173
|
+
# body.soure
|
174
|
+
#=> ["(eval)", 17] - source filename and line number
|
175
|
+
pp body
|
176
|
+
pp body.parameters
|
177
|
+
pp body.source_location
|
178
|
+
puts
|
179
|
+
puts body.source
|
180
|
+
|
181
|
+
|
182
|
+
## note: for now strip names/keys if returns is a hash - why? why not?
|
183
|
+
## e.g. function :getReserves, {}, :public, :view, returns: {
|
184
|
+
## _reserve0: :uint112,
|
185
|
+
## _reserve1: :uint112,
|
186
|
+
## _blockTimestampLast: :uint32 }
|
187
|
+
|
188
|
+
returns = returns.values if returns.is_a?( Hash )
|
189
|
+
|
190
|
+
###
|
191
|
+
## note: turn returns into an array - empty if nil, etc.
|
192
|
+
## always wrap into array
|
193
|
+
returns = if returns.nil?
|
194
|
+
[]
|
195
|
+
elsif returns.is_a?( Array )
|
196
|
+
returns
|
197
|
+
else ## assume single type
|
198
|
+
[returns]
|
199
|
+
end
|
200
|
+
|
201
|
+
@functions[ name ] = { args: args,
|
202
|
+
options: options,
|
203
|
+
returns: returns,
|
204
|
+
body: body.source }
|
205
|
+
end
|
206
|
+
|
207
|
+
def constructor(args = {}, *options, &body)
|
208
|
+
function(:constructor, args, *options, returns: nil, &body )
|
209
|
+
end
|
210
|
+
end # class ContractDef
|
211
|
+
|
212
|
+
|
213
|
+
|
214
|
+
|
215
|
+
|
@@ -0,0 +1,203 @@
|
|
1
|
+
#####
|
2
|
+
# codegen - code generator
|
3
|
+
|
4
|
+
##
|
5
|
+
## require(s.balanceOf[msg.sender] >= amount, 'Insufficient balance')
|
6
|
+
##
|
7
|
+
## s.balanceOf[msg.sender] -= amount
|
8
|
+
## s.balanceOf[to] += amount
|
9
|
+
##
|
10
|
+
## emit :Transfer, from: msg.sender, to: to, amount: amount
|
11
|
+
|
12
|
+
def patch( src )
|
13
|
+
## change require() to assert
|
14
|
+
src = src.gsub( /\brequire\b/, 'assert' )
|
15
|
+
|
16
|
+
## s. to ivars (@)
|
17
|
+
src = src.gsub( /\bs\./, '@' )
|
18
|
+
|
19
|
+
## change emit : to log
|
20
|
+
src = src.gsub( /\bemit[ ]+:/, 'log ' )
|
21
|
+
src
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
## "built-in" types by "classic" symbol lookup
|
27
|
+
|
28
|
+
## storage decimals{:type=>:uint8, :args=>[:public]}
|
29
|
+
## storage totalSupply{:type=>:uint256, :args=>[:public]}
|
30
|
+
## storage balanceOf{:type=>:mapping, :key_type=>:address, :value_type=>:uint256, :args=>[:public]}
|
31
|
+
## storage allowance{:type=>:mapping,
|
32
|
+
## :key_type=>:address,
|
33
|
+
## :value_type=>{:type=>:mapping, :key_type=>:address, :value_type=>:uint256, :args=>[]},
|
34
|
+
|
35
|
+
def spec_to_type( spec, structs: {} )
|
36
|
+
type = spec.is_a?( Symbol ) ? spec : spec[:type]
|
37
|
+
|
38
|
+
## check structs first
|
39
|
+
struct_class = structs[ type ]
|
40
|
+
if struct_class
|
41
|
+
puts " found struct class >#{type}<:"
|
42
|
+
pp struct_class
|
43
|
+
return struct_class
|
44
|
+
end
|
45
|
+
|
46
|
+
case type
|
47
|
+
when :uint8, :uint32, :uint112, :uint224, :uint256 then Types::UInt
|
48
|
+
when :address then Types::Address
|
49
|
+
when :string then Types::String
|
50
|
+
when :bytes then Types::Bytes
|
51
|
+
when :timestamp then Types::Timestamp
|
52
|
+
when :bool then Bool ## note: Bool is always "global" - why? why not?
|
53
|
+
when :mapping then mapping( spec_to_type( spec[:key_type], structs: structs ),
|
54
|
+
spec_to_type( spec[:value_type], structs: structs ) )
|
55
|
+
when :array then array( spec_to_type( spec[:sub_type], structs: structs ))
|
56
|
+
else
|
57
|
+
raise ArgumentError, "unknown type - #{type}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
##
|
64
|
+
# use static methods for now - why? why not?
|
65
|
+
class CodegenClassic
|
66
|
+
|
67
|
+
|
68
|
+
def self.generate( source )
|
69
|
+
pp source
|
70
|
+
|
71
|
+
contract_classes = {} ## lookup by name
|
72
|
+
struct_classes = {} ## lookup by name
|
73
|
+
|
74
|
+
source.contracts.each do |contract|
|
75
|
+
puts "==> generate contract #{contract.name}..."
|
76
|
+
base = contract.is.empty? ? Contract : contract_classes[contract.is[0]]
|
77
|
+
puts " base: #{base}"
|
78
|
+
pp contract.is
|
79
|
+
|
80
|
+
contract_class = Class.new( base )
|
81
|
+
|
82
|
+
## Use Kernel / global ?? scope for now
|
83
|
+
scope = Object
|
84
|
+
scope.const_set( contract.name, contract_class )
|
85
|
+
contract_classes[ contract.name ] = contract_class
|
86
|
+
|
87
|
+
## add events if any
|
88
|
+
##
|
89
|
+
## event :Transfer, { from: :address, to: :address, amount: :uint256 }
|
90
|
+
## event :Approval, { owner: :address, spender: :address, amount: :uint256 }
|
91
|
+
##
|
92
|
+
## event Transfer {:from=>:address, :to=>:address, :amount=>:uint256}
|
93
|
+
## event Approval {:owner=>:address, :spender=>:address, :amount=>:uint256}
|
94
|
+
contract.events.each do |event_name, event_args|
|
95
|
+
print "event #{event_name}"
|
96
|
+
pp event_args
|
97
|
+
|
98
|
+
attributes = event_args.map { |name, type| [name, spec_to_type(type)] }.to_h
|
99
|
+
pp attributes
|
100
|
+
contract_class.event( event_name, **attributes )
|
101
|
+
end
|
102
|
+
|
103
|
+
## add structs if any
|
104
|
+
contract.structs.each do |struct_name, struct_args|
|
105
|
+
print "struct #{struct_name}"
|
106
|
+
pp struct_args
|
107
|
+
|
108
|
+
attributes = struct_args.map { |name, type| [name, spec_to_type(type)] }.to_h
|
109
|
+
pp attributes
|
110
|
+
struct_class = contract_class.struct( struct_name, **attributes )
|
111
|
+
|
112
|
+
struct_classes[ struct_name ] = struct_class
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
## add storage/state if any
|
117
|
+
##
|
118
|
+
## storage name{:type=>:string, :args=>[:public]}
|
119
|
+
## storage symbol{:type=>:string, :args=>[:public]}
|
120
|
+
## storage decimals{:type=>:uint8, :args=>[:public]}
|
121
|
+
## storage totalSupply{:type=>:uint256, :args=>[:public]}
|
122
|
+
## storage balanceOf{:type=>:mapping, :key_type=>:address, :value_type=>:uint256, :args=>[:public]}
|
123
|
+
## storage allowance{:type=>:mapping,
|
124
|
+
## :key_type=>:address,
|
125
|
+
## :value_type=>{:type=>:mapping, :key_type=>:address, :value_type=>:uint256, :args=>[]},
|
126
|
+
## :args=>[:public]}
|
127
|
+
contract.storage.each do |storage_name, storage_args|
|
128
|
+
print "storage #{storage_name}"
|
129
|
+
pp storage_args
|
130
|
+
|
131
|
+
kwargs = {
|
132
|
+
storage_name => spec_to_type( storage_args, structs: struct_classes )
|
133
|
+
}
|
134
|
+
contract_class.storage( **kwargs )
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
## add functions if any
|
139
|
+
## constructor:
|
140
|
+
## {:args=>{:name=>:string, :symbol=>:string, :decimals=>:uint8},
|
141
|
+
## :options=>[],
|
142
|
+
## :body=>"..."}
|
143
|
+
## function approve:
|
144
|
+
## {:args=>{:spender=>:address, :amount=>:uint256},
|
145
|
+
## :options=>[:public, :virtual],
|
146
|
+
## :returns=>[:bool],
|
147
|
+
## :body=>"..."}
|
148
|
+
## function transfer:
|
149
|
+
## {:args=>{:to=>:address, :amount=>:uint256},
|
150
|
+
## :options=>[:public, :virtual],
|
151
|
+
## :returns=>[:bool],
|
152
|
+
## :body=>"..."}
|
153
|
+
contract.functions.each do |function_name, function_args|
|
154
|
+
print "function #{function_name}"
|
155
|
+
pp function_args
|
156
|
+
|
157
|
+
input_types = function_args[:args].values.map { |type| spec_to_type(type) }
|
158
|
+
|
159
|
+
kwargs = function_args[:args].keys.map {|arg| "#{arg}:" }.join( ', ' )
|
160
|
+
puts " kwargs: #{kwargs}"
|
161
|
+
body = function_args[:body]
|
162
|
+
body = patch( body ) ## use ruby-ish conventions
|
163
|
+
|
164
|
+
|
165
|
+
if function_name == :constructor
|
166
|
+
puts "==> add constructor..."
|
167
|
+
contract_class.sig input_types
|
168
|
+
## add unsafe method
|
169
|
+
|
170
|
+
code =<<RUBY
|
171
|
+
def constructor( #{kwargs} )
|
172
|
+
puts "==> calling #{contract_class.name}.constructor..."
|
173
|
+
puts " self: \#{self.class.name}"
|
174
|
+
#{body}
|
175
|
+
end
|
176
|
+
RUBY
|
177
|
+
puts " code:"
|
178
|
+
puts code
|
179
|
+
contract_class.class_eval( code )
|
180
|
+
else
|
181
|
+
output_types = function_args[:returns].map { |type| spec_to_type(type) }
|
182
|
+
|
183
|
+
puts "==> add #{function_name}..."
|
184
|
+
contract_class.sig input_types, returns: output_types
|
185
|
+
## add unsafe method
|
186
|
+
|
187
|
+
code =<<RUBY
|
188
|
+
def #{function_name}( #{kwargs} )
|
189
|
+
puts "==> calling #{contract_class.name}.#{function_name}..."
|
190
|
+
puts " self: \#{self.class.name}"
|
191
|
+
#{body}
|
192
|
+
end
|
193
|
+
RUBY
|
194
|
+
puts " code:"
|
195
|
+
puts code
|
196
|
+
contract_class.class_eval( code )
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end # method self.generate
|
201
|
+
|
202
|
+
end # class CodegenClassic
|
203
|
+
|
@@ -0,0 +1,42 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
class Proc
|
4
|
+
## add quick & dirty support for getting source!!!
|
5
|
+
|
6
|
+
## use non-greed .*? to "slurp"-up everything to the end
|
7
|
+
BLOCK_RX = /do
|
8
|
+
.*?
|
9
|
+
\n[ ]{0,2}end
|
10
|
+
/xm
|
11
|
+
|
12
|
+
def source ## fix: change to contract_source - why? why not?
|
13
|
+
filename, lineno = self.source_location
|
14
|
+
lines = File.open( "contracts/#{filename}.rb", 'r:utf-8' ) { |f| f.readlines }
|
15
|
+
## up to 10 for now
|
16
|
+
## note: lineno is starting counting at 1 (use -1 for offset in ary)
|
17
|
+
|
18
|
+
## for now assume no method longer than 100 lines
|
19
|
+
## note: lines INCLUDEs newlines e.g.
|
20
|
+
## [" constructor(name: :string, symbol: :string, decimals: :uint8) do |name, symbol, decimals|\n",
|
21
|
+
## " puts \"ERC20.constructor\"\n",
|
22
|
+
## " @name = name\n",
|
23
|
+
## " @symbol = symbol\n",
|
24
|
+
## " @decimals = decimals\n",
|
25
|
+
## " end\n",
|
26
|
+
|
27
|
+
pastie = lines[lineno-1, 100].join
|
28
|
+
## pp pastie
|
29
|
+
|
30
|
+
## use regex quick hack
|
31
|
+
## to extract use first do
|
32
|
+
## and end on its own line (with max indent of two spaces!!)
|
33
|
+
m = BLOCK_RX.match( pastie )
|
34
|
+
if m
|
35
|
+
m[0][2..-1][0..-4] ## return matched code - cut of do/end wrapper
|
36
|
+
else
|
37
|
+
raise "sorry; no do-end match for code block source"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Rubysol
|
2
|
+
module Module
|
3
|
+
module Rubidity
|
4
|
+
MAJOR = 0
|
5
|
+
MINOR = 9
|
6
|
+
PATCH = 0
|
7
|
+
VERSION = [MAJOR,MINOR,PATCH].join('.')
|
8
|
+
|
9
|
+
def self.version
|
10
|
+
VERSION
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.banner
|
14
|
+
"rubidity/#{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}] in (#{root})"
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.root
|
18
|
+
File.expand_path( File.dirname(File.dirname(File.dirname(File.dirname(__FILE__)))) )
|
19
|
+
end
|
20
|
+
|
21
|
+
end # module Rubidity
|
22
|
+
end # module Module
|
23
|
+
end # module Rubysol
|
data/lib/rubidity.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
|
2
|
+
require 'solidity/typed'
|
3
|
+
require 'rubysol'
|
4
|
+
|
5
|
+
|
6
|
+
## our own code
|
7
|
+
require_relative 'rubidity/version' # let version always go first
|
8
|
+
require_relative 'rubidity/proc'
|
9
|
+
require_relative 'rubidity/builder'
|
10
|
+
require_relative 'rubidity/codegen'
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
###
|
15
|
+
# for easy use / convenience
|
16
|
+
# pack everything into Contract.load( path/name )
|
17
|
+
|
18
|
+
class Contract
|
19
|
+
def self.load( path, generate: true )
|
20
|
+
|
21
|
+
source = Builder.load_file( path ).source
|
22
|
+
pp source
|
23
|
+
pp source.contracts
|
24
|
+
|
25
|
+
puts " #{source.contracts.size} contract(s) in source (unit):"
|
26
|
+
source.contracts.each do |contract|
|
27
|
+
print " #{contract.name}"
|
28
|
+
print " is #{contract.is.inspect}" unless contract.is.empty?
|
29
|
+
print "\n"
|
30
|
+
end
|
31
|
+
|
32
|
+
####################
|
33
|
+
### generate contract classes
|
34
|
+
source.generate if generate
|
35
|
+
|
36
|
+
source ## return source (unit) - why? why not?
|
37
|
+
end
|
38
|
+
end # class Contract
|
39
|
+
|
40
|
+
|
41
|
+
|
42
|
+
puts Rubysol::Module::Rubidity.banner ## say hello
|
metadata
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rubidity
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Gerald Bauer
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-11-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rubysol
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.1.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.1.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rdoc
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '4.0'
|
34
|
+
- - "<"
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '7'
|
37
|
+
type: :development
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '4.0'
|
44
|
+
- - "<"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '7'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: hoe
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '4.0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '4.0'
|
61
|
+
description: rubidity gem - rubidity "classic / o.g." contract builder; trying the
|
62
|
+
impossible and square the circle, that is, a rubidity "classic / o.g." dsl builder
|
63
|
+
generating rubysol "more ruby-ish" contract classes
|
64
|
+
email: gerald.bauer@gmail.com
|
65
|
+
executables: []
|
66
|
+
extensions: []
|
67
|
+
extra_rdoc_files:
|
68
|
+
- CHANGELOG.md
|
69
|
+
- Manifest.txt
|
70
|
+
- README.md
|
71
|
+
files:
|
72
|
+
- CHANGELOG.md
|
73
|
+
- Manifest.txt
|
74
|
+
- README.md
|
75
|
+
- Rakefile
|
76
|
+
- lib/rubidity.rb
|
77
|
+
- lib/rubidity/builder.rb
|
78
|
+
- lib/rubidity/codegen.rb
|
79
|
+
- lib/rubidity/proc.rb
|
80
|
+
- lib/rubidity/version.rb
|
81
|
+
homepage: https://github.com/s6ruby/rubidity
|
82
|
+
licenses:
|
83
|
+
- Public Domain
|
84
|
+
metadata: {}
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options:
|
87
|
+
- "--main"
|
88
|
+
- README.md
|
89
|
+
require_paths:
|
90
|
+
- lib
|
91
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '2.3'
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
requirements: []
|
102
|
+
rubygems_version: 3.4.10
|
103
|
+
signing_key:
|
104
|
+
specification_version: 4
|
105
|
+
summary: rubidity gem - rubidity "classic / o.g." contract builder; trying the impossible
|
106
|
+
and square the circle, that is, a rubidity "classic / o.g." dsl builder generating
|
107
|
+
rubysol "more ruby-ish" contract classes
|
108
|
+
test_files: []
|