Skip to content

Commit

Permalink
Award Scripts & Dynamic NFT Data Directory (#260)
Browse files Browse the repository at this point in the history
* wip

* award on sepolia

* award on mainnet

* award on local

* award on local

* use envs from root

* wip

* wip

* dynamic routing for nft images

* dynamic routing for nft images

* dynamic routing for nft metadata

* up for review

* fmt

* gofmt

* testing fmt

* scarb fmt with scarb 2.6.3

* updated markdown

* updated markdown

* updated markdown

* Various readme and test changes

* React app env var on docker compose

* Scarb fmt

* Go fmt

---------

Co-authored-by: Brandon Roberts <[email protected]>
  • Loading branch information
supreme2580 and b-j-roberts authored Nov 5, 2024
1 parent ec99f2f commit fc7ec9a
Show file tree
Hide file tree
Showing 20 changed files with 211 additions and 52 deletions.
12 changes: 5 additions & 7 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## 🛠️ Contributing to art/peace 🛠️
# 🛠️ Contributing to art/peace 🛠️

Welcome, contributing to `art/peace` is easy!

Expand All @@ -8,7 +8,7 @@ Welcome, contributing to `art/peace` is easy!
1. Submit your PR against `main`
1. Address PR Review

### Issue
## Issue

Project tracking is done via GitHub [issues](https://github.com/keep-starknet-strange/art-peace/issues).
First look at open issues to see if your request is already submitted.
Expand All @@ -26,12 +26,12 @@ These labels are used as prefixes as follows for `issue`, `branch name`, `pr tit
- `[bug]` -> `bug/{issue #}-{issue name}` -> `bug:`
- `[dev]` -> `dev/{issue #}-{issue name}` -> `dev:`

#### TODO
### TODO

If your PR includes a `TODO` comment please open an issue and comment the relevant
code with `TODO(#ISSUE_NUM):`.

### Submit PR
## Submit PR

Ensure your code is well formatted, well tested and well documented. A core contributor
will review your work. Address changes, ensure ci passes,
Expand All @@ -49,7 +49,7 @@ Scarb linter:
scarb fmt
```

### Additional Resources
## Additional Resources

- [Cairo Book](https://book.cairo-lang.org/)
- [Starknet Book](https://book.starknet.io/)
Expand All @@ -58,6 +58,4 @@ scarb fmt
- [Starkli Book](https://book.starkli.rs/)
- [Syncing a Fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/syncing-a-fork)

##

Thank you for your contribution!
4 changes: 1 addition & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,8 @@
</div>

> Art Peace Round 2 is live!
> Check live stats on this cool [Dune Dashboard](https://dune.com/hessish/artpeace)
> And go place some pixels and draw cool shit you mfer! ==> https://www.art-peace.net/
> And go place some pixels and draw cool shit you mfer! ==> [art-peace.net](https://www.art-peace.net)
## Overview

Expand Down
45 changes: 23 additions & 22 deletions backend/routes/indexer/nft.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,32 +162,33 @@ func processNFTMintedEvent(event IndexerEvent) {
}

// TODO: Check if file exists
if _, err := os.Stat("nfts"); os.IsNotExist(err) {
err = os.MkdirAll("nfts", os.ModePerm)
if err != nil {
PrintIndexerError("processNFTMintedEvent", "Error creating nfts directory", tokenIdLowHex, positionHex, widthHex, heightHex, nameHex, imageHashHex, blockNumberHex, minter)
return
}
roundNumber := os.Getenv("ROUND_NUMBER")
if roundNumber == "" {
PrintIndexerError("processNFTMintedEvent", "Error getting round number from environment", tokenIdLowHex, positionHex, widthHex, heightHex, nameHex, imageHashHex, blockNumberHex, minter)
return
}
roundDir := fmt.Sprintf("round-%s", roundNumber)

if _, err := os.Stat("nfts/images"); os.IsNotExist(err) {
err = os.MkdirAll("nfts/images", os.ModePerm)
if err != nil {
PrintIndexerError("processNFTMintedEvent", "Error creating nfts/images directory", tokenIdLowHex, positionHex, widthHex, heightHex, nameHex, imageHashHex, blockNumberHex, minter)
return
}
// Create base directories if they don't exist
dirs := []string{
"nfts",
fmt.Sprintf("nfts/%s", roundDir),
fmt.Sprintf("nfts/%s/images", roundDir),
fmt.Sprintf("nfts/%s/metadata", roundDir),
}

if _, err := os.Stat("nfts/meta"); os.IsNotExist(err) {
err = os.MkdirAll("nfts/meta", os.ModePerm)
if err != nil {
PrintIndexerError("processNFTMintedEvent", "Error creating nfts/meta directory", tokenIdLowHex, positionHex, widthHex, heightHex, nameHex, imageHashHex, blockNumberHex, minter)
return
for _, dir := range dirs {
if _, err := os.Stat(dir); os.IsNotExist(err) {
err = os.MkdirAll(dir, os.ModePerm)
if err != nil {
PrintIndexerError("processNFTMintedEvent", fmt.Sprintf("Error creating %s directory", dir), tokenIdLowHex, positionHex, widthHex, heightHex, nameHex, imageHashHex, blockNumberHex, minter)
return
}
}
}

// Save image to disk
filename := fmt.Sprintf("nfts/images/nft-%d.png", tokenId)
filename := fmt.Sprintf("nfts/%s/images/nft-%d.png", roundDir, tokenId)
file, err := os.Create(filename)
if err != nil {
PrintIndexerError("processNFTMintedEvent", "Error creating file", tokenIdLowHex, tokenIdHighHex, positionHex, widthHex, heightHex, nameHex, imageHashHex, blockNumberHex, minter)
Expand All @@ -207,15 +208,15 @@ func processNFTMintedEvent(event IndexerEvent) {
metadata := map[string]interface{}{
"name": name,
"description": "User minted art/peace NFT from the canvas.",
"image": fmt.Sprintf("%s/nft-images/nft-%d.png", core.ArtPeaceBackend.GetBackendUrl(), tokenId),
"image": fmt.Sprintf("%s/nft/%s/image/nft-%d.png", core.ArtPeaceBackend.GetBackendUrl(), roundDir, tokenId),
"attributes": []map[string]interface{}{
{
"trait_type": "Width",
"value": fmt.Sprintf("%d", scaledWidth),
"value": fmt.Sprintf("%d", width),
},
{
"trait_type": "Height",
"value": fmt.Sprintf("%d", scaledHeight),
"value": fmt.Sprintf("%d", height),
},
{
"trait_type": "Position",
Expand All @@ -242,7 +243,7 @@ func processNFTMintedEvent(event IndexerEvent) {
return
}

metadataFilename := fmt.Sprintf("nfts/meta/nft-%d.json", tokenId)
metadataFilename := fmt.Sprintf("nfts/%s/metadata/nft-%d.json", roundDir, tokenId)
err = os.WriteFile(metadataFilename, metadataFile, 0644)
if err != nil {
PrintIndexerError("processNFTMintedEvent", "Error writing NFT metadata file", tokenIdLowHex, positionHex, widthHex, heightHex, nameHex, imageHashHex, blockNumberHex, minter)
Expand Down
3 changes: 1 addition & 2 deletions backend/routes/nft.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ func InitNFTRoutes() {
}

func InitNFTStaticRoutes() {
http.Handle("/nft-images/", http.StripPrefix("/nft-images/", http.FileServer(http.Dir("./nfts/images"))))
http.Handle("/nft-meta/", http.StripPrefix("/nft-meta/", http.FileServer(http.Dir("./nfts/meta"))))
http.Handle("/nft/", http.StripPrefix("/nft/", http.FileServer(http.Dir("./nfts"))))
}

func getCanvasNFTAddress(w http.ResponseWriter, r *http.Request) {
Expand Down
3 changes: 3 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ services:
- POSTGRES_PASSWORD=password
- ART_PEACE_END_TIME=3000000000
- ART_PEACE_HOST=0328ced46664355fc4b885ae7011af202313056a7e3d44827fb24c9d3206aaa0
- ROUND_NUMBER=1
consumer:
build:
dockerfile: backend/Dockerfile.consumer
Expand All @@ -47,6 +48,7 @@ services:
restart: always
environment:
- POSTGRES_PASSWORD=password
- ROUND_NUMBER=1
volumes:
- nfts:/app/nfts
devnet:
Expand Down Expand Up @@ -138,6 +140,7 @@ services:
- devnet
environment:
- REACT_APP_BASE_PIXEL_TIMER=30000
- REACT_APP_ROUND_NUMBER=1
volumes:
- ./frontend/package.json:/app/package.json
- ./frontend/package-lock.json:/app/package-lock.json
Expand Down
10 changes: 8 additions & 2 deletions frontend/src/tabs/nfts/NFTs.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import {
import { PaginationView } from '../../ui/pagination.js';

const NFTsMainSection = (props) => {
const imageURL = nftUrl + '/nft-images/';
const roundNumber = process.env.REACT_APP_ROUND_NUMBER || '0';
const imageURL = `${nftUrl}/nft/round-${roundNumber}/images/`;
const metadataURL = `${nftUrl}/nft/round-${roundNumber}/metadata/`;
return (
<div
className={`${props.expanded ? 'NFTs__main__expanded' : 'NFTs__main'}`}
Expand Down Expand Up @@ -63,6 +65,7 @@ const NFTsMainSection = (props) => {
tokenId={nft.tokenId}
position={nft.position}
image={imageURL + 'nft-' + nft.tokenId + '.png'}
metadata={metadataURL + 'nft-' + nft.tokenId + '.json'}
width={nft.width}
height={nft.height}
name={nft.name}
Expand All @@ -86,7 +89,9 @@ const NFTsMainSection = (props) => {
};

const NFTsExpandedSection = (props) => {
const imageURL = nftUrl + '/nft-images/';
const roundNumber = process.env.REACT_APP_ROUND_NUMBER || '0';
const imageURL = `${nftUrl}/nft/round-${roundNumber}/images/`;
const metadataURL = `${nftUrl}/nft/round-${roundNumber}/metadata/`;

return (
<div className='NFTs__all'>
Expand Down Expand Up @@ -121,6 +126,7 @@ const NFTsExpandedSection = (props) => {
tokenId={nft.tokenId}
position={nft.position}
image={imageURL + 'nft-' + nft.tokenId + '.png'}
metadata={metadataURL + 'nft-' + nft.tokenId + '.json'}
width={nft.width}
height={nft.height}
name={nft.name}
Expand Down
4 changes: 2 additions & 2 deletions infra/instructions/kube-deploy-steps.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ deploy username store to sepolia
save address
deploy art peace to sepolia
vim ./tests/integration/sepolia/deploy.sh
change end time
change end time & round number
./tests/integration/sepolia/deploy.sh
save address
deploy canvas nft to sepolia
./tests/integration/sepolia/deploy-canvas-nft.sh
save address
save address & round number
and set in art_peace contract
deploy quests
vim ./tests/integration/sepolia/deploy-daily-quests.sh
Expand Down
4 changes: 2 additions & 2 deletions onchain/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ This directory contains the onchain Starknet contract components for `art/peace`

## Build

```
```bash
scarb build
```

## Test

```
```bash
scarb test
```
3 changes: 2 additions & 1 deletion onchain/src/art_peace.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,8 @@ pub mod ArtPeace {
}
let member_metadata = self.users_faction_meta.read(user);
if member_metadata.member_pixels > 0 {
// TODO: If member_pixels > 0 && < allocation && enough time has passed, return allocation instead of member_pixels
// TODO: If member_pixels > 0 && < allocation && enough time has passed, return
// allocation instead of member_pixels
return member_metadata.member_pixels;
} else {
let time_since_last_pixel = now - member_metadata.member_placed_time;
Expand Down
8 changes: 6 additions & 2 deletions onchain/src/nfts/canvas_nft.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,12 @@ mod CanvasNFT {


#[constructor]
fn constructor(ref self: ContractState, name: ByteArray, symbol: ByteArray) {
let base_uri = "https://api.art-peace.net/nft-meta/nft-";
fn constructor(
ref self: ContractState, name: ByteArray, symbol: ByteArray, round_number: felt252
) {
let base_uri = format!(
"https://api.art-peace.net/nft/round-{}/metadata/nft-", round_number
);
self.erc721.initializer(name, symbol, base_uri);
}

Expand Down
10 changes: 6 additions & 4 deletions onchain/src/tests/art_peace.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ pub(crate) fn deploy_contract() -> ContractAddress {
start_time: 0,
end_time: 1000000,
daily_quests_count: 3,
devmode: false
devmode: false,
}
.serialize(ref calldata);
let contract_addr = contract.deploy_at(@calldata, utils::ART_PEACE_CONTRACT()).unwrap();
Expand Down Expand Up @@ -162,6 +162,8 @@ fn deploy_nft_contract() -> ContractAddress {
let symbol: ByteArray = "A/P";
name.serialize(ref calldata);
symbol.serialize(ref calldata);
let round_number: u32 = 1;
round_number.serialize(ref calldata);
contract.deploy_at(@calldata, utils::NFT_CONTRACT()).unwrap()
}

Expand Down Expand Up @@ -398,7 +400,7 @@ fn nft_mint_test() {
assert!(nft.balance_of(utils::PLAYER2()) == 1, "NFT balance is not correct after transfer");

let nft_meta = IERC721MetadataDispatcher { contract_address: nft.contract_address };
let expected_uri = "https://api.art-peace.net/nft-meta/nft-0.json";
let expected_uri = "https://api.art-peace.net/nft/round-1/metadata/nft-0.json";
assert!(nft_meta.token_uri(0) == expected_uri, "NFT URI is not correct");
}

Expand All @@ -416,8 +418,8 @@ fn nft_set_base_uri_test() {
nft_minter.mint_nft(mint_params);
snf::stop_prank(CheatTarget::One(nft_minter.contract_address));

let _base_uri: ByteArray = "https://api.art-peace.net/nft-meta/nft-";
let expected_uri: ByteArray = "https://api.art-peace.net/nft-meta/nft-0.json";
let _base_uri: ByteArray = "https://api.art-peace.net/nft/round-1/metadata/nft-";
let expected_uri: ByteArray = "https://api.art-peace.net/nft/round-1/metadata/nft-0.json";
let nft_meta = IERC721MetadataDispatcher { contract_address: nft.contract_address };
assert!(nft_meta.token_uri(0) == expected_uri, "NFT URI is not correct before change");

Expand Down
2 changes: 1 addition & 1 deletion onchain/src/tests/vote_quest.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ fn vote_quest_double_claim_test() {
art_peace_dispatcher.claim_main_quest(0, utils::EMPTY_CALLDATA());
}

// When the `votable_colors` storage variable is not set for a particular day index, trying to
// When the `votable_colors` storage variable is not set for a particular day index, trying to
// vote for a color at that day panics with 'Color out of bounds' error
#[test]
#[should_panic(expected: 'Color out of bounds')]
Expand Down
35 changes: 35 additions & 0 deletions tests/integration/docker/award_pixels.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/bin/bash

# Load environment variables from `.env` file
if [ -f ../.env ]; then
export $(grep -v '^#' ../.env | xargs)
fi

# Set environment and network details
ENVIRONMENT="local"
NETWORK_URL="http://localhost:5050" # Use the URL from your console output
ART_PEACE_CONTRACT="0x6fe75e8821863019aa3e7b824cceaa8a42265bbf1374e3c92eebc698036d438"

# Define the JSON file path from command line argument
JSON_FILE_PATH="$1"

# Verify contract exists before proceeding
echo "Verifying contract at $ART_PEACE_CONTRACT..."
if ! starkli class-hash-at $ART_PEACE_CONTRACT --rpc $NETWORK_URL > /dev/null 2>&1; then
echo "Error: ArtPeace contract not found at $ART_PEACE_CONTRACT"
echo "Please verify the contract address is correct"
exit 1
fi

echo "Contract verified successfully!"

# Iterate through each user address and amount in the JSON file
jq -c '.[]' "$JSON_FILE_PATH" | while read -r entry; do
USER_ADDRESS=$(echo "$entry" | jq -r '.address')
AMOUNT=$(echo "$entry" | jq -r '.amount')
echo "Awarding $AMOUNT pixels to $USER_ADDRESS..."

# Invoke the host_award_user function using starkli
starkli invoke --rpc $NETWORK_URL --keystore $STARKNET_KEYSTORE --account $STARKNET_ACCOUNT \
--watch $ART_PEACE_CONTRACT host_award_user $USER_ADDRESS $AMOUNT
done
3 changes: 2 additions & 1 deletion tests/integration/docker/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ echo "Declared class \"$NFT_CLASS_NAME\" with hash $NFT_CLASS_HASH"

NFT_NAME="0 318195848183955342120051 10"
NFT_SYMBOL="0 4271952 3"
CALLDATA=$(echo -n $NFT_NAME $NFT_SYMBOL)
ROUND_NUMBER=3
CALLDATA=$(echo -n $NFT_NAME $NFT_SYMBOL $ROUND_NUMBER)

echo "Deploying contract \"$NFT_CLASS_NAME\"..."
echo "/root/.local/bin/sncast --url $RPC_URL --accounts-file $ACCOUNT_FILE --account $ACCOUNT_NAME --wait --json deploy --class-hash $NFT_CLASS_HASH --constructor-calldata $CALLDATA"
Expand Down
10 changes: 10 additions & 0 deletions tests/integration/docker/rewards.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[
{
"address": "0x328ced46664355fc4b885ae7011af202313056a7e3d44827fb24c9d3206aaa0",
"amount": 10
},
{
"address": "0x05e01dB693CBF7461a016343042786DaC5A6000104813cF134a1E8B1D0a6810b",
"amount": 20
}
]
3 changes: 2 additions & 1 deletion tests/integration/local/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ echo "Deployed contract \"$ART_PEACE_CLASS_NAME\" with address $ART_PEACE_CONTRA

NFT_NAME="0 318195848183955342120051 10"
NFT_SYMBOL="0 4271952 3"
CALLDATA=$(echo -n $NFT_NAME $NFT_SYMBOL)
ROUND_NUMBER=3
CALLDATA=$(echo -n $NFT_NAME $NFT_SYMBOL $ROUND_NUMBER)

echo "Deploying contract \"$NFT_CLASS_NAME\"..."
echo "sncast --url $RPC_URL --accounts-file $ACCOUNT_FILE --account $ACCOUNT_NAME --wait --json deploy --class-hash $NFT_CLASS_HASH --constructor-calldata $CALLDATA"
Expand Down
Loading

0 comments on commit fc7ec9a

Please sign in to comment.