Compare commits
786 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d24c8184a | ||
|
|
ae359322e9 | ||
|
|
33998d90f7 | ||
|
|
d9de7c1033 | ||
|
|
120f99b416 | ||
|
|
b95cb17135 | ||
|
|
507c252d36 | ||
|
|
82db6a1eb7 | ||
|
|
c60ab38882 | ||
|
|
cba5940f36 | ||
|
|
110e3de844 | ||
|
|
f60e85335d | ||
|
|
ef4cef65cd | ||
|
|
f69534683d | ||
|
|
4d849b48c2 | ||
|
|
531e16e0d7 | ||
|
|
f2575f2bec | ||
|
|
3bfc7ce2bc | ||
|
|
ef88c9c84c | ||
|
|
6580b6620f | ||
|
|
42d33e2af7 | ||
|
|
594c612318 | ||
|
|
fbfa92023d | ||
|
|
caed0aecc1 | ||
|
|
7bcc44c645 | ||
|
|
0ac390eaf0 | ||
|
|
5db4c61c28 | ||
|
|
37dc17d880 | ||
|
|
a04057f861 | ||
|
|
29e8f5852f | ||
|
|
26c76f453e | ||
|
|
9a1a1e7853 | ||
|
|
f583caa243 | ||
|
|
b399b923ab | ||
|
|
65e5d29dcd | ||
|
|
ba9782139e | ||
|
|
edef368976 | ||
|
|
77fddc9b16 | ||
|
|
69b2b8fd7b | ||
|
|
aba7d51f86 | ||
|
|
32d1b47964 | ||
|
|
cb3ff0fcb8 | ||
|
|
4d4338d3bc | ||
|
|
536410457e | ||
|
|
7469086a2a | ||
|
|
3496aa6478 | ||
|
|
60d5110905 | ||
|
|
0da64796ea | ||
|
|
41ab36d24c | ||
|
|
6bcd77fe5b | ||
|
|
4a221a43c7 | ||
|
|
37465288a7 | ||
|
|
c38b3c5255 | ||
|
|
dba7a6ca85 | ||
|
|
468a3314e3 | ||
|
|
a80b1136cd | ||
|
|
44328f1893 | ||
|
|
a0c1c27c35 | ||
|
|
f6be176df9 | ||
|
|
1a2eb3e18d | ||
|
|
44b25995e0 | ||
|
|
f1b3368529 | ||
|
|
5c64b09f51 | ||
|
|
fd08c3f2e7 | ||
|
|
01fa043b4d | ||
|
|
c10b57e3cf | ||
|
|
9a7e053f64 | ||
|
|
2e6b2d678a | ||
|
|
cf1c039545 | ||
|
|
0a496e5375 | ||
|
|
ff6835d1db | ||
|
|
b350cd4b66 | ||
|
|
f4508c4db5 | ||
|
|
2df42d49c0 | ||
|
|
fea6b8446c | ||
|
|
322051bd3a | ||
|
|
6ae1249618 | ||
|
|
8528e9e758 | ||
|
|
9bea6143a6 | ||
|
|
3b59bf6117 | ||
|
|
cecdcc3fb1 | ||
|
|
f57c2b4397 | ||
|
|
47f3651bc5 | ||
|
|
8006983643 | ||
|
|
3a7f18652f | ||
|
|
c7a4cf7121 | ||
|
|
66aa482b07 | ||
|
|
b89ed0ebb7 | ||
|
|
4cf00f33d0 | ||
|
|
a3309ab707 | ||
|
|
30449c4fe7 | ||
|
|
6b1f1a8c31 | ||
|
|
70c53b6203 | ||
|
|
3313be7f38 | ||
|
|
240fee73b4 | ||
|
|
7e85c1ddda | ||
|
|
18d724b166 | ||
|
|
f97a233798 | ||
|
|
e146f3e84e | ||
|
|
81fba57259 | ||
|
|
07515c5068 | ||
|
|
ad812f778b | ||
|
|
70727203fd | ||
|
|
b93acc884b | ||
|
|
c9e565a445 | ||
|
|
6cea8c5dcc | ||
|
|
5bd7cd93f7 | ||
|
|
da1b4f6d8f | ||
|
|
124f627d39 | ||
|
|
199dca2a3b | ||
|
|
51accfc939 | ||
|
|
1696294881 | ||
|
|
c85b758120 | ||
|
|
12fd0f7a80 | ||
|
|
78da99055d | ||
|
|
77e4770b53 | ||
|
|
799afc82f4 | ||
|
|
46fe2fc8f8 | ||
|
|
7bb9554388 | ||
|
|
d88a71ec4d | ||
|
|
2afc4ce1de | ||
|
|
64f7b24c53 | ||
|
|
88ba0af2d2 | ||
|
|
c2e8ba6856 | ||
|
|
f9a8d68641 | ||
|
|
df84100c22 | ||
|
|
b962339203 | ||
|
|
073f5a5772 | ||
|
|
b26f74a453 | ||
|
|
e9cac671f2 | ||
|
|
ecf8dcafb1 | ||
|
|
f90c99193a | ||
|
|
a826708320 | ||
|
|
43370202a7 | ||
|
|
a4d1180d26 | ||
|
|
5c42f9e09c | ||
|
|
c8362e373b | ||
|
|
06765f2f88 | ||
|
|
f26c84445e | ||
|
|
61a2d6adc2 | ||
|
|
eddd6b3dd5 | ||
|
|
fa561f8f00 | ||
|
|
c1842eefc3 | ||
|
|
320187ff89 | ||
|
|
abbf1a1d30 | ||
|
|
59269b067d | ||
|
|
21bd9fe540 | ||
|
|
f60a4c02f1 | ||
|
|
d012310ae1 | ||
|
|
f01cb91472 | ||
|
|
31e6954aff | ||
|
|
7518d938d1 | ||
|
|
083d8a03a4 | ||
|
|
16c9e90ae4 | ||
|
|
1fe2745594 | ||
|
|
18d4b740ca | ||
|
|
8392173370 | ||
|
|
26490304e4 | ||
|
|
1878ce2320 | ||
|
|
98aed04bd9 | ||
|
|
5a0585cec2 | ||
|
|
1a51263e5c | ||
|
|
4d7a976f06 | ||
|
|
fe07103ff2 | ||
|
|
aa230345fb | ||
|
|
d5c24c0f9c | ||
|
|
c1752dcb2e | ||
|
|
b00ff99e5e | ||
|
|
00e3c4a7f5 | ||
|
|
0176a67e3a | ||
|
|
4fa9f41483 | ||
|
|
64b9ef28e8 | ||
|
|
d7ce540f88 | ||
|
|
3411f55403 | ||
|
|
1bc492c39d | ||
|
|
6197a03734 | ||
|
|
6369fdfa71 | ||
|
|
48f779cd90 | ||
|
|
3ff6709a7b | ||
|
|
90bb4ccfa0 | ||
|
|
5f29690641 | ||
|
|
2665a440fa | ||
|
|
cf31913d27 | ||
|
|
103a676455 | ||
|
|
9aa5bc5840 | ||
|
|
894bc87952 | ||
|
|
cd4bd6ad91 | ||
|
|
dca7b67677 | ||
|
|
60d698f6ee | ||
|
|
f05e654f95 | ||
|
|
9a4fe6ba64 | ||
|
|
c8d472a286 | ||
|
|
c9711b9007 | ||
|
|
8afcac50c2 | ||
|
|
75c1c1373c | ||
|
|
2318628ca2 | ||
|
|
2603600716 | ||
|
|
3f0f1e006c | ||
|
|
2a4f2455d7 | ||
|
|
0ea221d9ea | ||
|
|
437674f3bc | ||
|
|
4202186c0b | ||
|
|
383e818833 | ||
|
|
74014a42b5 | ||
|
|
e875fa4aa3 | ||
|
|
186dae79d2 | ||
|
|
195d545a95 | ||
|
|
84245b9b1f | ||
|
|
461e91f53b | ||
|
|
2893feca3c | ||
|
|
4be2973761 | ||
|
|
01a30fd8af | ||
|
|
a70c1b001c | ||
|
|
30ed65d569 | ||
|
|
04a6649f2f | ||
|
|
a337021d8d | ||
|
|
4cd3863d03 | ||
|
|
c88639659d | ||
|
|
0ca17d078b | ||
|
|
f171506891 | ||
|
|
e085ecd0df | ||
|
|
b744ee1e7b | ||
|
|
8d51a6689d | ||
|
|
7bec0a4e42 | ||
|
|
71ed4d605d | ||
|
|
810a5358c0 | ||
|
|
636a9156b4 | ||
|
|
a679d047fa | ||
|
|
6d3e685284 | ||
|
|
3bfcf67b9d | ||
|
|
e16dbe663e | ||
|
|
0aca50ea34 | ||
|
|
54a6abdd2b | ||
|
|
de32db6d2d | ||
|
|
d43f9278e0 | ||
|
|
f3d8c4ee75 | ||
|
|
60fedc4e90 | ||
|
|
34d5743fa4 | ||
|
|
6810fcb5bb | ||
|
|
0bdf064614 | ||
|
|
7c4d2c2007 | ||
|
|
1bb57ca607 | ||
|
|
44860d70b9 | ||
|
|
d286a8f1e8 | ||
|
|
729ba41afd | ||
|
|
972fde32a7 | ||
|
|
c3d9e23e2a | ||
|
|
5fd7435c6d | ||
|
|
07888687a0 | ||
|
|
54c3a54597 | ||
|
|
81dba510c3 | ||
|
|
343cdabe9c | ||
|
|
fe6fbc1a0d | ||
|
|
f854492709 | ||
|
|
30af236547 | ||
|
|
d65b4aae47 | ||
|
|
245fb2cba8 | ||
|
|
f469ccc36a | ||
|
|
8ee1bd9c1b | ||
|
|
348a4327d7 | ||
|
|
faffde79f0 | ||
|
|
89a83c7b6b | ||
|
|
f36e8c1536 | ||
|
|
46ed508de8 | ||
|
|
821cdb7fd3 | ||
|
|
dd2df7ab06 | ||
|
|
71fdabe65c | ||
|
|
cce6c31461 | ||
|
|
f1aced7ee3 | ||
|
|
bd8b06bb7b | ||
|
|
9f692e57e8 | ||
|
|
32aabf81d2 | ||
|
|
a72a57ef63 | ||
|
|
4e5b4495a3 | ||
|
|
578d426d1a | ||
|
|
362b025fac | ||
|
|
84aa8d0eac | ||
|
|
6592a62f25 | ||
|
|
a59a2af50b | ||
|
|
1e68ed1fec | ||
|
|
2abd068cf8 | ||
|
|
8b746ff2bc | ||
|
|
23d9786e16 | ||
|
|
3b20402476 | ||
|
|
91bcc66c17 | ||
|
|
bcf9dad496 | ||
|
|
29195519fb | ||
|
|
17efdb38af | ||
|
|
8713e2b339 | ||
|
|
ef69356dca | ||
|
|
252b866daa | ||
|
|
5627f6eae7 | ||
|
|
45ef560324 | ||
|
|
ee80aa172e | ||
|
|
c0f106edb6 | ||
|
|
3aea38cc9b | ||
|
|
c6fe15cf13 | ||
|
|
6e948d63dc | ||
|
|
c2bd4df1d9 | ||
|
|
06e3063c3e | ||
|
|
cb52b3db20 | ||
|
|
88781ea466 | ||
|
|
2c47ad9f28 | ||
|
|
228ebd941a | ||
|
|
71f31f0910 | ||
|
|
7cf8eb11a1 | ||
|
|
6bbe45fb33 | ||
|
|
7b4e03c7fd | ||
|
|
8e0dd30ccc | ||
|
|
0661320c2a | ||
|
|
b419344297 | ||
|
|
fc636b732d | ||
|
|
5ac1cad96c | ||
|
|
33a6a3601e | ||
|
|
2dc8be161c | ||
|
|
a9dae718d3 | ||
|
|
f3ca0ce3d8 | ||
|
|
310b1a8dcd | ||
|
|
3c3d374c54 | ||
|
|
c6247ace3b | ||
|
|
1e9d56d3da | ||
|
|
a52a3bee1b | ||
|
|
e91bc74ab0 | ||
|
|
2594c4e1c2 | ||
|
|
a199b01a50 | ||
|
|
c97a0cbbb5 | ||
|
|
04fd0a3d91 | ||
|
|
3fb849f076 | ||
|
|
b3c83bac8b | ||
|
|
00c302aefc | ||
|
|
feee90e69d | ||
|
|
ca87f3601d | ||
|
|
a3abb810a9 | ||
|
|
5d19919474 | ||
|
|
5c66f16a5b | ||
|
|
4b2f9e4492 | ||
|
|
b593d1a0e9 | ||
|
|
d3b206ec3a | ||
|
|
adadacb218 | ||
|
|
b7411bfcb7 | ||
|
|
3feeb7f9fd | ||
|
|
05125c11d9 | ||
|
|
3188d3dbff | ||
|
|
04f57db7f0 | ||
|
|
066f38c471 | ||
|
|
37e8784471 | ||
|
|
889cf1890a | ||
|
|
1a4a7f36e0 | ||
|
|
3d6c24a51b | ||
|
|
781922436e | ||
|
|
277a790e36 | ||
|
|
d880925604 | ||
|
|
5466178f40 | ||
|
|
6cc4dc07bb | ||
|
|
98a35c0a37 | ||
|
|
bfeb24791d | ||
|
|
37e2614ac7 | ||
|
|
84bb9c89a4 | ||
|
|
a4aaf7f7ec | ||
|
|
b92a6e1c7a | ||
|
|
3afae28e06 | ||
|
|
63cddf3da2 | ||
|
|
499ec1cbeb | ||
|
|
fb1ffee89d | ||
|
|
f7bc7900e0 | ||
|
|
bc14210bc7 | ||
|
|
aabad94a81 | ||
|
|
eeac211c01 | ||
|
|
6e0236e9bc | ||
|
|
a9f8379545 | ||
|
|
ac3e7b42a3 | ||
|
|
2b414f8c24 | ||
|
|
1fcff83c1a | ||
|
|
eb2d6ee62b | ||
|
|
db624c24e0 | ||
|
|
95e5ff669b | ||
|
|
6a5e9e32d3 | ||
|
|
08d7621fc2 | ||
|
|
ca3a4c1971 | ||
|
|
dc0ceee50d | ||
|
|
6fadd3e745 | ||
|
|
e86228102e | ||
|
|
91f56941b4 | ||
|
|
22aadf2745 | ||
|
|
8458a623e0 | ||
|
|
e6a3699a56 | ||
|
|
ed74eff52a | ||
|
|
a7e5b28b00 | ||
|
|
08576e1717 | ||
|
|
80376ab81e | ||
|
|
32efdd952b | ||
|
|
f6b79ba5d2 | ||
|
|
1a995d7087 | ||
|
|
20fc3c58d7 | ||
|
|
7329ca6a48 | ||
|
|
0c9c0c9791 | ||
|
|
39a7e32143 | ||
|
|
87c27b9d03 | ||
|
|
4d07a1ede5 | ||
|
|
55fbd0f2ed | ||
|
|
6dca198995 | ||
|
|
5f80aab430 | ||
|
|
c30fbb8d1c | ||
|
|
be1014452e | ||
|
|
0036da93b1 | ||
|
|
c984adef0e | ||
|
|
b2d8667963 | ||
|
|
466be38e42 | ||
|
|
adcb8e085d | ||
|
|
e9a3770a39 | ||
|
|
d9a1387f2a | ||
|
|
a6710ad435 | ||
|
|
3d3a63a10d | ||
|
|
0daf5b7dae | ||
|
|
ef4b3dd4b2 | ||
|
|
0f2a13463a | ||
|
|
0a305c019d | ||
|
|
de8c7f20ca | ||
|
|
23d7fdff3a | ||
|
|
21b9965f57 | ||
|
|
023c8bae64 | ||
|
|
0d771eb9fa | ||
|
|
62f775737d | ||
|
|
12aacf24b3 | ||
|
|
b450dcec79 | ||
|
|
e909e1db99 | ||
|
|
92c9c9bcc8 | ||
|
|
5588256072 | ||
|
|
8cf04ddb98 | ||
|
|
ecca5a4ee9 | ||
|
|
0a5ebe60df | ||
|
|
12982f338a | ||
|
|
47c7923723 | ||
|
|
a35a505b92 | ||
|
|
9487de51b4 | ||
|
|
65fbd732f2 | ||
|
|
e6fe600ed0 | ||
|
|
28694d3413 | ||
|
|
d5d065dfb0 | ||
|
|
cb96c7c237 | ||
|
|
cca4cf4922 | ||
|
|
a97a150e98 | ||
|
|
5167710bb2 | ||
|
|
af878df5ab | ||
|
|
e4e895a881 | ||
|
|
95ab343fcc | ||
|
|
4f36e0587c | ||
|
|
f50a0765fd | ||
|
|
7739050346 | ||
|
|
07f2e14fcb | ||
|
|
f7d20a4760 | ||
|
|
65e6d7b16c | ||
|
|
dfac0716a5 | ||
|
|
43850ecd3c | ||
|
|
125947d179 | ||
|
|
4643775745 | ||
|
|
dd6b7765cf | ||
|
|
b7597485ec | ||
|
|
47b06b057c | ||
|
|
c06a836687 | ||
|
|
7b3acfd68f | ||
|
|
972cbb632b | ||
|
|
b37b0e2d7a | ||
|
|
48928db10f | ||
|
|
5fdf6c6547 | ||
|
|
8be15f2926 | ||
|
|
baf7b80f64 | ||
|
|
b501fd6f01 | ||
|
|
78cbd16cb6 | ||
|
|
94331b07b0 | ||
|
|
f9ff3b1ac2 | ||
|
|
f59588ac2f | ||
|
|
b285a19186 | ||
|
|
5e58a2dc4a | ||
|
|
b7b6e452d8 | ||
|
|
78dcfe1816 | ||
|
|
f67cafeb02 | ||
|
|
9ca5d8b8cb | ||
|
|
b80fe7d1d9 | ||
|
|
bf2f8024e3 | ||
|
|
ba6acccffb | ||
|
|
e829885311 | ||
|
|
84e92ed975 | ||
|
|
05adc1e2fe | ||
|
|
971b354e1c | ||
|
|
fa1e86c3b0 | ||
|
|
621f56a0c6 | ||
|
|
5470804e1c | ||
|
|
17e683debe | ||
|
|
29090843aa | ||
|
|
d7c677bb05 | ||
|
|
16ebaa61e5 | ||
|
|
52581a6d56 | ||
|
|
43803ec21d | ||
|
|
79313cbf6c | ||
|
|
36c3252b0c | ||
|
|
168215d23e | ||
|
|
75bef955ab | ||
|
|
68a65f9645 | ||
|
|
13b724a34f | ||
|
|
073ec22b34 | ||
|
|
5d6f7b0629 | ||
|
|
75fc12f867 | ||
|
|
e21ccedc15 | ||
|
|
68187531a5 | ||
|
|
3dba37fd06 | ||
|
|
17234ddfd2 | ||
|
|
536cf452bb | ||
|
|
efe56e80ac | ||
|
|
f09e930857 | ||
|
|
2e5650a5b8 | ||
|
|
7c3763c012 | ||
|
|
e67cf5d75b | ||
|
|
5a99b66f18 | ||
|
|
df9286a455 | ||
|
|
f4dd5ba694 | ||
|
|
8c49c4ed14 | ||
|
|
1f805543e8 | ||
|
|
a674301faf | ||
|
|
12e92840a4 | ||
|
|
ac7f03a695 | ||
|
|
20036c3739 | ||
|
|
8826ddb47b | ||
|
|
c074930aad | ||
|
|
f2a1d50603 | ||
|
|
3dc63c83bb | ||
|
|
0f62fe1356 | ||
|
|
10562ca54f | ||
|
|
f237a44980 | ||
|
|
ec99ab0759 | ||
|
|
7c48005647 | ||
|
|
3b61ceebde | ||
|
|
0d43e0b4c8 | ||
|
|
6cf990bc22 | ||
|
|
9875778ad9 | ||
|
|
6ef014fa7c | ||
|
|
06fbe09823 | ||
|
|
80d3ffb622 | ||
|
|
52dbe8b14e | ||
|
|
c4207c73b0 | ||
|
|
45a74798c7 | ||
|
|
5e7c3a5ae9 | ||
|
|
4975db4f58 | ||
|
|
aa96f0bbc1 | ||
|
|
6e5c6fc84c | ||
|
|
8c1119dba1 | ||
|
|
68f4bead0e | ||
|
|
bd78a71387 | ||
|
|
e8f34ea272 | ||
|
|
2a4e92449b | ||
|
|
57b61614c4 | ||
|
|
45f48799f0 | ||
|
|
42c934ec1b | ||
|
|
35822a480e | ||
|
|
56edbb6cd7 | ||
|
|
d0962fda19 | ||
|
|
d17c4a6e0c | ||
|
|
4080f4af20 | ||
|
|
9e74e1a278 | ||
|
|
a8e0c1474c | ||
|
|
d1fcf8bfe2 | ||
|
|
5a895f42f8 | ||
|
|
8a44757417 | ||
|
|
ad1d77b200 | ||
|
|
c31b9490ed | ||
|
|
4aeeb3e1cc | ||
|
|
23dac4c6ed | ||
|
|
4c4bfb568a | ||
|
|
8595e3cd0f | ||
|
|
01a5b156bb | ||
|
|
dd19a5e213 | ||
|
|
9e2b54ef16 | ||
|
|
51fe424473 | ||
|
|
5dbfaf3518 | ||
|
|
4e18efb75c | ||
|
|
55a89753bb | ||
|
|
8105d9761b | ||
|
|
55cef34142 | ||
|
|
bf42264033 | ||
|
|
0035a62d7a | ||
|
|
d34bded3cc | ||
|
|
e156cf77c2 | ||
|
|
4d662a4a0d | ||
|
|
67e907da2c | ||
|
|
7771f15e5a | ||
|
|
aff0c87366 | ||
|
|
77bc53d9a7 | ||
|
|
2965d8decf | ||
|
|
528ad6ef4e | ||
|
|
bd2145dc09 | ||
|
|
67dd25e604 | ||
|
|
25379748e6 | ||
|
|
9e6c0df6a2 | ||
|
|
07cbe28701 | ||
|
|
d212947d07 | ||
|
|
bdffdfe200 | ||
|
|
a8922efae1 | ||
|
|
4ee9913035 | ||
|
|
04df22ed56 | ||
|
|
abc8ef3cf6 | ||
|
|
c2a8aee15e | ||
|
|
292ce60b81 | ||
|
|
3a64ab44dd | ||
|
|
d597b461a7 | ||
|
|
a7a42f8d2c | ||
|
|
9f7aa7955f | ||
|
|
2ffaf28a12 | ||
|
|
bfaeb19d14 | ||
|
|
aed9fa6785 | ||
|
|
ac1ad33bfd | ||
|
|
e0e366c52a | ||
|
|
e6f7df17a9 | ||
|
|
c610ddb45a | ||
|
|
8c7d09a5c1 | ||
|
|
8d5968a3c0 | ||
|
|
936e0d76a6 | ||
|
|
b5389fed3d | ||
|
|
5f1ed4e08e | ||
|
|
37bb2976b8 | ||
|
|
17e6330798 | ||
|
|
a4ecc5a125 | ||
|
|
9bfe26d81d | ||
|
|
9756917fc7 | ||
|
|
83e29909c3 | ||
|
|
ae22bd7be9 | ||
|
|
3295483a24 | ||
|
|
b21a17ea70 | ||
|
|
d1419045e3 | ||
|
|
c136ba809f | ||
|
|
bf87f4a3af | ||
|
|
7ddd0e0920 | ||
|
|
498d14cde1 | ||
|
|
91867d53aa | ||
|
|
dbc9ae303d | ||
|
|
0c9d717fa8 | ||
|
|
a6256d2cdb | ||
|
|
9944eb3746 | ||
|
|
d5fd1f99a3 | ||
|
|
71dc72daa8 | ||
|
|
7dd398ebd9 | ||
|
|
28990a27f3 | ||
|
|
7993440e53 | ||
|
|
05c26c108c | ||
|
|
940e6fcb75 | ||
|
|
31e885a075 | ||
|
|
d37deffb73 | ||
|
|
9285fe54e6 | ||
|
|
7095e03162 | ||
|
|
ce09c9e4dd | ||
|
|
17d616e0ba | ||
|
|
af6f040429 | ||
|
|
eeb65e4639 | ||
|
|
aa544bf2c9 | ||
|
|
967e93789e | ||
|
|
ec5c6a1c23 | ||
|
|
79a6bfcbf7 | ||
|
|
a08c5460c3 | ||
|
|
1259d8e53e | ||
|
|
bea3aca4a9 | ||
|
|
04e89d83e0 | ||
|
|
83798c959e | ||
|
|
23a9fe78bf | ||
|
|
108160740d | ||
|
|
fdc66d3769 | ||
|
|
bad1176c60 | ||
|
|
6d8511c35c | ||
|
|
ec48e81fe0 | ||
|
|
bf7dda0666 | ||
|
|
ccb1d762c4 | ||
|
|
6ecf6c20af | ||
|
|
e575c6025b | ||
|
|
1f4120f30d | ||
|
|
ea6ff9d6a9 | ||
|
|
ec7387f1c5 | ||
|
|
04e716a917 | ||
|
|
3efea8c2bf | ||
|
|
a38c5b4360 | ||
|
|
fd7342f5b3 | ||
|
|
7ca5de85d6 | ||
|
|
6d313e194e | ||
|
|
e9ed84c868 | ||
|
|
010726618e | ||
|
|
df0c87f5e7 | ||
|
|
55079c25a9 | ||
|
|
1242e73d34 | ||
|
|
1be081c10a | ||
|
|
4a4ef3d3bf | ||
|
|
ba02e7719f | ||
|
|
406424c0a3 | ||
|
|
633a1a7a19 | ||
|
|
3680cc294d | ||
|
|
6d51e94a88 | ||
|
|
9bd085195f | ||
|
|
0826bbcfa3 | ||
|
|
4c643f012b | ||
|
|
1a3d35662c | ||
|
|
064aa99b3d | ||
|
|
eec2ff5c1f | ||
|
|
7527f159ad | ||
|
|
f06a12535f | ||
|
|
7ad7acfb54 | ||
|
|
76092003f7 | ||
|
|
817783e797 | ||
|
|
14a7493cc8 | ||
|
|
f15ba8fcda | ||
|
|
4e04ba2413 | ||
|
|
5b9a4aaceb | ||
|
|
af9b07aa20 | ||
|
|
1ca9e568c4 | ||
|
|
c21d0b6e6b | ||
|
|
56716cb258 | ||
|
|
04b585021f | ||
|
|
2a44bd6c75 | ||
|
|
7d54825ba1 | ||
|
|
0900615ae4 | ||
|
|
6837dd3cbd | ||
|
|
7888aebcd7 | ||
|
|
a6cf53520b | ||
|
|
0b66144c76 | ||
|
|
9f931d1f19 | ||
|
|
db8da587ab | ||
|
|
96b5e10d75 | ||
|
|
e15fd3174e | ||
|
|
b0bbb981a0 | ||
|
|
d7dc8bdd60 | ||
|
|
21b7ccabd3 | ||
|
|
778d5a1574 | ||
|
|
e7c515aabc | ||
|
|
a77fcf848f | ||
|
|
1c91e59f71 | ||
|
|
19e54ac348 | ||
|
|
b7b8190edc | ||
|
|
ead549e9f1 | ||
|
|
55938b0eaa | ||
|
|
e614cc68ac | ||
|
|
3a94fb72f9 | ||
|
|
1772e776a7 | ||
|
|
e6a5d1f4b6 | ||
|
|
5abfed2b77 | ||
|
|
82c4dea962 | ||
|
|
544046a392 | ||
|
|
541cd4298b | ||
|
|
67c6af125e | ||
|
|
a11449493b | ||
|
|
42031746fa | ||
|
|
ca2d5a193c | ||
|
|
3602219bb6 | ||
|
|
710e546eee | ||
|
|
882191996c | ||
|
|
a9630e436b | ||
|
|
b524655db1 | ||
|
|
7dead05108 | ||
|
|
ed355829cd | ||
|
|
c37496cf00 | ||
|
|
1be8ac5df5 | ||
|
|
bc8362f4f0 | ||
|
|
e84264b24d | ||
|
|
970b19257f | ||
|
|
421b7da0ba | ||
|
|
665ab42870 | ||
|
|
69c25d4b05 | ||
|
|
37337ccff6 | ||
|
|
583cb2fb2a | ||
|
|
d5a3781d01 | ||
|
|
4d6045a01c | ||
|
|
acf5d7dc98 | ||
|
|
358dd69753 | ||
|
|
57be569adb | ||
|
|
948685edf2 | ||
|
|
548d7bbe31 | ||
|
|
ceb20422b7 | ||
|
|
8e41b6cffd | ||
|
|
a4378925ac | ||
|
|
8f0c937023 | ||
|
|
ad6873bfb3 | ||
|
|
2a5ebeb6fb | ||
|
|
bc4b0acefd | ||
|
|
3824857b04 | ||
|
|
d945172ce9 | ||
|
|
8f7214a921 | ||
|
|
4510809a1b | ||
|
|
c7ca8dc02d | ||
|
|
589ade76d5 | ||
|
|
36d634c47e | ||
|
|
dabd686b1f | ||
|
|
5f8ad2b71b |
@@ -1,66 +0,0 @@
|
||||
# Javascript Node CircleCI 2.0 configuration file
|
||||
#
|
||||
# Check https://circleci.com/docs/2.0/language-javascript/ for more details
|
||||
#
|
||||
version: 2
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
# specify the version you desire here
|
||||
- image: circleci/node:11.14.0
|
||||
|
||||
working_directory: ~/semaphore/semaphorejs
|
||||
|
||||
steps:
|
||||
- checkout:
|
||||
path: ~/semaphore
|
||||
|
||||
# restore or install npm packages
|
||||
- restore_cache:
|
||||
name: restore-npm-cache
|
||||
keys:
|
||||
- v1.10-dependencies-{{ checksum "package-lock.json" }}
|
||||
|
||||
- run: npm install
|
||||
|
||||
- save_cache:
|
||||
paths:
|
||||
- node_modules
|
||||
key: v1.10-dependencies-{{ checksum "package-lock.json" }}
|
||||
|
||||
# checksum the snarks definitions
|
||||
- run:
|
||||
name: checksum-snarks
|
||||
command: cd scripts && ./checksum_snarks.sh
|
||||
|
||||
# Download and cache dependencies
|
||||
- restore_cache:
|
||||
name: restore-snark-cache
|
||||
keys:
|
||||
- v1.13-dependencies-{{ checksum "build/.snark_checksum" }}
|
||||
|
||||
# build snarks
|
||||
- run:
|
||||
name: end-to-end
|
||||
command: cd scripts && ./build_snarks.sh
|
||||
no_output_timeout: 600m
|
||||
|
||||
# cache generated snark circuit and keys
|
||||
- save_cache:
|
||||
key: v1.13-dependencies-{{ checksum "build/.snark_checksum" }}
|
||||
paths:
|
||||
- build/circuit.json
|
||||
- build/proving_key.bin
|
||||
- build/proving_key.json
|
||||
- build/verification_key.json
|
||||
- build/verifier.sol
|
||||
|
||||
# build snarks
|
||||
- run:
|
||||
name: integration_tests
|
||||
command: cd scripts && ./integration_tests.sh
|
||||
no_output_timeout: 600m
|
||||
|
||||
- store_artifacts:
|
||||
path: ~/semaphore/semaphorejs/build
|
||||
|
||||
3
.commitlintrc.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": ["@commitlint/config-conventional"]
|
||||
}
|
||||
13
.editorconfig
Normal file
@@ -0,0 +1,13 @@
|
||||
#root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
max_line_length = 120
|
||||
indent_size = 4
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
8
.env.example
Normal file
@@ -0,0 +1,8 @@
|
||||
DEFAULT_NETWORK=hardhat
|
||||
TREE_DEPTH=20
|
||||
ALL_SNARK_ARTIFACTS=true
|
||||
REPORT_GAS=false
|
||||
BACKEND_PRIVATE_KEY=
|
||||
INFURA_API_KEY=
|
||||
COINMARKETCAP_API_KEY=
|
||||
ETHERSCAN_API_KEY=
|
||||
37
.eslintignore
Normal file
@@ -0,0 +1,37 @@
|
||||
# dependencies
|
||||
node_modules
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
.yarn
|
||||
|
||||
# testing
|
||||
coverage
|
||||
coverage.json
|
||||
|
||||
# hardhat
|
||||
artifacts
|
||||
cache
|
||||
typechain-types
|
||||
|
||||
# types
|
||||
types
|
||||
|
||||
# circuits
|
||||
circuits
|
||||
|
||||
# production
|
||||
dist
|
||||
build
|
||||
docs
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# packages
|
||||
cli-template-*
|
||||
24
.eslintrc.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"root": true,
|
||||
"env": {
|
||||
"es6": true
|
||||
},
|
||||
"extends": ["airbnb-base", "airbnb-typescript/base", "plugin:jest/recommended", "plugin:jest/style", "prettier"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "module",
|
||||
"project": ["./tsconfig.json", "./packages/**/tsconfig.json"]
|
||||
},
|
||||
"plugins": ["@typescript-eslint", "jest"],
|
||||
"rules": {
|
||||
"no-underscore-dangle": "off",
|
||||
"import/no-extraneous-dependencies": "off",
|
||||
"no-bitwise": "off",
|
||||
"no-await-in-loop": "off",
|
||||
"no-restricted-syntax": "off",
|
||||
"no-console": ["warn", { "allow": ["info", "warn", "error"] }],
|
||||
"@typescript-eslint/lines-between-class-members": "off",
|
||||
"no-param-reassign": "off"
|
||||
}
|
||||
}
|
||||
18
.github/ISSUE_TEMPLATE/----network.md
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
name: "\U0001F578️ Network"
|
||||
about: Propose a new network in which to deploy Semaphore contracts
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Provide data related to the network**
|
||||
|
||||
* Name
|
||||
* Chain id
|
||||
* URL
|
||||
|
||||
**Why are you using this network?**
|
||||
|
||||
Describe the reasons why you think it is important for Semaphore to be deployed on this network.
|
||||
23
.github/ISSUE_TEMPLATE/----project.md
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
name: " \U0001F4A0 Project"
|
||||
about: If you are using Semaphore we can help you share your project
|
||||
title: ''
|
||||
labels: "documentation \U0001F4D6"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe your project**
|
||||
A brief description of your project. In what way have you used Semaphore?
|
||||
|
||||
**Other info**
|
||||
|
||||
- Name
|
||||
- Tagline
|
||||
- Icon
|
||||
|
||||
**Links**
|
||||
|
||||
- Website
|
||||
- Github
|
||||
- Socials
|
||||
34
.github/ISSUE_TEMPLATE/---bug.md
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
---
|
||||
name: "\U0001F41E Bug"
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: "bug \U0001F41B"
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Technologies (please complete the following information):**
|
||||
|
||||
- Node.js version
|
||||
- NPM version
|
||||
- Solidity version
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
20
.github/ISSUE_TEMPLATE/---feature.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: "\U0001F680 Feature"
|
||||
about: Suggest an idea for Semaphore
|
||||
title: ''
|
||||
labels: 'feature :rocket:'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
14
.github/ISSUE_TEMPLATE/---package.md
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
name: "\U0001F4E6 Package"
|
||||
about: Propose a new Semaphore package
|
||||
title: ''
|
||||
labels: 'feature :rocket:'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the package you'd like**
|
||||
A clear and concise description of the type of package you have in mind.
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the package here.
|
||||
24
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<!-- Please refer to our contributing documentation for any questions on submitting a pull request -->
|
||||
<!--- Provide a general summary of your changes in the Title above -->
|
||||
|
||||
## Description
|
||||
|
||||
<!--- Describe your changes in detail -->
|
||||
|
||||
## Related Issue
|
||||
|
||||
<!--- This project accepts pull requests related to open issues -->
|
||||
<!--- If suggesting a new feature or change, please discuss it in an issue first -->
|
||||
<!--- If fixing a bug, there should be an issue describing it with steps to reproduce -->
|
||||
<!--- Please link to the issue here: -->
|
||||
|
||||
## Does this introduce a breaking change?
|
||||
|
||||
- [ ] Yes
|
||||
- [ ] No
|
||||
|
||||
<!-- If this introduces a breaking change, please describe the impact and migration path for existing applications below. -->
|
||||
|
||||
## Other information
|
||||
|
||||
<!-- Any other information that is important to this PR such as screenshots of how the component looks before and after the change. -->
|
||||
14
.github/workflows/auto-assign.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
name: auto-assign
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
run:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: wow-actions/auto-assign@v3
|
||||
with:
|
||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
reviewers: org/core-devs
|
||||
46
.github/workflows/docs.yml
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
name: docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
gh-pages:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn config get cacheFolder)"
|
||||
|
||||
- name: Restore yarn cache
|
||||
uses: actions/cache@v3
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Generate doc website
|
||||
run: yarn docs
|
||||
|
||||
- name: Publish on Github Pages
|
||||
uses: crazy-max/ghaction-github-pages@v2.5.0
|
||||
with:
|
||||
build_dir: docs
|
||||
jekyll: false
|
||||
fqdn: js.semaphore.appliedzkp.org
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
107
.github/workflows/production.yml
vendored
Normal file
@@ -0,0 +1,107 @@
|
||||
name: production
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
TREE_DEPTH: 20
|
||||
ALL_SNARK_ARTIFACTS: false
|
||||
|
||||
jobs:
|
||||
style:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn config get cacheFolder)"
|
||||
|
||||
- name: Restore yarn cache
|
||||
uses: actions/cache@v3
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Compile contracts
|
||||
run: yarn compile:contracts
|
||||
|
||||
- name: Build libraries
|
||||
run: yarn build:libraries
|
||||
|
||||
- name: Run Prettier
|
||||
run: yarn prettier
|
||||
|
||||
- name: Run Eslint
|
||||
run: yarn lint
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
type:
|
||||
- libraries
|
||||
- contracts
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn config get cacheFolder)"
|
||||
|
||||
- name: Restore yarn cache
|
||||
uses: actions/cache@v3
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Build libraries
|
||||
run: yarn build:libraries
|
||||
|
||||
- name: Test contracts and libraries
|
||||
run: yarn test:${{ matrix.type }}
|
||||
|
||||
- name: Coveralls
|
||||
uses: coverallsapp/github-action@master
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
flag-name: run-${{ matrix.type }}
|
||||
path-to-lcov: ./coverage/${{ matrix.type }}/lcov.info
|
||||
parallel: true
|
||||
|
||||
coverage:
|
||||
runs-on: ubuntu-latest
|
||||
needs: test
|
||||
|
||||
steps:
|
||||
- name: Coveralls Finished
|
||||
uses: coverallsapp/github-action@master
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
parallel-finished: true
|
||||
81
.github/workflows/pull-requests.yml
vendored
Normal file
@@ -0,0 +1,81 @@
|
||||
name: pull-requests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
TREE_DEPTH: 20
|
||||
ALL_SNARK_ARTIFACTS: false
|
||||
|
||||
jobs:
|
||||
style:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn config get cacheFolder)"
|
||||
|
||||
- name: Restore yarn cache
|
||||
uses: actions/cache@v3
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Compile contracts
|
||||
run: yarn compile:contracts
|
||||
|
||||
- name: Build libraries
|
||||
run: yarn build:libraries
|
||||
|
||||
- name: Run Prettier
|
||||
run: yarn prettier
|
||||
|
||||
- name: Run Eslint
|
||||
run: yarn lint
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn config get cacheFolder)"
|
||||
|
||||
- name: Restore yarn cache
|
||||
uses: actions/cache@v3
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- name: Build libraries
|
||||
run: yarn build:libraries
|
||||
|
||||
- name: Test contracts and libraries
|
||||
run: yarn test
|
||||
43
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
name: release
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
- name: Get yarn cache directory path
|
||||
id: yarn-cache-dir-path
|
||||
run: echo "::set-output name=dir::$(yarn config get cacheFolder)"
|
||||
|
||||
- name: Restore yarn cache
|
||||
uses: actions/cache@v3
|
||||
id: yarn-cache
|
||||
with:
|
||||
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
|
||||
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-yarn-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn
|
||||
|
||||
- run: yarn version:release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
91
.gitignore
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# IDE
|
||||
.vscode
|
||||
.idea
|
||||
|
||||
# Testing
|
||||
coverage
|
||||
coverage.json
|
||||
*.lcov
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
.DS_Store
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# Production
|
||||
build
|
||||
dist
|
||||
docs/*
|
||||
!docs/CNAME
|
||||
!docs/index.html
|
||||
|
||||
# Hardhat
|
||||
artifacts
|
||||
cache
|
||||
typechain-types
|
||||
packages/contracts/deployed-contracts/undefined.json
|
||||
packages/contracts/deployed-contracts/hardhat.json
|
||||
packages/contracts/deployed-contracts/localhost.json
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v3
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
|
||||
# Other
|
||||
snark-artifacts
|
||||
4
.husky/commit-msg
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npx --no-install commitlint --edit $1
|
||||
3
.lintstagedrc.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"**/*.{js,ts}": ["prettier --write", "eslint --fix"]
|
||||
}
|
||||
47
.prettierignore
Normal file
@@ -0,0 +1,47 @@
|
||||
# dependencies
|
||||
node_modules
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
.yarn
|
||||
|
||||
# testing
|
||||
coverage
|
||||
coverage.json
|
||||
|
||||
# hardhat
|
||||
artifacts
|
||||
cache
|
||||
typechain-types
|
||||
packages/contracts/deployed-contracts/undefined.json
|
||||
packages/contracts/deployed-contracts/hardhat.json
|
||||
packages/contracts/deployed-contracts/localhost.json
|
||||
|
||||
# circuits
|
||||
circuits
|
||||
|
||||
# contracts
|
||||
Verifier*.sol
|
||||
|
||||
# production
|
||||
dist
|
||||
build
|
||||
docs
|
||||
|
||||
# github
|
||||
.github/ISSUE_TEMPLATE
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# other
|
||||
snark-artifacts
|
||||
|
||||
# Next.js
|
||||
.next/
|
||||
public
|
||||
5
.prettierrc.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"semi": false,
|
||||
"arrowParens": "always",
|
||||
"trailingComma": "none"
|
||||
}
|
||||
23
.yarn/patches/changelogithub-npm-0.12.7-72f348805d.patch
Normal file
@@ -0,0 +1,23 @@
|
||||
diff --git a/dist/shared/changelogithub.821fab93.mjs b/dist/shared/changelogithub.821fab93.mjs
|
||||
index 5fc2100867613c20f7827eac8715a5fc28bdc39e..97bd8dff878b81c63d2220e496904f6f3933589a 100644
|
||||
--- a/dist/shared/changelogithub.821fab93.mjs
|
||||
+++ b/dist/shared/changelogithub.821fab93.mjs
|
||||
@@ -181,7 +181,7 @@ function formatLine(commit, options) {
|
||||
function formatTitle(name, options) {
|
||||
if (!options.emoji)
|
||||
name = name.replace(emojisRE, "");
|
||||
- return `### ${name.trim()}`;
|
||||
+ return `## ${name.trim()}`;
|
||||
}
|
||||
function formatSection(commits, sectionName, options) {
|
||||
if (!commits.length)
|
||||
@@ -198,7 +198,8 @@ function formatSection(commits, sectionName, options) {
|
||||
Object.keys(scopes).sort().forEach((scope) => {
|
||||
let padding = "";
|
||||
let prefix = "";
|
||||
- const scopeText = `**${options.scopeMap[scope] || scope}**`;
|
||||
+ const url = `https://github.com/${options.github}/tree/main/packages/${scope}`
|
||||
+ const scopeText = `[**@${options.github.split("/")[0]}/${options.scopeMap[scope] || scope}**](${url})`
|
||||
if (scope && useScopeGroup) {
|
||||
lines.push(`- ${scopeText}:`);
|
||||
padding = " ";
|
||||
1
.yarn/plugins/@yarnpkg/plugin-version.cjs.REMOVED.git-id
vendored
Normal file
@@ -0,0 +1 @@
|
||||
87de4f440a77841135f97a187e09140c6d4e6ae2
|
||||
28
.yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
vendored
Normal file
1
.yarn/releases/yarn-3.2.1.cjs.REMOVED.git-id
vendored
Normal file
@@ -0,0 +1 @@
|
||||
b3cadff6efb37a12712d12c2553ec703dbcaa4dd
|
||||
11
.yarnrc.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
checksumBehavior: update
|
||||
|
||||
nodeLinker: node-modules
|
||||
|
||||
plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
|
||||
spec: "@yarnpkg/plugin-workspace-tools"
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-version.cjs
|
||||
spec: "@yarnpkg/plugin-version"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.2.1.cjs
|
||||
127
CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,127 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
- Demonstrating empathy and kindness toward other people
|
||||
- Being respectful of differing opinions, viewpoints, and experiences
|
||||
- Giving and gracefully accepting constructive feedback
|
||||
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
- Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
- The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
111
CONTRIBUTING.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# Contributing
|
||||
|
||||
:tada: Thank you for being interested in contributing to the Semaphore project! :tada:
|
||||
|
||||
Feel welcome and read the following sections in order to know how to ask questions and how to work on something.
|
||||
|
||||
All members of our community are expected to follow our [Code of Conduct](/CODE_OF_CONDUCT.md). Please make sure you are welcoming and friendly in all of our spaces.
|
||||
|
||||
We're really glad you're reading this, because we need volunteer developers to help this project come to fruition. 👏
|
||||
|
||||
## Issues
|
||||
|
||||
The best way to contribute to our projects is by opening a [new issue](https://github.com/semaphore-protocol/semaphore/issues/new/choose) or tackling one of the issues listed [here](https://github.com/semaphore-protocol/semaphore/contribute).
|
||||
|
||||
## Pull Requests
|
||||
|
||||
Pull requests are great if you want to add a feature or fix a bug. Here's a quick guide:
|
||||
|
||||
1. Fork the repo.
|
||||
|
||||
2. Run the tests. We only take pull requests with passing tests.
|
||||
|
||||
3. Add a test for your change. Only refactoring and documentation changes require no new tests.
|
||||
|
||||
4. Make sure to check out the [Style Guide](/CONTRIBUTING#style-guide) and ensure that your code complies with the rules.
|
||||
|
||||
5. Make the test pass.
|
||||
|
||||
6. Commit your changes.
|
||||
|
||||
7. Push to your fork and submit a pull request on our `dev` branch. Please provide us with some explanation of why you made the changes you made. For new features make sure to explain a standard use case to us.
|
||||
|
||||
## CI (Github Actions) Tests
|
||||
|
||||
We use GitHub Actions to test each PR before it is merged.
|
||||
|
||||
When you submit your PR (or later change that code), a CI build will automatically be kicked off. A note will be added to the PR, and will indicate the current status of the build.
|
||||
|
||||
## Style Guide
|
||||
|
||||
### Code rules
|
||||
|
||||
We always use ESLint and Prettier. To check that your code follows the rules, simply run the npm script `yarn lint`.
|
||||
|
||||
### Commits rules
|
||||
|
||||
For commits it is recommended to use [Conventional Commits](https://www.conventionalcommits.org).
|
||||
|
||||
Don't worry if it looks complicated, in our repositories, after `git add`, you can usually run the npm script `yarn commit` to make many of these steps interactive.
|
||||
|
||||
Each commit message consists of a **header**, a **body** and a **footer**. The **header** has a special format that includes a **type**, a **scope** and a **subject**:
|
||||
|
||||
<type>(<scope>): <subject>
|
||||
<BLANK LINE>
|
||||
<body>
|
||||
<BLANK LINE>
|
||||
<footer>
|
||||
|
||||
The **header** is mandatory and the **scope** of the header must contain the name of the package you are working on.
|
||||
|
||||
#### Type
|
||||
|
||||
The type must be one of the following:
|
||||
|
||||
- feat: A new feature
|
||||
- fix: A bug fix
|
||||
- docs: Documentation only changes
|
||||
- style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
|
||||
- refactor: A code change that neither fixes a bug nor adds a feature (improvements of the code structure)
|
||||
- perf: A code change that improves the performance
|
||||
- test: Adding missing or correcting existing tests
|
||||
- build: Changes that affect the build system or external dependencies (example scopes: gulp, npm)
|
||||
- ci: Changes to CI configuration files and scripts (example scopes: travis, circle)
|
||||
- chore: Other changes that don't modify src or test files
|
||||
- revert: Reverts a previous commit
|
||||
|
||||
#### Scope
|
||||
|
||||
The scope should be the name of the npm package affected (as perceived by the person reading the changelog generated from commit messages).
|
||||
|
||||
#### Subject
|
||||
|
||||
The subject contains a succinct description of the change:
|
||||
|
||||
- Use the imperative, present tense: "change" not "changed" nor "changes"
|
||||
- Don't capitalize the first letter
|
||||
- No dot (.) at the end
|
||||
|
||||
#### Body
|
||||
|
||||
Just as in the subject, use the imperative, present tense: "change" not "changed" nor "changes". The body should include the motivation for the change and contrast this with previous behavior.
|
||||
|
||||
### Branch rules
|
||||
|
||||
- There must be a `main` branch, used only for the releases.
|
||||
- There must be a `dev` branch, used to merge all the branches under it.
|
||||
- Avoid long descriptive names for long-lived branches.
|
||||
- Use kebab-case (no CamelCase).
|
||||
- Use grouping tokens (words) at the beginning of your branch names (in a similar way to the `type` of commit).
|
||||
- Define and use short lead tokens to differentiate branches in a way that is meaningful to your workflow.
|
||||
- Use slashes to separate parts of your branch names.
|
||||
- Remove branch after merge if it is not important.
|
||||
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
git branch -b docs/readme
|
||||
git branch -b test/a-feature
|
||||
git branch -b feat/sidebar
|
||||
git branch -b fix/b-feature
|
||||
```
|
||||
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ethereum Foundation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
556
README.md
@@ -1,168 +1,486 @@
|
||||
# Semaphore
|
||||
<p align="center">
|
||||
<h1 align="center">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/semaphore-protocol/website/blob/main/static/img/semaphore-icon-dark.svg">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://github.com/semaphore-protocol/website/blob/main/static/img/semaphore-icon.svg">
|
||||
<img width="40" alt="Semaphore icon." src="https://github.com/semaphore-protocol/website/blob/main/static/img/semaphore-icon.svg">
|
||||
</picture>
|
||||
Semaphore
|
||||
</h1>
|
||||
</p>
|
||||
|
||||
[](https://circleci.com/gh/kobigurk/semaphore)
|
||||
<p align="center">
|
||||
<a href="https://github.com/semaphore-protocol" target="_blank">
|
||||
<img src="https://img.shields.io/badge/project-Semaphore-blue.svg?style=flat-square">
|
||||
</a>
|
||||
<a href="/LICENSE">
|
||||
<img alt="Github license" src="https://img.shields.io/github/license/semaphore-protocol/semaphore.svg?style=flat-square">
|
||||
</a>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/actions?query=workflow%3Aproduction">
|
||||
<img alt="GitHub Workflow test" src="https://img.shields.io/github/actions/workflow/status/semaphore-protocol/semaphore/production.yml?branch=main&label=test&style=flat-square&logo=github">
|
||||
</a>
|
||||
<a href="https://coveralls.io/github/semaphore-protocol/semaphore">
|
||||
<img alt="Coveralls" src="https://img.shields.io/coveralls/github/semaphore-protocol/semaphore?style=flat-square&logo=coveralls">
|
||||
</a>
|
||||
<a href="https://deepscan.io/dashboard#view=project&tid=16502&pid=22324&bid=657461">
|
||||
<img src="https://deepscan.io/api/teams/16502/projects/22324/branches/657461/badge/grade.svg" alt="DeepScan grade">
|
||||
</a>
|
||||
<a href="https://eslint.org/">
|
||||
<img alt="Linter eslint" src="https://img.shields.io/badge/linter-eslint-8080f2?style=flat-square&logo=eslint">
|
||||
</a>
|
||||
<a href="https://prettier.io/">
|
||||
<img alt="Code style prettier" src="https://img.shields.io/badge/code%20style-prettier-f8bc45?style=flat-square&logo=prettier">
|
||||
</a>
|
||||
<img alt="Repository top language" src="https://img.shields.io/github/languages/top/semaphore-protocol/semaphore?style=flat-square">
|
||||
<a href="https://www.gitpoap.io/gh/semaphore-protocol/semaphore" target="_blank">
|
||||
<img src="https://public-api.gitpoap.io/v1/repo/semaphore-protocol/semaphore/badge">
|
||||
</a>
|
||||
|
||||
Join the [Telegram group](https://t.me/joinchat/B-PQx1U3GtAh--Z4Fwo56A) to discuss.
|
||||
</p>
|
||||
|
||||
## Introduction
|
||||
<div align="center">
|
||||
<h4>
|
||||
<a href="/CONTRIBUTING.md">
|
||||
👥 Contributing
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="/CODE_OF_CONDUCT.md">
|
||||
🤝 Code of conduct
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/contribute">
|
||||
🔎 Issues
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://semaphore.appliedzkp.org/discord">
|
||||
🗣️ Chat & Support
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
Semaphore has been introduced by [barryWhiteHat](https://github.com/barryWhiteHat) as a method of zero-knowledge signaling - a method for an approved user to broadcast an arbitrary string without exposing their identity. This repository is an implementation of an upgraded version of the concept, including the zero-knowledge circuits and the tools necessary to use it, both server-side and client-side.
|
||||
| Semaphore is a protocol, designed to be a simple and generic privacy layer for Ethereum DApps. Using zero knowledge, Ethereum users can prove their membership of a group and send signals such as votes or endorsements without revealing their original identity. |
|
||||
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
|
||||
The project is implemented in plain Node.JS and uses [circom](https://github.com/iden3/circom) for the zero-knowledge proofs.
|
||||
The core of the Semaphore protocol is in the [circuit logic](/packages/circuits/scheme.png). However Semaphore also provides [Solidity contracts](/packages/contracts) and JavaScript libraries to make the steps for offchain proof creation and onchain verification easier. To learn more about Semaphore visit [semaphore.appliedzkp.org](https://semaphore.appliedzkp.org).
|
||||
|
||||
## Design
|
||||
Semaphore is comprised of a zkSNARK statement, a few smart contracts, a server application and a client application.
|
||||
## 📦 Packages
|
||||
|
||||
### Smart contracts
|
||||
Implemented in [**semaphorejs/contracts**](semaphorejs/contracts).
|
||||
<table>
|
||||
<th>Package</th>
|
||||
<th>Version</th>
|
||||
<th>Downloads</th>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/packages/contracts">
|
||||
@semaphore-protocol/contracts
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- NPM version -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/contracts">
|
||||
<img src="https://img.shields.io/npm/v/@semaphore-protocol/contracts.svg?style=flat-square" alt="NPM version" />
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Downloads -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/contracts">
|
||||
<img src="https://img.shields.io/npm/dm/@semaphore-protocol/contracts.svg?style=flat-square" alt="Downloads" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/packages/identity">
|
||||
@semaphore-protocol/identity
|
||||
</a>
|
||||
<a href="https://js.semaphore.appliedzkp.org/identity">
|
||||
(docs)
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- NPM version -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/identity">
|
||||
<img src="https://img.shields.io/npm/v/@semaphore-protocol/identity.svg?style=flat-square" alt="NPM version" />
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Downloads -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/identity">
|
||||
<img src="https://img.shields.io/npm/dm/@semaphore-protocol/identity.svg?style=flat-square" alt="Downloads" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/packages/group">
|
||||
@semaphore-protocol/group
|
||||
</a>
|
||||
<a href="https://js.semaphore.appliedzkp.org/group">
|
||||
(docs)
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- NPM version -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/group">
|
||||
<img src="https://img.shields.io/npm/v/@semaphore-protocol/group.svg?style=flat-square" alt="NPM version" />
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Downloads -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/group">
|
||||
<img src="https://img.shields.io/npm/dm/@semaphore-protocol/group.svg?style=flat-square" alt="Downloads" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/packages/proof">
|
||||
@semaphore-protocol/proof
|
||||
</a>
|
||||
<a href="https://js.semaphore.appliedzkp.org/proof">
|
||||
(docs)
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- NPM version -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/proof">
|
||||
<img src="https://img.shields.io/npm/v/@semaphore-protocol/proof.svg?style=flat-square" alt="NPM version" />
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Downloads -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/proof">
|
||||
<img src="https://img.shields.io/npm/dm/@semaphore-protocol/proof.svg?style=flat-square" alt="Downloads" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/packages/data">
|
||||
@semaphore-protocol/data
|
||||
</a>
|
||||
<a href="https://js.semaphore.appliedzkp.org/data">
|
||||
(docs)
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- NPM version -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/data">
|
||||
<img src="https://img.shields.io/npm/v/@semaphore-protocol/data.svg?style=flat-square" alt="NPM version" />
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Downloads -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/data">
|
||||
<img src="https://img.shields.io/npm/dm/@semaphore-protocol/data.svg?style=flat-square" alt="Downloads" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/packages/hardhat">
|
||||
@semaphore-protocol/hardhat
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- NPM version -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/hardhat">
|
||||
<img src="https://img.shields.io/npm/v/@semaphore-protocol/hardhat.svg?style=flat-square" alt="NPM version" />
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Downloads -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/hardhat">
|
||||
<img src="https://img.shields.io/npm/dm/@semaphore-protocol/hardhat.svg?style=flat-square" alt="Downloads" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/packages/cli">
|
||||
@semaphore-protocol/cli
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- NPM version -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/cli">
|
||||
<img src="https://img.shields.io/npm/v/@semaphore-protocol/cli.svg?style=flat-square" alt="NPM version" />
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Downloads -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/cli">
|
||||
<img src="https://img.shields.io/npm/dm/@semaphore-protocol/cli.svg?style=flat-square" alt="Downloads" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/packages/heyauthn">
|
||||
@semaphore-protocol/heyauthn
|
||||
</a>
|
||||
<a href="https://js.semaphore.appliedzkp.org/heyauthn">
|
||||
(docs)
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- NPM version -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/heyauthn">
|
||||
<img src="https://img.shields.io/npm/v/@semaphore-protocol/heyauthn.svg?style=flat-square" alt="NPM version" />
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<!-- Downloads -->
|
||||
<a href="https://npmjs.org/package/@semaphore-protocol/heyauthn">
|
||||
<img src="https://img.shields.io/npm/dm/@semaphore-protocol/heyauthn.svg?style=flat-square" alt="Downloads" />
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tbody>
|
||||
</table>
|
||||
|
||||
#### Semaphore
|
||||
The Semaphore contract is the base layer of Semaphore. Other contracts can build upon this to create applications that rely on anonymous signaling. Semaphore has a tree of allowed identities, a tree of signals, a set of previously broadcast nullifiers hashes, multiple external nullifiers and a gas price refund price:
|
||||
## 💡 Projects
|
||||
|
||||
* The tree of allowed identities allows a prover to show that they are an identity which is approved to signal.
|
||||
* The tree of signals allows a user to verify the integrity of a list of signals.
|
||||
* The nullifiers hashes set and external nullifier allows the contract to prevent double signals by the same user, within the context of each external nullifier, without exposing the specific user.
|
||||
* The gas price refund price is a mechanism that supports transaction abstraction - a server can broadcast on behalf of a user to provide further anonymity, and in return they receive a refund and a small reward, with a maximum gas price for their transaction.
|
||||
The following are some of the internal and external projects that use Semaphore. If you want to include your project, open an [issue](https://github.com/semaphore-protocol/semaphore/issues/new?assignees=&labels=documentation++%F0%9F%93%96&template=----project.md&title=) or create a PR by adding the project information to the `projects.json` file.
|
||||
|
||||
The contract allows administrative operations that only the owner is allowed to perform:
|
||||
<table>
|
||||
<th>Project</th>
|
||||
<th>Description</th>
|
||||
<th>Links</th>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://explorer.semaphore.appliedzkp.org">
|
||||
Semaphore Explorer
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
Semaphore explorer for on-chain groups.
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://github.com/semaphore-protocol/explorer">
|
||||
Github
|
||||
</a>|
|
||||
<a href="https://semaphore.appliedzkp.org/discord">
|
||||
Discord
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://discord.com/api/oauth2/authorize?client_id=1082429985496772628&permissions=1024&scope=bot">
|
||||
Semaphore Discord Bot
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
A Discord bot for Semaphore.
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://github.com/semaphore-protocol/discord-bot">
|
||||
Github
|
||||
</a>|
|
||||
<a href="https://semaphore.appliedzkp.org/discord">
|
||||
Discord
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://developer.unirep.io">
|
||||
Unirep
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
Private and nonrepudiable reputation system based on ZKP.
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://github.com/Unirep">
|
||||
Github
|
||||
</a>|
|
||||
<a href="https://discord.gg/VzMMDJmYc5">
|
||||
Discord
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://zk-proof-of-humanity.vercel.app">
|
||||
ZK Proof of Humanity
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
A project to allows humans, registered in Proof of Humanity, to prove their humanity without doxing.
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://github.com/elmol/zk-proof-of-humanity">
|
||||
Github
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Plurality
|
||||
</td>
|
||||
<td>
|
||||
An Identity Lego Building Block for dapp creators that lets them identify their users without</br> using any third-party KYC provider or other middlemen, whilst preserving the privacy of users.
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://github.com/Web3-Plurality">
|
||||
Github
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://zerotherapy.vercel.app">
|
||||
ZeroTherapy
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
AMA privacy application built with Semaphore.
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://github.com/Pushpit07/ZeroTherapy">
|
||||
Github
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://bq2.netlify.app/">
|
||||
Block Qualified
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
On-chain and privacy preserving education platform built on Semaphore.
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://github.com/0xdeenz/bq2">
|
||||
Github
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="https://stealthcomms.surge.sh/">
|
||||
StealthComms
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
A project that allows users to prove their membership in a group and send messages/signals without revealing their original identity.
|
||||
</td>
|
||||
<td>
|
||||
<a href="https://github.com/atomniketh/zk-app">
|
||||
Github
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tbody>
|
||||
</table>
|
||||
|
||||
* Managing identities using the **insertIdentity** and **updateIdentity** methods.
|
||||
* Adding or removing an **external_nullifier**.
|
||||
* Setting the broadcast permissioning - whether only the owner can broadcast.
|
||||
## 🛠 Install
|
||||
|
||||
The contract allows anyone to read the current state:
|
||||
Clone this repository:
|
||||
|
||||
* Reading the roots of the two trees.
|
||||
* Reading the current parameters of **external_nullifier**.
|
||||
```bash
|
||||
git clone https://github.com/semaphore-protocol/semaphore.git
|
||||
```
|
||||
|
||||
The contract allows anyone to attempt broadcasting a signal, given a signal, a proof and the relevant public inputs.
|
||||
The contract allows anyone to fund the contract for gas refund and rewards.
|
||||
And install the dependencies:
|
||||
|
||||
Lastly, the contract has a few events to allow a server to build a local state to serve users wishing to generate proofs:
|
||||
```bash
|
||||
cd semaphore && yarn
|
||||
```
|
||||
|
||||
* **Funded** - when the contract has received some funding for refunds and rewards.
|
||||
* **SignalBroadcast** - when a signal has been broadcast successfully, after verification of the proof, the public inputs and double-signaling checks.
|
||||
* **LeafAdded**, **LeafUpdated** (from MerkleTreeLib) - when the trees have been updated.
|
||||
## 📜 Usage
|
||||
|
||||
Copy the `.env.example` file as `.env`:
|
||||
|
||||
#### MerkleTreeLib
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
Manages a number of append-only Merkle trees with efficient inserts and updates.
|
||||
And add your environment variables.
|
||||
|
||||
### zkSNARK statement
|
||||
Implemented in [**semaphorejs/snark**](semaphorejs/snark).
|
||||
### Code quality and formatting
|
||||
|
||||
The statement assures that given public inputs:
|
||||
Run [ESLint](https://eslint.org/) to analyze the code and catch bugs:
|
||||
|
||||
* **signal_hash**
|
||||
* **external_nullifier**
|
||||
* **root**
|
||||
* **nullifiers_hash**
|
||||
```bash
|
||||
yarn lint
|
||||
```
|
||||
|
||||
and private inputs:
|
||||
* **identity_pk**
|
||||
* **identity_nullifier**
|
||||
* **identity_trapdoor**
|
||||
* **identity_path_elements**
|
||||
* **identity_path_index**
|
||||
* **auth_sig_r**
|
||||
* **auth_sig_s**
|
||||
Run [Prettier](https://prettier.io/) to check formatting rules:
|
||||
|
||||
the following conditions hold:
|
||||
```bash
|
||||
yarn prettier
|
||||
```
|
||||
|
||||
* The commitment of the identity structure (**identity_pk**, **identity_nullifier**, **identity_trapdoor**) exists in the identity tree with the root **root**, using the path (**identity_path_elements**, **identity_path_index**). This ensures that the user was added to the system at some point in the past.
|
||||
* **nullifiers_hash** is uniquely derived from **external_nullifier**, **identity_nullifier** and **identity_path_index**. This ensures a user cannot broadcast a signal with the same **external_nullifier** more than once.
|
||||
* The message (**external_nullifier**, **signal_hash**) is signed by the secret key corresponding to **identity_pk**, having the signature (**auth_sig_r**, **auth_sig_s**). This ensures that a state of the contract having a specific **external_nullifier**, ensuring no double-signaling.
|
||||
Or to automatically format the code:
|
||||
|
||||
#### Cryptographic primitives
|
||||
```bash
|
||||
yarn prettier:write
|
||||
```
|
||||
|
||||
Semaphore uses a few cryptographic primitives provided by circomlib:
|
||||
### Conventional commits
|
||||
|
||||
* MiMCHash for the Merkle tree, the identity commitments and the message hash in the signature.
|
||||
* EdDSA for the signature.
|
||||
Semaphore uses [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/). A [command line utility](https://github.com/commitizen/cz-cli) to commit using the correct syntax can be used by running:
|
||||
|
||||
Note: MiMCHash, and especially the specific paramteres used in the circuit, have not been heavily audited yet by the cryptography community. Additionally, the circuit and code should also receive further review before relying on it for production applications.
|
||||
```bash
|
||||
yarn commit
|
||||
```
|
||||
|
||||
### Server
|
||||
It will also automatically check that the modified files comply with ESLint and Prettier rules.
|
||||
|
||||
Implemented in [**semaphorejs/src/server/server.js**](semaphorejs/src/server/server.js). Acts as a manager of the identities merkle tree and as an identity onboarder. The REST API allows:
|
||||
### Snark artifacts
|
||||
|
||||
* An owner to submit a transaction that adds an identity to the merkle tree, provided proper authentication.
|
||||
* A client to ask for a path from an identity commitment to the current root of the tree, relieving the client from the need to manage this tree by themselves.
|
||||
* A client to ask a list of signals, together with their paths to the signals tree root.
|
||||
* An owner to set the external nullifier.
|
||||
Download the Semaphore snark artifacts needed to generate and verify proofs:
|
||||
|
||||
The server relies on an Ethereum node and the events in the smart contract to synchronize to the current state and handle rollbacks if they occur.
|
||||
```bash
|
||||
yarn download:snark-artifacts
|
||||
```
|
||||
|
||||
It uses [**semaphore-merkle-tree**](https://github.com/weijiekoh/semaphore-merkle-tree) - Semaphore requires managing a growing merkle tree containing the identities allowed to signal and the signals broadcast by users. semaphore-merkle-tree manages the trees either in-memory, for browser usage or a database, making the tree scale by the disk size.
|
||||
### Testing
|
||||
|
||||
### Client
|
||||
Run [Jest](https://jestjs.io/) to test the JS libraries:
|
||||
|
||||
Implemented in [**src/client/client.js**](semaphorejs/src/client/client.js). Enables signaling a user's support of an arbitrary statemnt, given identity secrets of an identity existing in the tree. The client has 2 CLI functions:
|
||||
```bash
|
||||
yarn test:libraries
|
||||
```
|
||||
|
||||
* **generate_identity** - generate random identity secrets and randomness, save them to disk and print the identity commitment. The client can then send the commitment to the onboarder (using another channel), requesting they add them to the tree.
|
||||
* **signal STRING** - given an arbitrary string, generates a zero-knowledge proof of the client's authorization to signal. The signalling requests the path of the identity commitment from the server, and broadcasts the transaction directly to the contract.
|
||||
Run [Mocha](https://mochajs.org/) to test the contracts:
|
||||
|
||||
### Web
|
||||
```bash
|
||||
yarn test:contracts
|
||||
```
|
||||
|
||||
A web interface to run the server and client APIs, generate identities and proofs directly in the browser and broadcast signals.
|
||||
Or test everything with:
|
||||
|
||||
## Running modes
|
||||
```bash
|
||||
yarn test
|
||||
```
|
||||
|
||||
Schematically, Semaphore has the following actors:
|
||||
### Build libraries & compile contracts
|
||||
|
||||

|
||||
Run [Rollup](https://www.rollupjs.org) to build all the packages:
|
||||
|
||||
There are 3 main running modes:
|
||||
```bash
|
||||
yarn build:libraries
|
||||
```
|
||||
|
||||
* One server is the owner of the Semaphore contract, other servers act as intermediate miners and clients uses them to broadcast their signals. This the lightest running mode for clients, as they rely on servers to provide them with tree paths, a list of signals and broadcast. This is the default mode that is exposed in the CLI and web clients.
|
||||
* One server is the owner of the Semaphore contract and clients broadcast directly. This is still a light running mode for clients, and is less prone to server censorship, at the cost of less anonymity - as the user's Ethereum address is exposed.
|
||||
* One server is the owner of the Semaphore contract and clients run a server locally. This is a heavier running mode, allowing the clients to run autonomously, reconstructing the signals and identities states locally.
|
||||
Compile the smart contracts with [Hardhat](https://hardhat.org/):
|
||||
|
||||
## Configuration
|
||||
```bash
|
||||
yarn compile:contracts
|
||||
```
|
||||
|
||||
The server and the client look for **server-config.json** and **client-config.json**, respectively. They can also accept their configuration as environment variables:
|
||||
* **server**:
|
||||
* CONFIG_ENV - load configuration from environment variables rather than a file.
|
||||
* CONFIG_PATH - location of the configuration file.
|
||||
* LOG_LEVEL - error, info, debug or verbose.
|
||||
* DB_PATH - location of the RocksDB database.
|
||||
* CHAIN_ID - chain ID of the Ethereum network.
|
||||
* CONTRACT_ADDRESS - the deployed Semaphore contract address.
|
||||
* CREATION_HASH - the transaction hash in which the contract was created, to allow for faster initial sync.
|
||||
* NODE_URL - the RPC URL of the Ehtereum node.
|
||||
* SEMAPHORE_PORT - the port on which to serve the Semaphore server REST API.
|
||||
* SEMAPHORE_LOGIN - the password with which clients communicating with the Semaphore server REST API must authenticate.
|
||||
* FROM_ADDRESS - the address to send transactions from.
|
||||
* FROM_PRIVATE_KEY - the private key of FROM_ADDRESS.
|
||||
* TRANSACTION_CONFIRMATION_BLOCKS - the amount of blocks to wait until a transaction is considered confirmed. The default is 24.
|
||||
* **client:**
|
||||
* CONFIG_ENV - load configuration from environment variables rather than a file.
|
||||
* LOG_LEVEL - error, info, debug or verbose.
|
||||
* IDENTITY_PATH - location of the identity secrets file.
|
||||
* CHAIN_ID - chain ID of the Ethereum network.
|
||||
* CONTRACT_ADDRESS - the deployed Semaphore contract address.
|
||||
* NODE_URL - the RPC URL of the Ehtereum node.
|
||||
* FROM_ADDRESS - the address to send transactions from.
|
||||
* FROM_PRIVATE_KEY - the private key of FROM_ADDRESS.
|
||||
* TRANSACTION_CONFIRMATION_BLOCKS - the amount of blocks to wait until a transaction is considered confirmed. The default is 24.
|
||||
* EXTERNAL_NULLIFIER - the external nullifier to be used with the signal. Must match the one in the contract.
|
||||
* SEMAPHORE_SERVER_URL - the URL of the Semaphore REST server.
|
||||
* BROADCASTER_ADDRESS - the address of the Semaphore server that will be allowed to broadcast the client's signals.
|
||||
### Documentation (JS libraries)
|
||||
|
||||
## Running
|
||||
Run [TypeDoc](https://typedoc.org/) to generate a documentation website for each package:
|
||||
|
||||
The easiest way to try Semaphore out is to use [https://semaphore.kobi.one](https://semaphore.kobi.one) - a web interface to broadcast to a remote server and generate proofs locally. First, load the Rinkeby config using the button at the top. Then, you can generate an identity and send the commitment to @kobigurk on Telegram or open an issue in the repository. Then, you can broadcast signals, including the proof generation, directly in the browser. Lastly, you can see the signals that have been broadcast to date in the table.
|
||||
```bash
|
||||
yarn docs
|
||||
```
|
||||
|
||||
* To try out Semaphore locally you can clone the repository and run the following in `semaphore/semaphorejs/`:
|
||||
* **npm install && npm link**
|
||||
* **cd scripts && ./compile.sh && ./do_setup.sh && ./build_verifier.sh** - compile, do a setup and build the verifier of the Semaphore circuit.
|
||||
|
||||
* **scripts/run_ganache.sh** - runs ganache with appropriate parameters for Semaphore testing.
|
||||
* **scripts/run_all_test.sh** - runs a server and a client, generates a new random identity and broadcasts a signal.
|
||||
|
||||
It assumes bash, node and truffle are globally available.
|
||||
|
||||
Examples of run commands (roughly matching the test contract deployed on Rinkeby):
|
||||
* `LOG_LEVEL=debug CHAIN_ID=4 CONTRACT_ADDRESS=0x3dE2c3f8853594440c3363f8D491449Defa0bE1F NODE_URL=https://rinkeby.infura.io/v3/f4a3ad81db3f4750bd201955c8d20066 SEMAPHORE_PORT=3000 FROM_ADDRESS=0x1929c15f4e818abf2549510622a50c440c474223 FROM_PRIVATE_KEY=0x6738837df169e8d6ffc6e33a2947e58096d644fa4aa6d74358c8d9d57c12cd21 TRANSACTION_CONFIRMATION_BLOCKS=1 CREATION_HASH=0x4d6998f49f3ebb6e2bd3567c5adbf3f5ab711fbb24e618b4b53498d521f9c758 SEMAPHORE_LOGIN=test123 CONFIG_ENV=true npx semaphorejs-server`
|
||||
* `LOG_LEVEL=debug TRANSACTION_CONFIRMATION_BLOCKS=1 CHAIN_ID=4 CONTRACT_ADDRESS=0x3dE2c3f8853594440c3363f8D491449Defa0bE1F NODE_URL=https://rinkeby.infura.io/v3/f4a3ad81db3f4750bd201955c8d20066 EXTERNAL_NULLIFIER=12312 SEMAPHORE_SERVER_URL=https://semaphore-server.kobi.one BROADCASTER_ADDRESS=0x1929c15f4e818abf2549510622a50c440c474223 CONFIG_ENV=true npx semaphorejs-client signal "I vote for fork A"`
|
||||
The output will be placed on the `docs` folder.
|
||||
|
||||
3
babel.config.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"presets": [["@babel/preset-env", { "targets": { "node": "current" } }], "@babel/preset-typescript"]
|
||||
}
|
||||
7
changelogithub.config.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"types": {
|
||||
"feat": { "title": "🚀 Features" },
|
||||
"fix": { "title": "🐞 Bug Fixes" },
|
||||
"refactor": { "title": "♻️ Refactoring" }
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 25 KiB |
123
docs/index.html
Normal file
@@ -0,0 +1,123 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html style="height: 100%">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="x-ua-compatible" content="IE=edge" />
|
||||
<title>Semaphore packages</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="A monorepo of Semaphore packages."
|
||||
/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
|
||||
/>
|
||||
</head>
|
||||
<body
|
||||
style="
|
||||
margin: 0;
|
||||
background-color: #EAF0F4;
|
||||
color: #000;
|
||||
height: 100%;
|
||||
font-family: 'Courier New', monospace;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
"
|
||||
>
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: 1;
|
||||
padding: 0 20px;
|
||||
text-align: center;
|
||||
"
|
||||
>
|
||||
<div style="display: flex">
|
||||
<span style="margin-right: 5px">
|
||||
<img width="40" src="https://raw.githubusercontent.com/semaphore-protocol/website/main/static/img/semaphore-icon.svg">
|
||||
</span>
|
||||
<h1 style="margin: 0; font-size: 40px">Semaphore packages</h1>
|
||||
</div>
|
||||
<p style="max-width: 500px">
|
||||
A monorepo of Semaphore packages.
|
||||
</p>
|
||||
<ul style="list-style-type: none; padding: 0; margin: 0; margin-top: 10px"></ul>
|
||||
</div>
|
||||
<footer
|
||||
style="
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 15px 20px;
|
||||
background-color: #EAF0F4;
|
||||
"
|
||||
>
|
||||
<div
|
||||
style="
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 900px;
|
||||
"
|
||||
>
|
||||
<p style="margin: 0; font-size: 16px">
|
||||
Copyright © 2022 Ethereum Foundation
|
||||
</p>
|
||||
<div>
|
||||
<a
|
||||
style="margin-right: 15px; text-decoration: none"
|
||||
target="_blank"
|
||||
href="https://github.com/semaphore-protocol/semaphore"
|
||||
>
|
||||
<i
|
||||
class="fa fa-github"
|
||||
style="font-size: 24px; color: #000"
|
||||
></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
<script>
|
||||
const url =
|
||||
"https://api.github.com/repos/semaphore-protocol/semaphore/contents?ref=gh-pages"
|
||||
|
||||
function insertLinks(packages) {
|
||||
const [element] = window.document.getElementsByTagName("ul")
|
||||
let html = ""
|
||||
|
||||
for (const package of packages) {
|
||||
html += `<li style="display: flex; align-items: center; margin-bottom: 8px">
|
||||
<a style="margin-right: 15px" target="_blank" href="https://github.com/semaphore-protocol/semaphore/tree/main/packages/${package}">
|
||||
<i class="fa fa-github" style="font-size: 24px; color: #000"></i>
|
||||
</a>
|
||||
<a style="color: #000; text-decoration: none; font-size: 16px"
|
||||
onmouseover="this.style.color='#404A4E';"
|
||||
onmouseout="this.style.color='#000';"
|
||||
target="_blank" href="https://semaphore-protocol.github.io/semaphore/${package}">
|
||||
@semaphore-protocol/${package} >
|
||||
</a></li>`
|
||||
}
|
||||
|
||||
element.innerHTML = html
|
||||
}
|
||||
|
||||
fetch(url)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
const ignore = [".nojekyll", "index.html", "CNAME"]
|
||||
const packages = data
|
||||
.map((c) => c.name)
|
||||
.filter((name) => !ignore.includes(name))
|
||||
|
||||
localStorage.setItem("packages", JSON.stringify(packages))
|
||||
|
||||
insertLinks(packages)
|
||||
})
|
||||
</script>
|
||||
</html>
|
||||
29
jest.config.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import fs from "fs"
|
||||
import type { Config } from "@jest/types"
|
||||
|
||||
const projects: any = fs
|
||||
.readdirSync("./packages", { withFileTypes: true })
|
||||
.filter((directory) => directory.isDirectory())
|
||||
.map(({ name }) => ({
|
||||
rootDir: `packages/${name}`,
|
||||
displayName: name,
|
||||
setupFiles: ["dotenv/config"],
|
||||
moduleNameMapper: {
|
||||
"@semaphore-protocol/(.*)": "<rootDir>/../$1/src/index.ts" // Interdependency packages.
|
||||
}
|
||||
}))
|
||||
|
||||
export default async (): Promise<Config.InitialOptions> => ({
|
||||
projects,
|
||||
verbose: true,
|
||||
coverageDirectory: "./coverage/libraries",
|
||||
collectCoverageFrom: ["<rootDir>/src/**/*.ts", "!<rootDir>/src/**/index.ts", "!<rootDir>/src/**/*.d.ts"],
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
branches: 90,
|
||||
functions: 95,
|
||||
lines: 95,
|
||||
statements: 95
|
||||
}
|
||||
}
|
||||
})
|
||||
90
package.json
Normal file
@@ -0,0 +1,90 @@
|
||||
{
|
||||
"name": "semaphore-protocol",
|
||||
"description": "A zero-knowledge protocol for anonymous signalling on Ethereum.",
|
||||
"license": "MIT",
|
||||
"repository": "git@github.com:semaphore-protocol/semaphore.git",
|
||||
"homepage": "https://github.com/semaphore-protocol/semaphore",
|
||||
"bugs": "https://github.com/semaphore-protocol/semaphore/issues",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build:libraries": "yarn workspaces foreach -t --no-private run build",
|
||||
"compile:contracts": "yarn workspace contracts compile",
|
||||
"download:snark-artifacts": "rimraf snark-artifacts && ts-node scripts/download-snark-artifacts.ts",
|
||||
"remove:template-files": "ts-node scripts/remove-template-files.ts",
|
||||
"test": "yarn test:libraries && yarn test:contracts",
|
||||
"test:libraries": "jest --coverage",
|
||||
"test:contracts": "yarn workspace contracts test:coverage",
|
||||
"lint": "eslint . --ext .js,.ts && yarn workspace contracts lint",
|
||||
"prettier": "prettier -c .",
|
||||
"prettier:write": "prettier -w .",
|
||||
"docs": "yarn workspaces foreach --no-private run docs",
|
||||
"version:bump": "yarn workspaces foreach --no-private version -d ${0} && yarn version apply --all && git commit -am \"chore: v${0}\" && git tag v${0}",
|
||||
"version:publish": "yarn build:libraries && yarn remove:template-files && yarn workspaces foreach --no-private npm publish --tolerate-republish --access public",
|
||||
"version:release": "changelogithub",
|
||||
"commit": "cz",
|
||||
"precommit": "lint-staged",
|
||||
"postinstall": "yarn download:snark-artifacts && husky install"
|
||||
},
|
||||
"keywords": [
|
||||
"ethereum",
|
||||
"semaphore",
|
||||
"solidity",
|
||||
"circom",
|
||||
"javascript",
|
||||
"typescript",
|
||||
"zero-knowledge",
|
||||
"zk-snarks",
|
||||
"zero-knowledge-proofs",
|
||||
"proof-of-membership",
|
||||
"monorepo"
|
||||
],
|
||||
"workspaces": [
|
||||
"packages/*",
|
||||
"packages/contracts/contracts"
|
||||
],
|
||||
"packageManager": "yarn@3.2.1",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.16.7",
|
||||
"@babel/preset-env": "^7.16.8",
|
||||
"@babel/preset-typescript": "^7.17.12",
|
||||
"@commitlint/cli": "^16.0.2",
|
||||
"@commitlint/config-conventional": "^16.0.0",
|
||||
"@rollup/plugin-typescript": "^8.3.0",
|
||||
"@types/download": "^8.0.1",
|
||||
"@types/glob": "^7.2.0",
|
||||
"@types/jest": "^27.4.0",
|
||||
"@types/node": "^17.0.9",
|
||||
"@types/rimraf": "^3.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.9.1",
|
||||
"@typescript-eslint/parser": "^5.9.1",
|
||||
"babel-jest": "^27.4.6",
|
||||
"changelogithub": "0.12.7",
|
||||
"commitizen": "^4.2.4",
|
||||
"cz-conventional-changelog": "^3.3.0",
|
||||
"dotenv": "^16.0.2",
|
||||
"eslint": "^8.2.0",
|
||||
"eslint-config-airbnb-base": "15.0.0",
|
||||
"eslint-config-airbnb-typescript": "^16.1.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-import": "^2.25.2",
|
||||
"eslint-plugin-jest": "^25.7.0",
|
||||
"husky": "^8.0.3",
|
||||
"jest": "^27.4.1",
|
||||
"jest-config": "^27.4.7",
|
||||
"lint-staged": "^12.1.7",
|
||||
"prettier": "^2.5.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"rollup": "^2.64.0",
|
||||
"ts-node": "^10.4.0",
|
||||
"tslib": "^2.3.1",
|
||||
"typescript": "^4.7.0"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "./node_modules/cz-conventional-changelog"
|
||||
}
|
||||
},
|
||||
"resolutions": {
|
||||
"changelogithub@0.12.7": "patch:changelogithub@npm:0.12.7#.yarn/patches/changelogithub-npm-0.12.7-72f348805d.patch"
|
||||
}
|
||||
}
|
||||
37
packages/circuits/README.md
Normal file
@@ -0,0 +1,37 @@
|
||||
<p align="center">
|
||||
<h1 align="center">
|
||||
Semaphore circuits
|
||||
</h1>
|
||||
<p align="center">Semaphore circuits to create and verify zero-knowledge proofs.</p>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/semaphore-protocol">
|
||||
<img src="https://img.shields.io/badge/project-Semaphore-blue.svg?style=flat-square">
|
||||
</a>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/LICENSE">
|
||||
<img alt="Github license" src="https://img.shields.io/github/license/semaphore-protocol/semaphore.svg?style=flat-square">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
<h4>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/CONTRIBUTING.md">
|
||||
👥 Contributing
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/blob/main/CODE_OF_CONDUCT.md">
|
||||
🤝 Code of conduct
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://github.com/semaphore-protocol/semaphore/contribute">
|
||||
🔎 Issues
|
||||
</a>
|
||||
<span> | </span>
|
||||
<a href="https://semaphore.appliedzkp.org/discord">
|
||||
🗣️ Chat & Support
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
To learn more about circuits visit [semaphore.appliedzkp.org](https://semaphore.appliedzkp.org/docs/technical-reference/circuits).
|
||||
7
packages/circuits/package.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "circuits",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"circomlib": "^2.0.2"
|
||||
}
|
||||
}
|
||||
BIN
packages/circuits/scheme.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
90
packages/circuits/semaphore.circom
Normal file
@@ -0,0 +1,90 @@
|
||||
pragma circom 2.0.0;
|
||||
|
||||
include "../node_modules/circomlib/circuits/poseidon.circom";
|
||||
include "./tree.circom";
|
||||
|
||||
template CalculateSecret() {
|
||||
signal input identityNullifier;
|
||||
signal input identityTrapdoor;
|
||||
|
||||
signal output out;
|
||||
|
||||
component poseidon = Poseidon(2);
|
||||
|
||||
poseidon.inputs[0] <== identityNullifier;
|
||||
poseidon.inputs[1] <== identityTrapdoor;
|
||||
|
||||
out <== poseidon.out;
|
||||
}
|
||||
|
||||
template CalculateIdentityCommitment() {
|
||||
signal input secret;
|
||||
|
||||
signal output out;
|
||||
|
||||
component poseidon = Poseidon(1);
|
||||
|
||||
poseidon.inputs[0] <== secret;
|
||||
|
||||
out <== poseidon.out;
|
||||
}
|
||||
|
||||
template CalculateNullifierHash() {
|
||||
signal input externalNullifier;
|
||||
signal input identityNullifier;
|
||||
|
||||
signal output out;
|
||||
|
||||
component poseidon = Poseidon(2);
|
||||
|
||||
poseidon.inputs[0] <== externalNullifier;
|
||||
poseidon.inputs[1] <== identityNullifier;
|
||||
|
||||
out <== poseidon.out;
|
||||
}
|
||||
|
||||
// The current Semaphore smart contracts require nLevels <= 32 and nLevels >= 16.
|
||||
template Semaphore(nLevels) {
|
||||
signal input identityNullifier;
|
||||
signal input identityTrapdoor;
|
||||
signal input treePathIndices[nLevels];
|
||||
signal input treeSiblings[nLevels];
|
||||
|
||||
signal input signalHash;
|
||||
signal input externalNullifier;
|
||||
|
||||
signal output root;
|
||||
signal output nullifierHash;
|
||||
|
||||
component calculateSecret = CalculateSecret();
|
||||
calculateSecret.identityNullifier <== identityNullifier;
|
||||
calculateSecret.identityTrapdoor <== identityTrapdoor;
|
||||
|
||||
signal secret;
|
||||
secret <== calculateSecret.out;
|
||||
|
||||
component calculateIdentityCommitment = CalculateIdentityCommitment();
|
||||
calculateIdentityCommitment.secret <== secret;
|
||||
|
||||
component calculateNullifierHash = CalculateNullifierHash();
|
||||
calculateNullifierHash.externalNullifier <== externalNullifier;
|
||||
calculateNullifierHash.identityNullifier <== identityNullifier;
|
||||
|
||||
component inclusionProof = MerkleTreeInclusionProof(nLevels);
|
||||
inclusionProof.leaf <== calculateIdentityCommitment.out;
|
||||
|
||||
for (var i = 0; i < nLevels; i++) {
|
||||
inclusionProof.siblings[i] <== treeSiblings[i];
|
||||
inclusionProof.pathIndices[i] <== treePathIndices[i];
|
||||
}
|
||||
|
||||
root <== inclusionProof.root;
|
||||
|
||||
// Dummy square to prevent tampering signalHash.
|
||||
signal signalHashSquared;
|
||||
signalHashSquared <== signalHash * signalHash;
|
||||
|
||||
nullifierHash <== calculateNullifierHash.out;
|
||||
}
|
||||
|
||||
component main {public [signalHash, externalNullifier]} = Semaphore(20);
|
||||
40
packages/circuits/tree.circom
Normal file
@@ -0,0 +1,40 @@
|
||||
pragma circom 2.0.0;
|
||||
|
||||
include "../node_modules/circomlib/circuits/poseidon.circom";
|
||||
include "../node_modules/circomlib/circuits/mux1.circom";
|
||||
|
||||
template MerkleTreeInclusionProof(nLevels) {
|
||||
signal input leaf;
|
||||
signal input pathIndices[nLevels];
|
||||
signal input siblings[nLevels];
|
||||
|
||||
signal output root;
|
||||
|
||||
component poseidons[nLevels];
|
||||
component mux[nLevels];
|
||||
|
||||
signal hashes[nLevels + 1];
|
||||
hashes[0] <== leaf;
|
||||
|
||||
for (var i = 0; i < nLevels; i++) {
|
||||
pathIndices[i] * (1 - pathIndices[i]) === 0;
|
||||
|
||||
poseidons[i] = Poseidon(2);
|
||||
mux[i] = MultiMux1(2);
|
||||
|
||||
mux[i].c[0][0] <== hashes[i];
|
||||
mux[i].c[0][1] <== siblings[i];
|
||||
|
||||
mux[i].c[1][0] <== siblings[i];
|
||||
mux[i].c[1][1] <== hashes[i];
|
||||
|
||||
mux[i].s <== pathIndices[i];
|
||||
|
||||
poseidons[i].inputs[0] <== mux[i].out[0];
|
||||
poseidons[i].inputs[1] <== mux[i].out[1];
|
||||
|
||||
hashes[i + 1] <== poseidons[i].out;
|
||||
}
|
||||
|
||||
root <== hashes[nLevels];
|
||||
}
|
||||
4
packages/cli-template-contracts-hardhat/.env.example
Normal file
@@ -0,0 +1,4 @@
|
||||
INFURA_API_KEY=
|
||||
ETHEREUM_PRIVATE_KEY=
|
||||
REPORT_GAS=false
|
||||
COINMARKETCAP_API_KEY=
|
||||
10
packages/cli-template-contracts-hardhat/.gitignore
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
node_modules
|
||||
.env
|
||||
coverage
|
||||
coverage.json
|
||||
typechain
|
||||
typechain-types
|
||||
|
||||
# Hardhat files
|
||||
cache
|
||||
artifacts
|
||||
54
packages/cli-template-contracts-hardhat/README.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# Semaphore Hardhat template
|
||||
|
||||
This project demonstrates a basic Semaphore use case. It comes with a sample contract, a test for that contract and a sample task that deploys that contract.
|
||||
|
||||
## Usage
|
||||
|
||||
### Compile
|
||||
|
||||
```bash
|
||||
yarn compile
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
```bash
|
||||
yarn test
|
||||
```
|
||||
|
||||
You can also generate a test coverage report:
|
||||
|
||||
```bash
|
||||
yarn test:coverage
|
||||
```
|
||||
|
||||
Or a test gas report:
|
||||
|
||||
```bash
|
||||
yarn test:report-gas
|
||||
```
|
||||
|
||||
### Deploy
|
||||
|
||||
1. Copy the `.env.example` file as `.env`.
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
2. Add your environment variables.
|
||||
|
||||
> **Note**
|
||||
> You should at least set a valid Ethereum URL (e.g. Infura) and a private key with some ethers.
|
||||
|
||||
3. And deploy your contract.
|
||||
|
||||
```bash
|
||||
yarn deploy --semaphore <semaphore-address> --group <group-id> --network goerli
|
||||
```
|
||||
|
||||
> **Note**
|
||||
> Check the Semaphore contract addresses [here](https://semaphore.appliedzkp.org/docs/deployed-contracts#semaphore).
|
||||
|
||||
> **Warning**
|
||||
> The group id is a number!
|
||||
@@ -0,0 +1,30 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
import "@semaphore-protocol/contracts/interfaces/ISemaphore.sol";
|
||||
|
||||
contract Feedback {
|
||||
ISemaphore public semaphore;
|
||||
|
||||
uint256 public groupId;
|
||||
|
||||
constructor(address semaphoreAddress, uint256 _groupId) {
|
||||
semaphore = ISemaphore(semaphoreAddress);
|
||||
groupId = _groupId;
|
||||
|
||||
semaphore.createGroup(groupId, 20, address(this));
|
||||
}
|
||||
|
||||
function joinGroup(uint256 identityCommitment) external {
|
||||
semaphore.addMember(groupId, identityCommitment);
|
||||
}
|
||||
|
||||
function sendFeedback(
|
||||
uint256 feedback,
|
||||
uint256 merkleTreeRoot,
|
||||
uint256 nullifierHash,
|
||||
uint256[8] calldata proof
|
||||
) external {
|
||||
semaphore.verifyProof(groupId, merkleTreeRoot, feedback, nullifierHash, groupId, proof);
|
||||
}
|
||||
}
|
||||
77
packages/cli-template-contracts-hardhat/hardhat.config.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import "@nomiclabs/hardhat-ethers"
|
||||
import "@nomicfoundation/hardhat-chai-matchers"
|
||||
import "@semaphore-protocol/hardhat"
|
||||
import "@typechain/hardhat"
|
||||
import { config as dotenvConfig } from "dotenv"
|
||||
import "hardhat-gas-reporter"
|
||||
import { HardhatUserConfig } from "hardhat/config"
|
||||
import { NetworksUserConfig } from "hardhat/types"
|
||||
import "solidity-coverage"
|
||||
import { config } from "./package.json"
|
||||
import "./tasks/deploy"
|
||||
|
||||
dotenvConfig()
|
||||
|
||||
function getNetworks(): NetworksUserConfig {
|
||||
if (!process.env.INFURA_API_KEY || !process.env.ETHEREUM_PRIVATE_KEY) {
|
||||
return {}
|
||||
}
|
||||
|
||||
const accounts = [`0x${process.env.ETHEREUM_PRIVATE_KEY}`]
|
||||
const infuraApiKey = process.env.INFURA_API_KEY
|
||||
|
||||
return {
|
||||
goerli: {
|
||||
url: `https://goerli.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 5,
|
||||
accounts
|
||||
},
|
||||
sepolia: {
|
||||
url: `https://sepolia.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 11155111,
|
||||
accounts
|
||||
},
|
||||
mumbai: {
|
||||
url: `https://polygon-mumbai.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 80001,
|
||||
accounts
|
||||
},
|
||||
"optimism-goerli": {
|
||||
url: `https://optimism-goerli.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 420,
|
||||
accounts
|
||||
},
|
||||
arbitrum: {
|
||||
url: `https://arbitrum-mainnet.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 42161,
|
||||
accounts
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const hardhatConfig: HardhatUserConfig = {
|
||||
solidity: config.solidity,
|
||||
paths: {
|
||||
sources: config.paths.contracts,
|
||||
tests: config.paths.tests,
|
||||
cache: config.paths.cache,
|
||||
artifacts: config.paths.build.contracts
|
||||
},
|
||||
networks: {
|
||||
hardhat: {
|
||||
chainId: 1337
|
||||
},
|
||||
...getNetworks()
|
||||
},
|
||||
gasReporter: {
|
||||
currency: "USD",
|
||||
enabled: process.env.REPORT_GAS === "true",
|
||||
coinmarketcap: process.env.COINMARKETCAP_API_KEY
|
||||
},
|
||||
typechain: {
|
||||
outDir: config.paths.build.typechain,
|
||||
target: "ethers-v5"
|
||||
}
|
||||
}
|
||||
|
||||
export default hardhatConfig
|
||||
78
packages/cli-template-contracts-hardhat/package.json
Normal file
@@ -0,0 +1,78 @@
|
||||
{
|
||||
"name": "@semaphore-protocol/cli-template-contracts-hardhat",
|
||||
"version": "3.11.0",
|
||||
"description": "Semaphore Hardhat template.",
|
||||
"license": "Unlicense",
|
||||
"files": [
|
||||
"files.tgz",
|
||||
"contracts/",
|
||||
"scripts/",
|
||||
"tasks/",
|
||||
"test/",
|
||||
".env.example",
|
||||
"hardhat.config.ts",
|
||||
"tsconfig.json",
|
||||
"README.md"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "hardhat node & yarn compile && yarn deploy --network localhost",
|
||||
"compile": "hardhat compile",
|
||||
"download:snark-artifacts": "hardhat run scripts/download-snark-artifacts.ts",
|
||||
"deploy": "yarn compile && hardhat deploy",
|
||||
"test": "hardhat run scripts/download-snark-artifacts.ts && hardhat test",
|
||||
"test:report-gas": "REPORT_GAS=true hardhat test",
|
||||
"test:coverage": "hardhat coverage",
|
||||
"typechain": "hardhat typechain",
|
||||
"prepublish": "tar -czf files.tgz .gitignore"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ethersproject/abi": "^5.4.7",
|
||||
"@ethersproject/providers": "^5.4.7",
|
||||
"@nomicfoundation/hardhat-chai-matchers": "^1.0.0",
|
||||
"@nomicfoundation/hardhat-network-helpers": "^1.0.0",
|
||||
"@nomicfoundation/hardhat-toolbox": "^2.0.0",
|
||||
"@nomiclabs/hardhat-ethers": "^2.0.0",
|
||||
"@nomiclabs/hardhat-etherscan": "^3.0.0",
|
||||
"@semaphore-protocol/group": "3.11.0",
|
||||
"@semaphore-protocol/hardhat": "3.11.0",
|
||||
"@semaphore-protocol/identity": "3.11.0",
|
||||
"@semaphore-protocol/proof": "3.11.0",
|
||||
"@typechain/ethers-v5": "^10.1.0",
|
||||
"@typechain/hardhat": "^6.1.2",
|
||||
"@types/chai": "^4.2.0",
|
||||
"@types/download": "^8.0.1",
|
||||
"@types/mocha": "^9.1.0",
|
||||
"@types/node": ">=12.0.0",
|
||||
"chai": "^4.2.0",
|
||||
"dotenv": "^16.0.3",
|
||||
"download": "^8.0.0",
|
||||
"ethers": "^5.4.7",
|
||||
"hardhat": "^2.11.0",
|
||||
"hardhat-gas-reporter": "^1.0.8",
|
||||
"solidity-coverage": "^0.8.1",
|
||||
"ts-node": ">=8.0.0",
|
||||
"typechain": "^8.1.0",
|
||||
"typescript": ">=4.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@semaphore-protocol/contracts": "3.11.0"
|
||||
},
|
||||
"config": {
|
||||
"solidity": {
|
||||
"version": "0.8.4"
|
||||
},
|
||||
"paths": {
|
||||
"contracts": "./contracts",
|
||||
"tests": "./test",
|
||||
"cache": "./cache",
|
||||
"build": {
|
||||
"snark-artifacts": "./build/snark-artifacts",
|
||||
"contracts": "./build/contracts",
|
||||
"typechain": "./build/typechain"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import download from "download"
|
||||
import fs from "fs"
|
||||
import { config } from "../package.json"
|
||||
|
||||
async function main() {
|
||||
const snarkArtifactsPath = config.paths.build["snark-artifacts"]
|
||||
const url = `http://www.trusted-setup-pse.org/semaphore/${20}`
|
||||
|
||||
if (!fs.existsSync(snarkArtifactsPath)) {
|
||||
fs.mkdirSync(snarkArtifactsPath, { recursive: true })
|
||||
}
|
||||
|
||||
if (!fs.existsSync(`${snarkArtifactsPath}/semaphore.zkey`)) {
|
||||
await download(`${url}/semaphore.wasm`, snarkArtifactsPath)
|
||||
await download(`${url}/semaphore.zkey`, snarkArtifactsPath)
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
process.exit(1)
|
||||
})
|
||||
31
packages/cli-template-contracts-hardhat/tasks/deploy.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { task, types } from "hardhat/config"
|
||||
|
||||
task("deploy", "Deploy a Feedback contract")
|
||||
.addOptionalParam("semaphore", "Semaphore contract address", undefined, types.string)
|
||||
.addOptionalParam("group", "Group id", "42", types.string)
|
||||
.addOptionalParam("logs", "Print the logs", true, types.boolean)
|
||||
.setAction(async ({ logs, semaphore: semaphoreAddress, group: groupId }, { ethers, run }) => {
|
||||
if (!semaphoreAddress) {
|
||||
const { semaphore } = await run("deploy:semaphore", {
|
||||
logs
|
||||
})
|
||||
|
||||
semaphoreAddress = semaphore.address
|
||||
}
|
||||
|
||||
if (!groupId) {
|
||||
groupId = process.env.GROUP_ID
|
||||
}
|
||||
|
||||
const FeedbackFactory = await ethers.getContractFactory("Feedback")
|
||||
|
||||
const feedbackContract = await FeedbackFactory.deploy(semaphoreAddress, groupId)
|
||||
|
||||
await feedbackContract.deployed()
|
||||
|
||||
if (logs) {
|
||||
console.info(`Feedback contract has been deployed to: ${feedbackContract.address}`)
|
||||
}
|
||||
|
||||
return feedbackContract
|
||||
})
|
||||
69
packages/cli-template-contracts-hardhat/test/Feedback.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { Group } from "@semaphore-protocol/group"
|
||||
import { Identity } from "@semaphore-protocol/identity"
|
||||
import { generateProof } from "@semaphore-protocol/proof"
|
||||
import { expect } from "chai"
|
||||
import { formatBytes32String } from "ethers/lib/utils"
|
||||
import { run } from "hardhat"
|
||||
// @ts-ignore: typechain folder will be generated after contracts compilation
|
||||
import { Feedback } from "../build/typechain"
|
||||
import { config } from "../package.json"
|
||||
|
||||
describe("Feedback", () => {
|
||||
let feedbackContract: Feedback
|
||||
let semaphoreContract: string
|
||||
|
||||
const groupId = "42"
|
||||
const group = new Group(groupId)
|
||||
const users: Identity[] = []
|
||||
|
||||
before(async () => {
|
||||
const { semaphore } = await run("deploy:semaphore", {
|
||||
logs: false
|
||||
})
|
||||
|
||||
feedbackContract = await run("deploy", { logs: false, group: groupId, semaphore: semaphore.address })
|
||||
semaphoreContract = semaphore
|
||||
|
||||
users.push(new Identity())
|
||||
users.push(new Identity())
|
||||
})
|
||||
|
||||
describe("# joinGroup", () => {
|
||||
it("Should allow users to join the group", async () => {
|
||||
for await (const [i, user] of users.entries()) {
|
||||
const transaction = feedbackContract.joinGroup(user.commitment)
|
||||
|
||||
group.addMember(user.commitment)
|
||||
|
||||
await expect(transaction)
|
||||
.to.emit(semaphoreContract, "MemberAdded")
|
||||
.withArgs(groupId, i, user.commitment, group.root)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("# sendFeedback", () => {
|
||||
const wasmFilePath = `${config.paths.build["snark-artifacts"]}/semaphore.wasm`
|
||||
const zkeyFilePath = `${config.paths.build["snark-artifacts"]}/semaphore.zkey`
|
||||
|
||||
it("Should allow users to send feedback anonymously", async () => {
|
||||
const feedback = formatBytes32String("Hello World")
|
||||
|
||||
const fullProof = await generateProof(users[1], group, groupId, feedback, {
|
||||
wasmFilePath,
|
||||
zkeyFilePath
|
||||
})
|
||||
|
||||
const transaction = feedbackContract.sendFeedback(
|
||||
feedback,
|
||||
fullProof.merkleTreeRoot,
|
||||
fullProof.nullifierHash,
|
||||
fullProof.proof
|
||||
)
|
||||
|
||||
await expect(transaction)
|
||||
.to.emit(semaphoreContract, "ProofVerified")
|
||||
.withArgs(groupId, fullProof.merkleTreeRoot, fullProof.nullifierHash, groupId, fullProof.signal)
|
||||
})
|
||||
})
|
||||
})
|
||||
15
packages/cli-template-contracts-hardhat/tsconfig.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"moduleResolution": "Node",
|
||||
"noImplicitAny": true,
|
||||
"resolveJsonModule": true,
|
||||
"target": "ES2018",
|
||||
"module": "CommonJS",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"outDir": "dist",
|
||||
"typeRoots": ["node_modules/@types", "types"]
|
||||
},
|
||||
"include": ["scripts/**/*", "tasks/**/*", "test/**/*", "build/typechain/**/*", "types/**/*"],
|
||||
"files": ["./hardhat.config.ts"]
|
||||
}
|
||||
13
packages/cli-template-monorepo-ethers/.editorconfig
Normal file
@@ -0,0 +1,13 @@
|
||||
#root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
max_line_length = 120
|
||||
indent_size = 4
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
10
packages/cli-template-monorepo-ethers/.env.example
Normal file
@@ -0,0 +1,10 @@
|
||||
DEFAULT_NETWORK=localhost
|
||||
INFURA_API_KEY=
|
||||
ETHEREUM_PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
|
||||
FEEDBACK_CONTRACT_ADDRESS=0x5fc8d32690cc91d4c39d9d3abcbd16989f875707
|
||||
SEMAPHORE_CONTRACT_ADDRESS=0xdc64a140aa3e981100a9beca4e685f962f0cf6c9
|
||||
OPENZEPPELIN_AUTOTASK_WEBHOOK=
|
||||
GROUP_ID=42
|
||||
REPORT_GAS=false
|
||||
COINMARKETCAP_API_KEY=
|
||||
ETHERSCAN_API_KEY=
|
||||
45
packages/cli-template-monorepo-ethers/.eslintignore
Normal file
@@ -0,0 +1,45 @@
|
||||
# dependencies
|
||||
node_modules
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
.yarn
|
||||
|
||||
# testing
|
||||
coverage
|
||||
coverage.json
|
||||
|
||||
# docs
|
||||
docs
|
||||
|
||||
# types
|
||||
types
|
||||
|
||||
# production
|
||||
dist
|
||||
build
|
||||
cache
|
||||
contract-artifacts
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Next.js
|
||||
.next/
|
||||
out/
|
||||
|
||||
# The Graph
|
||||
generated
|
||||
|
||||
# Auto Generated PWA files
|
||||
**/public/sw.js
|
||||
**/public/workbox-*.js
|
||||
**/public/worker-*.js
|
||||
**/public/sw.js.map
|
||||
**/public/workbox-*.js.map
|
||||
**/public/worker-*.js.map
|
||||
36
packages/cli-template-monorepo-ethers/.eslintrc.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"root": true,
|
||||
"env": {
|
||||
"es6": true
|
||||
},
|
||||
"extends": ["airbnb-base", "airbnb-typescript/base", "prettier"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 6,
|
||||
"sourceType": "module",
|
||||
"project": ["./**/tsconfig.json"]
|
||||
},
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"rules": {
|
||||
"no-underscore-dangle": "off",
|
||||
"no-alert": "off",
|
||||
"no-nested-ternary": "off",
|
||||
"import/no-extraneous-dependencies": "off",
|
||||
"import/extensions": "off",
|
||||
"import/no-relative-packages": "off",
|
||||
"no-await-in-loop": "off",
|
||||
"no-bitwise": "off",
|
||||
"no-restricted-syntax": "off",
|
||||
"no-console": ["warn", { "allow": ["info", "warn", "error"] }],
|
||||
"@typescript-eslint/lines-between-class-members": "off",
|
||||
"no-param-reassign": "off",
|
||||
"@typescript-eslint/naming-convention": [
|
||||
"error",
|
||||
{
|
||||
"selector": "variable",
|
||||
"format": ["camelCase", "PascalCase", "UPPER_CASE", "snake_case"],
|
||||
"leadingUnderscore": "allow"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
122
packages/cli-template-monorepo-ethers/.gitignore
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# IDE
|
||||
.vscode
|
||||
.idea
|
||||
|
||||
# Cargo
|
||||
target
|
||||
|
||||
# Testing
|
||||
coverage
|
||||
coverage.json
|
||||
*.lcov
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# Generate output
|
||||
dist
|
||||
build
|
||||
cache
|
||||
|
||||
# Next.js
|
||||
.next/
|
||||
out/
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
next-env.d.ts
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
*.DS_Store
|
||||
|
||||
# yarn v3
|
||||
.pnp.*
|
||||
.pnp.js
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
|
||||
# The Graph
|
||||
generated
|
||||
|
||||
# Auto Generated PWA files
|
||||
**/public/sw.js
|
||||
**/public/workbox-*.js
|
||||
**/public/worker-*.js
|
||||
**/public/sw.js.map
|
||||
**/public/workbox-*.js.map
|
||||
**/public/worker-*.js.map
|
||||
|
||||
#amplify-do-not-edit-begin
|
||||
amplify/\#current-cloud-backend
|
||||
amplify/.config/local-*
|
||||
amplify/logs
|
||||
amplify/mock-data
|
||||
amplify/mock-api-resources
|
||||
amplify/backend/amplify-meta.json
|
||||
amplify/backend/.temp
|
||||
build/
|
||||
dist/
|
||||
node_modules/
|
||||
aws-exports.js
|
||||
awsconfiguration.json
|
||||
amplifyconfiguration.json
|
||||
amplifyconfiguration.dart
|
||||
amplify-build-config.json
|
||||
amplify-gradle-config.json
|
||||
amplifytools.xcconfig
|
||||
.secret-*
|
||||
**.sample
|
||||
#amplify-do-not-edit-end
|
||||
|
||||
# Others
|
||||
files.tgz
|
||||
45
packages/cli-template-monorepo-ethers/.prettierignore
Normal file
@@ -0,0 +1,45 @@
|
||||
# dependencies
|
||||
node_modules
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
.yarn
|
||||
|
||||
# testing
|
||||
coverage
|
||||
coverage.json
|
||||
|
||||
# docs
|
||||
docs
|
||||
|
||||
# production
|
||||
dist
|
||||
build
|
||||
cache
|
||||
contract-artifacts
|
||||
|
||||
# github
|
||||
.github/ISSUE_TEMPLATE
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Next.js
|
||||
.next/
|
||||
out/
|
||||
|
||||
# The Graph
|
||||
generated
|
||||
|
||||
# Auto Generated PWA files
|
||||
**/public/sw.js
|
||||
**/public/workbox-*.js
|
||||
**/public/worker-*.js
|
||||
**/public/sw.js.map
|
||||
**/public/workbox-*.js.map
|
||||
**/public/worker-*.js.map
|
||||
5
packages/cli-template-monorepo-ethers/.prettierrc.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"semi": false,
|
||||
"arrowParens": "always",
|
||||
"trailingComma": "none"
|
||||
}
|
||||
28
packages/cli-template-monorepo-ethers/.yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
vendored
Normal file
1
packages/cli-template-monorepo-ethers/.yarn/releases/yarn-3.2.1.cjs.REMOVED.git-id
vendored
Normal file
@@ -0,0 +1 @@
|
||||
b3cadff6efb37a12712d12c2553ec703dbcaa4dd
|
||||
9
packages/cli-template-monorepo-ethers/.yarnrc.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
checksumBehavior: update
|
||||
|
||||
nodeLinker: node-modules
|
||||
|
||||
plugins:
|
||||
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
|
||||
spec: "@yarnpkg/plugin-workspace-tools"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.2.1.cjs
|
||||
59
packages/cli-template-monorepo-ethers/README.md
Normal file
@@ -0,0 +1,59 @@
|
||||
# Semaphore Hardhat + Next.js + SemaphoreEthers template
|
||||
|
||||
This project is a complete application that demonstrates a basic Semaphore use case. It comes with a sample contract, a test for that contract and a sample task that deploys that contract. It also contains a frontend to play around with the contract.
|
||||
|
||||
## 📜 Usage
|
||||
|
||||
Copy the `.env.example` file as `.env`:
|
||||
|
||||
```bash
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
and add your environment variables or run the app in a local network.
|
||||
|
||||
### Local server
|
||||
|
||||
You can start your app locally with:
|
||||
|
||||
```bash
|
||||
yarn dev
|
||||
```
|
||||
|
||||
### Deploy the contract
|
||||
|
||||
1. Go to the `apps/contracts` directory and deploy your contract:
|
||||
|
||||
```bash
|
||||
yarn deploy --semaphore <semaphore-address> --group <group-id> --network arbitrum-goerli
|
||||
```
|
||||
|
||||
2. Update your `.env` file with your new contract address, the group id and the semaphore contract address.
|
||||
|
||||
3. Copy your contract artifacts from `apps/contracts/build/contracts/contracts` folder to `apps/web-app/contract-artifacts` folders manually. Or run `yarn copy:contract-artifacts` in the project root to do it automatically.
|
||||
|
||||
> **Note**
|
||||
> Check the Semaphore contract addresses [here](https://semaphore.appliedzkp.org/docs/deployed-contracts).
|
||||
|
||||
> **Warning**
|
||||
> The group id is a number!
|
||||
|
||||
### Code quality and formatting
|
||||
|
||||
Run [ESLint](https://eslint.org/) to analyze the code and catch bugs:
|
||||
|
||||
```bash
|
||||
yarn lint
|
||||
```
|
||||
|
||||
Run [Prettier](https://prettier.io/) to check formatting rules:
|
||||
|
||||
```bash
|
||||
yarn prettier
|
||||
```
|
||||
|
||||
or to automatically format the code:
|
||||
|
||||
```bash
|
||||
yarn prettier:write
|
||||
```
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "solhint:default"
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
//SPDX-License-Identifier: MIT
|
||||
pragma solidity ^0.8.4;
|
||||
|
||||
import "@semaphore-protocol/contracts/interfaces/ISemaphore.sol";
|
||||
|
||||
contract Feedback {
|
||||
ISemaphore public semaphore;
|
||||
|
||||
uint256 public groupId;
|
||||
|
||||
constructor(address semaphoreAddress, uint256 _groupId) {
|
||||
semaphore = ISemaphore(semaphoreAddress);
|
||||
groupId = _groupId;
|
||||
|
||||
semaphore.createGroup(groupId, 20, address(this));
|
||||
}
|
||||
|
||||
function joinGroup(uint256 identityCommitment) external {
|
||||
semaphore.addMember(groupId, identityCommitment);
|
||||
}
|
||||
|
||||
function sendFeedback(
|
||||
uint256 feedback,
|
||||
uint256 merkleTreeRoot,
|
||||
uint256 nullifierHash,
|
||||
uint256[8] calldata proof
|
||||
) external {
|
||||
semaphore.verifyProof(groupId, merkleTreeRoot, feedback, nullifierHash, groupId, proof);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import "@nomicfoundation/hardhat-chai-matchers"
|
||||
import "@nomiclabs/hardhat-ethers"
|
||||
import "@nomiclabs/hardhat-etherscan"
|
||||
import "@semaphore-protocol/hardhat"
|
||||
import "@typechain/hardhat"
|
||||
import { config as dotenvConfig } from "dotenv"
|
||||
import "hardhat-gas-reporter"
|
||||
import { HardhatUserConfig } from "hardhat/config"
|
||||
import { NetworksUserConfig } from "hardhat/types"
|
||||
import { resolve } from "path"
|
||||
import "solidity-coverage"
|
||||
import { config } from "./package.json"
|
||||
import "./tasks/deploy"
|
||||
|
||||
dotenvConfig({ path: resolve(__dirname, "../../.env") })
|
||||
|
||||
function getNetworks(): NetworksUserConfig {
|
||||
if (!process.env.INFURA_API_KEY || !process.env.ETHEREUM_PRIVATE_KEY) {
|
||||
return {}
|
||||
}
|
||||
|
||||
const accounts = [`0x${process.env.ETHEREUM_PRIVATE_KEY}`]
|
||||
const infuraApiKey = process.env.INFURA_API_KEY
|
||||
|
||||
return {
|
||||
goerli: {
|
||||
url: `https://goerli.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 5,
|
||||
accounts
|
||||
},
|
||||
sepolia: {
|
||||
url: `https://sepolia.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 11155111,
|
||||
accounts
|
||||
},
|
||||
mumbai: {
|
||||
url: `https://polygon-mumbai.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 80001,
|
||||
accounts
|
||||
},
|
||||
"optimism-goerli": {
|
||||
url: `https://optimism-goerli.infura.io/v3/${infuraApiKey}`,
|
||||
chainId: 420,
|
||||
accounts
|
||||
},
|
||||
"arbitrum-goerli": {
|
||||
url: "https://goerli-rollup.arbitrum.io/rpc",
|
||||
chainId: 421613,
|
||||
accounts
|
||||
},
|
||||
arbitrum: {
|
||||
url: "https://arb1.arbitrum.io/rpc",
|
||||
chainId: 42161,
|
||||
accounts
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const hardhatConfig: HardhatUserConfig = {
|
||||
solidity: config.solidity,
|
||||
paths: {
|
||||
sources: config.paths.contracts,
|
||||
tests: config.paths.tests,
|
||||
cache: config.paths.cache,
|
||||
artifacts: config.paths.build.contracts
|
||||
},
|
||||
networks: {
|
||||
hardhat: {
|
||||
chainId: 1337
|
||||
},
|
||||
...getNetworks()
|
||||
},
|
||||
gasReporter: {
|
||||
currency: "USD",
|
||||
enabled: process.env.REPORT_GAS === "true",
|
||||
coinmarketcap: process.env.COINMARKETCAP_API_KEY
|
||||
},
|
||||
typechain: {
|
||||
outDir: config.paths.build.typechain,
|
||||
target: "ethers-v5"
|
||||
},
|
||||
etherscan: {
|
||||
apiKey: process.env.ETHERSCAN_API_KEY
|
||||
}
|
||||
}
|
||||
|
||||
export default hardhatConfig
|
||||
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"name": "monorepo-ethers-contracts",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"dev": "hardhat node & yarn compile && yarn deploy --network localhost",
|
||||
"compile": "hardhat compile",
|
||||
"download:snark-artifacts": "hardhat run scripts/download-snark-artifacts.ts",
|
||||
"deploy": "yarn compile && hardhat deploy",
|
||||
"test": "hardhat run scripts/download-snark-artifacts.ts && hardhat test",
|
||||
"test:report-gas": "REPORT_GAS=true hardhat test",
|
||||
"test:coverage": "hardhat coverage",
|
||||
"typechain": "hardhat typechain",
|
||||
"lint": "solhint 'contracts/**/*.sol'"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nomicfoundation/hardhat-chai-matchers": "^1.0.5",
|
||||
"@nomiclabs/hardhat-ethers": "^2.0.0",
|
||||
"@nomiclabs/hardhat-etherscan": "^3.1.7",
|
||||
"@semaphore-protocol/group": "3.11.0",
|
||||
"@semaphore-protocol/hardhat": "3.11.0",
|
||||
"@semaphore-protocol/identity": "3.11.0",
|
||||
"@semaphore-protocol/proof": "3.11.0",
|
||||
"@typechain/ethers-v5": "^10.0.0",
|
||||
"@typechain/hardhat": "^6.0.0",
|
||||
"@types/chai": "^4.3.1",
|
||||
"@types/download": "^8.0.1",
|
||||
"@types/mocha": "^9.1.1",
|
||||
"chai": "^4.2.0",
|
||||
"dotenv": "^14.3.2",
|
||||
"download": "^8.0.0",
|
||||
"ethers": "^5.0.0",
|
||||
"hardhat": "^2.8.4",
|
||||
"hardhat-gas-reporter": "^1.0.8",
|
||||
"prettier-plugin-solidity": "^1.0.0-beta.19",
|
||||
"solhint": "^3.3.6",
|
||||
"solhint-plugin-prettier": "^0.0.5",
|
||||
"solidity-coverage": "^0.7.21",
|
||||
"typechain": "^8.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@semaphore-protocol/contracts": "3.11.0"
|
||||
},
|
||||
"config": {
|
||||
"solidity": {
|
||||
"version": "0.8.4"
|
||||
},
|
||||
"paths": {
|
||||
"contracts": "./contracts",
|
||||
"tests": "./test",
|
||||
"cache": "./cache",
|
||||
"build": {
|
||||
"snark-artifacts": "./build/snark-artifacts",
|
||||
"contracts": "./build/contracts",
|
||||
"typechain": "./build/typechain"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import download from "download"
|
||||
import fs from "fs"
|
||||
import { config } from "../package.json"
|
||||
|
||||
async function main() {
|
||||
const snarkArtifactsPath = config.paths.build["snark-artifacts"]
|
||||
const url = `http://www.trusted-setup-pse.org/semaphore/${20}`
|
||||
|
||||
if (!fs.existsSync(snarkArtifactsPath)) {
|
||||
fs.mkdirSync(snarkArtifactsPath, { recursive: true })
|
||||
}
|
||||
|
||||
if (!fs.existsSync(`${snarkArtifactsPath}/semaphore.zkey`)) {
|
||||
await download(`${url}/semaphore.wasm`, snarkArtifactsPath)
|
||||
await download(`${url}/semaphore.zkey`, snarkArtifactsPath)
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
process.exit(1)
|
||||
})
|
||||
@@ -0,0 +1,31 @@
|
||||
import { task, types } from "hardhat/config"
|
||||
|
||||
task("deploy", "Deploy a Feedback contract")
|
||||
.addOptionalParam("semaphore", "Semaphore contract address", undefined, types.string)
|
||||
.addOptionalParam("group", "Group id", "42", types.string)
|
||||
.addOptionalParam("logs", "Print the logs", true, types.boolean)
|
||||
.setAction(async ({ logs, semaphore: semaphoreAddress, group: groupId }, { ethers, run }) => {
|
||||
if (!semaphoreAddress) {
|
||||
const { semaphore } = await run("deploy:semaphore", {
|
||||
logs
|
||||
})
|
||||
|
||||
semaphoreAddress = semaphore.address
|
||||
}
|
||||
|
||||
if (!groupId) {
|
||||
groupId = process.env.GROUP_ID
|
||||
}
|
||||
|
||||
const FeedbackFactory = await ethers.getContractFactory("Feedback")
|
||||
|
||||
const feedbackContract = await FeedbackFactory.deploy(semaphoreAddress, groupId)
|
||||
|
||||
await feedbackContract.deployed()
|
||||
|
||||
if (logs) {
|
||||
console.info(`Feedback contract has been deployed to: ${feedbackContract.address}`)
|
||||
}
|
||||
|
||||
return feedbackContract
|
||||
})
|
||||
@@ -0,0 +1,69 @@
|
||||
import { Group } from "@semaphore-protocol/group"
|
||||
import { Identity } from "@semaphore-protocol/identity"
|
||||
import { generateProof } from "@semaphore-protocol/proof"
|
||||
import { expect } from "chai"
|
||||
import { formatBytes32String } from "ethers/lib/utils"
|
||||
import { run } from "hardhat"
|
||||
// @ts-ignore: typechain folder will be generated after contracts compilation
|
||||
import { Feedback } from "../build/typechain"
|
||||
import { config } from "../package.json"
|
||||
|
||||
describe("Feedback", () => {
|
||||
let feedbackContract: Feedback
|
||||
let semaphoreContract: string
|
||||
|
||||
const groupId = "42"
|
||||
const group = new Group(groupId)
|
||||
const users: Identity[] = []
|
||||
|
||||
before(async () => {
|
||||
const { semaphore } = await run("deploy:semaphore", {
|
||||
logs: false
|
||||
})
|
||||
|
||||
feedbackContract = await run("deploy", { logs: false, group: groupId, semaphore: semaphore.address })
|
||||
semaphoreContract = semaphore
|
||||
|
||||
users.push(new Identity())
|
||||
users.push(new Identity())
|
||||
})
|
||||
|
||||
describe("# joinGroup", () => {
|
||||
it("Should allow users to join the group", async () => {
|
||||
for await (const [i, user] of users.entries()) {
|
||||
const transaction = feedbackContract.joinGroup(user.commitment)
|
||||
|
||||
group.addMember(user.commitment)
|
||||
|
||||
await expect(transaction)
|
||||
.to.emit(semaphoreContract, "MemberAdded")
|
||||
.withArgs(groupId, i, user.commitment, group.root)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("# sendFeedback", () => {
|
||||
const wasmFilePath = `${config.paths.build["snark-artifacts"]}/semaphore.wasm`
|
||||
const zkeyFilePath = `${config.paths.build["snark-artifacts"]}/semaphore.zkey`
|
||||
|
||||
it("Should allow users to send feedback anonymously", async () => {
|
||||
const feedback = formatBytes32String("Hello World")
|
||||
|
||||
const fullProof = await generateProof(users[1], group, groupId, feedback, {
|
||||
wasmFilePath,
|
||||
zkeyFilePath
|
||||
})
|
||||
|
||||
const transaction = feedbackContract.sendFeedback(
|
||||
feedback,
|
||||
fullProof.merkleTreeRoot,
|
||||
fullProof.nullifierHash,
|
||||
fullProof.proof
|
||||
)
|
||||
|
||||
await expect(transaction)
|
||||
.to.emit(semaphoreContract, "ProofVerified")
|
||||
.withArgs(groupId, fullProof.merkleTreeRoot, fullProof.nullifierHash, groupId, fullProof.signal)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"moduleResolution": "Node",
|
||||
"noImplicitAny": true,
|
||||
"resolveJsonModule": true,
|
||||
"target": "ES2018",
|
||||
"module": "CommonJS",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"outDir": "dist",
|
||||
"typeRoots": ["node_modules/@types", "types"]
|
||||
},
|
||||
"include": ["scripts/**/*", "tasks/**/*", "test/**/*", "build/typechain/**/*", "types/**/*"],
|
||||
"files": ["./hardhat.config.ts"]
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
{
|
||||
"_format": "hh-sol-artifact-1",
|
||||
"contractName": "Feedback",
|
||||
"sourceName": "contracts/Feedback.sol",
|
||||
"abi": [
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "address",
|
||||
"name": "semaphoreAddress",
|
||||
"type": "address"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "_groupId",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "constructor"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "groupId",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "identityCommitment",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"name": "joinGroup",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [],
|
||||
"name": "semaphore",
|
||||
"outputs": [
|
||||
{
|
||||
"internalType": "contract ISemaphore",
|
||||
"name": "",
|
||||
"type": "address"
|
||||
}
|
||||
],
|
||||
"stateMutability": "view",
|
||||
"type": "function"
|
||||
},
|
||||
{
|
||||
"inputs": [
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "feedback",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "merkleTreeRoot",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256",
|
||||
"name": "nullifierHash",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"internalType": "uint256[8]",
|
||||
"name": "proof",
|
||||
"type": "uint256[8]"
|
||||
}
|
||||
],
|
||||
"name": "sendFeedback",
|
||||
"outputs": [],
|
||||
"stateMutability": "nonpayable",
|
||||
"type": "function"
|
||||
}
|
||||
],
|
||||
"bytecode": "0x608060405234801561001057600080fd5b506040516106e13803806106e18339818101604052810190610032919061013c565b816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508060018190555060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16639c1121416001546014306040518463ffffffff1660e01b81526004016100d9939291906101a5565b600060405180830381600087803b1580156100f357600080fd5b505af1158015610107573d6000803e3d6000fd5b505050505050610258565b6000815190506101218161022a565b92915050565b60008151905061013681610241565b92915050565b6000806040838503121561014f57600080fd5b600061015d85828601610112565b925050602061016e85828601610127565b9150509250929050565b610181816101dc565b82525050565b61019081610218565b82525050565b61019f8161020e565b82525050565b60006060820190506101ba6000830186610196565b6101c76020830185610187565b6101d46040830184610178565b949350505050565b60006101e7826101ee565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006102238261020e565b9050919050565b610233816101dc565b811461023e57600080fd5b50565b61024a8161020e565b811461025557600080fd5b50565b61047a806102676000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80637b5d253414610051578063a0f44c921461006f578063d18ed1e91461008d578063eed02e4b146100a9575b600080fd5b6100596100c5565b604051610066919061030f565b60405180910390f35b6100776100e9565b604051610084919061032a565b60405180910390f35b6100a760048036038101906100a2919061027c565b6100ef565b005b6100c360048036038101906100be9190610253565b61018e565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60015481565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633bc778e3600154858786600154876040518763ffffffff1660e01b81526004016101569695949392919061036e565b600060405180830381600087803b15801561017057600080fd5b505af1158015610184573d6000803e3d6000fd5b5050505050505050565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631783efc3600154836040518363ffffffff1660e01b81526004016101eb929190610345565b600060405180830381600087803b15801561020557600080fd5b505af1158015610219573d6000803e3d6000fd5b5050505050565b60008190508260206008028201111561023857600080fd5b92915050565b60008135905061024d8161042d565b92915050565b60006020828403121561026557600080fd5b60006102738482850161023e565b91505092915050565b600080600080610160858703121561029357600080fd5b60006102a18782880161023e565b94505060206102b28782880161023e565b93505060406102c38782880161023e565b92505060606102d487828801610220565b91505092959194509250565b6102ed610100838361041e565b5050565b6102fa816103fa565b82525050565b610309816103f0565b82525050565b600060208201905061032460008301846102f1565b92915050565b600060208201905061033f6000830184610300565b92915050565b600060408201905061035a6000830185610300565b6103676020830184610300565b9392505050565b60006101a0820190506103846000830189610300565b6103916020830188610300565b61039e6040830187610300565b6103ab6060830186610300565b6103b86080830185610300565b6103c560a08301846102e0565b979650505050505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006104058261040c565b9050919050565b6000610417826103d0565b9050919050565b82818337600083830152505050565b610436816103f0565b811461044157600080fd5b5056fea26469706673582212204d8dc3161abc759242364c3a754a86e5eb8653092bcdf1e20bd6fcd368e1997664736f6c63430008040033",
|
||||
"deployedBytecode": "0x608060405234801561001057600080fd5b506004361061004c5760003560e01c80637b5d253414610051578063a0f44c921461006f578063d18ed1e91461008d578063eed02e4b146100a9575b600080fd5b6100596100c5565b604051610066919061030f565b60405180910390f35b6100776100e9565b604051610084919061032a565b60405180910390f35b6100a760048036038101906100a2919061027c565b6100ef565b005b6100c360048036038101906100be9190610253565b61018e565b005b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60015481565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633bc778e3600154858786600154876040518763ffffffff1660e01b81526004016101569695949392919061036e565b600060405180830381600087803b15801561017057600080fd5b505af1158015610184573d6000803e3d6000fd5b5050505050505050565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16631783efc3600154836040518363ffffffff1660e01b81526004016101eb929190610345565b600060405180830381600087803b15801561020557600080fd5b505af1158015610219573d6000803e3d6000fd5b5050505050565b60008190508260206008028201111561023857600080fd5b92915050565b60008135905061024d8161042d565b92915050565b60006020828403121561026557600080fd5b60006102738482850161023e565b91505092915050565b600080600080610160858703121561029357600080fd5b60006102a18782880161023e565b94505060206102b28782880161023e565b93505060406102c38782880161023e565b92505060606102d487828801610220565b91505092959194509250565b6102ed610100838361041e565b5050565b6102fa816103fa565b82525050565b610309816103f0565b82525050565b600060208201905061032460008301846102f1565b92915050565b600060208201905061033f6000830184610300565b92915050565b600060408201905061035a6000830185610300565b6103676020830184610300565b9392505050565b60006101a0820190506103846000830189610300565b6103916020830188610300565b61039e6040830187610300565b6103ab6060830186610300565b6103b86080830185610300565b6103c560a08301846102e0565b979650505050505050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b60006104058261040c565b9050919050565b6000610417826103d0565b9050919050565b82818337600083830152505050565b610436816103f0565b811461044157600080fd5b5056fea26469706673582212204d8dc3161abc759242364c3a754a86e5eb8653092bcdf1e20bd6fcd368e1997664736f6c63430008040033",
|
||||
"linkReferences": {},
|
||||
"deployedLinkReferences": {}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
|
||||
const fs = require("fs")
|
||||
const withPWA = require("next-pwa")
|
||||
|
||||
if (!fs.existsSync("./.env")) {
|
||||
// eslint-disable-next-line global-require
|
||||
require("dotenv").config({ path: "../../.env" })
|
||||
}
|
||||
|
||||
const nextConfig = withPWA({
|
||||
dest: "public",
|
||||
disable: process.env.NODE_ENV === "development"
|
||||
})({
|
||||
eslint: {
|
||||
ignoreDuringBuilds: true
|
||||
},
|
||||
reactStrictMode: true,
|
||||
swcMinify: true,
|
||||
env: {
|
||||
DEFAULT_NETWORK: process.env.DEFAULT_NETWORK,
|
||||
INFURA_API_KEY: process.env.INFURA_API_KEY,
|
||||
ETHEREUM_PRIVATE_KEY: process.env.ETHEREUM_PRIVATE_KEY,
|
||||
FEEDBACK_CONTRACT_ADDRESS: process.env.FEEDBACK_CONTRACT_ADDRESS,
|
||||
SEMAPHORE_CONTRACT_ADDRESS: process.env.SEMAPHORE_CONTRACT_ADDRESS
|
||||
},
|
||||
publicRuntimeConfig: {
|
||||
DEFAULT_NETWORK: process.env.DEFAULT_NETWORK,
|
||||
FEEDBACK_CONTRACT_ADDRESS: process.env.FEEDBACK_CONTRACT_ADDRESS,
|
||||
SEMAPHORE_CONTRACT_ADDRESS: process.env.SEMAPHORE_CONTRACT_ADDRESS,
|
||||
OPENZEPPELIN_AUTOTASK_WEBHOOK: process.env.OPENZEPPELIN_AUTOTASK_WEBHOOK,
|
||||
GROUP_ID: process.env.GROUP_ID
|
||||
},
|
||||
webpack: (config, { isServer }) => {
|
||||
if (!isServer) {
|
||||
config.resolve.fallback = {
|
||||
fs: false
|
||||
}
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = nextConfig
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "monorepo-ethers-web-app",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"export": "next export",
|
||||
"start": "next start"
|
||||
},
|
||||
"dependencies": {
|
||||
"@next/font": "13.0.3",
|
||||
"@semaphore-protocol/data": "3.11.0",
|
||||
"@semaphore-protocol/group": "3.11.0",
|
||||
"@semaphore-protocol/identity": "3.11.0",
|
||||
"@semaphore-protocol/proof": "3.11.0",
|
||||
"@types/react": "18.0.25",
|
||||
"@types/react-dom": "18.0.8",
|
||||
"dotenv": "^16.0.3",
|
||||
"ethers": "^5.7.2",
|
||||
"next": "13.0.3",
|
||||
"next-pwa": "^5.6.0",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"typescript": "^4.7.3"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 376 B |
|
After Width: | Height: | Size: 827 B |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 7.0 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"theme_color": "#ebedff",
|
||||
"background_color": "#ebedff",
|
||||
"display": "standalone",
|
||||
"scope": "/",
|
||||
"start_url": "/",
|
||||
"name": "Semaphore Boilerplate",
|
||||
"short_name": "Semaphore",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icon-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/icon-256x256.png",
|
||||
"sizes": "256x256",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/icon-384x384.png",
|
||||
"sizes": "384x384",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/icon-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
export type StepperProps = {
|
||||
step: number
|
||||
onPrevClick?: () => void
|
||||
onNextClick?: () => void
|
||||
}
|
||||
|
||||
export default function Stepper({ step, onPrevClick, onNextClick }: StepperProps) {
|
||||
return (
|
||||
<div className="stepper">
|
||||
{onPrevClick !== undefined ? (
|
||||
<button className="button-stepper" disabled={!onPrevClick} onClick={onPrevClick || undefined}>
|
||||
Prev
|
||||
</button>
|
||||
) : (
|
||||
<span></span>
|
||||
)}
|
||||
|
||||
<p>{step.toString()}/3</p>
|
||||
|
||||
{onNextClick !== undefined ? (
|
||||
<button className="button-stepper" disabled={!onNextClick} onClick={onNextClick || undefined}>
|
||||
Next
|
||||
</button>
|
||||
) : (
|
||||
<span></span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import React from "react"
|
||||
|
||||
export type LogsContextType = {
|
||||
_logs: string
|
||||
setLogs: (logs: string) => void
|
||||
}
|
||||
|
||||
export default React.createContext<LogsContextType>({
|
||||
_logs: "",
|
||||
setLogs: (logs: string) => logs
|
||||
})
|
||||
@@ -0,0 +1,19 @@
|
||||
import React from "react"
|
||||
|
||||
export type SemaphoreContextType = {
|
||||
_users: string[]
|
||||
_feedback: string[]
|
||||
refreshUsers: () => Promise<void>
|
||||
addUser: (user: string) => void
|
||||
refreshFeedback: () => Promise<void>
|
||||
addFeedback: (feedback: string) => void
|
||||
}
|
||||
|
||||
export default React.createContext<SemaphoreContextType>({
|
||||
_users: [],
|
||||
_feedback: [],
|
||||
refreshUsers: () => Promise.resolve(),
|
||||
addUser: () => {},
|
||||
refreshFeedback: () => Promise.resolve(),
|
||||
addFeedback: () => {}
|
||||
})
|
||||
@@ -0,0 +1,57 @@
|
||||
import { SemaphoreEthers } from "@semaphore-protocol/data"
|
||||
import { BigNumber, utils } from "ethers"
|
||||
import getNextConfig from "next/config"
|
||||
import { useCallback, useState } from "react"
|
||||
import { SemaphoreContextType } from "../context/SemaphoreContext"
|
||||
|
||||
const { publicRuntimeConfig: env } = getNextConfig()
|
||||
|
||||
const ethereumNetwork = env.DEFAULT_NETWORK === "localhost" ? "http://localhost:8545" : env.DEFAULT_NETWORK
|
||||
|
||||
export default function useSemaphore(): SemaphoreContextType {
|
||||
const [_users, setUsers] = useState<any[]>([])
|
||||
const [_feedback, setFeedback] = useState<string[]>([])
|
||||
|
||||
const refreshUsers = useCallback(async (): Promise<void> => {
|
||||
const semaphore = new SemaphoreEthers(ethereumNetwork, {
|
||||
address: env.SEMAPHORE_CONTRACT_ADDRESS
|
||||
})
|
||||
|
||||
const members = await semaphore.getGroupMembers(env.GROUP_ID)
|
||||
|
||||
setUsers(members)
|
||||
}, [])
|
||||
|
||||
const addUser = useCallback(
|
||||
(user: any) => {
|
||||
setUsers([..._users, user])
|
||||
},
|
||||
[_users]
|
||||
)
|
||||
|
||||
const refreshFeedback = useCallback(async (): Promise<void> => {
|
||||
const semaphore = new SemaphoreEthers(ethereumNetwork, {
|
||||
address: env.SEMAPHORE_CONTRACT_ADDRESS
|
||||
})
|
||||
|
||||
const proofs = await semaphore.getGroupVerifiedProofs(env.GROUP_ID)
|
||||
|
||||
setFeedback(proofs.map(({ signal }: any) => utils.parseBytes32String(BigNumber.from(signal).toHexString())))
|
||||
}, [])
|
||||
|
||||
const addFeedback = useCallback(
|
||||
(feedback: string) => {
|
||||
setFeedback([..._feedback, feedback])
|
||||
},
|
||||
[_feedback]
|
||||
)
|
||||
|
||||
return {
|
||||
_users,
|
||||
_feedback,
|
||||
refreshUsers,
|
||||
addUser,
|
||||
refreshFeedback,
|
||||
addFeedback
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
import "../styles/globals.css"
|
||||
import type { AppProps } from "next/app"
|
||||
import Head from "next/head"
|
||||
import { useRouter } from "next/router"
|
||||
import { useEffect, useState } from "react"
|
||||
import LogsContext from "../context/LogsContext"
|
||||
import SemaphoreContext from "../context/SemaphoreContext"
|
||||
import useSemaphore from "../hooks/useSemaphore"
|
||||
import { Inter } from "@next/font/google"
|
||||
|
||||
const inter = Inter({ subsets: ["latin"] })
|
||||
|
||||
export default function App({ Component, pageProps }: AppProps) {
|
||||
const router = useRouter()
|
||||
const semaphore = useSemaphore()
|
||||
const [_logs, setLogs] = useState<string>("")
|
||||
|
||||
useEffect(() => {
|
||||
semaphore.refreshUsers()
|
||||
semaphore.refreshFeedback()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className={inter.className}>
|
||||
<Head>
|
||||
<title>Semaphore template</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<meta name="theme-color" content="#ebedff" />
|
||||
</Head>
|
||||
|
||||
<div>
|
||||
<div className="container">
|
||||
<div id="body">
|
||||
<SemaphoreContext.Provider value={semaphore}>
|
||||
<LogsContext.Provider
|
||||
value={{
|
||||
_logs,
|
||||
setLogs
|
||||
}}
|
||||
>
|
||||
<Component {...pageProps} />
|
||||
</LogsContext.Provider>
|
||||
</SemaphoreContext.Provider>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="footer">
|
||||
{_logs.endsWith("...")}
|
||||
<p>{_logs || `Current step: ${router.route}`}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import { Contract, providers, Wallet } from "ethers"
|
||||
import type { NextApiRequest, NextApiResponse } from "next"
|
||||
import Feedback from "../../../contract-artifacts/Feedback.json"
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (typeof process.env.FEEDBACK_CONTRACT_ADDRESS !== "string") {
|
||||
throw new Error("Please, define FEEDBACK_CONTRACT_ADDRESS in your .env file")
|
||||
}
|
||||
|
||||
if (typeof process.env.DEFAULT_NETWORK !== "string") {
|
||||
throw new Error("Please, define DEFAULT_NETWORK in your .env file")
|
||||
}
|
||||
|
||||
if (typeof process.env.INFURA_API_KEY !== "string") {
|
||||
throw new Error("Please, define INFURA_API_KEY in your .env file")
|
||||
}
|
||||
|
||||
if (typeof process.env.ETHEREUM_PRIVATE_KEY !== "string") {
|
||||
throw new Error("Please, define ETHEREUM_PRIVATE_KEY in your .env file")
|
||||
}
|
||||
|
||||
const ethereumPrivateKey = process.env.ETHEREUM_PRIVATE_KEY
|
||||
const ethereumNetwork = process.env.DEFAULT_NETWORK
|
||||
const infuraApiKey = process.env.INFURA_API_KEY
|
||||
const contractAddress = process.env.FEEDBACK_CONTRACT_ADDRESS
|
||||
|
||||
const provider =
|
||||
ethereumNetwork === "localhost"
|
||||
? new providers.JsonRpcProvider()
|
||||
: new providers.InfuraProvider(ethereumNetwork, infuraApiKey)
|
||||
|
||||
const signer = new Wallet(ethereumPrivateKey, provider)
|
||||
const contract = new Contract(contractAddress, Feedback.abi, signer)
|
||||
|
||||
const { feedback, merkleTreeRoot, nullifierHash, proof } = req.body
|
||||
|
||||
try {
|
||||
const transaction = await contract.sendFeedback(feedback, merkleTreeRoot, nullifierHash, proof)
|
||||
|
||||
await transaction.wait()
|
||||
|
||||
res.status(200).end()
|
||||
} catch (error: any) {
|
||||
console.error(error)
|
||||
|
||||
res.status(500).end()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import { Contract, providers, Wallet } from "ethers"
|
||||
import type { NextApiRequest, NextApiResponse } from "next"
|
||||
import Feedback from "../../../contract-artifacts/Feedback.json"
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (typeof process.env.FEEDBACK_CONTRACT_ADDRESS !== "string") {
|
||||
throw new Error("Please, define FEEDBACK_CONTRACT_ADDRESS in your .env file")
|
||||
}
|
||||
|
||||
if (typeof process.env.DEFAULT_NETWORK !== "string") {
|
||||
throw new Error("Please, define DEFAULT_NETWORK in your .env file")
|
||||
}
|
||||
|
||||
if (typeof process.env.INFURA_API_KEY !== "string") {
|
||||
throw new Error("Please, define INFURA_API_KEY in your .env file")
|
||||
}
|
||||
|
||||
if (typeof process.env.ETHEREUM_PRIVATE_KEY !== "string") {
|
||||
throw new Error("Please, define ETHEREUM_PRIVATE_KEY in your .env file")
|
||||
}
|
||||
|
||||
const ethereumPrivateKey = process.env.ETHEREUM_PRIVATE_KEY
|
||||
const ethereumNetwork = process.env.DEFAULT_NETWORK
|
||||
const infuraApiKey = process.env.INFURA_API_KEY
|
||||
const contractAddress = process.env.FEEDBACK_CONTRACT_ADDRESS
|
||||
|
||||
const provider =
|
||||
ethereumNetwork === "localhost"
|
||||
? new providers.JsonRpcProvider()
|
||||
: new providers.InfuraProvider(ethereumNetwork, infuraApiKey)
|
||||
|
||||
const signer = new Wallet(ethereumPrivateKey, provider)
|
||||
const contract = new Contract(contractAddress, Feedback.abi, signer)
|
||||
|
||||
const { identityCommitment } = req.body
|
||||
|
||||
try {
|
||||
const transaction = await contract.joinGroup(identityCommitment)
|
||||
|
||||
await transaction.wait()
|
||||
|
||||
res.status(200).end()
|
||||
} catch (error: any) {
|
||||
console.error(error)
|
||||
|
||||
res.status(500).end()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
import { Identity } from "@semaphore-protocol/identity"
|
||||
import getNextConfig from "next/config"
|
||||
import { useRouter } from "next/router"
|
||||
import { useCallback, useContext, useEffect, useState } from "react"
|
||||
import Feedback from "../../contract-artifacts/Feedback.json"
|
||||
import Stepper from "../components/Stepper"
|
||||
import LogsContext from "../context/LogsContext"
|
||||
import SemaphoreContext from "../context/SemaphoreContext"
|
||||
|
||||
const { publicRuntimeConfig: env } = getNextConfig()
|
||||
|
||||
export default function GroupsPage() {
|
||||
const router = useRouter()
|
||||
const { setLogs } = useContext(LogsContext)
|
||||
const { _users, refreshUsers, addUser } = useContext(SemaphoreContext)
|
||||
const [_loading, setLoading] = useState(false)
|
||||
const [_identity, setIdentity] = useState<Identity>()
|
||||
|
||||
useEffect(() => {
|
||||
const identityString = localStorage.getItem("identity")
|
||||
|
||||
if (!identityString) {
|
||||
router.push("/")
|
||||
return
|
||||
}
|
||||
|
||||
setIdentity(new Identity(identityString))
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (_users.length > 0) {
|
||||
setLogs(`${_users.length} user${_users.length > 1 ? "s" : ""} retrieved from the group 🤙🏽`)
|
||||
}
|
||||
}, [_users])
|
||||
|
||||
const joinGroup = useCallback(async () => {
|
||||
if (!_identity) {
|
||||
return
|
||||
}
|
||||
|
||||
setLoading(true)
|
||||
setLogs(`Joining the Feedback group...`)
|
||||
|
||||
let response: any
|
||||
|
||||
if (env.OPENZEPPELIN_AUTOTASK_WEBHOOK) {
|
||||
response = await fetch(env.OPENZEPPELIN_AUTOTASK_WEBHOOK, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
abi: Feedback.abi,
|
||||
address: env.FEEDBACK_CONTRACT_ADDRESS,
|
||||
functionName: "joinGroup",
|
||||
functionParameters: [_identity.commitment.toString()]
|
||||
})
|
||||
})
|
||||
} else {
|
||||
response = await fetch("api/join", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
identityCommitment: _identity.commitment.toString()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if (response.status === 200) {
|
||||
addUser(_identity.commitment.toString())
|
||||
|
||||
setLogs(`You joined the Feedback group event 🎉 Share your feedback anonymously!`)
|
||||
} else {
|
||||
setLogs("Some error occurred, please try again!")
|
||||
}
|
||||
|
||||
setLoading(false)
|
||||
}, [_identity])
|
||||
|
||||
const userHasJoined = useCallback((identity: Identity) => _users.includes(identity.commitment.toString()), [_users])
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>Groups</h2>
|
||||
|
||||
<p>
|
||||
Semaphore{" "}
|
||||
<a
|
||||
href="https://semaphore.appliedzkp.org/docs/guides/groups"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener nofollow"
|
||||
>
|
||||
groups
|
||||
</a>{" "}
|
||||
are binary incremental Merkle trees in which each leaf contains an identity commitment for a user.
|
||||
Groups can be abstracted to represent events, polls, or organizations.
|
||||
</p>
|
||||
|
||||
<div className="divider"></div>
|
||||
|
||||
<div className="text-top">
|
||||
<h3>Feedback users ({_users.length})</h3>
|
||||
<button className="button-link" onClick={refreshUsers}>
|
||||
Refresh
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button
|
||||
className="button"
|
||||
onClick={joinGroup}
|
||||
disabled={_loading || !_identity || userHasJoined(_identity)}
|
||||
>
|
||||
<span>Join group</span>
|
||||
{_loading && <div className="loader"></div>}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{_users.length > 0 && (
|
||||
<div>
|
||||
{_users.map((user, i) => (
|
||||
<div key={i}>
|
||||
<p className="box box-text">{user}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="divider"></div>
|
||||
|
||||
<Stepper
|
||||
step={2}
|
||||
onPrevClick={() => router.push("/")}
|
||||
onNextClick={_identity && userHasJoined(_identity) ? () => router.push("/proofs") : undefined}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import { Identity } from "@semaphore-protocol/identity"
|
||||
import { useRouter } from "next/router"
|
||||
import { useCallback, useContext, useEffect, useState } from "react"
|
||||
import Stepper from "../components/Stepper"
|
||||
import LogsContext from "../context/LogsContext"
|
||||
|
||||
export default function IdentitiesPage() {
|
||||
const router = useRouter()
|
||||
const { setLogs } = useContext(LogsContext)
|
||||
const [_identity, setIdentity] = useState<Identity>()
|
||||
|
||||
useEffect(() => {
|
||||
const identityString = localStorage.getItem("identity")
|
||||
|
||||
if (identityString) {
|
||||
const identity = new Identity(identityString)
|
||||
|
||||
setIdentity(identity)
|
||||
|
||||
setLogs("Your Semaphore identity was retrieved from the browser cache 👌🏽")
|
||||
} else {
|
||||
setLogs("Create your Semaphore identity 👆🏽")
|
||||
}
|
||||
}, [])
|
||||
|
||||
const createIdentity = useCallback(async () => {
|
||||
const identity = new Identity()
|
||||
|
||||
setIdentity(identity)
|
||||
|
||||
localStorage.setItem("identity", identity.toString())
|
||||
|
||||
setLogs("Your new Semaphore identity was just created 🎉")
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2 className="font-size: 3rem;">Identities</h2>
|
||||
|
||||
<p>
|
||||
Users interact with the protocol using a Semaphore{" "}
|
||||
<a
|
||||
href="https://semaphore.appliedzkp.org/docs/guides/identities"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener nofollow"
|
||||
>
|
||||
identity
|
||||
</a>{" "}
|
||||
(similar to Ethereum accounts). It contains three values:
|
||||
</p>
|
||||
<ol>
|
||||
<li>Trapdoor: private, known only by user</li>
|
||||
<li>Nullifier: private, known only by user</li>
|
||||
<li>Commitment: public</li>
|
||||
</ol>
|
||||
|
||||
<div className="divider"></div>
|
||||
|
||||
<div className="text-top">
|
||||
<h3>Identity</h3>
|
||||
{_identity && (
|
||||
<button className="button-link" onClick={createIdentity}>
|
||||
New
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{_identity ? (
|
||||
<div>
|
||||
<div className="box">
|
||||
<p className="box-text">Trapdoor: {_identity.trapdoor.toString()}</p>
|
||||
<p className="box-text">Nullifier: {_identity.nullifier.toString()}</p>
|
||||
<p className="box-text">Commitment: {_identity.commitment.toString()}</p>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<button className="button" onClick={createIdentity}>
|
||||
Create identity
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="divider"></div>
|
||||
|
||||
<Stepper step={1} onNextClick={_identity && (() => router.push("/groups"))} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
import { Group } from "@semaphore-protocol/group"
|
||||
import { Identity } from "@semaphore-protocol/identity"
|
||||
import { generateProof } from "@semaphore-protocol/proof"
|
||||
import { BigNumber, utils } from "ethers"
|
||||
import getNextConfig from "next/config"
|
||||
import { useRouter } from "next/router"
|
||||
import { useCallback, useContext, useEffect, useState } from "react"
|
||||
import Feedback from "../../contract-artifacts/Feedback.json"
|
||||
import Stepper from "../components/Stepper"
|
||||
import LogsContext from "../context/LogsContext"
|
||||
import SemaphoreContext from "../context/SemaphoreContext"
|
||||
|
||||
const { publicRuntimeConfig: env } = getNextConfig()
|
||||
|
||||
export default function ProofsPage() {
|
||||
const router = useRouter()
|
||||
const { setLogs } = useContext(LogsContext)
|
||||
const { _users, _feedback, refreshFeedback, addFeedback } = useContext(SemaphoreContext)
|
||||
const [_loading, setLoading] = useState(false)
|
||||
const [_identity, setIdentity] = useState<Identity>()
|
||||
|
||||
useEffect(() => {
|
||||
const identityString = localStorage.getItem("identity")
|
||||
|
||||
if (!identityString) {
|
||||
router.push("/")
|
||||
return
|
||||
}
|
||||
|
||||
setIdentity(new Identity(identityString))
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (_feedback.length > 0) {
|
||||
setLogs(`${_feedback.length} feedback retrieved from the group 🤙🏽`)
|
||||
}
|
||||
}, [_feedback])
|
||||
|
||||
const sendFeedback = useCallback(async () => {
|
||||
if (!_identity) {
|
||||
return
|
||||
}
|
||||
|
||||
const feedback = prompt("Please enter your feedback:")
|
||||
|
||||
if (feedback && _users) {
|
||||
setLoading(true)
|
||||
|
||||
setLogs(`Posting your anonymous feedback...`)
|
||||
|
||||
try {
|
||||
const group = new Group(env.GROUP_ID)
|
||||
|
||||
const signal = BigNumber.from(utils.formatBytes32String(feedback)).toString()
|
||||
|
||||
group.addMembers(_users)
|
||||
|
||||
const { proof, merkleTreeRoot, nullifierHash } = await generateProof(
|
||||
_identity,
|
||||
group,
|
||||
env.GROUP_ID,
|
||||
signal
|
||||
)
|
||||
|
||||
let response: any
|
||||
|
||||
if (env.OPENZEPPELIN_AUTOTASK_WEBHOOK) {
|
||||
response = await fetch(env.OPENZEPPELIN_AUTOTASK_WEBHOOK, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
abi: Feedback.abi,
|
||||
address: env.FEEDBACK_CONTRACT_ADDRESS,
|
||||
functionName: "sendFeedback",
|
||||
functionParameters: [signal, merkleTreeRoot, nullifierHash, proof]
|
||||
})
|
||||
})
|
||||
} else {
|
||||
response = await fetch("api/feedback", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
feedback: signal,
|
||||
merkleTreeRoot,
|
||||
nullifierHash,
|
||||
proof
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if (response.status === 200) {
|
||||
addFeedback(feedback)
|
||||
|
||||
setLogs(`Your feedback was posted 🎉`)
|
||||
} else {
|
||||
setLogs("Some error occurred, please try again!")
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
|
||||
setLogs("Some error occurred, please try again!")
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
}, [_identity])
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2>Proofs</h2>
|
||||
|
||||
<p>
|
||||
Semaphore members can anonymously{" "}
|
||||
<a
|
||||
href="https://semaphore.appliedzkp.org/docs/guides/proofs"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener nofollow"
|
||||
>
|
||||
prove
|
||||
</a>{" "}
|
||||
that they are part of a group and that they are generating their own signals. Signals could be anonymous
|
||||
votes, leaks, reviews, or feedback.
|
||||
</p>
|
||||
|
||||
<div className="divider"></div>
|
||||
|
||||
<div className="text-top">
|
||||
<h3>Feedback signals ({_feedback.length})</h3>
|
||||
<button className="button-link" onClick={refreshFeedback}>
|
||||
Refresh
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button className="button" onClick={sendFeedback} disabled={_loading}>
|
||||
<span>Send Feedback</span>
|
||||
{_loading && <div className="loader"></div>}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{_feedback.length > 0 && (
|
||||
<div>
|
||||
{_feedback.map((f, i) => (
|
||||
<div key={i}>
|
||||
<p className="box box-text">{f}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="divider"></div>
|
||||
|
||||
<Stepper step={3} onPrevClick={() => router.push("/groups")} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
:root {
|
||||
--slate100: #f1f5f9;
|
||||
--slate300: #cbd5e1;
|
||||
--slate400: #94a3b8;
|
||||
--slate700: #334155;
|
||||
--blue200: #bfdbfe;
|
||||
--blue600: #2563eb;
|
||||
--blue800: #1e40af;
|
||||
--blue900: #1e3a8a;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
max-width: 100vw;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
color: var(--slate700);
|
||||
}
|
||||
|
||||
p {
|
||||
line-height: 1.5rem;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
max-width: 32rem;
|
||||
|
||||
margin: auto;
|
||||
padding: 1rem;
|
||||
|
||||
min-height: calc(100vh - 3.5rem);
|
||||
}
|
||||
|
||||
.container #body {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.text-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ol {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 2.25rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.divider {
|
||||
height: 1px;
|
||||
background: var(--slate400);
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
.stepper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--blue600);
|
||||
}
|
||||
|
||||
.button {
|
||||
background-color: var(--blue800);
|
||||
width: 100%;
|
||||
padding: 0.8rem 1rem;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
color: var(--slate100);
|
||||
font-size: 1.125rem;
|
||||
font-weight: 700;
|
||||
transition: all 200ms linear;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1.5rem;
|
||||
opacity: 0.9;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 3.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background-color: var(--blue900);
|
||||
}
|
||||
|
||||
.button:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.button:disabled:hover {
|
||||
background-color: var(--blue800);
|
||||
}
|
||||
|
||||
.button-stepper {
|
||||
cursor: pointer;
|
||||
color: var(--blue900);
|
||||
font-size: 1.1rem;
|
||||
border: none;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.button-link {
|
||||
cursor: pointer;
|
||||
color: var(--slate700);
|
||||
font-size: 1.1rem;
|
||||
border: none;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.box {
|
||||
padding: 0.8rem;
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-color: var(--slate300);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.box-text {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
margin: 0.5rem;
|
||||
}
|
||||
|
||||
.footer {
|
||||
background-color: var(--blue200);
|
||||
padding: 1rem 0;
|
||||
|
||||
height: 3.5rem;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.loader {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
border-radius: 50%;
|
||||
border-top: 2px solid var(--slate100);
|
||||
border-right: 2px solid transparent;
|
||||
animation: spin 1s linear infinite;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true
|
||||
},
|
||||
"include": ["next.config.js", "next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
44
packages/cli-template-monorepo-ethers/package.json
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "@semaphore-protocol/cli-template-monorepo-ethers",
|
||||
"version": "3.11.0",
|
||||
"description": "Semaphore Hardhat + Next.js + SemaphoreEthers template.",
|
||||
"license": "Unlicense",
|
||||
"files": [
|
||||
"files.tgz",
|
||||
"scripts/",
|
||||
".editorconfig",
|
||||
".env.example",
|
||||
".eslintignore",
|
||||
".eslintrc.json",
|
||||
".prettierignore",
|
||||
".prettierrc.json",
|
||||
"tsconfig.json",
|
||||
"README.md"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "yarn workspaces foreach -pi run dev",
|
||||
"dev:web-app": "yarn workspace monorepo-ethers-web-app dev",
|
||||
"dev:contracts": "yarn workspace monorepo-ethers-contracts dev",
|
||||
"lint": "eslint . --ext .js,.ts",
|
||||
"prettier": "prettier -c .",
|
||||
"prettier:write": "prettier -w .",
|
||||
"copy:contract-artifacts": "ts-node scripts/copy-contract-artifacts.ts",
|
||||
"prepublish": "tar -czf files.tgz .gitignore .yarn .yarnrc.yml apps"
|
||||
},
|
||||
"workspaces": [
|
||||
"apps/*"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@types/node": "^17.0.9",
|
||||
"@typescript-eslint/eslint-plugin": "^5.9.1",
|
||||
"@typescript-eslint/parser": "^5.9.1",
|
||||
"eslint": "^8.2.0",
|
||||
"eslint-config-airbnb-base": "15.0.0",
|
||||
"eslint-config-airbnb-typescript": "^16.1.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-import": "^2.25.2",
|
||||
"prettier": "^2.5.1",
|
||||
"ts-node": "^10.8.1",
|
||||
"typescript": "^4.7.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import * as fs from "fs"
|
||||
|
||||
async function main() {
|
||||
const contractArtifactsPath = "apps/contracts/build/contracts/contracts/Feedback.sol"
|
||||
const webAppArtifactsPath = "apps/web-app/contract-artifacts"
|
||||
|
||||
await fs.promises.copyFile(`${contractArtifactsPath}/Feedback.json`, `${webAppArtifactsPath}/Feedback.json`)
|
||||
}
|
||||
|
||||
main()
|
||||
.then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
process.exit(1)
|
||||
})
|
||||
3
packages/cli-template-monorepo-ethers/tsconfig.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"include": ["scripts/**/*"]
|
||||
}
|
||||
13
packages/cli-template-monorepo-subgraph/.editorconfig
Normal file
@@ -0,0 +1,13 @@
|
||||
#root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
max_line_length = 120
|
||||
indent_size = 4
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
10
packages/cli-template-monorepo-subgraph/.env.example
Normal file
@@ -0,0 +1,10 @@
|
||||
DEFAULT_NETWORK=localhost
|
||||
INFURA_API_KEY=
|
||||
ETHEREUM_PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
|
||||
FEEDBACK_CONTRACT_ADDRESS=0x5fc8d32690cc91d4c39d9d3abcbd16989f875707
|
||||
SEMAPHORE_CONTRACT_ADDRESS=0xdc64a140aa3e981100a9beca4e685f962f0cf6c9
|
||||
OPENZEPPELIN_AUTOTASK_WEBHOOK=
|
||||
GROUP_ID=42
|
||||
REPORT_GAS=false
|
||||
COINMARKETCAP_API_KEY=
|
||||
ETHERSCAN_API_KEY=
|
||||