synthpunks 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +3 -0
- data/Manifest.txt +10 -0
- data/README.md +28 -0
- data/Rakefile +39 -0
- data/config/spritesheet.json +1 -0
- data/config/spritesheet.png +0 -0
- data/lib/synthpunks/contract.rb +263 -0
- data/lib/synthpunks/image.rb +24 -0
- data/lib/synthpunks/version.rb +26 -0
- data/lib/synthpunks.rb +22 -0
- metadata +125 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: a0c5233aa985d25b010831dca85d14e481489665af2e69f428959d4db3a56228
|
|
4
|
+
data.tar.gz: 6f1e5ee50bb527fcdc4baa1e82673885a1883d3dc06b982d3c9f018f58045899
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 90b18b54a84d472b62c0694389ca633b948f0ac45e5f63219f08e7ce63f0994542930e29ad86c6d1a97ae1483d10995c9603aa1fc7c8212d158c139fad5d823d
|
|
7
|
+
data.tar.gz: b0e77cb3d73d74a124628ea7da1af8f63c9ed5f5e595dbdffbf1a126e4bc29971236f3a7443006b081a2fbac7efeddbecbb2be49047a6abdf61f1706670f16fd
|
data/CHANGELOG.md
ADDED
data/Manifest.txt
ADDED
data/README.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Synth(etic) Punks - (Free Unlimited) 24×24 Pixel Punks For Everyone
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
synthpunks - (free unlimited) 24×24 pixel punks for everyone - yes, you can - generate / claim your own synth(etic) punks ("off-blockchain") for your own (ethereum) account 40-hexdigit addresses e.g. 0x054f3b6eadc9631ccd60246054fdb0fcfe99b322; incl. 2x/4x/8x zoom for bigger sizes
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
* home :: [github.com/cryptopunksnotdead/cryptopunks](https://github.com/cryptopunksnotdead/cryptopunks)
|
|
8
|
+
* bugs :: [github.com/cryptopunksnotdead/cryptopunks/issues](https://github.com/cryptopunksnotdead/cryptopunks/issues)
|
|
9
|
+
* gem :: [rubygems.org/gems/synthpunks](https://rubygems.org/gems/synthpunks)
|
|
10
|
+
* rdoc :: [rubydoc.info/gems/synthpunks](http://rubydoc.info/gems/synthpunks)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
...
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## License
|
|
21
|
+
|
|
22
|
+
The scripts are dedicated to the public domain.
|
|
23
|
+
Use it as you please with no restrictions whatsoever.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
## Questions? Comments?
|
|
27
|
+
|
|
28
|
+
Post them on the [D.I.Y. Punk (Pixel) Art reddit](https://old.reddit.com/r/DIYPunkArt). Thanks.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require 'hoe'
|
|
2
|
+
require './lib/synthpunks/version.rb'
|
|
3
|
+
|
|
4
|
+
###
|
|
5
|
+
# hack/ quick fix for broken intuit_values - overwrite with dummy
|
|
6
|
+
class Hoe
|
|
7
|
+
def intuit_values( input ); end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
Hoe.spec 'synthpunks' do
|
|
13
|
+
|
|
14
|
+
self.version = Pixelart::Module::Synthpunks::VERSION
|
|
15
|
+
|
|
16
|
+
self.summary = 'synthpunks - (free unlimited) 24×24 pixel punks for everyone - yes, you can - generate / claim your own synth(etic) punks ("off-blockchain") for your own (ethereum) account 40-hexdigit addresses e.g. 0x054f3b6eadc9631ccd60246054fdb0fcfe99b322; incl. 2x/4x/8x zoom for bigger sizes'
|
|
17
|
+
self.description = summary
|
|
18
|
+
|
|
19
|
+
self.urls = { home: 'https://github.com/cryptopunksnotdead/cryptopunks' }
|
|
20
|
+
|
|
21
|
+
self.author = 'Gerald Bauer'
|
|
22
|
+
self.email = 'wwwmake@googlegroups.com'
|
|
23
|
+
|
|
24
|
+
# switch extension to .markdown for gihub formatting
|
|
25
|
+
self.readme_file = 'README.md'
|
|
26
|
+
self.history_file = 'CHANGELOG.md'
|
|
27
|
+
|
|
28
|
+
self.extra_deps = [
|
|
29
|
+
['pixelart', '>= 1.3.6'],
|
|
30
|
+
['etherlite', '>= 0.3.1'],
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
self.licenses = ['Public Domain']
|
|
34
|
+
|
|
35
|
+
self.spec_extras = {
|
|
36
|
+
required_ruby_version: '>= 2.3'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"attributes": [[0, "Human 1", "m", "l", "base"], [1, "Human 2", "m", "l", "base"], [2, "Human 3", "m", "l", "base"], [3, "Human 4", "m", "l", "base"], [4, "Zombie", "m", "l", "base"], [5, "Ape", "m", "l", "base"], [6, "Alien", "m", "l", "base"], [7, "Human Female 1", "f", "s", "base"], [8, "Human Female 2", "f", "s", "base"], [9, "Human Female 3", "f", "s", "base"], [10, "Human Female 4", "f", "s", "base"], [11, "Mole", "m", "l", "cheeks"], [12, "Rosy Cheeks", "m", "l", "cheeks"], [13, "Spots", "m", "l", "cheeks"], [14, "Clown Nose", "u", "l", "cheeks"], [15, "Mole", "f", "s", "cheeks"], [16, "Rosy Cheeks", "f", "s", "cheeks"], [17, "Spots", "f", "s", "cheeks"], [18, "Clown Nose", "f", "s", "cheeks"], [19, "Buck Teeth", "m", "l", "mouth"], [20, "Frown", "m", "l", "mouth"], [21, "Smile", "m", "l", "mouth"], [22, "Black Lipstick", "f", "u", "mouth"], [23, "Hot Lipstick", "f", "u", "mouth"], [24, "Purple Lipstick", "f", "u", "mouth"], [25, "Gold Chain", "m", "l", "neck"], [26, "Silver Chain", "m", "l", "neck"], [27, "Choker", "f", "u", "neck"], [28, "Gold Chain", "f", "u", "neck"], [29, "Silver Chain", "f", "u", "neck"], [30, "Big Beard", "m", "l", "beard"], [31, "Chinstrap", "m", "l", "beard"], [32, "Front Beard", "m", "l", "beard"], [33, "Front Beard Dark", "m", "l", "beard"], [34, "Goat", "m", "l", "beard"], [35, "Handlebars", "m", "l", "beard"], [36, "Luxurious Beard", "m", "l", "beard"], [37, "Mustache", "m", "l", "beard"], [38, "Muttonchops", "m", "l", "beard"], [39, "Normal Beard", "m", "l", "beard"], [40, "Normal Beard Black", "m", "l", "beard"], [41, "Shadow Beard", "m", "l", "beard"], [42, "Bandana", "m", "l", "headAccessory"], [43, "Beanie", "m", "l", "headAccessory"], [44, "Cap Forward", "u", "l", "headAccessory"], [45, "Cowboy Hat", "m", "l", "headAccessory"], [46, "Do-rag", "m", "l", "headAccessory"], [47, "Fedora", "m", "l", "headAccessory"], [48, "Headband", "u", "l", "headAccessory"], [49, "Hoodie", "m", "l", "headAccessory"], [50, "Knitted Cap", "u", "l", "headAccessory"], [51, "Police Cap", "m", "l", "headAccessory"], [52, "Top Hat", "m", "l", "headAccessory"], [53, "Cap", "u", "l", "headAccessory"], [54, "Crazy Hair", "m", "l", "headAccessory"], [55, "Frumpy Hair", "m", "l", "headAccessory"], [56, "Messy Hair", "u", "l", "headAccessory"], [57, "Mohawk", "u", "l", "headAccessory"], [58, "Mohawk Dark", "u", "l", "headAccessory"], [59, "Mohawk Thin", "u", "l", "headAccessory"], [60, "Peak Spike", "m", "l", "headAccessory"], [61, "Purple Hair", "m", "l", "headAccessory"], [62, "Shaved Head", "m", "l", "headAccessory"], [63, "Stringy Hair", "m", "l", "headAccessory"], [64, "Vampire Hair", "m", "l", "headAccessory"], [65, "Wild Hair", "m", "l", "headAccessory"], [66, "Clown Hair Green", "m", "l", "headAccessory"], [67, "Bandana", "f", "s", "headAccessory"], [68, "Beanie", "f", "s", "headAccessory"], [69, "Cap Forward", "f", "s", "headAccessory"], [70, "Cowboy Hat", "f", "s", "headAccessory"], [71, "Do-rag", "f", "s", "headAccessory"], [72, "Fedora", "f", "s", "headAccessory"], [73, "Headband", "f", "s", "headAccessory"], [74, "Hoodie", "f", "s", "headAccessory"], [75, "Knitted Cap", "f", "s", "headAccessory"], [76, "Pilot Helmet", "f", "s", "headAccessory"], [77, "Police Cap", "f", "s", "headAccessory"], [78, "Tassle Hat", "f", "s", "headAccessory"], [79, "Tiara", "f", "s", "headAccessory"], [80, "Top Hat", "f", "s", "headAccessory"], [81, "Cap", "f", "s", "headAccessory"], [82, "Blonde Short", "f", "s", "headAccessory"], [83, "Crazy Hair", "f", "s", "headAccessory"], [84, "Dark Hair", "f", "s", "headAccessory"], [85, "Frumpy Hair", "f", "s", "headAccessory"], [86, "Half Shaved", "f", "s", "headAccessory"], [87, "Messy Hair", "f", "s", "headAccessory"], [88, "Mohawk", "f", "s", "headAccessory"], [89, "Mohawk Dark", "f", "s", "headAccessory"], [90, "Mohawk Thin", "f", "s", "headAccessory"], [91, "Orange Side", "f", "s", "headAccessory"], [92, "Pigtails", "f", "s", "headAccessory"], [93, "Pink With Hat", "f", "s", "headAccessory"], [94, "Purple Hair", "f", "s", "headAccessory"], [95, "Red Mohawk", "f", "s", "headAccessory"], [96, "Shaved Head", "f", "s", "headAccessory"], [97, "Straight Hair", "f", "s", "headAccessory"], [98, "Straight Hair Blonde", "f", "s", "headAccessory"], [99, "Straight Hair Dark", "f", "s", "headAccessory"], [100, "Stringy Hair", "f", "s", "headAccessory"], [101, "Wild Blonde", "f", "s", "headAccessory"], [102, "Wild Hair", "f", "s", "headAccessory"], [103, "Wild White Hair", "f", "s", "headAccessory"], [104, "Blonde Bob", "f", "s", "headAccessory"], [105, "Clown Hair Green", "f", "s", "headAccessory"], [106, "Cigarette", "u", "l", "mouthAccessory"], [107, "Medical Mask", "u", "l", "mouthAccessory"], [108, "Pipe", "u", "l", "mouthAccessory"], [109, "Vape", "u", "l", "mouthAccessory"], [110, "Cigarette", "f", "s", "mouthAccessory"], [111, "Medical Mask", "f", "s", "mouthAccessory"], [112, "Pipe", "f", "s", "mouthAccessory"], [113, "Vape", "f", "s", "mouthAccessory"], [114, "Eyebrows", "u", "l", "eyes"], [115, "3D Glasses", "u", "l", "eyes"], [116, "Big Shades", "u", "l", "eyes"], [117, "Classic Shades", "u", "l", "eyes"], [118, "Eye Mask", "u", "l", "eyes"], [119, "Eye Patch", "u", "l", "eyes"], [120, "Horned Rim Glasses", "u", "l", "eyes"], [121, "Nerd Glasses", "u", "l", "eyes"], [122, "Regular Shades", "u", "l", "eyes"], [123, "Small Shades", "u", "l", "eyes"], [124, "VR", "u", "l", "eyes"], [125, "Clown Eyes Blue", "u", "l", "eyes"], [126, "Clown Eyes Green", "u", "l", "eyes"], [127, "3D Glasses", "f", "s", "eyes"], [128, "Big Shades", "f", "s", "eyes"], [129, "Classic Shades", "f", "s", "eyes"], [130, "Eye Mask", "f", "s", "eyes"], [131, "Eye Patch", "f", "s", "eyes"], [132, "Horned Rim Glasses", "f", "s", "eyes"], [133, "Nerd Glasses", "f", "s", "eyes"], [134, "Regular Shades", "f", "s", "eyes"], [135, "Small Shades", "f", "s", "eyes"], [136, "VR", "f", "s", "eyes"], [137, "Welding Goggles", "f", "s", "eyes"], [138, "Blue Eye Shadow", "f", "s", "eyes"], [139, "Green Eye Shadow", "f", "s", "eyes"], [140, "Clown Eyes Blue", "f", "s", "eyes"], [141, "Clown Eyes Green", "f", "s", "eyes"], [142, "Earring", "u", "l", "ears"], [143, "Earring", "f", "s", "ears"]], "ranges": {"base": [0, 7, 7, 11], "eyes": [114, 127, 127, 142], "cheeks": [11, 15, 15, 19], "ears": [142, 143, 143, 144], "mouth": [19, 22, 22, 25], "mouthAccessory": [106, 110, 110, 114], "neck": [25, 27, 27, 30], "beard": [30, 42, 42, 42], "headAccessory": [42, 67, 67, 106]}, "render_order": ["base", "cheeks", "mouth", "neck", "beard", "headAccessory", "mouthAccessory", "eyes", "ears"]}
|
|
Binary file
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
module Synthpunks
|
|
4
|
+
|
|
5
|
+
SPRITESHEET = Pixelart::ImageComposite.read( "#{Pixelart::Module::Synthpunks.root}/config/spritesheet.png",
|
|
6
|
+
width: 24, height: 24 )
|
|
7
|
+
|
|
8
|
+
SPRITESHEET_ATTRIBUTES, SPRITESHEET_RANGES = begin
|
|
9
|
+
## build from spritesheet.json
|
|
10
|
+
config = read_json( "#{Pixelart::Module::Synthpunks.root}/config/spritesheet.json" )
|
|
11
|
+
|
|
12
|
+
attributes = []
|
|
13
|
+
config[ 'attributes' ].each do |rec|
|
|
14
|
+
attributes << rec[1] ## get attribute name
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
ranges = []
|
|
18
|
+
config[ 'render_order'].each do |key|
|
|
19
|
+
values = config[ 'ranges'][key]
|
|
20
|
+
## note: use exclusive (three dot...) ranges !!!
|
|
21
|
+
## e.g. 42...42 - range.size == 0 that is, MUST equal 0 !!!
|
|
22
|
+
ranges << [(values[0]...values[1]),
|
|
23
|
+
(values[2]...values[3])]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
[attributes, ranges]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
=begin
|
|
30
|
+
## note: use exclusive (three dot...) ranges !!!
|
|
31
|
+
SPRITESHEET_RANGES = [
|
|
32
|
+
[(0...7), (7...11)], ## base archetypes (male / female)
|
|
33
|
+
[(11...15), (15...19)],
|
|
34
|
+
[(19...22), (22...25)],
|
|
35
|
+
[(25...27), (27...30)],
|
|
36
|
+
[(30...42), (42...42)],
|
|
37
|
+
[(42...67), (67...106)],
|
|
38
|
+
[(106...110), (110...114)],
|
|
39
|
+
[(114...127), (127...142)],
|
|
40
|
+
[(142...143), (143...144)]
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
"ranges":
|
|
44
|
+
{"base": [0, 7, 7, 11],
|
|
45
|
+
"cheeks": [11, 15, 15, 19],
|
|
46
|
+
"mouth": [19, 22, 22, 25],
|
|
47
|
+
"neck": [25, 27, 27, 30],
|
|
48
|
+
"beard": [30, 42, 42, 42],
|
|
49
|
+
"headAccessory": [42, 67, 67, 106],
|
|
50
|
+
"mouthAccessory": [106, 110, 110, 114],
|
|
51
|
+
"eyes": [114, 127, 127, 142],
|
|
52
|
+
"ears": [142, 143, 143, 144]},
|
|
53
|
+
"render_order": ["base", "cheeks", "mouth",
|
|
54
|
+
"neck", "beard", "headAccessory",
|
|
55
|
+
"mouthAccessory", "eyes", "ears"]}
|
|
56
|
+
=end
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# // Entropy 1,2-9
|
|
60
|
+
# function getAttributeCategories(uint256 id) public view returns (uint256[] memory) {
|
|
61
|
+
# uint256[4][9] memory spritesheetRanges = assets.spritesheetRanges();
|
|
62
|
+
# uint256 checks = 2 + randomUint(id, 1) % (spritesheetRanges.length - 3); // Number of bytes to check
|
|
63
|
+
# uint256[] memory attributes = new uint256[](checks);
|
|
64
|
+
# uint256 length = 0;
|
|
65
|
+
# for (uint256 i; i < checks; i++) {
|
|
66
|
+
# uint256 newAttribute = randomUint(id, 2+i) % (spritesheetRanges.length - 2) + 1; // Skip base category
|
|
67
|
+
#
|
|
68
|
+
# bool added = contains(attributes, newAttribute);
|
|
69
|
+
#
|
|
70
|
+
# if (added) {
|
|
71
|
+
# continue;
|
|
72
|
+
# }
|
|
73
|
+
#
|
|
74
|
+
# if (getGender(id) == Gender.Female) {
|
|
75
|
+
# if (!(spritesheetRanges[newAttribute][3] - spritesheetRanges[newAttribute][1] == 0)) {
|
|
76
|
+
# attributes[length] = newAttribute;
|
|
77
|
+
# length++;
|
|
78
|
+
# }
|
|
79
|
+
# } else {
|
|
80
|
+
# if (!(spritesheetRanges[newAttribute][2] - spritesheetRanges[newAttribute][0] == 0)) {
|
|
81
|
+
# attributes[length] = newAttribute;
|
|
82
|
+
# length++;
|
|
83
|
+
# }
|
|
84
|
+
# }
|
|
85
|
+
# }
|
|
86
|
+
#
|
|
87
|
+
# uint256[] memory attributesResized = new uint256[](length+1);
|
|
88
|
+
# attributesResized[0] = 0;
|
|
89
|
+
# for (uint256 i; i < length; i++) {
|
|
90
|
+
# attributesResized[i+1] = attributes[i];
|
|
91
|
+
# }
|
|
92
|
+
#
|
|
93
|
+
# return attributesResized;
|
|
94
|
+
# }
|
|
95
|
+
|
|
96
|
+
GENDER_MALE = 0
|
|
97
|
+
GENDER_FEMALE = 1
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
## Entropy 1,2-9
|
|
101
|
+
def self.getAttributeCategories( id )
|
|
102
|
+
## note: add 0 dummy for base upfront
|
|
103
|
+
attributes = [0]
|
|
104
|
+
|
|
105
|
+
gender = getGender(id) # note: (re)use 0-MALE, 1-FEMALE for range index
|
|
106
|
+
|
|
107
|
+
checks = 2 + randomUint(id, 1) % (SPRITESHEET_RANGES.size - 3) ## Number of bytes to check
|
|
108
|
+
|
|
109
|
+
checks.times do |i|
|
|
110
|
+
newAttribute = randomUint(id, 2+i) % (SPRITESHEET_RANGES.size - 2) + 1 ## Skip base category
|
|
111
|
+
|
|
112
|
+
next if attributes.include?( newAttribute )
|
|
113
|
+
attributes << newAttribute if SPRITESHEET_RANGES[newAttribute][gender].size != 0
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
attributes
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# function getAttributes(uint256 id) public view returns (uint256[] memory) {
|
|
121
|
+
# uint256[] memory attributeCategories = getAttributeCategories(id);
|
|
122
|
+
# uint256[] memory layers = new uint256[](attributeCategories.length);
|
|
123
|
+
# for (uint256 i = 0; i < attributeCategories.length; i++) {
|
|
124
|
+
# layers[i] = getAttribute(id, attributeCategories[i]);
|
|
125
|
+
# }
|
|
126
|
+
# return layers;
|
|
127
|
+
# }
|
|
128
|
+
|
|
129
|
+
def self.getAttributes( id )
|
|
130
|
+
getAttributeCategories( id ).map do |attributeCategory|
|
|
131
|
+
getAttribute( id, attributeCategory )
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
### add a extra/bonus helper for attribute metadata names
|
|
136
|
+
def self.getAttributeNames( id )
|
|
137
|
+
## note: MUST sort first for correct rendering order!!!
|
|
138
|
+
getAttributes( id ).sort.map do |attribute|
|
|
139
|
+
SPRITESHEET_ATTRIBUTES[attribute]
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
# // Entropy 10
|
|
148
|
+
# function getAttribute(uint256 id, uint256 _attributeId) public view returns (uint256) {
|
|
149
|
+
# uint256[4] memory ranges = assets.spritesheetRanges()[_attributeId];
|
|
150
|
+
# Gender gender = getGender(id);
|
|
151
|
+
# if (gender == Gender.Female) {
|
|
152
|
+
# return ranges[1] + randomUint(id, 10+_attributeId) % (ranges[3] - ranges[1]);
|
|
153
|
+
# } else {
|
|
154
|
+
# return ranges[0] + randomUint(id, 10+_attributeId) % (ranges[2] - ranges[0]);
|
|
155
|
+
# }
|
|
156
|
+
# }
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
## Entropy 10
|
|
160
|
+
def self.getAttribute( id, attributeId )
|
|
161
|
+
gender = getGender( id )
|
|
162
|
+
range = SPRITESHEET_RANGES[attributeId][ gender ]
|
|
163
|
+
range.begin + randomUint( id, 10+attributeId ) % range.size
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
# // Entropy 0
|
|
168
|
+
# function getGender(uint256 id) public view returns (Gender) {
|
|
169
|
+
# return randomUint(id, 0) % 2 == 0 ? Gender.Male : Gender.Female;
|
|
170
|
+
# }
|
|
171
|
+
|
|
172
|
+
## Entropy 0
|
|
173
|
+
def self.getGender( id )
|
|
174
|
+
randomUint( id, 0 ) % 2 == 0 ? GENDER_MALE : GENDER_FEMALE
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
## note: address is really a uint160 number
|
|
179
|
+
## 160bit (is 20 bytes is 40 hexdigits)
|
|
180
|
+
## convert to addr_to_uint
|
|
181
|
+
## is really just hex_to_int (hexstring to big integer)
|
|
182
|
+
|
|
183
|
+
## do the "on-chain" pure method in ruby here "off-chain"
|
|
184
|
+
#
|
|
185
|
+
# function getTokenID(address _address) public pure returns (uint256) {
|
|
186
|
+
# return uint256(uint160(_address));
|
|
187
|
+
# }
|
|
188
|
+
|
|
189
|
+
## note: (ethereum) address as hexstring (160 bit/20 bytes/40 hexdigits)
|
|
190
|
+
## e.g. 0x054f3b6eadc9631ccd60246054fdb0fcfe99b322
|
|
191
|
+
def self.getTokenID( address )
|
|
192
|
+
address.to_i( 16 )
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
# Non-standard Packed Mode
|
|
197
|
+
# Through abi.encodePacked(),
|
|
198
|
+
# Solidity supports a non-standard packed mode where:
|
|
199
|
+
#
|
|
200
|
+
# types shorter than 32 bytes are concatenated directly,
|
|
201
|
+
# without padding or sign extension
|
|
202
|
+
|
|
203
|
+
# // note:
|
|
204
|
+
# // address(this) is the address of the contract itself.
|
|
205
|
+
# // this keyword refers to the instance of a Contract.
|
|
206
|
+
ADDRESS = '0xaf9CE4B327A3b690ABEA6F78eCCBfeFFfbEa9FDf'
|
|
207
|
+
|
|
208
|
+
## note:
|
|
209
|
+
## returns a random unit number between 0-255 (0-ff)
|
|
210
|
+
## use offset (0-31),that is, byte out of byte32 entropy buffer/hash
|
|
211
|
+
|
|
212
|
+
def self.randomUint( seed, offset )
|
|
213
|
+
## assert( offset < 32, "Offset out of bounds")
|
|
214
|
+
## 256bit hash (= 32 bytes)
|
|
215
|
+
entropy = keccak256( hex_to_bin( ADDRESS ) +
|
|
216
|
+
hex_to_bin( uint256_to_hex(seed)) )
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
## convert binary bytes32 buffer to unint256 (big int)
|
|
220
|
+
## uint256 = bin_to_hex(entropy).to_i(16)
|
|
221
|
+
|
|
222
|
+
# mask = 0xff << (offset*8)
|
|
223
|
+
# out = (uint256 & mask) >> (offset*8)
|
|
224
|
+
# out
|
|
225
|
+
|
|
226
|
+
## note: pick a uint/byte (shorter version)
|
|
227
|
+
bytes = entropy.bytes.reverse
|
|
228
|
+
# puts "(random) 32 bytes (from 0 to 31):"
|
|
229
|
+
# pp bytes
|
|
230
|
+
bytes[offset]
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
########
|
|
235
|
+
## hex-to-bin/bin-to-hex helpers
|
|
236
|
+
def self.uint256_to_hex( num ) ## 256 bit (=32 bytes/64 hexdigits)
|
|
237
|
+
## note: adds padding that is leading zeros
|
|
238
|
+
"0x%064x" % num
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# puts 'unit256_to_hex:'
|
|
242
|
+
# pp uint256_to_hex( 699372119169819039191610289391678040975564001026 )
|
|
243
|
+
#=> "0x0000000000000000000000007a80ee32044f496a7bfef65af738fdda3a02cf02"
|
|
244
|
+
|
|
245
|
+
def self.hex_to_bin( hex )
|
|
246
|
+
raise TypeError, "Value must be an instance of String" unless hex.instance_of?( String )
|
|
247
|
+
## remove/strip hex prefix e.g. 0x/0X
|
|
248
|
+
hex = hex[2..-1] if hex.start_with?( '0x' ) ||
|
|
249
|
+
hex.start_with?( '0X' )
|
|
250
|
+
[hex].pack("H*")
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def self.bin_to_hex( bin )
|
|
254
|
+
raise TypeError, "Value must be an instance of String" unless bin.instance_of?( String )
|
|
255
|
+
bin.unpack("H*").first
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def self.keccak256( bin )
|
|
259
|
+
Digest::KeccakLite.new(256).digest( bin )
|
|
260
|
+
end
|
|
261
|
+
end # module Synthpunks
|
|
262
|
+
|
|
263
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
|
|
2
|
+
module Synthpunks
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class Image < Pixelart::Image
|
|
6
|
+
def self.generate( addr )
|
|
7
|
+
puts "==> generate SyntheticPunk for #{addr}..."
|
|
8
|
+
token_id = Synthpunks.getTokenID( addr )
|
|
9
|
+
## note: MUST sort for correct rendering order
|
|
10
|
+
puts " using (derived) token_id >#{token_id}<"
|
|
11
|
+
attributes = Synthpunks.getAttributes( token_id ).sort
|
|
12
|
+
puts " using (pseudo-random derived) attributes >#{attributes}<:"
|
|
13
|
+
attributes.each do |attribute|
|
|
14
|
+
puts " #{attribute} => #{SPRITESHEET_ATTRIBUTES[attribute]}"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
img = new( 24, 24 )
|
|
18
|
+
attributes.each do |attribute|
|
|
19
|
+
img.compose!( SPRITESHEET[ attribute ] )
|
|
20
|
+
end
|
|
21
|
+
img
|
|
22
|
+
end
|
|
23
|
+
end # class Image
|
|
24
|
+
end # module Synthpunks
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
|
|
2
|
+
module Pixelart
|
|
3
|
+
module Module
|
|
4
|
+
module Synthpunks
|
|
5
|
+
|
|
6
|
+
MAJOR = 0
|
|
7
|
+
MINOR = 0
|
|
8
|
+
PATCH = 1
|
|
9
|
+
VERSION = [MAJOR,MINOR,PATCH].join('.')
|
|
10
|
+
|
|
11
|
+
def self.version
|
|
12
|
+
VERSION
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.banner
|
|
16
|
+
"synthpunks/#{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}] in (#{root})"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.root
|
|
20
|
+
File.expand_path( File.dirname(File.dirname(File.dirname(__FILE__))) )
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end # module Synthpunks
|
|
24
|
+
end # module Module
|
|
25
|
+
end # module Pixelart
|
|
26
|
+
|
data/lib/synthpunks.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'pixelart'
|
|
2
|
+
require 'ethlite' ## needed for Digest::Keccak (otherwise no dep for now)
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
## our own code
|
|
6
|
+
require_relative 'synthpunks/version'
|
|
7
|
+
require_relative 'synthpunks/contract'
|
|
8
|
+
require_relative 'synthpunks/image'
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## add some alternate names / aliases - why? why not?
|
|
13
|
+
Synthpunk = Synthpunks
|
|
14
|
+
Syntheticpunk = Synthpunks
|
|
15
|
+
Syntheticpunks = Synthpunks
|
|
16
|
+
SynthPunk = Synthpunks
|
|
17
|
+
SynthPunks = Synthpunks
|
|
18
|
+
SyntheticPunk = Synthpunks
|
|
19
|
+
SyntheticPunks = Synthpunks
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
puts Pixelart::Module::Synthpunks.banner # say hello
|
metadata
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: synthpunks
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Gerald Bauer
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2022-12-06 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: pixelart
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 1.3.6
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 1.3.6
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: etherlite
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 0.3.1
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: 0.3.1
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rdoc
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '4.0'
|
|
48
|
+
- - "<"
|
|
49
|
+
- !ruby/object:Gem::Version
|
|
50
|
+
version: '7'
|
|
51
|
+
type: :development
|
|
52
|
+
prerelease: false
|
|
53
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
54
|
+
requirements:
|
|
55
|
+
- - ">="
|
|
56
|
+
- !ruby/object:Gem::Version
|
|
57
|
+
version: '4.0'
|
|
58
|
+
- - "<"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '7'
|
|
61
|
+
- !ruby/object:Gem::Dependency
|
|
62
|
+
name: hoe
|
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '3.23'
|
|
68
|
+
type: :development
|
|
69
|
+
prerelease: false
|
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '3.23'
|
|
75
|
+
description: synthpunks - (free unlimited) 24×24 pixel punks for everyone - yes, you
|
|
76
|
+
can - generate / claim your own synth(etic) punks ("off-blockchain") for your own
|
|
77
|
+
(ethereum) account 40-hexdigit addresses e.g. 0x054f3b6eadc9631ccd60246054fdb0fcfe99b322;
|
|
78
|
+
incl. 2x/4x/8x zoom for bigger sizes
|
|
79
|
+
email: wwwmake@googlegroups.com
|
|
80
|
+
executables: []
|
|
81
|
+
extensions: []
|
|
82
|
+
extra_rdoc_files:
|
|
83
|
+
- CHANGELOG.md
|
|
84
|
+
- Manifest.txt
|
|
85
|
+
- README.md
|
|
86
|
+
files:
|
|
87
|
+
- CHANGELOG.md
|
|
88
|
+
- Manifest.txt
|
|
89
|
+
- README.md
|
|
90
|
+
- Rakefile
|
|
91
|
+
- config/spritesheet.json
|
|
92
|
+
- config/spritesheet.png
|
|
93
|
+
- lib/synthpunks.rb
|
|
94
|
+
- lib/synthpunks/contract.rb
|
|
95
|
+
- lib/synthpunks/image.rb
|
|
96
|
+
- lib/synthpunks/version.rb
|
|
97
|
+
homepage: https://github.com/cryptopunksnotdead/cryptopunks
|
|
98
|
+
licenses:
|
|
99
|
+
- Public Domain
|
|
100
|
+
metadata: {}
|
|
101
|
+
post_install_message:
|
|
102
|
+
rdoc_options:
|
|
103
|
+
- "--main"
|
|
104
|
+
- README.md
|
|
105
|
+
require_paths:
|
|
106
|
+
- lib
|
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
108
|
+
requirements:
|
|
109
|
+
- - ">="
|
|
110
|
+
- !ruby/object:Gem::Version
|
|
111
|
+
version: '2.3'
|
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - ">="
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: '0'
|
|
117
|
+
requirements: []
|
|
118
|
+
rubygems_version: 3.3.7
|
|
119
|
+
signing_key:
|
|
120
|
+
specification_version: 4
|
|
121
|
+
summary: synthpunks - (free unlimited) 24×24 pixel punks for everyone - yes, you can
|
|
122
|
+
- generate / claim your own synth(etic) punks ("off-blockchain") for your own (ethereum)
|
|
123
|
+
account 40-hexdigit addresses e.g. 0x054f3b6eadc9631ccd60246054fdb0fcfe99b322; incl.
|
|
124
|
+
2x/4x/8x zoom for bigger sizes
|
|
125
|
+
test_files: []
|