rubysol-contracts 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 93eaae4a1c50deb7cd758c3d4722586ae4db0043658ff5f67c4e3511f1c39067
4
+ data.tar.gz: 5abc50f516b527db09d6b88d8d1b2ecdcb7813f60149bea0eacf674e627b767d
5
+ SHA512:
6
+ metadata.gz: ed4147e02ef81e836a395d62d3bdebf85badb296b51015588141c1f0f0ad8206fa70c689856c5ecce70d8d8abc0ed656d33307a878ef5986f1e3f72124e2d57f
7
+ data.tar.gz: a9772883af9fb960c6771711274ae79d94715609c4bdf2ed8926cdde93330e10b9bfee5adbd629a883480ca05bb204043104e58361d6f98f2eb02c5f0c77b348
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ### 0.1.0 / 2023-11-16
2
+
3
+ * Everything is new. First release
data/Manifest.txt ADDED
@@ -0,0 +1,14 @@
1
+ CHANGELOG.md
2
+ Manifest.txt
3
+ README.md
4
+ Rakefile
5
+ lib/rubysol/contracts.rb
6
+ lib/rubysol/contracts/erc20.rb
7
+ lib/rubysol/contracts/erc20_liquidity_pool.rb
8
+ lib/rubysol/contracts/erc721.rb
9
+ lib/rubysol/contracts/ether_erc20_bridge.rb
10
+ lib/rubysol/contracts/ethscription_erc20_bridge.rb
11
+ lib/rubysol/contracts/generative_erc721.rb
12
+ lib/rubysol/contracts/open_edition_erc721.rb
13
+ lib/rubysol/contracts/public_mint_erc20.rb
14
+ lib/rubysol/contracts/version.rb
data/README.md ADDED
@@ -0,0 +1,315 @@
1
+ # Rubysol Contracts
2
+
3
+ rubysol-contracts - standard contracts (incl. erc20, erc721, etc) for ruby for layer 1 (l1) with "off-chain" indexer
4
+
5
+ * home :: [github.com/s6ruby/rubidity](https://github.com/s6ruby/rubidity)
6
+ * bugs :: [github.com/s6ruby/rubidity/issues](https://github.com/s6ruby/rubidity/issues)
7
+ * gem :: [rubygems.org/gems/rubysol-contracts](https://rubygems.org/gems/rubysol-contracts)
8
+ * rdoc :: [rubydoc.info/gems/rubysol-contracts](http://rubydoc.info/gems/rubysol-contracts)
9
+
10
+
11
+
12
+
13
+ ## What's Solidity?! What's Rubidity?! What's Rubysol?!
14
+
15
+ See [**Solidity - Contract Application Binary Interface (ABI) Specification** »](https://docs.soliditylang.org/en/latest/abi-spec.html)
16
+
17
+ See [**Rubidity - Ruby for Layer 1 (L1) Contracts / Protocols with "Off-Chain" Indexer** »](https://github.com/s6ruby/rubidity)
18
+
19
+ See [**Rubysol - Ruby for Layer 1 (L1) Contracts / Protocols with "Off-Chain" Indexer** »](https://github.com/s6ruby/rubidity/tree/master/rubysol)
20
+
21
+
22
+
23
+
24
+ ## Usage
25
+
26
+ Let's try the PublicMintERC20 contract...
27
+
28
+ <details>
29
+ <summary markdown="1">Show Source</summary>
30
+
31
+ [contracts/public_mint_erc20.rb](lib/rubysol/contracts/public_mint_erc20.rb):
32
+
33
+ ```ruby
34
+ class PublicMintERC20 < ERC20
35
+
36
+ storage maxSupply: UInt,
37
+ perMintLimit: UInt
38
+
39
+ sig [String, String, UInt, UInt, UInt]
40
+ def constructor(
41
+ name:,
42
+ symbol:,
43
+ maxSupply:,
44
+ perMintLimit:,
45
+ decimals:
46
+ )
47
+ super( name: name,
48
+ symbol: symbol,
49
+ decimals: decimals)
50
+
51
+ @maxSupply = maxSupply
52
+ @perMintLimit = perMintLimit
53
+ end
54
+
55
+
56
+ sig [UInt]
57
+ def mint( amount: )
58
+ assert(amount > 0, 'Amount must be positive')
59
+ assert(amount <= @perMintLimit, 'Exceeded mint limit')
60
+
61
+ assert( @totalSupply + amount <= @maxSupply, 'Exceeded max supply')
62
+
63
+ _mint(to: msg.sender, amount: amount)
64
+ end
65
+
66
+ sig [Address, UInt]
67
+ def airdrop( to:, amount: )
68
+ assert(amount > 0, 'Amount must be positive')
69
+ assert(amount <= @perMintLimit, 'Exceeded mint limit')
70
+
71
+ assert(@totalSupply + amount <= @maxSupply, 'Exceeded max supply')
72
+
73
+ _mint(to: to, amount: amount)
74
+ end
75
+ end
76
+ ```
77
+
78
+ </details>
79
+
80
+ that builds on the ERC20 (base) contract.
81
+
82
+ <details>
83
+ <summary markdown="1">Show Source</summary>
84
+
85
+ [contracts/erc20.rb](lib/rubysol/contracts/erc20.rb):
86
+
87
+ ```ruby
88
+ class ERC20 < Contract
89
+
90
+ event :Transfer, from: Address,
91
+ to: Address,
92
+ amount: UInt
93
+ event :Approval, owner: Address,
94
+ spender: Address,
95
+ amount: UInt
96
+
97
+ storage name: String,
98
+ symbol: String,
99
+ decimals: UInt,
100
+ totalSupply: UInt,
101
+ balanceOf: mapping( Address, UInt ),
102
+ allowance: mapping( Address, mapping( Address, UInt ))
103
+
104
+
105
+ sig [String, String, UInt]
106
+ def constructor(name:,
107
+ symbol:,
108
+ decimals:)
109
+ @name = name
110
+ @symbol = symbol
111
+ @decimals = decimals
112
+ end
113
+
114
+
115
+ sig [Address, UInt], returns: Bool
116
+ def approve( spender:,
117
+ amount: )
118
+ @allowance[msg.sender][spender] = amount
119
+
120
+ log Approval, owner: msg.sender, spender: spender, amount: amount
121
+
122
+ true
123
+ end
124
+
125
+
126
+ sig [Address, UInt], returns: Bool
127
+ def decreaseAllowanceUntilZero( spender:,
128
+ difference: )
129
+ allowed = @allowance[msg.sender][spender]
130
+
131
+ newAllowed = allowed > difference ? allowed - difference : 0
132
+
133
+ approve(spender: spender, amount: newAllowed)
134
+
135
+ true
136
+ end
137
+
138
+
139
+ sig [Address, UInt], returns: Bool
140
+ def transfer( to:,
141
+ amount: )
142
+ assert @balanceOf[msg.sender] >= amount, 'Insufficient balance'
143
+
144
+ @balanceOf[msg.sender] -= amount
145
+ @balanceOf[to] += amount
146
+
147
+ log Transfer, from: msg.sender, to: to, amount: amount
148
+
149
+ true
150
+ end
151
+
152
+ sig [Address, Address, UInt], returns: Bool
153
+ def transferFrom(
154
+ from:,
155
+ to:,
156
+ amount:)
157
+ allowed = @allowance[from][msg.sender]
158
+
159
+ assert @balanceOf[from] >= amount, 'Insufficient balance'
160
+ assert allowed >= amount, 'Insufficient allowance'
161
+
162
+ @allowance[from][msg.sender] = allowed - amount
163
+
164
+ @balanceOf[from] -= amount
165
+ @balanceOf[to] += amount
166
+
167
+ log Transfer, from: from, to: to, amount: amount
168
+
169
+ true
170
+ end
171
+
172
+ sig [Address, UInt]
173
+ def _mint( to:,
174
+ amount: )
175
+ @totalSupply += amount
176
+ @balanceOf[to] += amount
177
+
178
+ log Transfer, from: address(0), to: to, amount: amount
179
+ end
180
+
181
+ sig [Address, UInt]
182
+ def _burn( from:,
183
+ amount: )
184
+ @balanceOf[from] -= amount
185
+ @totalSupply -= amount
186
+
187
+ log Transfer, from: from, to: address(0), amount: amount
188
+ end
189
+ end
190
+ ```
191
+
192
+ </details>
193
+
194
+
195
+
196
+ Let's go.
197
+
198
+ ``` ruby
199
+ require 'rubysol/contracts'
200
+
201
+ contract = PublicMintERC20.construct(
202
+ name: 'My Fun Token', # String,
203
+ symbol: 'FUN', # String,
204
+ maxSupply: 21000000, # UInt,
205
+ perMintLimit: 1000, # UInt,
206
+ decimals: 18, # UInt
207
+ )
208
+
209
+
210
+ contract.serialize
211
+ # {:name=>"My Fun Token",
212
+ # :symbol=>"FUN",
213
+ # :decimals=>18,
214
+ # :totalSupply=>0,
215
+ # :balanceOf=>{},
216
+ # :allowance=>{},
217
+ # :maxSupply=>21000000,
218
+ # :perMintLimit=>1000}
219
+
220
+ alice = '0x'+'a'*40 # e.g. '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
221
+ bob = '0x'+'b'*40 # e.g. '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'
222
+ charlie = '0x'+'c'*40 # e.g. '0xcccccccccccccccccccccccccccccccccccccccc'
223
+
224
+
225
+ # sig [UInt]
226
+ # def mint( amount: )
227
+ contract.msg.sender = alice
228
+
229
+ contract.mint( 100 )
230
+ contract.mint( 200 )
231
+
232
+ contract.msg.sender = bob
233
+
234
+ contract.mint( 300 )
235
+ contract.mint( 400 )
236
+
237
+ # sig [Address, UInt]
238
+ # def airdrop( to:, amount: )
239
+ contract.airdrop( alice, 500 )
240
+ contract.airdrop( charlie, 600 )
241
+
242
+ contract.serialize
243
+ # {:name=>"My Fun Token",
244
+ # :symbol=>"FUN",
245
+ # :decimals=>18,
246
+ # :totalSupply=>2100,
247
+ # :balanceOf=>
248
+ # {"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"=>800,
249
+ # "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"=>700,
250
+ # "0xcccccccccccccccccccccccccccccccccccccccc"=>600},
251
+ # :allowance=>{},
252
+ # :maxSupply=>21000000,
253
+ # :perMintLimit=>1000}
254
+
255
+
256
+ # sig [Address, UInt], returns: Bool
257
+ # def transfer( to:, amount: )
258
+ contract.transfer( alice, 1 )
259
+ contract.transfer( charlie, 2 )
260
+
261
+ # sig [Address, UInt], returns: Bool
262
+ # def approve( spender:, amount: )
263
+ contract.approve( alice, 11 )
264
+ contract.approve( charlie, 22 )
265
+
266
+
267
+ # sig [Address, Address, UInt], returns: Bool
268
+ # def transferFrom( from:, to:, amount:)
269
+ contract.msg.sender = alice
270
+
271
+ contract.approve( bob, 33 )
272
+
273
+ contract.transferFrom( bob, charlie, 3 )
274
+ contract.transferFrom( bob, alice, 4 )
275
+
276
+ contract.serialize
277
+ # {:name=>"My Fun Token",
278
+ # :symbol=>"FUN",
279
+ # :decimals=>18,
280
+ # :totalSupply=>2100,
281
+ # :balanceOf=>
282
+ # {"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"=>805,
283
+ # "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"=>690,
284
+ # "0xcccccccccccccccccccccccccccccccccccccccc"=>605},
285
+ # :allowance=>
286
+ # {"0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"=> {
287
+ # "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"=>4,
288
+ # "0xcccccccccccccccccccccccccccccccccccccccc"=>22},
289
+ # "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"=> {
290
+ # "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"=>33}},
291
+ # :maxSupply=>21000000,
292
+ # :perMintLimit=>1000}
293
+ ```
294
+
295
+ And so on. That's it for now.
296
+
297
+
298
+
299
+ ## Bonus - More Blockchain (Crypto) Tools, Libraries & Scripts In Ruby
300
+
301
+ See [**/blockchain**](https://github.com/rubycocos/blockchain)
302
+ at the ruby code commons (rubycocos) org.
303
+
304
+
305
+
306
+
307
+
308
+ ## Questions? Comments?
309
+
310
+ Join us in the [Rubidity & Rubysol (community) discord (chat server)](https://discord.gg/3JRnDUap6y). Yes you can.
311
+ Your questions and commentary welcome.
312
+
313
+ Or post them over at the [Help & Support](https://github.com/geraldb/help) page. Thanks.
314
+
315
+
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ require 'hoe'
2
+ require './lib/rubysol/contracts/version.rb'
3
+
4
+ Hoe.spec 'rubysol-contracts' do
5
+
6
+ self.version = Rubysol::Module::Contracts::VERSION
7
+
8
+ self.summary = 'rubysol-contracts - standard contracts (incl. erc20, erc721, etc) for ruby for layer 1 (l1) with "off-chain" indexer'
9
+ self.description = summary
10
+
11
+ self.urls = { home: 'https://github.com/s6ruby/rubidity' }
12
+
13
+ self.author = 'Gerald Bauer'
14
+ self.email = 'gerald.bauer@gmail.com'
15
+
16
+ # switch extension to .markdown for gihub formatting
17
+ self.readme_file = 'README.md'
18
+ self.history_file = 'CHANGELOG.md'
19
+
20
+ self.extra_deps = [
21
+ ['rubysol', '>= 0.1.0'],
22
+ ]
23
+
24
+ self.licenses = ['Public Domain']
25
+
26
+ self.spec_extras = {
27
+ required_ruby_version: '>= 2.3'
28
+ }
29
+ end
@@ -0,0 +1,77 @@
1
+ class ERC20 < Contract # abstract: true
2
+
3
+ event :Transfer, from: Address,
4
+ to: Address,
5
+ amount: UInt
6
+ event :Approval, owner: Address,
7
+ spender: Address,
8
+ amount: UInt
9
+
10
+ storage name: String,
11
+ symbol: String,
12
+ decimals: UInt,
13
+ totalSupply: UInt,
14
+ balanceOf: mapping( Address, UInt),
15
+ allowance: mapping( Address, mapping( Address, UInt))
16
+
17
+ sig [String, String, UInt]
18
+ def constructor(name:, symbol:, decimals:)
19
+ @name = name
20
+ @symbol = symbol
21
+ @decimals = decimals
22
+ end
23
+
24
+ sig [Address, UInt], returns: Bool
25
+ def approve( spender:, amount: )
26
+ @allowance[msg.sender][spender] = amount
27
+
28
+ log Approval, owner: msg.sender, spender: spender, amount: amount
29
+
30
+ true
31
+ end
32
+
33
+ sig [Address, UInt], returns: Bool
34
+ def transfer( to:, amount: )
35
+ assert @balanceOf[msg.sender] >= amount, "Insufficient balance"
36
+
37
+ @balanceOf[msg.sender] -= amount
38
+ @balanceOf[to] += amount
39
+
40
+ log Transfer, from: msg.sender, to: to, amount: amount
41
+
42
+ true
43
+ end
44
+
45
+ sig [Address, Address, UInt], returns: Bool
46
+ def transferFrom( from:, to:, amount: )
47
+ allowed = @allowance[from][msg.sender]
48
+
49
+ assert @balanceOf[from] >= amount, "Insufficient balance"
50
+ assert allowed >= amount, "Insufficient allowance"
51
+
52
+ @allowance[from][msg.sender] = allowed - amount
53
+
54
+ @balanceOf[from] -= amount
55
+ @balanceOf[to] += amount
56
+
57
+ log Transfer, from: from, to: to, amount: amount
58
+
59
+ true
60
+ end
61
+
62
+ sig [Address, UInt]
63
+ def _mint( to:, amount: )
64
+ @totalSupply += amount
65
+ @balanceOf[to] += amount
66
+
67
+ log Transfer, from: address(0), to: to, amount: amount
68
+ end
69
+
70
+ sig [Address, UInt]
71
+ def _burn( from:, amount: )
72
+ @balanceOf[from] -= amount
73
+ @totalSupply -= amount
74
+
75
+ log Transfer, from: from, to: address(0), amount: amount
76
+ end
77
+ end # class ERC20
@@ -0,0 +1,84 @@
1
+ class ERC20LiquidityPool < Contract
2
+
3
+ storage token0: Address,
4
+ token1: Address
5
+
6
+ sig [Address, Address]
7
+ def constructor( token0:,
8
+ token1: )
9
+ @token0 = token0
10
+ @token1 = token1
11
+ end
12
+
13
+ sig [UInt, UInt]
14
+ def addLiquidity( token0Amount:,
15
+ token1Amount: )
16
+ ERC20(@token0).transferFrom(
17
+ from: msg.sender,
18
+ to: address( this ), ## note: add support for this (alias for self ??)
19
+ amount: token0Amount
20
+ )
21
+
22
+ ERC20(@token1).transferFrom(
23
+ from: msg.sender,
24
+ to: address(this),
25
+ amount: token1Amount
26
+ )
27
+ end
28
+
29
+
30
+ sig [], :view ### fix- support multiple, returns: [UInt, UInt]
31
+ def reserves
32
+ {
33
+ token0: ERC20(@token0).balanceOf(address(this)),
34
+ token1: ERC20(@token1).balanceOf(address(this))
35
+ }
36
+ end
37
+
38
+
39
+
40
+ sig [Address, Address, UInt], :view, returns: UInt
41
+ def calculateOutputAmount( inputToken:,
42
+ outputToken:,
43
+ inputAmount: )
44
+ inputReserve = ERC20(inputToken).balanceOf(address(this))
45
+ outputReserve = ERC20(outputToken).balanceOf(address(this))
46
+
47
+ ((inputAmount * outputReserve) / (inputReserve + inputAmount)).to_i
48
+ end
49
+
50
+
51
+ sig [Address, Address, UInt], returns: UInt
52
+ def swap(
53
+ inputToken:,
54
+ outputToken:,
55
+ inputAmount: )
56
+ assert [@token0, @token1].include?(inputToken), "Invalid input token"
57
+ assert [@token0, @token1].include?(outputToken), "Invalid output token"
58
+
59
+ assert inputToken != outputToken, "Input and output tokens can't be the same"
60
+
61
+ outputAmount = calculateOutputAmount(
62
+ inputToken: inputToken,
63
+ outputToken: outputToken,
64
+ inputAmount: inputAmount
65
+ )
66
+
67
+ outputReserve = ERC20(outputToken).balanceOf( address(this))
68
+
69
+ assert outputAmount <= outputReserve, "Insufficient output reserve"
70
+
71
+ ERC20(inputToken).transferFrom(
72
+ from: msg.sender,
73
+ to: dumbContractId(this),
74
+ amount: inputAmount
75
+ )
76
+
77
+ ERC20(outputToken).transfer(
78
+ msg.sender,
79
+ outputAmount
80
+ )
81
+
82
+ outputAmount
83
+ end
84
+ end
@@ -0,0 +1,121 @@
1
+ class ERC721 < Contract
2
+
3
+ event :Transfer, from: Address,
4
+ to: Address,
5
+ id: UInt
6
+ event :Approval, owner: Address,
7
+ spender: Address,
8
+ id: UInt
9
+ event :ApprovalForAll, owner: Address,
10
+ operator: Address,
11
+ approved: Bool
12
+
13
+ storage name: String,
14
+ symbol: String,
15
+ _ownerOf: mapping( UInt, Address ),
16
+ _balanceOf: mapping( Address, UInt ),
17
+ getApproved: mapping( UInt, Address ),
18
+ isApprovedForAll: mapping( Address, mapping( Address, Bool ))
19
+
20
+ sig [String, String]
21
+ def constructor( name:,
22
+ symbol: )
23
+ @name = name
24
+ @symbol = symbol
25
+ end
26
+
27
+
28
+ sig [ UInt ], :view, returns: Address
29
+ def ownerOf( id )
30
+ owner = @_ownerOf[id]
31
+ assert owner != address(0), "ERC721: owner query for nonexistent token"
32
+
33
+ owner
34
+ end
35
+
36
+ sig [ Address ], :view, returns: UInt
37
+ def balanceOf( owner: )
38
+ assert owner != address(0), "ERC721: balance query for nonexistent owner"
39
+
40
+ @_balanceOf[owner]
41
+ end
42
+
43
+ sig [ Address, UInt]
44
+ def approve( spender:,
45
+ id: )
46
+ owner = @_ownerOf[id]
47
+
48
+ assert msg.sender == owner || @isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED"
49
+
50
+ @getApproved[id] = spender;
51
+
52
+ log Approval, owner: owner, spender: spender, id: id
53
+ end
54
+
55
+ sig [Address, Bool]
56
+ def setApprovalForAll( operator:,
57
+ approved: )
58
+ @isApprovedForAll[msg.sender][operator] = approved;
59
+
60
+ log ApprovalForAll, owner: msg.sender, operator: operator, approved: approved
61
+ end
62
+
63
+ sig [ Address, Address, UInt]
64
+ def transferFrom( from:,
65
+ to:,
66
+ id: )
67
+ assert from == @_ownerOf[id], "ERC721: transfer of token that is not own"
68
+ assert to != address(0), "ERC721: transfer to the zero address"
69
+
70
+ assert(
71
+ msg.sender == from ||
72
+ @getApproved[id] == msg.sender ||
73
+ @isApprovedForAll[from][msg.sender],
74
+ "NOT_AUTHORIZED"
75
+ )
76
+
77
+ @_balanceOf[from] -= 1
78
+ @_balanceOf[to] += 1
79
+
80
+ @_ownerOf[id] = to
81
+
82
+ @getApproved[id] = address(0)
83
+ end
84
+
85
+ sig [UInt]
86
+ def _exists( id )
87
+ @_ownerOf[id] != address(0)
88
+ end
89
+
90
+ sig [Address, UInt]
91
+ def _mint( to:,
92
+ id: )
93
+ assert to != address(0), "ERC721: mint to the zero address"
94
+ assert @_ownerOf[id] == address(0), "ERC721: token already minted"
95
+
96
+ @_balanceOf[to] += 1
97
+
98
+ @_ownerOf[id] = to
99
+
100
+ log Transfer, from: address(0), to: to, id: id
101
+ end
102
+
103
+ sig [UInt]
104
+ def _burn( id: )
105
+ owner = @_ownerOf[id]
106
+
107
+ assert owner != address(0), "ERC721: burn of nonexistent token"
108
+
109
+ @_balanceOf[owner] -= 1
110
+
111
+ @_ownerOf[id] = address(0)
112
+
113
+ @getApproved[id] = address(0)
114
+
115
+ log Transfer, from: owner, to: address(0), id: id
116
+ end
117
+
118
+ sig [UInt], :view, returns: String
119
+ def tokenURI( id: )
120
+ end
121
+ end
@@ -0,0 +1,54 @@
1
+ class EtherERC20Bridge < ERC20
2
+
3
+ event :InitiateWithdrawal, from: Address, amount: UInt
4
+ event :WithdrawalComplete, to: Address, amount: UInt
5
+
6
+ storage trustedSmartContract: Address,
7
+ pendingWithdrawals: mapping( Address, UInt )
8
+
9
+ sig [String, String, Address]
10
+ def constructor(
11
+ name:,
12
+ symbol:,
13
+ trustedSmartContract:)
14
+ super(name: name, symbol: symbol, decimals: 18)
15
+
16
+ @trustedSmartContract = trustedSmartContract
17
+ end
18
+
19
+ sig [Address, UInt]
20
+ def bridgeIn( to:, amount: )
21
+ assert(
22
+ address(msg.sender) == @trustedSmartContract,
23
+ "Only the trusted smart contract can bridge in tokens"
24
+ )
25
+
26
+ _mint(to: to, amount: amount)
27
+ end
28
+
29
+ sig [UInt]
30
+ def bridgeOut( amount: )
31
+ _burn(from: msg.sender, amount: amount)
32
+
33
+ @pendingWithdrawals[address(msg.sender)] += amount
34
+
35
+ log InitiateWithdrawal, from: address(msg.sender), amount: amount
36
+ end
37
+
38
+ sig [Address, UInt]
39
+ def markWithdrawalComplete( to:, amount: )
40
+ assert(
41
+ address(msg.sender) == @trustedSmartContract,
42
+ 'Only the trusted smart contract can mark withdrawals as complete'
43
+ )
44
+
45
+ assert(
46
+ @pendingWithdrawals[to] >= amount,
47
+ 'Insufficient pending withdrawal'
48
+ )
49
+
50
+ @pendingWithdrawals[to] -= amount
51
+
52
+ log WithdrawalComplete, to: to, amount: amount
53
+ end
54
+ end
@@ -0,0 +1,119 @@
1
+ class EthscriptionERC20Bridge < ERC20
2
+
3
+ event :InitiateWithdrawal, from: Address, escrowedId: InscriptionId
4
+ event :WithdrawalComplete, to: Address, escrowedId: InscriptionId
5
+
6
+ storage ethscriptionsTicker: String,
7
+ ethscriptionMintAmount: UInt,
8
+ ethscriptionMaxSupply: UInt,
9
+ ethscriptionDeployId: InscriptionId,
10
+ trustedSmartContract: Address,
11
+ pendingWithdrawalEthscriptionToOwner: mapping( InscriptionId, Address ),
12
+ bridgedEthscriptionToOwner: mapping( InscriptionId, Address )
13
+
14
+ sig [String, String, Address, InscriptionId]
15
+ def constructor(
16
+ name:,
17
+ symbol:,
18
+ trustedSmartContract:,
19
+ ethscriptionDeployId:)
20
+ super(name: name, symbol: symbol, decimals: 18)
21
+
22
+ @trustedSmartContract = trustedSmartContract
23
+ @ethscriptionDeployId = ethscriptionDeployId
24
+
25
+ deploy = esc.findEthscriptionById( ethscriptionDeployId )
26
+ uri = deploy.contentUri
27
+ parsed = JSON.parse(uri.split("data:,").last)
28
+
29
+ assert(parsed['op'] == 'deploy', "Invalid ethscription deploy id")
30
+ assert(parsed['p'] == 'erc-20', "Invalid protocol")
31
+
32
+ @ethscriptionsTicker = parsed['tick']
33
+ @ethscriptionMintAmount = parsed['lim']
34
+ @ethscriptionMaxSupply = parsed['max']
35
+ end
36
+
37
+
38
+ sig [Address, InscriptionId]
39
+ def bridgeIn( to:, escrowedId: )
40
+ assert(
41
+ address(msg.sender) == @trustedSmartContract,
42
+ "Only the trusted smart contract can bridge in tokens"
43
+ )
44
+
45
+ assert(
46
+ @bridgedEthscriptionToOwner[escrowedId] == address(0),
47
+ "Ethscription already bridged in"
48
+ )
49
+
50
+ assert(
51
+ @pendingWithdrawalEthscriptionToOwner[escrowedId] == address(0),
52
+ "Ethscription withdrawal initiated"
53
+ )
54
+
55
+ ethscription = esc.findEthscriptionById(escrowedId)
56
+ uri = ethscription.contentUri
57
+
58
+ match_data = uri.match(/data:,{"p":"erc-20","op":"mint","tick":"([a-z]+)","id":"([1-9]+\d*)","amt":"([1-9]+\d*)"}/)
59
+
60
+ assert(match_data.present?, "Invalid ethscription content uri")
61
+
62
+ tick, id, amt = match_data.captures
63
+
64
+ tick = tick.cast(:string)
65
+ id = id.cast(:uint256)
66
+ amt = amt.cast(:uint256)
67
+
68
+ assert(tick == @ethscriptionsTicker, "Invalid ethscription ticker")
69
+ assert(amt == @ethscriptionMintAmount, "Invalid ethscription mint amount")
70
+
71
+ maxId = @ethscriptionMaxSupply / @ethscriptionMintAmount
72
+
73
+ assert(id > 0 && id <= maxId, "Invalid token id")
74
+
75
+ assert(
76
+ ethscription.currentOwner == @trustedSmartContract,
77
+ "Ethscription not owned by recipient. Observed owner: #{ethscription.currentOwner}, expected owner: #{@trustedSmartContract}"
78
+ )
79
+
80
+ assert(
81
+ ethscription.previousOwner == to,
82
+ "Ethscription not previously owned by to. Observed previous owner: #{ethscription.previousOwner}, expected previous owner: #{to}"
83
+ )
84
+
85
+ @bridgedEthscriptionToOwner[escrowedId] = to
86
+ _mint(to: to, amount: @ethscriptionMintAmount * (10 ** decimals))
87
+ end
88
+
89
+
90
+ sig [InscriptionId]
91
+ def bridgeOut( escrowedId )
92
+ assert( @bridgedEthscriptionToOwner[escrowedId] == address(msg.sender), "Ethscription not owned by sender")
93
+
94
+ _burn(from: msg.sender, amount: @ethscriptionMintAmount * (10 ** decimals))
95
+
96
+ @bridgedEthscriptionToOwner[escrowedId] = address(0)
97
+ @pendingWithdrawalEthscriptionToOwner[escrowedId] = address(msg.sender)
98
+
99
+ log InitiateWithdrawal, from: address(msg.sender), escrowedId: :ethscriptionId
100
+ end
101
+
102
+
103
+ sig [Address, InscriptionId]
104
+ def markWithdrawalComplete( to:, escrowedId: )
105
+ assert(
106
+ address(msg.sender) == @trustedSmartContract,
107
+ 'Only the trusted smart contract can mark withdrawals as complete'
108
+ )
109
+
110
+ assert(
111
+ @pendingWithdrawalEthscriptionToOwner[escrowedId] == to,
112
+ "Withdrawal not initiated"
113
+ )
114
+
115
+ @pendingWithdrawalEthscriptionToOwner[escrowedId] = address(0)
116
+
117
+ log WithdrawalComplete, to: to, escrowedId: :ethscriptionId
118
+ end
119
+ end
@@ -0,0 +1,93 @@
1
+ class GenerativeERC721 < ERC721
2
+
3
+ storage generativeScript: String,
4
+ tokenIdToSeed: mapping( UInt, UInt ),
5
+ totalSupply: UInt,
6
+ maxSupply: UInt,
7
+ maxPerAddress: UInt,
8
+ description: String
9
+
10
+ sig [ String, String, String, UInt, String, UInt]
11
+ def constructor(
12
+ name:,
13
+ symbol:,
14
+ generativeScript:,
15
+ maxSupply:,
16
+ description:,
17
+ maxPerAddress: )
18
+ super(name: name, symbol: symbol)
19
+
20
+ @maxSupply = maxSupply
21
+ @maxPerAddress = maxPerAddress
22
+ @description = description
23
+ @generativeScript = generativeScript
24
+ end
25
+
26
+ sig [UInt]
27
+ def mint( amount )
28
+ assert(amount > 0, 'Amount must be positive')
29
+ assert(amount + @_balanceOf[msg.sender] <= @maxPerAddress, 'Exceeded mint limit')
30
+ assert(amount + @totalSupply <= @maxSupply, 'Exceeded max supply')
31
+
32
+ hash = block.blockhash(block.number).cast(:uint256) % (2 ** 48)
33
+
34
+ amount.times do |id|
35
+ tokenId = @totalSupply + id
36
+ seed = hash + tokenId
37
+
38
+ @tokenIdToSeed[tokenId] = seed
39
+
40
+ _mint(to: msg.sender, id: tokenId)
41
+ end
42
+
43
+ @totalSupply += amount
44
+ end
45
+
46
+ sig [UInt], :view, returns: String
47
+ def tokenURI( id )
48
+ assert( _exists(id: id), 'ERC721Metadata: URI query for nonexistent token')
49
+
50
+ html = getHTML(seed: @tokenIdToSeed[id])
51
+
52
+ html_as_base_64_data_uri = "data:text/html;base64,#{Base64.strict_encode64(html)}"
53
+
54
+ json_data = {
55
+ name: "#{@name} ##{string(id)}",
56
+ description: @description,
57
+ animation_url: html_as_base_64_data_uri,
58
+ }.to_json
59
+
60
+ "data:application/json,#{json_data}"
61
+ end
62
+
63
+ sig [UInt], :view, returns: String
64
+ def getHTML( seed )
65
+ %{<!DOCTYPE html>
66
+ <html>
67
+ <head>
68
+ <style>
69
+ body,
70
+ html {
71
+ width: 100%;
72
+ height: 100%;
73
+ margin: 0;
74
+ padding: 0;
75
+ overflow: hidden;
76
+ display: block;
77
+ }
78
+
79
+ #canvas {
80
+ position: absolute;
81
+ }
82
+ </style>
83
+ </head>
84
+ <body>
85
+ <canvas id="canvas"></canvas>
86
+ </body>
87
+ <script>
88
+ window.SEED = #{string(seed)};
89
+ #{@generativeScript}
90
+ </script>
91
+ </html>}
92
+ end
93
+ end
@@ -0,0 +1,54 @@
1
+ class OpenEditionERC721 < ERC721
2
+
3
+ storage contentURI: String,
4
+ maxPerAddress: UInt,
5
+ totalSupply: UInt,
6
+ description: String,
7
+ mintStart: Timestamp,
8
+ mintEnd: Timestamp
9
+
10
+ sig [String, String, String, UInt, String, Timestamp, Timestamp]
11
+ def constructor(
12
+ name:,
13
+ symbol:,
14
+ contentURI:,
15
+ maxPerAddress:,
16
+ description:,
17
+ mintStart:,
18
+ mintEnd: )
19
+ super(name: name, symbol: symbol)
20
+
21
+ @maxPerAddress = maxPerAddress
22
+ @description = description
23
+ @contentURI = contentURI
24
+ @mintStart = mintStart
25
+ @mintEnd = mintEnd
26
+ end
27
+
28
+ sig [UInt]
29
+ def mint( amount: )
30
+ assert(amount > 0, 'Amount must be positive')
31
+ assert(amount + @_balanceOf[msg.sender] <= @maxPerAddress, 'Exceeded mint limit')
32
+ assert(block.timestamp >= @mintStart, 'Minting has not started')
33
+ assert(block.timestamp < @mintEnd, 'Minting has ended')
34
+
35
+ amount.times do |id|
36
+ _mint(to: msg.sender, id: @totalSupply + id)
37
+ end
38
+
39
+ @totalSupply += amount
40
+ end
41
+
42
+ sig [UInt], :view, returns: String
43
+ def tokenURI( id: )
44
+ assert(_exists(id: id), 'ERC721Metadata: URI query for nonexistent token')
45
+
46
+ json_data = {
47
+ name: "#{@name} ##{string(id)}",
48
+ description: @description,
49
+ image: @contentURI,
50
+ }.to_json
51
+
52
+ "data:application/json,#{json_data}"
53
+ end
54
+ end
@@ -0,0 +1,37 @@
1
+
2
+ class PublicMintERC20 < ERC20
3
+
4
+ storage maxSupply: UInt,
5
+ perMintLimit: UInt
6
+
7
+ sig [String, String, UInt, UInt, UInt]
8
+ def constructor(
9
+ name:,
10
+ symbol:,
11
+ maxSupply:,
12
+ perMintLimit:,
13
+ decimals:
14
+ )
15
+ super( name: name, symbol: symbol, decimals: decimals )
16
+ @maxSupply = maxSupply
17
+ @perMintLimit = perMintLimit
18
+ end
19
+
20
+ sig [UInt]
21
+ def mint( amount: )
22
+ assert amount > 0, 'Amount must be positive'
23
+ assert amount <= @perMintLimit, 'Exceeded mint limit'
24
+ assert @totalSupply + amount <= @maxSupply, 'Exceeded max supply'
25
+
26
+ _mint( to: msg.sender, amount: amount )
27
+ end
28
+
29
+ sig [Address, UInt]
30
+ def airdrop( to:, amount: )
31
+ assert amount > 0, 'Amount must be positive'
32
+ assert amount <= @perMintLimit, 'Exceeded mint limit'
33
+ assert @totalSupply + amount <= @maxSupply, 'Exceeded max supply'
34
+
35
+ _mint( to: to, amount: amount )
36
+ end
37
+ end # class PublicMintERC20
@@ -0,0 +1,23 @@
1
+ module Rubysol
2
+ module Module
3
+ module Contracts
4
+ MAJOR = 0
5
+ MINOR = 1
6
+ PATCH = 0
7
+ VERSION = [MAJOR,MINOR,PATCH].join('.')
8
+
9
+ def self.version
10
+ VERSION
11
+ end
12
+
13
+ def self.banner
14
+ "rubysol-contracts/#{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 Contracts
22
+ end # module Module
23
+ end # module Rubysol
@@ -0,0 +1,18 @@
1
+ require 'solidity/typed'
2
+ require 'rubysol'
3
+
4
+ ## our own code / contracts
5
+ require_relative 'contracts/version'
6
+ require_relative 'contracts/erc20.rb'
7
+ require_relative 'contracts/erc20_liquidity_pool.rb'
8
+ require_relative 'contracts/erc721.rb'
9
+ require_relative 'contracts/ether_erc20_bridge.rb'
10
+ require_relative 'contracts/ethscription_erc20_bridge.rb'
11
+ require_relative 'contracts/generative_erc721.rb'
12
+ require_relative 'contracts/open_edition_erc721.rb'
13
+ require_relative 'contracts/public_mint_erc20.rb'
14
+
15
+
16
+
17
+
18
+ puts Rubysol::Module::Contracts.banner ## say hello
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubysol-contracts
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.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: rubysol-contracts - standard contracts (incl. erc20, erc721, etc) for
62
+ ruby for layer 1 (l1) with "off-chain" indexer
63
+ email: gerald.bauer@gmail.com
64
+ executables: []
65
+ extensions: []
66
+ extra_rdoc_files:
67
+ - CHANGELOG.md
68
+ - Manifest.txt
69
+ - README.md
70
+ files:
71
+ - CHANGELOG.md
72
+ - Manifest.txt
73
+ - README.md
74
+ - Rakefile
75
+ - lib/rubysol/contracts.rb
76
+ - lib/rubysol/contracts/erc20.rb
77
+ - lib/rubysol/contracts/erc20_liquidity_pool.rb
78
+ - lib/rubysol/contracts/erc721.rb
79
+ - lib/rubysol/contracts/ether_erc20_bridge.rb
80
+ - lib/rubysol/contracts/ethscription_erc20_bridge.rb
81
+ - lib/rubysol/contracts/generative_erc721.rb
82
+ - lib/rubysol/contracts/open_edition_erc721.rb
83
+ - lib/rubysol/contracts/public_mint_erc20.rb
84
+ - lib/rubysol/contracts/version.rb
85
+ homepage: https://github.com/s6ruby/rubidity
86
+ licenses:
87
+ - Public Domain
88
+ metadata: {}
89
+ post_install_message:
90
+ rdoc_options:
91
+ - "--main"
92
+ - README.md
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '2.3'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubygems_version: 3.4.10
107
+ signing_key:
108
+ specification_version: 4
109
+ summary: rubysol-contracts - standard contracts (incl. erc20, erc721, etc) for ruby
110
+ for layer 1 (l1) with "off-chain" indexer
111
+ test_files: []