Skip to content

Commit

Permalink
feat: ColorAdded event (#234)
Browse files Browse the repository at this point in the history
* feat: added the color

* feat: added Color event

* feat: Added the ColorEvent

* feat:Added ColorAdded event

* onchain/src/art_peace.cairo

* src/art_peace.cairo

* feat:fixs

* fix: hot fixes

* Finish integration of new colors added

---------

Co-authored-by: Immanuelolivia1 <[email protected]>
  • Loading branch information
b-j-roberts and Immanuelolivia1 authored Jun 12, 2024
1 parent 2e5fa84 commit 38ed003
Show file tree
Hide file tree
Showing 16 changed files with 151 additions and 64 deletions.
2 changes: 0 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,6 @@ helm-upgrade:
helm upgrade --set postgres.password=$(POSTGRES_PASSWORD) --set deployments.sha=$(COMMIT_SHA) --set apibara.authToken=$(AUTH_TOKEN) art-peace-infra infra/art-peace-infra

init-infra-prod:
$(eval COLORS := $(shell cat configs/canvas.config.json | jq -r '.colors | map("\"\(.)\"") | join(",")'))
@echo "Initializing infra..."
curl https://api.art-peace.net/init-canvas -X POST
curl https://api.art-peace.net/init-colors -X POST -d "[$(COLORS)]"
curl https://api.art-peace.net/init-quests -X POST -d "@configs/production-quests.config.json"
4 changes: 2 additions & 2 deletions backend/routes/colors.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func InitColors(w http.ResponseWriter, r *http.Request) {
}

func GetAllColors(w http.ResponseWriter, r *http.Request) {
colors, err := core.PostgresQueryJson[ColorType]("SELECT hex FROM colors ORDER BY key")
colors, err := core.PostgresQueryJson[ColorType]("SELECT hex FROM colors ORDER BY color_key")
if err != nil {
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to retrieve colors")
return
Expand All @@ -57,7 +57,7 @@ func GetSingleColor(w http.ResponseWriter, r *http.Request) {
return
}

color, err := core.PostgresQueryOne[ColorType]("SELECT hex FROM colors WHERE key = $1", colorKey)
color, err := core.PostgresQueryOne[ColorType]("SELECT hex FROM colors WHERE color_key = $1", colorKey)
if err != nil {
routeutils.WriteErrorJson(w, http.StatusNotFound, "Color not found")
return
Expand Down
45 changes: 45 additions & 0 deletions backend/routes/indexer/color.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package indexer

import (
"context"
"strconv"

"github.com/keep-starknet-strange/art-peace/backend/core"
)

func processColorAddedEvent(event IndexerEvent) {
colorKeyHex := event.Event.Keys[1]
colorHex := event.Event.Data[0]

colorKey, err := strconv.ParseInt(colorKeyHex, 0, 64)
if err != nil {
PrintIndexerError("processColorAddedEvent", "Error converting color key hex to int", colorKeyHex, colorHex)
return
}

color := colorHex[len(colorHex)-6:]

// Set color in postgres
_, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "INSERT INTO Colors (color_key, hex) VALUES ($1, $2)", colorKey, color)
if err != nil {
PrintIndexerError("processColorAddedEvent", "Error inserting color into postgres", colorKey, color)
return
}
}

func revertColorAddedEvent(event IndexerEvent) {
colorIdxHex := event.Event.Keys[1]

colorIdx, err := strconv.ParseInt(colorIdxHex, 0, 64)
if err != nil {
PrintIndexerError("revertColorAddedEvent", "Error converting color index hex to int", colorIdxHex)
return
}

// Delete color from postgres
_, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "DELETE FROM colors WHERE color_key = $1", colorIdx)
if err != nil {
PrintIndexerError("revertColorAddedEvent", "Error deleting color from postgres", colorIdx)
return
}
}
7 changes: 6 additions & 1 deletion backend/routes/indexer/nft.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,12 @@ func processNFTMintedEvent(event IndexerEvent) {
return
}

colorPaletteHex := core.ArtPeaceBackend.CanvasConfig.Colors
colorPaletteHex, err := core.PostgresQuery[string]("SELECT hex FROM colors ORDER BY color_key")
if err != nil {
PrintIndexerError("processNFTMintedEvent", "Error getting color palette from postgres", tokenIdLowHex, tokenIdHighHex, positionHex, widthHex, heightHex, imageHashHex, blockNumberHex, minter)
return
}

colorPalette := make([]color.RGBA, len(colorPaletteHex))
for idx, colorHex := range colorPaletteHex {
r, err := strconv.ParseInt(colorHex[0:2], 16, 64)
Expand Down
8 changes: 0 additions & 8 deletions backend/routes/indexer/pixel.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,6 @@ func processPixelPlacedEvent(event IndexerEvent) {
return
}

// TODO: Only validate onchain?
//validate color
colorsLength := len(core.ArtPeaceBackend.CanvasConfig.Colors)
if int(color) < 0 || int(color) >= colorsLength {
PrintIndexerError("processPixelPlacedEvent", "Color value exceeds color palette", address, posHex, dayIdxHex, colorHex)
return
}

// Set pixel in redis
bitfieldType := "u" + strconv.Itoa(int(core.ArtPeaceBackend.CanvasConfig.ColorsBitWidth))
pos := uint(position) * core.ArtPeaceBackend.CanvasConfig.ColorsBitWidth
Expand Down
4 changes: 4 additions & 0 deletions backend/routes/indexer/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ var FinalizedMessageLock = &sync.Mutex{}

const (
newDayEvent = "0x00df776faf675d0c64b0f2ec596411cf1509d3966baba3478c84771ddbac1784"
colorAddedEvent = "0x0004a301e4d01f413a1d4d0460c4ba976e23392f49126d90f5bd45de7dd7dbeb"
pixelPlacedEvent = "0x02d7b50ebf415606d77c7e7842546fc13f8acfbfd16f7bcf2bc2d08f54114c23"
basicPixelPlacedEvent = "0x03089ae3085e1c52442bb171f26f92624095d32dc8a9c57c8fb09130d32daed8"
memberPixelsPlacedEvent = "0x0165248ea72ba05120b18ec02e729e1f03a465f728283e6bb805bb284086c859"
Expand All @@ -74,6 +75,7 @@ const (

var eventProcessors = map[string](func(IndexerEvent)){
newDayEvent: processNewDayEvent,
colorAddedEvent: processColorAddedEvent,
pixelPlacedEvent: processPixelPlacedEvent,
basicPixelPlacedEvent: processBasicPixelPlacedEvent,
memberPixelsPlacedEvent: processMemberPixelsPlacedEvent,
Expand All @@ -93,6 +95,7 @@ var eventProcessors = map[string](func(IndexerEvent)){

var eventReverters = map[string](func(IndexerEvent)){
newDayEvent: revertNewDayEvent,
colorAddedEvent: revertColorAddedEvent,
pixelPlacedEvent: revertPixelPlacedEvent,
basicPixelPlacedEvent: revertBasicPixelPlacedEvent,
memberPixelsPlacedEvent: revertMemberPixelsPlacedEvent,
Expand All @@ -112,6 +115,7 @@ var eventReverters = map[string](func(IndexerEvent)){

var eventRequiresOrdering = map[string]bool{
newDayEvent: false,
colorAddedEvent: true,
pixelPlacedEvent: true,
basicPixelPlacedEvent: false,
memberPixelsPlacedEvent: false,
Expand Down
17 changes: 13 additions & 4 deletions backend/routes/pixel.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,12 @@ func placePixelDevnet(w http.ResponseWriter, r *http.Request) {
}

// Validate color format (e.g., validate against allowed colors)
colorsLength := len(core.ArtPeaceBackend.CanvasConfig.Colors)
if color < 0 || color > colorsLength {
colorsLength, err := core.PostgresQueryOne[int]("SELECT COUNT(*) FROM colors")
if err != nil {
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to get colors count")
return
}
if color < 0 || color > *colorsLength {
routeutils.WriteErrorJson(w, http.StatusBadRequest, "Color out of range")
return
}
Expand Down Expand Up @@ -196,8 +200,13 @@ func placePixelRedis(w http.ResponseWriter, r *http.Request) {
}

// Validate color range (e.g., ensure color value fits within bit width)
colorsLength := uint(len(core.ArtPeaceBackend.CanvasConfig.Colors))
if color >= colorsLength {
colorsLength, err := core.PostgresQueryOne[uint]("SELECT COUNT(*) FROM colors")
if err != nil {
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to get colors count")
return
}

if color >= *colorsLength {
routeutils.WriteErrorJson(w, http.StatusBadRequest, "Color out of range")
return
}
Expand Down
6 changes: 5 additions & 1 deletion backend/routes/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,11 @@ func addTemplateData(w http.ResponseWriter, r *http.Request) {
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to insert template data in database")
return
}
colorPaletteHex := core.ArtPeaceBackend.CanvasConfig.Colors
colorPaletteHex, err := core.PostgresQuery[string]("SELECT hex FROM colors ORDER BY color_key")
if err != nil {
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to get color palette")
return
}
colorPalette := make([]color.RGBA, len(colorPaletteHex))
for idx, colorHex := range colorPaletteHex {
r, err := strconv.ParseInt(colorHex[0:2], 16, 64)
Expand Down
73 changes: 40 additions & 33 deletions frontend/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,50 +99,57 @@ function App() {
}
}, [readyState]);

useEffect(() => {
if (lastJsonMessage) {
// Check the message type and handle accordingly
if (lastJsonMessage.messageType === 'colorPixel') {
colorPixel(lastJsonMessage.position, lastJsonMessage.color);
} else if (
lastJsonMessage.messageType === 'nftMinted' &&
activeTab === 'NFTs'
) {
if (lastJsonMessage.minter === queryAddress) {
setLatestMintedTokenId(lastJsonMessage.token_id);
}
}
}
}, [lastJsonMessage]);

// Colors
const staticColors = canvasConfig.colors;
const [colors, setColors] = useState([]);

const [notificationMessage, setNotificationMessage] = useState('');

const fetchColors = async () => {
try {
let getColorsEndpoint = backendUrl + '/get-colors';
let response = await fetch(getColorsEndpoint);
let colors = await response.json();
if (colors.error) {
setColors(staticColors);
console.error(colors.error);
return;
}
if (colors.data) {
setColors(colors.data);
}
} catch (error) {
setColors(staticColors);
console.error(error);
}
};
useEffect(() => {
const fetchColors = async () => {
try {
let getColorsEndpoint = backendUrl + '/get-colors';
let response = await fetch(getColorsEndpoint);
let colors = await response.json();
if (colors.error) {
setColors(staticColors);
console.error(colors.error);
return;
}
if (colors.data) {
setColors(colors.data);
fetchColors();
}, []);

useEffect(() => {
const processMessage = async (message) => {
if (message) {
// Check the message type and handle accordingly
if (message.messageType === 'colorPixel') {
if (message.color >= colors.length) {
// Get new colors from backend
await fetchColors();
}
colorPixel(message.position, message.color);
} else if (
message.messageType === 'nftMinted' &&
activeTab === 'NFTs'
) {
if (message.minter === queryAddress) {
setLatestMintedTokenId(message.token_id);
}
}
} catch (error) {
setColors(staticColors);
console.error(error);
}
};

fetchColors();
}, []);
processMessage(lastJsonMessage);
}, [lastJsonMessage]);

// Canvas
const width = canvasConfig.canvas.width;
Expand Down
9 changes: 3 additions & 6 deletions frontend/src/tabs/templates/Templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,10 @@ import EventItem from './EventItem.js';
import TemplateItem from './TemplateItem.js';
import ExpandableTab from '../ExpandableTab.js';
import { backendUrl } from '../../utils/Consts.js';
import canvasConfig from '../../configs/canvas.config.json';
import { fetchWrapper } from '../../services/apiService.js';

const TemplatesMainSection = (props) => {
// Each color represented as 'RRGGBB'
const colorsPalette = canvasConfig.colors;

const imageURL = backendUrl + '/templates/';

const imageToPalette = (image) => {
Expand All @@ -37,10 +34,10 @@ const TemplatesMainSection = (props) => {
continue;
}
let minDistance = 1000000;
let minColor = colorsPalette[0];
let minColor = props.colors[0];
let minColorIndex = 0;
for (let j = 0; j < colorsPalette.length; j++) {
const color = colorsPalette[j]
for (let j = 0; j < props.colors.length; j++) {
const color = props.colors[j]
.match(/[A-Za-z0-9]{2}/g)
.map((x) => parseInt(x, 16));
const distance = Math.sqrt(
Expand Down
10 changes: 10 additions & 0 deletions indexer/prod-script.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ export const config = {
includeTransaction: false,
includeReceipt: false
},
{
// Color Added Event
fromAddress: Deno.env.get("ART_PEACE_CONTRACT_ADDRESS"),
keys: [
"0x0004a301e4d01f413a1d4d0460c4ba976e23392f49126d90f5bd45de7dd7dbeb"
],
includeReverted: false,
includeTransaction: false,
includeReceipt: false
},
{
// Pixel Placed Event
fromAddress: Deno.env.get("ART_PEACE_CONTRACT_ADDRESS"),
Expand Down
11 changes: 11 additions & 0 deletions indexer/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@ export const config = {
includeTransaction: false,
includeReceipt: false
},

{
// Color Added Event
fromAddress: Deno.env.get("ART_PEACE_CONTRACT_ADDRESS"),
keys: [
"0x0004a301e4d01f413a1d4d0460c4ba976e23392f49126d90f5bd45de7dd7dbeb"
],
includeReverted: false,
includeTransaction: false,
includeReceipt: false
},
{
// Pixel Placed Event
fromAddress: Deno.env.get("ART_PEACE_CONTRACT_ADDRESS"),
Expand Down
10 changes: 10 additions & 0 deletions onchain/src/art_peace.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ pub mod ArtPeace {
#[derive(Drop, starknet::Event)]
enum Event {
NewDay: NewDay,
ColorAdded: ColorAdded,
PixelPlaced: PixelPlaced,
BasicPixelPlaced: BasicPixelPlaced,
MemberPixelsPlaced: MemberPixelsPlaced,
Expand All @@ -93,6 +94,13 @@ pub mod ArtPeace {
TemplateEvent: TemplateStoreComponent::Event,
}

#[derive(Drop, starknet::Event)]
struct ColorAdded {
#[key]
color_key: u8,
color: u32,
}

#[derive(Drop, starknet::Event)]
struct NewDay {
#[key]
Expand Down Expand Up @@ -226,6 +234,7 @@ pub mod ArtPeace {
let mut i: u8 = 0;
while i < color_count {
self.color_palette.write(i, *init_params.color_palette.at(i.into()));
self.emit(ColorAdded { color_key: i, color: *init_params.color_palette.at(i.into()) });
i += 1;
};

Expand Down Expand Up @@ -1164,6 +1173,7 @@ pub mod ArtPeace {
let color = self.votable_colors.read((votable_index, day));
if vote >= threshold {
self.color_palette.write(color_index, color);
self.emit(ColorAdded { color_key: color_index, color });
color_index += 1;
} else {
self.votable_colors.write((next_day_votable_index, next_day), color);
Expand Down
2 changes: 2 additions & 0 deletions postgres/init.sql
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,10 @@ CREATE INDEX userMainQuests_quest_id_index ON UserMainQuests (quest_id);
CREATE TABLE Colors (
-- Postgres auto-incrementing primary key
key int PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
color_key integer NOT NULL,
hex text NOT NULL
);
CREATE INDEX colors_color_key_index ON Colors (color_key);

-- TODO: Add day_index
CREATE TABLE VotableColors (
Expand Down
5 changes: 0 additions & 5 deletions tests/integration/docker/initialize.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@ echo "Set the username store address"
USERNAME_STORE_ADDRESS=$(cat /configs/configs.env | grep "^USERNAME_STORE_ADDRESS" | cut -d '=' -f2)
curl http://backend:8080/set-username-store-address -X POST -d "$USERNAME_STORE_ADDRESS"

echo "Setup the colors from the color config"
# flatten colors with quotes and join them with comma and wrap in []
COLORS=$(cat /configs/canvas.config.json | jq -r '.colors | map("\"\(.)\"") | join(",")')
curl http://backend:8080/init-colors -X POST -d "[$COLORS]"

echo "Setup the quests from the quest config"
QUESTS_CONFIG_FILE="/configs/quests.config.json"
curl http://backend:8080/init-quests -X POST -d "@$QUESTS_CONFIG_FILE"
Expand Down
Loading

0 comments on commit 38ed003

Please sign in to comment.