Skip to content

Commit e914250

Browse files
authored
Pixel Shield (#428)
* feat: shield ui flow * feat: added shield UI and multicall to register shield * fix: avoid empty pixel call * feat: improve call * fix: allow only one pixel selection * fix: remove unsed import
1 parent af23819 commit e914250

File tree

10 files changed

+783
-206
lines changed

10 files changed

+783
-206
lines changed

backend/cmd/backend/backend.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ func main() {
7878
indexer.InitIndexerRoutes()
7979
routes.InitWebsocketRoutes()
8080
routes.InitNFTStaticRoutes()
81+
indexer.StartMessageProcessor()
8182

8283
core.AFKBackend.Start(core.AFKBackend.BackendConfig.Port)
8384
}

backend/postgres/init.sql

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ CREATE TABLE Pixels (
55
position integer NOT NULL,
66
day integer NOT NULL,
77
color integer NOT NULL,
8-
time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
8+
time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
9+
metadata JSONB DEFAULT NULL
910
);
1011
CREATE INDEX pixels_address_index ON Pixels (address);
1112
CREATE INDEX pixels_position_index ON Pixels (position);

backend/routes/indexer/pixel.go

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package indexer
22

33
import (
44
"context"
5+
"encoding/json"
6+
"fmt"
57
"strconv"
68

79
"github.com/AFK-AlignedFamKernel/afk_monorepo/backend/core"
@@ -52,6 +54,7 @@ func processPixelPlacedEvent(event IndexerEvent) {
5254
return
5355
}
5456

57+
fmt.Printf(address, position, dayIdx, color, "print")
5558
// Set pixel in postgres
5659
_, err = core.AFKBackend.Databases.Postgres.Exec(context.Background(), "INSERT INTO Pixels (address, position, day, color) VALUES ($1, $2, $3, $4)", address, position, dayIdx, color)
5760
if err != nil {
@@ -116,7 +119,6 @@ func revertPixelPlacedEvent(event IndexerEvent) {
116119
func processBasicPixelPlacedEvent(event IndexerEvent) {
117120
address := event.Event.Keys[1][2:] // Remove 0x prefix
118121
timestampHex := event.Event.Data[0]
119-
120122
timestamp, err := strconv.ParseInt(timestampHex, 0, 64)
121123
if err != nil {
122124
PrintIndexerError("processBasicPixelPlacedEvent", "Error converting timestamp hex to int", address, timestampHex)
@@ -234,3 +236,117 @@ func revertExtraPixelsPlacedEvent(event IndexerEvent) {
234236
return
235237
}
236238
}
239+
240+
func processBasicPixelPlacedEventWithMetadata(event IndexerEvent) {
241+
address := event.Event.Keys[1][2:] // Remove 0x prefix
242+
timestampHex := event.Event.Data[0]
243+
timestamp, err := strconv.ParseInt(timestampHex, 0, 64)
244+
if err != nil {
245+
PrintIndexerError("processBasicPixelPlacedEventWithMetadata", "Error converting timestamp hex to int", address, timestampHex)
246+
return
247+
}
248+
249+
// Extract position and color from the event (position is Keys[2], color is in Data[1])
250+
positionHex := event.Event.Keys[2]
251+
position, err := strconv.Atoi(positionHex)
252+
if err != nil {
253+
PrintIndexerError("processBasicPixelPlacedEventWithMetadata", "Error converting position hex to int", address, positionHex)
254+
return
255+
}
256+
257+
colorHex := event.Event.Data[1]
258+
color, err := strconv.Atoi(colorHex)
259+
if err != nil {
260+
PrintIndexerError("processBasicPixelPlacedEventWithMetadata", "Error converting color hex to int", address, colorHex)
261+
return
262+
}
263+
264+
// Extract metadata from the last index in Data (metadata is in Data[n])
265+
metadata := event.Event.Data[len(event.Event.Data)-1]
266+
267+
// Unmarshal metadata (if it exists)
268+
var metadataMap map[string]interface{}
269+
if len(metadata) > 0 {
270+
err = json.Unmarshal([]byte(metadata), &metadataMap)
271+
if err != nil {
272+
PrintIndexerError("processBasicPixelPlacedEventWithMetadata", "Error parsing metadata", address, string(metadata))
273+
return
274+
}
275+
}
276+
277+
// Prepare SQL statement for inserting pixel info and metadata together
278+
metadataJson, err := json.Marshal(metadataMap)
279+
if err != nil {
280+
PrintIndexerError("processBasicPixelPlacedEventWithMetadata", "Error serializing metadata", address, string(metadata))
281+
return
282+
}
283+
284+
// Use a single query to insert the pixel information and metadata into the database
285+
_, err = core.AFKBackend.Databases.Postgres.Exec(context.Background(),
286+
`INSERT INTO Pixels (address, position, color, time)
287+
VALUES ($1, $2, $3, TO_TIMESTAMP($4))
288+
ON CONFLICT (address, position)
289+
DO UPDATE SET color = $3, time = TO_TIMESTAMP($4),
290+
metadata = COALESCE(metadata, $5)`,
291+
address, position, color, timestamp, metadataJson)
292+
if err != nil {
293+
PrintIndexerError("processBasicPixelPlacedEventWithMetadata", "Error inserting/updating pixel and metadata", address, string(metadataJson))
294+
return
295+
}
296+
297+
// Insert or update the last placed time in the LastPlacedTime table
298+
_, err = core.AFKBackend.Databases.Postgres.Exec(context.Background(),
299+
"INSERT INTO LastPlacedTime (address, time) VALUES ($1, TO_TIMESTAMP($2)) ON CONFLICT (address) DO UPDATE SET time = TO_TIMESTAMP($2)",
300+
address, timestamp)
301+
if err != nil {
302+
PrintIndexerError("processBasicPixelPlacedEventWithMetadata", "Error inserting last placed time into postgres", address, timestampHex)
303+
return
304+
}
305+
}
306+
307+
func revertBasicPixelPlacedEventWithMetadata(event IndexerEvent) {
308+
address := event.Event.Keys[1][2:] // Remove 0x prefix
309+
posHex := event.Event.Keys[2]
310+
311+
// Convert hex to int for position
312+
position, err := strconv.ParseInt(posHex, 0, 64)
313+
if err != nil {
314+
PrintIndexerError("revertPixelPlacedEvent", "Error converting position hex to int", address, posHex)
315+
return
316+
}
317+
318+
// We can also retrieve the metadata from the event if needed
319+
metadata := event.Event.Data[len(event.Event.Data)-1]
320+
var metadataMap map[string]interface{}
321+
if len(metadata) > 0 {
322+
err = json.Unmarshal([]byte(metadata), &metadataMap) // Unmarshal from metadata (which is a string) to map
323+
if err != nil {
324+
PrintIndexerError("revertPixelPlacedEvent", "Error parsing metadata", address, string(metadata))
325+
return
326+
}
327+
}
328+
329+
// Delete the pixel entry (including metadata) from the PostgreSQL database
330+
_, err = core.AFKBackend.Databases.Postgres.Exec(context.Background(), `
331+
DELETE FROM Pixels
332+
WHERE address = $1 AND position = $2
333+
ORDER BY time LIMIT 1`, address, position)
334+
if err != nil {
335+
PrintIndexerError("revertPixelPlacedEvent", "Error deleting pixel from postgres", address, posHex)
336+
return
337+
}
338+
339+
// Optionally, you can also delete the metadata from the database,
340+
// but usually deleting the pixel entry will automatically take care of it since metadata is part of the same row.
341+
342+
// Delete the pixel's associated last placed time entry from the LastPlacedTime table
343+
_, err = core.AFKBackend.Databases.Postgres.Exec(context.Background(),
344+
"DELETE FROM LastPlacedTime WHERE address = $1", address)
345+
if err != nil {
346+
PrintIndexerError("revertPixelPlacedEvent", "Error deleting last placed time from postgres", address, posHex)
347+
return
348+
}
349+
350+
// Optionally log the event if needed
351+
fmt.Printf("Pixel at position %d for address %s has been reverted.\n", position, address)
352+
}

backend/routes/pixel.go

Lines changed: 67 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,27 @@ package routes
22

33
import (
44
"context"
5+
"encoding/json"
56
"fmt"
67
"net/http"
78
"os"
89
"os/exec"
910
"strconv"
11+
"time"
1012

1113
"github.com/AFK-AlignedFamKernel/afk_monorepo/backend/core"
1214
routeutils "github.com/AFK-AlignedFamKernel/afk_monorepo/backend/routes/utils"
1315
)
1416

17+
// Define a struct to represent a Pixel record
18+
type Pixel struct {
19+
Address string `json:"address"`
20+
Position int `json:"position"`
21+
Day int `json:"day"`
22+
Color int `json:"color"`
23+
Time time.Time `json:"time"`
24+
}
25+
1526
func InitPixelRoutes() {
1627
http.HandleFunc("/get-pixel", getPixel)
1728
http.HandleFunc("/get-pixel-info", getPixelInfo)
@@ -53,32 +64,78 @@ func getPixel(w http.ResponseWriter, r *http.Request) {
5364
}
5465

5566
type PixelInfo struct {
56-
Address string `json:"address"`
57-
Name string `json:"username"`
67+
Address string `json:"address"`
68+
Name string `json:"username"`
69+
Metadata json.RawMessage `json:"metadata,omitempty"`
5870
}
5971

72+
// func getPixelInfo(w http.ResponseWriter, r *http.Request) {
73+
// position, err := strconv.Atoi(r.URL.Query().Get("position"))
74+
// if err != nil {
75+
// routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid query position")
76+
// return
77+
// }
78+
79+
// queryRes, err := core.PostgresQueryOne[PixelInfo](`
80+
// SELECT p.address, COALESCE(u.name, '') as name FROM Pixels p
81+
// LEFT JOIN Users u ON p.address = u.address WHERE p.position = $1
82+
// ORDER BY p.time DESC LIMIT 1`, position)
83+
// if err != nil {
84+
// routeutils.WriteDataJson(w, "\"0x0000000000000000000000000000000000000000000000000000000000000000\"")
85+
// return
86+
// }
87+
88+
// if queryRes.Name == "" {
89+
// routeutils.WriteDataJson(w, "\"0x"+queryRes.Address+"\"")
90+
// } else {
91+
// routeutils.WriteDataJson(w, "\""+queryRes.Name+"\"")
92+
// }
93+
// }
94+
95+
// New implmentation with metadata
6096
func getPixelInfo(w http.ResponseWriter, r *http.Request) {
6197
position, err := strconv.Atoi(r.URL.Query().Get("position"))
6298
if err != nil {
6399
routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid query position")
64100
return
65101
}
66102

103+
// Update the query to include metadata
67104
queryRes, err := core.PostgresQueryOne[PixelInfo](`
68-
SELECT p.address, COALESCE(u.name, '') as name FROM Pixels p
69-
LEFT JOIN Users u ON p.address = u.address WHERE p.position = $1
70-
ORDER BY p.time DESC LIMIT 1`, position)
105+
SELECT
106+
p.address,
107+
COALESCE(u.name, '') as name,
108+
p.metadata -- Fetch the metadata column as well
109+
FROM Pixels p
110+
LEFT JOIN Users u ON p.address = u.address
111+
WHERE p.position = $1
112+
ORDER BY p.time DESC
113+
LIMIT 1`, position)
71114
if err != nil {
72115
routeutils.WriteDataJson(w, "\"0x0000000000000000000000000000000000000000000000000000000000000000\"")
73116
return
74117
}
75118

119+
// If queryRes.Name is empty, return the address, else return the name
76120
if queryRes.Name == "" {
77-
routeutils.WriteDataJson(w, "\"0x"+queryRes.Address+"\"")
121+
response := "\"0x" + queryRes.Address + "\""
122+
if queryRes.Metadata != nil {
123+
// If metadata exists, include it in the response
124+
metadataJson, _ := json.Marshal(queryRes.Metadata)
125+
response = fmt.Sprintf("{\"address\": \"%s\", \"metadata\": %s}", queryRes.Address, string(metadataJson))
126+
}
127+
routeutils.WriteDataJson(w, response)
78128
} else {
79-
routeutils.WriteDataJson(w, "\""+queryRes.Name+"\"")
129+
response := "\"" + queryRes.Name + "\""
130+
if queryRes.Metadata != nil {
131+
// If metadata exists, include it in the response
132+
metadataJson, _ := json.Marshal(queryRes.Metadata)
133+
response = fmt.Sprintf("{\"name\": \"%s\", \"metadata\": %s}", queryRes.Name, string(metadataJson))
134+
}
135+
routeutils.WriteDataJson(w, response)
80136
}
81137
}
138+
82139
func getPixelMetadata(w http.ResponseWriter, r *http.Request) {
83140
position, err := strconv.Atoi(r.URL.Query().Get("position"))
84141
if err != nil {
@@ -102,7 +159,6 @@ func getPixelMetadata(w http.ResponseWriter, r *http.Request) {
102159
}
103160
}
104161

105-
106162
func placePixelDevnet(w http.ResponseWriter, r *http.Request) {
107163
// Disable this in production
108164
if routeutils.NonProductionMiddleware(w, r) {
@@ -208,7 +264,7 @@ func placePixelRedis(w http.ResponseWriter, r *http.Request) {
208264
}
209265

210266
jsonBody, err := routeutils.ReadJsonBody[map[string]uint](r)
211-
fmt.Println("jsonBody", jsonBody)
267+
fmt.Println("jsonBody", r.Body)
212268

213269
if err != nil {
214270
routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid JSON request body")
@@ -218,6 +274,8 @@ func placePixelRedis(w http.ResponseWriter, r *http.Request) {
218274
position := (*jsonBody)["position"]
219275
color := (*jsonBody)["color"]
220276

277+
fmt.Println("jsonBody", position)
278+
221279
canvasWidth := core.AFKBackend.CanvasConfig.Canvas.Width
222280
canvasHeight := core.AFKBackend.CanvasConfig.Canvas.Height
223281

@@ -250,7 +308,5 @@ func placePixelRedis(w http.ResponseWriter, r *http.Request) {
250308
return
251309
}
252310

253-
fmt.Println("Error redis insert pixel", err.Error())
254-
255311
routeutils.WriteResultJson(w, "Pixel placed on redis")
256312
}

0 commit comments

Comments
 (0)