rubidity 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|