synthpunks 0.0.1 → 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 +4 -4
- data/README.md +278 -2
- data/Rakefile +1 -0
- data/lib/synthpunks/version.rb +2 -2
- data/lib/synthpunks.rb +1 -0
- metadata +15 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c0238c880c91ece2f04aaa0f63efd9709c796f135a10b83619a664bd7655fac9
|
4
|
+
data.tar.gz: b4ce29e7fcb469c83f6a642f26e5090a35c6e760e504ff87b0ba471452185e98
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bcc2e2999185747150907e51993ba03b8e55564bcf18d601dbd2f6d8c41b370f2189871ac28e74d0836313d9cc86d0630b4e96f78bc355d2f48f03cfc32f70d6
|
7
|
+
data.tar.gz: de49e479e2104a7695854d47617c1796867e4cadd18b4bedca25fb0d77032a9cab9730d647c042bb7501b38dbb483bf9913f5735caa73399e42ea8ec52a4ba8e
|
data/README.md
CHANGED
@@ -11,9 +11,285 @@ synthpunks - (free unlimited) 24×24 pixel punks for everyone - yes, you can - g
|
|
11
11
|
|
12
12
|
|
13
13
|
|
14
|
-
##
|
14
|
+
## What are Synthetic (Crypto) Punks V1?
|
15
15
|
|
16
|
-
|
16
|
+
> Fully on-chain (crypto) punks for every ethereum address. Check yours out for free!
|
17
|
+
>
|
18
|
+
> -- [**syntheticpunks.com**](https://syntheticpunks.com)
|
19
|
+
|
20
|
+
|
21
|
+
[Stephan Cilliers](https://github.com/stephancill) (from South Africa)
|
22
|
+
has put together an open-source blockchain service ( - see [**stephancill/synthetic-punks**](https://github.com/stephancill/synthetic-punks) - )
|
23
|
+
to generate for free¹ 24×24 punk pixel heads for every ethereum address (account).
|
24
|
+
|
25
|
+
¹: and optionally clain / mint a (non-fungible) token for a 0.02 ether fee.
|
26
|
+
|
27
|
+
|
28
|
+
The F.A.Q. reads:
|
29
|
+
|
30
|
+
> Q: What are Synthetic Punks?
|
31
|
+
>
|
32
|
+
> A: Synthetic Punks is inspired by the historical
|
33
|
+
> collection of 10 000 Matt & John's® Punks [Anno 2017]
|
34
|
+
> by Larva Labs and Synthetic Loot by Dom Hofmann.
|
35
|
+
> It generates a unique, fully on-blockchain punk for each Ethereum address.
|
36
|
+
>
|
37
|
+
> They are free to view for any address, but can be claimed [minted]
|
38
|
+
> as an ERC-721 non-fungible token (NFT)
|
39
|
+
> for a price of 0.02 ether.
|
40
|
+
>
|
41
|
+
> Features - Each Synthetic Punk
|
42
|
+
> - Is generated from assets stored fully on-blockchain
|
43
|
+
> - Is uniquely associated with an ethereum wallet address
|
44
|
+
> - Supports Ethereum Name Service (ENS) in its metadata
|
45
|
+
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
## "Off-Chain" Usage In Your Script
|
50
|
+
|
51
|
+
|
52
|
+
Anyways, the synthpunks gem / library is inspired by Synthetic Punks V1
|
53
|
+
by Stephan Cilliers (see above)
|
54
|
+
and lets you generate punks using the "original"
|
55
|
+
Synthetic Punks V1 pseudo-random number and image generation formula
|
56
|
+
"off-blockchain"¹
|
57
|
+
using the "original" Synthetic Punks V1 spritesheet
|
58
|
+
|
59
|
+

|
60
|
+
|
61
|
+
and attribute categories (ranges) and names.
|
62
|
+
|
63
|
+
|
64
|
+
Let's try
|
65
|
+
the (ethereum) address 0x054f3b6eadc9631ccd60246054fdb0fcfe99b322:
|
66
|
+
|
67
|
+
|
68
|
+
``` ruby
|
69
|
+
require 'synthpunks'
|
70
|
+
|
71
|
+
punk = Synthpunks::Image.generate('0x054f3b6eadc9631ccd60246054fdb0fcfe99b322' )
|
72
|
+
punk.save( 'punk1.png' )
|
73
|
+
punk.zoom(8).save( 'punk1@8x.png' )
|
74
|
+
```
|
75
|
+
|
76
|
+
resulting in:
|
77
|
+
|
78
|
+
 8x
|
79
|
+

|
80
|
+
|
81
|
+
|
82
|
+
Tip: Use `Synthpunks.getTokenID( addr )`
|
83
|
+
to get the "official" token id
|
84
|
+
from the address (that really is only a "convenience" helper that converts the 40-digit / 20-byte hexstring to a decimal (big) integer number). Example:
|
85
|
+
|
86
|
+
``` ruby
|
87
|
+
Synthpunks.getTokenID('0x054f3b6eadc9631ccd60246054fdb0fcfe99b322' )
|
88
|
+
#=> 30311890011735557186986086868537068337617285922
|
89
|
+
|
90
|
+
# note: yes, Synthpunks.getTokenID same as String#to_i(16)
|
91
|
+
'0x054f3b6eadc9631ccd60246054fdb0fcfe99b322'.to_i( 16 )
|
92
|
+
#=> 30311890011735557186986086868537068337617285922
|
93
|
+
```
|
94
|
+
|
95
|
+
And use the new exclusive "off-chain" only `Synthpunks.getAssetNames( id )`
|
96
|
+
helper
|
97
|
+
to get all (random) punk attributes by name.
|
98
|
+
|
99
|
+
``` ruby
|
100
|
+
Synthpunks.getAttributeNames( 30311890011735557186986086868537068337617285922 )
|
101
|
+
#=> ["Alien", "Clown Nose", "Frown", "Vape", "Eyebrows"]
|
102
|
+
```
|
103
|
+
|
104
|
+
|
105
|
+
Now lookup the minted
|
106
|
+
Synthetic (Crypto) Punks V1 on the open sea (secondary)
|
107
|
+
market:
|
108
|
+
|
109
|
+
[Minted Synthetic Punk for 0xaf9ce4b327a3b690abea6f78eccbfefffbea9fdf
|
110
|
+
with Token ID #30311890011735557186986086868537068337617285922 @ Open Sea](https://opensea.io/assets/ethereum/0xaf9ce4b327a3b690abea6f78eccbfefffbea9fdf/30311890011735557186986086868537068337617285922)
|
111
|
+
|
112
|
+
Or see
|
113
|
+
[Synthetic Punk for 0xaf9ce4b327a3b690abea6f78eccbfefffbea9fdf](https://syntheticpunks.com/#/address/0xaf9ce4b327a3b690abea6f78eccbfefffbea9fdf)
|
114
|
+
|
115
|
+
|
116
|
+
And spot the differencence - hint: none other
|
117
|
+
than the "dark / black-ish" background is missing.
|
118
|
+
|
119
|
+
|
120
|
+
Let's retry and let's add the "dark / black-ish" background
|
121
|
+
that is, #1A1A1A, using the rgb hex color code:
|
122
|
+
|
123
|
+
``` ruby
|
124
|
+
punk = Synthpunks::Image.generate('0x054f3b6eadc9631ccd60246054fdb0fcfe99b322' )
|
125
|
+
punk = punk.background( '#1A1A1A' )
|
126
|
+
punk.save( 'punk1b.png' )
|
127
|
+
punk.zoom(8).save( 'punk1b@8x.png' )
|
128
|
+
```
|
129
|
+
|
130
|
+
resulting in:
|
131
|
+
|
132
|
+
 8x
|
133
|
+

|
134
|
+
|
135
|
+
|
136
|
+
Or for fun let's try some more new "custom" backgrounds.
|
137
|
+
Example: Stand with Ukraine / Ukraine Flag:
|
138
|
+
|
139
|
+
``` ruby
|
140
|
+
punk = Synthpunks::Image.generate('0x054f3b6eadc9631ccd60246054fdb0fcfe99b322' )
|
141
|
+
punk = punk.background( 'ukraine' )
|
142
|
+
punk.save( 'punk1c.png' )
|
143
|
+
punk.zoom(8).save( 'punk1c@8x.png' )
|
144
|
+
```
|
145
|
+
|
146
|
+
resulting in:
|
147
|
+
|
148
|
+
 8x
|
149
|
+

|
150
|
+
|
151
|
+
|
152
|
+
or
|
153
|
+
|
154
|
+
``` ruby
|
155
|
+
punk = punk.background( 'rainbow' )
|
156
|
+
```
|
157
|
+
|
158
|
+
resulting in:
|
159
|
+
|
160
|
+
 8x
|
161
|
+

|
162
|
+
|
163
|
+
and so on.
|
164
|
+
|
165
|
+
Tip: Looking for left-looking phunks? Try `Image#mirror`.
|
166
|
+
Example:
|
167
|
+
|
168
|
+
``` ruby
|
169
|
+
punk = Synthpunks::Image.generate('0x054f3b6eadc9631ccd60246054fdb0fcfe99b322' )
|
170
|
+
phunk = punk.mirror
|
171
|
+
phunk.save( 'phunk1.png' )
|
172
|
+
phunk.zoom(8).save( 'phunk1@8x.png' )
|
173
|
+
```
|
174
|
+
|
175
|
+
resulting in:
|
176
|
+
|
177
|
+
 8x
|
178
|
+

|
179
|
+
|
180
|
+
|
181
|
+
Bonus Tip: Derive your own custom default profile pictures
|
182
|
+
using `Image#silhouette`. Example:
|
183
|
+
|
184
|
+
|
185
|
+
``` ruby
|
186
|
+
punk = Synthpunks::Image.generate('0x054f3b6eadc9631ccd60246054fdb0fcfe99b322' )
|
187
|
+
punk = punk.silhouette( '0x647785' ).background( '0xCCD5DE' )
|
188
|
+
punk.save( 'punk1-default1.png' )
|
189
|
+
punk.zoom(8).save( 'punk1-default1@8x.png' )
|
190
|
+
```
|
191
|
+
|
192
|
+
resulting in:
|
193
|
+
|
194
|
+
 8x
|
195
|
+

|
196
|
+
|
197
|
+
or is blue the new gray?
|
198
|
+
|
199
|
+
``` ruby
|
200
|
+
punk = punk.silhouette( '0x4474E0' ).background( '0xA0C2FF' )
|
201
|
+
```
|
202
|
+
|
203
|
+
resulting in:
|
204
|
+
|
205
|
+
 8x
|
206
|
+

|
207
|
+
|
208
|
+
and so on.
|
209
|
+
|
210
|
+
|
211
|
+
|
212
|
+
|
213
|
+
Yes, you can.
|
214
|
+
Let's try some more
|
215
|
+
the (ethereum) addresses.
|
216
|
+
|
217
|
+
``` ruby
|
218
|
+
punk = Synthpunks::Image.generate( '0x7a80ee32044f496a7bfef65af738fdda3a02cf02' )
|
219
|
+
punk.save( "punk2.png" )
|
220
|
+
punk.zoom(8).save( "punk2@8x.png" )
|
221
|
+
```
|
222
|
+
|
223
|
+
resulting in:
|
224
|
+
|
225
|
+
 8x
|
226
|
+

|
227
|
+
|
228
|
+
|
229
|
+
and the attributes
|
230
|
+
|
231
|
+
``` ruby
|
232
|
+
tokenID = Synthpunks.getTokenID( '0x7a80ee32044f496a7bfef65af738fdda3a02cf02' )
|
233
|
+
#=> 699372119169819039191610289391678040975564001026
|
234
|
+
Synthpunks.getAttributeNames( tokenID )
|
235
|
+
#=> ["Human 1", "Rosy Cheeks", "Shadow Beard", "Wild Hair", "VR"]
|
236
|
+
```
|
237
|
+
|
238
|
+
and compare with the "original" with the attributes:
|
239
|
+
- Human 1
|
240
|
+
- Rosy Cheeks
|
241
|
+
- Wild Hair
|
242
|
+
- VR
|
243
|
+
- Shadow Beard
|
244
|
+
|
245
|
+
|
246
|
+
[Minted Synthetic Punk for 0x7a80Ee32044F496A7bFeF65Af738FddA3a02CF02
|
247
|
+
with Token ID #699372119169819039191610289391678040975564001026 @ Open Sea](https://opensea.io/assets/ethereum/0xaf9ce4b327a3b690abea6f78eccbfefffbea9fdf/699372119169819039191610289391678040975564001026)
|
248
|
+
|
249
|
+
Or see [Synthetic Punk for 0x7a80Ee32044F496A7bFeF65Af738FddA3a02CF02](https://syntheticpunks.com/#/address/0x7a80Ee32044F496A7bFeF65Af738FddA3a02CF02)
|
250
|
+
|
251
|
+
and so on.
|
252
|
+
|
253
|
+
|
254
|
+
|
255
|
+
|
256
|
+
|
257
|
+
|
258
|
+
¹: Yes, the (on-chain) solidity contract code
|
259
|
+
got translated "by-hand" to (off-chain) ruby script.
|
260
|
+
|
261
|
+
Example - the entropy pseudo-random number generator source:
|
262
|
+
|
263
|
+
``` solidity
|
264
|
+
function randomUint(uint256 seed, uint256 offset) public view returns (uint256)
|
265
|
+
{
|
266
|
+
require(offset < 32, "Offset out of bounds");
|
267
|
+
bytes32 entropy = keccak256(abi.encodePacked(address(this), seed));
|
268
|
+
bytes32 mask = bytes32(0xff << (offset * 8));
|
269
|
+
uint256 out = uint256((entropy & mask) >> (offset * 8));
|
270
|
+
return out;
|
271
|
+
}
|
272
|
+
```
|
273
|
+
|
274
|
+
and the matching ruby version:
|
275
|
+
|
276
|
+
``` ruby
|
277
|
+
ADDRESS = '0xaf9CE4B327A3b690ABEA6F78eCCBfeFFfbEa9FDf'
|
278
|
+
|
279
|
+
## note:
|
280
|
+
## returns a random unit number between 0-255 (0-ff)
|
281
|
+
## use offset (0-31),that is, byte out of byte32 entropy buffer/hash
|
282
|
+
|
283
|
+
def randomUint( seed, offset )
|
284
|
+
# assert( offset < 32, "Offset out of bounds")
|
285
|
+
entropy = keccak256( hex_to_bin( ADDRESS ) +
|
286
|
+
hex_to_bin( uint256_to_hex(seed)) )
|
287
|
+
|
288
|
+
## note: pick a uint/byte (shorter version)
|
289
|
+
bytes = entropy.bytes.reverse
|
290
|
+
bytes[offset]
|
291
|
+
end
|
292
|
+
```
|
17
293
|
|
18
294
|
|
19
295
|
|
data/Rakefile
CHANGED
data/lib/synthpunks/version.rb
CHANGED
data/lib/synthpunks.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: synthpunks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gerald Bauer
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 1.3.6
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: backgrounds
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: etherlite
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|