diff --git a/.all-contributorsrc b/.all-contributorsrc
index 51c74d2d..62934edc 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -81,6 +81,42 @@
"contributions": [
"code"
]
+ },
+ {
+ "login": "fishonamos",
+ "name": "Fishon Amos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/43862685?v=4",
+ "profile": "https://fishonsnote.medium.com/",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "Xaxxoo",
+ "name": "Xaxxoo",
+ "avatar_url": "https://avatars.githubusercontent.com/u/51526246?v=4",
+ "profile": "https://github.com/Xaxxoo",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "manoahLinks",
+ "name": "Mano.dev",
+ "avatar_url": "https://avatars.githubusercontent.com/u/100848212?v=4",
+ "profile": "https://github.com/manoahLinks",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "Otaiki1",
+ "name": "Abdulsamad sadiq",
+ "avatar_url": "https://avatars.githubusercontent.com/u/38711713?v=4",
+ "profile": "https://github.com/Otaiki1",
+ "contributions": [
+ "code"
+ ]
}
],
"contributorsPerLine": 7,
diff --git a/README.md b/README.md
index 727a898e..a5519478 100644
--- a/README.md
+++ b/README.md
@@ -118,6 +118,10 @@ Thanks goes to these wonderful people. Follow the [contributors guide](https://g
0xK2 💻 |
+ Fishon Amos 💻 |
+ Xaxxoo 💻 |
+ Mano.dev 💻 |
+ Abdulsamad sadiq 💻 |
diff --git a/configs/canvas.config.json b/configs/canvas.config.json
index 20b71057..a8c24d4a 100644
--- a/configs/canvas.config.json
+++ b/configs/canvas.config.json
@@ -1,7 +1,7 @@
{
"canvas": {
- "width": 35,
- "height": 24
+ "width": 256,
+ "height": 256
},
"colors": [
"010001",
diff --git a/docker-compose.yml b/docker-compose.yml
index 4e9e2252..cdcce894 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -107,11 +107,11 @@ services:
- backend
- devnet
volumes:
- - configs:/configs
- ./frontend/package.json:/app/package.json
- ./frontend/package-lock.json:/app/package-lock.json
- ./frontend/public/:/app/public
- ./frontend/src:/app/src
+ - configs:/app/src/configs
volumes:
redis:
diff --git a/frontend/Dockerfile b/frontend/Dockerfile
index ef89246e..8651eba2 100644
--- a/frontend/Dockerfile
+++ b/frontend/Dockerfile
@@ -6,7 +6,8 @@ WORKDIR /app
COPY ./frontend/package.json ./frontend/package-lock.json ./
RUN npm install
COPY ./frontend ./
-COPY ./configs/docker-backend.config.json ./src/backend.config.json
+COPY ./configs/canvas.config.json ./src/configs/canvas.config.json
+COPY ./configs/docker-backend.config.json ./src/configs/backend.config.json
SHELL ["/bin/bash", "-c"]
# Clear the entrypoint
diff --git a/frontend/src/canvas/Canvas.js b/frontend/src/canvas/Canvas.js
index e29ae923..804bd622 100644
--- a/frontend/src/canvas/Canvas.js
+++ b/frontend/src/canvas/Canvas.js
@@ -18,7 +18,6 @@ const Canvas = props => {
const maxScale = 40;
- const canvasRef = useRef(null)
const canvasPositionRef = useRef(null)
const canvasScaleRef = useRef(null)
@@ -410,7 +409,7 @@ const Canvas = props => {
return null;
}
if (props.selectedColorId === -1) {
- let color = canvasRef.current
+ let color = props.canvasRef.current
.getContext("2d")
.getImageData(
props.selectedPositionX,
@@ -434,7 +433,7 @@ const Canvas = props => {
return null;
}
if (props.selectedColorId === -1) {
- let color = canvasRef.current
+ let color = props.canvasRef.current
.getContext("2d")
.getImageData(
props.selectedPositionX,
@@ -543,7 +542,7 @@ const Canvas = props => {
return () => {
window.removeEventListener("mousemove", setFromEvent);
};
- }, [props.selectedColorId, pixelSelect, props.nftSelectionMode, nftSelectionStarted, nftSelectionPositionX, nftSelectionPositionY, nftSelectionWidth, nftSelectionHeight, height, width, props.canvasRef, nftSelectionEndX, nftSelectionEndY, nftSelectionStartX, nftSelectionStartY]);
+ }, [props.selectedColorId, pixelSelect, props.nftSelectionMode, nftSelectionStarted, nftSelectionPositionX, nftSelectionPositionY, nftSelectionWidth, nftSelectionHeight, height, width, props.canvasRef, nftSelectionEndX, nftSelectionEndY, nftSelectionStartX, nftSelectionStartY, props]);
// TODO: both place options
return (
diff --git a/frontend/src/configs/canvas.config.json b/frontend/src/configs/canvas.config.json
index 20b71057..a8c24d4a 100644
--- a/frontend/src/configs/canvas.config.json
+++ b/frontend/src/configs/canvas.config.json
@@ -1,7 +1,7 @@
{
"canvas": {
- "width": 35,
- "height": 24
+ "width": 256,
+ "height": 256
},
"colors": [
"010001",
diff --git a/onchain/src/lib.cairo b/onchain/src/lib.cairo
index e9992ddc..77feec43 100644
--- a/onchain/src/lib.cairo
+++ b/onchain/src/lib.cairo
@@ -8,8 +8,13 @@ mod quests {
pub mod interfaces;
pub mod pixel_quest;
pub mod template_quest;
+ pub mod unruggable_quest;
- use interfaces::{IQuest, IPixelQuest, QuestClaimed, IQuestDispatcher, IQuestDispatcherTrait};
+ use interfaces::{
+ IQuest, IPixelQuest, IUnruggableQuest, QuestClaimed, IQuestDispatcher,
+ IQuestDispatcherTrait, IUnruggableMemecoin, IUnruggableMemecoinDispatcher,
+ IUnruggableMemecoinDispatcherTrait
+ };
}
mod templates {
@@ -44,7 +49,8 @@ mod username_store {
}
mod mocks {
- pub mod erc20_mock;
+ pub(crate) mod erc20_mock;
+ pub(crate) mod unruggable_token;
}
#[cfg(test)]
diff --git a/onchain/src/mocks/unruggable_token.cairo b/onchain/src/mocks/unruggable_token.cairo
new file mode 100644
index 00000000..b10029a7
--- /dev/null
+++ b/onchain/src/mocks/unruggable_token.cairo
@@ -0,0 +1,52 @@
+//
+// https://github.com/OpenZeppelin/cairo-contracts/blob/main/src/tests/mocks/erc20_mocks.cairo
+//
+
+#[starknet::contract]
+pub mod UnruggableMock {
+ use art_peace::quests::interfaces::IUnruggableMemecoin;
+ use openzeppelin::token::erc20::ERC20Component;
+ use starknet::ContractAddress;
+
+ component!(path: ERC20Component, storage: erc20, event: ERC20Event);
+
+ #[abi(embed_v0)]
+ impl ERC20Impl = ERC20Component::ERC20Impl;
+ #[abi(embed_v0)]
+ impl ERC20MetadataImpl = ERC20Component::ERC20MetadataImpl;
+ impl InternalImpl = ERC20Component::InternalImpl;
+
+ #[storage]
+ struct Storage {
+ is_launched: bool,
+ owner: ContractAddress,
+ #[substorage(v0)]
+ erc20: ERC20Component::Storage
+ }
+
+ #[event]
+ #[derive(Drop, starknet::Event)]
+ enum Event {
+ #[flat]
+ ERC20Event: ERC20Component::Event
+ }
+
+ #[constructor]
+ fn constructor(
+ ref self: ContractState, name: ByteArray, symbol: ByteArray, owner: ContractAddress
+ ) {
+ self.erc20.initializer(name, symbol);
+ self.owner.write(owner);
+ self.is_launched.write(true);
+ }
+
+ #[abi(embed_v0)]
+ impl UnruggableImpl of IUnruggableMemecoin {
+ fn owner(self: @ContractState) -> ContractAddress {
+ self.owner.read()
+ }
+ fn is_launched(self: @ContractState) -> bool {
+ self.is_launched.read()
+ }
+ }
+}
diff --git a/onchain/src/quests/interfaces.cairo b/onchain/src/quests/interfaces.cairo
index 5010679a..7d200194 100644
--- a/onchain/src/quests/interfaces.cairo
+++ b/onchain/src/quests/interfaces.cairo
@@ -26,3 +26,25 @@ pub trait IPixelQuest {
fn is_color(self: @TContractState) -> bool;
fn color(self: @TContractState) -> u8;
}
+
+#[starknet::interface]
+pub trait IUnruggableQuest {
+ fn is_claimed(self: @TContractState, user: starknet::ContractAddress) -> bool;
+}
+
+#[starknet::interface]
+pub trait IUnruggableMemecoin {
+ // ************************************
+ // * Ownership
+ // ************************************
+ fn owner(self: @TState) -> ContractAddress;
+
+ // ************************************
+ // * Additional functions
+ // ************************************
+ /// Checks whether token has launched
+ ///
+ /// # Returns
+ /// bool: whether token has launched
+ fn is_launched(self: @TState) -> bool;
+}
diff --git a/onchain/src/quests/unruggable_quest.cairo b/onchain/src/quests/unruggable_quest.cairo
new file mode 100644
index 00000000..50cad795
--- /dev/null
+++ b/onchain/src/quests/unruggable_quest.cairo
@@ -0,0 +1,84 @@
+#[starknet::contract]
+pub mod UnruggableQuest {
+ use starknet::{ContractAddress, get_caller_address};
+ use art_peace::{IArtPeaceDispatcher, IArtPeaceDispatcherTrait};
+ use art_peace::quests::{
+ IQuest, IUnruggableQuest, IUnruggableMemecoinDispatcher, IUnruggableMemecoinDispatcherTrait,
+ QuestClaimed
+ };
+
+ #[storage]
+ struct Storage {
+ art_peace: ContractAddress,
+ reward: u32,
+ claimed: LegacyMap,
+ }
+
+ #[event]
+ #[derive(Drop, starknet::Event)]
+ enum Event {
+ QuestClaimed: QuestClaimed,
+ }
+
+ #[derive(Drop, Serde)]
+ pub struct UnruggableQuestInitParams {
+ pub art_peace: ContractAddress,
+ pub reward: u32,
+ }
+
+ #[constructor]
+ fn constructor(ref self: ContractState, init_params: UnruggableQuestInitParams) {
+ self.art_peace.write(init_params.art_peace);
+ self.reward.write(init_params.reward);
+ }
+
+ #[abi(embed_v0)]
+ impl UnruggableQuestImpl of IUnruggableQuest {
+ fn is_claimed(self: @ContractState, user: ContractAddress) -> bool {
+ self.claimed.read(user)
+ }
+ }
+
+ #[abi(embed_v0)]
+ impl UnruggableQuest of IQuest {
+ fn get_reward(self: @ContractState) -> u32 {
+ self.reward.read()
+ }
+
+ fn is_claimable(
+ self: @ContractState, user: ContractAddress, calldata: Span
+ ) -> bool {
+ if self.claimed.read(user) {
+ return false;
+ }
+
+ let coin_address_as_felt252: felt252 = *calldata.at(0);
+ let coin = IUnruggableMemecoinDispatcher {
+ contract_address: coin_address_as_felt252.try_into().unwrap()
+ };
+
+ if coin.owner() != user {
+ return false;
+ }
+
+ if coin.is_launched() != true {
+ return false;
+ }
+
+ true
+ }
+
+ fn claim(ref self: ContractState, user: ContractAddress, calldata: Span) -> u32 {
+ assert(get_caller_address() == self.art_peace.read(), 'Only ArtPeace can claim quests');
+
+ assert(self.is_claimable(user, calldata), 'quest not claimable');
+
+ self.claimed.write(user, true);
+ let reward = self.reward.read();
+ self.emit(QuestClaimed { user, reward, calldata });
+
+ reward
+ }
+ }
+}
+
diff --git a/onchain/src/tests/art_peace.cairo b/onchain/src/tests/art_peace.cairo
index dc08d8f0..ca739a4d 100644
--- a/onchain/src/tests/art_peace.cairo
+++ b/onchain/src/tests/art_peace.cairo
@@ -1,7 +1,9 @@
use art_peace::{IArtPeaceDispatcher, IArtPeaceDispatcherTrait};
use art_peace::ArtPeace::InitParams;
use art_peace::quests::pixel_quest::PixelQuest::PixelQuestInitParams;
+use art_peace::quests::unruggable_quest::UnruggableQuest::UnruggableQuestInitParams;
use art_peace::mocks::erc20_mock::SnakeERC20Mock;
+use art_peace::mocks::unruggable_token::UnruggableMock;
use art_peace::tests::utils;
use art_peace::nfts::interfaces::{
IArtPeaceNFTMinterDispatcher, IArtPeaceNFTMinterDispatcherTrait, ICanvasNFTStoreDispatcher,
@@ -36,6 +38,10 @@ fn ERC20_MOCK_CONTRACT() -> ContractAddress {
contract_address_const::<'erc20mock'>()
}
+fn UNRUGGABLE_MOCK_CONTRACT() -> ContractAddress {
+ contract_address_const::<'unruggable'>()
+}
+
fn EMPTY_CALLDATA() -> Span {
array![].span()
}
@@ -186,6 +192,15 @@ fn deploy_pixel_quests_main(pixel_quest: snf::ContractClass) -> Array ContractAddress {
+ let mut unruggable_calldata = array![];
+ UnruggableQuestInitParams { art_peace: ART_PEACE_CONTRACT(), reward: 20, }
+ .serialize(ref unruggable_calldata);
+ let main_unruggable_quest = unruggable_quest.deploy(@unruggable_calldata).unwrap();
+
+ main_unruggable_quest
+}
+
fn deploy_nft_contract() -> ContractAddress {
let contract = snf::declare("CanvasNFT");
let mut calldata = array![];
@@ -215,6 +230,22 @@ fn deploy_erc20_mock() -> ContractAddress {
contract_addr
}
+fn deploy_unruggable_mock() -> ContractAddress {
+ let contract = snf::declare("UnruggableMock");
+ let name: ByteArray = "Unruggable mock";
+ let symbol: ByteArray = "UNRUGGABLE";
+ let owner: ContractAddress = get_contract_address();
+
+ let mut calldata: Array = array![];
+ Serde::serialize(@name, ref calldata);
+ Serde::serialize(@symbol, ref calldata);
+ Serde::serialize(@owner, ref calldata);
+
+ let contract_addr = contract.deploy_at(@calldata, UNRUGGABLE_MOCK_CONTRACT()).unwrap();
+
+ contract_addr
+}
+
fn warp_to_next_available_time(art_peace: IArtPeaceDispatcher) {
let last_time = art_peace.get_last_placed_time();
snf::start_warp(CheatTarget::One(art_peace.contract_address), last_time + TIME_BETWEEN_PIXELS);
@@ -277,7 +308,7 @@ fn place_pixel_test() {
}
#[test]
-fn deploy_quest_test() {
+fn deploy_pixel_quest_test() {
let pixel_quest = snf::declare("PixelQuest");
let daily_quests = deploy_pixel_quests_daily(pixel_quest);
let main_quests = deploy_pixel_quests_main(pixel_quest);
@@ -293,6 +324,29 @@ fn deploy_quest_test() {
);
}
+#[test]
+fn deploy_unruggable_quest_test() {
+ let unruggable_quest = snf::declare("UnruggableQuest");
+ let main_unruggable_quest = deploy_unruggable_quest_main(unruggable_quest);
+
+ let art_peace = IArtPeaceDispatcher {
+ contract_address: deploy_with_quests_contract(
+ array![].span(), array![main_unruggable_quest].span()
+ )
+ };
+
+ let zero_address = contract_address_const::<0>();
+
+ assert!(
+ art_peace.get_days_quests(0) == array![zero_address, zero_address, zero_address].span(),
+ "Daily quests were not set correctly"
+ );
+ assert!(
+ art_peace.get_main_quests() == array![main_unruggable_quest].span(),
+ "Main quests were not set correctly"
+ );
+}
+
// TODO: Daily quest test day 2, stats, other fields ...
#[test]
@@ -383,6 +437,27 @@ fn pixel_quests_test() {
);
}
+#[test]
+fn unruggable_quest_test() {
+ let unruggable_quest = snf::declare("UnruggableQuest");
+ let main_unruggable_quest = deploy_unruggable_quest_main(unruggable_quest);
+
+ let art_peace = IArtPeaceDispatcher {
+ contract_address: deploy_with_quests_contract(
+ array![].span(), array![main_unruggable_quest].span()
+ )
+ };
+
+ let unruggable_token = deploy_unruggable_mock();
+
+ let calldata: Span = array![unruggable_token.try_into().unwrap()].span();
+ art_peace.claim_main_quest(0, calldata);
+
+ assert!(
+ art_peace.get_extra_pixels_count() == 20, "Extra pixels are wrong after main quest claim"
+ );
+}
+
#[test]
fn template_full_basic_test() {
let art_peace = IArtPeaceDispatcher { contract_address: deploy_contract() };
diff --git a/tests/integration/local/run.sh b/tests/integration/local/run.sh
index a190d19d..aa83c6ec 100755
--- a/tests/integration/local/run.sh
+++ b/tests/integration/local/run.sh
@@ -31,6 +31,7 @@ touch $REDIS_LOG_FILE
redis-server 2>&1 > $REDIS_LOG_FILE &
REDIS_PID=$!
sleep 2 # Wait for redis to start; TODO: Check if redis is actually running
+redis-cli del canvas
# Start the art-peace-db with postgres
echo "Starting art-peace-db ..."